From 253d1e9beb365db483f48151010bee5a283d822a Mon Sep 17 00:00:00 2001 From: Jeffrey Chen Date: Sun, 12 Apr 2026 19:47:13 -0700 Subject: [PATCH 001/153] Underline entire wrapped OSC 8 link on hover --- src/browser/Linkifier.test.ts | 40 +++++++++++ src/browser/OscLinkProvider.test.ts | 105 ++++++++++++++++++++++++++++ src/browser/OscLinkProvider.ts | 92 ++++++++++++++++++++---- 3 files changed, 224 insertions(+), 13 deletions(-) create mode 100644 src/browser/OscLinkProvider.test.ts diff --git a/src/browser/Linkifier.test.ts b/src/browser/Linkifier.test.ts index 4294ef105..685c404f4 100644 --- a/src/browser/Linkifier.test.ts +++ b/src/browser/Linkifier.test.ts @@ -43,6 +43,20 @@ describe('Linkifier2', () => { }, activate: () => { } }; + const multiLineLink: ILink = { + text: 'foo', + range: { + start: { + x: 2, + y: 1 + }, + end: { + x: 4, + y: 2 + } + }, + activate: () => { } + }; beforeEach(() => { const dom = new jsdom.JSDOM(); @@ -86,4 +100,30 @@ describe('Linkifier2', () => { linkifier.linkLeave({ classList: { add: () => { } } } as any, link, {} as any); }); + it('onShowLinkUnderline event range is correct for wrapped links', done => { + linkifier.onShowLinkUnderline(e => { + assert.equal(multiLineLink.range.start.x - 1, e.x1); + assert.equal(multiLineLink.range.start.y - 1, e.y1); + assert.equal(multiLineLink.range.end.x, e.x2); + assert.equal(multiLineLink.range.end.y - 1, e.y2); + + done(); + }); + + linkifier.linkHover({ classList: { add: () => { } } } as any, multiLineLink, {} as any); + }); + + it('onHideLinkUnderline event range is correct for wrapped links', done => { + linkifier.onHideLinkUnderline(e => { + assert.equal(multiLineLink.range.start.x - 1, e.x1); + assert.equal(multiLineLink.range.start.y - 1, e.y1); + assert.equal(multiLineLink.range.end.x, e.x2); + assert.equal(multiLineLink.range.end.y - 1, e.y2); + + done(); + }); + + linkifier.linkLeave({ classList: { add: () => { } } } as any, multiLineLink, {} as any); + }); + }); diff --git a/src/browser/OscLinkProvider.test.ts b/src/browser/OscLinkProvider.test.ts new file mode 100644 index 000000000..9e844cd16 --- /dev/null +++ b/src/browser/OscLinkProvider.test.ts @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2026 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { assert } from 'chai'; +import { OscLinkProvider } from 'browser/OscLinkProvider'; +import { ILink } from 'browser/Types'; +import { createCellData, MockBufferService, MockOptionsService } from 'common/TestUtils.test'; +import { IBufferService, IOscLinkService } from 'common/services/Services'; +import { IBufferLine, IOscLinkData } from 'common/Types'; + +class TestOscLinkService implements IOscLinkService { + public serviceBrand: any; + public registerLink(_linkData: IOscLinkData): number { return 0; } + public addLineToLink(_linkId: number, _y: number): void { } + public getLinkData(linkId: number): IOscLinkData | undefined { + return { uri: `https://example.com/${linkId}` }; + } +} + +function setText(line: IBufferLine | undefined, x: number, text: string): void { + if (!line) { + throw new Error('Missing buffer line'); + } + for (let i = 0; i < text.length; i++) { + line.setCell(x + i, createCellData(0, text[i], 1)); + } +} + +function setUrl(line: IBufferLine | undefined, x: number, text: string, linkId: number): void { + if (!line) { + throw new Error('Missing buffer line'); + } + for (let i = 0; i < text.length; i++) { + const cell = createCellData(0, text[i], 1); + cell.extended.urlId = linkId; + cell.updateExtended(); + line.setCell(x + i, cell); + } +} + +function getLinks(provider: OscLinkProvider, y: number): Promise { + return new Promise(resolve => provider.provideLinks(y, links => resolve(links ?? []))); +} + +describe('OscLinkProvider', () => { + let bufferService: IBufferService; + let provider: OscLinkProvider; + + beforeEach(() => { + const optionsService = new MockOptionsService(); + bufferService = new MockBufferService(5, 5, optionsService); + provider = new OscLinkProvider(bufferService, optionsService, new TestOscLinkService()); + }); + + it('expands a wrapped link range backward to the previous line', async () => { + const line1 = bufferService.buffer.lines.get(0); + const line2 = bufferService.buffer.lines.get(1); + setText(line1, 0, 'aa'); + setUrl(line1, 2, 'bbb', 1); + setUrl(line2, 0, 'cccc', 1); + setText(line2, 4, 'x'); + line2!.isWrapped = true; + + const links = await getLinks(provider, 2); + assert.lengthOf(links, 1); + assert.deepEqual(links[0].range, { + start: { x: 3, y: 1 }, + end: { x: 4, y: 2 } + }); + }); + + it('expands a wrapped link range forward when a link ends at line boundary', async () => { + const line1 = bufferService.buffer.lines.get(0); + const line2 = bufferService.buffer.lines.get(1); + setUrl(line1, 0, 'aaaaa', 1); + setUrl(line2, 0, 'bb', 1); + setText(line2, 2, 'ccc'); + line2!.isWrapped = true; + + const links = await getLinks(provider, 1); + assert.lengthOf(links, 1); + assert.deepEqual(links[0].range, { + start: { x: 1, y: 1 }, + end: { x: 2, y: 2 } + }); + }); + + it('does not merge wrapped links with different url ids', async () => { + const line1 = bufferService.buffer.lines.get(0); + const line2 = bufferService.buffer.lines.get(1); + setUrl(line1, 0, 'aaaaa', 1); + setUrl(line2, 0, 'bbb', 2); + setText(line2, 3, 'cc'); + line2!.isWrapped = true; + + const links = await getLinks(provider, 1); + assert.lengthOf(links, 1); + assert.deepEqual(links[0].range, { + start: { x: 1, y: 1 }, + end: { x: 5, y: 1 } + }); + }); +}); diff --git a/src/browser/OscLinkProvider.ts b/src/browser/OscLinkProvider.ts index b6a3cce0a..d01a20b35 100644 --- a/src/browser/OscLinkProvider.ts +++ b/src/browser/OscLinkProvider.ts @@ -6,6 +6,7 @@ import { IBufferRange, ILink } from 'browser/Types'; import { ILinkProvider } from 'browser/services/Services'; import { CellData } from 'common/buffer/CellData'; +import { IBufferLine } from 'common/Types'; import { IBufferService, IOptionsService, IOscLinkService } from 'common/services/Services'; export class OscLinkProvider implements ILinkProvider { @@ -57,19 +58,8 @@ export class OscLinkProvider implements ILinkProvider { if (finishLink || (currentStart !== -1 && x === lineLength - 1)) { const text = this._oscLinkService.getLinkData(currentLinkId)?.uri; if (text) { - // These ranges are 1-based - const range: IBufferRange = { - start: { - x: currentStart + 1, - y - }, - end: { - // Offset end x if it's a link that ends on the last cell in the line - x: x + (!finishLink && x === lineLength - 1 ? 1 : 0), - y - } - }; - + const endX = x + (!finishLink && x === lineLength - 1 ? 1 : 0); + const range = this._getRangeWithLineWrap(y, currentStart, endX, currentLinkId); let ignoreLink = false; if (!linkHandler?.allowNonHttpProtocols) { try { @@ -111,6 +101,82 @@ export class OscLinkProvider implements ILinkProvider { // id callback(result); } + + /** + * Expand a single-line OSC 8 range to a contiguous wrapped range for the same link id. + */ + private _getRangeWithLineWrap(y: number, startX: number, endX: number, linkId: number): IBufferRange { + let startY = y; + let finalStartX = startX; + let endY = y; + let finalEndX = endX; + + // Expand upward only when this segment starts at column 0 and the current line is wrapped. + while (finalStartX === 0) { + const currentLine = this._bufferService.buffer.lines.get(startY - 1); + if (!currentLine?.isWrapped) { + break; + } + const previousLine = this._bufferService.buffer.lines.get(startY - 2); + if (!previousLine) { + break; + } + const previousLineLength = previousLine.getTrimmedLength(); + if (previousLineLength === 0 || !this._hasUrlId(previousLine, previousLineLength - 1, linkId)) { + break; + } + let previousStartX = previousLineLength - 1; + while (previousStartX > 0 && this._hasUrlId(previousLine, previousStartX - 1, linkId)) { + previousStartX--; + } + startY--; + finalStartX = previousStartX; + } + + // Expand downward only when this segment reaches trimmed EOL and the next line is wrapped. + while (true) { + const currentLine = this._bufferService.buffer.lines.get(endY - 1); + if (!currentLine) { + break; + } + const currentLineLength = currentLine.getTrimmedLength(); + if (finalEndX !== currentLineLength) { + break; + } + const nextLine = this._bufferService.buffer.lines.get(endY); + if (!nextLine?.isWrapped) { + break; + } + const nextLineLength = nextLine.getTrimmedLength(); + if (nextLineLength === 0 || !this._hasUrlId(nextLine, 0, linkId)) { + break; + } + let nextEndX = 1; + while (nextEndX < nextLineLength && this._hasUrlId(nextLine, nextEndX, linkId)) { + nextEndX++; + } + endY++; + finalEndX = nextEndX; + } + + // IBufferRange uses 1-based coordinates. + return { + start: { + x: finalStartX + 1, + y: startY + }, + end: { + x: finalEndX, + y: endY + } + }; + } + + private _hasUrlId(line: IBufferLine, x: number, linkId: number): boolean { + const cell = this._workCell; + line.loadCell(x, cell); + return !!cell.hasExtendedAttrs() && cell.extended.urlId === linkId; + } } function defaultActivate(e: MouseEvent, uri: string): void { From 69ee4ad23b10bdf8e3fe71a1644518f469ed0820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Mon, 13 Apr 2026 12:51:17 +0200 Subject: [PATCH 002/153] fix: use element window instead of global object --- src/browser/services/MouseCoordsService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/services/MouseCoordsService.ts b/src/browser/services/MouseCoordsService.ts index e2455fd2b..60f0a6ab9 100644 --- a/src/browser/services/MouseCoordsService.ts +++ b/src/browser/services/MouseCoordsService.ts @@ -18,7 +18,7 @@ export class MouseCoordsService implements IMouseCoordsService { public getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined { return getCoords( - window, + getWindow(element), event, element, colCount, From ff72c00219718c14e6aabb086b49746c7077256b Mon Sep 17 00:00:00 2001 From: lawrence3699 Date: Wed, 22 Apr 2026 12:57:27 +1000 Subject: [PATCH 003/153] fix(addon-ligatures): run package build on publish --- addons/addon-ligatures/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-ligatures/package.json b/addons/addon-ligatures/package.json index 5fb0c4f66..cd70ada2b 100644 --- a/addons/addon-ligatures/package.json +++ b/addons/addon-ligatures/package.json @@ -20,7 +20,7 @@ "package": "webpack", "pretest": "npm run build", "test": "nyc mocha out/**/*.test.js", - "prepublish": "npm run package" + "prepublishOnly": "npm run package" }, "keywords": [ "font", From 685434ca847cfca6152604b8be878f3e3a749ef5 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Thu, 23 Apr 2026 11:35:56 -0400 Subject: [PATCH 004/153] Fix IntersectionObserver retention on dispose --- src/browser/services/RenderService.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index 83e3d3379..e999b7eac 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -29,6 +29,7 @@ export class RenderService extends Disposable implements IRenderService { private _renderDebouncer: IRenderDebouncerWithCallback; private _pausedResizeTask: DebouncedIdleTask; private _observerDisposable = this._register(new MutableDisposable()); + private _intersectionObserver: IntersectionObserver | undefined; private _isPaused: boolean = false; private _needsFullRefresh: boolean = false; @@ -127,8 +128,12 @@ export class RenderService extends Disposable implements IRenderService { // and resume based on terminal visibility if so if ('IntersectionObserver' in w) { const observer = new w.IntersectionObserver(e => this._handleIntersectionChange(e[e.length - 1]), { threshold: 0 }); + this._observerDisposable.value = toDisposable(() => { + this._intersectionObserver?.disconnect(); + this._intersectionObserver = undefined; + }); + this._intersectionObserver = observer; observer.observe(screenElement); - this._observerDisposable.value = toDisposable(() => observer.disconnect()); } } From 9e27c7debba94d4380c5297aa911b2d385717f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Wed, 29 Apr 2026 21:36:45 +0200 Subject: [PATCH 005/153] filepart handling in IIP --- addons/addon-image/src/IIPHandler.ts | 71 +++++++-- .../addon-image/src/IIPHeaderParser.test.ts | 136 +++++++++++++++++- addons/addon-image/src/IIPHeaderParser.ts | 97 +++++++++++-- 3 files changed, 272 insertions(+), 32 deletions(-) diff --git a/addons/addon-image/src/IIPHandler.ts b/addons/addon-image/src/IIPHandler.ts index 76492c405..5872e29fa 100644 --- a/addons/addon-image/src/IIPHandler.ts +++ b/addons/addon-image/src/IIPHandler.ts @@ -7,7 +7,7 @@ import { ImageRenderer } from './ImageRenderer'; import { IIPImageStorage } from './IIPImageStorage'; import { CELL_SIZE_DEFAULT } from './ImageStorage'; import Base64Decoder from 'xterm-wasm-parts/lib/base64/Base64Decoder.wasm'; -import { HeaderParser, IHeaderFields, HeaderState } from './IIPHeaderParser'; +import { HeaderParser, IHeaderFields, HeaderState, SequenceType } from './IIPHeaderParser'; import { imageType, UNSUPPORTED_TYPE } from './IIPMetrics'; // Local const enum mirror - esbuild can't inline const enums from external packages @@ -22,6 +22,7 @@ const enum DecoderConst { // default IIP header values const DEFAULT_HEADER: IHeaderFields = { + type: SequenceType.INVALID, name: 'Unnamed file', size: 0, width: 'auto', @@ -37,6 +38,8 @@ export class IIPHandler implements IOscHandler, IResetHandler { private _header: IHeaderFields = DEFAULT_HEADER; private _dec: Base64Decoder; private _metrics = UNSUPPORTED_TYPE; + private _isMultipart = false; + private _abortMulti = false; constructor( private readonly _opts: IImageAddonOptions, @@ -53,7 +56,6 @@ export class IIPHandler implements IOscHandler, IResetHandler { public start(): void { this._aborted = false; - this._header = DEFAULT_HEADER; this._metrics = UNSUPPORTED_TYPE; this._hp.reset(); } @@ -73,15 +75,27 @@ export class IIPHandler implements IOscHandler, IResetHandler { return; } if (dataPos > 0) { - this._header = Object.assign({}, DEFAULT_HEADER, this._hp.fields); - if (!this._header.inline || !this._header.size || this._header.size > this._opts.iipSizeLimit) { + const seqType = this._hp.fields.type; + if (seqType === SequenceType.FILE) { + if (this._isMultipart) { + this._isMultipart = false; + this._abortMulti = false; + this._dec.release(); + } + this._header = Object.assign({}, DEFAULT_HEADER, this._hp.fields); + if (!this._header.inline) { + this._aborted = true; + return; + } + this._dec.init(); + } else if (this._abortMulti) { this._aborted = true; return; } - this._dec.init(); if ((this._dec.put(data.subarray(dataPos, end)) as number) !== DecoderConst.OK) { this._dec.release(); this._aborted = true; + if (this._isMultipart) this._abortMulti = true; } } } @@ -90,6 +104,37 @@ export class IIPHandler implements IOscHandler, IResetHandler { public end(success: boolean): boolean | Promise { if (this._aborted) return true; + if (this._hp.state !== HeaderState.END) { + if (this._hp.end()) return true; + } + const seqType = this._hp.fields.type; + + if (seqType === SequenceType.FILEPART) return true; + + if (seqType === SequenceType.REPORTCELLSIZE) { + // OSC 1337 ; ReportCellSize=[height];[width];[scale] ST + // TODO: fill stub with real values + const report = `\x1b]1337;ReportCellSize=[height];[width];[scale]\x1b\\`; + this._coreTerminal?._core.coreService.triggerDataEvent(report); + return true; + } + + if (seqType === SequenceType.MULTIPARTFILE) { + this._header = Object.assign({}, DEFAULT_HEADER, this._hp.fields); + this._isMultipart = true; + this._abortMulti = false; + this._dec.init(); + return true; + } + + if (seqType === SequenceType.FILEEND) { + if (!this._isMultipart) return true; + this._isMultipart = false; + if (this._abortMulti || this._header.type !== SequenceType.MULTIPARTFILE) return true; + } + + // fallthrough for SequenceType.FILE & SequenceType.FILEEND + let w = 0; let h = 0; @@ -97,15 +142,13 @@ export class IIPHandler implements IOscHandler, IResetHandler { let cond: number | boolean = true; if (cond = success) { if (cond = !this._dec.end()) { - if (cond = this._dec.data8.length === this._header.size) { - this._metrics = imageType(this._dec.data8); - if (cond = this._metrics.mime !== 'unsupported') { - w = this._metrics.width; - h = this._metrics.height; - if (cond = w && h && w * h < this._opts.pixelLimit) { - [w, h] = this._resize(w, h).map(Math.floor); - cond = w && h && w * h < this._opts.pixelLimit; - } + this._metrics = imageType(this._dec.data8); + if (cond = this._metrics.mime !== 'unsupported') { + w = this._metrics.width; + h = this._metrics.height; + if (cond = w && h && w * h < this._opts.pixelLimit) { + [w, h] = this._resize(w, h).map(Math.floor); + cond = w && h && w * h < this._opts.pixelLimit; } } } diff --git a/addons/addon-image/src/IIPHeaderParser.test.ts b/addons/addon-image/src/IIPHeaderParser.test.ts index e42782c14..ba6d5f052 100644 --- a/addons/addon-image/src/IIPHeaderParser.test.ts +++ b/addons/addon-image/src/IIPHeaderParser.test.ts @@ -4,19 +4,20 @@ */ import { assert } from 'chai'; -import { HeaderParser, HeaderState, IHeaderFields } from './IIPHeaderParser'; +import { HeaderParser, HeaderState, IHeaderFields, SequenceType } from './IIPHeaderParser'; const CASES: [string, IHeaderFields][] = [ - ['File=size=123456;name=dGVzdA==:', {name: 'test', size: 123456}], - ['File=size=123456;name=dGVzdA:', {name: 'test', size: 123456}], + ['File=size=123456;name=dGVzdA==:', {type: SequenceType.FILE, name: 'test', size: 123456}], + ['File=size=123456;name=dGVzdA:', {type: SequenceType.FILE, name: 'test', size: 123456}], // utf-8 encoding in name - ['File=size=123456;name=w7xtbMOkdXTDnw==:', {name: 'ümläutß', size: 123456}], - ['File=size=123456;name=w7xtbMOkdXTDnw:', {name: 'ümläutß', size: 123456}], + ['File=size=123456;name=w7xtbMOkdXTDnw==:', {type: SequenceType.FILE, name: 'ümläutß', size: 123456}], + ['File=size=123456;name=w7xtbMOkdXTDnw:', {type: SequenceType.FILE, name: 'ümläutß', size: 123456}], // full header spec [ 'File=inline=1;width=10px;height=20%;preserveAspectRatio=1;size=123456;name=w7xtbMOkdXTDnw:', { + type: SequenceType.FILE, inline: 1, width: '10px', height: '20%', @@ -28,6 +29,7 @@ const CASES: [string, IHeaderFields][] = [ [ 'File=inline=1;width=auto;height=20;preserveAspectRatio=1;size=123456;name=w7xtbMOkdXTDnw:', { + type: SequenceType.FILE, inline: 1, width: 'auto', height: '20', @@ -101,7 +103,7 @@ describe('IIPHeaderParser', () => { let res = hp.parse(inp, 0, inp.length); assert.strictEqual(res, inp.length); assert.strictEqual(hp.state, HeaderState.END); - assert.deepEqual(hp.fields, {name: 'test', size: 0}); + assert.deepEqual(hp.fields, {type: SequenceType.FILE, name: 'test', size: 0}); hp.reset(); inp = fromBs(CASES[0][0]); res = hp.parse(inp, 0, inp.length); @@ -115,7 +117,7 @@ describe('IIPHeaderParser', () => { let res = hp.parse(inp, 0, inp.length); assert.strictEqual(res, inp.length); assert.strictEqual(hp.state, HeaderState.END); - assert.deepEqual(hp.fields, {name: '', size: 123456}); + assert.deepEqual(hp.fields, {type: SequenceType.FILE, name: '', size: 123456}); hp.reset(); inp = fromBs(CASES[0][0]); res = hp.parse(inp, 0, inp.length); @@ -135,4 +137,124 @@ describe('IIPHeaderParser', () => { assert.strictEqual(hp.state, HeaderState.END); assert.deepEqual(hp.fields, CASES[0][1]); }); + it('FilePart sequence', () => { + const hp = new HeaderParser(); + const inp = fromBs('FilePart=w7xtbMOkdXTDnw'); + const res = hp.parse(inp, 0, inp.length); + assert.strictEqual(res, 9); + assert.strictEqual(hp.state, HeaderState.END); + assert.deepEqual(hp.fields.type, SequenceType.FILEPART); + }); + it('FilePart sequence - bytewise', () => { + const hp = new HeaderParser(); + const inp = fromBs('FilePart=w7xtbMOkdXTDnw'); + let res = -2; + let pos = 0; + while (res === -2 && pos < inp.length) { + res = hp.parse(new Uint32Array([inp[pos++]]), 0, 1); + } + assert.strictEqual(res, 1); + assert.strictEqual(hp.state, HeaderState.END); + assert.deepEqual(hp.fields.type, SequenceType.FILEPART); + }); + it('MultipartFile sequence', () => { + const hp = new HeaderParser(); + const inp = fromBs('MultipartFile=inline=1;width=10px;height=20%;preserveAspectRatio=1;size=123456;name=w7xtbMOkdXTDnw'); + let res = hp.parse(inp, 0, inp.length); + assert.strictEqual(res, -2); + assert.strictEqual(hp.state, HeaderState.VALUE); + res = hp.end(); + assert.strictEqual(res, 0); + assert.strictEqual(hp.state, HeaderState.END); + assert.deepEqual( + hp.fields, + { + type: SequenceType.MULTIPARTFILE, + inline: 1, + width: '10px', + height: '20%', + preserveAspectRatio: 1, + size: 123456, + name: 'ümläutß' + } + ); + }); + it('MultipartFile sequence - bytewise', () => { + const hp = new HeaderParser(); + const inp = fromBs('MultipartFile=inline=1;width=10px;height=20%;preserveAspectRatio=1;size=123456;name=w7xtbMOkdXTDnw'); + let res = -2; + let pos = 0; + while (res === -2 && pos < inp.length) { + res = hp.parse(new Uint32Array([inp[pos++]]), 0, 1); + } + assert.strictEqual(res, -2); + assert.strictEqual(hp.state, HeaderState.VALUE); + res = hp.end(); + assert.strictEqual(res, 0); + assert.strictEqual(hp.state, HeaderState.END); + assert.deepEqual( + hp.fields, + { + type: SequenceType.MULTIPARTFILE, + inline: 1, + width: '10px', + height: '20%', + preserveAspectRatio: 1, + size: 123456, + name: 'ümläutß' + } + ); + }); + it('FileEnd sequence', () => { + const hp = new HeaderParser(); + const inp = fromBs('FileEnd'); + let res = hp.parse(inp, 0, inp.length); + assert.strictEqual(res, -2); + assert.strictEqual(hp.state, HeaderState.START); + res = hp.end(); + assert.strictEqual(res, 0); + assert.strictEqual(hp.state, HeaderState.END); + assert.deepEqual(hp.fields.type, SequenceType.FILEEND); + }); + it('FileEnd sequence - bytewise', () => { + const hp = new HeaderParser(); + const inp = fromBs('FileEnd'); + let res = -2; + let pos = 0; + while (res === -2 && pos < inp.length) { + res = hp.parse(new Uint32Array([inp[pos++]]), 0, 1); + } + assert.strictEqual(res, -2); + assert.strictEqual(hp.state, HeaderState.START); + res = hp.end(); + assert.strictEqual(res, 0); + assert.strictEqual(hp.state, HeaderState.END); + assert.deepEqual(hp.fields.type, SequenceType.FILEEND); + }); + it('ReportCellSize sequence', () => { + const hp = new HeaderParser(); + const inp = fromBs('ReportCellSize'); + let res = hp.parse(inp, 0, inp.length); + assert.strictEqual(res, -2); + assert.strictEqual(hp.state, HeaderState.START); + res = hp.end(); + assert.strictEqual(res, 0); + assert.strictEqual(hp.state, HeaderState.END); + assert.deepEqual(hp.fields, { type: SequenceType.REPORTCELLSIZE }); + }); + it('ReportCellSize sequence - bytewise', () => { + const hp = new HeaderParser(); + const inp = fromBs('ReportCellSize'); + let res = -2; + let pos = 0; + while (res === -2 && pos < inp.length) { + res = hp.parse(new Uint32Array([inp[pos++]]), 0, 1); + } + assert.strictEqual(res, -2); + assert.strictEqual(hp.state, HeaderState.START); + res = hp.end(); + assert.strictEqual(res, 0); + assert.strictEqual(hp.state, HeaderState.END); + assert.deepEqual(hp.fields, { type: SequenceType.REPORTCELLSIZE }); + }); }); diff --git a/addons/addon-image/src/IIPHeaderParser.ts b/addons/addon-image/src/IIPHeaderParser.ts index af4e9a68d..9c20262b1 100644 --- a/addons/addon-image/src/IIPHeaderParser.ts +++ b/addons/addon-image/src/IIPHeaderParser.ts @@ -6,9 +6,27 @@ // eslint-disable-next-line declare const Buffer: any; +export const enum HeaderState { + START = 0, + ABORT = 1, + KEY = 2, + VALUE = 3, + END = 4 +} + +export const enum SequenceType { + INVALID = 0, + FILE = 1, + MULTIPARTFILE = 2, + FILEPART = 3, + FILEEND = 4, + REPORTCELLSIZE = 5 +} export interface IHeaderFields { [key: string]: number | string | Uint32Array | null | undefined; + // sequence type + type: SequenceType; // base-64 encoded filename. Defaults to "Unnamed file". name: string; // File size in bytes. The file transfer will be canceled if this size is exceeded. @@ -29,14 +47,6 @@ export interface IHeaderFields { inline?: number; } -export const enum HeaderState { - START = 0, - ABORT = 1, - KEY = 2, - VALUE = 3, - END = 4 -} - // field value decoders // ASCII bytes to string @@ -92,7 +102,19 @@ const DECODERS: {[key: string]: (v: Uint32Array) => number | string} = { }; +// sequence type markers +// File const FILE_MARKER = [70, 105, 108, 101]; +// MultipartFile +const MULTIPARTFILE_MARKER = [77, 117, 108, 116, 105, 112, 97, 114, 116, 70, 105, 108, 101]; +// FilePart +const FILEPART_MARKER = [70, 105, 108, 101, 80, 97, 114, 116]; +// FileEnd +const FILEEND_MARKER = [70, 105, 108, 101, 69, 110, 100]; +// ReportCellSize +const REPORTCELLSIZE_MARKER = [82, 101, 112, 111, 114, 116, 67, 101, 108, 108, 83, 105, 122, 101]; + +// max allowed chars for sequence header const MAX_FIELDCHARS = 1024; @@ -111,12 +133,43 @@ export class HeaderParser { this._key = ''; } + public end(): number { + if (this.state === HeaderState.START) { + if (this._position === FILEEND_MARKER.length) { + for (let k = 0; k < FILEEND_MARKER.length; ++k) { + if (this._buffer[k] !== FILEEND_MARKER[k]) return this._a(); + } + this.fields['type'] = SequenceType.FILEEND; + this.state = HeaderState.END; + return 0; + } + if (this._position === REPORTCELLSIZE_MARKER.length) { + for (let k = 0; k < REPORTCELLSIZE_MARKER.length; ++k) { + if (this._buffer[k] !== REPORTCELLSIZE_MARKER[k]) return this._a(); + } + this.fields['type'] = SequenceType.REPORTCELLSIZE; + this.state = HeaderState.END; + return 0; + } + return this._a(); + } + if (this.state === HeaderState.END) return 0; + if (this.state === HeaderState.VALUE + && this.fields.type === SequenceType.MULTIPARTFILE + ) { + if (!this._storeValue(this._position)) return this._a(); + this.state = HeaderState.END; + return 0; + } + return this._a(); + } + public parse(data: Uint32Array, start: number, end: number): number { let state = this.state; let pos = this._position; const buffer = this._buffer; if (state === HeaderState.ABORT || state === HeaderState.END) return -1; - if (state === HeaderState.START && pos > 6) return -1; + if (state === HeaderState.START && pos > 14) return -1; for (let i = start; i < end; ++i) { const c = data[i]; switch (c) { @@ -127,8 +180,29 @@ export class HeaderParser { break; case 61: // = if (state === HeaderState.START) { - for (let k = 0; k < FILE_MARKER.length; ++k) { - if (buffer[k] !== FILE_MARKER[k]) return this._a(); + if (buffer[0] === 70) { + // 'File' or 'FilePart' + let k = 0; + for (; k < FILE_MARKER.length; ++k) { + if (buffer[k] !== FILE_MARKER[k]) return this._a(); + } + this.fields['type'] = SequenceType.FILE; + if (pos === FILEPART_MARKER.length) { + for (; k < FILEPART_MARKER.length; ++k) { + if (buffer[k] !== FILEPART_MARKER[k]) return this._a(); + } + this.fields['type'] = SequenceType.FILEPART; + this.state = HeaderState.END; + return i + 1; + } + } else if (buffer[0] === 77) { + // 'MultipartFile' + for (let k = 0; k < MULTIPARTFILE_MARKER.length; ++k) { + if (buffer[k] !== MULTIPARTFILE_MARKER[k]) return this._a(); + } + this.fields['type'] = SequenceType.MULTIPARTFILE; + } else { + return this._a(); } state = HeaderState.KEY; pos = 0; @@ -158,6 +232,7 @@ export class HeaderParser { } private _a(): number { + this.fields.type = SequenceType.INVALID; this.state = HeaderState.ABORT; return -1; } From 05ea5ac3245d8f7d20e4d31d84de46221af367c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 30 Apr 2026 10:33:34 +0200 Subject: [PATCH 006/153] implement ReportCellSize --- addons/addon-image/src/IIPHandler.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/addons/addon-image/src/IIPHandler.ts b/addons/addon-image/src/IIPHandler.ts index 5872e29fa..c1e9bac46 100644 --- a/addons/addon-image/src/IIPHandler.ts +++ b/addons/addon-image/src/IIPHandler.ts @@ -113,8 +113,16 @@ export class IIPHandler implements IOscHandler, IResetHandler { if (seqType === SequenceType.REPORTCELLSIZE) { // OSC 1337 ; ReportCellSize=[height];[width];[scale] ST - // TODO: fill stub with real values - const report = `\x1b]1337;ReportCellSize=[height];[width];[scale]\x1b\\`; + let width = CELL_SIZE_DEFAULT.width; + let height = CELL_SIZE_DEFAULT.height; + if (this._renderer.dimensions) { + width = this._renderer.dimensions.css.canvas.width / this._coreTerminal.cols; + height = this._renderer.dimensions.css.canvas.height / this._coreTerminal.rows; + console.log(this._renderer.dimensions); + console.log({width, height}); + } + const scale = window ? window.devicePixelRatio : 1; + const report = `\x1b]1337;ReportCellSize=${height.toFixed(3)};${width.toFixed(3)};${scale.toFixed(3)}\x1b\\`; this._coreTerminal?._core.coreService.triggerDataEvent(report); return true; } @@ -123,6 +131,7 @@ export class IIPHandler implements IOscHandler, IResetHandler { this._header = Object.assign({}, DEFAULT_HEADER, this._hp.fields); this._isMultipart = true; this._abortMulti = false; + this._dec.release(); this._dec.init(); return true; } From 51c8d8abf86bc8963ccf857deb7c4d769b19c062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 30 Apr 2026 10:36:41 +0200 Subject: [PATCH 007/153] implement ReportCellSize --- addons/addon-image/src/IIPHandler.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/addons/addon-image/src/IIPHandler.ts b/addons/addon-image/src/IIPHandler.ts index c1e9bac46..942b243b5 100644 --- a/addons/addon-image/src/IIPHandler.ts +++ b/addons/addon-image/src/IIPHandler.ts @@ -118,8 +118,6 @@ export class IIPHandler implements IOscHandler, IResetHandler { if (this._renderer.dimensions) { width = this._renderer.dimensions.css.canvas.width / this._coreTerminal.cols; height = this._renderer.dimensions.css.canvas.height / this._coreTerminal.rows; - console.log(this._renderer.dimensions); - console.log({width, height}); } const scale = window ? window.devicePixelRatio : 1; const report = `\x1b]1337;ReportCellSize=${height.toFixed(3)};${width.toFixed(3)};${scale.toFixed(3)}\x1b\\`; From 53554a05f750326df4d69d4ef29ff90f8ba27922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 30 Apr 2026 10:44:38 +0200 Subject: [PATCH 008/153] use _coreBrowserService.dpr --- addons/addon-image/src/IIPHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-image/src/IIPHandler.ts b/addons/addon-image/src/IIPHandler.ts index 942b243b5..e34b387c7 100644 --- a/addons/addon-image/src/IIPHandler.ts +++ b/addons/addon-image/src/IIPHandler.ts @@ -119,7 +119,7 @@ export class IIPHandler implements IOscHandler, IResetHandler { width = this._renderer.dimensions.css.canvas.width / this._coreTerminal.cols; height = this._renderer.dimensions.css.canvas.height / this._coreTerminal.rows; } - const scale = window ? window.devicePixelRatio : 1; + const scale = this._coreTerminal._core._coreBrowserService?.dpr ?? 1; const report = `\x1b]1337;ReportCellSize=${height.toFixed(3)};${width.toFixed(3)};${scale.toFixed(3)}\x1b\\`; this._coreTerminal?._core.coreService.triggerDataEvent(report); return true; From 83e3657a822f492e08e91f4150cbbf2324a5167d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 30 Apr 2026 15:21:41 +0200 Subject: [PATCH 009/153] fix _mouseCoordsService access in demo --- demo/client/components/window/addonImageWindow.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/client/components/window/addonImageWindow.ts b/demo/client/components/window/addonImageWindow.ts index 392b7177b..a18fb9c98 100644 --- a/demo/client/components/window/addonImageWindow.ts +++ b/demo/client/components/window/addonImageWindow.ts @@ -176,7 +176,7 @@ export class AddonImageWindow extends BaseWindow implements IControlWindow { // } // } - const pos = (this._terminal as any)._core._mouseService!.getCoords(ev, (this._terminal as any)._core.screenElement!, this._terminal.cols, this._terminal.rows); + const pos = (this._terminal as any)._core._mouseCoordsService!.getCoords(ev, (this._terminal as any)._core.screenElement!, this._terminal.cols, this._terminal.rows); const x = pos[0] - 1; const y = pos[1] - 1; const canvas = ev.shiftKey From 645865efec0a14c38654e38d93c6b7c1887ec12a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 30 Apr 2026 16:46:22 +0200 Subject: [PATCH 010/153] fix cursor placement in IIP --- addons/addon-image/src/IIPImageStorage.ts | 4 +++- addons/addon-image/src/ImageStorage.ts | 24 ++++++++++++------- addons/addon-image/src/SixelImageStorage.ts | 6 +++-- addons/addon-image/src/Types.ts | 8 +++++++ .../src/kitty/KittyImageStorage.ts | 8 +++++-- 5 files changed, 37 insertions(+), 13 deletions(-) diff --git a/addons/addon-image/src/IIPImageStorage.ts b/addons/addon-image/src/IIPImageStorage.ts index 0da91607f..6c773f53d 100644 --- a/addons/addon-image/src/IIPImageStorage.ts +++ b/addons/addon-image/src/IIPImageStorage.ts @@ -3,6 +3,7 @@ * @license MIT */ +import { IAddImageOpts } from 'Types'; import { ImageStorage } from './ImageStorage'; /** @@ -12,6 +13,7 @@ import { ImageStorage } from './ImageStorage'; * - Always uses scrolling mode (cursor advances with image) */ export class IIPImageStorage { + private _addImageOpts: IAddImageOpts = {scrolling: true, layer: 'top', zIndex: 0, cursorPos: 'iip'}; constructor( private readonly _storage: ImageStorage ) {} @@ -21,6 +23,6 @@ export class IIPImageStorage { * Always uses scrolling mode — cursor advances past the image. */ public addImage(img: HTMLCanvasElement | ImageBitmap): void { - this._storage.addImage(img, true); + this._storage.addImage(img, this._addImageOpts); } } diff --git a/addons/addon-image/src/ImageStorage.ts b/addons/addon-image/src/ImageStorage.ts index 8c89a16b0..ea5934f1f 100644 --- a/addons/addon-image/src/ImageStorage.ts +++ b/addons/addon-image/src/ImageStorage.ts @@ -5,7 +5,11 @@ import { IDisposable } from '@xterm/xterm'; import { ImageRenderer } from './ImageRenderer'; -import { ITerminalExt, IExtendedAttrsImage, IImageAddonOptions, IImageSpec, IBufferLineExt, BgFlags, Cell, Content, ICellSize, ExtFlags, Attributes, UnderlineStyle, ImageLayer } from './Types'; +import { + ITerminalExt, IExtendedAttrsImage, IImageAddonOptions, IImageSpec, + IBufferLineExt, BgFlags, Cell, Content, ICellSize, ExtFlags, Attributes, + UnderlineStyle, IAddImageOpts +} from './Types'; // fallback default cell size @@ -241,7 +245,7 @@ export class ImageStorage implements IDisposable { * @param zIndex - Z-index for image layering within the same layer. * @returns The internal image ID assigned to the stored image. */ - public addImage(img: HTMLCanvasElement | ImageBitmap, scrolling: boolean, layer: ImageLayer = 'top', zIndex: number = 0): number { + public addImage(img: HTMLCanvasElement | ImageBitmap, opts: IAddImageOpts): number { // never allow storage to exceed memory limit this._evictOldest(img.width * img.height); @@ -263,7 +267,7 @@ export class ImageStorage implements IDisposable { let offset = originX; let tileCount = 0; - if (!scrolling) { + if (!opts.scrolling) { buffer.x = 0; buffer.y = 0; offset = 0; @@ -277,7 +281,7 @@ export class ImageStorage implements IDisposable { this._writeToCell(line as IBufferLineExt, offset + col, imageId, row * cols + col); tileCount++; } - if (scrolling) { + if (opts.scrolling) { if (row < rows - 1) this._terminal._core._inputHandler.lineFeed(); } else { if (++buffer.y >= termRows) break; @@ -287,8 +291,12 @@ export class ImageStorage implements IDisposable { this._terminal._core._inputHandler._dirtyRowTracker.markDirty(buffer.y); // cursor positioning modes - if (scrolling) { - buffer.x = offset; + if (opts.scrolling) { + if (opts.cursorPos === 'iip') { + buffer.x = Math.min(offset + cols, termCols); + } else { + buffer.x = offset; + } } else { buffer.x = originX; buffer.y = originY; @@ -331,8 +339,8 @@ export class ImageStorage implements IDisposable { marker: endMarker || undefined, tileCount, bufferType: this._terminal.buffer.active.type, - layer, - zIndex + layer: opts.layer, + zIndex: opts.zIndex }; // finally add the image diff --git a/addons/addon-image/src/SixelImageStorage.ts b/addons/addon-image/src/SixelImageStorage.ts index 4c5f555bb..b9f57fae0 100644 --- a/addons/addon-image/src/SixelImageStorage.ts +++ b/addons/addon-image/src/SixelImageStorage.ts @@ -4,7 +4,7 @@ */ import { ImageStorage, CELL_SIZE_DEFAULT } from './ImageStorage'; -import { IImageAddonOptions, ITerminalExt } from './Types'; +import { IImageAddonOptions, ITerminalExt, IAddImageOpts } from './Types'; import { ImageRenderer } from './ImageRenderer'; /** @@ -15,6 +15,7 @@ import { ImageRenderer } from './ImageRenderer'; * - advanceCursor for empty sixels carrying only height */ export class SixelImageStorage { + private _addImageOpts: IAddImageOpts = {scrolling: true, layer: 'top', zIndex: 0, cursorPos: 'vt340'}; constructor( private readonly _storage: ImageStorage, private readonly _opts: IImageAddonOptions, @@ -27,7 +28,8 @@ export class SixelImageStorage { * Cursor behavior depends on the sixelScrolling option (DECSET 80). */ public addImage(img: HTMLCanvasElement | ImageBitmap): void { - this._storage.addImage(img, this._opts.sixelScrolling); + this._addImageOpts.scrolling = this._opts.sixelScrolling; + this._storage.addImage(img, this._addImageOpts); } /** diff --git a/addons/addon-image/src/Types.ts b/addons/addon-image/src/Types.ts index 80cec9e24..19a1acca1 100644 --- a/addons/addon-image/src/Types.ts +++ b/addons/addon-image/src/Types.ts @@ -100,6 +100,14 @@ export interface ICellSize { } export type ImageLayer = 'top' | 'bottom'; +export type CursorPos = 'vt340' | 'iip'; + +export interface IAddImageOpts { + scrolling: boolean; + layer: ImageLayer; + zIndex: number; + cursorPos: CursorPos; +} export interface IImageSpec { orig: HTMLCanvasElement | ImageBitmap | undefined; diff --git a/addons/addon-image/src/kitty/KittyImageStorage.ts b/addons/addon-image/src/kitty/KittyImageStorage.ts index 95ea594c7..b55b6cf7e 100644 --- a/addons/addon-image/src/kitty/KittyImageStorage.ts +++ b/addons/addon-image/src/kitty/KittyImageStorage.ts @@ -5,7 +5,7 @@ import { IDisposable } from '@xterm/xterm'; import { ImageStorage } from '../ImageStorage'; -import { ImageLayer } from '../Types'; +import { ImageLayer, IAddImageOpts } from '../Types'; import { IKittyImageData } from './KittyGraphicsTypes'; // Kitty-specific image storage controller. @@ -42,6 +42,7 @@ export class KittyImageStorage implements IDisposable { this._images.delete(kittyId); } }; + private _addImageOpts: IAddImageOpts = {scrolling: true, layer: 'top', zIndex: 0, cursorPos: 'iip'}; constructor( private readonly _storage: ImageStorage @@ -98,7 +99,10 @@ export class KittyImageStorage implements IDisposable { if (oldStorageId !== undefined) { this._storageIdToKittyId.delete(oldStorageId); } - const storageId = this._storage.addImage(image, scrolling, layer, zIndex); + this._addImageOpts.scrolling = scrolling; + this._addImageOpts.layer = layer; + this._addImageOpts.zIndex = zIndex; + const storageId = this._storage.addImage(image, this._addImageOpts); this._kittyIdToStorageId.set(kittyId, storageId); this._storageIdToKittyId.set(storageId, kittyId); } From 494cf1f2e1254df74841b5be039ced51a67f7daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 30 Apr 2026 16:54:54 +0200 Subject: [PATCH 011/153] make linter happy --- addons/addon-image/src/IIPImageStorage.ts | 2 +- addons/addon-image/src/ImageStorage.ts | 10 ++++++---- addons/addon-image/src/SixelImageStorage.ts | 2 +- addons/addon-image/src/kitty/KittyImageStorage.ts | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/addons/addon-image/src/IIPImageStorage.ts b/addons/addon-image/src/IIPImageStorage.ts index 6c773f53d..282fea9f0 100644 --- a/addons/addon-image/src/IIPImageStorage.ts +++ b/addons/addon-image/src/IIPImageStorage.ts @@ -13,7 +13,7 @@ import { ImageStorage } from './ImageStorage'; * - Always uses scrolling mode (cursor advances with image) */ export class IIPImageStorage { - private _addImageOpts: IAddImageOpts = {scrolling: true, layer: 'top', zIndex: 0, cursorPos: 'iip'}; + private _addImageOpts: IAddImageOpts = { scrolling: true, layer: 'top', zIndex: 0, cursorPos: 'iip' }; constructor( private readonly _storage: ImageStorage ) {} diff --git a/addons/addon-image/src/ImageStorage.ts b/addons/addon-image/src/ImageStorage.ts index ea5934f1f..8aae7b5c6 100644 --- a/addons/addon-image/src/ImageStorage.ts +++ b/addons/addon-image/src/ImageStorage.ts @@ -239,10 +239,12 @@ export class ImageStorage implements IDisposable { /** * Method to add an image to the storage. * @param img - The image to add (canvas or bitmap). - * @param scrolling - When true, cursor advances with the image (lineFeed per row). - * When false, image is placed at (0,0) and cursor is restored (DECSET 80 / sixel origin mode). - * @param layer - Which canvas layer to render on ('top' or 'bottom'). - * @param zIndex - Z-index for image layering within the same layer. + * @param opts - Options for addImage: + * - scrolling: When true, cursor advances with the image. + * When false, image is placed at ORIGIN and cursor does not move. + * - layer: Which canvas layer to render on ('top' or 'bottom'). + * - zIndex: Z-index for image layering within the same layer. + * - cursorPos: 'vt340' for bottom-left, 'iip' for bottom.right. * @returns The internal image ID assigned to the stored image. */ public addImage(img: HTMLCanvasElement | ImageBitmap, opts: IAddImageOpts): number { diff --git a/addons/addon-image/src/SixelImageStorage.ts b/addons/addon-image/src/SixelImageStorage.ts index b9f57fae0..c2376a68a 100644 --- a/addons/addon-image/src/SixelImageStorage.ts +++ b/addons/addon-image/src/SixelImageStorage.ts @@ -15,7 +15,7 @@ import { ImageRenderer } from './ImageRenderer'; * - advanceCursor for empty sixels carrying only height */ export class SixelImageStorage { - private _addImageOpts: IAddImageOpts = {scrolling: true, layer: 'top', zIndex: 0, cursorPos: 'vt340'}; + private _addImageOpts: IAddImageOpts = { scrolling: true, layer: 'top', zIndex: 0, cursorPos: 'vt340' }; constructor( private readonly _storage: ImageStorage, private readonly _opts: IImageAddonOptions, diff --git a/addons/addon-image/src/kitty/KittyImageStorage.ts b/addons/addon-image/src/kitty/KittyImageStorage.ts index b55b6cf7e..1f5c09ec9 100644 --- a/addons/addon-image/src/kitty/KittyImageStorage.ts +++ b/addons/addon-image/src/kitty/KittyImageStorage.ts @@ -42,7 +42,7 @@ export class KittyImageStorage implements IDisposable { this._images.delete(kittyId); } }; - private _addImageOpts: IAddImageOpts = {scrolling: true, layer: 'top', zIndex: 0, cursorPos: 'iip'}; + private _addImageOpts: IAddImageOpts = { scrolling: true, layer: 'top', zIndex: 0, cursorPos: 'iip' }; constructor( private readonly _storage: ImageStorage From dc1ae4514b0c8986fb4443c6fa6e9a2b744e1f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sat, 9 May 2026 17:42:49 +0200 Subject: [PATCH 012/153] package upgrades & linter fixes --- addons/addon-image/src/IIPHandler.ts | 2 +- addons/addon-image/src/ImageRenderer.ts | 2 +- addons/addon-image/src/ImageStorage.ts | 2 +- addons/addon-image/test/KittyGraphics.test.ts | 18 +- addons/addon-ligatures/package.json | 13 +- .../src/fontLigatures/index.ts | 6 +- .../src/fontLigatures/mergeRange.ts | 1 - addons/addon-search/src/SearchEngine.ts | 6 +- .../addon-serialize/benchmark/tsconfig.json | 4 +- .../benchmark/tsconfig.json | 4 +- addons/addon-web-fonts/test/tsconfig.json | 1 - addons/addon-web-links/src/WebLinkProvider.ts | 4 +- addons/addon-web-links/test/tsconfig.json | 1 - addons/addon-webgl/src/WebglRenderer.ts | 4 +- addons/addon-webgl/src/tsconfig.json | 1 - demo/client/tsconfig.json | 1 + demo/server/server.ts | 2 +- eslint.config.mjs | 2 +- eslint.config.typings.mjs | 2 +- package-lock.json | 5001 +++++++++-------- package.json | 55 +- src/browser/CoreBrowserTerminal.ts | 2 +- src/browser/Terminal2.test.ts | 2 +- .../renderer/dom/DomRendererRowFactory.ts | 5 +- src/browser/renderer/dom/WidthCache.ts | 2 +- src/browser/scrollable/touch.ts | 2 +- .../services/CharacterJoinerService.test.ts | 2 +- src/browser/services/SelectionService.ts | 8 +- src/common/TaskQueue.ts | 4 +- src/common/data/Charsets.ts | 9 +- src/common/input/TextDecoder.ts | 2 +- src/common/parser/EscapeSequenceParser.ts | 4 +- src/common/services/DecorationService.ts | 8 +- src/tsconfig-library-base.json | 3 +- test/benchmark/tsconfig.json | 4 +- test/playwright/tsconfig.json | 4 +- 36 files changed, 2804 insertions(+), 2389 deletions(-) diff --git a/addons/addon-image/src/IIPHandler.ts b/addons/addon-image/src/IIPHandler.ts index 6f94ef8ed..e35687c10 100644 --- a/addons/addon-image/src/IIPHandler.ts +++ b/addons/addon-image/src/IIPHandler.ts @@ -97,7 +97,7 @@ export class IIPHandler implements IOscHandler, IResetHandler { let h = 0; // early exit condition chain - let cond: number | boolean = true; + let cond: number | boolean; if (cond = success) { if (cond = !this._dec.end()) { if (cond = this._dec.data8.length === this._header.size) { diff --git a/addons/addon-image/src/ImageRenderer.ts b/addons/addon-image/src/ImageRenderer.ts index b36cdd004..5854efaec 100644 --- a/addons/addon-image/src/ImageRenderer.ts +++ b/addons/addon-image/src/ImageRenderer.ts @@ -146,7 +146,7 @@ export class ImageRenderer extends Disposable implements IDisposable { public clearLines(start: number, end: number, layer?: ImageLayer): void { const y = start * (this.dimensions?.css.cell.height || 0); const w = this.dimensions?.css.canvas.width || 0; - const h = (++end - start) * (this.dimensions?.css.cell.height || 0); + const h = (end + 1 - start) * (this.dimensions?.css.cell.height || 0); if (!layer || layer === 'top') { this._layers.get('top')?.clearRect(0, y, w, h); } diff --git a/addons/addon-image/src/ImageStorage.ts b/addons/addon-image/src/ImageStorage.ts index 8c89a16b0..a3f714571 100644 --- a/addons/addon-image/src/ImageStorage.ts +++ b/addons/addon-image/src/ImageStorage.ts @@ -455,7 +455,7 @@ export class ImageStorage implements IDisposable { if (!nextE || nextE.imageId !== imageId || nextE.tileId !== startTile + count) { break; } - e = nextE; + // e = nextE; count++; } col--; diff --git a/addons/addon-image/test/KittyGraphics.test.ts b/addons/addon-image/test/KittyGraphics.test.ts index ac5acae77..38248a9cf 100644 --- a/addons/addon-image/test/KittyGraphics.test.ts +++ b/addons/addon-image/test/KittyGraphics.test.ts @@ -539,7 +539,6 @@ test.describe('Kitty Graphics Protocol', () => { test.describe('Query support (a=q)', () => { test('responds with OK for capability query without payload', async () => { - let response = ''; await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); @@ -548,12 +547,11 @@ test.describe('Kitty Graphics Protocol', () => { await ctx.proxy.write('\x1b_Gi=31,a=q;\x1b\\'); await timeout(100); - response = await ctx.page.evaluate('window.kittyResponse'); + const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=31;OK\x1b\\'); }); test('responds with OK for valid PNG query', async () => { - let response = ''; await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); @@ -562,7 +560,7 @@ test.describe('Kitty Graphics Protocol', () => { await ctx.proxy.write(`\x1b_Gi=42,a=q,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); - response = await ctx.page.evaluate('window.kittyResponse'); + const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=42;OK\x1b\\'); }); @@ -578,7 +576,6 @@ test.describe('Kitty Graphics Protocol', () => { }); test('responds with error for invalid base64', async () => { - let response = ''; await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); @@ -587,12 +584,11 @@ test.describe('Kitty Graphics Protocol', () => { await ctx.proxy.write('\x1b_Gi=60,a=q,f=100;!!!invalid!!!\x1b\\'); await timeout(100); - response = await ctx.page.evaluate('window.kittyResponse'); + const response: string = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response.startsWith('\x1b_Gi=60;EINVAL:'), true); }); test('responds with error for RGB data without dimensions', async () => { - let response = ''; await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); @@ -601,7 +597,7 @@ test.describe('Kitty Graphics Protocol', () => { await ctx.proxy.write('\x1b_Gi=70,a=q,f=24;AAAA\x1b\\'); await timeout(100); - response = await ctx.page.evaluate('window.kittyResponse'); + const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=70;EINVAL:width and height required for raw pixel data\x1b\\'); }); @@ -630,7 +626,6 @@ test.describe('Kitty Graphics Protocol', () => { }); test('responds with EINVAL when both i and I keys are specified', async () => { - let response = ''; await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); @@ -640,12 +635,11 @@ test.describe('Kitty Graphics Protocol', () => { await ctx.proxy.write(`\x1b_Gi=100,I=200,a=q,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); - response = await ctx.page.evaluate('window.kittyResponse'); + const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=100;EINVAL:cannot specify both i and I keys\x1b\\'); }); test('responds with EINVAL for i+I conflict even without payload', async () => { - let response = ''; await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); @@ -655,7 +649,7 @@ test.describe('Kitty Graphics Protocol', () => { await ctx.proxy.write('\x1b_Gi=101,I=201,a=d\x1b\\'); await timeout(100); - response = await ctx.page.evaluate('window.kittyResponse'); + const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=101;EINVAL:cannot specify both i and I keys\x1b\\'); }); }); diff --git a/addons/addon-ligatures/package.json b/addons/addon-ligatures/package.json index fc7a433de..be4876fba 100644 --- a/addons/addon-ligatures/package.json +++ b/addons/addon-ligatures/package.json @@ -31,15 +31,12 @@ ], "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0", - "opentype.js": "^0.8.0" + "lru-cache": "^11.3.6", + "opentype.js": "^2.0.0" }, "devDependencies": { - "@types/lru-cache": "^5.1.0", - "@types/opentype.js": "^0.7.0", - "axios": "^1.15.0", - "font-finder": "^1.1.0", - "mkdirp": "0.5.5", - "yauzl": "^3.2.1" + "@types/lru-cache": "^7.10.9", + "@types/opentype.js": "^1.3.9", + "font-finder": "^1.1.0" } } diff --git a/addons/addon-ligatures/src/fontLigatures/index.ts b/addons/addon-ligatures/src/fontLigatures/index.ts index d65e73bc4..d3a515c71 100644 --- a/addons/addon-ligatures/src/fontLigatures/index.ts +++ b/addons/addon-ligatures/src/fontLigatures/index.ts @@ -1,5 +1,5 @@ import * as opentype from 'opentype.js'; -import LRUCache = require('lru-cache'); +import { LRUCache } from 'lru-cache'; import { IFont, ILigatureData, IFlattenedLookupTree, ILookupTree, IOptions } from './types'; import mergeTrees from './merge'; @@ -23,8 +23,8 @@ class FontImpl implements IFont { if (options.cacheSize > 0) { this._cache = new LRUCache({ - max: options.cacheSize, - length: ((val: ILigatureData | [number, number][], key: string) => key.length) as any + maxSize: options.cacheSize, + sizeCalculation: ((val: ILigatureData | [number, number][], key: string) => key.length) as any }); } diff --git a/addons/addon-ligatures/src/fontLigatures/mergeRange.ts b/addons/addon-ligatures/src/fontLigatures/mergeRange.ts index ec5305083..5fcdf55cb 100644 --- a/addons/addon-ligatures/src/fontLigatures/mergeRange.ts +++ b/addons/addon-ligatures/src/fontLigatures/mergeRange.ts @@ -44,7 +44,6 @@ export default function mergeRange(ranges: [number, number][], newRangeStart: nu // current range ranges[i - 1][1] = Math.max(newRangeEnd, range[1]); ranges.splice(i, 1); - inRange = false; return ranges; } // Case 7: New range extends from previous range past the diff --git a/addons/addon-search/src/SearchEngine.ts b/addons/addon-search/src/SearchEngine.ts index b9991974d..7e89c03a1 100644 --- a/addons/addon-search/src/SearchEngine.ts +++ b/addons/addon-search/src/SearchEngine.ts @@ -174,7 +174,7 @@ export class SearchEngine { this._terminal.clearSelection(); let startRow = this._terminal.buffer.active.baseY + this._terminal.rows - 1; - let startCol = this._terminal.cols; + const startCol = this._terminal.cols; const isReverseSearch = true; this._lineCache.initLinesCache(); @@ -186,14 +186,14 @@ export class SearchEngine { let result: ISearchResult | undefined; if (prevSelectedPos) { searchPosition.startRow = startRow = prevSelectedPos.start.y; - searchPosition.startCol = startCol = prevSelectedPos.start.x; + searchPosition.startCol = prevSelectedPos.start.x; if (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; + searchPosition.startCol = prevSelectedPos.end.x; } } } diff --git a/addons/addon-serialize/benchmark/tsconfig.json b/addons/addon-serialize/benchmark/tsconfig.json index afc3944a2..ad7ee6d04 100644 --- a/addons/addon-serialize/benchmark/tsconfig.json +++ b/addons/addon-serialize/benchmark/tsconfig.json @@ -7,10 +7,10 @@ "rootDir": "..", "outDir": "../out-benchmark", "types": ["../../../node_modules/@types/node"], - "moduleResolution": "node", + "moduleResolution": "nodenext", "strict": false, "target": "es2021", - "module": "commonjs", + "module": "nodenext", "paths": { "common/*": ["../../../src/common/*"], "browser/*": ["../../../src/browser/*"], diff --git a/addons/addon-unicode-graphemes/benchmark/tsconfig.json b/addons/addon-unicode-graphemes/benchmark/tsconfig.json index 47e6b53a4..1781ca318 100644 --- a/addons/addon-unicode-graphemes/benchmark/tsconfig.json +++ b/addons/addon-unicode-graphemes/benchmark/tsconfig.json @@ -4,10 +4,10 @@ "rootDir": "..", "outDir": "../out-benchmark", "types": ["../../../node_modules/@types/node"], - "moduleResolution": "node", + "moduleResolution": "nodenext", "strict": false, "target": "es2015", - "module": "commonjs", + "module": "nodenext", "paths": { "common/*": ["../../../src/common/*"], "browser/*": ["../../../src/browser/*"], diff --git a/addons/addon-web-fonts/test/tsconfig.json b/addons/addon-web-fonts/test/tsconfig.json index 4151488a8..59664e32b 100644 --- a/addons/addon-web-fonts/test/tsconfig.json +++ b/addons/addon-web-fonts/test/tsconfig.json @@ -5,7 +5,6 @@ "lib": [ "es2021", ], - // "downlevelIteration": true, "rootDir": ".", "outDir": "../out-test", "sourceMap": true, diff --git a/addons/addon-web-links/src/WebLinkProvider.ts b/addons/addon-web-links/src/WebLinkProvider.ts index efaead5c4..0a826eb3a 100644 --- a/addons/addon-web-links/src/WebLinkProvider.ts +++ b/addons/addon-web-links/src/WebLinkProvider.ts @@ -113,8 +113,8 @@ export class LinkComputer { let line: IBufferLine | undefined; let topIdx = lineIndex; let bottomIdx = lineIndex; - let length = 0; - let content = ''; + let length; + let content; const lines: string[] = []; if ((line = terminal.buffer.active.getLine(lineIndex))) { diff --git a/addons/addon-web-links/test/tsconfig.json b/addons/addon-web-links/test/tsconfig.json index 4151488a8..59664e32b 100644 --- a/addons/addon-web-links/test/tsconfig.json +++ b/addons/addon-web-links/test/tsconfig.json @@ -5,7 +5,6 @@ "lib": [ "es2021", ], - // "downlevelIteration": true, "rootDir": ".", "outDir": "../out-test", "sourceMap": true, diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 91867ea26..5bb165de7 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -407,8 +407,8 @@ export class WebglRenderer extends Disposable implements IRenderer { let line: IBufferLine; let joinedRanges: [number, number][]; let isJoined: boolean; - let skipJoinedCheckUntilX: number = 0; - let isValidJoinRange: boolean = true; + let skipJoinedCheckUntilX: number; + let isValidJoinRange: boolean; let lastCharX: number; let range: [number, number]; let isCursorRow: boolean; diff --git a/addons/addon-webgl/src/tsconfig.json b/addons/addon-webgl/src/tsconfig.json index aeb42a1bf..7035daa86 100644 --- a/addons/addon-webgl/src/tsconfig.json +++ b/addons/addon-webgl/src/tsconfig.json @@ -25,7 +25,6 @@ ] }, "strict": true, - "downlevelIteration": true, "experimentalDecorators": true, "types": [ "../../../node_modules/@types/mocha" diff --git a/demo/client/tsconfig.json b/demo/client/tsconfig.json index 244f19c6e..8490ceb3f 100644 --- a/demo/client/tsconfig.json +++ b/demo/client/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "module": "commonjs", "target": "es2021", + "types": ["node"], "outDir": "../out-demo/client", "rootDir": ".", "sourceMap": true, diff --git a/demo/server/server.ts b/demo/server/server.ts index ef98eb84c..3fe5e1b80 100644 --- a/demo/server/server.ts +++ b/demo/server/server.ts @@ -118,7 +118,7 @@ function startServer(): void { }); appWs.ws('/terminals/:pid', function (ws, req) { - const term = terminals[parseInt(req.params.pid)]; + const term = terminals[parseInt(Array.isArray(req.params.pid) ? req.params.pid[0] : req.params.pid)]; console.log('Connected to terminal ' + term.pid); temporaryDisposable[term.pid].dispose(); delete temporaryDisposable[term.pid]; diff --git a/eslint.config.mjs b/eslint.config.mjs index b73eece7a..2b502098f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -37,7 +37,7 @@ export default tseslint.config( '@stylistic/comma-spacing': ['warn', { before: false, after: true }], '@stylistic/indent': ['warn', 2], '@stylistic/semi': ['warn', 'always'], - '@stylistic/quotes': ['warn', 'single', { allowTemplateLiterals: true }], + '@stylistic/quotes': ['warn', 'single', { allowTemplateLiterals: 'always' }], '@stylistic/member-delimiter-style': ['warn', { multiline: { delimiter: 'semi', requireLast: true }, singleline: { delimiter: 'comma', requireLast: false } diff --git a/eslint.config.typings.mjs b/eslint.config.typings.mjs index 773a91593..a772f7c60 100644 --- a/eslint.config.typings.mjs +++ b/eslint.config.typings.mjs @@ -17,7 +17,7 @@ export default tseslint.config( rules: { '@stylistic/indent': ['warn', 2], '@stylistic/semi': ['warn', 'always'], - '@stylistic/quotes': ['warn', 'single', { allowTemplateLiterals: true }], + '@stylistic/quotes': ['warn', 'single', { allowTemplateLiterals: 'always' }], '@stylistic/member-delimiter-style': ['warn', { multiline: { delimiter: 'semi', requireLast: true }, singleline: { delimiter: 'comma', requireLast: false } diff --git a/package-lock.json b/package-lock.json index ebe3acbef..cccdca03f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,48 +12,49 @@ "addons/*" ], "devDependencies": { + "@eslint/js": "^10.0.1", "@lunapaint/png-codec": "^0.2.0", - "@playwright/test": "^1.57.0", - "@stylistic/eslint-plugin": "^4.4.1", - "@types/chai": "^4.2.22", - "@types/debug": "^4.1.7", + "@playwright/test": "^1.59.1", + "@stylistic/eslint-plugin": "^5.10.0", + "@types/chai": "^5.2.3", + "@types/debug": "^4.1.13", "@types/deep-equal": "^1.0.1", - "@types/express": "4", + "@types/express": "^5.0.6", "@types/express-ws": "^3.0.1", - "@types/jsdom": "^27.0.0", - "@types/mocha": "^9.0.0", + "@types/jsdom": "^28.0.1", + "@types/mocha": "^10.0.10", "@types/node": "^22.19.3", - "@types/trusted-types": "^1.0.6", + "@types/trusted-types": "^2.0.7", "@types/utf8": "^3.0.0", "@types/webpack": "^5.28.0", "@types/ws": "^8.2.0", - "@typescript-eslint/eslint-plugin": "^8.50.1", - "@typescript-eslint/parser": "^8.50.1", - "@typescript/native-preview": "^7.0.0-dev.20260213.1", - "chai": "^4.3.4", + "@typescript-eslint/eslint-plugin": "^8.59.2", + "@typescript-eslint/parser": "^8.59.2", + "@typescript/native-preview": "^7.0.0-dev.20260509.2", + "chai": "^6.2.2", "concurrently": "^9.1.2", - "cross-env": "^7.0.3", + "cross-env": "^10.1.0", "deep-equal": "^2.0.5", - "esbuild": "~0.25.2", - "eslint": "^9.39.2", - "eslint-plugin-jsdoc": "^50.8.0", - "express": "^4.19.2", + "esbuild": "^0.28.0", + "eslint": "^10.3.0", + "eslint-plugin-jsdoc": "^62.9.0", + "express": "^5.2.1", "express-ws": "^5.0.2", - "jsdom": "^27.3.0", - "mocha": "^10.1.0", + "jsdom": "^29.1.1", + "mocha": "^11.7.5", "mustache": "^4.2.0", "node-pty": "^1.2.0-beta.9", - "nyc": "^17.1.0", - "source-map-loader": "^3.0.0", + "nyc": "^18.0.0", + "source-map-loader": "^5.0.0", "source-map-support": "^0.5.20", - "ts-loader": "^9.3.1", + "ts-loader": "^9.5.7", "typescript": "^5.9.3", - "typescript-eslint": "^8.50.1", + "typescript-eslint": "^8.59.2", "utf8": "^3.0.0", - "webpack": "^5.61.0", - "webpack-cli": "^4.9.1", - "ws": "^8.2.3", - "xterm-benchmark": "^0.3.1" + "webpack": "^5.106.2", + "webpack-cli": "^7.0.2", + "ws": "^8.20.0", + "xterm-benchmark": "^0.3.2" } }, "addons/addon-attach": { @@ -88,39 +89,18 @@ "version": "0.10.0", "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0", - "opentype.js": "^0.8.0" + "lru-cache": "^11.3.6", + "opentype.js": "^2.0.0" }, "devDependencies": { - "@types/lru-cache": "^5.1.0", - "@types/opentype.js": "^0.7.0", - "axios": "^1.15.0", - "font-finder": "^1.1.0", - "mkdirp": "0.5.5", - "yauzl": "^3.2.1" + "@types/lru-cache": "^7.10.9", + "@types/opentype.js": "^1.3.9", + "font-finder": "^1.1.0" }, "engines": { "node": ">8.0.0" } }, - "addons/addon-ligatures/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "addons/addon-ligatures/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, "addons/addon-progress": { "name": "@xterm/addon-progress", "version": "0.2.0", @@ -161,69 +141,65 @@ "version": "0.19.0", "license": "MIT" }, - "node_modules/@acemir/cssom": { - "version": "0.9.30", - "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.30.tgz", - "integrity": "sha512-9CnlMCI0LmCIq0olalQqdWrJHPzm0/tw3gzOA9zJSgvFX7Xau3D24mAGa4BtwxwY69nsuJW6kQqqCzf/mEcQgg==", - "dev": true - }, "node_modules/@asamuzakjp/css-color": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.1.tgz", - "integrity": "sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ==", + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", + "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", "dev": true, + "license": "MIT", "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-color-parser": "^3.1.0", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "lru-cache": "^11.2.4" - } - }, - "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { - "version": "11.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", - "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", - "dev": true, + "@asamuzakjp/generational-cache": "^1.0.1", + "@csstools/css-calc": "^3.2.0", + "@csstools/css-color-parser": "^4.1.0", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + }, "engines": { - "node": "20 || >=22" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, "node_modules/@asamuzakjp/dom-selector": { - "version": "6.7.6", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.7.6.tgz", - "integrity": "sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", + "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", "dev": true, + "license": "MIT", "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", - "css-tree": "^3.1.0", - "is-potential-custom-element-name": "^1.0.1", - "lru-cache": "^11.2.4" + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, - "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { - "version": "11.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", - "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "node_modules/@asamuzakjp/generational-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", + "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", "dev": true, + "license": "MIT", "engines": { - "node": "20 || >=22" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, "node_modules/@asamuzakjp/nwsapi": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -232,30 +208,31 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", - "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", - "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -275,25 +252,28 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -303,12 +283,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", + "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -318,11 +299,22 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -332,32 +324,35 @@ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -371,6 +366,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -380,6 +376,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -389,30 +386,33 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.28.5" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -422,40 +422,43 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", - "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", - "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", "debug": "^4.3.1" }, "engines": { @@ -463,10 +466,11 @@ } }, "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" @@ -475,10 +479,23 @@ "node": ">=6.9.0" } }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, "node_modules/@csstools/color-helpers": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", - "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", + "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", "dev": true, "funding": [ { @@ -490,14 +507,15 @@ "url": "https://opencollective.com/csstools" } ], + "license": "MIT-0", "engines": { - "node": ">=18" + "node": ">=20.19.0" } }, "node_modules/@csstools/css-calc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", - "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.0.tgz", + "integrity": "sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==", "dev": true, "funding": [ { @@ -509,18 +527,19 @@ "url": "https://opencollective.com/csstools" } ], + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=20.19.0" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" } }, "node_modules/@csstools/css-color-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", - "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.0.tgz", + "integrity": "sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==", "dev": true, "funding": [ { @@ -532,22 +551,23 @@ "url": "https://opencollective.com/csstools" } ], + "license": "MIT", "dependencies": { - "@csstools/color-helpers": "^5.1.0", - "@csstools/css-calc": "^2.1.4" + "@csstools/color-helpers": "^6.0.2", + "@csstools/css-calc": "^3.2.0" }, "engines": { - "node": ">=18" + "node": ">=20.19.0" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" } }, "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", - "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", "dev": true, "funding": [ { @@ -559,18 +579,18 @@ "url": "https://opencollective.com/csstools" } ], - "peer": true, + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=20.19.0" }, "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.4" + "@csstools/css-tokenizer": "^4.0.0" } }, "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.0.22", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.22.tgz", - "integrity": "sha512-qBcx6zYlhleiFfdtzkRgwNC7VVoAwfK76Vmsw5t+PbvtdknO9StgRk7ROvq9so1iqbdW4uLIDAsXRsTfUrIoOw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.3.tgz", + "integrity": "sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==", "dev": true, "funding": [ { @@ -582,14 +602,20 @@ "url": "https://opencollective.com/csstools" } ], - "engines": { - "node": ">=18" + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } } }, "node_modules/@csstools/css-tokenizer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", - "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", "dev": true, "funding": [ { @@ -601,44 +627,64 @@ "url": "https://opencollective.com/csstools" } ], - "peer": true, + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=20.19.0" } }, "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-1.1.0.tgz", + "integrity": "sha512-Xc3VhU02wqZ1HvHRJUwL09HkZSTvidqY5Ya0NXBSYOxAp+Ln9dcJr9fySI+CkONzP3PekQo9WdzCv0PGER/mOA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">=14.17.0" } }, - "node_modules/@es-joy/jsdoccomment": { - "version": "0.50.2", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.50.2.tgz", - "integrity": "sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==", + "node_modules/@epic-web/invariant": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", + "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==", "dev": true, + "license": "MIT" + }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.86.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.86.0.tgz", + "integrity": "sha512-ukZmRQ81WiTpDWO6D/cTBM7XbrNtutHKvAVnZN/8pldAwLoJArGOvkNyxPTBGsPjsoaQBJxlH+tE2TNA/92Qgw==", + "dev": true, + "license": "MIT", "dependencies": { - "@types/estree": "^1.0.6", - "@typescript-eslint/types": "^8.11.0", - "comment-parser": "1.4.1", - "esquery": "^1.6.0", - "jsdoc-type-pratt-parser": "~4.1.0" + "@types/estree": "^1.0.8", + "@typescript-eslint/types": "^8.58.0", + "comment-parser": "1.4.6", + "esquery": "^1.7.0", + "jsdoc-type-pratt-parser": "~7.2.0" }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@es-joy/resolve.exports": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@es-joy/resolve.exports/-/resolve.exports-1.2.0.tgz", + "integrity": "sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", + "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" @@ -648,13 +694,14 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", + "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -664,13 +711,14 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", + "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -680,13 +728,14 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", + "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -696,13 +745,14 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", + "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -712,13 +762,14 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", + "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -728,13 +779,14 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", + "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -744,13 +796,14 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", + "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -760,13 +813,14 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", + "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -776,13 +830,14 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", + "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -792,13 +847,14 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", + "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -808,13 +864,14 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", + "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -824,13 +881,14 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", + "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", "cpu": [ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -840,13 +898,14 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", + "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -856,13 +915,14 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", + "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -872,13 +932,14 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", + "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -888,13 +949,14 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", + "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -904,13 +966,14 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", + "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -920,13 +983,14 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", + "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -936,13 +1000,14 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", + "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -952,13 +1017,14 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", + "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -968,13 +1034,14 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", + "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openharmony" @@ -984,13 +1051,14 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", + "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" @@ -1000,13 +1068,14 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", + "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -1016,13 +1085,14 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", + "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -1032,13 +1102,14 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", + "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -1048,10 +1119,11 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, + "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -1070,6 +1142,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -1082,187 +1155,159 @@ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.7", + "@eslint/object-schema": "^3.0.5", "debug": "^4.3.1", - "minimatch": "^3.1.2" + "minimatch": "^10.2.4" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", + "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0" + "@eslint/core": "^1.2.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", - "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/js": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", "dev": true, + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0", + "@eslint/core": "^1.2.1", "levn": "^0.4.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", + "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } } }, "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -1276,6 +1321,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -1284,11 +1330,30 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, + "license": "ISC", "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", @@ -1305,6 +1370,7 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, + "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } @@ -1314,6 +1380,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -1327,6 +1394,7 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -1340,6 +1408,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -1352,6 +1421,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -1367,6 +1437,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -1374,20 +1445,12 @@ "node": ">=8" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1397,6 +1460,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" @@ -1407,6 +1471,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -1417,6 +1482,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -1426,6 +1492,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -1435,13 +1502,15 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1452,18 +1521,30 @@ "resolved": "https://registry.npmjs.org/@lunapaint/png-codec/-/png-codec-0.2.0.tgz", "integrity": "sha512-S2Fk8+I27j8ZL585PlEK9hhljZcp6j+JWB5ZHAeePdufJMYHxXD2zlatLRaiy5riXRFqkCi/gong7yP9kSsEZg==", "dev": true, + "license": "MIT", "dependencies": { "pako": "^2.0.4" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@playwright/test": { - "version": "1.57.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.57.0.tgz", - "integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.57.0" + "playwright": "1.59.1" }, "bin": { "playwright": "cli.js" @@ -1472,82 +1553,102 @@ "node": ">=18" } }, - "node_modules/@stylistic/eslint-plugin": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-4.4.1.tgz", - "integrity": "sha512-CEigAk7eOLyHvdgmpZsKFwtiqS2wFwI1fn4j09IU9GmD4euFM4jEBAViWeCqaNLlbX2k2+A/Fq9cje4HQBXuJQ==", + "node_modules/@sindresorhus/base62": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/base62/-/base62-1.0.0.tgz", + "integrity": "sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@stylistic/eslint-plugin": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.10.0.tgz", + "integrity": "sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.32.1", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/types": "^8.56.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "estraverse": "^5.3.0", - "picomatch": "^4.0.2" + "picomatch": "^4.0.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "peerDependencies": { - "eslint": ">=9.0.0" + "eslint": "^9.0.0 || ^10.0.0" } }, - "node_modules/@types/app-root-path": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@types/app-root-path/-/app-root-path-1.2.8.tgz", - "integrity": "sha512-l12miuN6JXAi3yuADZNhRKbyN7IIyaUP9hFVZ/BbHhWYpBkHLbOaX2WkQoXGJyAgMcP9iZ0S9+tz/FN40VrwWQ==", - "dev": true - }, "node_modules/@types/body-parser": { "version": "1.19.6", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "dev": true, + "license": "MIT", "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "node_modules/@types/chai": { - "version": "4.3.20", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz", - "integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==", - "dev": true - }, - "node_modules/@types/cli-table": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@types/cli-table/-/cli-table-0.3.4.tgz", - "integrity": "sha512-GsALrTL69mlwbAw/MHF1IPTadSLZQnsxe7a80G8l4inN/iEXCOcVeT/S7aRc6hbhqzL9qZ314kHPDQnQ3ev+HA==", - "dev": true + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", "dev": true, + "license": "MIT", "dependencies": { "@types/ms": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/deep-equal": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@types/deep-equal/-/deep-equal-1.0.4.tgz", "integrity": "sha512-tqdiS4otQP4KmY0PR3u6KbZ5EWvhNdUoS/jc93UuK23C220lOZ/9TvjfxdPcKvqwwDVtmtSCrnr0p/2dirAxkA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -1558,34 +1659,44 @@ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, + "license": "MIT", "dependencies": { "@types/eslint": "*", "@types/estree": "*" } }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" }, "node_modules/@types/express": { - "version": "4.17.25", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", - "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", "dev": true, + "license": "MIT", "dependencies": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "^1" + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.7", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz", - "integrity": "sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -1598,6 +1709,7 @@ "resolved": "https://registry.npmjs.org/@types/express-ws/-/express-ws-3.0.6.tgz", "integrity": "sha512-6ZDt+tMEQgM4RC1sMX1fIO7kHQkfUDlWfxoPddXUeeDjmc+Yt/fCzqXfp8rFahNr5eIxdomrWphLEWDkB2q3UQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/express": "*", "@types/express-serve-static-core": "*", @@ -1608,123 +1720,109 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/jsdom": { - "version": "27.0.0", - "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-27.0.0.tgz", - "integrity": "sha512-NZyFl/PViwKzdEkQg96gtnB8wm+1ljhdDay9ahn4hgb+SfVtPCbm3TlmDUFXTA+MGN3CijicnMhG18SI5H3rFw==", + "version": "28.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-28.0.1.tgz", + "integrity": "sha512-GJq2QE4TAZ5ajSoCasn5DOFm8u1mI3tIFvM5tIq3W5U/RTB6gsHwc6Yhpl91X9VSDOUVblgXmG+2+sSvFQrdlw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "@types/tough-cookie": "*", - "parse5": "^7.0.0" + "parse5": "^7.0.0", + "undici-types": "^7.21.0" } }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "node_modules/@types/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", "dev": true, "license": "MIT" }, - "node_modules/@types/mathjs": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@types/mathjs/-/mathjs-6.0.12.tgz", - "integrity": "sha512-bpKs8CDJ0aOiiJguywryE/U6Wre/uftJ89xhp4aCgF4oRb3Yug2VyZ87958gmSeq4WMsvWPMs2Q5TtFv+dJtaA==", + "node_modules/@types/lru-cache": { + "version": "7.10.9", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-7.10.9.tgz", + "integrity": "sha512-wrwgkdJ0xr8AbzKhVaRI8SXZN9saapPwwLoydBEr4HqMZET1LUTi1gdoaj82XmRJ9atqN7MtB0aja29iiK+7ag==", "dev": true, + "license": "MIT", "dependencies": { - "decimal.js": "^10.0.0" + "lru-cache": "*" } }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true - }, "node_modules/@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", - "dev": true + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "22.19.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz", - "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", + "version": "22.19.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.18.tgz", + "integrity": "sha512-9v00a+dn2yWVsYDEunWC4g/TcRKVq3r8N5FuZp7u0SGrPvdN9c2yXI9bBuf5Fl0hNCb+QTIePTn5pJs2pwBOQQ==", "dev": true, + "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, - "node_modules/@types/opentype.js": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@types/opentype.js/-/opentype.js-0.7.2.tgz", - "integrity": "sha512-Riz6WyBUBEFs7YqSsJya3SbDHJZ6BmMkY7bzNoue6rtwj+RNilLc+mgOX/eJ0Y0asq16FSU6DatBeOg8ZMy2UQ==", + "node_modules/@types/node/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, - "node_modules/@types/puppeteer": { - "version": "5.4.7", - "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.7.tgz", - "integrity": "sha512-JdGWZZYL0vKapXF4oQTC5hLVNfOgdPrqeZ1BiQnGk5cB7HeE91EWUiTdVSdQPobRN8rIcdffjiOgCYJ/S8QrnQ==", + "node_modules/@types/opentype.js": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/@types/opentype.js/-/opentype.js-1.3.9.tgz", + "integrity": "sha512-KOGywvDPncA4/tTWV5xKNhjpsoSSAHIx3mHOhL5l3XX+c6Xu2dQnHvGs7mRNQsQRte1EqmQ0cPQQ8Z14lkv+yw==", "dev": true, - "dependencies": { - "@types/node": "*" - } + "license": "MIT" }, "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "dev": true + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/send": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/serve-static": { - "version": "1.15.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", - "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "<1" - } - }, - "node_modules/@types/serve-static/node_modules/@types/send": { - "version": "0.17.6", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", - "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", - "dev": true, - "dependencies": { - "@types/mime": "^1", "@types/node": "*" } }, @@ -1732,25 +1830,29 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/trusted-types": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-1.0.6.tgz", - "integrity": "sha512-230RC8sFeHoT6sSUlRO6a8cAnclO06eeiq1QDfiv2FGCLWFvvERWgwIQD4FWqD9A69BN7Lzee4OXwoMVnnsWDw==", - "dev": true + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/utf8": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/utf8/-/utf8-3.0.3.tgz", "integrity": "sha512-+lqLGxWZsEe4Z6OrzBI7Ym4SMUTaMS5yOrHZ0/IL0bpIye1Qbs4PpobJL2mLDbftUXlPFZR7fu6d1yM+bHLX1w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/webpack": { "version": "5.28.5", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.5.tgz", "integrity": "sha512-wR87cgvxj3p6D0Crt1r5avwqffqPXUkNlnQ1mjU93G7gCuFjufZR4I6j8cz5g1F1tTYpfOOFvly+cmIQwL9wvw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "tapable": "^2.2.0", @@ -1762,24 +1864,26 @@ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.50.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.50.1.tgz", - "integrity": "sha512-PKhLGDq3JAg0Jk/aK890knnqduuI/Qj+udH7wCf0217IGi4gt+acgCyPVe79qoT+qKUvHMDQkwJeKW9fwl8Cyw==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz", + "integrity": "sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.50.1", - "@typescript-eslint/type-utils": "8.50.1", - "@typescript-eslint/utils": "8.50.1", - "@typescript-eslint/visitor-keys": "8.50.1", - "ignore": "^7.0.0", + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/type-utils": "8.59.2", + "@typescript-eslint/utils": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1789,23 +1893,23 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.50.1", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "@typescript-eslint/parser": "^8.59.2", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.50.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.50.1.tgz", - "integrity": "sha512-hM5faZwg7aVNa819m/5r7D0h0c9yC4DUlWAOvHAtISdFTc8xB86VmX5Xqabrama3wIPJ/q9RbGS1worb6JfnMg==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.2.tgz", + "integrity": "sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.50.1", - "@typescript-eslint/types": "8.50.1", - "@typescript-eslint/typescript-estree": "8.50.1", - "@typescript-eslint/visitor-keys": "8.50.1", - "debug": "^4.3.4" + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1815,19 +1919,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.50.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.50.1.tgz", - "integrity": "sha512-E1ur1MCVf+YiP89+o4Les/oBAVzmSbeRB0MQLfSlYtbWU17HPxZ6Bhs5iYmKZRALvEuBoXIZMOIRRc/P++Ortg==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.2.tgz", + "integrity": "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.50.1", - "@typescript-eslint/types": "^8.50.1", - "debug": "^4.3.4" + "@typescript-eslint/tsconfig-utils": "^8.59.2", + "@typescript-eslint/types": "^8.59.2", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1837,17 +1942,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.50.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.50.1.tgz", - "integrity": "sha512-mfRx06Myt3T4vuoHaKi8ZWNTPdzKPNBhiblze5N50//TSHOAQQevl/aolqA/BcqqbJ88GUnLqjjcBc8EWdBcVw==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz", + "integrity": "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.50.1", - "@typescript-eslint/visitor-keys": "8.50.1" + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1858,10 +1964,11 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.50.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.50.1.tgz", - "integrity": "sha512-ooHmotT/lCWLXi55G4mvaUF60aJa012QzvLK0Y+Mp4WdSt17QhMhWOaBWeGTFVkb2gDgBe19Cxy1elPXylslDw==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz", + "integrity": "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -1870,20 +1977,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.50.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.50.1.tgz", - "integrity": "sha512-7J3bf022QZE42tYMO6SL+6lTPKFk/WphhRPe9Tw/el+cEwzLz1Jjz2PX3GtGQVxooLDKeMVmMt7fWpYRdG5Etg==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.2.tgz", + "integrity": "sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.50.1", - "@typescript-eslint/typescript-estree": "8.50.1", - "@typescript-eslint/utils": "8.50.1", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/utils": "8.59.2", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1893,15 +2001,16 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.50.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.50.1.tgz", - "integrity": "sha512-v5lFIS2feTkNyMhd7AucE/9j/4V9v5iIbpVRncjk/K0sQ6Sb+Np9fgYS/63n6nwqahHQvbmujeBL7mp07Q9mlA==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.2.tgz", + "integrity": "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -1911,20 +2020,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.50.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.50.1.tgz", - "integrity": "sha512-woHPdW+0gj53aM+cxchymJCrh0cyS7BTIdcDxWUNsclr9VDkOSbqC13juHzxOmQ22dDkMZEpZB+3X1WpUvzgVQ==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz", + "integrity": "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.50.1", - "@typescript-eslint/tsconfig-utils": "8.50.1", - "@typescript-eslint/types": "8.50.1", - "@typescript-eslint/visitor-keys": "8.50.1", - "debug": "^4.3.4", - "minimatch": "^9.0.4", - "semver": "^7.6.0", + "@typescript-eslint/project-service": "8.59.2", + "@typescript-eslint/tsconfig-utils": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.1.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1934,19 +2044,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.50.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.50.1.tgz", - "integrity": "sha512-lCLp8H1T9T7gPbEuJSnHwnSuO9mDf8mfK/Nion5mZmiEaQD9sWf9W4dfeFqRyqRjF06/kBuTmAqcs9sewM2NbQ==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.2.tgz", + "integrity": "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.50.1", - "@typescript-eslint/types": "8.50.1", - "@typescript-eslint/typescript-estree": "8.50.1" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1956,18 +2067,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.50.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.50.1.tgz", - "integrity": "sha512-IrDKrw7pCRUR94zeuCSUWQ+w8JEf5ZX5jl/e6AHGSLi1/zIr0lgutfn/7JpfCey+urpgQEdrZVYzCaVVKiTwhQ==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz", + "integrity": "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.50.1", - "eslint-visitor-keys": "^4.2.1" + "@typescript-eslint/types": "8.59.2", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1977,29 +2089,45 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@typescript/native-preview": { - "version": "7.0.0-dev.20260213.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20260213.1.tgz", - "integrity": "sha512-TZ/0Tv954jRpn5IHQJZQF0gaNmw//ZTvkgr3nC2H8u9t4GxUhIk5vfAHVFgdl8JnNGbl7gZks37FVsEwXjdHqg==", + "version": "7.0.0-dev.20260509.2", + "resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20260509.2.tgz", + "integrity": "sha512-JAJpEX0yBaEle2zzbX5z9QAhmEfML1SyQafLwbKCdcOtnkGdk5xD8NKIVxq+nTwYjRwuV7kKnQ+fqU3gpWY0qQ==", "dev": true, "license": "Apache-2.0", "bin": { "tsgo": "bin/tsgo.js" }, + "engines": { + "node": ">=16.20.0" + }, "optionalDependencies": { - "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260213.1", - "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260213.1", - "@typescript/native-preview-linux-arm": "7.0.0-dev.20260213.1", - "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260213.1", - "@typescript/native-preview-linux-x64": "7.0.0-dev.20260213.1", - "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260213.1", - "@typescript/native-preview-win32-x64": "7.0.0-dev.20260213.1" + "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260509.2", + "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260509.2", + "@typescript/native-preview-linux-arm": "7.0.0-dev.20260509.2", + "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260509.2", + "@typescript/native-preview-linux-x64": "7.0.0-dev.20260509.2", + "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260509.2", + "@typescript/native-preview-win32-x64": "7.0.0-dev.20260509.2" } }, "node_modules/@typescript/native-preview-darwin-arm64": { - "version": "7.0.0-dev.20260213.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20260213.1.tgz", - "integrity": "sha512-nqfuHgFZ8MvaHeb1XOkAGIGc6cR91dwQdlHmlbH918n63JguKzkYjzzulpr48IVn1SVewGwjtfs1moCpaDpWcg==", + "version": "7.0.0-dev.20260509.2", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20260509.2.tgz", + "integrity": "sha512-oG9KahiCpx4q70Ood/rRJhYio4oIMHEHfX0g0LhfenlSIjIonitZWjUmUVG9N9q1ev9QWcM8pWpDrGGP0Osp3Q==", "cpu": [ "arm64" ], @@ -2008,12 +2136,15 @@ "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": ">=16.20.0" + } }, "node_modules/@typescript/native-preview-darwin-x64": { - "version": "7.0.0-dev.20260213.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20260213.1.tgz", - "integrity": "sha512-JDVDVLPIYhYZR12omTKwzKbdw+wnPEtLnkmM+nioqbISlV29fg7IBgFx0/mf1D+Gz1ztUQVHDTqw4GEgOZ9VTg==", + "version": "7.0.0-dev.20260509.2", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20260509.2.tgz", + "integrity": "sha512-xdEkp23Gu8I7PJCMmSMYtSLX76NKODWj74AoWFPi6MM59ICsjnTSqZf/HmXKSvuNZ5MGb4CMpP3c40dLjGB2PQ==", "cpu": [ "x64" ], @@ -2022,12 +2153,15 @@ "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": ">=16.20.0" + } }, "node_modules/@typescript/native-preview-linux-arm": { - "version": "7.0.0-dev.20260213.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20260213.1.tgz", - "integrity": "sha512-O6iVB8tTnLZJVYrChH6VJuCwqAa2ObWVdMtrKQw5UtmLXBhsOhZ3isttSuGzQrPfTiGN091IdW1KuRi8Ft1lnQ==", + "version": "7.0.0-dev.20260509.2", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20260509.2.tgz", + "integrity": "sha512-ar5HN/V/4HLF4FZCoVVFj+ET1Soi758hb4WhhzYQfSUXQ/bpVGUGP86JAy8EhVMoeN6qxqWet93MkLSszJOIVg==", "cpu": [ "arm" ], @@ -2036,12 +2170,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=16.20.0" + } }, "node_modules/@typescript/native-preview-linux-arm64": { - "version": "7.0.0-dev.20260213.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20260213.1.tgz", - "integrity": "sha512-d7Gwl8W4aics0KI4H0zz3TP1ngnP8L5COND0TPBZ7ifWVrxKDNSLhF2V9OuStW43RPfA4Gzik9iIv+l0Lg+eGw==", + "version": "7.0.0-dev.20260509.2", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20260509.2.tgz", + "integrity": "sha512-rd+bMRtUAFBClOAKi9p2rOu6jPmnrjZVljoFyxHw+6bIRLerEQlxP+nIH1olC3HOZPyZ6/x75WtfzTHYeqffiQ==", "cpu": [ "arm64" ], @@ -2050,12 +2187,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=16.20.0" + } }, "node_modules/@typescript/native-preview-linux-x64": { - "version": "7.0.0-dev.20260213.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20260213.1.tgz", - "integrity": "sha512-Aq55fID1fE5/8K4XvfVddgw8LKUgY3kp5IOb+QEklfyle4MuAV5gtat3MnEv9XMbiidk8iKVxYrYASlUQEQzDw==", + "version": "7.0.0-dev.20260509.2", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20260509.2.tgz", + "integrity": "sha512-lB26mGzdolYIZiOdBII8roVJCxCUR8zkYszvvHyjB1IPs7d5fmOhT6OzI1zYPYujiSRJi4HVYM1iXTcIfp7KDg==", "cpu": [ "x64" ], @@ -2064,12 +2204,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=16.20.0" + } }, "node_modules/@typescript/native-preview-win32-arm64": { - "version": "7.0.0-dev.20260213.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20260213.1.tgz", - "integrity": "sha512-VlyULgXApmGMSqAANjskGx0AcjHchf3I8rhZ47AxF/XKAkZPe/5ewfleJ97iln7dg1UL3lq2cJY2CXxU+JPcFQ==", + "version": "7.0.0-dev.20260509.2", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20260509.2.tgz", + "integrity": "sha512-gH3UmtyxHiRNEP0LgQXCVlB5+ZN/U+/Z7jM/zULQtTOxIIFK3Y4b8gbGLvP7uW3u2cqYOg2hc2nuN8OdsCmOig==", "cpu": [ "arm64" ], @@ -2078,12 +2221,15 @@ "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": ">=16.20.0" + } }, "node_modules/@typescript/native-preview-win32-x64": { - "version": "7.0.0-dev.20260213.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20260213.1.tgz", - "integrity": "sha512-lEtWvZDa2oCgqJ2gbP6BRLUeTyHLyrt5BfJmVlvmVSTZzdoBpFROJdp9yprsa2Zry2o5GK8HFAxd3RlvxkOU5Q==", + "version": "7.0.0-dev.20260509.2", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20260509.2.tgz", + "integrity": "sha512-kZV0Vh64hp10saOghPlFZE1qahonqvRgU3iubt8pUY4XLe8IQIofwWCN5vzNNeULE4W4mRtAJbHuvP/muOFomw==", "cpu": [ "x64" ], @@ -2092,13 +2238,17 @@ "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": ">=16.20.0" + } }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" @@ -2108,25 +2258,29 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", "@webassemblyjs/helper-api-error": "1.13.2", @@ -2137,13 +2291,15 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -2156,6 +2312,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, + "license": "MIT", "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -2165,6 +2322,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@xtuc/long": "4.2.2" } @@ -2173,13 +2331,15 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -2196,6 +2356,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", @@ -2209,6 +2370,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -2221,6 +2383,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-api-error": "1.13.2", @@ -2235,47 +2398,12 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, - "node_modules/@webpack-cli/configtest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", - "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", - "dev": true, - "peerDependencies": { - "webpack": "4.x.x || 5.x.x", - "webpack-cli": "4.x.x" - } - }, - "node_modules/@webpack-cli/info": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", - "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", - "dev": true, - "dependencies": { - "envinfo": "^7.7.3" - }, - "peerDependencies": { - "webpack-cli": "4.x.x" - } - }, - "node_modules/@webpack-cli/serve": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", - "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", - "dev": true, - "peerDependencies": { - "webpack-cli": "4.x.x" - }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } - }, "node_modules/@xterm/addon-attach": { "resolved": "addons/addon-attach", "link": true @@ -2332,40 +2460,36 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "deprecated": "Use your platform's native atob() and btoa() methods instead", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "dev": true, + "license": "MIT", "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" }, "engines": { "node": ">= 0.6" } }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, - "peer": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -2378,6 +2502,7 @@ "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.13.0" }, @@ -2390,24 +2515,17 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "engines": { - "node": ">= 14" - } - }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, + "license": "MIT", "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -2417,10 +2535,11 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2437,6 +2556,7 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^8.0.0" }, @@ -2450,10 +2570,11 @@ } }, "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -2469,24 +2590,20 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, - "engines": { - "node": ">=6" - } + "license": "MIT" }, "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/ansi-styles": { @@ -2494,6 +2611,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -2504,37 +2622,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/app-root-path": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6.0.0" } @@ -2544,6 +2637,7 @@ "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", "dev": true, + "license": "MIT", "dependencies": { "default-require-extensions": "^3.0.0" }, @@ -2555,13 +2649,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/are-docs-informative": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" } @@ -2570,13 +2666,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" @@ -2588,32 +2686,22 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true - }, "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, + "license": "MIT", "engines": { - "node": "*" + "node": ">=12" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -2624,31 +2712,27 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/axios": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", - "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.11", - "form-data": "^4.0.5", - "proxy-from-env": "^2.1.0" + "engines": { + "node": "18 || 20 || >=22" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, "node_modules/baseline-browser-mapping": { - "version": "2.9.11", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", - "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", + "version": "2.10.28", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.28.tgz", + "integrity": "sha512-Ic44hnOtFIgravCunj1ifSoQPSUrkNiJuH9Mf6jr2jjoA74icqV8wU0KuadXeOR8zuIJMOoTv0GuQjZ9ZYNMeA==", "dev": true, + "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/bidi-js": { @@ -2656,68 +2740,47 @@ "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", "dev": true, + "license": "MIT", "dependencies": { "require-from-string": "^2.0.2" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/body-parser": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", - "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", - "dev": true, - "dependencies": { - "bytes": "~3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "~1.2.0", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "on-finished": "~2.4.1", - "qs": "~6.14.0", - "raw-body": "~2.5.3", - "type-is": "~1.6.18", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "dev": true, + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/braces": { @@ -2725,6 +2788,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -2736,12 +2800,13 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", "dev": true, "funding": [ { @@ -2757,13 +2822,13 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, + "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -2772,26 +2837,19 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -2801,6 +2859,7 @@ "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", "dev": true, + "license": "MIT", "dependencies": { "hasha": "^5.0.0", "make-dir": "^3.0.0", @@ -2812,14 +2871,15 @@ } }, "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" }, "engines": { @@ -2834,6 +2894,7 @@ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -2847,6 +2908,7 @@ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -2858,28 +2920,20 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001761", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz", - "integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==", + "version": "1.0.30001792", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", + "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==", "dev": true, "funding": [ { @@ -2894,24 +2948,17 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chai": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", - "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.1.0" - }, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=18" } }, "node_modules/chalk": { @@ -2919,6 +2966,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2930,52 +2978,33 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { - "get-func-name": "^2.0.2" + "has-flag": "^4.0.0" }, "engines": { - "node": "*" + "node": ">=8" } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, + "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" } }, "node_modules/chrome-trace-event": { @@ -2983,6 +3012,7 @@ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0" } @@ -2992,6 +3022,7 @@ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -3009,14 +3040,81 @@ } }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/clone": { @@ -3024,6 +3122,7 @@ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8" } @@ -3033,6 +3132,7 @@ "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, + "license": "MIT", "dependencies": { "is-plain-object": "^2.0.4", "kind-of": "^6.0.2", @@ -3047,6 +3147,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -3058,19 +3159,15 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/colors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.1.90" } @@ -3080,6 +3177,7 @@ "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz", "integrity": "sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==", "dev": true, + "license": "MIT", "dependencies": { "strip-ansi": "^6.0.1", "wcwidth": "^1.0.0" @@ -3088,29 +3186,42 @@ "node": ">=8.0.0" } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/columnify/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/columnify/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", "dependencies": { - "delayed-stream": "~1.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">=8" } }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/comment-parser": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", - "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.6.tgz", + "integrity": "sha512-ObxuY6vnbWTN6Od72xfwN9DbzC7Y2vv8u1Soi9ahRKL37gb6y1qk6/dgjs+3JWuXJHWvsg3BXIwzd/rkmAwavg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 12.0.0" } @@ -3119,13 +3230,15 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/complex.js": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.4.3.tgz", "integrity": "sha512-UrQVSUur14tNX6tiP4y8T4w4FeJAX3bi2cIv0pu/DTLFNxoq7z2Yh83Vfzztj6Px3X/lubqQ9IrPp7Bpn6p4MQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" }, @@ -3134,12 +3247,6 @@ "url": "https://github.com/sponsors/rawify" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, "node_modules/concurrently": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", @@ -3165,76 +3272,18 @@ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, - "node_modules/concurrently/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/concurrently/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "dev": true, "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/concurrently/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/concurrently/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/content-type": { @@ -3242,6 +3291,7 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -3250,39 +3300,45 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cookie": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "dev": true + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } }, "node_modules/cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", + "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", "dev": true, + "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.1" + "@epic-web/invariant": "^1.0.0", + "cross-spawn": "^7.0.6" }, "bin": { - "cross-env": "src/bin/cross-env.js", - "cross-env-shell": "src/bin/cross-env-shell.js" + "cross-env": "dist/bin/cross-env.js", + "cross-env-shell": "dist/bin/cross-env-shell.js" }, "engines": { - "node": ">=10.14", - "npm": ">=6", - "yarn": ">=1" + "node": ">=20" } }, "node_modules/cross-spawn": { @@ -3290,6 +3346,7 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3300,43 +3357,31 @@ } }, "node_modules/css-tree": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", - "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", "dev": true, + "license": "MIT", "dependencies": { - "mdn-data": "2.12.2", - "source-map-js": "^1.0.1" + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, - "node_modules/cssstyle": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.5.tgz", - "integrity": "sha512-GlsEptulso7Jg0VaOZ8BXQi3AkYM5BOJKEO/rjMidSCq70FkIC5y0eawrCXeYzxgt3OCf4Ls+eoxN+/05vN0Ag==", - "dev": true, - "dependencies": { - "@asamuzakjp/css-color": "^4.1.1", - "@csstools/css-syntax-patches-for-csstree": "^1.0.21", - "css-tree": "^3.1.0" - }, - "engines": { - "node": ">=20" - } - }, "node_modules/data-urls": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-6.0.0.tgz", - "integrity": "sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", "dev": true, + "license": "MIT", "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^15.0.0" + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" }, "engines": { - "node": ">=20" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, "node_modules/debug": { @@ -3344,6 +3389,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -3361,6 +3407,7 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -3369,25 +3416,15 @@ "version": "10.6.0", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", - "dev": true - }, - "node_modules/deep-eql": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", - "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } + "license": "MIT" }, "node_modules/deep-equal": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.0", "call-bind": "^1.0.5", @@ -3419,13 +3456,15 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/default-require-extensions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", "dev": true, + "license": "MIT", "dependencies": { "strip-bom": "^4.0.0" }, @@ -3441,6 +3480,7 @@ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, + "license": "MIT", "dependencies": { "clone": "^1.0.2" }, @@ -3453,6 +3493,7 @@ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -3470,6 +3511,7 @@ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -3482,39 +3524,22 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -3524,6 +3549,7 @@ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dev": true, + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -3533,41 +3559,53 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.267", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", - "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", - "dev": true + "version": "1.5.353", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.353.tgz", + "integrity": "sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==", + "dev": true, + "license": "ISC" }, "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/enhanced-resolve": { - "version": "5.18.4", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", - "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==", + "version": "5.21.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.2.tgz", + "integrity": "sha512-xe9vQb5kReirPUxgQrXA3ihgbCqssmTiM7cOZ+Gzu+VeGWgpV98lLZvp0dl4yriyAePcewxGUs9UpKD8PET9KQ==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "tapable": "^2.3.3" }, "engines": { "node": ">=10.13.0" @@ -3578,6 +3616,7 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.12" }, @@ -3590,6 +3629,7 @@ "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", "dev": true, + "license": "MIT", "bin": { "envinfo": "dist/cli.js" }, @@ -3602,6 +3642,7 @@ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -3611,6 +3652,7 @@ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -3620,6 +3662,7 @@ "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.3", @@ -3636,16 +3679,18 @@ } }, "node_modules/es-module-lexer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", - "dev": true + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -3653,33 +3698,20 @@ "node": ">= 0.4" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", + "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -3687,32 +3719,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" + "@esbuild/aix-ppc64": "0.28.0", + "@esbuild/android-arm": "0.28.0", + "@esbuild/android-arm64": "0.28.0", + "@esbuild/android-x64": "0.28.0", + "@esbuild/darwin-arm64": "0.28.0", + "@esbuild/darwin-x64": "0.28.0", + "@esbuild/freebsd-arm64": "0.28.0", + "@esbuild/freebsd-x64": "0.28.0", + "@esbuild/linux-arm": "0.28.0", + "@esbuild/linux-arm64": "0.28.0", + "@esbuild/linux-ia32": "0.28.0", + "@esbuild/linux-loong64": "0.28.0", + "@esbuild/linux-mips64el": "0.28.0", + "@esbuild/linux-ppc64": "0.28.0", + "@esbuild/linux-riscv64": "0.28.0", + "@esbuild/linux-s390x": "0.28.0", + "@esbuild/linux-x64": "0.28.0", + "@esbuild/netbsd-arm64": "0.28.0", + "@esbuild/netbsd-x64": "0.28.0", + "@esbuild/openbsd-arm64": "0.28.0", + "@esbuild/openbsd-x64": "0.28.0", + "@esbuild/openharmony-arm64": "0.28.0", + "@esbuild/sunos-x64": "0.28.0", + "@esbuild/win32-arm64": "0.28.0", + "@esbuild/win32-ia32": "0.28.0", + "@esbuild/win32-x64": "0.28.0" } }, "node_modules/escalade": { @@ -3720,6 +3752,7 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -3728,19 +3761,22 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/escape-latex": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -3749,33 +3785,30 @@ } }, "node_modules/eslint": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", - "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", + "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.2", - "@eslint/plugin-kit": "^0.4.1", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.5.5", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "ajv": "^6.12.4", - "chalk": "^4.0.0", + "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", @@ -3785,8 +3818,7 @@ "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", + "minimatch": "^10.2.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -3794,7 +3826,7 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://eslint.org/donate" @@ -3809,40 +3841,79 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "50.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.8.0.tgz", - "integrity": "sha512-UyGb5755LMFWPrZTEqqvTJ3urLz1iqj+bYOHFNag+sw3NvaMWP9K2z+uIn37XfNALmQLQyrBlJ5mkiVPL7ADEg==", + "version": "62.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.9.0.tgz", + "integrity": "sha512-PY7/X4jrVgoIDncUmITlUqK546Ltmx/Pd4Hdsu4CvSjryQZJI2mEV4vrdMufyTetMiZ5taNSqvK//BTgVUlNkA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@es-joy/jsdoccomment": "~0.50.2", + "@es-joy/jsdoccomment": "~0.86.0", + "@es-joy/resolve.exports": "1.2.0", "are-docs-informative": "^0.0.2", - "comment-parser": "1.4.1", - "debug": "^4.4.1", + "comment-parser": "1.4.6", + "debug": "^4.4.3", "escape-string-regexp": "^4.0.0", - "espree": "^10.3.0", - "esquery": "^1.6.0", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "html-entities": "^2.6.0", + "object-deep-merge": "^2.0.0", "parse-imports-exports": "^0.2.4", - "semver": "^7.7.2", - "spdx-expression-parse": "^4.0.0" + "semver": "^7.7.4", + "spdx-expression-parse": "^4.0.0", + "to-valid-identifier": "^1.0.0" }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" @@ -3853,6 +3924,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -3860,14 +3932,35 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/ignore": { @@ -3875,28 +3968,17 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", @@ -3914,6 +3996,7 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, + "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -3923,10 +4006,11 @@ } }, "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -3939,6 +4023,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -3951,6 +4036,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -3960,6 +4046,7 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -3969,6 +4056,7 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -3978,50 +4066,49 @@ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.x" } }, "node_modules/express": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", - "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "dev": true, + "license": "MIT", "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "~1.20.3", - "content-disposition": "~0.5.4", - "content-type": "~1.0.4", - "cookie": "~0.7.1", - "cookie-signature": "~1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.3.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "~0.1.12", - "proxy-addr": "~2.0.7", - "qs": "~6.14.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "~0.19.0", - "serve-static": "~1.16.2", - "setprototypeof": "1.2.0", - "statuses": "~2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" }, "engines": { - "node": ">= 0.10.0" + "node": ">= 18" }, "funding": { "type": "opencollective", @@ -4033,6 +4120,7 @@ "resolved": "https://registry.npmjs.org/express-ws/-/express-ws-5.0.2.tgz", "integrity": "sha512-0uvmuk61O9HXgLhGl3QhNSEtRsQevtmbL94/eILaliEADZBHZOQUAiHFrGPrgsjikohyrmSG5g+sCfASTt0lkQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "ws": "^7.4.6" }, @@ -4048,6 +4136,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.3.0" }, @@ -4064,43 +4153,31 @@ } } }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "dev": true, "funding": [ { @@ -4111,13 +4188,15 @@ "type": "opencollective", "url": "https://opencollective.com/fastify" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4.9.1" } @@ -4127,6 +4206,7 @@ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.0.0" }, @@ -4144,6 +4224,7 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, + "license": "MIT", "dependencies": { "flat-cache": "^4.0.0" }, @@ -4156,6 +4237,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -4164,43 +4246,33 @@ } }, "node_modules/finalhandler": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", - "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "dev": true, + "license": "MIT", "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "statuses": "~2.0.2", - "unpipe": "~1.0.0" + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, "node_modules/find-cache-dir": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", "dev": true, + "license": "MIT", "dependencies": { "commondir": "^1.0.1", "make-dir": "^3.0.2", @@ -4218,6 +4290,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -4234,6 +4307,7 @@ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, + "license": "BSD-3-Clause", "bin": { "flat": "cli.js" } @@ -4243,6 +4317,7 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" @@ -4258,27 +4333,6 @@ "dev": true, "license": "ISC" }, - "node_modules/follow-redirects": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/font-finder": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/font-finder/-/font-finder-1.1.0.tgz", @@ -4298,6 +4352,7 @@ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, + "license": "MIT", "dependencies": { "is-callable": "^1.2.7" }, @@ -4313,6 +4368,7 @@ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, + "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" @@ -4324,63 +4380,38 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" }, "funding": { - "type": "patreon", + "type": "github", "url": "https://github.com/sponsors/rawify" } }, "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/fromentries": { @@ -4401,20 +4432,16 @@ "type": "consulting", "url": "https://feross.org/support" } - ] - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + ], + "license": "MIT" }, "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -4428,6 +4455,7 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4437,6 +4465,7 @@ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4446,6 +4475,7 @@ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -4455,24 +4485,17 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -4497,6 +4520,7 @@ "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.0.0" } @@ -4506,6 +4530,7 @@ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dev": true, + "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -4525,20 +4550,22 @@ } }, "node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, + "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=12" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4549,6 +4576,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -4560,31 +4588,40 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } }, "node_modules/glob/node_modules/minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { - "node": ">=10" - } - }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "engines": { - "node": ">=18" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/gopd": { @@ -4592,6 +4629,7 @@ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4603,13 +4641,15 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4622,6 +4662,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -4631,6 +4672,7 @@ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" }, @@ -4643,6 +4685,7 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4655,6 +4698,7 @@ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, + "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -4670,6 +4714,7 @@ "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", "dev": true, + "license": "MIT", "dependencies": { "is-stream": "^2.0.0", "type-fest": "^0.8.0" @@ -4682,10 +4727,11 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -4698,33 +4744,54 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, + "license": "MIT", "bin": { "he": "bin/he" } }, "node_modules/html-encoding-sniffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", "dev": true, + "license": "MIT", "dependencies": { - "whatwg-encoding": "^3.1.1" + "@exodus/bytes": "^1.6.0" }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "dev": true, + "license": "MIT", "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", @@ -4740,42 +4807,21 @@ "url": "https://opencollective.com/express" } }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", "dev": true, + "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/ignore": { @@ -4783,31 +4829,17 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/import-local": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, + "license": "MIT", "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" @@ -4827,6 +4859,7 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } @@ -4836,32 +4869,24 @@ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", @@ -4872,12 +4897,13 @@ } }, "node_modules/interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">=10.13.0" } }, "node_modules/inwasm-runtime": { @@ -4892,6 +4918,7 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.10" } @@ -4901,6 +4928,7 @@ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" @@ -4917,6 +4945,7 @@ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -4934,6 +4963,7 @@ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, + "license": "MIT", "dependencies": { "has-bigints": "^1.0.2" }, @@ -4944,23 +4974,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-boolean-object": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" @@ -4977,6 +4996,7 @@ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4985,12 +5005,13 @@ } }, "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "dev": true, + "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -5004,6 +5025,7 @@ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" @@ -5020,6 +5042,7 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5029,6 +5052,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -5038,6 +5062,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -5050,6 +5075,7 @@ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5062,6 +5088,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -5071,6 +5098,7 @@ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" @@ -5082,11 +5110,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -5096,6 +5135,7 @@ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, + "license": "MIT", "dependencies": { "isobject": "^3.0.1" }, @@ -5107,13 +5147,22 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", @@ -5132,6 +5181,7 @@ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5144,6 +5194,7 @@ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3" }, @@ -5159,6 +5210,7 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -5171,6 +5223,7 @@ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" @@ -5187,6 +5240,7 @@ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", @@ -5203,13 +5257,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -5222,6 +5278,7 @@ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5234,6 +5291,7 @@ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" @@ -5250,6 +5308,7 @@ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5258,19 +5317,22 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5280,6 +5342,7 @@ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=8" } @@ -5289,6 +5352,7 @@ "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "append-transform": "^2.0.0" }, @@ -5301,6 +5365,7 @@ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@babel/core": "^7.23.9", "@babel/parser": "^7.23.9", @@ -5313,20 +5378,21 @@ } }, "node_modules/istanbul-lib-processinfo": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", - "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-3.0.0.tgz", + "integrity": "sha512-P7nLXRRlo7Sqinty6lNa7+4o9jBUYGpqtejqCOZKfgXlRoxY/QArflcB86YO500Ahj4pDJEG34JjMRbQgePLnQ==", "dev": true, + "license": "ISC", "dependencies": { "archy": "^1.0.0", "cross-spawn": "^7.0.3", "istanbul-lib-coverage": "^3.2.0", "p-map": "^3.0.0", - "rimraf": "^3.0.0", + "rimraf": "^6.1.3", "uuid": "^8.3.2" }, "engines": { - "node": ">=8" + "node": "20 || >=22" } }, "node_modules/istanbul-lib-report": { @@ -5334,6 +5400,7 @@ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", @@ -5348,6 +5415,7 @@ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, + "license": "MIT", "dependencies": { "semver": "^7.5.3" }, @@ -5358,11 +5426,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/istanbul-lib-source-maps": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", @@ -5377,6 +5459,7 @@ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" @@ -5385,17 +5468,35 @@ "node": ">=8" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/javascript-natural-sort": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -5405,37 +5506,25 @@ "node": ">= 10.13.0" } }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/js-base64": { "version": "3.7.8", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.8.tgz", - "integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==" + "integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==", + "license": "BSD-3-Clause" }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -5444,43 +5533,46 @@ } }, "node_modules/jsdoc-type-pratt-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz", - "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-7.2.0.tgz", + "integrity": "sha512-dh140MMgjyg3JhJZY/+iEzW+NO5xR2gpbDFKHqotCmexElVntw7GjWjt511+C/Ef02RU5TKYrJo/Xlzk+OLaTw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=12.0.0" + "node": ">=20.0.0" } }, "node_modules/jsdom": { - "version": "27.3.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.3.0.tgz", - "integrity": "sha512-GtldT42B8+jefDUC4yUKAvsaOrH7PDHmZxZXNgF2xMmymjUbRYJvpAybZAKEmXDGTM0mCsz8duOa4vTm5AY2Kg==", + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", + "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", "dev": true, + "license": "MIT", "dependencies": { - "@acemir/cssom": "^0.9.28", - "@asamuzakjp/dom-selector": "^6.7.6", - "cssstyle": "^5.3.4", - "data-urls": "^6.0.0", + "@asamuzakjp/css-color": "^5.1.11", + "@asamuzakjp/dom-selector": "^7.1.1", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.3", + "@exodus/bytes": "^1.15.0", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", "decimal.js": "^10.6.0", - "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.6", + "html-encoding-sniffer": "^6.0.0", "is-potential-custom-element-name": "^1.0.1", - "parse5": "^8.0.0", + "lru-cache": "^11.3.5", + "parse5": "^8.0.1", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^6.0.0", + "tough-cookie": "^6.0.1", + "undici": "^7.25.0", "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^8.0.0", - "whatwg-encoding": "^3.1.1", - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^15.1.0", - "ws": "^8.18.3", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.1", "xml-name-validator": "^5.0.0" }, "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24.0.0" }, "peerDependencies": { "canvas": "^3.0.0" @@ -5491,13 +5583,27 @@ } } }, - "node_modules/jsdom/node_modules/parse5": { + "node_modules/jsdom/node_modules/entities": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", - "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/jsdom/node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", "dependencies": { - "entities": "^6.0.0" + "entities": "^8.0.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" @@ -5508,6 +5614,7 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -5519,31 +5626,29 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -5556,6 +5661,7 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } @@ -5565,6 +5671,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5574,6 +5681,7 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -5583,10 +5691,11 @@ } }, "node_modules/loader-runner": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", - "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", + "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.11.5" }, @@ -5600,6 +5709,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -5614,19 +5724,15 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -5638,22 +5744,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.1" - } - }, "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", + "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" } }, "node_modules/make-dir": { @@ -5661,6 +5758,7 @@ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, + "license": "MIT", "dependencies": { "semver": "^6.0.0" }, @@ -5676,6 +5774,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -5685,53 +5784,61 @@ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/mathjs": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-9.5.2.tgz", - "integrity": "sha512-c0erTq0GP503/Ch2OtDOAn50GIOsuxTMjmE00NI/vKJFSWrDaQHRjx6ai+16xYv70yBSnnpUgHZGNf9FR9IwmA==", + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-15.2.0.tgz", + "integrity": "sha512-UAQzSVob9rNLdGpqcFMYmSu9dkuLYy7Lr2hBEQS5SHQdknA9VppJz3cy2KkpMzTODunad6V6cNv+5kOLsePLow==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@babel/runtime": "^7.15.4", - "complex.js": "^2.0.15", - "decimal.js": "^10.3.1", + "@babel/runtime": "^7.26.10", + "complex.js": "^2.2.5", + "decimal.js": "^10.4.3", "escape-latex": "^1.2.0", - "fraction.js": "^4.1.1", + "fraction.js": "^5.2.1", "javascript-natural-sort": "^0.7.1", "seedrandom": "^3.0.5", "tiny-emitter": "^2.1.0", - "typed-function": "^2.0.0" + "typed-function": "^4.2.1" }, "bin": { "mathjs": "bin/cli.js" }, "engines": { - "node": ">= 12" + "node": ">= 18" } }, "node_modules/mdn-data": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", - "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", - "dev": true + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" }, "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } @@ -5740,22 +5847,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true, - "engines": { - "node": ">= 0.6" - } + "license": "MIT" }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -5777,40 +5877,114 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "dev": true, + "license": "MIT", "dependencies": { - "mime-db": "1.52.0" + "mime-db": "^1.54.0" }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mocha": { + "version": "11.7.5", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", + "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/mocha/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/minimatch": { "version": "9.0.9", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", @@ -5826,101 +6000,19 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mocha": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", - "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.3", - "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", - "debug": "^4.3.5", - "diff": "^5.2.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^8.1.0", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", - "ms": "^2.1.3", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/mustache": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", "dev": true, + "license": "MIT", "bin": { "mustache": "bin/mustache" } @@ -5929,13 +6021,15 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -5944,19 +6038,22 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/node-addon-api": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", "dev": true, + "license": "MIT", "dependencies": { "process-on-spawn": "^1.0.0" }, @@ -5965,9 +6062,9 @@ } }, "node_modules/node-pty": { - "version": "1.2.0-beta.9", - "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.2.0-beta.9.tgz", - "integrity": "sha512-XR3x/4OQZ7DlceespOQ99kk9jZHwmO78RuEIZtGsSPdsDi11s79VcK04aqcaK3x09Y6ORJM/KBz/oQ/p+WGRLg==", + "version": "1.2.0-beta.12", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.2.0-beta.12.tgz", + "integrity": "sha512-uExTCG/4VmSJa4+TjxFwPXv8BfacmfFEBL6JpxCMDghcwqzvD0yTcGmZ1fKOK6HY33tp0CelLblqTECJizc+Yw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -5976,25 +6073,18 @@ } }, "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", "dev": true, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, "node_modules/nyc": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-17.1.0.tgz", - "integrity": "sha512-U42vQ4czpKa0QdI1hu950XuNhYqgoM+ZF1HT+VuUHL9hPfDPVvNQyltmMqdE9bUHMVa+8yNbc3QKTj8zQhlVxQ==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-18.0.0.tgz", + "integrity": "sha512-G5UyHinFkB1BxqGTrmZdB6uIYH0+v7ZnVssuflUDi+J+RhKWyAhRT1RCehBSI6jLFLuUUgFDyLt49mUtdO1XeQ==", "dev": true, + "license": "ISC", "dependencies": { "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", @@ -6005,11 +6095,11 @@ "find-up": "^4.1.0", "foreground-child": "^3.3.0", "get-package-type": "^0.1.0", - "glob": "^7.1.6", + "glob": "^13.0.6", "istanbul-lib-coverage": "^3.0.0", "istanbul-lib-hook": "^3.0.0", "istanbul-lib-instrument": "^6.0.2", - "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-processinfo": "^3.0.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.0.2", @@ -6018,27 +6108,27 @@ "p-map": "^3.0.0", "process-on-spawn": "^1.0.0", "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", + "rimraf": "^6.1.3", "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", + "spawn-wrap": "^3.0.0", + "test-exclude": "^8.0.0", "yargs": "^15.0.2" }, "bin": { "nyc": "bin/nyc.js" }, "engines": { - "node": ">=18" + "node": "20 || >=22" } }, - "node_modules/nyc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/nyc/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "license": "MIT", + "engines": { + "node": ">=8" } }, "node_modules/nyc/node_modules/cliui": { @@ -6046,17 +6136,26 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, + "node_modules/nyc/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, "node_modules/nyc/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -6066,21 +6165,18 @@ } }, "node_modules/nyc/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, "engines": { - "node": "*" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -6091,6 +6187,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -6098,24 +6195,12 @@ "node": ">=8" } }, - "node_modules/nyc/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/nyc/node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -6131,6 +6216,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -6138,11 +6224,54 @@ "node": ">=8" } }, - "node_modules/nyc/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "node_modules/nyc/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nyc/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/nyc/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { "node": ">=8" } @@ -6152,6 +6281,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -6165,13 +6295,15 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/nyc/node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", @@ -6194,6 +6326,7 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, + "license": "ISC", "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -6202,11 +6335,19 @@ "node": ">=6" } }, + "node_modules/object-deep-merge": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/object-deep-merge/-/object-deep-merge-2.0.0.tgz", + "integrity": "sha512-3DC3UMpeffLTHiuXSy/UG4NOIYTLlY9u3V82+djSCLYClWobZiS4ivYzpIUWrRY/nfsJ8cWsKyG3QfyLePmhvg==", + "dev": true, + "license": "MIT" + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6219,6 +6360,7 @@ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1" @@ -6235,6 +6377,7 @@ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -6244,6 +6387,7 @@ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -6264,6 +6408,7 @@ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -6276,17 +6421,16 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, + "license": "ISC", "dependencies": { "wrappy": "1" } }, "node_modules/opentype.js": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-0.8.0.tgz", - "integrity": "sha512-FQHR4oGP+a0m/f6yHoRpBOIbn/5ZWxKd4D/djHVJu8+KpBTYrJda0b7mLcgDEMWXE9xBCJm+qb0yv6FcvPjukg==", - "dependencies": { - "tiny-inflate": "^1.0.2" - }, + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-2.0.0.tgz", + "integrity": "sha512-kCyjv6xdDY1W/jLWZ/L3QhhTlKUqDZMQ5+Jdlw12b3dXkKNpYBqqlMMj0YDQPShWFTMwgZI1hG14kN3XUDSg/A==", + "license": "MIT", "bin": { "ot": "bin/ot" } @@ -6296,6 +6440,7 @@ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -6313,6 +6458,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -6328,6 +6474,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -6343,6 +6490,7 @@ "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", "dev": true, + "license": "MIT", "dependencies": { "aggregate-error": "^3.0.0" }, @@ -6355,6 +6503,7 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -6364,6 +6513,7 @@ "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", "dev": true, + "license": "ISC", "dependencies": { "graceful-fs": "^4.1.15", "hasha": "^5.0.0", @@ -6374,29 +6524,26 @@ "node": ">=8" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/pako": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", - "dev": true - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } + "license": "(MIT AND Zlib)" }, "node_modules/parse-imports-exports": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz", "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==", "dev": true, + "license": "MIT", "dependencies": { "parse-statements": "1.0.11" } @@ -6405,13 +6552,15 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz", "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/parse5": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", "dev": true, + "license": "MIT", "dependencies": { "entities": "^6.0.0" }, @@ -6424,6 +6573,7 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -6433,24 +6583,17 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -6459,35 +6602,50 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "dev": true - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true, "license": "MIT" }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.4", @@ -6507,6 +6665,7 @@ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, + "license": "MIT", "dependencies": { "find-up": "^4.0.0" }, @@ -6519,6 +6678,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -6532,6 +6692,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -6544,6 +6705,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -6559,6 +6721,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -6567,12 +6730,13 @@ } }, "node_modules/playwright": { - "version": "1.57.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", - "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.57.0" + "playwright-core": "1.59.1" }, "bin": { "playwright": "cli.js" @@ -6585,10 +6749,11 @@ } }, "node_modules/playwright-core": { - "version": "1.57.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", - "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", "dev": true, + "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" }, @@ -6596,25 +6761,12 @@ "node": ">=18" } }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -6624,6 +6776,7 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } @@ -6633,6 +6786,7 @@ "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.1.0.tgz", "integrity": "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==", "dev": true, + "license": "MIT", "dependencies": { "fromentries": "^1.2.0" }, @@ -6655,6 +6809,7 @@ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, + "license": "MIT", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -6663,29 +6818,20 @@ "node": ">= 0.10" } }, - "node_modules/proxy-from-env": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", - "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -6703,6 +6849,7 @@ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" } @@ -6712,60 +6859,52 @@ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "dev": true, + "license": "MIT", "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", + "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.10" } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">= 14.18.0" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, + "license": "MIT", "dependencies": { - "resolve": "^1.9.0" + "resolve": "^1.20.0" }, "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, "node_modules/regexp.prototype.flags": { @@ -6773,6 +6912,7 @@ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -6793,6 +6933,7 @@ "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", "dev": true, + "license": "ISC", "dependencies": { "es6-error": "^4.0.1" }, @@ -6805,6 +6946,7 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6814,6 +6956,7 @@ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6822,14 +6965,30 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true + "dev": true, + "license": "ISC" + }, + "node_modules/reserved-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/reserved-identifiers/-/reserved-identifiers-1.2.0.tgz", + "integrity": "sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", "dev": true, + "license": "MIT", "dependencies": { + "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" @@ -6849,6 +7008,7 @@ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, + "license": "MIT", "dependencies": { "resolve-from": "^5.0.0" }, @@ -6856,82 +7016,86 @@ "node": ">=8" } }, - "node_modules/resolve-cwd/node_modules/resolve-from": { + "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "glob": "^7.1.3" + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" }, "bin": { - "rimraf": "bin.js" + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, "engines": { - "node": "*" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "node_modules/rimraf/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^1.1.7" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": "*" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" } }, "node_modules/rxjs": { @@ -6962,13 +7126,15 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/safe-regex-test": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -6985,13 +7151,15 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "dev": true, + "license": "ISC", "dependencies": { "xmlchars": "^2.2.0" }, @@ -7004,6 +7172,7 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -7019,11 +7188,11 @@ } }, "node_modules/schema-utils/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -7040,6 +7209,7 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -7051,19 +7221,22 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/seedrandom": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -7072,79 +7245,75 @@ } }, "node_modules/send": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", - "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "dev": true, + "license": "MIT", "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.4.1", - "range-parser": "~1.2.1", - "statuses": "~2.0.2" + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, "node_modules/serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/serve-static": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", - "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "dev": true, + "license": "MIT", "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "~0.19.1" + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -7162,6 +7331,7 @@ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -7176,13 +7346,15 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, + "license": "MIT", "dependencies": { "kind-of": "^6.0.2" }, @@ -7195,6 +7367,7 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -7207,6 +7380,7 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -7229,6 +7403,7 @@ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -7244,13 +7419,14 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -7264,6 +7440,7 @@ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -7282,6 +7459,7 @@ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -7297,22 +7475,31 @@ } }, "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/sixel": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/sixel/-/sixel-0.16.0.tgz", "integrity": "sha512-xicu6Y6Cyhmv5rjyHxq2r5RnKerlL/nyZEGjOU5bLCshXkZryc9JFJThTCKPOAtWXCfeWquEKFVFfMPcTD25PA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -7322,29 +7509,30 @@ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/source-map-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz", - "integrity": "sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-5.0.0.tgz", + "integrity": "sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==", "dev": true, + "license": "MIT", "dependencies": { - "abab": "^2.0.5", "iconv-lite": "^0.6.3", - "source-map-js": "^1.0.1" + "source-map-js": "^1.0.2" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "^5.0.0" + "webpack": "^5.72.1" } }, "node_modules/source-map-loader/node_modules/iconv-lite": { @@ -7352,6 +7540,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -7364,21 +7553,24 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, + "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "node_modules/spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-3.0.0.tgz", + "integrity": "sha512-z+s5vv4KzFPJVddGab0xX2n7kQPGMdNUX5l9T8EJqsXdKTWpcxmAqWHpsgHEXoC1taGBCc7b79bi62M5kdbrxQ==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { + "cross-spawn": "^7.0.6", "foreground-child": "^2.0.0", "is-windows": "^1.0.2", "make-dir": "^3.0.0", - "rimraf": "^3.0.0", + "rimraf": "^6.1.3", "signal-exit": "^3.0.2", "which": "^2.0.1" }, @@ -7391,6 +7583,7 @@ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", "dev": true, + "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^3.0.2" @@ -7399,39 +7592,51 @@ "node": ">=8.0.0" } }, + "node_modules/spawn-wrap/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, "node_modules/spdx-exceptions": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true + "dev": true, + "license": "CC-BY-3.0" }, "node_modules/spdx-expression-parse": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", "dev": true, + "license": "MIT", "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "node_modules/spdx-license-ids": { - "version": "3.0.22", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", - "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", - "dev": true + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -7441,6 +7646,7 @@ "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" @@ -7450,10 +7656,30 @@ } }, "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -7463,11 +7689,29 @@ "node": ">=8" } }, - "node_modules/strip-ansi": { + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -7475,11 +7719,52 @@ "node": ">=8" } }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -7489,6 +7774,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -7497,15 +7783,19 @@ } }, "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/supports-preserve-symlinks-flag": { @@ -7513,6 +7803,7 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -7524,13 +7815,15 @@ "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" }, @@ -7540,10 +7833,11 @@ } }, "node_modules/terser": { - "version": "5.44.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz", - "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.47.1.tgz", + "integrity": "sha512-tPbLXTI6ohPASb/1YViL428oEHu6/qv1OxqYnfaonVCFHqx4+wCd95pHrQWsL5X4pl90CTyW9piSAsS2L0VoMw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -7558,15 +7852,15 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.16", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz", - "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.0.tgz", + "integrity": "sha512-Eum+5ajkaOhf5KbM26osvv21kLD7BaGqQ1UA4Ami4arYwylmGUQTgHFpHDdmJod1q4QXa66p0to/FBKID+J1vA==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", "terser": "^5.31.1" }, "engines": { @@ -7580,94 +7874,110 @@ "webpack": "^5.1.0" }, "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, "@swc/core": { "optional": true }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, "esbuild": { "optional": true }, + "html-minifier-terser": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, "uglify-js": { "optional": true } } }, "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-8.0.0.tgz", + "integrity": "sha512-ZOffsNrXYggvU1mDGHk54I96r26P8SyMjO5slMKSc7+IWmtB/MQKnEC2fP51imB3/pT6YK5cT5E8f+Dd9KdyOQ==", "dev": true, + "license": "ISC", "dependencies": { "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" + "glob": "^13.0.6", + "minimatch": "^10.2.2" }, "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node": "20 || >=22" } }, "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, "engines": { - "node": "*" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "node_modules/test-exclude/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^1.1.7" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": "*" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/tiny-emitter": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", - "dev": true - }, - "node_modules/tiny-inflate": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", - "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" + "dev": true, + "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "dev": true, + "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -7677,28 +7987,31 @@ } }, "node_modules/tldts": { - "version": "7.0.19", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.19.tgz", - "integrity": "sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==", + "version": "7.0.30", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.30.tgz", + "integrity": "sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==", "dev": true, + "license": "MIT", "dependencies": { - "tldts-core": "^7.0.19" + "tldts-core": "^7.0.30" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.0.19", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.19.tgz", - "integrity": "sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==", - "dev": true + "version": "7.0.30", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.30.tgz", + "integrity": "sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==", + "dev": true, + "license": "MIT" }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -7706,20 +8019,39 @@ "node": ">=8.0" } }, + "node_modules/to-valid-identifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-valid-identifier/-/to-valid-identifier-1.0.0.tgz", + "integrity": "sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/base62": "^1.0.0", + "reserved-identifiers": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.6" } }, "node_modules/tough-cookie": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", - "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "tldts": "^7.0.5" }, @@ -7732,6 +8064,7 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", "dev": true, + "license": "MIT", "dependencies": { "punycode": "^2.3.1" }, @@ -7750,10 +8083,11 @@ } }, "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18.12" }, @@ -7762,10 +8096,11 @@ } }, "node_modules/ts-loader": { - "version": "9.5.4", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.4.tgz", - "integrity": "sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ==", + "version": "9.5.7", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.7.tgz", + "integrity": "sha512-/ZNrKgA3K3PtpMYOC71EeMWIloGw3IYEa5/t1cyz2r5/PyUwTXGzYJvcD3kfUvmhlfpz1rhV8B2O6IVTQ0avsg==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.0.0", @@ -7786,6 +8121,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">= 12" } @@ -7802,6 +8138,7 @@ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -7809,44 +8146,39 @@ "node": ">= 0.8.0" } }, - "node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=8" } }, "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "dev": true, + "license": "MIT", "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" }, "engines": { "node": ">= 0.6" } }, "node_modules/typed-function": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.1.0.tgz", - "integrity": "sha512-bctQIOqx2iVbWGDGPWwIm18QScpu2XRmkC19D8rQGFsjKSgteq/o1hTZvIG/wuDq8fanpBDrLkLq+aEN/6y5XQ==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.2.2.tgz", + "integrity": "sha512-VwaXim9Gp1bngi/q3do8hgttYn2uC3MoT/gfuMWylnj1IeZBUAyPddHZlo1K05BDoj8DYPpMdiHqH1dDYdJf2A==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 10" + "node": ">= 18" } }, "node_modules/typedarray-to-buffer": { @@ -7854,6 +8186,7 @@ "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", "dev": true, + "license": "MIT", "dependencies": { "is-typedarray": "^1.0.0" } @@ -7864,7 +8197,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7874,15 +8206,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.50.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.50.1.tgz", - "integrity": "sha512-ytTHO+SoYSbhAH9CrYnMhiLx8To6PSSvqnvXyPUgPETCvB6eBKmTI9w6XMPS3HsBRGkwTVBX+urA8dYQx6bHfQ==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.2.tgz", + "integrity": "sha512-pJw051uomb3ZeCzGTpRb8RbEqB5Y4WWet8gl/GcTlU35BSx0PVdZ86/bqkQCyKKuraVQEK7r6kBHQXF+fBhkoQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.50.1", - "@typescript-eslint/parser": "8.50.1", - "@typescript-eslint/typescript-estree": "8.50.1", - "@typescript-eslint/utils": "8.50.1" + "@typescript-eslint/eslint-plugin": "8.59.2", + "@typescript-eslint/parser": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/utils": "8.59.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7892,21 +8225,33 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" } }, "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.25.0.tgz", + "integrity": "sha512-AXNgS1Byr27fTI+2bsPEkV9CxkT8H6xNyRI68b3TatlZo3RkzlqQBLL+w7SmGPVpokjHbcuNVQUWE7FRTg+LRA==", + "dev": true, + "license": "MIT" }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -7930,6 +8275,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" @@ -7946,6 +8292,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -7954,22 +8301,16 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", - "dev": true - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "dev": true, - "engines": { - "node": ">= 0.4.0" - } + "license": "MIT" }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", "dev": true, + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -7979,6 +8320,7 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -7988,6 +8330,7 @@ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "dev": true, + "license": "MIT", "dependencies": { "xml-name-validator": "^5.0.0" }, @@ -7996,10 +8339,11 @@ } }, "node_modules/watchpack": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.0.tgz", - "integrity": "sha512-e6vZvY6xboSwLz2GD36c16+O/2Z6fKvIf4pOXptw2rY9MVwE/TXc6RGqxD3I3x0a28lwBY7DE+76uTPSsBrrCA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "dev": true, + "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -8013,25 +8357,27 @@ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, + "license": "MIT", "dependencies": { "defaults": "^1.0.3" } }, "node_modules/webidl-conversions": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.0.tgz", - "integrity": "sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=20" } }, "node_modules/webpack": { - "version": "5.104.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.104.1.tgz", - "integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==", + "version": "5.106.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.106.2.tgz", + "integrity": "sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -8039,25 +8385,24 @@ "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.15.0", + "acorn": "^8.16.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.4", + "enhanced-resolve": "^5.20.0", "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.3.1", - "mime-types": "^2.1.27", + "mime-db": "^1.54.0", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.3.16", - "watchpack": "^2.4.4", - "webpack-sources": "^3.3.3" + "terser-webpack-plugin": "^5.3.17", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.4" }, "bin": { "webpack": "bin/webpack.js" @@ -8076,45 +8421,38 @@ } }, "node_modules/webpack-cli": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", - "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-7.0.2.tgz", + "integrity": "sha512-dB0R4T+C/8YuvM+fabdvil6QE44/ChDXikV5lOOkrUeCkW5hTJv2pGLE3keh+D5hjYw8icBaJkZzpFoaHV4T+g==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.2.0", - "@webpack-cli/info": "^1.5.0", - "@webpack-cli/serve": "^1.7.0", - "colorette": "^2.0.14", - "commander": "^7.0.0", - "cross-spawn": "^7.0.3", + "@discoveryjs/json-ext": "^1.0.0", + "commander": "^14.0.3", + "cross-spawn": "^7.0.6", + "envinfo": "^7.14.0", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", - "interpret": "^2.2.0", - "rechoir": "^0.7.0", - "webpack-merge": "^5.7.3" + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^6.0.1" }, "bin": { "webpack-cli": "bin/cli.js" }, "engines": { - "node": ">=10.13.0" + "node": ">=20.9.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "4.x.x || 5.x.x" + "webpack": "^5.101.0", + "webpack-bundle-analyzer": "^4.0.0 || ^5.0.0", + "webpack-dev-server": "^5.0.0" }, "peerDependenciesMeta": { - "@webpack-cli/generators": { - "optional": true - }, - "@webpack-cli/migrate": { - "optional": true - }, "webpack-bundle-analyzer": { "optional": true }, @@ -8124,33 +8462,36 @@ } }, "node_modules/webpack-cli/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 10" + "node": ">=20" } }, "node_modules/webpack-merge": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", - "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", "dev": true, + "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", "flat": "^5.0.2", - "wildcard": "^2.0.0" + "wildcard": "^2.0.1" }, "engines": { - "node": ">=10.0.0" + "node": ">=18.0.0" } }, "node_modules/webpack-sources": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", - "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.4.1.tgz", + "integrity": "sha512-eACpxRN02yaawnt+uUNIF7Qje6A9zArxBbcAJjK1PK3S9Ycg5jIuJ8pW4q8EMnwNZCEGltcjkRx1QzOxOkKD8A==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.13.0" } @@ -8160,6 +8501,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -8173,54 +8515,34 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", - "dev": true, - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/whatwg-url": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz", - "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", "dev": true, + "license": "MIT", "dependencies": { + "@exodus/bytes": "^1.11.0", "tr46": "^6.0.0", - "webidl-conversions": "^8.0.0" + "webidl-conversions": "^8.0.1" }, "engines": { - "node": ">=20" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, "node_modules/which": { @@ -8228,6 +8550,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -8243,6 +8566,7 @@ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, + "license": "MIT", "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", @@ -8262,6 +8586,7 @@ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, + "license": "MIT", "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", @@ -8279,13 +8604,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/which-typed-array": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", @@ -8306,28 +8633,51 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", - "dev": true + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true, + "license": "Apache-2.0" }, "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8340,17 +8690,77 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, + "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", @@ -8358,11 +8768,19 @@ "typedarray-to-buffer": "^3.1.5" } }, - "node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -8384,6 +8802,7 @@ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18" } @@ -8392,65 +8811,36 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/xterm-benchmark": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/xterm-benchmark/-/xterm-benchmark-0.3.1.tgz", - "integrity": "sha512-JjsCrSxkYKWf5CmBt2BeXm83KQdStyoGWREWQ0jSFF5N8CYVbdKQoWgs56mmy6qWD5GDKxO+V89Cvnbc8YUFjw==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/xterm-benchmark/-/xterm-benchmark-0.3.2.tgz", + "integrity": "sha512-Z9FFAhG6lGMm7j7MRmOCMIbR+KYxS9FhxYef68n+xJcUSV5dMV0dHRcMOINhS63IGpHGsoE9tPqZUJBtgLu/1Q==", "dev": true, + "license": "MIT", "dependencies": { - "@types/app-root-path": "^1.2.4", - "@types/cli-table": "^0.3.0", - "@types/mathjs": "^6.0.11", - "@types/mocha": "^8.2.1", - "@types/node": "^12.12.37", - "@types/puppeteer": "^5.4.3", - "app-root-path": "^3.0.0", - "cli-table": "^0.3.6", - "columnify": "^1.5.4", + "app-root-path": "^3.1.0", + "cli-table": "^0.3.11", + "columnify": "^1.6.0", "commander": "^6.2.1", - "mathjs": "^9.3.0", - "typescript": "^4.2.3" + "mathjs": "^15.2.0" }, "bin": { "xterm-benchmark": "lib/cli.js" } }, - "node_modules/xterm-benchmark/node_modules/@types/mocha": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.3.tgz", - "integrity": "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==", - "dev": true - }, - "node_modules/xterm-benchmark/node_modules/@types/node": { - "version": "12.20.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", - "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", - "dev": true - }, "node_modules/xterm-benchmark/node_modules/commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } }, - "node_modules/xterm-benchmark/node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, "node_modules/xterm-wasm-parts": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/xterm-wasm-parts/-/xterm-wasm-parts-0.4.1.tgz", @@ -8466,6 +8856,7 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } @@ -8474,33 +8865,36 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, + "license": "MIT", "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, + "license": "ISC", "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-unparser": { @@ -8508,6 +8902,7 @@ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, + "license": "MIT", "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", @@ -8523,6 +8918,7 @@ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -8535,6 +8931,7 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -8542,18 +8939,49 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yauzl": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.1.tgz", - "integrity": "sha512-k1isifdbpNSFEHFJ1ZY4YDewv0IH9FR61lDetaRMD3j2ae3bIXGV+7c+LHCqtQGofSd8PIyV4X6+dHMAnSr60A==", + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { - "buffer-crc32": "~0.2.3", - "pend": "~1.2.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=12" + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, "node_modules/yocto-queue": { @@ -8561,6 +8989,7 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index 3f059d109..efac7f989 100644 --- a/package.json +++ b/package.json @@ -76,47 +76,48 @@ "prepublishOnly": "npm run package" }, "devDependencies": { + "@eslint/js": "^10.0.1", "@lunapaint/png-codec": "^0.2.0", - "@playwright/test": "^1.57.0", - "@stylistic/eslint-plugin": "^4.4.1", - "@types/chai": "^4.2.22", - "@types/debug": "^4.1.7", + "@playwright/test": "^1.59.1", + "@stylistic/eslint-plugin": "^5.10.0", + "@types/chai": "^5.2.3", + "@types/debug": "^4.1.13", "@types/deep-equal": "^1.0.1", - "@types/express": "4", + "@types/express": "^5.0.6", "@types/express-ws": "^3.0.1", - "@types/jsdom": "^27.0.0", - "@types/mocha": "^9.0.0", + "@types/jsdom": "^28.0.1", + "@types/mocha": "^10.0.10", "@types/node": "^22.19.3", - "@types/trusted-types": "^1.0.6", + "@types/trusted-types": "^2.0.7", "@types/utf8": "^3.0.0", "@types/webpack": "^5.28.0", "@types/ws": "^8.2.0", - "@typescript-eslint/eslint-plugin": "^8.50.1", - "@typescript-eslint/parser": "^8.50.1", - "@typescript/native-preview": "^7.0.0-dev.20260213.1", - "chai": "^4.3.4", + "@typescript-eslint/eslint-plugin": "^8.59.2", + "@typescript-eslint/parser": "^8.59.2", + "@typescript/native-preview": "^7.0.0-dev.20260509.2", + "chai": "^6.2.2", "concurrently": "^9.1.2", - "cross-env": "^7.0.3", + "cross-env": "^10.1.0", "deep-equal": "^2.0.5", - "esbuild": "~0.25.2", - "eslint": "^9.39.2", - "eslint-plugin-jsdoc": "^50.8.0", - "express": "^4.19.2", + "esbuild": "^0.28.0", + "eslint": "^10.3.0", + "eslint-plugin-jsdoc": "^62.9.0", + "express": "^5.2.1", "express-ws": "^5.0.2", - "jsdom": "^27.3.0", - "mocha": "^10.1.0", + "jsdom": "^29.1.1", + "mocha": "^11.7.5", "mustache": "^4.2.0", "node-pty": "^1.2.0-beta.9", - "nyc": "^17.1.0", - "source-map-loader": "^3.0.0", + "nyc": "^18.0.0", + "source-map-loader": "^5.0.0", "source-map-support": "^0.5.20", - "ts-loader": "^9.3.1", + "ts-loader": "^9.5.7", "typescript": "^5.9.3", - "typescript-eslint": "^8.50.1", + "typescript-eslint": "^8.59.2", "utf8": "^3.0.0", - "webpack": "^5.61.0", - "webpack-cli": "^4.9.1", - "ws": "^8.2.3", - "xterm-benchmark": "^0.3.1" + "webpack": "^5.106.2", + "webpack-cli": "^7.0.2", + "ws": "^8.20.0", + "xterm-benchmark": "^0.3.2" } } diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index 52eb00025..65c77e694 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -212,7 +212,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { if (!this._themeService) return; for (const req of event) { let acc: 'foreground' | 'background' | 'cursor' | 'ansi'; - let ident = ''; + let ident: string; switch (req.index) { case SpecialColorIndex.FOREGROUND: // OSC 10 | 110 acc = 'foreground'; diff --git a/src/browser/Terminal2.test.ts b/src/browser/Terminal2.test.ts index 6c2b3db65..a405d0602 100644 --- a/src/browser/Terminal2.test.ts +++ b/src/browser/Terminal2.test.ts @@ -125,7 +125,7 @@ function formatError(input: string, output: string, expected: string): string { // simple debug output of terminal cells function terminalToString(term: CoreBrowserTerminal): string { let result = ''; - let lineText = ''; + let lineText: string; for (let line = term.buffer.ybase; line < term.buffer.ybase + term.rows; line++) { lineText = term.buffer.lines.get(line)!.translateToString(true); // rtrim empty cells as xterm does diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts index 0cff9c25f..e5bf31139 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.ts @@ -90,14 +90,14 @@ export class DomRendererRowFactory { let charElement: HTMLSpanElement | undefined; let cellAmount = 0; let text = ''; - let i = 0; + let i; let oldBg = 0; let oldFg = 0; let oldExt = 0; let oldLinkHover: number | boolean = false; let oldSpacing = 0; let oldIsInSelection: boolean = false; - let spacing = 0; + let spacing; let skipJoinedCheckUntilX = 0; const classes: string[] = []; @@ -231,6 +231,7 @@ export class DomRendererRowFactory { } charElement = this._document.createElement('span'); cellAmount = 0; + // eslint-disable-next-line no-useless-assignment text = ''; } } diff --git a/src/browser/renderer/dom/WidthCache.ts b/src/browser/renderer/dom/WidthCache.ts index b598bbd6f..ddacfd6a6 100644 --- a/src/browser/renderer/dom/WidthCache.ts +++ b/src/browser/renderer/dom/WidthCache.ts @@ -110,7 +110,7 @@ export class WidthCache implements IDisposable { * `variant` denotes the font variant to be used. */ public get(c: string, bold: boolean | number, italic: boolean | number): number { - let cp = 0; + let cp: number; if (!bold && !italic && c.length === 1 && (cp = c.charCodeAt(0)) < WidthCacheSettings.FLAT_SIZE) { if (this._flat[cp] !== WidthCacheSettings.FLAT_UNSET) { return this._flat[cp]; diff --git a/src/browser/scrollable/touch.ts b/src/browser/scrollable/touch.ts index 8ffdfe699..475784cf6 100644 --- a/src/browser/scrollable/touch.ts +++ b/src/browser/scrollable/touch.ts @@ -367,7 +367,7 @@ export class Gesture extends Disposable { private _dispatchEvent(event: IGestureEvent): void { if (event.type === EventType.TAP) { const currentTime = (new Date()).getTime(); - let setTapCount = 0; + let setTapCount; if (currentTime - this._lastSetTapCountTime > Gesture._clearTapCountTime) { setTapCount = 1; } else { diff --git a/src/browser/services/CharacterJoinerService.test.ts b/src/browser/services/CharacterJoinerService.test.ts index 06730c7ac..42301015c 100644 --- a/src/browser/services/CharacterJoinerService.test.ts +++ b/src/browser/services/CharacterJoinerService.test.ts @@ -285,7 +285,7 @@ function substringJoiner(substring: string): (sequence: string) => [number, numb return (sequence: string): [number, number][] => { const ranges: [number, number][] = []; let searchIndex = 0; - let matchIndex = -1; + let matchIndex; while ((matchIndex = sequence.indexOf(substring, searchIndex)) !== -1) { const matchEndIndex = matchIndex + substring.length; diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index 39692a871..e4a06ad0c 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -919,10 +919,10 @@ export class SelectionService extends Disposable implements ISelectionService { // Calculate the start _column_, converting the the string indexes back to // column coordinates. let start = - startIndex // The index of the selection's start char in the line string - + charOffset // The difference between the initial char's column and index - - leftWideCharCount // The number of wide chars left of the initial char - + leftLongCharOffset; // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis) + startIndex // The index of the selection's start char in the line string + + charOffset // The difference between the initial char's column and index + - leftWideCharCount // The number of wide chars left of the initial char + + leftLongCharOffset; // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis) // Calculate the length in _columns_, converting the the string indexes back // to column coordinates. diff --git a/src/common/TaskQueue.ts b/src/common/TaskQueue.ts index 3cad0a140..77f9aea55 100644 --- a/src/common/TaskQueue.ts +++ b/src/common/TaskQueue.ts @@ -74,10 +74,10 @@ abstract class TaskQueue implements ITaskQueue { private _process(deadline: ITaskDeadline): void { this._idleCallback = undefined; - let taskDuration = 0; + let taskDuration: number; let longestTask = 0; let lastDeadlineRemaining = deadline.timeRemaining(); - let deadlineRemaining = 0; + let deadlineRemaining: number; while (this._i < this._tasks.length) { taskDuration = performance.now(); if (!this._tasks[this._i]()) { diff --git a/src/common/data/Charsets.ts b/src/common/data/Charsets.ts index 9a9a4e552..06c1f4b84 100644 --- a/src/common/data/Charsets.ts +++ b/src/common/data/Charsets.ts @@ -98,8 +98,7 @@ CHARSETS['4'] = { * ESC (C or ESC (5 * Reference: http://vt100.net/docs/vt220-rm/table2-7.html */ -CHARSETS['C'] = -CHARSETS['5'] = { +CHARSETS['C'] = CHARSETS['5'] = { '[': 'Ä', '\\': 'Ö', ']': 'Å', @@ -185,8 +184,7 @@ CHARSETS['Y'] = { * ESC (E or ESC (6 * Reference: http://vt100.net/docs/vt220-rm/table2-12.html */ -CHARSETS['E'] = -CHARSETS['6'] = { +CHARSETS['E'] = CHARSETS['6'] = { '@': 'Ä', '[': 'Æ', '\\': 'Ø', @@ -220,8 +218,7 @@ CHARSETS['Z'] = { * ESC (H or ESC (7 * Reference: http://vt100.net/docs/vt220-rm/table2-14.html */ -CHARSETS['H'] = -CHARSETS['7'] = { +CHARSETS['H'] = CHARSETS['7'] = { '@': 'É', '[': 'Ä', '\\': 'Ö', diff --git a/src/common/input/TextDecoder.ts b/src/common/input/TextDecoder.ts index 7ec9c7cd2..ac7f8d155 100644 --- a/src/common/input/TextDecoder.ts +++ b/src/common/input/TextDecoder.ts @@ -148,7 +148,7 @@ export class Utf8ToUtf32 { let byte2: number; let byte3: number; let byte4: number; - let codepoint = 0; + let codepoint; let startPos = 0; // handle leftover bytes diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index d944112db..2780d8916 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -572,8 +572,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP * ``` */ public parse(data: Uint32Array, length: number, promiseResult?: boolean): void | Promise { - let code = 0; - let transition = 0; + let code: number; + let transition: number; let start = 0; let handlerResult: void | boolean | Promise; diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts index 133e1a985..375daa20b 100644 --- a/src/common/services/DecorationService.ts +++ b/src/common/services/DecorationService.ts @@ -72,10 +72,10 @@ export class DecorationService extends Disposable implements IDecorationService } public *getDecorationsAtCell(x: number, line: number, layer?: 'bottom' | 'top'): IterableIterator { - let xmin = 0; - let xmax = 0; - let ymin = 0; - let ymax = 0; + let xmin; + let xmax; + let ymin; + let ymax; for (const d of this._decorations.values()) { ymin = d.marker.line; ymax = ymin + (d.options.height ?? 1); diff --git a/src/tsconfig-library-base.json b/src/tsconfig-library-base.json index 00cecc066..e08695d75 100644 --- a/src/tsconfig-library-base.json +++ b/src/tsconfig-library-base.json @@ -4,7 +4,6 @@ "composite": true, "strict": true, "declarationMap": true, - "experimentalDecorators": true, - "downlevelIteration": true + "experimentalDecorators": true } } diff --git a/test/benchmark/tsconfig.json b/test/benchmark/tsconfig.json index 2f24f0762..2ddb57e32 100644 --- a/test/benchmark/tsconfig.json +++ b/test/benchmark/tsconfig.json @@ -8,10 +8,10 @@ "types": [ "../../node_modules/@types/node" ], - "moduleResolution": "node", + "moduleResolution": "nodenext", "strict": false, "target": "es2021", - "module": "commonjs", + "module": "nodenext", "paths": { "common/*": [ "../../src/common/*" ], "browser/*": [ "../../src/browser/*" ], diff --git a/test/playwright/tsconfig.json b/test/playwright/tsconfig.json index f1372e89f..7fea8d812 100644 --- a/test/playwright/tsconfig.json +++ b/test/playwright/tsconfig.json @@ -16,7 +16,7 @@ "strict": true, "noImplicitAny": false, "declaration": true, - "moduleResolution": "node", + "moduleResolution": "nodenext", "paths": { "browser/*": [ "../../src/browser/*" @@ -26,7 +26,7 @@ ] }, "target": "ESNext", - "module": "commonjs", + "module": "nodenext", "composite": true }, "include": [ From 44a27dc958e71de48c90f2a84061ae5e39ce82e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sat, 9 May 2026 17:59:33 +0200 Subject: [PATCH 013/153] make webfont loading tests more resilient --- addons/addon-web-fonts/test/WebFontsAddon.test.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/addons/addon-web-fonts/test/WebFontsAddon.test.ts b/addons/addon-web-fonts/test/WebFontsAddon.test.ts index c94bff84d..ed5e63863 100644 --- a/addons/addon-web-fonts/test/WebFontsAddon.test.ts +++ b/addons/addon-web-fonts/test/WebFontsAddon.test.ts @@ -78,7 +78,17 @@ test.describe('WebFontsAddon', () => { const data = await ctx.page.evaluate(` document.styleSheets[0].insertRule("@font-face {font-family: Kongtext; src: url(/fonts/kongtext.regular.ttf) format('truetype'); unicode-range: U+00A0-00FF}", 0); `); - deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'unloaded' }]); + // safari may have loaded the font, firefox & chrome never load it + if (browser.browserType().name() === 'webkit') { + try { + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loading' }]); + } catch (e) { + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }]); + } + } else { + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'unloaded' }]); + } + // broken case: webfont in ctor without addon usage await ctx.page.evaluate(` From 636baa8d79de7699015331b5a5a75c49e54a8076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sat, 9 May 2026 18:06:59 +0200 Subject: [PATCH 014/153] make webfont loading tests more resilient --- addons/addon-web-fonts/test/WebFontsAddon.test.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/addons/addon-web-fonts/test/WebFontsAddon.test.ts b/addons/addon-web-fonts/test/WebFontsAddon.test.ts index ed5e63863..74b3ca23a 100644 --- a/addons/addon-web-fonts/test/WebFontsAddon.test.ts +++ b/addons/addon-web-fonts/test/WebFontsAddon.test.ts @@ -78,15 +78,16 @@ test.describe('WebFontsAddon', () => { const data = await ctx.page.evaluate(` document.styleSheets[0].insertRule("@font-face {font-family: Kongtext; src: url(/fonts/kongtext.regular.ttf) format('truetype'); unicode-range: U+00A0-00FF}", 0); `); + let fonts = await getDocumentFonts(); // safari may have loaded the font, firefox & chrome never load it if (browser.browserType().name() === 'webkit') { try { - deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loading' }]); + deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'loading' }]); } catch (e) { - deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }]); + deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'unloaded' }]); } } else { - deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'unloaded' }]); + deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'unloaded' }]); } @@ -96,15 +97,16 @@ test.describe('WebFontsAddon', () => { window.helperTerm.open(term.element); `); + fonts = await getDocumentFonts(); // safari loads the font, firefox & chrome dont if (browser.browserType().name() === 'webkit') { try { - deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loading' }]); + deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'loading' }]); } catch (e) { - deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }]); + deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'loaded' }]); } } else { - deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'unloaded' }]); + deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'unloaded' }]); } // good case: addon fixes layout for webfont in ctor From 740fa44ae4079410882833ac73521becd7c9be25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sat, 9 May 2026 18:13:16 +0200 Subject: [PATCH 015/153] make webfont loading tests more resilient --- addons/addon-web-fonts/test/WebFontsAddon.test.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/addons/addon-web-fonts/test/WebFontsAddon.test.ts b/addons/addon-web-fonts/test/WebFontsAddon.test.ts index 74b3ca23a..ce5f7a446 100644 --- a/addons/addon-web-fonts/test/WebFontsAddon.test.ts +++ b/addons/addon-web-fonts/test/WebFontsAddon.test.ts @@ -84,7 +84,11 @@ test.describe('WebFontsAddon', () => { try { deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'loading' }]); } catch (e) { - deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'unloaded' }]); + try { + deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'loaded' }]); + } catch (e) { + deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'unloaded' }]); + } } } else { deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'unloaded' }]); @@ -98,12 +102,16 @@ test.describe('WebFontsAddon', () => { `); fonts = await getDocumentFonts(); - // safari loads the font, firefox & chrome dont + // safari might load the font, firefox & chrome dont if (browser.browserType().name() === 'webkit') { try { deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'loading' }]); } catch (e) { - deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'loaded' }]); + try { + deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'loaded' }]); + } catch (e) { + deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'unloaded' }]); + } } } else { deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'unloaded' }]); From d92ca87cbeb561a5c7e6ea6bf49152816884a788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 10 May 2026 19:32:47 +0200 Subject: [PATCH 016/153] add qoi metrics test --- addons/addon-image/fixture/testimages/dice.qoi | Bin 0 -> 519653 bytes addons/addon-image/src/IIPMetrics.test.ts | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 addons/addon-image/fixture/testimages/dice.qoi diff --git a/addons/addon-image/fixture/testimages/dice.qoi b/addons/addon-image/fixture/testimages/dice.qoi new file mode 100644 index 0000000000000000000000000000000000000000..bbc8154fc8fe3c7ff92103ae1ebd74bdac2ddda7 GIT binary patch literal 519653 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�@5z{f# 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 z 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

3TY=nWt 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 z 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 z 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 literal 0 HcmV?d00001 diff --git a/addons/addon-image/src/IIPMetrics.test.ts b/addons/addon-image/src/IIPMetrics.test.ts index 9fa476bf6..cc381e5cb 100644 --- a/addons/addon-image/src/IIPMetrics.test.ts +++ b/addons/addon-image/src/IIPMetrics.test.ts @@ -30,7 +30,8 @@ const TEST_IMAGES: [string, IMetrics][] = [ ['iphone_hdr_YES.jpg', { mime: 'image/jpeg', width: 3264, height: 2448 }], ['nikon-e950.jpg', { mime: 'image/jpeg', width: 800, height: 600 }], ['agfa-makernotes.jpg', { mime: 'image/jpeg', width: 8, height: 8 }], - ['sony-alpha-6000.jpg', { mime: 'image/jpeg', width: 6000, height: 4000 }] + ['sony-alpha-6000.jpg', { mime: 'image/jpeg', width: 6000, height: 4000 }], + ['dice.qoi', { mime: 'image/qoi', width: 800, height: 600 }], ]; From eb58cefa8e716270a42360a51c55cb1b728ff9db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 10 May 2026 20:34:43 +0200 Subject: [PATCH 017/153] add integration tests for filepart --- addons/addon-image/src/IIPHandler.ts | 8 ++- addons/addon-image/src/ImageAddon.ts | 7 +-- addons/addon-image/test/ImageAddon.test.ts | 61 ++++++++++++++++++++++ 3 files changed, 68 insertions(+), 8 deletions(-) diff --git a/addons/addon-image/src/IIPHandler.ts b/addons/addon-image/src/IIPHandler.ts index 7cacb88a0..249a89f3c 100644 --- a/addons/addon-image/src/IIPHandler.ts +++ b/addons/addon-image/src/IIPHandler.ts @@ -55,7 +55,11 @@ export class IIPHandler implements IOscHandler, IResetHandler { this._qoiDec = new QoiDecoder(DecoderConst.KEEP_DATA); } - public reset(): void {} + public reset(): void { + this._hp.reset(); + this._dec.release(); + this._qoiDec.release(); + } public start(): void { this._aborted = false; @@ -124,7 +128,7 @@ export class IIPHandler implements IOscHandler, IResetHandler { } const scale = this._coreTerminal._core._coreBrowserService?.dpr ?? 1; const report = `\x1b]1337;ReportCellSize=${height.toFixed(3)};${width.toFixed(3)};${scale.toFixed(3)}\x1b\\`; - this._coreTerminal?._core.coreService.triggerDataEvent(report); + this._coreTerminal.input(report, false); return true; } diff --git a/addons/addon-image/src/ImageAddon.ts b/addons/addon-image/src/ImageAddon.ts index 7e0f29d3f..8fd395431 100644 --- a/addons/addon-image/src/ImageAddon.ts +++ b/addons/addon-image/src/ImageAddon.ts @@ -137,11 +137,6 @@ export class ImageAddon implements ITerminalAddon, IImageApi { // enable size reports if (this._opts.enableSizeReports) { - // const windowOptions = terminal.getOption('windowOptions'); - // windowOptions.getWinSizePixels = true; - // windowOptions.getCellSizePixels = true; - // windowOptions.getWinSizeChars = true; - // terminal.setOption('windowOptions', windowOptions); const windowOps = terminal.options.windowOptions ?? {}; windowOps.getWinSizePixels = true; windowOps.getCellSizePixels = true; @@ -260,7 +255,7 @@ export class ImageAddon implements ITerminalAddon, IImageApi { } private _report(s: string): void { - this._terminal?._core.coreService.triggerDataEvent(s); + this._terminal?._core.input(s, false); } private _decset(params: (number | number[])[]): boolean { diff --git a/addons/addon-image/test/ImageAddon.test.ts b/addons/addon-image/test/ImageAddon.test.ts index dc729ec63..f3cde06a8 100644 --- a/addons/addon-image/test/ImageAddon.test.ts +++ b/addons/addon-image/test/ImageAddon.test.ts @@ -290,6 +290,67 @@ test.describe('ImageAddon', () => { }); }); + test.describe('IIP support', () => { + test('size params is optional/informative', async () => { + // too small size param + await ctx.proxy.write(`\x1b]1337;File=inline=1;size=5:${PALETTE_PNG_BASE64}\x07`); + deepStrictEqual(await getOrigSize(1), [640, 80]); + // no size aparm at all + await ctx.proxy.write(`\x1b[10H\x1b]1337;File=inline=1:${PALETTE_QOI_BASE64}\x07`); + deepStrictEqual(await getOrigSize(2), [640, 80]); + }); + test.skip('FilePart support - bytewise', async () => { + // opener + await ctx.proxy.write(`\x1b]1337;MultipartFile=inline=1\x07`); + // byte-wise + for (let i = 0; i < PALETTE_PNG_BASE64.length; ++i) { + await ctx.proxy.write(`\x1b]1337;FilePart=${PALETTE_PNG_BASE64[i]}\x07`); + } + // finalizer + await ctx.proxy.write(`\x1b]1337;FileEnd\x07`); + deepStrictEqual(await getOrigSize(1), [640, 80]); + }); + test('FilePart support - chunks', async () => { + // opener + await ctx.proxy.write(`\x1b]1337;MultipartFile=inline=1\x07`); + // chunks + for (let i = 0; i < PALETTE_PNG_BASE64.length; i += 256) { + await ctx.proxy.write(`\x1b]1337;FilePart=${PALETTE_PNG_BASE64.slice(i, i + 256)}\x07`); + } + // finalizer + await ctx.proxy.write(`\x1b]1337;FileEnd\x07`); + deepStrictEqual(await getOrigSize(1), [640, 80]); + }); + test('FilePart support - edgecases', async () => { + // multiple opener do not harm + for (let i = 0; i < 10; ++i) { + await ctx.proxy.write(`\x1b]1337;MultipartFile=inline=1\x07`); + } + // multiple finalizer do not harm + for (let i = 0; i < 5; ++i) { + await ctx.proxy.write(`\x1b]1337;FileEnd\x07`); + } + // chunks w'o opener are ignored + for (let i = 0; i < PALETTE_PNG_BASE64.length; i += 256) { + await ctx.proxy.write(`\x1b]1337;FilePart=${PALETTE_PNG_BASE64.slice(i, i + 256)}\x07`); + } + await ctx.proxy.write(`\x1b]1337;FileEnd\x07`); + pollFor(ctx.page, 'window.imageAddon._storage._images.size', 0); + }); + test('FilePart support - other IIP sequence cancels chunked image', async () => { + // opener + await ctx.proxy.write(`\x1b]1337;MultipartFile=inline=1\x07`); + // chunks + for (let i = 0; i < PALETTE_PNG_BASE64.length; i += 256) { + await ctx.proxy.write(`\x1b]1337;FilePart=${PALETTE_PNG_BASE64.slice(i, i + 256)}\x07`); + } + // finalizer skipped + // write spinfox.png + await ctx.proxy.write(TESTDATA_IIP[1][0]); + deepStrictEqual(await getOrigSize(1), TESTDATA_IIP[1][1]); + }); + }); + test.describe('IIP support - testimages', () => { test('palette.png', async () => { await ctx.proxy.write(TESTDATA_IIP[0][0]); From a2f321c88740ecaf23bf09b43fe7c61311b2e7d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 10 May 2026 21:08:22 +0200 Subject: [PATCH 018/153] add multipart demo --- .../components/window/addonImageWindow.ts | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/demo/client/components/window/addonImageWindow.ts b/demo/client/components/window/addonImageWindow.ts index a18fb9c98..e5402182a 100644 --- a/demo/client/components/window/addonImageWindow.ts +++ b/demo/client/components/window/addonImageWindow.ts @@ -65,7 +65,8 @@ export class AddonImageWindow extends BaseWindow implements IControlWindow { const dtIip = document.createElement('dt'); dtIip.textContent = 'IIP (iTerm)'; dlIip.appendChild(dtIip); - this._addDdWithButton(dlIip, 'image-demo3', 'palette'); + this._addDdWithButton(dlIip, 'image-demo3', 'palette (png File)'); + this._addDdWithButton(dlIip, 'image-demo4', 'dice (qoi MultipartFile)'); container.appendChild(dlIip); // Kitty demos @@ -141,6 +142,21 @@ export class AddonImageWindow extends BaseWindow implements IControlWindow { this._terminal.write(`\x1b]1337;File=inline=1;size=${data.length}:${btoa(sdata)}\x1b\\`); }); + const iipDemoMulti = (url: string) => () => fetch(url) + .then(resp => resp.arrayBuffer()) + .then(buffer => { + const data = new Uint8Array(buffer); + let sdata = ''; + for (let i = 0; i < data.length; ++i) sdata += String.fromCharCode(data[i]); + const encoded = btoa(sdata); + this._terminal.write('\r\n'); + this._terminal.write(`\x1b]1337;MultipartFile=inline=1\x1b\\`); + for (let i = 0; i < encoded.length; i += 100) { + this._terminal.write(`\x1b]1337;FilePart=${encoded.slice(i, i + 100)}\x1b\\`); + } + this._terminal.write(`\x1b]1337;FileEnd\x1b\\`); + }); + const kittyDemo = (url: string) => () => fetch(url) .then(resp => resp.arrayBuffer()) .then(buffer => { @@ -158,6 +174,8 @@ export class AddonImageWindow extends BaseWindow implements IControlWindow { sixelDemo('https://raw.githubusercontent.com/jerch/node-sixel/master/testfiles/test2.sixel')); document.getElementById('image-demo3')!.addEventListener('click', iipDemo('https://raw.githubusercontent.com/jerch/node-sixel/master/palette.png')); + document.getElementById('image-demo4')!.addEventListener('click', + iipDemoMulti('https://raw.githubusercontent.com/jerch/xterm-wasm-parts/master/fixtures/qoi/dice.qoi')); document.getElementById('image-demo-kitty1')!.addEventListener('click', kittyDemo('https://raw.githubusercontent.com/jerch/node-sixel/master/palette.png')); From f82a8029d257e2de3292b1f226534544fa5b3d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 10 May 2026 21:27:52 +0200 Subject: [PATCH 019/153] fix README --- addons/addon-image/README.md | 51 ++++++++++++++---------------------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/addons/addon-image/README.md b/addons/addon-image/README.md index a18ce18bb..80c88de8e 100644 --- a/addons/addon-image/README.md +++ b/addons/addon-image/README.md @@ -1,6 +1,7 @@ ## @xterm/addon-image -Inline image output in xterm.js. Supports SIXEL and iTerm's inline image protocol (IIP). +Inline image output in xterm.js. Supports SIXEL, iTerm's inline image protocol (IIP) +and partially Kitty's terminal graphics prtocol (TGP). ![](fixture/example.png) @@ -43,9 +44,6 @@ terminal.loadAddon(imageAddon); ### General Notes -- *IMPORTANT:* The worker approach as done in previous releases got removed. - The addon contructor no longer expects a worker path as first argument. - - By default the addon will activate these `windowOptions` reports on the terminal: - getWinSizePixels (CSI 14 t) - getCellSizePixels (CSI 16 t) @@ -73,7 +71,7 @@ terminal.loadAddon(imageAddon); - **Cursor Positioning** If scrolling is set, the cursor will be placed at the first image column of the last image row (VT340 mode). - Other cursor positioning modes as used by xterm or mintty are not supported. + Other cursor positioning modes as used by xterm or mintty are not supported for sixel. - **SIXEL Palette Handling** By default the addon limits the palette size to 256 registers (as demanded by the DEC specification). @@ -100,17 +98,24 @@ terminal.loadAddon(imageAddon); Set by default, change it with `{iipSupport: true}`. The IIP implementation has the following features / restrictions (sequence will silently fail for unmet conditions): - - Supported formats: PNG, JPEG and GIF + - Supported formats: PNG, JPEG, GIF and QOI - No animation support. - Image type hinting is not supported (always deducted from data header). - File download is not supported. - Filename gets parsed but not used. - Strict base64 handling as of RFC4648 §4 (standard alphabet, optional padding, no separator bytes allowed). - - Payload size may not exceed CEIL(sizeParameter * 4 / 3). - Image scaling beyond terminal viewport size is allowed (e.g. `width=200%`). - Image pixel size is restricted by `pixelLimit` (pre- and post resizing). - - Size parameter is restricted by `iipSizeLimit`. - - Cursor positioning behaves the same as for sixel (see above). + - Playload size is restricted by `iipSizeLimit`. + - size parameter is not mandatory anymore. + - Cursor gets positioned at the next cell of the last image line. + - ReportCellSize sequence is supported. + - Hi-res display support is currently limited (image gets blurred). + +- **Kitty Graphics Support (TGP)** + Set by default, change it with `{kittySupport: true}`. + + Note that the kitty graphics support is still WIP. ### Storage and Drawing Settings @@ -127,6 +132,7 @@ The addon exposes two properties to interact with the storage limits at runtime: By default the addon will show a placeholder pattern for evicted images that are still part of the terminal (e.g. in the scrollback). The pattern can be deactivated by toggling `showPlaceholder`. + ### Image Data Retrieval The addon provides the following API endpoints to retrieve raw image data as canvas: @@ -140,6 +146,7 @@ The addon provides the following API endpoints to retrieve raw image data as can The buffer position is the 0-based absolute index (including scrollback at top). Note that the canvas gets created and data copied over for every call, thus it is not suitable for performance critical actions. + ### Memory Usage The addon does most image processing in Javascript and therefore can occupy a rather big amount of memory. To get an idea where the memory gets eaten, lets look at the data flow and processing steps: @@ -180,8 +187,8 @@ _How can I adjust the memory usage?_ A constructor setting, thus you would have to anticipate, whether (multiple) terminals in your page gonna do lots of concurrent decoding. Since this is normally not the case and the memory usage is only temporarily peaking, a rather high value should work even with multiple terminals in one page. - `storageLimit` A constructor and a runtime setting. In conjunction with `storageUsage` you can do runtime checks and adjust the limit to your needs. If you have to lower the limit below the current usage, images will be removed in FIFO order and may turn into a placeholder in the terminal's scrollback (if `showPlaceholder` is set). When adjusting keep in mind to leave enough room for memory peaking for decoding. -- `sixelSizeLimit` - A constructor setting. This has only a small impact on peaking memory during decoding. It is meant to avoid processing of overly big or broken SIXEL sequences at an earlier phase, thus may stop the decoder from entering its memory intensive task for potentially invalid data. +- `sixelSizeLimit | iipSizeLimit | kittySizeLimit` + Constructor settings. This has only a small impact on peaking memory during decoding. It is meant to avoid processing of overly big or broken image sequences at an earlier phase, thus may stop the invoked decoders from entering memory intensive tasks for potentially invalid data. ### Terminal Interaction @@ -211,23 +218,5 @@ _How can I adjust the memory usage?_ ### Status -- Sixel support and image handling in xterm.js is considered beta quality. -- IIP support is in alpha stage. Please file a bug for any awkwardities. - - -### Changelog - -- 0.5.0 integrate with xtermjs base repo (at v0.4.3) -- 0.4.3 defer canvas creation -- 0.4.2 fix image canvas resize -- 0.4.1 compat release for xterm.js 5.2.0 -- 0.4.0 IIP support -- 0.3.1 compat release for xterm.js 5.1.0 -- 0.3.0 important change: worker removed from addon -- 0.2.0 compat release for xterm.js 5.0.0 -- 0.1.3 bugfix: avoid striping -- 0.1.2 bugfix: reset clear flag -- 0.1.1 bugfixes: - - clear sticky image tiles on render - - create folder from bootstrap.sh - - fix peer dependency in package.json +- Sixel and IIP support and image handling in xterm.js is considered beta quality. +- Kitty support is in alpha stage. Please file a bug for any awkwardities or missing features. From 41d9c4080f759e95139aa3094f2dcae7f7f797c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 10:06:23 +0000 Subject: [PATCH 020/153] Bump fast-uri from 3.1.0 to 3.1.2 Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.0 to 3.1.2. - [Release notes](https://github.com/fastify/fast-uri/releases) - [Commits](https://github.com/fastify/fast-uri/compare/v3.1.0...v3.1.2) --- updated-dependencies: - dependency-name: fast-uri dependency-version: 3.1.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index e0ac4e728..9f35e360c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3945,9 +3945,9 @@ "dev": true }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "dev": true, "funding": [ { @@ -3958,7 +3958,8 @@ "type": "opencollective", "url": "https://opencollective.com/fastify" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/fastest-levenshtein": { "version": "1.0.16", From 17e7dafa17af28b1e0aaf9ce789db287546d19a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 10:06:53 +0000 Subject: [PATCH 021/153] Bump axios from 1.15.0 to 1.15.2 Bumps [axios](https://github.com/axios/axios) from 1.15.0 to 1.15.2. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.15.0...v1.15.2) --- updated-dependencies: - dependency-name: axios dependency-version: 1.15.2 dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- addons/addon-ligatures/package.json | 2 +- package-lock.json | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/addons/addon-ligatures/package.json b/addons/addon-ligatures/package.json index fc7a433de..0e06add18 100644 --- a/addons/addon-ligatures/package.json +++ b/addons/addon-ligatures/package.json @@ -37,7 +37,7 @@ "devDependencies": { "@types/lru-cache": "^5.1.0", "@types/opentype.js": "^0.7.0", - "axios": "^1.15.0", + "axios": "^1.15.2", "font-finder": "^1.1.0", "mkdirp": "0.5.5", "yauzl": "^3.2.1" diff --git a/package-lock.json b/package-lock.json index e0ac4e728..a8072f3f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -89,7 +89,7 @@ "devDependencies": { "@types/lru-cache": "^5.1.0", "@types/opentype.js": "^0.7.0", - "axios": "^1.15.0", + "axios": "^1.15.2", "font-finder": "^1.1.0", "mkdirp": "0.5.5", "yauzl": "^3.2.1" @@ -2578,9 +2578,9 @@ "dev": true }, "node_modules/axios": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", - "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.2.tgz", + "integrity": "sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==", "dev": true, "license": "MIT", "dependencies": { From f484584b706d2a91363f17e98c61f07d713f1c34 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 26 May 2026 09:30:25 -0700 Subject: [PATCH 022/153] Speed up decoration cell lookup with a per-line index Refs #5176. forEachDecorationAtCell and getDecorationsAtCell previously scanned every registered decoration on each call. With thousands of search highlights this becomes O(total decorations) per cell and blocks rendering during viewport refreshes. Maintain a Map of buffer line -> decorations that cover that line. Multi-line decorations are indexed on every line in their span, so mixed single-line and multi-line workloads stay fast without falling back to a full scan. Keep the index aligned with buffer mutations: - Register and unregister decorations in the line buckets on add/dispose. - On trim, shift line keys synchronously (before marker handlers run). - On insert/delete, re-index affected decorations in a microtask after marker line updates are applied. Inject IBufferService so line listeners follow the active buffer. Add unit tests for trim, dispose-on-trim, insert, and dense single-line plus multi-line lookup. Add DecorationService.benchmark.ts to measure sparse hits and viewport-sized grid scans with 20k decorations. Co-authored-by: Cursor --- src/common/services/DecorationService.test.ts | 105 +++++++++- src/common/services/DecorationService.ts | 185 ++++++++++++++++-- src/common/services/Services.ts | 2 + test/benchmark/DecorationService.benchmark.ts | 109 +++++++++++ 4 files changed, 376 insertions(+), 25 deletions(-) create mode 100644 test/benchmark/DecorationService.benchmark.ts diff --git a/src/common/services/DecorationService.test.ts b/src/common/services/DecorationService.test.ts index 95c7c0e24..b70232f36 100644 --- a/src/common/services/DecorationService.test.ts +++ b/src/common/services/DecorationService.test.ts @@ -8,7 +8,9 @@ import { DecorationService } from './DecorationService'; import { IMarker } from 'common/Types'; import { Disposable } from 'common/Lifecycle'; import { Emitter } from 'common/Event'; -import { MockLogService } from 'common/TestUtils.test'; +import { MockLogService, MockBufferService, MockOptionsService } from 'common/TestUtils.test'; +import { Buffer } from 'common/buffer/Buffer'; +import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; function createFakeMarker(line: number): IMarker { return Object.freeze(new class extends Disposable { @@ -19,11 +21,16 @@ function createFakeMarker(line: number): IMarker { }()); } +function createDecorationService(): DecorationService { + const bufferService = new MockBufferService(80, 24, new MockOptionsService()); + return new DecorationService(new MockLogService(), bufferService); +} + const fakeMarker: IMarker = createFakeMarker(1); describe('DecorationService', () => { it('should set isDisposed to true after dispose', () => { - const service = new DecorationService(new MockLogService()); + const service = createDecorationService(); const decoration = service.registerDecoration({ marker: fakeMarker }); @@ -35,7 +42,7 @@ describe('DecorationService', () => { describe('forEachDecorationAtCell', () => { it('should find decoration at its marker line', () => { - const service = new DecorationService(new MockLogService()); + const service = createDecorationService(); const decoration = service.registerDecoration({ marker: createFakeMarker(5), width: 10 @@ -48,7 +55,7 @@ describe('DecorationService', () => { }); it('should find decoration with height > 1 on subsequent lines', () => { - const service = new DecorationService(new MockLogService()); + const service = createDecorationService(); const decoration = service.registerDecoration({ marker: createFakeMarker(5), width: 10, @@ -74,7 +81,7 @@ describe('DecorationService', () => { }); it('should not find decoration outside its x range', () => { - const service = new DecorationService(new MockLogService()); + const service = createDecorationService(); const decoration = service.registerDecoration({ marker: createFakeMarker(5), x: 5, @@ -99,11 +106,35 @@ describe('DecorationService', () => { service.forEachDecorationAtCell(8, 5, undefined, d => foundAtX8.push(d)); assert.strictEqual(foundAtX8.length, 0); }); + + it('should find multi-line decoration when single-line decorations exist on other lines', () => { + const bufferService = new MockBufferService(80, 24, new MockOptionsService()); + const serviceWithBuffer = new DecorationService(new MockLogService(), bufferService); + const buffer = bufferService.buffer; + (buffer as Buffer).fillViewportRows(); + + for (let i = 0; i < 100; i++) { + serviceWithBuffer.registerDecoration({ + marker: buffer.addMarker(i), + width: 5 + }); + } + const multiLine = serviceWithBuffer.registerDecoration({ + marker: buffer.addMarker(10), + width: 10, + height: 3 + }); + assert.ok(multiLine); + + const found: typeof multiLine[] = []; + serviceWithBuffer.forEachDecorationAtCell(0, 11, undefined, d => found.push(d)); + assert.include(found, multiLine); + }); }); describe('getDecorationsAtCell', () => { it('should find decoration with height > 1 on subsequent lines', () => { - const service = new DecorationService(new MockLogService()); + const service = createDecorationService(); const decoration = service.registerDecoration({ marker: createFakeMarker(5), width: 10, @@ -117,4 +148,66 @@ describe('DecorationService', () => { assert.strictEqual([...service.getDecorationsAtCell(0, 8)].length, 0); }); }); + + describe('line index maintenance', () => { + it('should keep lookups correct after buffer trim', () => { + const bufferService = new MockBufferService(80, 5, new MockOptionsService({ scrollback: 0 })); + const service = new DecorationService(new MockLogService(), bufferService); + const buffer = bufferService.buffer; + (buffer as Buffer).fillViewportRows(); + + const marker = buffer.addMarker(buffer.lines.length - 1); + const decoration = service.registerDecoration({ marker, width: 10 }); + assert.ok(decoration); + + buffer.lines.onTrimEmitter.fire(1); + + const found: typeof decoration[] = []; + service.forEachDecorationAtCell(0, marker.line, undefined, d => found.push(d)); + assert.strictEqual(found.length, 1); + }); + + it('should remove decoration from line index when marker is trimmed off buffer', () => { + const bufferService = new MockBufferService(80, 5, new MockOptionsService({ scrollback: 0 })); + const service = new DecorationService(new MockLogService(), bufferService); + const buffer = bufferService.buffer; + (buffer as Buffer).fillViewportRows(); + + const marker = buffer.addMarker(0); + const decoration = service.registerDecoration({ marker, width: 10 }); + assert.ok(decoration); + + buffer.lines.onTrimEmitter.fire(1); + assert.isTrue(marker.isDisposed); + assert.isTrue(decoration!.isDisposed); + + const found: typeof decoration[] = []; + service.forEachDecorationAtCell(0, 0, undefined, d => found.push(d)); + assert.strictEqual(found.length, 0); + }); + + it('should keep multi-line decoration indexed after line insert', async () => { + const bufferService = new MockBufferService(80, 10, new MockOptionsService({ scrollback: 100 })); + const service = new DecorationService(new MockLogService(), bufferService); + const buffer = bufferService.buffer; + (buffer as Buffer).fillViewportRows(); + + const marker = buffer.addMarker(3); + const decoration = service.registerDecoration({ marker, width: 10, height: 3 }); + assert.ok(decoration); + + buffer.lines.splice(5, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA)); + await new Promise(resolve => queueMicrotask(resolve)); + + const foundOnSpan: typeof decoration[] = []; + for (let line = marker.line; line < marker.line + 3; line++) { + service.forEachDecorationAtCell(0, line, undefined, d => foundOnSpan.push(d)); + } + assert.include(foundOnSpan, decoration); + + const foundOutsideSpan: typeof decoration[] = []; + service.forEachDecorationAtCell(0, marker.line + 3, undefined, d => foundOutsideSpan.push(d)); + assert.strictEqual(foundOutsideSpan.length, 0); + }); + }); }); diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts index 133e1a985..2bac9717c 100644 --- a/src/common/services/DecorationService.ts +++ b/src/common/services/DecorationService.ts @@ -4,18 +4,17 @@ */ import { css } from 'common/Color'; -import { Disposable, DisposableStore, toDisposable } from 'common/Lifecycle'; -import { IDecorationService, IInternalDecoration, ILogService } from 'common/services/Services'; +import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'common/Lifecycle'; +import { IBufferService, IDecorationService, IInternalDecoration, ILogService } from 'common/services/Services'; import { SortedList } from 'common/SortedList'; import { IColor } from 'common/Types'; import { IDecoration, IDecorationOptions, IMarker } from '@xterm/xterm'; import { Emitter } from 'common/Event'; +import type { IDeleteEvent, IInsertEvent } from 'common/CircularList'; // Work variables to avoid garbage collection let $xmin = 0; let $xmax = 0; -let $ymin = 0; -let $ymax = 0; export class DecorationService extends Disposable implements IDecorationService { public serviceBrand: any; @@ -27,6 +26,14 @@ export class DecorationService extends Disposable implements IDecorationService */ private readonly _decorations: SortedList; + /** + * Decorations indexed by buffer line. Multi-line decorations are present in every line bucket + * they span so cell lookup only iterates decorations relevant to that line. + */ + private readonly _decorationsByLine: Map = new Map(); + + private readonly _bufferLineListeners = this._register(new MutableDisposable()); + private readonly _onDecorationRegistered = this._register(new Emitter()); public readonly onDecorationRegistered = this._onDecorationRegistered.event; private readonly _onDecorationRemoved = this._register(new Emitter()); @@ -34,12 +41,17 @@ export class DecorationService extends Disposable implements IDecorationService public get decorations(): IterableIterator { return this._decorations.values(); } - constructor(@ILogService private readonly _logService: ILogService) { + constructor( + @ILogService private readonly _logService: ILogService, + @IBufferService private readonly _bufferService: IBufferService + ) { super(); this._decorations = new SortedList(e => e?.marker.line, this._logService); this._register(toDisposable(() => this.reset())); + this._register(this._bufferService.buffers.onBufferActivate(() => this._attachBufferLineListeners())); + this._attachBufferLineListeners(); } public registerDecoration(options: IDecorationOptions): IDecoration | undefined { @@ -53,12 +65,14 @@ export class DecorationService extends Disposable implements IDecorationService listener.dispose(); if (decoration) { if (this._decorations.delete(decoration)) { + this._removeFromLineIndex(decoration); this._onDecorationRemoved.fire(decoration); } markerDispose.dispose(); } }); this._decorations.insert(decoration); + this._addToLineIndex(decoration); this._onDecorationRegistered.fire(decoration); } return decoration; @@ -69,19 +83,17 @@ export class DecorationService extends Disposable implements IDecorationService d.dispose(); } this._decorations.clear(); + this._decorationsByLine.clear(); } public *getDecorationsAtCell(x: number, line: number, layer?: 'bottom' | 'top'): IterableIterator { + const bucket = this._decorationsByLine.get(line); + if (!bucket) { + return; + } let xmin = 0; let xmax = 0; - let ymin = 0; - let ymax = 0; - for (const d of this._decorations.values()) { - ymin = d.marker.line; - ymax = ymin + (d.options.height ?? 1); - if (line < ymin || line >= ymax) { - continue; - } + for (const d of bucket) { xmin = d.options.x ?? 0; xmax = xmin + (d.options.width ?? 1); if (x >= xmin && x < xmax && (!layer || (d.options.layer ?? 'bottom') === layer)) { @@ -91,12 +103,11 @@ export class DecorationService extends Disposable implements IDecorationService } public forEachDecorationAtCell(x: number, line: number, layer: 'bottom' | 'top' | undefined, callback: (decoration: IInternalDecoration) => void): void { - for (const d of this._decorations.values()) { - $ymin = d.marker.line; - $ymax = $ymin + (d.options.height ?? 1); - if (line < $ymin || line >= $ymax) { - continue; - } + const bucket = this._decorationsByLine.get(line); + if (!bucket) { + return; + } + for (const d of bucket) { $xmin = d.options.x ?? 0; $xmax = $xmin + (d.options.width ?? 1); if (x >= $xmin && x < $xmax && (!layer || (d.options.layer ?? 'bottom') === layer)) { @@ -104,12 +115,147 @@ export class DecorationService extends Disposable implements IDecorationService } } } + + private _attachBufferLineListeners(): void { + const store = new DisposableStore(); + this._bufferLineListeners.value = store; + const lines = this._bufferService.buffer.lines; + store.add(lines.onTrim(amount => this._handleBufferLinesTrim(amount))); + store.add(lines.onInsert(event => this._handleBufferLinesInsert(event))); + store.add(lines.onDelete(event => this._handleBufferLinesDelete(event))); + } + + private _getDecorationHeight(decoration: IInternalDecoration): number { + return decoration.options.height ?? 1; + } + + private _addToLineIndex(decoration: IInternalDecoration): void { + const start = decoration.marker.line; + if (start < 0) { + return; + } + decoration._indexedStartLine = start; + const height = this._getDecorationHeight(decoration); + for (let line = start; line < start + height; line++) { + let bucket = this._decorationsByLine.get(line); + if (!bucket) { + bucket = []; + this._decorationsByLine.set(line, bucket); + } + bucket.push(decoration); + } + } + + private _removeFromLineIndex(decoration: IInternalDecoration): void { + const start = decoration._indexedStartLine; + const height = this._getDecorationHeight(decoration); + for (let line = start; line < start + height; line++) { + const bucket = this._decorationsByLine.get(line); + if (!bucket) { + continue; + } + const index = bucket.indexOf(decoration); + if (index !== -1) { + bucket.splice(index, 1); + } + if (bucket.length === 0) { + this._decorationsByLine.delete(line); + } + } + } + + private _reindexDecoration(decoration: IInternalDecoration): void { + this._removeFromLineIndex(decoration); + if (!decoration.marker.isDisposed && decoration.marker.line >= 0) { + this._addToLineIndex(decoration); + } + } + + private _lineIndexSyncCallbacks: (() => void)[] = []; + + /** Re-index after marker line updates (buffer listeners may run before markers). */ + private _scheduleLineIndexSync(callback: () => void): void { + this._lineIndexSyncCallbacks.push(callback); + if (this._lineIndexSyncCallbacks.length > 1) { + return; + } + queueMicrotask(() => { + const callbacks = this._lineIndexSyncCallbacks; + this._lineIndexSyncCallbacks = []; + for (const cb of callbacks) { + cb(); + } + }); + } + + private _handleBufferLinesTrim(amount: number): void { + if (amount <= 0) { + return; + } + const newMap = new Map(); + for (const [line, bucket] of this._decorationsByLine) { + const newLine = line - amount; + if (newLine < 0) { + continue; + } + const existing = newMap.get(newLine); + if (existing) { + existing.push(...bucket); + } else { + newMap.set(newLine, bucket.slice()); + } + } + this._decorationsByLine.clear(); + for (const [line, bucket] of newMap) { + this._decorationsByLine.set(line, bucket); + } + for (const d of this._decorations.values()) { + if (!d.marker.isDisposed) { + d._indexedStartLine -= amount; + } + } + } + + private _handleBufferLinesInsert(event: IInsertEvent): void { + this._scheduleLineIndexSync(() => { + for (const d of this._decorations.values()) { + if (d.marker.isDisposed) { + continue; + } + const height = this._getDecorationHeight(d); + if (d.marker.line + height > event.index) { + this._reindexDecoration(d); + } + } + }); + } + + private _handleBufferLinesDelete(event: IDeleteEvent): void { + const deleteEnd = event.index + event.amount; + this._scheduleLineIndexSync(() => { + for (const d of this._decorations.values()) { + if (d.marker.isDisposed) { + continue; + } + const start = d.marker.line; + const end = start + this._getDecorationHeight(d); + if (start >= deleteEnd) { + this._reindexDecoration(d); + } else if (start < event.index && end > event.index) { + this._reindexDecoration(d); + } + } + }); + } } class Decoration extends DisposableStore implements IInternalDecoration { public readonly marker: IMarker; public element: HTMLElement | undefined; + /** Start line used for line-index removal when marker.line is cleared on dispose. */ + public _indexedStartLine: number; + public readonly onRenderEmitter = this.add(new Emitter()); public readonly onRender = this.onRenderEmitter.event; private readonly _onDispose = this.add(new Emitter()); @@ -144,6 +290,7 @@ class Decoration extends DisposableStore implements IInternalDecoration { ) { super(); this.marker = options.marker; + this._indexedStartLine = options.marker.line; if (this.options.overviewRulerOptions && !this.options.overviewRulerOptions.position) { this.options.overviewRulerOptions.position = 'full'; } diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index a15dc23dc..a858193c3 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -396,4 +396,6 @@ export interface IInternalDecoration extends IDecoration { readonly backgroundColorRGB: IColor | undefined; readonly foregroundColorRGB: IColor | undefined; readonly onRenderEmitter: Emitter; + /** @internal Start line for line-index removal; kept in sync on buffer line shifts. */ + _indexedStartLine: number; } diff --git a/test/benchmark/DecorationService.benchmark.ts b/test/benchmark/DecorationService.benchmark.ts new file mode 100644 index 000000000..1a2033de1 --- /dev/null +++ b/test/benchmark/DecorationService.benchmark.ts @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2026 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { perfContext, before, RuntimeCase } from 'xterm-benchmark'; +import { DecorationService } from 'common/services/DecorationService'; +import { MockLogService, MockBufferService, MockOptionsService } from 'common/TestUtils.test'; +const enum Constants { + COLS = 80, + ROWS = 30, + SINGLE_LINE_DECORATION_COUNT = 20_000, + MULTI_LINE_DECORATION_COUNT = 19_999, + MULTI_LINE_HEIGHT = 2, + VIEWPORT_SCAN_ITERATIONS = 20 +} + +function registerSingleLineDecorations(service: DecorationService, bufferService: MockBufferService, count: number): void { + const buffer = bufferService.buffer; + for (let i = 0; i < count; i++) { + const line = i % 5000; + const marker = buffer.addMarker(line); + service.registerDecoration({ marker, width: Constants.COLS }); + } +} + +function registerMixedDecorations(service: DecorationService, bufferService: MockBufferService): void { + const buffer = bufferService.buffer; + for (let i = 0; i < Constants.MULTI_LINE_DECORATION_COUNT; i++) { + const line = i % 5000; + const marker = buffer.addMarker(line); + service.registerDecoration({ marker, width: Constants.COLS }); + } + const multiLineMarker = buffer.addMarker(10); + service.registerDecoration({ + marker: multiLineMarker, + width: Constants.COLS, + height: Constants.MULTI_LINE_HEIGHT + }); +} + +function scanVisibleGrid(service: DecorationService, rows: number, cols: number): number { + let hitCount = 0; + for (let row = 0; row < rows; row++) { + for (let col = 0; col < cols; col++) { + service.forEachDecorationAtCell(col, row, undefined, () => { + hitCount++; + }); + } + } + return hitCount; +} + +perfContext('DecorationService.forEachDecorationAtCell', () => { + perfContext('single-line dense / sparse line hit', () => { + let service: DecorationService; + let bufferService: MockBufferService; + before(() => { + bufferService = new MockBufferService(Constants.COLS, Constants.ROWS, new MockOptionsService()); + service = new DecorationService(new MockLogService(), bufferService); + registerSingleLineDecorations(service, bufferService, Constants.SINGLE_LINE_DECORATION_COUNT); + }); + new RuntimeCase('', () => { + let hitCount = 0; + for (let i = 0; i < Constants.VIEWPORT_SCAN_ITERATIONS; i++) { + service.forEachDecorationAtCell(0, 0, undefined, () => { + hitCount++; + }); + } + return { payloadSize: Constants.VIEWPORT_SCAN_ITERATIONS, hitCount }; + }, { fork: false }).showAverageRuntime(); + }); + + perfContext('mixed single-line + multi-line / sparse line hit', () => { + let service: DecorationService; + let bufferService: MockBufferService; + before(() => { + bufferService = new MockBufferService(Constants.COLS, Constants.ROWS, new MockOptionsService()); + service = new DecorationService(new MockLogService(), bufferService); + registerMixedDecorations(service, bufferService); + }); + new RuntimeCase('', () => { + let hitCount = 0; + for (let i = 0; i < Constants.VIEWPORT_SCAN_ITERATIONS; i++) { + service.forEachDecorationAtCell(0, 10, undefined, () => { + hitCount++; + }); + } + return { payloadSize: Constants.VIEWPORT_SCAN_ITERATIONS, hitCount }; + }, { fork: false }).showAverageRuntime(); + }); + + perfContext('viewport grid scan', () => { + let service: DecorationService; + let bufferService: MockBufferService; + before(() => { + bufferService = new MockBufferService(Constants.COLS, Constants.ROWS, new MockOptionsService()); + service = new DecorationService(new MockLogService(), bufferService); + registerSingleLineDecorations(service, bufferService, Constants.SINGLE_LINE_DECORATION_COUNT); + }); + new RuntimeCase('', () => { + let totalHits = 0; + for (let i = 0; i < Constants.VIEWPORT_SCAN_ITERATIONS; i++) { + totalHits += scanVisibleGrid(service, Constants.ROWS, Constants.COLS); + } + return { payloadSize: Constants.VIEWPORT_SCAN_ITERATIONS * Constants.ROWS * Constants.COLS, totalHits }; + }, { fork: false }).showAverageRuntime(); + }); +}); From 9e72a4b6d3376609216d6ba34624f9fbc0e6bce6 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 26 May 2026 09:47:11 -0700 Subject: [PATCH 023/153] Refactor decoration line index into DecorationLineCache Extract the per-line lookup index from DecorationService into an exported DecorationLineCache class in the same module, with DecorationService listed first for readability. Optimize buffer insert/delete maintenance to shift map keys in O(unique indexed lines) instead of full re-indexing every affected decoration. Only multi-line decorations that span the mutation point get a full re-index. Add a direct DecorationLineCache unit test in DecorationService.test.ts. Co-authored-by: Cursor --- src/common/services/DecorationService.test.ts | 9 +- src/common/services/DecorationService.ts | 187 +++++++++++++----- 2 files changed, 145 insertions(+), 51 deletions(-) diff --git a/src/common/services/DecorationService.test.ts b/src/common/services/DecorationService.test.ts index b70232f36..e3a6c2f04 100644 --- a/src/common/services/DecorationService.test.ts +++ b/src/common/services/DecorationService.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { DecorationService } from './DecorationService'; +import { DecorationLineCache, DecorationService } from './DecorationService'; import { IMarker } from 'common/Types'; import { Disposable } from 'common/Lifecycle'; import { Emitter } from 'common/Event'; @@ -149,6 +149,13 @@ describe('DecorationService', () => { }); }); + describe('DecorationLineCache', () => { + it('should return undefined for lines with no indexed decorations', () => { + const cache = new DecorationLineCache(); + assert.isUndefined(cache.getDecorationsOnLine(0)); + }); + }); + describe('line index maintenance', () => { it('should keep lookups correct after buffer trim', () => { const bufferService = new MockBufferService(80, 5, new MockOptionsService({ scrollback: 0 })); diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts index 2bac9717c..645a63818 100644 --- a/src/common/services/DecorationService.ts +++ b/src/common/services/DecorationService.ts @@ -3,14 +3,14 @@ * @license MIT */ +import type { IDeleteEvent, IInsertEvent } from 'common/CircularList'; import { css } from 'common/Color'; import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'common/Lifecycle'; import { IBufferService, IDecorationService, IInternalDecoration, ILogService } from 'common/services/Services'; import { SortedList } from 'common/SortedList'; -import { IColor } from 'common/Types'; +import { IColor, ICircularList } from 'common/Types'; import { IDecoration, IDecorationOptions, IMarker } from '@xterm/xterm'; import { Emitter } from 'common/Event'; -import type { IDeleteEvent, IInsertEvent } from 'common/CircularList'; // Work variables to avoid garbage collection let $xmin = 0; @@ -26,13 +26,7 @@ export class DecorationService extends Disposable implements IDecorationService */ private readonly _decorations: SortedList; - /** - * Decorations indexed by buffer line. Multi-line decorations are present in every line bucket - * they span so cell lookup only iterates decorations relevant to that line. - */ - private readonly _decorationsByLine: Map = new Map(); - - private readonly _bufferLineListeners = this._register(new MutableDisposable()); + private readonly _lineCache = this._register(new DecorationLineCache()); private readonly _onDecorationRegistered = this._register(new Emitter()); public readonly onDecorationRegistered = this._onDecorationRegistered.event; @@ -50,8 +44,10 @@ export class DecorationService extends Disposable implements IDecorationService this._decorations = new SortedList(e => e?.marker.line, this._logService); this._register(toDisposable(() => this.reset())); - this._register(this._bufferService.buffers.onBufferActivate(() => this._attachBufferLineListeners())); - this._attachBufferLineListeners(); + this._register(this._bufferService.buffers.onBufferActivate(() => { + this._lineCache.attachToBufferLines(this._bufferService.buffer.lines); + })); + this._lineCache.attachToBufferLines(this._bufferService.buffer.lines); } public registerDecoration(options: IDecorationOptions): IDecoration | undefined { @@ -65,14 +61,14 @@ export class DecorationService extends Disposable implements IDecorationService listener.dispose(); if (decoration) { if (this._decorations.delete(decoration)) { - this._removeFromLineIndex(decoration); + this._lineCache.remove(decoration); this._onDecorationRemoved.fire(decoration); } markerDispose.dispose(); } }); this._decorations.insert(decoration); - this._addToLineIndex(decoration); + this._lineCache.add(decoration); this._onDecorationRegistered.fire(decoration); } return decoration; @@ -83,11 +79,11 @@ export class DecorationService extends Disposable implements IDecorationService d.dispose(); } this._decorations.clear(); - this._decorationsByLine.clear(); + this._lineCache.clear(); } public *getDecorationsAtCell(x: number, line: number, layer?: 'bottom' | 'top'): IterableIterator { - const bucket = this._decorationsByLine.get(line); + const bucket = this._lineCache.getDecorationsOnLine(line); if (!bucket) { return; } @@ -103,7 +99,7 @@ export class DecorationService extends Disposable implements IDecorationService } public forEachDecorationAtCell(x: number, line: number, layer: 'bottom' | 'top' | undefined, callback: (decoration: IInternalDecoration) => void): void { - const bucket = this._decorationsByLine.get(line); + const bucket = this._lineCache.getDecorationsOnLine(line); if (!bucket) { return; } @@ -115,11 +111,44 @@ export class DecorationService extends Disposable implements IDecorationService } } } +} - private _attachBufferLineListeners(): void { +/** + * Per-logical-line index of decorations for fast cell lookup. + * + * Keys are marker.line coordinates (logical buffer lines), not CircularList ring slots. + * Multi-line decorations appear in every line bucket they span. The index is kept aligned + * with marker.line updates via buffer line trim/insert/delete events. + */ +export class DecorationLineCache extends Disposable { + private readonly _decorationsByLine: Map = new Map(); + private readonly _decorations = new Set(); + private readonly _bufferLineListeners = this._register(new MutableDisposable()); + + private _lineIndexSyncCallbacks: (() => void)[] = []; + + public clear(): void { + this._decorationsByLine.clear(); + this._decorations.clear(); + } + + public add(decoration: IInternalDecoration): void { + this._decorations.add(decoration); + this._addToLineBuckets(decoration); + } + + public remove(decoration: IInternalDecoration): void { + this._decorations.delete(decoration); + this._removeFromLineBuckets(decoration); + } + + public getDecorationsOnLine(line: number): ReadonlyArray | undefined { + return this._decorationsByLine.get(line); + } + + public attachToBufferLines(lines: ICircularList): void { const store = new DisposableStore(); this._bufferLineListeners.value = store; - const lines = this._bufferService.buffer.lines; store.add(lines.onTrim(amount => this._handleBufferLinesTrim(amount))); store.add(lines.onInsert(event => this._handleBufferLinesInsert(event))); store.add(lines.onDelete(event => this._handleBufferLinesDelete(event))); @@ -129,7 +158,7 @@ export class DecorationService extends Disposable implements IDecorationService return decoration.options.height ?? 1; } - private _addToLineIndex(decoration: IInternalDecoration): void { + private _addToLineBuckets(decoration: IInternalDecoration): void { const start = decoration.marker.line; if (start < 0) { return; @@ -146,7 +175,7 @@ export class DecorationService extends Disposable implements IDecorationService } } - private _removeFromLineIndex(decoration: IInternalDecoration): void { + private _removeFromLineBuckets(decoration: IInternalDecoration): void { const start = decoration._indexedStartLine; const height = this._getDecorationHeight(decoration); for (let line = start; line < start + height; line++) { @@ -165,14 +194,12 @@ export class DecorationService extends Disposable implements IDecorationService } private _reindexDecoration(decoration: IInternalDecoration): void { - this._removeFromLineIndex(decoration); + this._removeFromLineBuckets(decoration); if (!decoration.marker.isDisposed && decoration.marker.line >= 0) { - this._addToLineIndex(decoration); + this._addToLineBuckets(decoration); } } - private _lineIndexSyncCallbacks: (() => void)[] = []; - /** Re-index after marker line updates (buffer listeners may run before markers). */ private _scheduleLineIndexSync(callback: () => void): void { this._lineIndexSyncCallbacks.push(callback); @@ -209,7 +236,7 @@ export class DecorationService extends Disposable implements IDecorationService for (const [line, bucket] of newMap) { this._decorationsByLine.set(line, bucket); } - for (const d of this._decorations.values()) { + for (const d of this._decorations) { if (!d.marker.isDisposed) { d._indexedStartLine -= amount; } @@ -217,35 +244,95 @@ export class DecorationService extends Disposable implements IDecorationService } private _handleBufferLinesInsert(event: IInsertEvent): void { - this._scheduleLineIndexSync(() => { - for (const d of this._decorations.values()) { - if (d.marker.isDisposed) { - continue; - } - const height = this._getDecorationHeight(d); - if (d.marker.line + height > event.index) { - this._reindexDecoration(d); - } - } - }); + this._scheduleLineIndexSync(() => this._applyBufferLinesInsert(event)); } private _handleBufferLinesDelete(event: IDeleteEvent): void { - const deleteEnd = event.index + event.amount; - this._scheduleLineIndexSync(() => { - for (const d of this._decorations.values()) { - if (d.marker.isDisposed) { - continue; - } - const start = d.marker.line; - const end = start + this._getDecorationHeight(d); - if (start >= deleteEnd) { - this._reindexDecoration(d); - } else if (start < event.index && end > event.index) { - this._reindexDecoration(d); - } + this._scheduleLineIndexSync(() => this._applyBufferLinesDelete(event)); + } + + private _mergeLineBucket(newMap: Map, line: number, bucket: IInternalDecoration[]): void { + const existing = newMap.get(line); + if (existing) { + existing.push(...bucket); + } else { + newMap.set(line, bucket.slice()); + } + } + + /** + * Shift indexed line keys and sync start lines. O(unique indexed lines), not O(decoration count). + * Decorations that span the insert point are re-indexed individually (rare vs single-line hits). + */ + private _applyBufferLinesInsert(event: IInsertEvent): void { + const { index, amount } = event; + const spanCrossers: IInternalDecoration[] = []; + for (const d of this._decorations) { + if (d.marker.isDisposed) { + continue; } - }); + const start = d._indexedStartLine; + if (start < index && start + this._getDecorationHeight(d) > index) { + spanCrossers.push(d); + this._removeFromLineBuckets(d); + } + } + const newMap = new Map(); + for (const [line, bucket] of this._decorationsByLine) { + const newLine = line >= index ? line + amount : line; + this._mergeLineBucket(newMap, newLine, bucket); + } + this._decorationsByLine.clear(); + for (const [line, bucket] of newMap) { + this._decorationsByLine.set(line, bucket); + } + for (const d of this._decorations) { + if (d.marker.isDisposed) { + continue; + } + if (d._indexedStartLine >= index) { + d._indexedStartLine = d.marker.line; + } + } + for (const d of spanCrossers) { + this._addToLineBuckets(d); + } + } + + /** + * Drop deleted line keys, shift keys below, sync start lines. Full re-index only when a + * multi-line decoration spans across the deleted range but survives. + */ + private _applyBufferLinesDelete(event: IDeleteEvent): void { + const deleteEnd = event.index + event.amount; + const newMap = new Map(); + for (const [line, bucket] of this._decorationsByLine) { + if (line >= event.index && line < deleteEnd) { + continue; + } + const newLine = line >= deleteEnd ? line - event.amount : line; + this._mergeLineBucket(newMap, newLine, bucket); + } + this._decorationsByLine.clear(); + for (const [line, bucket] of newMap) { + this._decorationsByLine.set(line, bucket); + } + const toReindex: IInternalDecoration[] = []; + for (const d of this._decorations) { + if (d.marker.isDisposed) { + continue; + } + const start = d._indexedStartLine; + const height = this._getDecorationHeight(d); + if (start >= deleteEnd) { + d._indexedStartLine = d.marker.line; + } else if (start < event.index && start + height > deleteEnd) { + toReindex.push(d); + } + } + for (const d of toReindex) { + this._reindexDecoration(d); + } } } From 4a0cb5ae6bd46bc7870d1c8351b754a57111e2d2 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 26 May 2026 09:52:53 -0700 Subject: [PATCH 024/153] Add MicrotaskTimer and use it for decoration line index sync Introduce MicrotaskTimer as a TimeoutTimer-style helper that schedules a single runner via queueMicrotask, with set and cancel but no cancelAndSet. Use it in DecorationLineCache to batch insert/delete line-index updates after marker line adjustments, replacing ad-hoc queueMicrotask scheduling. Co-authored-by: Cursor --- src/common/Async.ts | 36 ++++++++++++++++++++++++ src/common/services/DecorationService.ts | 10 +++---- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/common/Async.ts b/src/common/Async.ts index 7345f19da..276532fb8 100644 --- a/src/common/Async.ts +++ b/src/common/Async.ts @@ -73,6 +73,42 @@ export class TimeoutTimer implements IDisposable { } } +/** + * Schedules a single runner on the microtask queue. Unlike {@link TimeoutTimer}, a scheduled + * microtask cannot be unqueued; {@link cancel} prevents the runner from executing if it has not + * run yet. + */ +export class MicrotaskTimer implements IDisposable { + private _isScheduled = false; + private _isDisposed = false; + + public dispose(): void { + this.cancel(); + this._isDisposed = true; + } + + public cancel(): void { + this._isScheduled = false; + } + + public set(runner: () => void): void { + if (this._isDisposed) { + throw new Error('Calling set on a disposed MicrotaskTimer'); + } + if (this._isScheduled) { + return; + } + this._isScheduled = true; + queueMicrotask(() => { + if (!this._isScheduled) { + return; + } + this._isScheduled = false; + runner(); + }); + } +} + export class IntervalTimer implements IDisposable { private _disposable: IDisposable | undefined; private _isDisposed = false; diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts index 645a63818..a582c18af 100644 --- a/src/common/services/DecorationService.ts +++ b/src/common/services/DecorationService.ts @@ -4,6 +4,7 @@ */ import type { IDeleteEvent, IInsertEvent } from 'common/CircularList'; +import { MicrotaskTimer } from 'common/Async'; import { css } from 'common/Color'; import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'common/Lifecycle'; import { IBufferService, IDecorationService, IInternalDecoration, ILogService } from 'common/services/Services'; @@ -124,10 +125,12 @@ export class DecorationLineCache extends Disposable { private readonly _decorationsByLine: Map = new Map(); private readonly _decorations = new Set(); private readonly _bufferLineListeners = this._register(new MutableDisposable()); - + private readonly _lineIndexSyncTimer = this._register(new MicrotaskTimer()); private _lineIndexSyncCallbacks: (() => void)[] = []; public clear(): void { + this._lineIndexSyncCallbacks.length = 0; + this._lineIndexSyncTimer.cancel(); this._decorationsByLine.clear(); this._decorations.clear(); } @@ -203,10 +206,7 @@ export class DecorationLineCache extends Disposable { /** Re-index after marker line updates (buffer listeners may run before markers). */ private _scheduleLineIndexSync(callback: () => void): void { this._lineIndexSyncCallbacks.push(callback); - if (this._lineIndexSyncCallbacks.length > 1) { - return; - } - queueMicrotask(() => { + this._lineIndexSyncTimer.set(() => { const callbacks = this._lineIndexSyncCallbacks; this._lineIndexSyncCallbacks = []; for (const cb of callbacks) { From d195dc9a3b2222b49970121f75879c8bccd986ec Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 26 May 2026 18:03:15 +0000 Subject: [PATCH 025/153] Add space-unary-ops lint rule and fix SixelHandler violation Enable @stylistic/space-unary-ops to warn on spaces after unary operators like ! in conditions. Fix existing violation in SixelHandler. Fixes #5898 Co-authored-by: Daniel Imms --- addons/addon-image/src/SixelHandler.ts | 2 +- eslint.config.mjs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/addon-image/src/SixelHandler.ts b/addons/addon-image/src/SixelHandler.ts index 2f12fbe19..1af2d85bc 100644 --- a/addons/addon-image/src/SixelHandler.ts +++ b/addons/addon-image/src/SixelHandler.ts @@ -91,7 +91,7 @@ export class SixelHandler implements IDcsHandler, IResetHandler { const height = this._dec.height; // partial fix for https://github.com/jerch/xterm-addon-image/issues/37 - if (!width || ! height) { + if (!width || !height) { if (height) { this._storage.advanceCursor(height); } diff --git a/eslint.config.mjs b/eslint.config.mjs index b73eece7a..6c2f80e30 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -43,6 +43,7 @@ export default tseslint.config( singleline: { delimiter: 'comma', requireLast: false } }], '@stylistic/type-annotation-spacing': 'warn', + '@stylistic/space-unary-ops': 'warn', '@typescript-eslint/array-type': ['warn', { default: 'array', readonly: 'generic' }], '@typescript-eslint/consistent-type-assertions': 'warn', From 4ec85e67baeaa767bed73f68b839e71a2cc5ebb4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 26 May 2026 20:15:55 +0000 Subject: [PATCH 026/153] Add multiline-comment-style lint rule and fix violations Enable multiline-comment-style with starred-block to require JSDoc-style block comments with content on separate lines from the opening delimiter. Auto-fix existing violations across the codebase. Fixes #5899 Co-authored-by: Daniel Imms --- addons/addon-fit/test/FitAddon.test.ts | 32 +- addons/addon-image/src/IIPHeaderParser.ts | 6 +- addons/addon-image/src/IIPMetrics.ts | 6 +- addons/addon-image/src/ImageAddon.ts | 30 +- addons/addon-image/src/ImageRenderer.ts | 36 +- addons/addon-image/src/ImageStorage.ts | 48 +- addons/addon-image/src/SixelHandler.ts | 12 +- .../src/kitty/KittyGraphicsHandler.ts | 146 ++-- .../src/kitty/KittyGraphicsTypes.ts | 30 +- .../src/kitty/KittyImageStorage.ts | 46 +- addons/addon-image/test/ImageAddon.test.ts | 6 +- addons/addon-image/test/KittyGraphics.test.ts | 90 +- addons/addon-ligatures/src/font.ts | 16 +- .../src/fontLigatures/index.ts | 28 +- .../src/fontLigatures/merge.ts | 40 +- .../src/fontLigatures/mergeRange.ts | 30 +- .../src/fontLigatures/processors/6-2.ts | 6 +- .../src/fontLigatures/processors/6-3.ts | 6 +- .../src/fontLigatures/processors/helper.ts | 12 +- .../fontLigatures/processors/substitution.ts | 6 +- addons/addon-ligatures/src/index.ts | 24 +- addons/addon-ligatures/src/parse.ts | 18 +- addons/addon-search/src/SearchEngine.ts | 18 +- addons/addon-search/src/SearchLineCache.ts | 6 +- addons/addon-serialize/src/SerializeAddon.ts | 104 ++- .../test/SerializeAddon.test.ts | 28 +- .../src/UnicodeGraphemeProvider.ts | 8 +- .../test/WebFontsAddon.test.ts | 18 +- addons/addon-web-links/src/WebLinkProvider.ts | 18 +- addons/addon-web-links/src/WebLinksAddon.ts | 24 +- addons/addon-webgl/src/CellColorResolver.ts | 18 +- addons/addon-webgl/src/CharAtlasCache.ts | 6 +- addons/addon-webgl/src/CharAtlasUtils.ts | 6 +- addons/addon-webgl/src/Constants.ts | 8 +- .../src/CursorBlinkStateManager.ts | 22 +- addons/addon-webgl/src/DevicePixelObserver.ts | 10 +- addons/addon-webgl/src/GlyphRenderer.ts | 60 +- addons/addon-webgl/src/RectangleRenderer.ts | 8 +- addons/addon-webgl/src/TextureAtlas.ts | 206 +++-- addons/addon-webgl/src/WebglRenderer.ts | 164 ++-- .../customGlyphs/CustomGlyphDefinitions.ts | 68 +- .../src/customGlyphs/CustomGlyphRasterizer.ts | 18 +- addons/addon-webgl/test/WebglRenderer.test.ts | 8 +- demo/client/client.ts | 20 +- .../components/window/addonImageWindow.ts | 22 +- .../components/window/addonProgressWindow.ts | 12 +- demo/client/components/window/testWindow.ts | 76 +- demo/client/unicodeTable.ts | 6 +- demo/server/server.ts | 14 +- eslint.config.mjs | 1 + src/browser/AccessibilityManager.ts | 20 +- src/browser/Clipboard.ts | 6 +- src/browser/CoreBrowserTerminal.ts | 140 +-- src/browser/Linkifier.ts | 40 +- src/browser/OscLinkProvider.ts | 12 +- src/browser/Terminal.test.ts | 24 +- src/browser/TimeBasedDebouncer.ts | 6 +- src/browser/Types.ts | 6 +- src/browser/Viewport.ts | 32 +- .../decorations/BufferDecorationRenderer.ts | 6 +- src/browser/decorations/ColorZoneStore.ts | 14 +- .../decorations/OverviewRulerRenderer.ts | 12 +- src/browser/input/CompositionHelper.test.ts | 8 +- src/browser/input/CompositionHelper.ts | 86 +- src/browser/input/Mouse.ts | 8 +- src/browser/public/Terminal.ts | 8 +- src/browser/renderer/dom/DomRenderer.ts | 22 +- .../dom/DomRendererRowFactory.test.ts | 6 +- .../renderer/dom/DomRendererRowFactory.ts | 76 +- src/browser/renderer/dom/WidthCache.ts | 20 +- .../renderer/shared/RendererUtils.test.ts | 18 +- src/browser/renderer/shared/RendererUtils.ts | 14 +- src/browser/scrollable/abstractScrollbar.ts | 6 +- src/browser/scrollable/fastDomNode.ts | 6 +- .../scrollable/globalPointerMoveMonitor.ts | 6 +- src/browser/scrollable/horizontalScrollbar.ts | 6 +- src/browser/scrollable/mouseEvent.ts | 6 +- src/browser/scrollable/scrollable.ts | 6 +- src/browser/scrollable/scrollableElement.ts | 6 +- .../scrollable/scrollableElementOptions.ts | 6 +- src/browser/scrollable/scrollbarArrow.ts | 6 +- src/browser/scrollable/scrollbarState.ts | 6 +- .../scrollbarVisibilityController.ts | 6 +- src/browser/scrollable/touch.ts | 6 +- src/browser/scrollable/verticalScrollbar.ts | 6 +- src/browser/scrollable/widget.ts | 6 +- src/browser/services/CharSizeService.ts | 6 +- .../services/CharacterJoinerService.ts | 94 +- src/browser/services/MouseService.test.ts | 6 +- src/browser/services/MouseService.ts | 38 +- src/browser/services/RenderService.ts | 34 +- src/browser/services/SelectionService.test.ts | 12 +- src/browser/services/SelectionService.ts | 158 ++-- src/common/CircularList.ts | 6 +- src/common/Color.ts | 34 +- src/common/CoreTerminal.ts | 6 +- src/common/InputHandler.test.ts | 12 +- src/common/InputHandler.ts | 324 ++++--- src/common/Platform.ts | 22 +- src/common/SortedList.ts | 6 +- src/common/TaskQueue.ts | 20 +- src/common/Types.ts | 46 +- src/common/WindowsMode.ts | 22 +- src/common/buffer/Buffer.test.ts | 112 ++- src/common/buffer/Buffer.ts | 102 ++- src/common/buffer/BufferLine.test.ts | 34 +- src/common/buffer/BufferLine.ts | 34 +- src/common/buffer/BufferReflow.ts | 26 +- src/common/buffer/BufferSet.ts | 20 +- src/common/buffer/CellData.ts | 6 +- src/common/input/Keyboard.test.ts | 6 +- src/common/input/Keyboard.ts | 36 +- src/common/input/KittyKeyboard.test.ts | 12 +- src/common/input/KittyKeyboard.ts | 32 +- src/common/input/TextDecoder.ts | 12 +- src/common/input/UnicodeV6.test.ts | 32 +- src/common/input/UnicodeV6.ts | 18 +- src/common/input/Win32InputMode.ts | 14 +- src/common/input/WriteBuffer.ts | 64 +- src/common/input/XParseColor.ts | 8 +- src/common/parser/ApcParser.ts | 6 +- src/common/parser/DcsParser.ts | 16 +- .../parser/EscapeSequenceParser.test.ts | 18 +- src/common/parser/EscapeSequenceParser.ts | 82 +- src/common/parser/OscParser.ts | 22 +- src/common/services/BufferService.ts | 18 +- src/common/services/InstantiationService.ts | 6 +- src/common/services/LogService.ts | 6 +- src/common/services/MouseStateService.ts | 16 +- src/common/services/OptionsService.ts | 6 +- src/common/services/ServiceRegistry.ts | 6 +- src/common/services/UnicodeService.ts | 18 +- src/headless/public/Terminal.ts | 8 +- test/playwright/InputHandler.test.ts | 28 +- test/playwright/MouseTracking.test.ts | 810 ++++++++++-------- test/playwright/SharedRendererTests.ts | 30 +- test/playwright/Terminal.test.ts | 36 +- test/playwright/TestUtils.ts | 12 +- 138 files changed, 3040 insertions(+), 1837 deletions(-) diff --git a/addons/addon-fit/test/FitAddon.test.ts b/addons/addon-fit/test/FitAddon.test.ts index 6a7c41fa8..d82d3640a 100644 --- a/addons/addon-fit/test/FitAddon.test.ts +++ b/addons/addon-fit/test/FitAddon.test.ts @@ -26,18 +26,24 @@ test.describe('FitAddon', () => { `); }); - // test.beforeEach(async function(): Promise { - // await ctx.page.evaluate(`document.querySelector('#terminal-container').style.display=''`); - // await openTerminal(page); - // }); + /* + * test.beforeEach(async function(): Promise { + * await ctx.page.evaluate(`document.querySelector('#terminal-container').style.display=''`); + * await openTerminal(page); + * }); + */ - // after(async () => { - // await browser.close(); - // }); + /* + * after(async () => { + * await browser.close(); + * }); + */ - // afterEach(async function(): Promise { - // await ctx.proxy.dispose(); - // }); + /* + * afterEach(async function(): Promise { + * await ctx.proxy.dispose(); + * }); + */ test('no terminal', async function(): Promise { await ctx.page.evaluate(`window.fit2 = new FitAddon();`); @@ -79,8 +85,10 @@ test.describe('FitAddon', () => { await ctx.page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`); await setDimensions(); const dimensions: { cols: number, rows: number } | undefined = await ctx.page.evaluate(`window.fit.proposeDimensions()`); - // The value of dims will be undefined if the char measure strategy falls back to the DOM - // method, so only assert if it's not undefined. + /* + * The value of dims will be undefined if the char measure strategy falls back to the DOM + * method, so only assert if it's not undefined. + */ if (dimensions) { ok(dimensions.cols > 85); ok(dimensions.cols < 88); diff --git a/addons/addon-image/src/IIPHeaderParser.ts b/addons/addon-image/src/IIPHeaderParser.ts index af4e9a68d..74e753289 100644 --- a/addons/addon-image/src/IIPHeaderParser.ts +++ b/addons/addon-image/src/IIPHeaderParser.ts @@ -24,8 +24,10 @@ export interface IHeaderFields { height?: string; // Optional, defaults to 1 respecting aspect ratio (width takes precedence). preserveAspectRatio?: number; - // Optional, defaults to 0. If set to 1, the file will be displayed inline, else downloaded - // (download not supported). + /* + * Optional, defaults to 0. If set to 1, the file will be displayed inline, else downloaded + * (download not supported). + */ inline?: number; } diff --git a/addons/addon-image/src/IIPMetrics.ts b/addons/addon-image/src/IIPMetrics.ts index b377cf061..1937a2740 100644 --- a/addons/addon-image/src/IIPMetrics.ts +++ b/addons/addon-image/src/IIPMetrics.ts @@ -23,8 +23,10 @@ export function imageType(d: Uint8Array): IMetrics { return UNSUPPORTED_TYPE; } const d32 = new Uint32Array(d.buffer, d.byteOffset, 6); - // PNG: 89 50 4E 47 0D 0A 1A 0A (8 first bytes == magic number for PNG) - // + first chunk must be IHDR + /* + * PNG: 89 50 4E 47 0D 0A 1A 0A (8 first bytes == magic number for PNG) + * + first chunk must be IHDR + */ if (d32[0] === 0x474E5089 && d32[1] === 0x0A1A0A0D && d32[3] === 0x52444849) { return { mime: 'image/png', diff --git a/addons/addon-image/src/ImageAddon.ts b/addons/addon-image/src/ImageAddon.ts index 7e0f29d3f..930a74bd8 100644 --- a/addons/addon-image/src/ImageAddon.ts +++ b/addons/addon-image/src/ImageAddon.ts @@ -137,11 +137,13 @@ export class ImageAddon implements ITerminalAddon, IImageApi { // enable size reports if (this._opts.enableSizeReports) { - // const windowOptions = terminal.getOption('windowOptions'); - // windowOptions.getWinSizePixels = true; - // windowOptions.getCellSizePixels = true; - // windowOptions.getWinSizeChars = true; - // terminal.setOption('windowOptions', windowOptions); + /* + * const windowOptions = terminal.getOption('windowOptions'); + * windowOptions.getWinSizePixels = true; + * windowOptions.getCellSizePixels = true; + * windowOptions.getWinSizeChars = true; + * terminal.setOption('windowOptions', windowOptions); + */ const windowOps = terminal.options.windowOptions ?? {}; windowOps.getWinSizePixels = true; windowOps.getCellSizePixels = true; @@ -290,11 +292,13 @@ export class ImageAddon implements ITerminalAddon, IImageApi { if (params[0]) { return true; } - // reported features: - // 62 - VT220 - // 4 - SIXEL support - // 9 - charsets - // 22 - ANSI colors + /* + * reported features: + * 62 - VT220 + * 4 - SIXEL support + * 9 - charsets + * 22 - ANSI colors + */ if (this._opts.sixelSupport) { this._report(`\x1b[?62;4;9;22c`); return true; @@ -352,8 +356,10 @@ export class ImageAddon implements ITerminalAddon, IImageApi { let width = this._renderer?.dimensions?.css.canvas.width; let height = this._renderer?.dimensions?.css.canvas.height; if (!width || !height) { - // for some reason we have no working image renderer - // --> fallback to default cell size + /* + * for some reason we have no working image renderer + * --> fallback to default cell size + */ const cellSize = CELL_SIZE_DEFAULT; width = (this._terminal?.cols || 80) * cellSize.width; height = (this._terminal?.rows || 24) * cellSize.height; diff --git a/addons/addon-image/src/ImageRenderer.ts b/addons/addon-image/src/ImageRenderer.ts index b36cdd004..b00002ca2 100644 --- a/addons/addon-image/src/ImageRenderer.ts +++ b/addons/addon-image/src/ImageRenderer.ts @@ -197,10 +197,12 @@ export class ImageRenderer extends Disposable implements IDisposable { const finalWidth = count * width + sx > img.width ? img.width - sx : count * width; const finalHeight = sy + height > img.height ? img.height - sy : height; - // Floor all pixel offsets to get stable tile mapping without any overflows. - // Note: For not pixel perfect aligned cells like in the DOM renderer - // this will move a tile slightly to the top/left (subpixel range, thus ignore it). - // FIX #34: avoid striping on displays with pixelDeviceRatio != 1 by ceiling height and width + /* + * Floor all pixel offsets to get stable tile mapping without any overflows. + * Note: For not pixel perfect aligned cells like in the DOM renderer + * this will move a tile slightly to the top/left (subpixel range, thus ignore it). + * FIX #34: avoid striping on displays with pixelDeviceRatio != 1 by ceiling height and width + */ ctx.drawImage( img, Math.floor(sx), Math.floor(sy), Math.ceil(finalWidth), Math.ceil(finalHeight), @@ -342,21 +344,27 @@ export class ImageRenderer extends Disposable implements IDisposable { ); canvas.classList.add(`xterm-image-layer-${layer}`); const screenElement = this._terminal._core.screenElement; - // Use isolation to create a stacking context without overriding z-index, - // which would conflict with integrators (e.g. VS Code) that set their - // own z-index on the screen element. + /* + * Use isolation to create a stacking context without overriding z-index, + * which would conflict with integrators (e.g. VS Code) that set their + * own z-index on the screen element. + */ screenElement.style.isolation = 'isolate'; if (layer === 'bottom') { - // Use z-index:-1 so it paints behind non-positioned text elements. - // The screen element needs to be a stacking context (via isolation) - // to contain the negative z-index, otherwise it would go behind the - // entire terminal. + /* + * Use z-index:-1 so it paints behind non-positioned text elements. + * The screen element needs to be a stacking context (via isolation) + * to contain the negative z-index, otherwise it would go behind the + * entire terminal. + */ canvas.style.zIndex = '-1'; screenElement.insertBefore(canvas, screenElement.firstChild); } else { - // Explicit z-index ensures the image canvas reliably stacks above - // the text layer (DOM renderer rows). z-index: 0 is below the - // selection overlay (z-index: 1). + /* + * Explicit z-index ensures the image canvas reliably stacks above + * the text layer (DOM renderer rows). z-index: 0 is below the + * selection overlay (z-index: 1). + */ canvas.style.zIndex = '0'; screenElement.appendChild(canvas); } diff --git a/addons/addon-image/src/ImageStorage.ts b/addons/addon-image/src/ImageStorage.ts index 8c89a16b0..3e3a7c298 100644 --- a/addons/addon-image/src/ImageStorage.ts +++ b/addons/addon-image/src/ImageStorage.ts @@ -154,8 +154,10 @@ export class ImageStorage implements IDisposable { for (const spec of this._images.values()) { spec.marker?.dispose(); } - // NOTE: marker.dispose above already calls ImageBitmap.close - // therefore we can just wipe the map here + /* + * NOTE: marker.dispose above already calls ImageBitmap.close + * therefore we can just wipe the map here + */ this._images.clear(); this._renderer.clearAll(); } @@ -306,8 +308,10 @@ export class ImageStorage implements IDisposable { this._delImg(id); } - // eviction marker: - // delete the image when the marker gets disposed + /* + * eviction marker: + * delete the image when the marker gets disposed + */ const endMarker = this._terminal.registerMarker(0); endMarker?.onDispose(() => { const spec = this._images.get(imageId); @@ -316,8 +320,10 @@ export class ImageStorage implements IDisposable { } }); - // since markers do not work on alternate for some reason, - // we evict images here manually + /* + * since markers do not work on alternate for some reason, + * we evict images here manually + */ if (this._terminal.buffer.active.type === 'alternate') { this._evictOnAlternate(); } @@ -416,10 +422,12 @@ export class ImageStorage implements IDisposable { const drawCalls: { imgSpec: IImageSpec, tileId: number, col: number, row: number, count: number }[] = []; const placeholderCalls: { col: number, row: number, count: number }[] = []; - // walk all cells in viewport and collect tiles found - // Note: We check _extendedAttrs directly (not just HAS_EXTENDED flag) - // because text writes clear the BG flag but leave image tile data intact. - // This lets top-layer images survive text overwrites (kitty C=1 behavior). + /* + * walk all cells in viewport and collect tiles found + * Note: We check _extendedAttrs directly (not just HAS_EXTENDED flag) + * because text writes clear the BG flag but leave image tile data intact. + * This lets top-layer images survive text overwrites (kitty C=1 behavior). + */ for (let row = start; row <= end; ++row) { const line = buffer.lines.get(row + buffer.ydisp) as IBufferLineExt; if (!line) return; @@ -492,8 +500,10 @@ export class ImageStorage implements IDisposable { return; } - // handle only viewport width enlargements, exit all other cases - // TODO: needs patch for tile counter + /* + * handle only viewport width enlargements, exit all other cases + * TODO: needs patch for tile counter + */ if (this._viewportMetrics.cols >= metrics.cols) { this._viewportMetrics = metrics; return; @@ -581,8 +591,10 @@ export class ImageStorage implements IDisposable { } } - // TODO: Do we need some blob offloading tricks here to avoid early eviction? - // also see https://stackoverflow.com/questions/28307789/is-there-any-limitation-on-javascript-max-blob-size + /* + * TODO: Do we need some blob offloading tricks here to avoid early eviction? + * also see https://stackoverflow.com/questions/28307789/is-there-any-limitation-on-javascript-max-blob-size + */ private _evictOldest(room: number): number { const used = this._getStoredPixels(); let current = used; @@ -605,9 +617,11 @@ export class ImageStorage implements IDisposable { const old = line._extendedAttrs[x]; if (old) { if (old.imageId !== undefined) { - // found an old ExtendedAttrsImage, since we know that - // they are always isolated instances (single cell usage), - // we can re-use it and just update their id entries + /* + * found an old ExtendedAttrsImage, since we know that + * they are always isolated instances (single cell usage), + * we can re-use it and just update their id entries + */ const oldSpec = this._images.get(old.imageId); if (oldSpec) { // early eviction for in-viewport overwrites diff --git a/addons/addon-image/src/SixelHandler.ts b/addons/addon-image/src/SixelHandler.ts index 1af2d85bc..da4798bf6 100644 --- a/addons/addon-image/src/SixelHandler.ts +++ b/addons/addon-image/src/SixelHandler.ts @@ -113,13 +113,17 @@ export class SixelHandler implements IDcsHandler, IResetHandler { * Some helpers to extract current terminal colors. */ -// get currently active background color from terminal -// also respect INVERSE setting +/* + * get currently active background color from terminal + * also respect INVERSE setting + */ function extractActiveBg(attr: AttributeData, colors: ReadonlyColorSet | undefined): RGBA8888 { let bg = 0; if (!colors) { - // FIXME: theme service is prolly not available yet, - // happens if .open() was not called yet (bug in core?) + /* + * FIXME: theme service is prolly not available yet, + * happens if .open() was not called yet (bug in core?) + */ return bg; } if (attr.isInverse()) { diff --git a/addons/addon-image/src/kitty/KittyGraphicsHandler.ts b/addons/addon-image/src/kitty/KittyGraphicsHandler.ts index de889dfff..8ad8ef794 100644 --- a/addons/addon-image/src/kitty/KittyGraphicsHandler.ts +++ b/addons/addon-image/src/kitty/KittyGraphicsHandler.ts @@ -62,9 +62,11 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos // Storage related states private _pendingTransmissions: Map = new Map(); - // Tracks the pending key of the most recently started chunked upload. - // Per spec, subsequent chunks only need m= (and optionally q=), without i=. - // When a chunk arrives with no i=, this key is used to find the pending upload. + /* + * Tracks the pending key of the most recently started chunked upload. + * Per spec, subsequent chunks only need m= (and optionally q=), without i=. + * When a chunk arrives with no i=, this key is used to find the pending upload. + */ private _lastPendingKey: number | undefined; constructor( @@ -173,9 +175,11 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos private _streamPayload(data: Uint32Array, start: number, end: number): void { if (this._aborted) return; - // Check size limit (compare encoded bytes against pre-calculated limit) - // Include cumulative size from pending transmission for multi-chunk images. - // Per spec, subsequent chunks may omit i=, so fall back to _lastPendingKey. + /* + * Check size limit (compare encoded bytes against pre-calculated limit) + * Include cumulative size from pending transmission for multi-chunk images. + * Per spec, subsequent chunks may omit i=, so fall back to _lastPendingKey. + */ const pendingKey = this._parsedCommand?.id ?? this._lastPendingKey ?? 0; const pending = this._pendingTransmissions.get(pendingKey); const previousEncodedSize = pending?.totalEncodedSize ?? 0; @@ -285,9 +289,11 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos } this._activeDecoder = null; - // Handle command first — handlers create Blob/ImageData from imageBytes, - // which copies the data. Only then is it safe to release the decoder's - // wasm memory that imageBytes points into. + /* + * Handle command first — handlers create Blob/ImageData from imageBytes, + * which copies the data. Only then is it safe to release the decoder's + * wasm memory that imageBytes points into. + */ const result = this._handleCommandWithBytesAndCmd(finalCmd, imageBytes, decodeError); if (decoder) { decoder.release(); @@ -325,10 +331,12 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos case KittyAction.PLACEMENT: return this._handlePlacement(cmd); default: - // TODO: Implement remaining actions when needed: - // - a=f (frame): animation frame operations - // - a=a (animation): animation control - // - a=c (compose): compose images + /* + * TODO: Implement remaining actions when needed: + * - a=f (frame): animation frame operations + * - a=a (animation): animation control + * - a=c (compose): compose images + */ if (cmd.id !== undefined) { this._sendResponse(cmd.id, 'EINVAL:unsupported action', cmd.quiet ?? 0); } @@ -342,8 +350,10 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos switch (action) { case KittyAction.TRANSMIT: { const result = this._handleTransmit(cmd, bytes, decodeError); - // Only send response when _handleTransmit didn't already respond - // (it handles unsupported transmission medium responses internally) + /* + * Only send response when _handleTransmit didn't already respond + * (it handles unsupported transmission medium responses internally) + */ if ((cmd.transmission ?? 'd') === 'd' && cmd.id !== undefined) { if (decodeError) { this._sendResponse(cmd.id, 'EINVAL:invalid base64 data', cmd.quiet ?? 0); @@ -361,10 +371,12 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos // a=p ignores any payload — image data was already transmitted return this._handlePlacement(cmd); default: - // TODO: Implement remaining actions when needed: - // - a=f (frame): animation frame operations - // - a=a (animation): animation control - // - a=c (compose): compose images + /* + * TODO: Implement remaining actions when needed: + * - a=f (frame): animation frame operations + * - a=a (animation): animation control + * - a=c (compose): compose images + */ if (cmd.id !== undefined) { this._sendResponse(cmd.id, 'EINVAL:unsupported action', cmd.quiet ?? 0); } @@ -390,16 +402,18 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos } private _handleTransmit(cmd: IKittyCommand, bytes: Uint8Array, decodeError: boolean): boolean { - // TODO: Support file-based transmission modes (t=f, t=t, t=s) - // Currently only supports direct transmission (t=d, the default). - // - t=f (file): Payload is base64-encoded file path. Terminal reads image from that path. - // - t=t (temp file): Payload is base64-encoded path in temp directory. Terminal reads, deletes. - // - t=s: Payload is base64-encoded POSIX shm name. Terminal reads from shared memory. - // These modes require filesystem/IPC access not available in browsers. For Node.js/Electron: - // 1. Check cmd.transmission (t key) before treating bytes as image data - // 2. For t=f/t/s: decode bytes as UTF-8 string (the path/name), then read file contents - // 3. For t=d: treat bytes as image data (current behavior) - // When implementing, also update _handleQuery to accept these transmission mediums. + /* + * TODO: Support file-based transmission modes (t=f, t=t, t=s) + * Currently only supports direct transmission (t=d, the default). + * - t=f (file): Payload is base64-encoded file path. Terminal reads image from that path. + * - t=t (temp file): Payload is base64-encoded path in temp directory. Terminal reads, deletes. + * - t=s: Payload is base64-encoded POSIX shm name. Terminal reads from shared memory. + * These modes require filesystem/IPC access not available in browsers. For Node.js/Electron: + * 1. Check cmd.transmission (t key) before treating bytes as image data + * 2. For t=f/t/s: decode bytes as UTF-8 string (the path/name), then read file contents + * 3. For t=d: treat bytes as image data (current behavior) + * When implementing, also update _handleQuery to accept these transmission mediums. + */ const transmission = cmd.transmission ?? 'd'; if (transmission !== 'd') { if (cmd.id !== undefined) { @@ -449,9 +463,11 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos const id = cmd.id ?? 0; const quiet = cmd.quiet ?? 0; - // Per spec: reject unsupported transmission mediums (only t=d is supported atm) - // TODO: When filesystem support is added (Node.js/Electron), update this to accept - // t=f (file), t=t (temp file), and t=s (shared memory) and respond OK for queries. + /* + * Per spec: reject unsupported transmission mediums (only t=d is supported atm) + * TODO: When filesystem support is added (Node.js/Electron), update this to accept + * t=f (file), t=t (temp file), and t=s (shared memory) and respond OK for queries. + */ const transmission = cmd.transmission ?? 'd'; if (transmission !== 'd') { this._sendResponse(id, 'EINVAL:unsupported transmission medium', quiet); @@ -500,9 +516,11 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos // Per spec: default delete selector is 'a' (delete all visible placements) const selector = cmd.deleteSelector ?? 'a'; - // TODO: Distinguish lowercase (delete placements only) from uppercase - // (delete placements + free stored image data). Currently both variants - // free everything since we don't separate stored data from placements. + /* + * TODO: Distinguish lowercase (delete placements only) from uppercase + * (delete placements + free stored image data). Currently both variants + * free everything since we don't separate stored data from placements. + */ switch (selector) { case 'a': case 'A': @@ -511,10 +529,12 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos break; case 'i': case 'I': - // TODO: When placement id tracking is implemented (see TODO in - // KittyImageStorage), d=i with p= should delete only that - // specific placement, while d=i without p should delete all - // placements for the image. + /* + * TODO: When placement id tracking is implemented (see TODO in + * KittyImageStorage), d=i with p= should delete only that + * specific placement, while d=i without p should delete all + * placements for the image. + */ if (cmd.id !== undefined) { const pending = this._pendingTransmissions.get(cmd.id); if (pending) { @@ -576,8 +596,10 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos const cw = this._renderer.dimensions?.css.cell.width || CELL_SIZE_DEFAULT.width; const ch = this._renderer.dimensions?.css.cell.height || CELL_SIZE_DEFAULT.height; - // Per spec: c/r default to image's natural cell dimensions. - // If only one of c/r is specified, compute the other from image aspect ratio. + /* + * Per spec: c/r default to image's natural cell dimensions. + * If only one of c/r is specified, compute the other from image aspect ratio. + */ let imgCols: number; let imgRows: number; if (cmd.columns !== undefined && cmd.rows !== undefined) { @@ -613,10 +635,12 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos const savedY = buffer.y; const savedYbase = buffer.ybase; - // Determine layer based on z-index: negative = behind text, 0+ = on top. - // When z<0 we always use the bottom layer even without allowTransparency — - // the image will simply be hidden behind the opaque text background, which - // is the correct behavior (client asked for "behind text"). + /* + * Determine layer based on z-index: negative = behind text, 0+ = on top. + * When z<0 we always use the bottom layer even without allowTransparency — + * the image will simply be hidden behind the opaque text background, which + * is the correct behavior (client asked for "behind text"). + */ const wantsBottom = cmd.zIndex !== undefined && cmd.zIndex < 0; const layer: ImageLayer = wantsBottom ? 'bottom' : 'top'; @@ -630,10 +654,12 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos const xOffset = Math.min(Math.max(0, cmd.xOffset ?? 0), cw - 1); const yOffset = Math.min(Math.max(0, cmd.yOffset ?? 0), ch - 1); if (xOffset !== 0 || yOffset !== 0) { - // Per spec: X/Y is not added to c/r area. When c/r are explicit, the - // total placement area remains c*cw × r*ch pixels and the offset image - // is clipped to fit. When c/r are unset, the padded canvas determines - // the natural cell dimensions. + /* + * Per spec: X/Y is not added to c/r area. When c/r are explicit, the + * total placement area remains c*cw × r*ch pixels and the offset image + * is clipped to fit. When c/r are unset, the padded canvas determines + * the natural cell dimensions. + */ const canvasW = (cmd.columns !== undefined) ? Math.round(imgCols * cw) : bitmap.width + xOffset; const canvasH = (cmd.rows !== undefined) ? Math.round(imgRows * ch) : bitmap.height + yOffset; const offsetCanvas = ImageRenderer.createCanvas(window.document, canvasW, canvasH); @@ -664,9 +690,11 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos this._kittyStorage.addImage(image.id, bitmap, true, layer, zIndex); bitmap = undefined; // ownership transferred to storage - // Kitty cursor movement - // Per spec: cursor placed at first column after last image column, - // on the last row of the image. C=1 means don't move cursor. + /* + * Kitty cursor movement + * Per spec: cursor placed at first column after last image column, + * on the last row of the image. C=1 means don't move cursor. + */ if (cmd.cursorMovement === 1) { // C=1: restore cursor to position before image was placed const scrolled = buffer.ybase - savedYbase; @@ -674,8 +702,10 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos // Can't restore cursor to scrollback? buffer.y = Math.max(savedY - scrolled, 0); } else { - // Default (C=0): advance cursor horizontally past the image - // addImage already positioned cursor on the last row via lineFeeds + /* + * Default (C=0): advance cursor horizontally past the image + * addImage already positioned cursor on the last row via lineFeeds + */ buffer.x = Math.min(savedX + imgCols, this._coreTerminal.cols); } } catch (e) { @@ -736,9 +766,11 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos return createImageBitmap(new ImageData(new Uint8ClampedArray(bytes.buffer as ArrayBuffer, bytes.byteOffset, pixelCount * KittyPixelConstants.BYTES_PER_PIXEL_RGBA), width, height)); } - // RGB→RGBA: interleave alpha using uint32 block processing (4 pixels per iteration). - // 3 uint32 reads + 4 uint32 writes per 4 pixels vs 28 byte reads/writes — ~6x faster. - // Assumes little-endian (all modern browsers/Node.js). + /* + * RGB→RGBA: interleave alpha using uint32 block processing (4 pixels per iteration). + * 3 uint32 reads + 4 uint32 writes per 4 pixels vs 28 byte reads/writes — ~6x faster. + * Assumes little-endian (all modern browsers/Node.js). + */ const data = new Uint8ClampedArray(pixelCount * KittyPixelConstants.BYTES_PER_PIXEL_RGBA); const src32 = new Uint32Array(bytes.buffer, bytes.byteOffset, Math.floor(bytes.byteLength / 4)); const dst32 = new Uint32Array(data.buffer); diff --git a/addons/addon-image/src/kitty/KittyGraphicsTypes.ts b/addons/addon-image/src/kitty/KittyGraphicsTypes.ts index 448ebcc05..da94675d8 100644 --- a/addons/addon-image/src/kitty/KittyGraphicsTypes.ts +++ b/addons/addon-image/src/kitty/KittyGraphicsTypes.ts @@ -7,8 +7,10 @@ import type Base64Decoder from 'xterm-wasm-parts/lib/base64/Base64Decoder.wasm'; -// Kitty graphics protocol action types. -// See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'a'. +/* + * Kitty graphics protocol action types. + * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'a'. + */ export const enum KittyAction { TRANSMIT = 't', TRANSMIT_DISPLAY = 'T', @@ -17,23 +19,29 @@ export const enum KittyAction { DELETE = 'd' } -// Kitty graphics protocol format types. -// See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference +/* + * Kitty graphics protocol format types. + * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference + */ export const enum KittyFormat { RGB = 24, RGBA = 32, PNG = 100 } -// Kitty graphics protocol compression types. -// See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'o'. +/* + * Kitty graphics protocol compression types. + * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'o'. + */ export const enum KittyCompression { NONE = '', ZLIB = 'z' } -// Kitty graphics protocol control data keys. -// See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference +/* + * Kitty graphics protocol control data keys. + * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference + */ export const enum KittyKey { // Action to perform (t=transmit, T=transmit+display, q=query, p=placement, d=delete) ACTION = 'a', @@ -115,8 +123,10 @@ export interface IKittyCommand { payload?: string; } -// Pending chunked transmission state. -// Stores metadata from the first chunk while accumulating decoded payload data. +/* + * Pending chunked transmission state. + * Stores metadata from the first chunk while accumulating decoded payload data. + */ export interface IPendingTransmission { // The parsed command from the first chunk (contains action, format, dimensions, etc.) cmd: IKittyCommand; diff --git a/addons/addon-image/src/kitty/KittyImageStorage.ts b/addons/addon-image/src/kitty/KittyImageStorage.ts index 95ea594c7..766806532 100644 --- a/addons/addon-image/src/kitty/KittyImageStorage.ts +++ b/addons/addon-image/src/kitty/KittyImageStorage.ts @@ -8,27 +8,31 @@ import { ImageStorage } from '../ImageStorage'; import { ImageLayer } from '../Types'; import { IKittyImageData } from './KittyGraphicsTypes'; -// Kitty-specific image storage controller. -// -// Wraps shared ImageStorage with kitty protocol semantics: -// - tracks transmitted image payloads by kitty image id -// - tracks kitty image id -> shared ImageStorage id mapping for displayed images -// - mirrors shared-storage evictions into kitty maps -// - applies protocol-level undisplayed-image eviction policy +/* + * Kitty-specific image storage controller. + * + * Wraps shared ImageStorage with kitty protocol semantics: + * - tracks transmitted image payloads by kitty image id + * - tracks kitty image id -> shared ImageStorage id mapping for displayed images + * - mirrors shared-storage evictions into kitty maps + * - applies protocol-level undisplayed-image eviction policy + */ export class KittyImageStorage implements IDisposable { private static readonly _maxStoredImages = 256; private _nextImageId = 1; private readonly _images: Map = new Map(); - // TODO: Support multiple placements per image. The kitty spec identifies - // placements by an (image id, placement id) pair — same i + different p - // values should coexist, and same i + same p should replace the prior - // placement. Currently we track only one storage entry per kitty image id, - // so multiple placements of the same image overwrite each other. Fixing - // this requires changing these maps to Map> - // (kittyId → placementId → storageId) and updating addImage/deleteById - // accordingly. The underlying shared ImageStorage would also need to - // support multiple entries per logical image. + /* + * TODO: Support multiple placements per image. The kitty spec identifies + * placements by an (image id, placement id) pair — same i + different p + * values should coexist, and same i + same p should replace the prior + * placement. Currently we track only one storage entry per kitty image id, + * so multiple placements of the same image overwrite each other. Fixing + * this requires changing these maps to Map> + * (kittyId → placementId → storageId) and updating addImage/deleteById + * accordingly. The underlying shared ImageStorage would also need to + * support multiple entries per logical image. + */ private readonly _kittyIdToStorageId: Map = new Map(); private readonly _storageIdToKittyId: Map = new Map(); @@ -90,10 +94,12 @@ export class KittyImageStorage implements IDisposable { } public addImage(kittyId: number, image: HTMLCanvasElement | ImageBitmap, scrolling: boolean, layer: ImageLayer, zIndex: number): void { - // Clean up stale reverse-mapping from a previous placement of the same - // kitty image. The old shared-storage entry is kept (it may still be - // visible on screen) but its reverse mapping is removed so that eviction - // of the old entry won't incorrectly delete the kitty image data. + /* + * Clean up stale reverse-mapping from a previous placement of the same + * kitty image. The old shared-storage entry is kept (it may still be + * visible on screen) but its reverse mapping is removed so that eviction + * of the old entry won't incorrectly delete the kitty image data. + */ const oldStorageId = this._kittyIdToStorageId.get(kittyId); if (oldStorageId !== undefined) { this._storageIdToKittyId.delete(oldStorageId); diff --git a/addons/addon-image/test/ImageAddon.test.ts b/addons/addon-image/test/ImageAddon.test.ts index dc729ec63..9db26cd7f 100644 --- a/addons/addon-image/test/ImageAddon.test.ts +++ b/addons/addon-image/test/ImageAddon.test.ts @@ -64,8 +64,10 @@ const TESTDATA: ITestData = (() => { }; })(); const SIXEL_SEQ_0 = introducer(0) + TESTDATA.sixel + FINALIZER; -// const SIXEL_SEQ_1 = introducer(1) + TESTDATA.sixel + FINALIZER; -// const SIXEL_SEQ_2 = introducer(2) + TESTDATA.sixel + FINALIZER; +/* + * const SIXEL_SEQ_1 = introducer(1) + TESTDATA.sixel + FINALIZER; + * const SIXEL_SEQ_2 = introducer(2) + TESTDATA.sixel + FINALIZER; + */ // NOTE: the data is loaded as string for easier transport through playwright const TESTDATA_IIP: [string, [number, number]][] = [ diff --git a/addons/addon-image/test/KittyGraphics.test.ts b/addons/addon-image/test/KittyGraphics.test.ts index a9b5d6845..2822cce6b 100644 --- a/addons/addon-image/test/KittyGraphics.test.ts +++ b/addons/addon-image/test/KittyGraphics.test.ts @@ -101,12 +101,14 @@ test.beforeAll(async ({ browser }) => { test.afterAll(async () => await ctx.page.close()); test.describe('Kitty Graphics Protocol', () => { - // TODO: Add tests for larger images with various dimensions - // TODO: Add tests for virtual placement (U=1) - // TODO: Add tests for animation frames - // TODO: Add performance tests for streaming large images - // TODO: Implement cursor movement per Kitty spec - cursor should move by cols/rows after placement (unless C=1) - // TODO: Distinguish lowercase delete selectors (placement only) from uppercase (placement + free data) + /* + * TODO: Add tests for larger images with various dimensions + * TODO: Add tests for virtual placement (U=1) + * TODO: Add tests for animation frames + * TODO: Add performance tests for streaming large images + * TODO: Implement cursor movement per Kitty spec - cursor should move by cols/rows after placement (unless C=1) + * TODO: Distinguish lowercase delete selectors (placement only) from uppercase (placement + free data) + */ test.beforeEach(async ({}, testInfo) => { await ctx.page.evaluate(` @@ -209,8 +211,10 @@ test.describe('Kitty Graphics Protocol', () => { }); test('enforces size limit across chunked transmissions', async () => { - // Create a custom addon with very small size limit (100 bytes) - // The 1x1 PNG is ~164 bytes base64, so 2 chunks should exceed 100 + /* + * Create a custom addon with very small size limit (100 bytes) + * The 1x1 PNG is ~164 bytes base64, so 2 chunks should exceed 100 + */ await ctx.page.evaluate(() => { (window as any).smallLimitAddon = new ImageAddon({ kittySupport: true, @@ -1279,8 +1283,10 @@ test.describe('Kitty Graphics Protocol', () => { }); test('only c specified computes r from aspect ratio', async () => { - // 200x100 image (2:1 aspect) with c=10. - // Per spec: r = ceil((h/w) * c * cw / ch) = ceil(0.5 * 10 * cw / ch) + /* + * 200x100 image (2:1 aspect) with c=10. + * Per spec: r = ceil((h/w) * c * cw / ch) = ceil(0.5 * 10 * cw / ch) + */ await ctx.proxy.write(`\x1b_Ga=t,f=100,i=225;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(100); @@ -1297,8 +1303,10 @@ test.describe('Kitty Graphics Protocol', () => { }); test('only r specified computes c from aspect ratio', async () => { - // 200x100 image (2:1 aspect) with r=5. - // Per spec: c = ceil((w/h) * r * ch / cw) = ceil(2 * 5 * ch / cw) + /* + * 200x100 image (2:1 aspect) with r=5. + * Per spec: c = ceil((w/h) * r * ch / cw) = ceil(2 * 5 * ch / cw) + */ await ctx.proxy.write(`\x1b_Ga=t,f=100,i=226;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(100); @@ -1316,9 +1324,11 @@ test.describe('Kitty Graphics Protocol', () => { }); test.describe('Cursor positioning', () => { - // NOTE: Current tests document ACTUAL behavior (MVP - cursor doesn't move) - // Per Kitty spec: cursor placed at first column after last image column, - // on the last row of the image. C=1 means don't move cursor. + /* + * NOTE: Current tests document ACTUAL behavior (MVP - cursor doesn't move) + * Per Kitty spec: cursor placed at first column after last image column, + * on the last row of the image. C=1 means don't move cursor. + */ test('cursor advances past 1x1 image', async () => { const cursorBefore = await getCursor(); @@ -1376,8 +1386,10 @@ test.describe('Kitty Graphics Protocol', () => { }); test('cursor should move right by cols when c specified', async () => { - // c=5, r computed from 1x1 aspect ratio: r = ceil((1/1) * (5*cw) / ch) - // With 1:1 aspect, image height in pixels = 5*cw, so r = ceil(5*cw/ch) + /* + * c=5, r computed from 1x1 aspect ratio: r = ceil((1/1) * (5*cw) / ch) + * With 1:1 aspect, image height in pixels = 5*cw, so r = ceil(5*cw/ch) + */ await ctx.proxy.write(`\x1b_Ga=T,f=100,c=5;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); @@ -1634,10 +1646,12 @@ test.describe('Kitty Graphics Protocol', () => { }); test.describe('Pixel verification', () => { - // The 200x100 image has 20 colored rectangles in a 10x2 grid. - // Each rectangle is 20px wide x 50px tall. - // Top row (y=0..49): Red, Orange, Yellow, Lime, Green, Cyan, SkyBlue, Blue, Purple, Magenta - // Bottom row (y=50..99): Pink, Brown, Maroon, Olive, Teal, Navy, Gray, DarkGray, LightGray, White + /* + * The 200x100 image has 20 colored rectangles in a 10x2 grid. + * Each rectangle is 20px wide x 50px tall. + * Top row (y=0..49): Red, Orange, Yellow, Lime, Green, Cyan, SkyBlue, Blue, Purple, Magenta + * Bottom row (y=50..99): Pink, Brown, Maroon, Olive, Teal, Navy, Gray, DarkGray, LightGray, White + */ test('renders red rectangle at top-left origin (0,0)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); @@ -1650,9 +1664,11 @@ test.describe('Kitty Graphics Protocol', () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); - // Sample center of each top-row rectangle (y=25, x=10,30,50,...,190) - // All within the first cell row, so we read from the canvas at cell (0,0) - // Red at x=10 + /* + * Sample center of each top-row rectangle (y=25, x=10,30,50,...,190) + * All within the first cell row, so we read from the canvas at cell (0,0) + * Red at x=10 + */ deepStrictEqual(await getPixel(0, 0, 10, 25), [255, 0, 0, 255]); // Orange at x=30 deepStrictEqual(await getPixel(0, 0, 30, 25), [255, 128, 0, 255]); @@ -1668,8 +1684,10 @@ test.describe('Kitty Graphics Protocol', () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); - // Bottom row starts at y=50. Center at y=75. - // Pink at x=10 + /* + * Bottom row starts at y=50. Center at y=75. + * Pink at x=10 + */ deepStrictEqual(await getPixel(0, 0, 10, 75), [255, 192, 203, 255]); // Brown at x=30 deepStrictEqual(await getPixel(0, 0, 30, 75), [165, 42, 42, 255]); @@ -1725,8 +1743,10 @@ test.describe('Kitty Graphics Protocol', () => { }); test('scales cropped source region to c/r placement rectangle', async () => { - // Firefox's createImageBitmap uses different resize sampling, producing - // slightly off pixel values compared to Chromium, so skip on Firefox. + /* + * Firefox's createImageBitmap uses different resize sampling, producing + * slightly off pixel values compared to Chromium, so skip on Firefox. + */ if (ctx.browser.browserType().name() === 'firefox') { test.skip(); } @@ -2137,8 +2157,10 @@ test.describe('Kitty Graphics Protocol', () => { }); test('memory limit eviction cleans Kitty handler maps', async () => { - // Resize terminal to fit 7 non-overlapping 200x100 images without scrolling. - // Each image ≈ 29 cols × 8 rows at default cell size. + /* + * Resize terminal to fit 7 non-overlapping 200x100 images without scrolling. + * Each image ≈ 29 cols × 8 rows at default cell size. + */ await ctx.page.evaluate(` window.term.reset(); window.imageAddon?.dispose(); @@ -2147,9 +2169,11 @@ test.describe('Kitty Graphics Protocol', () => { window.term.loadAddon(window.imageAddon); `); - // storageLimit 0.5 MB = 125,000 pixels. Each 200x100 image = 20,000 pixels. - // 6 images = 120K pixels (under limit). 7th triggers eviction (140K > 125K). - // Place non-overlapping so tile-count eviction doesn't interfere. + /* + * storageLimit 0.5 MB = 125,000 pixels. Each 200x100 image = 20,000 pixels. + * 6 images = 120K pixels (under limit). 7th triggers eviction (140K > 125K). + * Place non-overlapping so tile-count eviction doesn't interfere. + */ const positions = [[1, 1], [30, 1], [1, 9], [30, 9], [1, 17], [30, 17]]; for (let n = 0; n < 6; n++) { const [c, r] = positions[n]; diff --git a/addons/addon-ligatures/src/font.ts b/addons/addon-ligatures/src/font.ts index 60ab3d746..8e5f93dd5 100644 --- a/addons/addon-ligatures/src/font.ts +++ b/addons/addon-ligatures/src/font.ts @@ -43,9 +43,11 @@ export default async function load(fontFamily: string, cacheSize: number): Promi throw new Error('Permission to access local fonts not granted.'); } } catch (err: unknown) { - // A `TypeError` indicates the 'local-fonts' - // permission is not yet implemented, so - // only `throw` if this is _not_ the problem. + /* + * A `TypeError` indicates the 'local-fonts' + * permission is not yet implemented, so + * only `throw` if this is _not_ the problem. + */ if (err instanceof Error && err.name !== 'TypeError') { throw err; } @@ -89,9 +91,11 @@ export default async function load(fontFamily: string, cacheSize: number): Promi const fonts = await fontsPromise; for (const family of parse(fontFamily)) { - // If we reach one of the generic font families, the font resolution - // will end for the browser and we can't determine the specific font - // used. Throw. + /* + * If we reach one of the generic font families, the font resolution + * will end for the browser and we can't determine the specific font + * used. Throw. + */ if (genericFontFamilies.includes(family)) { return undefined; } diff --git a/addons/addon-ligatures/src/fontLigatures/index.ts b/addons/addon-ligatures/src/fontLigatures/index.ts index d65e73bc4..794b39ecc 100644 --- a/addons/addon-ligatures/src/fontLigatures/index.ts +++ b/addons/addon-ligatures/src/fontLigatures/index.ts @@ -88,9 +88,11 @@ class FontImpl implements IFont { glyphIds.push(this._font.charToGlyphIndex(char)); } - // If there are no lookup groups, there's no point looking for - // replacements. This gives us a minor performance boost for fonts with - // no ligatures + /* + * If there are no lookup groups, there's no point looking for + * replacements. This gives us a minor performance boost for fonts with + * no ligatures + */ if (this._lookupTrees.length === 0) { return { inputGlyphs: glyphIds, @@ -113,8 +115,10 @@ class FontImpl implements IFont { } public findLigatureRanges(text: string): [number, number][] { - // Short circuit the process if there are no possible ligatures in the - // font + /* + * Short circuit the process if there are no possible ligatures in the + * font + */ if (this._lookupTrees.length === 0) { return []; } @@ -170,9 +174,11 @@ class FontImpl implements IFont { } } } else { - // We don't need to do the lastGlyphIndex tracking here because - // reverse processing isn't allowed to replace more than one - // character at a time. + /* + * We don't need to do the lastGlyphIndex tracking here because + * reverse processing isn't allowed to replace more than one + * character at a time. + */ for (let i = nextLookup.last - 1; i >= nextLookup.first; i--) { const result = walkTree(lookup.tree, sequence, i, i); if (result) { @@ -224,8 +230,10 @@ class FontImpl implements IFont { for (let j = 0; j < lookups.length; j++) { const lookupIndex = lookups[j]; if (lookupIndex >= start) { - // Update the lookup information if it's the one we're - // storing or earlier than it. + /* + * Update the lookup information if it's the one we're + * storing or earlier than it. + */ if (result.index === null || lookupIndex <= result.index) { result.index = lookupIndex; diff --git a/addons/addon-ligatures/src/fontLigatures/merge.ts b/addons/addon-ligatures/src/fontLigatures/merge.ts index 262ea85ae..a16f748a9 100644 --- a/addons/addon-ligatures/src/fontLigatures/merge.ts +++ b/addons/addon-ligatures/src/fontLigatures/merge.ts @@ -32,9 +32,11 @@ export default function mergeTrees(trees: ILookupTree[]): ILookupTree { function mergeSubtree(mainTree: ILookupTree, mergeTree: ILookupTree, mergedEntries: WeakMap>): void { // Need to fix this recursively (and handle lookups) for (const [glyphId, value] of Object.entries(mergeTree.individual)) { - // The main tree is guaranteed to have no overlaps between the - // individual and range values, so if we match an invididual, there - // must not be a range + /* + * The main tree is guaranteed to have no overlaps between the + * individual and range values, so if we match an invididual, there + * must not be a range + */ if (mainTree.individual[glyphId]) { mergeTreeEntry(mainTree.individual[glyphId], value, mergedEntries); } else { @@ -49,13 +51,17 @@ function mergeSubtree(mainTree: ILookupTree, mergeTree: ILookupTree, mergedEntri matched = true; - // If they overlap, we have to split the range and then - // merge the overlap + /* + * If they overlap, we have to split the range and then + * merge the overlap + */ mainTree.individual[glyphId] = value; mergeTreeEntry(mainTree.individual[glyphId], cloneEntry(entry), mergedEntries); - // When there's an overlap, we also have to fix up the range - // that we had already processed + /* + * When there's an overlap, we also have to fix up the range + * that we had already processed + */ mainTree.range.splice(index, 1); for (const glyph of overlap.second) { if (Array.isArray(glyph)) { @@ -76,9 +82,11 @@ function mergeSubtree(mainTree: ILookupTree, mergeTree: ILookupTree, mergedEntri } for (const { range, entry } of mergeTree.range) { - // Ranges are more complicated, because they can overlap with - // multiple things, individual and range alike. We start by - // eliminating ranges that are already present in another range + /* + * Ranges are more complicated, because they can overlap with + * multiple things, individual and range alike. We start by + * eliminating ranges that are already present in another range + */ let remainingRanges: (number | [number, number])[] = [range]; for (let index = 0; index < mainTree.range.length; index++) { @@ -123,13 +131,17 @@ function mergeSubtree(mainTree: ILookupTree, mergeTree: ILookupTree, mergedEntri continue; } - // If they overlap, we have to split the range and then - // merge the overlap + /* + * If they overlap, we have to split the range and then + * merge the overlap + */ mainTree.individual[remainingRange] = cloneEntry(entry); mergeTreeEntry(mainTree.individual[remainingRange], cloneEntry(resultEntry), mergedEntries); - // When there's an overlap, we also have to fix up the range - // that we had already processed + /* + * When there's an overlap, we also have to fix up the range + * that we had already processed + */ mainTree.range.splice(index, 1); index--; diff --git a/addons/addon-ligatures/src/fontLigatures/mergeRange.ts b/addons/addon-ligatures/src/fontLigatures/mergeRange.ts index ec5305083..8058e077f 100644 --- a/addons/addon-ligatures/src/fontLigatures/mergeRange.ts +++ b/addons/addon-ligatures/src/fontLigatures/mergeRange.ts @@ -18,14 +18,18 @@ export default function mergeRange(ranges: [number, number][], newRangeStart: nu return ranges; } if (newRangeEnd <= range[1]) { - // Case 2: New range is either wholly contained within the - // search range or overlaps with the front of it + /* + * Case 2: New range is either wholly contained within the + * search range or overlaps with the front of it + */ range[0] = Math.min(newRangeStart, range[0]); return ranges; } if (newRangeStart < range[1]) { - // Case 3: New range either wholly contains the search range - // or overlaps with the end of it + /* + * Case 3: New range either wholly contains the search range + * or overlaps with the end of it + */ range[0] = Math.min(newRangeStart, range[0]); inRange = true; } else { @@ -34,21 +38,27 @@ export default function mergeRange(ranges: [number, number][], newRangeStart: nu } } else { if (newRangeEnd <= range[0]) { - // Case 5: New range extends from previous range but doesn't - // reach the current one + /* + * Case 5: New range extends from previous range but doesn't + * reach the current one + */ ranges[i - 1][1] = newRangeEnd; return ranges; } if (newRangeEnd <= range[1]) { - // Case 6: New range extends from prvious range into the - // current range + /* + * Case 6: New range extends from prvious range into the + * current range + */ ranges[i - 1][1] = Math.max(newRangeEnd, range[1]); ranges.splice(i, 1); inRange = false; return ranges; } - // Case 7: New range extends from previous range past the - // end of the current range + /* + * Case 7: New range extends from previous range past the + * end of the current range + */ ranges.splice(i, 1); i--; } diff --git a/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts b/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts index f39682423..890188508 100644 --- a/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts +++ b/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts @@ -72,8 +72,10 @@ export default function buildTree(table: ChainingContextualSubstitutionTable.IFo ); } - // When we get to the end, all of the entries we've accumulated - // should have a lookup defined + /* + * When we get to the end, all of the entries we've accumulated + * should have a lookup defined + */ for (const { entry, substitutions } of currentEntries) { entry.lookup = { substitutions, diff --git a/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts b/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts index 334e6a566..2f8290824 100644 --- a/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts +++ b/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts @@ -53,8 +53,10 @@ export default function buildTree(table: ChainingContextualSubstitutionTable.IFo ); } - // When we get to the end, all of the entries we've accumulated - // should have a lookup defined + /* + * When we get to the end, all of the entries we've accumulated + * should have a lookup defined + */ for (const { entry, substitutions } of currentEntries) { entry.lookup = { substitutions, diff --git a/addons/addon-ligatures/src/fontLigatures/processors/helper.ts b/addons/addon-ligatures/src/fontLigatures/processors/helper.ts index e6259b6d4..6bb6060d1 100644 --- a/addons/addon-ligatures/src/fontLigatures/processors/helper.ts +++ b/addons/addon-ligatures/src/fontLigatures/processors/helper.ts @@ -57,8 +57,10 @@ export function processLookaheadPosition( range: [] }; - // All glyphs at this position share ONE entry - lookahead just needs to match, - // all paths lead to the same result + /* + * All glyphs at this position share ONE entry - lookahead just needs to match, + * all paths lead to the same result + */ const sharedEntry: ILookupTreeEntry = {}; for (const glyph of glyphs) { @@ -100,8 +102,10 @@ export function processBacktrackPosition( range: [] }; - // All glyphs at this position share ONE entry - backtrack just needs to match, - // all paths lead to the same result + /* + * All glyphs at this position share ONE entry - backtrack just needs to match, + * all paths lead to the same result + */ const sharedEntry: ILookupTreeEntry = {}; for (const glyph of glyphs) { diff --git a/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts b/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts index 3a41e936c..7e461b87c 100644 --- a/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts +++ b/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts @@ -49,8 +49,10 @@ export function getIndividualSubstitutionGlyph(table: SubstitutionTable, glyphId switch (table.substFormat) { // https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#11-single-substitution-format-1 case 1: - // TODO: determine if there's a rhyme or reason to the 16-bit - // wraparound and if it can ever be a different number + /* + * TODO: determine if there's a rhyme or reason to the 16-bit + * wraparound and if it can ever be a different number + */ return (glyphId + table.deltaGlyphId) % (2 ** 16); // https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#12-single-substitution-format-2 case 2: diff --git a/addons/addon-ligatures/src/index.ts b/addons/addon-ligatures/src/index.ts index fb8e85e73..33990b773 100644 --- a/addons/addon-ligatures/src/index.ts +++ b/addons/addon-ligatures/src/index.ts @@ -15,8 +15,10 @@ const enum LoadingState { FAILED } -// Caches 100K characters worth of ligatures. In practice this works out to -// about 650 KB worth of cache, when a moderate number of ligatures are present. +/* + * Caches 100K characters worth of ligatures. In practice this works out to + * about 650 KB worth of cache, when a moderate number of ligatures are present. + */ const CACHE_SIZE = 100000; /** @@ -46,8 +48,10 @@ export function enableLigatures(term: Terminal, fallbackLigatures: string[] = [] load(currentCallFontName, CACHE_SIZE) .then(f => { - // Another request may have come in while we were waiting, so make - // sure our font is still vaild. + /* + * Another request may have come in while we were waiting, so make + * sure our font is still vaild. + */ if (currentCallFontName === term.options.fontFamily) { loadingState = LoadingState.LOADED; font = f; @@ -59,8 +63,10 @@ export function enableLigatures(term: Terminal, fallbackLigatures: string[] = [] } }) .catch(e => { - // Another request may have come in while we were waiting, so make - // sure our font is still vaild. + /* + * Another request may have come in while we were waiting, so make + * sure our font is still vaild. + */ if (currentCallFontName === term.options.fontFamily) { loadingState = LoadingState.FAILED; if (term.options.logLevel === 'debug') { @@ -73,8 +79,10 @@ export function enableLigatures(term: Terminal, fallbackLigatures: string[] = [] } if (font && loadingState === LoadingState.LOADED) { - // We clone the entries to avoid the internal cache of the ligature finder - // getting messed up. + /* + * We clone the entries to avoid the internal cache of the ligature finder + * getting messed up. + */ return font.findLigatureRanges(text).map<[number, number]>( range => [range[0], range[1]] ); diff --git a/addons/addon-ligatures/src/parse.ts b/addons/addon-ligatures/src/parse.ts index 6289e4681..7cf7ad2f8 100644 --- a/addons/addon-ligatures/src/parse.ts +++ b/addons/addon-ligatures/src/parse.ts @@ -27,8 +27,10 @@ export default function parse(family: string): string[] { const families = []; let currentFamily = ''; - // Work through the input character by character until there are none left. - // This lexing and parsing in one pass. + /* + * Work through the input character by character until there are none left. + * This lexing and parsing in one pass. + */ while (context.offset < context.input.length) { const char = context.input[context.offset++]; switch (char) { @@ -152,13 +154,17 @@ function parseUnicode(context: IParseContext): string { while (context.offset < context.input.length) { const char = context.input[context.offset++]; if (/\s/.test(char)) { - // The first whitespace character after a unicode escape indicates the end - // of the escape and is swallowed. + /* + * The first whitespace character after a unicode escape indicates the end + * of the escape and is swallowed. + */ return unicodeToString(str); } if (str.length >= 6 || !/[\dA-Fa-f]/.test(char)) { - // If the next character is not a valid hex digit or we have reached the - // maximum of 6 digits in the escape, terminate the escape. + /* + * If the next character is not a valid hex digit or we have reached the + * maximum of 6 digits in the escape, terminate the escape. + */ context.offset--; return unicodeToString(str); } diff --git a/addons/addon-search/src/SearchEngine.ts b/addons/addon-search/src/SearchEngine.ts index b9991974d..cbff3eb8e 100644 --- a/addons/addon-search/src/SearchEngine.ts +++ b/addons/addon-search/src/SearchEngine.ts @@ -261,8 +261,10 @@ export class SearchEngine { return; } - // This will iterate until we find the line start. - // When we find it, we will search using the calculated start column. + /* + * This will iterate until we find the line start. + * When we find it, we will search using the calculated start column. + */ searchPosition.startRow--; searchPosition.startCol += this._terminal.cols; return this._findInLine(term, searchPosition, searchOptions); @@ -315,8 +317,10 @@ export class SearchEngine { return; } - // Adjust the row number and search index if needed since a "line" of text can span multiple - // rows + /* + * Adjust the row number and search index if needed since a "line" of text can span multiple + * rows + */ let startRowOffset = 0; while (startRowOffset < offsets.length - 1 && resultIndex >= offsets[startRowOffset + 1]) { startRowOffset++; @@ -355,8 +359,10 @@ export class SearchEngine { if (char.length > 1) { offset -= char.length - 1; } - // Adjust the searchIndex for empty characters following wide unicode - // chars (eg. CJK) + /* + * Adjust the searchIndex for empty characters following wide unicode + * chars (eg. CJK) + */ const nextCell = line.getCell(i + 1); if (nextCell && nextCell.getWidth() === 0) { offset++; diff --git a/addons/addon-search/src/SearchLineCache.ts b/addons/addon-search/src/SearchLineCache.ts index 526f4bfcc..d62f47cd8 100644 --- a/addons/addon-search/src/SearchLineCache.ts +++ b/addons/addon-search/src/SearchLineCache.ts @@ -38,8 +38,10 @@ export class SearchLineCache extends Disposable { private _linesCache: LineCacheEntry[] | undefined; private _linesCacheTimeout = this._register(new MutableDisposable()); private _linesCacheDisposables = this._register(new MutableDisposable()); - // Track access to avoid recreating a timeout on every init call which occurs once per search - // result (findNext/findPrevious -> _highlightAllMatches -> find loop). + /* + * Track access to avoid recreating a timeout on every init call which occurs once per search + * result (findNext/findPrevious -> _highlightAllMatches -> find loop). + */ private _lastAccessTimestamp = 0; constructor(private readonly _terminal: Terminal) { diff --git a/addons/addon-serialize/src/SerializeAddon.ts b/addons/addon-serialize/src/SerializeAddon.ts index 4980c8eda..1d46fd918 100644 --- a/addons/addon-serialize/src/SerializeAddon.ts +++ b/addons/addon-serialize/src/SerializeAddon.ts @@ -84,8 +84,10 @@ function equalBg(cell1: IBufferCell | IAttributeData, cell2: IBufferCell): boole } function equalUnderline(cell1: IBufferCell | IAttributeData, cell2: IBufferCell): boolean { - // If neither cell has underline, consider them equal regardless of internal underline color - // values + /* + * If neither cell has underline, consider them equal regardless of internal underline color + * values + */ if (!cell1.isUnderline() && !cell2.isUnderline()) { return true; } @@ -134,14 +136,18 @@ class StringSerializeHandler extends BaseSerializeHandler { private _currentRow: string = ''; private _nullCellCount: number = 0; - // we can see a full colored cell and a null cell that only have background the same style - // but the information isn't preserved by null cell itself - // so wee need to record it when required. + /* + * we can see a full colored cell and a null cell that only have background the same style + * but the information isn't preserved by null cell itself + * so wee need to record it when required. + */ private _cursorStyle: IBufferCell = this._buffer.getNullCell(); - // where exact the cursor styles comes from - // because we can't copy the cell directly - // so we remember where the content comes from instead + /* + * where exact the cursor styles comes from + * because we can't copy the cell directly + * so we remember where the content comes from instead + */ private _cursorStyleRow: number = 0; private _cursorStyleCol: number = 0; @@ -172,8 +178,10 @@ class StringSerializeHandler extends BaseSerializeHandler { private _thisRowLastSecondChar: IBufferCell = this._buffer.getNullCell(); private _nextRowFirstChar: IBufferCell = this._buffer.getNullCell(); protected _rowEnd(row: number, isLastRow: boolean): void { - // if there is colorful empty cell at line end, whe must pad it back, or the the color block - // will missing + /* + * if there is colorful empty cell at line end, whe must pad it back, or the the color block + * will missing + */ if (this._nullCellCount > 0 && !equalBg(this._cursorStyle, this._backgroundCell)) { // use clear right to set background. this._currentRow += `\u001b[${this._nullCellCount}X`; @@ -206,8 +214,10 @@ class StringSerializeHandler extends BaseSerializeHandler { const nextRowFirstChar = nextLine.getCell(0, this._nextRowFirstChar)!; const isNextRowFirstCharDoubleWidth = nextRowFirstChar.getWidth() > 1; - // validate whether this line wrap is ever possible - // which mean whether cursor can placed at a overflow position (x === row) naturally + /* + * validate whether this line wrap is ever possible + * which mean whether cursor can placed at a overflow position (x === row) naturally + */ let isValid = false; if ( @@ -216,23 +226,31 @@ class StringSerializeHandler extends BaseSerializeHandler { isNextRowFirstCharDoubleWidth ? this._nullCellCount <= 1 : this._nullCellCount <= 0 ) { if ( - // the last character can't be null, - // you can't use control sequence to move cursor to (x === row) + /* + * the last character can't be null, + * you can't use control sequence to move cursor to (x === row) + */ (thisRowLastChar.getChars() || thisRowLastChar.getWidth() === 0) && - // change background of the first wrapped cell also affects BCE - // so we mark it as invalid to simply the process to determine line separator + /* + * change background of the first wrapped cell also affects BCE + * so we mark it as invalid to simply the process to determine line separator + */ equalBg(thisRowLastChar, nextRowFirstChar) ) { isValid = true; } if ( - // the second to last character can't be null if the next line starts with CJK, - // you can't use control sequence to move cursor to (x === row) + /* + * the second to last character can't be null if the next line starts with CJK, + * you can't use control sequence to move cursor to (x === row) + */ isNextRowFirstCharDoubleWidth && (thisRowLastSecondChar.getChars() || thisRowLastSecondChar.getWidth() === 0) && - // change background of the first wrapped cell also affects BCE - // so we mark it as invalid to simply the process to determine line separator + /* + * change background of the first wrapped cell also affects BCE + * so we mark it as invalid to simply the process to determine line separator + */ equalBg(thisRowLastChar, nextRowFirstChar) && equalBg(thisRowLastSecondChar, nextRowFirstChar) ) { @@ -241,8 +259,10 @@ class StringSerializeHandler extends BaseSerializeHandler { } if (!isValid) { - // force the wrap with magic - // insert enough character to force the wrap + /* + * force the wrap with magic + * insert enough character to force the wrap + */ rowSeparator = '-'.repeat(this._nullCellCount + 1); // move back and erase next line head rowSeparator += '\u001b[1D\u001b[1X'; @@ -256,8 +276,10 @@ class StringSerializeHandler extends BaseSerializeHandler { rowSeparator += '\u001b[B'; } - // This is content and need the be serialized even it is invisible. - // without this, wrap will be missing from outputs. + /* + * This is content and need the be serialized even it is invisible. + * without this, wrap will be missing from outputs. + */ this._lastContentCursorRow = row + 1; this._lastContentCursorCol = 0; @@ -358,8 +380,10 @@ class StringSerializeHandler extends BaseSerializeHandler { const sgrSeq = this._diffStyle(cell, this._cursorStyle); - // the empty cell style is only assumed to be changed when background changed, because - // foreground is always 0. + /* + * the empty cell style is only assumed to be changed when background changed, because + * foreground is always 0. + */ const styleChanged = isEmptyCell ? !equalBg(this._cursorStyle, cell) : sgrSeq.length > 0; /** @@ -398,9 +422,11 @@ class StringSerializeHandler extends BaseSerializeHandler { this._nullCellCount += cell.getWidth(); } else { if (this._nullCellCount > 0) { - // we can just assume we have same style with previous one here - // because style change is handled by previous stage - // use move right when background is empty, use clear right when there is background. + /* + * we can just assume we have same style with previous one here + * because style change is handled by previous stage + * use move right when background is empty, use clear right when there is background. + */ if (equalBg(this._cursorStyle, this._backgroundCell)) { this._currentRow += `\u001b[${this._nullCellCount}C`; } else { @@ -421,8 +447,10 @@ class StringSerializeHandler extends BaseSerializeHandler { protected _serializeString(excludeFinalCursorPosition: boolean): string { let rowEnd = this._allRows.length; - // the fixup is only required for data without scrollback - // because it will always be placed at last line otherwise + /* + * the fixup is only required for data without scrollback + * because it will always be placed at last line otherwise + */ if (this._buffer.length - this._firstRow <= this._terminal.rows) { rowEnd = this._lastContentCursorRow + 1 - this._firstRow; this._lastCursorCol = this._lastContentCursorCol; @@ -466,9 +494,11 @@ class StringSerializeHandler extends BaseSerializeHandler { } } - // Restore the cursor's current style, see https://github.com/xtermjs/xterm.js/issues/3677 - // HACK: Internal API access since it's awkward to expose this in the API and serialize will - // likely be the only consumer + /* + * Restore the cursor's current style, see https://github.com/xtermjs/xterm.js/issues/3677 + * HACK: Internal API access since it's awkward to expose this in the API and serialize will + * likely be the only consumer + */ const curAttrData: IAttributeData = (this._terminal as any)._core._inputHandler._curAttrData; const sgrSeq = this._diffStyle(curAttrData, this._cursorStyle); if (sgrSeq.length > 0) { @@ -581,8 +611,10 @@ export class SerializeAddon implements ITerminalAddon, ISerializeApi { } } - // Cursor visibility (DECTCEM) - // Default: visible + /* + * Cursor visibility (DECTCEM) + * Default: visible + */ if (!modes.showCursor) { content += '\x1b[?25l'; } diff --git a/addons/addon-serialize/test/SerializeAddon.test.ts b/addons/addon-serialize/test/SerializeAddon.test.ts index fb42d807d..02188146d 100644 --- a/addons/addon-serialize/test/SerializeAddon.test.ts +++ b/addons/addon-serialize/test/SerializeAddon.test.ts @@ -416,11 +416,13 @@ test.describe('SerializeAddon', () => { '中文中文', '12中文', '中文12', - // This line is going to be wrapped at last character - // because it has line length of 11 (1+2*5). - // We concat it back without the null cell currently. - // But this may be incorrect. - // see also #3097 + /* + * This line is going to be wrapped at last character + * because it has line length of 11 (1+2*5). + * We concat it back without the null cell currently. + * But this may be incorrect. + * see also #3097 + */ '1中文中文中' ]; await ctx.proxy.write(lines.join('\r\n')); @@ -503,9 +505,11 @@ test.describe('SerializeAddon', () => { const MOVE_DOWN = (l: number): string => `\u001b[${l}B`; const MOVE_LEFT = (l: number): string => `\u001b[${l}D`; - // A line wrap happened after current line. - // But there is no content. - // so wrap shouldn't even be able to happen. + /* + * A line wrap happened after current line. + * But there is no content. + * so wrap shouldn't even be able to happen. + */ const segments = [ `123456789012345`, MOVE_UP(1), @@ -531,9 +535,11 @@ test.describe('SerializeAddon', () => { 10 ); - // A line wrap happened after current line. - // But there is no content. - // so wrap shouldn't even be able to happen. + /* + * A line wrap happened after current line. + * But there is no content. + * so wrap shouldn't even be able to happen. + */ const lines = [ padLines.join('\r\n'), '\r\n', diff --git a/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts b/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts index 3d5717a0f..e68185f76 100644 --- a/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts +++ b/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts @@ -22,9 +22,11 @@ export class UnicodeGraphemeProvider implements IUnicodeVersionProvider { = UnicodeService.createPropertyValue(UC.GRAPHEME_BREAK_Other, 1, false); public charProperties(codepoint: number, preceding: UnicodeCharProperties): UnicodeCharProperties { - // Optimize the simple ASCII case, under the condition that - // UnicodeService.extractCharKind(preceding) === GRAPHEME_BREAK_Other - // (which also covers the case that preceding === 0). + /* + * Optimize the simple ASCII case, under the condition that + * UnicodeService.extractCharKind(preceding) === GRAPHEME_BREAK_Other + * (which also covers the case that preceding === 0). + */ if ((codepoint >= 32 && codepoint < 127) && (preceding >> 3) === 0) { return UnicodeGraphemeProvider._plainNarrowProperties; } diff --git a/addons/addon-web-fonts/test/WebFontsAddon.test.ts b/addons/addon-web-fonts/test/WebFontsAddon.test.ts index c94bff84d..100e34f55 100644 --- a/addons/addon-web-fonts/test/WebFontsAddon.test.ts +++ b/addons/addon-web-fonts/test/WebFontsAddon.test.ts @@ -21,8 +21,10 @@ test.describe('WebFontsAddon', () => { deepStrictEqual(empty, []); }); test.afterEach(async () => { - // for font loading tests to work, we have to remove added rules and fonts - // to work around the quite aggressive font caching done by the browsers + /* + * for font loading tests to work, we have to remove added rules and fonts + * to work around the quite aggressive font caching done by the browsers + */ await ctx.page.evaluate(` document.styleSheets[0].deleteRule(1); document.styleSheets[0].deleteRule(0); @@ -73,8 +75,10 @@ test.describe('WebFontsAddon', () => { }); test('autoload & relayout from ctor', async ({ browser }) => { - // to make this test work, we exclude the default measurement char W (x57) by restricting unicode-range - // now the browser will postpone font loading until codepoint is hit --> wrong glyph metrics on first usage + /* + * to make this test work, we exclude the default measurement char W (x57) by restricting unicode-range + * now the browser will postpone font loading until codepoint is hit --> wrong glyph metrics on first usage + */ const data = await ctx.page.evaluate(` document.styleSheets[0].insertRule("@font-face {font-family: Kongtext; src: url(/fonts/kongtext.regular.ttf) format('truetype'); unicode-range: U+00A0-00FF}", 0); `); @@ -97,8 +101,10 @@ test.describe('WebFontsAddon', () => { deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'unloaded' }]); } - // good case: addon fixes layout for webfont in ctor - // the relayout happens async, so wait a bit with a promise + /* + * good case: addon fixes layout for webfont in ctor + * the relayout happens async, so wait a bit with a promise + */ await ctx.page.evaluate(` window.helperTerm.dispose(); window.helperTerm = new Terminal({fontFamily: '"Kongtext", ' + term.options.fontFamily}); diff --git a/addons/addon-web-links/src/WebLinkProvider.ts b/addons/addon-web-links/src/WebLinkProvider.ts index efaead5c4..3f4a894dd 100644 --- a/addons/addon-web-links/src/WebLinkProvider.ts +++ b/addons/addon-web-links/src/WebLinkProvider.ts @@ -73,8 +73,10 @@ export class LinkComputer { continue; } - // map string positions back to buffer positions - // values are 0-based right side excluding + /* + * map string positions back to buffer positions + * values are 0-based right side excluding + */ const [startY, startX] = LinkComputer._mapStrIdx(terminal, startLineIndex, 0, match.index); const [endY, endX] = LinkComputer._mapStrIdx(terminal, startY, startX, text.length); @@ -172,11 +174,13 @@ export class LinkComputer { if (width) { stringIndex -= chars.length || 1; - // correct stringIndex for early wrapped wide chars: - // - currently only happens at last cell - // - cells to the right are reset with chars='' and width=1 in InputHandler.print - // - follow-up line must be wrapped and contain wide char at first cell - // --> if all these conditions are met, correct stringIndex by +1 + /* + * correct stringIndex for early wrapped wide chars: + * - currently only happens at last cell + * - cells to the right are reset with chars='' and width=1 in InputHandler.print + * - follow-up line must be wrapped and contain wide char at first cell + * --> if all these conditions are met, correct stringIndex by +1 + */ if (i === line.length - 1 && chars === '') { const line = buf.getLine(lineIndex + 1); if (line && line.isWrapped) { diff --git a/addons/addon-web-links/src/WebLinksAddon.ts b/addons/addon-web-links/src/WebLinksAddon.ts index 3328a3c3f..0a7171987 100644 --- a/addons/addon-web-links/src/WebLinksAddon.ts +++ b/addons/addon-web-links/src/WebLinksAddon.ts @@ -7,17 +7,19 @@ import type { Terminal, ITerminalAddon, IDisposable } from '@xterm/xterm'; import type { WebLinksAddon as IWebLinksApi } from '@xterm/addon-web-links'; import { ILinkProviderOptions, WebLinkProvider } from './WebLinkProvider'; -// consider everthing starting with http:// or https:// -// up to first whitespace, `"` or `'` as url -// NOTE: The repeated end clause is needed to not match a dangling `:` -// resembling the old (...)*([^:"\'\\s]) final path clause -// additionally exclude early + final: -// - unsafe from rfc3986: !*'() -// - unsafe chars from rfc1738: {}|\^~[]` (minus [] as we need them for ipv6 adresses, also allow ~) -// also exclude as finals: -// - final interpunction like ,.!? -// - any sort of brackets <>()[]{} (not spec conform, but often used to enclose urls) -// - unsafe chars from rfc1738: {}|\^~[]` +/* + * consider everthing starting with http:// or https:// + * up to first whitespace, `"` or `'` as url + * NOTE: The repeated end clause is needed to not match a dangling `:` + * resembling the old (...)*([^:"\'\\s]) final path clause + * additionally exclude early + final: + * - unsafe from rfc3986: !*'() + * - unsafe chars from rfc1738: {}|\^~[]` (minus [] as we need them for ipv6 adresses, also allow ~) + * also exclude as finals: + * - final interpunction like ,.!? + * - any sort of brackets <>()[]{} (not spec conform, but often used to enclose urls) + * - unsafe chars from rfc1738: {}|\^~[]` + */ const strictUrlRegex = /(https?|HTTPS?):[/]{2}[^\s"'!*(){}|\\\^<>`]*[^\s"':,.!?{}|\\\^~\[\]`()<>]/; diff --git a/addons/addon-webgl/src/CellColorResolver.ts b/addons/addon-webgl/src/CellColorResolver.ts index 3a4661cc3..fe8baa35a 100644 --- a/addons/addon-webgl/src/CellColorResolver.ts +++ b/addons/addon-webgl/src/CellColorResolver.ts @@ -47,8 +47,10 @@ export class CellColorResolver { this.result.bg = cell.bg; this.result.fg = cell.fg; this.result.ext = cell.bg & BgFlags.HAS_EXTENDED ? cell.extended.ext : 0; - // Get any foreground/background overrides, this happens on the model to avoid spreading - // override logic throughout the different sub-renderers + /* + * Get any foreground/background overrides, this happens on the model to avoid spreading + * override logic throughout the different sub-renderers + */ // Reset overrides work variables $bg = 0; @@ -186,8 +188,10 @@ export class CellColorResolver { } }); - // Convert any overrides from rgba to the fg/bg packed format. This resolves the inverse flag - // ahead of time in order to use the correct cache key + /* + * Convert any overrides from rgba to the fg/bg packed format. This resolves the inverse flag + * ahead of time in order to use the correct cache key + */ if ($hasBg) { if ($isSelected) { // Non-RGB attributes from model + force non-dim + override + force RGB color mode @@ -202,8 +206,10 @@ export class CellColorResolver { $fg = (cell.fg & ~Attributes.RGB_MASK & ~FgFlags.INVERSE) | $fg | Attributes.CM_RGB; } - // Handle case where inverse was specified by only one of bg override or fg override was set, - // resolving the other inverse color and setting the inverse flag if needed. + /* + * Handle case where inverse was specified by only one of bg override or fg override was set, + * resolving the other inverse color and setting the inverse flag if needed. + */ if (this.result.fg & FgFlags.INVERSE) { if ($hasBg && !$hasFg) { // Resolve bg color type (default color has a different meaning in fg vs bg) diff --git a/addons/addon-webgl/src/CharAtlasCache.ts b/addons/addon-webgl/src/CharAtlasCache.ts index 823a74cc6..5581ea22d 100644 --- a/addons/addon-webgl/src/CharAtlasCache.ts +++ b/addons/addon-webgl/src/CharAtlasCache.ts @@ -13,8 +13,10 @@ import type { ILogService } from 'common/services/Services'; interface ITextureAtlasCacheEntry { atlas: ITextureAtlas; config: ICharAtlasConfig; - // N.B. This implementation potentially holds onto copies of the terminal forever, so - // this may cause memory leaks. + /* + * N.B. This implementation potentially holds onto copies of the terminal forever, so + * this may cause memory leaks. + */ ownedBy: Terminal[]; } diff --git a/addons/addon-webgl/src/CharAtlasUtils.ts b/addons/addon-webgl/src/CharAtlasUtils.ts index 4ce103e98..15e796256 100644 --- a/addons/addon-webgl/src/CharAtlasUtils.ts +++ b/addons/addon-webgl/src/CharAtlasUtils.ts @@ -25,8 +25,10 @@ export function generateConfig(deviceCellWidth: number, deviceCellHeight: number scrollbarSliderBackground: NULL_COLOR, scrollbarSliderHoverBackground: NULL_COLOR, scrollbarSliderActiveBackground: NULL_COLOR, - // For the static char atlas, we only use the first 16 colors, but we need all 256 for the - // dynamic character atlas. + /* + * For the static char atlas, we only use the first 16 colors, but we need all 256 for the + * dynamic character atlas. + */ ansi: colors.ansi.slice(), contrastCache: colors.contrastCache, halfContrastCache: colors.halfContrastCache diff --git a/addons/addon-webgl/src/Constants.ts b/addons/addon-webgl/src/Constants.ts index a00434d61..af08fe8bb 100644 --- a/addons/addon-webgl/src/Constants.ts +++ b/addons/addon-webgl/src/Constants.ts @@ -6,7 +6,9 @@ import { isFirefox, isLegacyEdge } from 'common/Platform'; export const DIM_OPACITY = 0.5; -// The text baseline is set conditionally by browser. Using 'ideographic' for Firefox or Legacy Edge -// would result in truncated text (Issue 3353). Using 'bottom' for Chrome would result in slightly -// unaligned Powerline fonts (PR 3356#issuecomment-850928179). +/* + * The text baseline is set conditionally by browser. Using 'ideographic' for Firefox or Legacy Edge + * would result in truncated text (Issue 3353). Using 'bottom' for Chrome would result in slightly + * unaligned Powerline fonts (PR 3356#issuecomment-850928179). + */ export const TEXT_BASELINE: CanvasTextBaseline = isFirefox || isLegacyEdge ? 'bottom' : 'ideographic'; diff --git a/addons/addon-webgl/src/CursorBlinkStateManager.ts b/addons/addon-webgl/src/CursorBlinkStateManager.ts index 688aee7b7..1e9695c94 100644 --- a/addons/addon-webgl/src/CursorBlinkStateManager.ts +++ b/addons/addon-webgl/src/CursorBlinkStateManager.ts @@ -87,13 +87,17 @@ export class CursorBlinkStateManager { this._blinkInterval = undefined; } - // Setup the initial timeout which will hide the cursor, this is done before - // the regular interval is setup in order to support restarting the blink - // animation in a lightweight way (without thrashing clearInterval and - // setInterval). + /* + * Setup the initial timeout which will hide the cursor, this is done before + * the regular interval is setup in order to support restarting the blink + * animation in a lightweight way (without thrashing clearInterval and + * setInterval). + */ this._blinkStartTimeout = this._coreBrowserService.window.setTimeout(() => { - // Check if another animation restart was requested while this was being - // started + /* + * Check if another animation restart was requested while this was being + * started + */ if (this._animationTimeRestarted) { const time = Constants.BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted); this._animationTimeRestarted = undefined; @@ -114,8 +118,10 @@ export class CursorBlinkStateManager { this._blinkInterval = this._coreBrowserService.window.setInterval(() => { // Adjust the animation time if it was restarted if (this._animationTimeRestarted) { - // calc time diff - // Make restart interval do a setTimeout initially? + /* + * calc time diff + * Make restart interval do a setTimeout initially? + */ const time = Constants.BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted); this._animationTimeRestarted = undefined; this._restartInterval(time); diff --git a/addons/addon-webgl/src/DevicePixelObserver.ts b/addons/addon-webgl/src/DevicePixelObserver.ts index dcd8de0a6..c7af6a1bb 100644 --- a/addons/addon-webgl/src/DevicePixelObserver.ts +++ b/addons/addon-webgl/src/DevicePixelObserver.ts @@ -6,10 +6,12 @@ import { toDisposable, IDisposable } from 'common/Lifecycle'; export function observeDevicePixelDimensions(element: HTMLElement, parentWindow: Window & typeof globalThis, callback: (deviceWidth: number, deviceHeight: number) => void): IDisposable { - // Observe any resizes to the element and extract the actual pixel size of the element if the - // devicePixelContentBoxSize API is supported. This allows correcting rounding errors when - // converting between CSS pixels and device pixels which causes blurry rendering when device - // pixel ratio is not a round number. + /* + * Observe any resizes to the element and extract the actual pixel size of the element if the + * devicePixelContentBoxSize API is supported. This allows correcting rounding errors when + * converting between CSS pixels and device pixels which causes blurry rendering when device + * pixel ratio is not a round number. + */ let observer: ResizeObserver | undefined = new parentWindow.ResizeObserver((entries) => { const entry = entries.find((entry) => entry.target === element); if (!entry) { diff --git a/addons/addon-webgl/src/GlyphRenderer.ts b/addons/addon-webgl/src/GlyphRenderer.ts index 46b8efeb9..82ca01b63 100644 --- a/addons/addon-webgl/src/GlyphRenderer.ts +++ b/addons/addon-webgl/src/GlyphRenderer.ts @@ -148,9 +148,11 @@ export class GlyphRenderer extends Disposable { gl.enableVertexAttribArray(VertexAttribLocations.UNIT_QUAD); gl.vertexAttribPointer(VertexAttribLocations.UNIT_QUAD, 2, this._gl.FLOAT, false, 0, 0); - // Setup the unit quad element array buffer, this points to indices in - // unitQuadVertices to allow is to draw 2 triangles from the vertices via a - // triangle strip + /* + * Setup the unit quad element array buffer, this points to indices in + * unitQuadVertices to allow is to draw 2 triangles from the vertices via a + * triangle strip + */ const unitQuadElementIndices = new Uint8Array([0, 1, 2, 3]); const elementIndicesBuffer = gl.createBuffer(); this._register(toDisposable(() => gl.deleteBuffer(elementIndicesBuffer))); @@ -189,8 +191,10 @@ export class GlyphRenderer extends Disposable { gl.uniform1iv(this._textureLocation, textureUnits); gl.uniformMatrix4fv(this._projectionLocation, false, PROJECTION_MATRIX); - // Setup 1x1 red pixel textures for all potential atlas pages, if one of these invalid textures - // is ever drawn it will show characters as red rectangles. + /* + * Setup 1x1 red pixel textures for all potential atlas pages, if one of these invalid textures + * is ever drawn it will show characters as red rectangles. + */ this._atlasTextures = []; for (let i = 0; i < TextureAtlas.maxAtlasPages; i++) { const glTexture = new GLTexture(throwIfFalsy(gl.createTexture())); @@ -216,18 +220,22 @@ export class GlyphRenderer extends Disposable { } public updateCell(x: number, y: number, code: number, bg: number, fg: number, ext: number, chars: string, width: number, lastBg: number): void { - // Since this function is called for every cell (`rows*cols`), it must be very optimized. It - // should not instantiate any variables unless a new glyph is drawn to the cache where the - // slight slowdown is acceptable for the developer ergonomics provided as it's a once of for - // each glyph. + /* + * Since this function is called for every cell (`rows*cols`), it must be very optimized. It + * should not instantiate any variables unless a new glyph is drawn to the cache where the + * slight slowdown is acceptable for the developer ergonomics provided as it's a once of for + * each glyph. + */ this._updateCell(this._vertices.attributes, x, y, code, bg, fg, ext, chars, width, lastBg); } private _updateCell(array: Float32Array, x: number, y: number, code: number | undefined, bg: number, fg: number, ext: number, chars: string, width: number, lastBg: number): void { $i = (y * this._terminal.cols + x) * Constants.INDICES_PER_CELL; - // Exit early if this is a null character, allow space character to continue as it may have - // underline/strikethrough styles + /* + * Exit early if this is a null character, allow space character to continue as it may have + * underline/strikethrough styles + */ if (code === NULL_CELL_CODE || code === undefined/* This is used for the right side of wide chars */) { array.fill(0, $i, $i + Constants.INDICES_PER_CELL - 1 - Constants.CELL_POSITION_INDICES); return; @@ -279,8 +287,10 @@ export class GlyphRenderer extends Disposable { } // a_cellpos only changes on resize - // Reduce scale horizontally for wide glyphs printed in cells that would overlap with the - // following cell (ie. the width is not 2). + /* + * Reduce scale horizontally for wide glyphs printed in cells that would overlap with the + * following cell (ie. the width is not 2). + */ if (this._optionsService.rawOptions.rescaleOverlappingGlyphs) { if (allowRescaling(code, width, $glyph.size.x, this._dimensions.device.cell.width)) { array[$i + 2] = (this._dimensions.device.cell.width - 1) / this._dimensions.device.canvas.width; // - 1 to improve readability @@ -339,13 +349,15 @@ export class GlyphRenderer extends Disposable { this._activeBuffer = (this._activeBuffer + 1) % 2; const activeBuffer = this._vertices.attributesBuffers[this._activeBuffer]; - // Copy data for each cell of each line up to its line length (the last non-whitespace cell) - // from the attributes buffer into activeBuffer, which is the one that gets bound to the GPU. - // The reasons for this are as follows: - // - So the active buffer can be alternated so we don't get blocked on rendering finishing - // - To copy either the normal attributes buffer or the selection attributes buffer when there - // is a selection - // - So we don't send vertices for all the line-ending whitespace to the GPU + /* + * Copy data for each cell of each line up to its line length (the last non-whitespace cell) + * from the attributes buffer into activeBuffer, which is the one that gets bound to the GPU. + * The reasons for this are as follows: + * - So the active buffer can be alternated so we don't get blocked on rendering finishing + * - To copy either the normal attributes buffer or the selection attributes buffer when there + * is a selection + * - So we don't send vertices for all the line-ending whitespace to the GPU + */ let bufferLength = 0; for (let y = 0; y < renderModel.lineLengths.length; y++) { const si = y * this._terminal.cols * Constants.INDICES_PER_CELL; @@ -358,9 +370,11 @@ export class GlyphRenderer extends Disposable { gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer); gl.bufferData(gl.ARRAY_BUFFER, activeBuffer.subarray(0, bufferLength), gl.STREAM_DRAW); - // Bind the atlas page texture if they have changed. AtlasPage.version is globally - // monotonic, so a page object swap at the same index (which happens after a page merge) - // is detected by the same comparison. + /* + * Bind the atlas page texture if they have changed. AtlasPage.version is globally + * monotonic, so a page object swap at the same index (which happens after a page merge) + * is detected by the same comparison. + */ for (let i = 0; i < this._atlas.pages.length; i++) { if (this._atlas.pages[i].version !== this._atlasTextures[i].version) { this._bindAtlasPageTexture(gl, this._atlas, i); diff --git a/addons/addon-webgl/src/RectangleRenderer.ts b/addons/addon-webgl/src/RectangleRenderer.ts index 81e129e5c..e87bc690c 100644 --- a/addons/addon-webgl/src/RectangleRenderer.ts +++ b/addons/addon-webgl/src/RectangleRenderer.ts @@ -114,9 +114,11 @@ export class RectangleRenderer extends Disposable { gl.enableVertexAttribArray(VertexAttribLocations.UNIT_QUAD); gl.vertexAttribPointer(VertexAttribLocations.UNIT_QUAD, 2, this._gl.FLOAT, false, 0, 0); - // Setup the unit quad element array buffer, this points to indices in - // unitQuadVertices to allow is to draw 2 triangles from the vertices via a - // triangle strip + /* + * Setup the unit quad element array buffer, this points to indices in + * unitQuadVertices to allow is to draw 2 triangles from the vertices via a + * triangle strip + */ const unitQuadElementIndices = new Uint8Array([0, 1, 2, 3]); const elementIndicesBuffer = gl.createBuffer(); this._register(toDisposable(() => gl.deleteBuffer(elementIndicesBuffer))); diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index 34188235c..09293d4fa 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -151,13 +151,17 @@ export class TextureAtlas implements ITextureAtlas { } private _createNewPage(): AtlasPage { - // Try merge the set of the 4 most used pages of the largest size. This is is deferred to a - // microtask to ensure it does not interrupt textures that will be rendered in the current - // animation frame which would result in blank rendered areas. This is actually not that - // expensive relative to drawing the glyphs, so there is no need to wait for an idle callback. + /* + * Try merge the set of the 4 most used pages of the largest size. This is is deferred to a + * microtask to ensure it does not interrupt textures that will be rendered in the current + * animation frame which would result in blank rendered areas. This is actually not that + * expensive relative to drawing the glyphs, so there is no need to wait for an idle callback. + */ if (TextureAtlas.maxAtlasPages && this._pages.length >= Math.max(4, TextureAtlas.maxAtlasPages)) { - // Find the set of the largest 4 images, below the maximum size, with the highest - // percentages used + /* + * Find the set of the largest 4 images, below the maximum size, with the highest + * percentages used + */ const pagesBySize = this._pages.filter(e => { return e.canvas.width * 2 <= (TextureAtlas.maxTextureSize || Constants.FORCED_MAX_TEXTURE_SIZE); }).sort((a, b) => { @@ -180,8 +184,10 @@ export class TextureAtlas implements ITextureAtlas { // Gather details of the merge const mergingPages = pagesBySize.slice(sameSizeI, sameSizeI + 4); - // Only proceed with merge if we have exactly 4 same-sized pages. If not, we cannot - // effectively reduce page count and merging would cause issues. + /* + * Only proceed with merge if we have exactly 4 same-sized pages. If not, we cannot + * effectively reduce page count and merging would cause issues. + */ if (mergingPages.length < 4 || mergingPages.some(p => p.canvas.width !== mergingPages[0].canvas.width)) { const newPage = new AtlasPage(this._document, this._textureSize); this._pages.push(newPage); @@ -294,9 +300,11 @@ export class TextureAtlas implements ITextureAtlas { private _getBackgroundColor(bgColorMode: number, bgColor: number, inverse: boolean, dim: boolean): IColor { if (this._config.allowTransparency) { - // The background color might have some transparency, so we need to render it as fully - // transparent in the atlas. Otherwise we'd end up drawing the transparent background twice - // around the anti-aliased edges of the glyph, and it would look too dark. + /* + * The background color might have some transparency, so we need to render it as fully + * transparent in the atlas. Otherwise we'd end up drawing the transparent background twice + * around the anti-aliased edges of the glyph, and it would look too dark. + */ return NULL_COLOR; } @@ -418,8 +426,10 @@ export class TextureAtlas implements ITextureAtlas { const bgRgba = this._resolveBackgroundRgba(bgColorMode, bgColor, inverse); const fgRgba = this._resolveForegroundRgba(fgColorMode, fgColor, inverse, bold); - // Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from - // non-dim cells + /* + * Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from + * non-dim cells + */ const result = rgba.ensureContrastRatio(bgRgba, fgRgba, this._config.minimumContrastRatio / (dim ? 2 : 1)); if (!result) { @@ -447,20 +457,26 @@ export class TextureAtlas implements ITextureAtlas { private _drawToCache(codeOrChars: number | string, bg: number, fg: number, ext: number, restrictToCellHeight: boolean, domContainer: HTMLElement | undefined): IRasterizedGlyph { const chars = typeof codeOrChars === 'number' ? String.fromCharCode(codeOrChars) : codeOrChars; - // Uncomment for debugging - // console.log(`draw to cache "${chars}"`, bg, fg, ext); + /* + * Uncomment for debugging + * console.log(`draw to cache "${chars}"`, bg, fg, ext); + */ - // Attach the canvas to the DOM in order to inherit font-feature-settings - // from the parent elements. This is necessary for ligatures and variants to - // work. + /* + * Attach the canvas to the DOM in order to inherit font-feature-settings + * from the parent elements. This is necessary for ligatures and variants to + * work. + */ if (domContainer && this._tmpCanvas.parentElement !== domContainer) { this._tmpCanvas.style.display = 'none'; domContainer.append(this._tmpCanvas); } - // 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. + /* + * 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 = Math.min(this._config.deviceCellWidth * Math.max(chars.length, 2) + TMP_CANVAS_GLYPH_PADDING * 2, this._config.deviceMaxTextureSize); if (this._tmpCanvas.width < allowedWidth) { this._tmpCanvas.width = allowedWidth; @@ -503,8 +519,10 @@ export class TextureAtlas implements ITextureAtlas { // draw the background const backgroundColor = this._getBackgroundColor(bgColorMode, bgColor, inverse, dim); - // Use a 'copy' composite operation to clear any existing glyph out of _tmpCtxWithAlpha, - // regardless of transparency in backgroundColor + /* + * Use a 'copy' composite operation to clear any existing glyph out of _tmpCtxWithAlpha, + * regardless of transparency in backgroundColor + */ this._tmpCtx.globalCompositeOperation = 'copy'; this._tmpCtx.fillStyle = backgroundColor.css; this._tmpCtx.fillRect(0, 0, this._tmpCanvas.width, this._tmpCanvas.height); @@ -532,9 +550,11 @@ export class TextureAtlas implements ITextureAtlas { customGlyph = tryDrawCustomGlyph(this._tmpCtx, chars, padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight, this._config.deviceCharWidth, this._config.deviceCharHeight, this._config.fontSize, this._config.devicePixelRatio, backgroundColor.css, variantOffset); } - // Whether to clear pixels based on a threshold difference between the glyph color and the - // background color. This should be disabled when the glyph contains multiple colors such as - // underline colors to prevent important colors could get cleared. + /* + * Whether to clear pixels based on a threshold difference between the glyph color and the + * background color. This should be disabled when the glyph contains multiple colors such as + * underline colors to prevent important colors could get cleared. + */ let enableClearThresholdCheck = !powerlineGlyph; let chWidth: number; @@ -599,8 +619,10 @@ export class TextureAtlas implements ITextureAtlas { clipRegion.rect(xChLeft, yTop, this._config.deviceCellWidth, yBot - yTop); this._tmpCtx.clip(clipRegion); - // Draw a zigzag pattern, this is derived from the SVG used in monaco for the same - // style. The viewbox is 6x3 so scale it using that. + /* + * Draw a zigzag pattern, this is derived from the SVG used in monaco for the same + * style. The viewbox is 6x3 so scale it using that. + */ const cellW = this._config.deviceCellWidth; const curlyH = (yBot - yTop); const scaleX = cellW / 6; @@ -671,15 +693,21 @@ export class TextureAtlas implements ITextureAtlas { } this._tmpCtx.restore(); - // Draw stroke in the background color for non custom characters in order to give an outline - // between the text and the underline. Only do this when font size is >= 12 as the underline - // looks odd when the font size is too small + /* + * Draw stroke in the background color for non custom characters in order to give an outline + * between the text and the underline. Only do this when font size is >= 12 as the underline + * looks odd when the font size is too small + */ if (!customGlyph && this._config.fontSize >= 12) { - // This only works when transparency is disabled because it's not clear how to clear stroked - // text + /* + * This only works when transparency is disabled because it's not clear how to clear stroked + * text + */ if (!this._config.allowTransparency && chars !== ' ') { - // Measure the text, only draw the stroke if there is a descent beyond an alphabetic text - // baseline + /* + * Measure the text, only draw the stroke if there is a descent beyond an alphabetic text + * baseline + */ this._tmpCtx.save(); this._tmpCtx.textBaseline = 'alphabetic'; const metrics = this._tmpCtx.measureText(chars); @@ -687,9 +715,11 @@ export class TextureAtlas implements ITextureAtlas { if ('actualBoundingBoxDescent' in metrics && metrics.actualBoundingBoxDescent > 0) { // This translates to 1/2 the line width in either direction this._tmpCtx.save(); - // Clip the region to only draw in valid pixels near the underline to avoid a slight - // outline around the whole glyph, as well as additional pixels in the glyph at the top - // which would increase GPU memory demands + /* + * Clip the region to only draw in valid pixels near the underline to avoid a slight + * outline around the whole glyph, as well as additional pixels in the glyph at the top + * which would increase GPU memory demands + */ const clipRegion = new Path2D(); clipRegion.rect(xLeft, yTop - Math.ceil(lineWidth / 2), this._config.deviceCellWidth * chWidth, yBot - yTop + Math.ceil(lineWidth / 2)); this._tmpCtx.clip(clipRegion); @@ -719,8 +749,10 @@ export class TextureAtlas implements ITextureAtlas { this._tmpCtx.fillText(chars, padding, padding + this._config.deviceCharHeight); } - // 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 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); if (isBeyondCellBounds) { @@ -752,8 +784,10 @@ export class TextureAtlas implements ITextureAtlas { this._tmpCtx.restore(); - // clear the background from the character to avoid issues with drawing over the previous - // character if it extends past it's bounds + /* + * clear the background from the character to avoid issues with drawing over the previous + * character if it extends past it's bounds + */ const imageData = this._tmpCtx.getImageData( 0, 0, this._tmpCanvas.width, this._tmpCanvas.height ); @@ -796,12 +830,16 @@ export class TextureAtlas implements ITextureAtlas { } } - // TODO: This algorithm could be simplified: - // - Search for the page with ROW_PIXEL_THRESHOLD in mind - // - Keep track of current/fixed rows in a Map + /* + * TODO: This algorithm could be simplified: + * - Search for the page with ROW_PIXEL_THRESHOLD in mind + * - Keep track of current/fixed rows in a Map + */ - // Replace the best current row with a fixed row if there is one at least as good as the - // current row. Search in reverse to prioritize filling in older pages. + /* + * Replace the best current row with a fixed row if there is one at least as good as the + * current row. Search in reverse to prioritize filling in older pages. + */ for (let i = this._activePages.length - 1; i >= 0; i--) { for (const row of this._activePages[i].fixedRows) { if (row.height <= activeRow.height && rasterizedGlyph.size.y <= row.height) { @@ -832,12 +870,16 @@ export class TextureAtlas implements ITextureAtlas { break; } - // Create a new page if too much vertical space would be wasted or there is not enough room - // left in the page. The previous active row will become fixed in the process as it now has a - // fixed height + /* + * Create a new page if too much vertical space would be wasted or there is not enough room + * left in the page. The previous active row will become fixed in the process as it now has a + * fixed height + */ if (activeRow.y + rasterizedGlyph.size.y >= activePage.canvas.height || activeRow.height > rasterizedGlyph.size.y + Constants.ROW_PIXEL_THRESHOLD) { - // Create the new fixed height row, creating a new page if there isn't enough room on the - // current page + /* + * Create the new fixed height row, creating a new page if there isn't enough room on the + * current page + */ let wasPageAndRowFound = false; if (activePage.currentRow.y + activePage.currentRow.height + rasterizedGlyph.size.y >= activePage.canvas.height) { // Find the first page with room to create the new row on @@ -852,10 +894,12 @@ export class TextureAtlas implements ITextureAtlas { if (candidatePage) { activePage = candidatePage; } else { - // Before creating a new atlas page that would trigger a page merge, check if the - // current active row is sufficient when ignoring the ROW_PIXEL_THRESHOLD. This will - // improve texture utilization by using the available space before the page is merged - // and becomes static. + /* + * Before creating a new atlas page that would trigger a page merge, check if the + * current active row is sufficient when ignoring the ROW_PIXEL_THRESHOLD. This will + * improve texture utilization by using the available space before the page is merged + * and becomes static. + */ if ( TextureAtlas.maxAtlasPages && this._pages.length >= TextureAtlas.maxAtlasPages && @@ -923,8 +967,10 @@ export class TextureAtlas implements ITextureAtlas { rasterizedGlyph.sizeClipSpace.x /= activePage.canvas.width; rasterizedGlyph.sizeClipSpace.y /= activePage.canvas.height; - // Update atlas current row, for fixed rows the glyph height will never be larger than the row - // height + /* + * Update atlas current row, for fixed rows the glyph height will never be larger than the row + * height + */ activeRow.height = Math.max(activeRow.height, rasterizedGlyph.size.y); activeRow.x += rasterizedGlyph.size.x; @@ -1057,16 +1103,18 @@ class AtlasPage { public static nextVersion: number = 0; public version = ++AtlasPage.nextVersion; - // Texture atlas current positioning data. The texture packing strategy used is to fill from - // left-to-right and top-to-bottom. When the glyph being written is less than half of the current - // row's height, the following happens: - // - // - The current row becomes the fixed height row A - // - A new fixed height row B the exact size of the glyph is created below the current row - // - A new dynamic height current row is created below B - // - // This strategy does a good job preventing space being wasted for very short glyphs such as - // underscores, hyphens etc. or those with underlines rendered. + /* + * Texture atlas current positioning data. The texture packing strategy used is to fill from + * left-to-right and top-to-bottom. When the glyph being written is less than half of the current + * row's height, the following happens: + * + * - The current row becomes the fixed height row A + * - A new fixed height row B the exact size of the glyph is created below the current row + * - A new dynamic height current row is created below B + * + * This strategy does a good job preventing space being wasted for very short glyphs such as + * underscores, hyphens etc. or those with underlines rendered. + */ public currentRow: ICharAtlasActiveRow = { x: 0, y: 0, @@ -1086,9 +1134,11 @@ class AtlasPage { } } this.canvas = createCanvas(document, size, size); - // The canvas needs alpha because we use clearColor to convert the background color to alpha. - // It might also contain some characters with transparent backgrounds if allowTransparency is - // set. + /* + * The canvas needs alpha because we use clearColor to convert the background color to alpha. + * It might also contain some characters with transparent backgrounds if allowTransparency is + * set. + */ this.ctx = throwIfFalsy(this.canvas.getContext('2d', { alpha: true })); } @@ -1116,12 +1166,14 @@ function clearColor(imageData: ImageData, bg: IColor, fg: IColor, enableThreshol const fgG = fg.rgba >>> 16 & 0xFF; const fgB = fg.rgba >>> 8 & 0xFF; - // Calculate a threshold that when below a color will be treated as transpart when the sum of - // channel value differs. This helps improve rendering when glyphs overlap with others. This - // threshold is calculated relative to the difference between the background and foreground to - // ensure important details of the glyph are always shown, even when the contrast ratio is low. - // The number 12 is largely arbitrary to ensure the pixels that escape the cell in the test case - // were covered (fg=#8ae234, bg=#c4a000). + /* + * Calculate a threshold that when below a color will be treated as transpart when the sum of + * channel value differs. This helps improve rendering when glyphs overlap with others. This + * threshold is calculated relative to the difference between the background and foreground to + * ensure important details of the glyph are always shown, even when the contrast ratio is low. + * The number 12 is largely arbitrary to ensure the pixels that escape the cell in the test case + * were covered (fg=#8ae234, bg=#c4a000). + */ const threshold = Math.floor((Math.abs(r - fgR) + Math.abs(g - fgG) + Math.abs(b - fgB)) / 12); // Set alpha channel of relevent pixels to 0 diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 500308bd1..2997129f2 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -84,9 +84,11 @@ export class WebglRenderer extends Disposable implements IRenderer { ) { super(); - // IMPORTANT: Canvas initialization and fetching of the context must be first in order to - // prevent possible listeners leaking and continuing to operate after the WebglRenderer has been - // discarded. + /* + * IMPORTANT: Canvas initialization and fetching of the context must be first in order to + * prevent possible listeners leaking and continuing to operate after the WebglRenderer has been + * discarded. + */ this._canvas = this._coreBrowserService.mainDocument.createElement('canvas'); const contextAttributes = { antialias: false, @@ -125,8 +127,10 @@ export class WebglRenderer extends Disposable implements IRenderer { console.log('webglcontextlost event received'); // Prevent the default behavior in order to enable WebGL context restoration. e.preventDefault(); - // Wait a few seconds to see if the 'webglcontextrestored' event is fired. - // If not, dispatch the onContextLoss notification to observers. + /* + * Wait a few seconds to see if the 'webglcontextrestored' event is fired. + * If not, dispatch the onContextLoss notification to observers. + */ this._contextRestorationTimeout = setTimeout(() => { this._contextRestorationTimeout = undefined; console.warn('webgl context not restored; firing onContextLoss'); @@ -137,8 +141,10 @@ export class WebglRenderer extends Disposable implements IRenderer { console.warn('webglcontextrestored event received'); clearTimeout(this._contextRestorationTimeout); this._contextRestorationTimeout = undefined; - // The texture atlas and glyph renderer must be fully reinitialized - // because their contents have been lost. + /* + * The texture atlas and glyph renderer must be fully reinitialized + * because their contents have been lost. + */ removeTerminalFromCache(this._terminal); this._initializeWebGLState(); this._requestRedrawViewport(); @@ -178,8 +184,10 @@ export class WebglRenderer extends Disposable implements IRenderer { } public handleDevicePixelRatioChange(): void { - // If the device pixel ratio changed, the char atlas needs to be regenerated - // and the terminal needs to refreshed + /* + * If the device pixel ratio changed, the char atlas needs to be regenerated + * and the terminal needs to refreshed + */ if (this._devicePixelRatio !== this._coreBrowserService.dpr) { this._devicePixelRatio = this._coreBrowserService.dpr; this.handleResize(this._terminal.cols, this._terminal.rows); @@ -215,8 +223,10 @@ export class WebglRenderer extends Disposable implements IRenderer { this._refreshCharAtlas(); - // Force a full refresh. Resizing `_glyphRenderer` should clear it already, - // so there is no need to clear it again here. + /* + * Force a full refresh. Resizing `_glyphRenderer` should clear it already, + * so there is no need to clear it again here. + */ this._clearModel(false); // Render synchronously to avoid flicker when the canvas is cleared @@ -368,9 +378,11 @@ export class WebglRenderer extends Disposable implements IRenderer { return; } - // 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) + /* + * 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.value.beginFrame()) { this._clearModel(true); this._updateModel(0, this._terminal.rows - 1); @@ -379,8 +391,10 @@ export class WebglRenderer extends Disposable implements IRenderer { this._updateModel(start, end); } - // A mid-update atlas page merge invalidates vertex data and may not bump the host - // page's version, so re-run the update and force a full texture rebind. + /* + * A mid-update atlas page merge invalidates vertex data and may not bump the host + * page's version, so re-run the update and force a full texture rebind. + */ let merged = false; let mergeRetries = 0; while (this._charAtlas && this._glyphRenderer.value.beginFrame() && mergeRetries++ < Constants.MERGE_RETRY_LIMIT) { @@ -408,8 +422,10 @@ export class WebglRenderer extends Disposable implements IRenderer { } else { this._cursorBlinkStateManager.clear(); } - // Request a refresh from the terminal as management of rendering is being - // moved back to the terminal + /* + * Request a refresh from the terminal as management of rendering is being + * moved back to the terminal + */ this._requestRedrawCursor(); } @@ -470,20 +486,26 @@ export class WebglRenderer extends Disposable implements IRenderer { // If true, indicates that the current character(s) to draw were joined. isJoined = false; - // Indicates whether this cell is part of a joined range that should be ignored as it cannot - // be rendered entirely, like the selection state differs across the range. + /* + * Indicates whether this cell is part of a joined range that should be ignored as it cannot + * be rendered entirely, like the selection state differs across the range. + */ isValidJoinRange = (x >= skipJoinedCheckUntilX); lastCharX = x; - // Process any joined character ranges as needed. Because of how the - // ranges are produced, we know that they are valid for the characters - // and attributes of our input. + /* + * Process any joined character ranges as needed. Because of how the + * ranges are produced, we know that they are valid for the characters + * and attributes of our input. + */ if (joinedRanges.length > 0 && x === joinedRanges[0][0] && isValidJoinRange) { range = joinedRanges.shift()!; - // If the ligature's selection state is not consistent, don't join it. This helps the - // selection render correctly regardless whether they should be joined. + /* + * If the ligature's selection state is not consistent, don't join it. This helps the + * selection render correctly regardless whether they should be joined. + */ const firstSelectionState = this._model.selection.isCellSelected(this._terminal, range[0], row); for (i = range[0] + 1; i < range[1]; i++) { isValidJoinRange &&= (firstSelectionState === this._model.selection.isCellSelected(this._terminal, i, row)); @@ -495,8 +517,10 @@ export class WebglRenderer extends Disposable implements IRenderer { } else { isJoined = true; - // We already know the exact start and end column of the joined range, - // so we get the string and width representing it directly. + /* + * We already know the exact start and end column of the joined range, + * so we get the string and width representing it directly. + */ cell = new JoinedCellData( cell, line!.translateToString(true, range[0], range[1]), @@ -586,8 +610,10 @@ export class WebglRenderer extends Disposable implements IRenderer { j = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL; this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0, 0); this._model.cells[j] = NULL_CELL_CODE; - // Don't re-resolve the cell color since multi-colored ligature backgrounds are not - // supported + /* + * Don't re-resolve the cell color since multi-colored ligature backgrounds are not + * supported + */ this._model.cells[j + RenderModelConstants.BG_OFFSET] = this._cellColorResolver.result.bg; this._model.cells[j + RenderModelConstants.FG_OFFSET] = this._cellColorResolver.result.fg; this._model.cells[j + RenderModelConstants.EXT_OFFSET] = this._cellColorResolver.result.ext; @@ -631,48 +657,64 @@ export class WebglRenderer extends Disposable implements IRenderer { return; } - // Calculate the device character width. Width is floored as it must be drawn to an integer grid - // in order for the char atlas glyphs to not be blurry. + /* + * Calculate the device character width. Width is floored as it must be drawn to an integer grid + * in order for the char atlas glyphs to not be blurry. + */ this.dimensions.device.char.width = Math.floor(this._charSizeService.width * this._devicePixelRatio); - // Calculate the device character height. Height is ceiled in case devicePixelRatio is a - // floating point number in order to ensure there is enough space to draw the character to the - // cell. + /* + * Calculate the device character height. Height is ceiled in case devicePixelRatio is a + * floating point number in order to ensure there is enough space to draw the character to the + * cell. + */ this.dimensions.device.char.height = Math.ceil(this._charSizeService.height * this._devicePixelRatio); - // Calculate the device cell height, if lineHeight is _not_ 1, the resulting value will be - // floored since lineHeight can never be lower then 1, this guarentees the device cell height - // will always be larger than device char height. + /* + * Calculate the device cell height, if lineHeight is _not_ 1, the resulting value will be + * floored since lineHeight can never be lower then 1, this guarentees the device cell height + * will always be larger than device char height. + */ this.dimensions.device.cell.height = Math.floor(this.dimensions.device.char.height * this._optionsService.rawOptions.lineHeight); - // Calculate the y offset within a cell that glyph should draw at in order for it to be centered - // correctly within the cell. + /* + * Calculate the y offset within a cell that glyph should draw at in order for it to be centered + * correctly within the cell. + */ this.dimensions.device.char.top = this._optionsService.rawOptions.lineHeight === 1 ? 0 : Math.round((this.dimensions.device.cell.height - this.dimensions.device.char.height) / 2); // Calculate the device cell width, taking the letterSpacing into account. this.dimensions.device.cell.width = this.dimensions.device.char.width + Math.round(this._optionsService.rawOptions.letterSpacing); - // Calculate the x offset with a cell that text should draw from in order for it to be centered - // correctly within the cell. + /* + * Calculate the x offset with a cell that text should draw from in order for it to be centered + * correctly within the cell. + */ this.dimensions.device.char.left = Math.floor(this._optionsService.rawOptions.letterSpacing / 2); - // Recalculate the canvas dimensions, the device dimensions define the actual number of pixel in - // the canvas + /* + * Recalculate the canvas dimensions, the device dimensions define the actual number of pixel in + * the canvas + */ this.dimensions.device.canvas.height = this._terminal.rows * this.dimensions.device.cell.height; this.dimensions.device.canvas.width = this._terminal.cols * this.dimensions.device.cell.width; - // The the size of the canvas on the page. It's important that this rounds to nearest integer - // and not ceils as browsers often have floating point precision issues where - // `window.devicePixelRatio` ends up being something like `1.100000023841858` for example, when - // it's actually 1.1. Ceiling may causes blurriness as the backing canvas image is 1 pixel too - // large for the canvas element size. + /* + * The the size of the canvas on the page. It's important that this rounds to nearest integer + * and not ceils as browsers often have floating point precision issues where + * `window.devicePixelRatio` ends up being something like `1.100000023841858` for example, when + * it's actually 1.1. Ceiling may causes blurriness as the backing canvas image is 1 pixel too + * large for the canvas element size. + */ this.dimensions.css.canvas.height = Math.round(this.dimensions.device.canvas.height / this._devicePixelRatio); this.dimensions.css.canvas.width = Math.round(this.dimensions.device.canvas.width / this._devicePixelRatio); - // Get the CSS dimensions of an individual cell. This needs to be derived from the calculated - // device pixel canvas value above. CharMeasure.width/height by itself is insufficient when the - // page is not at 100% zoom level as CharMeasure is measured in CSS pixels, but the actual char - // size on the canvas can differ. + /* + * Get the CSS dimensions of an individual cell. This needs to be derived from the calculated + * device pixel canvas value above. CharMeasure.width/height by itself is insufficient when the + * page is not at 100% zoom level as CharMeasure is measured in CSS pixels, but the actual char + * size on the canvas can differ. + */ this.dimensions.css.cell.height = this.dimensions.device.cell.height / this._devicePixelRatio; this.dimensions.css.cell.width = this.dimensions.device.cell.width / this._devicePixelRatio; } @@ -681,8 +723,10 @@ export class WebglRenderer extends Disposable implements IRenderer { if (this._canvas.width === width && this._canvas.height === height) { return; } - // While the actual canvas size has changed, keep device canvas dimensions as the value before - // the change as it's an exact multiple of the cell sizes. + /* + * While the actual canvas size has changed, keep device canvas dimensions as the value before + * the change as it's an exact multiple of the cell sizes. + */ this._canvas.width = width; this._canvas.height = height; // Render synchronously to avoid flicker when the canvas is cleared @@ -702,8 +746,10 @@ export class WebglRenderer extends Disposable implements IRenderer { // TODO: Share impl with core export class JoinedCellData extends AttributeData implements ICellData { private _width: number; - // .content carries no meaning for joined CellData, simply nullify it - // thus we have to overload all other .content accessors + /* + * .content carries no meaning for joined CellData, simply nullify it + * thus we have to overload all other .content accessors + */ public content: number = 0; public fg: number; public bg: number; @@ -731,8 +777,10 @@ export class JoinedCellData extends AttributeData implements ICellData { } public getCode(): number { - // code always gets the highest possible fake codepoint (read as -1) - // this is needed as code is used by caches as identifier + /* + * code always gets the highest possible fake codepoint (read as -1) + * this is needed as code is used by caches as identifier + */ return 0x1FFFFF; } diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index c35299809..d2c95723a 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -274,15 +274,17 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // #region Powerline Symbols (E0A0-E0BF) - // This contains the definitions of the primarily used box drawing characters as vector shapes. - // The reason these characters are defined specially is to avoid common problems if a user's font - // has not been patched with powerline characters and also to get pixel perfect rendering as - // rendering issues can occur around AA/SPAA. - // - // The line variants draw beyond the cell and get clipped to ensure the end of the line is not - // visible. - // - // Original symbols defined in https://github.com/powerline/fontpatcher + /* + * This contains the definitions of the primarily used box drawing characters as vector shapes. + * The reason these characters are defined specially is to avoid common problems if a user's font + * has not been patched with powerline characters and also to get pixel perfect rendering as + * rendering issues can occur around AA/SPAA. + * + * The line variants draw beyond the cell and get clipped to ensure the end of the line is not + * visible. + * + * Original symbols defined in https://github.com/powerline/fontpatcher + */ // Git branch '\u{E0A0}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.3,1 L.03,1 L.03,.88 C.03,.82,.06,.78,.11,.73 C.15,.7,.2,.68,.28,.65 L.43,.6 C.49,.58,.53,.56,.56,.53 C.59,.5,.6,.47,.6,.43 L.6,.27 L.4,.27 L.69,.1 L.98,.27 L.78,.27 L.78,.46 C.78,.52,.76,.56,.72,.61 C.68,.66,.63,.67,.56,.7 L.48,.72 C.42,.74,.38,.76,.35,.78 C.32,.8,.31,.84,.31,.88 L.31,1 M.3,.5 L.03,.59 L.03,.09 L.3,.09 L.3,.655', type: CustomGlyphVectorType.FILL } }, @@ -355,8 +357,10 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // #region Progress Indicators (EE00-EE0B) - // initially added in Fira Code and later Nerd Fonts - // https://github.com/ryanoasis/nerd-fonts/pull/1733 + /* + * initially added in Fira Code and later Nerd Fonts + * https://github.com/ryanoasis/nerd-fonts/pull/1733 + */ // Progress bars (EE00-EE05) '\u{EE00}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,.05 L.1,.05 L.1,.95 L1,.95 L1,1 L0,1 Z' }, // PROGRESS BAR EMPTY START @@ -378,8 +382,10 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // #region Git Branch Symbols (F5D0-F60D) - // Initially added in Kitty (https://github.com/kovidgoyal/kitty/pull/7681) - // then in Wezterm (https://github.com/wezterm/wezterm/issues/6328). + /* + * Initially added in Kitty (https://github.com/kovidgoyal/kitty/pull/7681) + * then in Wezterm (https://github.com/wezterm/wezterm/issues/6328). + */ // Straight lines (F5D0-F5D9) '\u{F5D0}': GitBranchSymbolsParts.LINE_H, // Same as 2500 @@ -433,8 +439,10 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi '\u{F5FA}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_UP], '\u{F5FB}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_UP], - // Extended Nodes (F5FC-F60D) - // These were added a little later https://github.com/kovidgoyal/kitty/pull/7805 + /* + * Extended Nodes (F5FC-F60D) + * These were added a little later https://github.com/kovidgoyal/kitty/pull/7805 + */ '\u{F5FC}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F5FD}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F5FE}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_LEFT], @@ -460,8 +468,10 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // https://www.unicode.org/charts/PDF/U1FB00.pdf - // Block mosaic terminal graphic characters (1FB00-1FB3B) - // The term "sextant" refers to block mosaics divided into six parts. + /* + * Block mosaic terminal graphic characters (1FB00-1FB3B) + * The term "sextant" refers to block mosaics divided into six parts. + */ '\u{1FB00}': sextant(0b000001), // BLOCK SEXTANT-1 '\u{1FB01}': sextant(0b000010), // BLOCK SEXTANT-2 '\u{1FB02}': sextant(0b000011), // BLOCK SEXTANT-12 (upper one third block) @@ -724,8 +734,10 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi '\u{1FBB2}': { type: CustomGlyphDefinitionType.PATH, data: 'M.29,.27 L0.13,.56 L.22,.59 L.35,0.35 L.67,.35 L.57,.57 L.71,.76 L.22,.76 L.42,1 L.53,.98 L.43,.86 L.9,.86 L.71,.6 L1,.6 L1,.52 L.83,.52 L.92,.36 L1,.36 L1,.27Z M.99,.13 A.12,.12,0,1,1,.75,.13 A.12,.12,0,1,1,.99,.13' }, // LEFT HALF RUNNING MAN '\u{1FBB3}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,.27 L.3,.27 L.55,.12 L.63,.18 L.33,.36 L0,.36 M0,.52 L.33,.52 L.59,.89 L.73,.89 L.73,.98 L.53,.98 L.28,.6 L0,.6' }, // RIGHT HALF RUNNING MAN - // Arrows (1FBB4-1FBB8) - // TODO: Improve all arrows, use hybrid approach + /* + * Arrows (1FBB4-1FBB8) + * TODO: Improve all arrows, use hybrid approach + */ '\u{1FBB4}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.15,.55 L.5,.4 L.5,.45 L.65,.45 L.65,.35 L.85,.35 L.85,.625 L.5,.625 L.5,.7 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // INVERSE DOWNWARDS ARROW WITH TIP LEFTWARDS '\u{1FBB5}': [ // LEFTWARDS ARROW AND UPPER AND LOWER ONE EIGHTH BLOCK { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L1,0 L1,.0625 L0,.0625 Z M0,.9375 L1,.9375 L1,1 L0,1 Z', type: CustomGlyphVectorType.FILL } }, @@ -880,9 +892,11 @@ function sextant(pattern: number): { type: CustomGlyphDefinitionType.PATH_FUNCTI return { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: () => { - // Sextant grid: 2 columns, 3 rows - // Row heights in 8ths: top=3, middle=2, bottom=3 - // Column widths: left=4, right=4 + /* + * Sextant grid: 2 columns, 3 rows + * Row heights in 8ths: top=3, middle=2, bottom=3 + * Column widths: left=4, right=4 + */ const rects: string[] = []; const colW = 0.5; // Each column is half width const rowH = [3 / 8, 2 / 8, 3 / 8]; // Row heights as fractions @@ -932,10 +946,12 @@ function sextant(pattern: number): { type: CustomGlyphDefinitionType.PATH_FUNCTI function segmentedDigit(pattern: number): string { const paths: string[] = []; - // Each segment should have approximately the same stroke width, this is somewhat difficult to be - // precise since coordinates are 0-1 of the whole cell (percentage-based). To handle this, the - // fact that terminal cells are typically sized at ~2:1 (height:width) is leveraged. - // for horizontal vs vertical to make segments appear the same thickness + /* + * Each segment should have approximately the same stroke width, this is somewhat difficult to be + * precise since coordinates are 0-1 of the whole cell (percentage-based). To handle this, the + * fact that terminal cells are typically sized at ~2:1 (height:width) is leveraged. + * for horizontal vs vertical to make segments appear the same thickness + */ const segW = 0.15; // Width of vertical segments (fraction of cell width) const segH = 0.075; // Height of horizontal segments (fraction of cell height, ~half of segW for 2:1 cells) const padX = 0.05; // Horizontal padding from edge diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts index ba8925727..ad36378cd 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts @@ -489,8 +489,10 @@ function drawPatternChar( pattern = throwIfFalsy(ctx.createPattern(tmpCanvas, null)); patternSet.set(fillStyle, pattern); } - // Apply pattern offset to ensure seamless tiling across cells when cell dimensions are odd. - // variantOffset encodes: bit 1 = x pixel shift, bit 0 = y pixel shift. + /* + * Apply pattern offset to ensure seamless tiling across cells when cell dimensions are odd. + * variantOffset encodes: bit 1 = x pixel shift, bit 0 = y pixel shift. + */ const dx = (variantOffset >> 1) & 1; const dy = variantOffset & 1; if (dx !== 0 || dy !== 0) { @@ -713,8 +715,10 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO for (let x = 0; x < result.length; x += 2) { // Translate from 0-1 to 0-cellWidth result[x] *= cellWidth - (leftPadding * devicePixelRatio) - (rightPadding * devicePixelRatio); - // Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally - // clamp to the cell bounds. + /* + * Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally + * clamp to the cell bounds. + */ if (doClamp && result[x] !== 0) { const rounded = Math.round(result[x] + 0.5) - 0.5; result[x] = clampToCell ? clamp(rounded, cellWidth, 0) : rounded; @@ -726,8 +730,10 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO for (let y = 1; y < result.length; y += 2) { // Translate from 0-1 to 0-cellHeight result[y] *= cellHeight; - // Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally - // clamp to the cell bounds. + /* + * Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally + * clamp to the cell bounds. + */ if (doClamp && result[y] !== 0) { const rounded = Math.round(result[y] + 0.5) - 0.5; result[y] = clampToCell ? clamp(rounded, cellHeight, 0) : rounded; diff --git a/addons/addon-webgl/test/WebglRenderer.test.ts b/addons/addon-webgl/test/WebglRenderer.test.ts index c425fa000..b39521180 100644 --- a/addons/addon-webgl/test/WebglRenderer.test.ts +++ b/addons/addon-webgl/test/WebglRenderer.test.ts @@ -24,9 +24,11 @@ test.beforeAll(async ({ browser }) => { test.afterAll(async () => await ctx.page.close()); test.describe('WebGL Renderer Integration Tests', async () => { - // HACK: webgl2 is often not supported in headless firefox on Linux - // https://github.com/microsoft/playwright/issues/11566 - // also disable safari due to #5852 + /* + * HACK: webgl2 is often not supported in headless firefox on Linux + * https://github.com/microsoft/playwright/issues/11566 + * also disable safari due to #5852 + */ if (platform() === 'linux') { test.skip(({ browserName }) => browserName === 'firefox' || browserName === 'webkit'); } diff --git a/demo/client/client.ts b/demo/client/client.ts index aab723b7e..b1718f64f 100644 --- a/demo/client/client.ts +++ b/demo/client/client.ts @@ -173,9 +173,11 @@ const createNewWindowButtonHandler: () => void = () => { terminalContainer.id = 'terminal-container'; win.document.body.appendChild(terminalContainer); - // Stylesheets are needed to get the terminal in the popout window to render - // correctly. We also need to wait for them to load before creating the - // terminal, otherwise we will not compute the correct metrics when rendering. + /* + * Stylesheets are needed to get the terminal in the popout window to render + * correctly. We also need to wait for them to load before creating the + * terminal, otherwise we will not compute the correct metrics when rendering. + */ let pendingStylesheets = 0; for (const linkNode of document.querySelectorAll('head link[rel=stylesheet]')) { const newLink = document.createElement('link'); @@ -359,8 +361,10 @@ function createTerminal(): Terminal { const resizeObserver = new ResizeObserver(entries => { if (optionsWindow.autoResize) { - // In general this should be debounced to avoid excessive work on the main - // thread by firing the expensive resize action repeatedly + /* + * In general this should be debounced to avoid excessive work on the main + * thread by firing the expensive resize action repeatedly + */ addons.fit.instance!.fit(); } }); @@ -547,8 +551,10 @@ function initAddons(term: Terminal): void { addon.instance = undefined; } if (name === 'ligatures') { - // Recreate webgl when ligatures are toggled so texture atlas picks up any font feature - // settings changes + /* + * Recreate webgl when ligatures are toggled so texture atlas picks up any font feature + * settings changes + */ if (addons.webgl.instance) { preDisposeWebgl(); addons.webgl.instance.dispose(); diff --git a/demo/client/components/window/addonImageWindow.ts b/demo/client/components/window/addonImageWindow.ts index 392b7177b..c089cf5f7 100644 --- a/demo/client/components/window/addonImageWindow.ts +++ b/demo/client/components/window/addonImageWindow.ts @@ -165,16 +165,18 @@ export class AddonImageWindow extends BaseWindow implements IControlWindow { this._terminal.element!.addEventListener('click', (ev: MouseEvent) => { if (!ev.ctrlKey || !imageAddon) return; - // TODO... - // if (ev.altKey) { - // const sel = term.getSelectionPosition(); - // if (sel) { - // addons.image.instance - // .extractCanvasAtBufferRange(term.getSelectionPosition()) - // ?.toBlob(data => window.open(URL.createObjectURL(data), '_blank')); - // return; - // } - // } + /* + * TODO... + * if (ev.altKey) { + * const sel = term.getSelectionPosition(); + * if (sel) { + * addons.image.instance + * .extractCanvasAtBufferRange(term.getSelectionPosition()) + * ?.toBlob(data => window.open(URL.createObjectURL(data), '_blank')); + * return; + * } + * } + */ const pos = (this._terminal as any)._core._mouseService!.getCoords(ev, (this._terminal as any)._core.screenElement!, this._terminal.cols, this._terminal.rows); const x = pos[0] - 1; diff --git a/demo/client/components/window/addonProgressWindow.ts b/demo/client/components/window/addonProgressWindow.ts index 167c9ca28..fe4e51ca7 100644 --- a/demo/client/components/window/addonProgressWindow.ts +++ b/demo/client/components/window/addonProgressWindow.ts @@ -66,11 +66,13 @@ export class AddonProgressWindow extends BaseWindow implements IControlWindow { const colors = { 0: '', 1: 'green', 2: 'red', 3: '', 4: 'yellow' }; function progressHandler({ state, value }: IProgressState): void { - // Simulate windows taskbar hack by windows terminal: - // Since the taskbar has no means to indicate error/pause state other than by coloring - // the current progress, we move 0 to 10% and distribute higher values in the remaining 90 % - // NOTE: This is most likely not what you want to do for other progress indicators, - // that have a proper visual state for error/paused. + /* + * Simulate windows taskbar hack by windows terminal: + * Since the taskbar has no means to indicate error/pause state other than by coloring + * the current progress, we move 0 to 10% and distribute higher values in the remaining 90 % + * NOTE: This is most likely not what you want to do for other progress indicators, + * that have a proper visual state for error/paused. + */ value = Math.min(10 + value * 0.9, 100); document.getElementById('progress-percent')!.style.width = `${value}%`; document.getElementById('progress-percent')!.style.backgroundColor = colors[state]; diff --git a/demo/client/components/window/testWindow.ts b/demo/client/components/window/testWindow.ts index 1ea6b3e67..e66795ab1 100644 --- a/demo/client/components/window/testWindow.ts +++ b/demo/client/components/window/testWindow.ts @@ -435,9 +435,11 @@ function customGlyphAlignmentHandler(term: Terminal): void { } function customGlyphRangesHandler(term: Terminal): void { - // Box Drawing - // 2500-257F - // https://www.unicode.org/charts/PDF/U2500.pdf + /* + * Box Drawing + * 2500-257F + * https://www.unicode.org/charts/PDF/U2500.pdf + */ writeUnicodeTable(term, 'Box Drawing', 0x2500, 0x257F, [ ['Light and heavy solid lines', 0x2500, 0x2503], ['Light and heavy dashed lines', 0x2504, 0x250B], @@ -450,39 +452,49 @@ function customGlyphRangesHandler(term: Terminal): void { ['Light and heavy half lines', 0x2574, 0x257B], ['Mixed light and heavy lines', 0x257C, 0x257F], ]); - // Box Elements - // 2580-259F - // https://www.unicode.org/charts/PDF/U2580.pdf + /* + * Box Elements + * 2580-259F + * https://www.unicode.org/charts/PDF/U2580.pdf + */ writeUnicodeTable(term, 'Box Elements', 0x2580, 0x259F, [ ['Block elements', 0x2580, 0x2590], ['Shade characters', 0x2591, 0x2593], ['Block elements', 0x2594, 0x2595], ['Terminal graphic characters', 0x2596, 0x259F], ]); - // Braille Patterns - // 2800-28FF - // https://www.unicode.org/charts/PDF/U2800.pdf + /* + * Braille Patterns + * 2800-28FF + * https://www.unicode.org/charts/PDF/U2800.pdf + */ writeUnicodeTable(term, 'Braille patterns', 0x2800, 0x28FF, [ ['Braille patterns', 0x2800, 0x28FF], ]); - // Powerline Symbols - // Range: E0A0–E0D4 - // https://github.com/ryanoasis/nerd-fonts + /* + * Powerline Symbols + * Range: E0A0–E0D4 + * https://github.com/ryanoasis/nerd-fonts + */ writeUnicodeTable(term, 'Powerline Symbols', 0xE0A0, 0xE0D4, [ ['Powerline symbols', 0xE0A0, 0xE0B3, [0xE0A4, 0xE0A5, 0xE0A6, 0xE0A7, 0xE0A8, 0xE0A9, 0xE0AA, 0xE0AB, 0xE0AC, 0xE0AD, 0xE0AE, 0xE0AF]], ['Powerline extra symbols', 0xE0B4, 0xE0D4, [0xE0C9, 0xE0CB, 0xE0D3]], ]); - // Progress Indicators - // Range: EE00-EE0B - // https://github.com/tonsky/FiraCode + /* + * Progress Indicators + * Range: EE00-EE0B + * https://github.com/tonsky/FiraCode + */ writeUnicodeTable(term, 'Progress Indicators', 0xEE00, 0xEE0B, [ ['Progress bars', 0xEE00, 0xEE05], ['Progress spinners', 0xEE06, 0xEE0B], ]); - // https://github.com/ryanoasis/nerd-fonts/pull/1733 - // Git Branch Symbols - // F5D0-F60D - // https://github.com/xtermjs/xterm.js/issues/5477 + /* + * https://github.com/ryanoasis/nerd-fonts/pull/1733 + * Git Branch Symbols + * F5D0-F60D + * https://github.com/xtermjs/xterm.js/issues/5477 + */ writeUnicodeTable(term, 'Git Branch Symbols', 0xF5D0, 0xF5FB, [ ['Straight lines', 0xF5D0, 0xF5D5], ['Curved lines', 0xF5D6, 0xF5D9], @@ -490,9 +502,11 @@ function customGlyphRangesHandler(term: Terminal): void { ['Nodes', 0xF5EE, 0xF5FB], ['Extended nodes', 0xF5FC, 0xF60D], ]); - // Symbols for Legacy Computing - // Range: 1FB00–1FBFF - // https://www.unicode.org/charts/PDF/U1FB00.pdf + /* + * Symbols for Legacy Computing + * Range: 1FB00–1FBFF + * https://www.unicode.org/charts/PDF/U1FB00.pdf + */ writeUnicodeTable(term, 'Symbols for Legacy Computing', 0x1FB00, 0x1FBFF, [ ['Block mosaic terminal graphic characters (Sextants)', 0x1FB00, 0x1FB3B], ['Smooth mosaic terminal graphic characters', 0x1FB3C, 0x1FB6F], @@ -690,8 +704,10 @@ function loadTest(term: Terminal, addons: AddonCollection): void { for (let i = 1; i < count + 1; i++) { data[i] = 0x61 + Math.floor(Math.random() * (0x7A - 0x61)); } - // End each line with \r so the cursor remains constant, this is what ls/tree do and improves - // performance significantly due to the cursor DOM element not needing to change + /* + * End each line with \r so the cursor remains constant, this is what ls/tree do and improves + * performance significantly due to the cursor DOM element not needing to change + */ data[data.length - 1] = 0x0D; // \r testData.push(data); } @@ -723,8 +739,10 @@ async function loadTestLongLines(term: Terminal, addons: AddonCollection): Promi for (let i = 1; i < count + 1; i++) { data[i] = 0x61 + Math.floor(Math.random() * (0x7A - 0x61)); } - // End each line with \r so the cursor remains constant, this is what ls/tree do and improves - // performance significantly due to the cursor DOM element not needing to change + /* + * End each line with \r so the cursor remains constant, this is what ls/tree do and improves + * performance significantly due to the cursor DOM element not needing to change + */ data[data.length - 1] = 0x0D; // \r testData.push(data); } @@ -734,8 +752,10 @@ async function loadTestLongLines(term: Terminal, addons: AddonCollection): Promi try { term.write(d); } catch { - // Flush events when cap is hit and try again(workaround for not having flow control in - // demo) + /* + * Flush events when cap is hit and try again(workaround for not having flow control in + * demo) + */ await new Promise(r => term.write('', () => r())); term.write(d); } diff --git a/demo/client/unicodeTable.ts b/demo/client/unicodeTable.ts index 61bba36f2..9755b911a 100644 --- a/demo/client/unicodeTable.ts +++ b/demo/client/unicodeTable.ts @@ -220,8 +220,10 @@ export function writeUnicodeTable(term: Terminal, name: string, start: number, e term.write('\n\r'); - // Render reserved labels that appear after the first label (below the row) - // Show one label per non-contiguous reserved range + /* + * Render reserved labels that appear after the first label (below the row) + * Show one label per non-contiguous reserved range + */ const lateReserved = rowLabels.length > 0 ? rowReserved.filter(r => r.col >= rowLabels[0].col) : rowReserved; diff --git a/demo/server/server.ts b/demo/server/server.ts index ef98eb84c..5bd594dcb 100644 --- a/demo/server/server.ts +++ b/demo/server/server.ts @@ -180,9 +180,11 @@ function startServer(): void { } 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 - // the problem and how to implement flow control at https://xtermjs.org/docs/guides/flowcontrol/ + /* + * 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 + * the problem and how to implement flow control at https://xtermjs.org/docs/guides/flowcontrol/ + */ term.onData(function(data) { try { send(data as string & Buffer); @@ -209,8 +211,10 @@ function startServer(): void { app.listen(port, host, 0); } -// HACK: There is an EPIPE error thrown when reloading a page. This only seems to happen on Windows -// and it's unclear why it happens. Suppressing the error here since this is just the demo server. +/* + * HACK: There is an EPIPE error thrown when reloading a page. This only seems to happen on Windows + * and it's unclear why it happens. Suppressing the error here since this is just the demo server. + */ process.on('uncaughtException', (error) => { if (process.platform === 'win32' && error.message === 'read EPIPE') { return; diff --git a/eslint.config.mjs b/eslint.config.mjs index 6c2f80e30..18a4dfb59 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -88,6 +88,7 @@ export default tseslint.config( 'jsdoc/check-param-names': 'warn', 'jsdoc/no-multi-asterisks': 'warn', 'keyword-spacing': 'warn', + 'multiline-comment-style': ['warn', 'starred-block'], 'max-len': ['warn', { code: 1000, comments: 100, diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index 6fc421337..5960c8d4f 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -21,8 +21,10 @@ const enum BoundaryPosition { BOTTOM } -// Turn this on to unhide the accessibility tree and display it under -// (instead of overlapping with) the terminal. +/* + * Turn this on to unhide the accessibility tree and display it under + * (instead of overlapping with) the terminal. + */ const DEBUG = false; export class AccessibilityManager extends Disposable { @@ -223,8 +225,10 @@ export class AccessibilityManager extends Disposable { return; } - // Don't scroll when the last focused item was not the second row (focus is going the other - // direction) + /* + * Don't scroll when the last focused item was not the second row (focus is going the other + * direction) + */ if (e.relatedTarget !== beforeBoundaryElement) { return; } @@ -283,9 +287,11 @@ export class AccessibilityManager extends Disposable { } if (selection.isCollapsed) { - // Only do something when the anchorNode is inside the row container. This - // behavior mirrors what we do with mouse --- if the mouse clicks - // somewhere outside of the terminal, we don't clear the selection. + /* + * Only do something when the anchorNode is inside the row container. This + * behavior mirrors what we do with mouse --- if the mouse clicks + * somewhere outside of the terminal, we don't clear the selection. + */ if (this._rowContainer.contains(selection.anchorNode)) { this._terminal.clearSelection(); } diff --git a/src/browser/Clipboard.ts b/src/browser/Clipboard.ts index aaf8a48e4..513dfa60c 100644 --- a/src/browser/Clipboard.ts +++ b/src/browser/Clipboard.ts @@ -22,8 +22,10 @@ export function bracketTextForPaste(text: string, bracketedPasteMode: boolean): if (!bracketedPasteMode) { return text; } - // Sanitize pasted text to prevent injected escape sequences (e.g. exiting bracketed paste) - // by replacing ESC (\x1b) with its visible representation U+241B (␛). + /* + * Sanitize pasted text to prevent injected escape sequences (e.g. exiting bracketed paste) + * by replacing ESC (\x1b) with its visible representation U+241B (␛). + */ const sanitizedText = text.replace(/\x1b/g, '\u241b'); return `\x1b[200~${sanitizedText}\x1b[201~`; } diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index 52eb00025..519d8b1dc 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -323,8 +323,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { * Binds the desired blur behavior on a given terminal object. */ private _handleTextAreaBlur(): void { - // Text can safely be removed on blur. Doing it earlier could interfere with - // screen readers reading it out. + /* + * Text can safely be removed on blur. Doing it earlier could interfere with + * screen readers reading it out. + */ this.textarea!.value = ''; this.refresh(this.buffer.y, this.buffer.y); if (this.coreService.decPrivateModes.sendFocus) { @@ -350,8 +352,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { const cursorTop = this.buffer.y * this._renderService.dimensions.css.cell.height; const cursorLeft = cursorX * this._renderService.dimensions.css.cell.width; - // Sync the textarea to the exact position of the composition view so the IME knows where the - // text is. + /* + * Sync the textarea to the exact position of the composition view so the IME knows where the + * text is. + */ this.textarea.style.left = cursorLeft + 'px'; this.textarea.style.top = cursorTop + 'px'; this.textarea.style.width = cellWidth + 'px'; @@ -368,8 +372,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { // Bind clipboard functionality this._register(addDisposableListener(this.element!, 'copy', (event: ClipboardEvent) => { - // If mouse events are active it means the selection manager is disabled and - // copy should be handled by the host program. + /* + * If mouse events are active it means the selection manager is disabled and + * copy should be handled by the host program. + */ if (!this.hasSelection()) { return; } @@ -393,12 +399,16 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { })); } - // Move the textarea under the cursor when middle clicking on Linux to ensure - // middle click to paste selection works. This only appears to work in Chrome - // at the time is writing. + /* + * Move the textarea under the cursor when middle clicking on Linux to ensure + * middle click to paste selection works. This only appears to work in Chrome + * at the time is writing. + */ if (Browser.isLinux) { - // Use auxclick event over mousedown the latter doesn't seem to work. Note - // that the regular click event doesn't fire for the middle mouse button. + /* + * Use auxclick event over mousedown the latter doesn't seem to work. Note + * that the regular click event doesn't fire for the middle mouse button. + */ this._register(addDisposableListener(this.element!, 'auxclick', (event: MouseEvent) => { if (event.button === 1) { moveTextAreaUnderMouseCursor(event, this.textarea!, this.screenElement!); @@ -415,11 +425,13 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._register(addDisposableListener(this.textarea!, 'keydown', (ev: KeyboardEvent) => this._keyDown(ev), true)); this._register(addDisposableListener(this.textarea!, 'keypress', (ev: KeyboardEvent) => this._keyPress(ev), true)); this._register(addDisposableListener(this.textarea!, 'compositionstart', () => { - // Ensure the textarea is synced to the latest cursor location before composition begins. This - // is to workaround a problem where highly dynamic TUIs like agentic CLIs reprint agressively - // would cause the IME to appear in the wrong position. The theory is that when the IME is - // triggered during a partial render the textarea position becomes locked and will not move - // until it is hidden and a custom move occurs. + /* + * Ensure the textarea is synced to the latest cursor location before composition begins. This + * is to workaround a problem where highly dynamic TUIs like agentic CLIs reprint agressively + * would cause the IME to appear in the wrong position. The theory is that when the IME is + * triggered during a partial render the textarea position becomes locked and will not move + * until it is hidden and a custom move occurs. + */ this._syncTextArea(); this._compositionHelper!.compositionstart(); this._compositionHelper!.updateCompositionElements(); @@ -467,8 +479,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._register(this.optionsService.onSpecificOptionChange('allowTransparency', value => this.element!.classList.toggle('allow-transparency', value))); parent.appendChild(this.element); - // Performance: Use a document fragment to build the terminal - // viewport and helper elements detached from the DOM + /* + * Performance: Use a document fragment to build the terminal + * viewport and helper elements detached from the DOM + */ const fragment = this._document.createDocumentFragment(); this._viewportElement = this._document.createElement('div'); this._viewportElement.classList.add('xterm-viewport'); @@ -477,8 +491,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this.screenElement = this._document.createElement('div'); this.screenElement.classList.add('xterm-screen'); this._register(addDisposableListener(this.screenElement, 'mousemove', (ev: MouseEvent) => this.updateCursorStyle(ev))); - // Create the container that will hold helpers like the textarea for - // capturing DOM Events. Then produce the helpers. + /* + * Create the container that will hold helpers like the textarea for + * capturing DOM Events. Then produce the helpers. + */ this._helperContainer = this._document.createElement('div'); this._helperContainer.classList.add('xterm-helpers'); this.screenElement.appendChild(this._helperContainer); @@ -488,8 +504,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this.textarea.classList.add('xterm-helper-textarea'); this.textarea.setAttribute('aria-label', Strings.promptLabel.get()); if (!Browser.isChromeOS) { - // ChromeVox on ChromeOS does not like this. See - // https://issuetracker.google.com/issues/260170397 + /* + * ChromeVox on ChromeOS does not like this. See + * https://issuetracker.google.com/issues/260170397 + */ this.textarea.setAttribute('aria-multiline', 'false'); } this.textarea.setAttribute('autocorrect', 'off'); @@ -499,8 +517,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._register(this.optionsService.onSpecificOptionChange('disableStdin', () => textarea.readOnly = this.optionsService.rawOptions.disableStdin)); this.textarea.readOnly = this.optionsService.rawOptions.disableStdin; - // Register the core browser service before the generic textarea handlers are registered so it - // handles them first. Otherwise the renderers may use the wrong focus state. + /* + * Register the core browser service before the generic textarea handlers are registered so it + * handles them first. Otherwise the renderers may use the wrong focus state. + */ this._coreBrowserService = this._register(this._instantiationService.createInstance(CoreBrowserService, this.textarea, parent.ownerDocument.defaultView ?? window, @@ -598,9 +618,11 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._register(this._selectionService.onSelectionChange(() => this._onSelectionChange.fire())); this._register(this._selectionService.onRequestRedraw(e => this._renderService!.handleSelectionChanged(e.start, e.end, e.columnSelectMode))); this._register(this._selectionService.onLinuxMouseSelection(text => { - // If there's a new selection, put it into the textarea, focus and select it - // in order to register it as a selection on the OS. This event is fired - // only on Linux to enable middle click to paste selection. + /* + * If there's a new selection, put it into the textarea, focus and select it + * in order to register it as a selection on the OS. This event is fired + * only on Linux to enable middle click to paste selection. + */ this.textarea!.value = text; this.textarea!.focus(); this.textarea!.select(); @@ -625,8 +647,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { } if (this.options.screenReaderMode) { - // Note that this must be done *after* the renderer is created in order to - // ensure the correct order of the dprchange event + /* + * Note that this must be done *after* the renderer is created in order to + * ensure the correct order of the dprchange event + */ this._accessibilityManager.value = this._instantiationService.createInstance(AccessibilityManager, this); } this._register(this.optionsService.onSpecificOptionChange('screenReaderMode', e => this._handleScreenReaderModeOptionChange(e))); @@ -651,8 +675,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { // Initialize global actions that need to be taken on the document. this._initGlobal(); - // Listen for mouse events and translate - // them into terminal mouse protocols. + /* + * Listen for mouse events and translate + * them into terminal mouse protocols. + */ this._mouseService.bindMouse({ element: this.element!, screenElement: this.screenElement!, @@ -892,9 +918,11 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { return true; } - // HACK: Process A-Z in the keypress event to fix an issue with macOS IMEs where lower case - // letters cannot be input while caps lock is on. Skip this hack when using kitty protocol - // or Win32 input mode as they need to send proper sequences for all key events. + /* + * HACK: Process A-Z in the keypress event to fix an issue with macOS IMEs where lower case + * letters cannot be input while caps lock is on. Skip this hack when using kitty protocol + * or Win32 input mode as they need to send proper sequences for all key events. + */ if (!this._keyboardService.useKitty && !this._keyboardService.useWin32InputMode && event.key && !event.ctrlKey && !event.altKey && !event.metaKey && event.key.length === 1) { if (event.key.charCodeAt(0) >= 65 && event.key.charCodeAt(0) <= 90) { return true; @@ -906,9 +934,11 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { return true; } - // If ctrl+c or enter is being sent, clear out the textarea. This is done so that screen readers - // will announce deleted characters. This will not work 100% of the time but it should cover - // most scenarios. + /* + * If ctrl+c or enter is being sent, clear out the textarea. This is done so that screen readers + * will announce deleted characters. This will not work 100% of the time but it should cover + * most scenarios. + */ if (result.key === C0.ETX || result.key === C0.CR) { this.textarea!.value = ''; } @@ -918,10 +948,12 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._showCursor(); this.coreService.triggerDataEvent(result.key, !wasModifierOnly); - // Cancel events when not in screen reader mode so events don't get bubbled up and handled by - // other listeners. When screen reader mode is enabled, we don't cancel them (unless ctrl or alt - // is also depressed) so that the cursor textarea can be updated, which triggers the screen - // reader to read it. + /* + * Cancel events when not in screen reader mode so events don't get bubbled up and handled by + * other listeners. When screen reader mode is enabled, we don't cancel them (unless ctrl or alt + * is also depressed) so that the cursor textarea can be updated, which triggers the screen + * reader to read it. + */ if (!this.optionsService.rawOptions.screenReaderMode || event.altKey || event.ctrlKey) { event.preventDefault(); event.stopPropagation(); @@ -1010,8 +1042,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._keyPressHandled = true; - // The key was handled so clear the dead key state, otherwise certain keystrokes like arrow - // keys could be ignored + /* + * The key was handled so clear the dead key state, otherwise certain keystrokes like arrow + * keys could be ignored + */ this._unprocessedDeadKey = false; return true; @@ -1024,16 +1058,20 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { * @param ev The input event to be handled. */ protected _inputEvent(ev: InputEvent): boolean { - // Only support emoji IMEs when screen reader mode is disabled as the event must bubble up to - // support reading out character input which can doubling up input characters - // Based on these event traces: https://github.com/xtermjs/xterm.js/issues/3679 + /* + * Only support emoji IMEs when screen reader mode is disabled as the event must bubble up to + * support reading out character input which can doubling up input characters + * Based on these event traces: https://github.com/xtermjs/xterm.js/issues/3679 + */ if (ev.data && ev.inputType === 'insertText' && (!ev.composed || !this._keyDownSeen) && !this.optionsService.rawOptions.screenReaderMode) { if (this._keyPressHandled) { return false; } - // The key was handled so clear the dead key state, otherwise certain keystrokes like arrow - // keys could be ignored + /* + * The key was handled so clear the dead key state, otherwise certain keystrokes like arrow + * keys could be ignored + */ this._unprocessedDeadKey = false; const text = ev.data; @@ -1083,8 +1121,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { for (let i = 1; i < this.rows; i++) { this.buffer.lines.push(this.buffer.getBlankLine(DEFAULT_ATTR_DATA)); } - // IMPORTANT: Fire scroll event before viewport is reset. This ensures embedders get the clear - // scroll event and that the viewport's state will be valid for immediate writes. + /* + * IMPORTANT: Fire scroll event before viewport is reset. This ensures embedders get the clear + * scroll event and that the viewport's state will be valid for immediate writes. + */ this._onScroll.fire({ position: this.buffer.ydisp }); this.refresh(0, this.rows - 1); } diff --git a/src/browser/Linkifier.ts b/src/browser/Linkifier.ts index 31c91daee..e99c41dfa 100644 --- a/src/browser/Linkifier.ts +++ b/src/browser/Linkifier.ts @@ -87,9 +87,11 @@ export class Linkifier extends Disposable implements ILinkifier2 { } private _handleHover(position: IBufferCellPosition): void { - // TODO: This currently does not cache link provider results across wrapped lines, activeLine - // should be something like `activeRange: {startY, endY}` - // Check if we need to clear the link + /* + * TODO: This currently does not cache link provider results across wrapped lines, activeLine + * should be something like `activeRange: {startY, endY}` + * Check if we need to clear the link + */ if (this._activeLine !== position.y || this._wasResized) { this._clearCurrentLink(); this._askForLink(position, false); @@ -125,9 +127,11 @@ export class Linkifier extends Disposable implements ILinkifier2 { const existingReply = this._activeProviderReplies?.get(i); // If there isn't a reply, the provider hasn't responded yet. - // TODO: If there isn't a reply yet it means that the provider is still resolving. Ensuring - // provideLinks isn't triggered again saves ILink.hover firing twice though. This probably - // needs promises to get fixed + /* + * TODO: If there isn't a reply yet it means that the provider is still resolving. Ensuring + * provideLinks isn't triggered again saves ILink.hover firing twice though. This probably + * needs promises to get fixed + */ if (existingReply) { linkProvided = this._checkLinkProviderResult(i, position, linkProvided); } @@ -140,8 +144,10 @@ export class Linkifier extends Disposable implements ILinkifier2 { this._activeProviderReplies?.set(i, linksWithState); linkProvided = this._checkLinkProviderResult(i, position, linkProvided); - // If all providers have responded, remove lower priority links that intersect ranges of - // higher priority links + /* + * If all providers have responded, remove lower priority links that intersect ranges of + * higher priority links + */ if (this._activeProviderReplies?.size === this._linkProviderService.linkProviders.length) { this._removeIntersectingLinks(position.y, this._activeProviderReplies); } @@ -187,8 +193,10 @@ export class Linkifier extends Disposable implements ILinkifier2 { } } - // If all providers with higher priority came back undefined, then this provider's link for - // the position should be used + /* + * If all providers with higher priority came back undefined, then this provider's link for + * the position should be used + */ if (!hasLinkBefore && links) { const linkAtPosition = links.find(link => this._linkAtPosition(link.link, position)); if (linkAtPosition) { @@ -296,15 +304,19 @@ export class Linkifier extends Disposable implements ILinkifier2 { } }); - // Listen to viewport changes to re-render the link under the cursor (only when the line the - // link is on changes) + /* + * Listen to viewport changes to re-render the link under the cursor (only when the line the + * link is on changes) + */ this._linkCacheDisposables.push(this._renderService.onRenderedViewportChange(e => { // Sanity check, this shouldn't happen in practice as this listener would be disposed if (!this._currentLink) { return; } - // When start is 0 a scroll most likely occurred, make sure links above the fold also get - // cleared. + /* + * When start is 0 a scroll most likely occurred, make sure links above the fold also get + * cleared. + */ const start = e.start === 0 ? 0 : e.start + 1 + this._bufferService.buffer.ydisp; const end = this._bufferService.buffer.ydisp + 1 + e.end; // Only clear the link if the viewport change happened on this line diff --git a/src/browser/OscLinkProvider.ts b/src/browser/OscLinkProvider.ts index b6a3cce0a..8ed823203 100644 --- a/src/browser/OscLinkProvider.ts +++ b/src/browser/OscLinkProvider.ts @@ -33,8 +33,10 @@ export class OscLinkProvider implements ILinkProvider { let currentStart = -1; let finishLink = false; for (let x = 0; x < lineLength; x++) { - // Minor optimization, only check for content if there isn't a link in case the link ends with - // a null cell + /* + * Minor optimization, only check for content if there isn't a link in case the link ends with + * a null cell + */ if (currentStart === -1 && !line.hasContent(x)) { continue; } @@ -107,8 +109,10 @@ export class OscLinkProvider implements ILinkProvider { } } - // TODO: Handle fetching and returning other link ranges to underline other links with the same - // id + /* + * TODO: Handle fetching and returning other link ranges to underline other links with the same + * id + */ callback(result); } } diff --git a/src/browser/Terminal.test.ts b/src/browser/Terminal.test.ts index d359fa28c..425955982 100644 --- a/src/browser/Terminal.test.ts +++ b/src/browser/Terminal.test.ts @@ -576,14 +576,16 @@ describe('Terminal', () => { beforeEach(() => { term.clearSelection = () => { }; - // term.compositionHelper = { - // isComposing: false, - // keydown: { - // bind: () => { - // return () => { return true; }; - // } - // } - // }; + /* + * term.compositionHelper = { + * isComposing: false, + * keydown: { + * bind: () => { + * return () => { return true; }; + * } + * } + * }; + */ evKeyDown = { preventDefault: () => { }, stopPropagation: () => { }, @@ -1148,8 +1150,10 @@ describe('Terminal', () => { // FIXME: move to common/CoreTerminal.test once the trimming is moved over describe('marker lifecycle', () => { - // create a 10x5 terminal with markers on every line - // to test marker lifecycle under various terminal actions + /* + * create a 10x5 terminal with markers on every line + * to test marker lifecycle under various terminal actions + */ let markers: IMarker[]; let disposeStack: IMarker[]; let term: TestTerminal; diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index 997312901..8d609cb23 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -43,8 +43,10 @@ export class TimeBasedDebouncer implements IRenderDebouncer { this._rowStart = this._rowStart !== undefined ? Math.min(this._rowStart, rowStart) : rowStart; this._rowEnd = this._rowEnd !== undefined ? Math.max(this._rowEnd, rowEnd) : rowEnd; - // Only refresh if the time since last refresh is above a threshold, otherwise wait for - // enough time to pass before refreshing again. + /* + * Only refresh if the time since last refresh is above a threshold, otherwise wait for + * enough time to pass before refreshing again. + */ const refreshRequestTime: number = performance.now(); if (refreshRequestTime - this._lastRefreshMs >= this._debounceThresholdMS) { // Enough time has lapsed since the last refresh; refresh immediately diff --git a/src/browser/Types.ts b/src/browser/Types.ts index fa08de2e3..9c76fb07d 100644 --- a/src/browser/Types.ts +++ b/src/browser/Types.ts @@ -201,8 +201,10 @@ export const DEFAULT_ANSI_COLORS = Object.freeze((() => { css.toColor('#eeeeec') ]; - // Fill in the remaining 240 ANSI colors. - // Generate colors (16-231) + /* + * Fill in the remaining 240 ANSI colors. + * Generate colors (16-231) + */ const v = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]; for (let i = 0; i < 216; i++) { const r = v[(i / 36) % 6 | 0]; diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index 3b6ae8301..c629bc7a3 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -99,16 +99,20 @@ export class Viewport extends Disposable { this._register(this._bufferService.onResize(() => this.queueSync())); this._register(this._bufferService.buffers.onBufferActivate(() => { - // Reset _latestYDisp when switching buffers to prevent stale scroll position - // from alt buffer contaminating normal buffer scroll position + /* + * Reset _latestYDisp when switching buffers to prevent stale scroll position + * from alt buffer contaminating normal buffer scroll position + */ this._latestYDisp = undefined; this.queueSync(); })); this._register(this._bufferService.onScroll(() => this._sync())); - // Flush deferred viewport sync after a render completes (e.g. after ESU ends - // synchronized output mode). This ensures DOM scroll position updates atomically - // with the canvas render. + /* + * Flush deferred viewport sync after a render completes (e.g. after ESU ends + * synchronized output mode). This ensures DOM scroll position updates atomically + * with the canvas render. + */ this._register(this._renderService.onRender(() => { if (this._needsSyncOnRender) { this._needsSyncOnRender = false; @@ -173,16 +177,20 @@ export class Viewport extends Disposable { if (!this._renderService || this._isSyncing) { return; } - // Defer DOM scroll updates during synchronized output to prevent visible - // scroll position flickering while the canvas content is frozen. + /* + * Defer DOM scroll updates during synchronized output to prevent visible + * scroll position flickering while the canvas content is frozen. + */ if (this._coreService.decPrivateModes.synchronizedOutput) { this._needsSyncOnRender = true; return; } this._isSyncing = true; - // Ignore any onScroll event that happens as a result of dimensions changing as this should - // never cause a scrollLines call, only setScrollPosition can do that. + /* + * Ignore any onScroll event that happens as a result of dimensions changing as this should + * never cause a scrollLines call, only setScrollPosition can do that. + */ this._suppressOnScrollHandler = true; this._scrollableElement.setScrollDimensions({ height: this._renderService.dimensions.css.canvas.height, @@ -190,8 +198,10 @@ export class Viewport extends Disposable { }); this._suppressOnScrollHandler = false; - // If ydisp has been changed by some other component (input/buffer), then stop animating smooth - // scroll and scroll there immediately. + /* + * If ydisp has been changed by some other component (input/buffer), then stop animating smooth + * scroll and scroll there immediately. + */ if (ydisp !== this._latestYDisp) { this._scrollableElement.setScrollPosition({ scrollTop: ydisp * this._renderService.dimensions.css.cell.height diff --git a/src/browser/decorations/BufferDecorationRenderer.ts b/src/browser/decorations/BufferDecorationRenderer.ts index e374af260..fb4e047a7 100644 --- a/src/browser/decorations/BufferDecorationRenderer.ts +++ b/src/browser/decorations/BufferDecorationRenderer.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { ICoreBrowserService, IRenderService } from 'browser/services/Services'; import { Disposable, toDisposable } from 'common/Lifecycle'; diff --git a/src/browser/decorations/ColorZoneStore.ts b/src/browser/decorations/ColorZoneStore.ts index d066bedb8..d1795ab8e 100644 --- a/src/browser/decorations/ColorZoneStore.ts +++ b/src/browser/decorations/ColorZoneStore.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { IInternalDecoration } from 'common/services/Services'; @@ -32,9 +34,11 @@ interface IMinimalDecorationForColorZone { export class ColorZoneStore implements IColorZoneStore { private _zones: IColorZone[] = []; - // The zone pool is used to keep zone objects from being freed between clearing the color zone - // store and fetching the zones. This helps reduce GC pressure since the color zones are - // accumulated on potentially every scroll event. + /* + * The zone pool is used to keep zone objects from being freed between clearing the color zone + * store and fetching the zones. This helps reduce GC pressure since the color zones are + * accumulated on potentially every scroll event. + */ private _zonePool: IColorZone[] = []; private _zonePoolIndex = 0; diff --git a/src/browser/decorations/OverviewRulerRenderer.ts b/src/browser/decorations/OverviewRulerRenderer.ts index b28b25965..8ea3000b3 100644 --- a/src/browser/decorations/OverviewRulerRenderer.ts +++ b/src/browser/decorations/OverviewRulerRenderer.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { ColorZoneStore, IColorZone, IColorZoneStore } from 'browser/decorations/ColorZoneStore'; import { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; @@ -12,8 +14,10 @@ const enum Constants { OVERVIEW_RULER_BORDER_WIDTH = 1 } -// Helper objects to avoid excessive calculation and garbage collection during rendering. These are -// static values for each render and can be accessed using the decoration position as the key. +/* + * Helper objects to avoid excessive calculation and garbage collection during rendering. These are + * static values for each render and can be accessed using the decoration position as the key. + */ const drawHeight = { full: 0, left: 0, diff --git a/src/browser/input/CompositionHelper.test.ts b/src/browser/input/CompositionHelper.test.ts index 94b5841af..512d5a773 100644 --- a/src/browser/input/CompositionHelper.test.ts +++ b/src/browser/input/CompositionHelper.test.ts @@ -236,9 +236,11 @@ describe('CompositionHelper', () => { it('Should insert middle composition and subsequent input without appending existing trailing text', (done) => { textarea.value = '一二'; - // screenReaderMode keeps textarea content/selection for assistive technologies (eg. screen - // readers), so the caret can be moved within the textarea (eg. via arrow keys) before - // starting composition. + /* + * screenReaderMode keeps textarea content/selection for assistive technologies (eg. screen + * readers), so the caret can be moved within the textarea (eg. via arrow keys) before + * starting composition. + */ textarea.selectionStart = 1; textarea.selectionEnd = 1; diff --git a/src/browser/input/CompositionHelper.ts b/src/browser/input/CompositionHelper.ts index 283874139..f211e2553 100644 --- a/src/browser/input/CompositionHelper.ts +++ b/src/browser/input/CompositionHelper.ts @@ -72,8 +72,10 @@ export class CompositionHelper { */ public compositionstart(): void { this._isComposing = true; - // It's important to use the selection here instead of textarea length to avoid conflicts with - // screen reader mode + /* + * It's important to use the selection here instead of textarea length to avoid conflicts with + * screen reader mode + */ const start = this._textarea.selectionStart ?? this._textarea.value.length; const end = this._textarea.selectionEnd ?? start; this._compositionPosition.start = Math.min(start, end); @@ -89,8 +91,10 @@ export class CompositionHelper { * @param ev The event. */ public compositionupdate(ev: Pick): void { - // Mark text as LTR, direction=rtl is used in CSS so the end of the text is followed for long - // compositions + /* + * Mark text as LTR, direction=rtl is used in CSS so the end of the text is followed for long + * compositions + */ this._compositionView.textContent = `\u200E${ev.data}\u200E`; this.updateCompositionElements(); setTimeout(() => { @@ -115,22 +119,28 @@ export class CompositionHelper { public keydown(ev: KeyboardEvent): boolean { if (this._isComposing || this._isSendingComposition) { if (ev.keyCode === 20 || ev.keyCode === 229) { - // 20 is CapsLock, 229 is Enter - // Continue composing if the keyCode is the "composition character" + /* + * 20 is CapsLock, 229 is Enter + * Continue composing if the keyCode is the "composition character" + */ return false; } if (ev.keyCode === 16 || ev.keyCode === 17 || ev.keyCode === 18) { // Continue composing if the keyCode is a modifier key return false; } - // Finish composition immediately. This is mainly here for the case where enter is - // pressed and the handler needs to be triggered before the command is executed. + /* + * Finish composition immediately. This is mainly here for the case where enter is + * pressed and the handler needs to be triggered before the command is executed. + */ this._finalizeComposition(false); } if (ev.keyCode === 229) { - // If the "composition character" is used but gets to this point it means a non-composition - // character (eg. numbers and punctuation) was pressed when the IME was active. + /* + * If the "composition character" is used but gets to this point it means a non-composition + * character (eg. numbers and punctuation) was pressed when the IME was active. + */ this._handleAnyTextareaChanges(); return false; } @@ -156,39 +166,49 @@ export class CompositionHelper { const input = this._textarea.value.substring(this._compositionPosition.start, this._compositionPosition.end); this._coreService.triggerDataEvent(input, true); } else { - // Make a deep copy of the composition position here as a new compositionstart event may - // fire before the setTimeout executes. + /* + * Make a deep copy of the composition position here as a new compositionstart event may + * fire before the setTimeout executes. + */ const currentCompositionPosition = { start: this._compositionPosition.start, end: this._compositionPosition.end }; const currentCompositionSuffix = this._compositionSuffix; - // Since composition* events happen before the changes take place in the textarea on most - // browsers, use a setTimeout with 0ms time to allow the native compositionend event to - // complete. This ensures the correct character is retrieved. - // This solution was used because: - // - The compositionend event's data property is unreliable, at least on Chromium - // - The last compositionupdate event's data property does not always accurately describe - // the character, a counter example being Korean where an ending consonsant can move to - // the following character if the following input is a vowel. + /* + * Since composition* events happen before the changes take place in the textarea on most + * browsers, use a setTimeout with 0ms time to allow the native compositionend event to + * complete. This ensures the correct character is retrieved. + * This solution was used because: + * - The compositionend event's data property is unreliable, at least on Chromium + * - The last compositionupdate event's data property does not always accurately describe + * the character, a counter example being Korean where an ending consonsant can move to + * the following character if the following input is a vowel. + */ this._isSendingComposition = true; setTimeout(() => { // Ensure that the input has not already been sent if (this._isSendingComposition) { this._isSendingComposition = false; let input; - // Add length of data already sent due to keydown event, - // otherwise input characters can be duplicated. (Issue #3191) + /* + * Add length of data already sent due to keydown event, + * otherwise input characters can be duplicated. (Issue #3191) + */ currentCompositionPosition.start += this._dataAlreadySent.length; if (this._isComposing) { - // Use the start position of the new composition to get the string - // if a new composition has started. + /* + * Use the start position of the new composition to get the string + * if a new composition has started. + */ input = this._textarea.value.substring(currentCompositionPosition.start, this._compositionPosition.start); } else { - // Keep support for non-composition characters typed immediately after composition end - // while avoiding re-sending the trailing text that was already present - // before composition started. + /* + * Keep support for non-composition characters typed immediately after composition end + * while avoiding re-sending the trailing text that was already present + * before composition started. + */ const value = this._textarea.value; const valueEnd = currentCompositionSuffix.length > 0 && value.endsWith(currentCompositionSuffix) ? value.length - currentCompositionSuffix.length @@ -260,14 +280,18 @@ export class CompositionHelper { this._compositionView.style.lineHeight = cellHeight + 'px'; this._compositionView.style.fontFamily = this._optionsService.rawOptions.fontFamily; this._compositionView.style.fontSize = this._optionsService.rawOptions.fontSize + 'px'; - // Limit the composition view width to the space between the cursor and - // the terminal's right edge, preventing it from overflowing the terminal. + /* + * Limit the composition view width to the space between the cursor and + * the terminal's right edge, preventing it from overflowing the terminal. + */ const maxWidth = this._bufferService.cols * this._renderService.dimensions.css.cell.width - cursorLeft; this._compositionView.style.maxWidth = maxWidth + 'px'; this._compositionView.style.overflow = 'hidden'; this._compositionView.style.direction = 'rtl'; - // Sync the textarea to the exact position of the composition view so the IME knows where the - // text is. + /* + * Sync the textarea to the exact position of the composition view so the IME knows where the + * text is. + */ const compositionViewBounds = this._compositionView.getBoundingClientRect(); this._textarea.style.left = cursorLeft + 'px'; this._textarea.style.top = cursorTop + 'px'; diff --git a/src/browser/input/Mouse.ts b/src/browser/input/Mouse.ts index c40a7cc78..1094cdcf4 100644 --- a/src/browser/input/Mouse.ts +++ b/src/browser/input/Mouse.ts @@ -44,9 +44,11 @@ export function getCoords(window: Pick, event: Pick< coords[0] = Math.ceil((coords[0] + (isSelection ? cssCellWidth / 2 : 0)) / cssCellWidth); coords[1] = Math.ceil(coords[1] / cssCellHeight); - // Ensure coordinates are within the terminal viewport. Note that selections - // need an addition point of precision to cover the end point (as characters - // cover half of one char and half of the next). + /* + * Ensure coordinates are within the terminal viewport. Note that selections + * need an addition point of precision to cover the end point (as characters + * cover half of one char and half of the next). + */ coords[0] = Math.min(Math.max(coords[0], 1), colCount + (isSelection ? 1 : 0)); coords[1] = Math.min(Math.max(coords[1], 1), rowCount); diff --git a/src/browser/public/Terminal.ts b/src/browser/public/Terminal.ts index 75aada048..9255f67f2 100644 --- a/src/browser/public/Terminal.ts +++ b/src/browser/public/Terminal.ts @@ -54,9 +54,11 @@ export class Terminal extends Disposable implements ITerminalApi { } private _checkReadonlyOptions(propName: string): void { - // Throw an error if any constructor only option is modified - // from terminal.options - // Modifications from anywhere else are allowed + /* + * Throw an error if any constructor only option is modified + * from terminal.options + * Modifications from anywhere else are allowed + */ if (CONSTRUCTOR_ONLY_OPTIONS.includes(propName)) { throw new Error(`Option "${propName}" can only be set in the constructor`); } diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index 6f873af7b..943906524 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -114,8 +114,10 @@ export class DomRenderer extends Disposable implements IRenderer { this._register(toDisposable(() => { this._element.classList.remove(Constants.TERMINAL_CLASS_PREFIX + this._terminalClass); - // Outside influences such as React unmounts may manipulate the DOM before our disposal. - // https://github.com/xtermjs/xterm.js/issues/2960 + /* + * Outside influences such as React unmounts may manipulate the DOM before our disposal. + * https://github.com/xtermjs/xterm.js/issues/2960 + */ this._rowContainer.remove(); this._selectionContainer.remove(); this._widthCache.dispose(); @@ -184,9 +186,11 @@ export class DomRenderer extends Disposable implements IRenderer { // Base CSS let styles = `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} {` + - // Disabling pointer events circumvents a browser behavior that prevents `click` events from - // being delivered if the target element is replaced during the click. This happened due to - // refresh() being called during the mousedown handler to start a selection. + /* + * Disabling pointer events circumvents a browser behavior that prevents `click` events from + * being delivered if the target element is replaced during the click. This happened due to + * refresh() being called during the mousedown handler to start a selection. + */ ` pointer-events: none;` + ` color: ${colors.foreground.css};` + `}`; @@ -257,9 +261,11 @@ export class DomRenderer extends Disposable implements IRenderer { `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS}.${Constants.CURSOR_BLINK_IDLE_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS} {` + ` animation: none !important;` + `}` + - // !important helps fix an issue where the cursor will not render on top of the selection, - // however it's very hard to fix this issue and retain the blink animation without the use of - // !important. So this edge case fails when cursor blink is on. + /* + * !important helps fix an issue where the cursor will not render on top of the selection, + * however it's very hard to fix this issue and retain the blink animation without the use of + * !important. So this edge case fails when cursor blink is on. + */ `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` + ` background-color: ${colors.cursor.css};` + ` color: ${colors.cursorAccent.css};` + diff --git a/src/browser/renderer/dom/DomRendererRowFactory.test.ts b/src/browser/renderer/dom/DomRendererRowFactory.test.ts index 6ca84569a..4bd5b8e7b 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.test.ts @@ -359,8 +359,10 @@ describe('DomRendererRowFactory', () => { }); describe('createRow with merged spans', () => { - // for test purpose assume all in codepoints 0..255 are merging - // const ALL_MERGING = new Uint8Array(FontMetrics.MAX); + /* + * for test purpose assume all in codepoints 0..255 are merging + * const ALL_MERGING = new Uint8Array(FontMetrics.MAX); + */ beforeEach(() => { lineData = createEmptyLineData(10); diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts index 0cff9c25f..fc03e27b3 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.ts @@ -115,20 +115,26 @@ export class DomRendererRowFactory { // If true, indicates that the current character(s) to draw were joined. let isJoined = false; - // Indicates whether this cell is part of a joined range that should be ignored as it cannot - // be rendered entirely, like the selection state differs across the range. + /* + * Indicates whether this cell is part of a joined range that should be ignored as it cannot + * be rendered entirely, like the selection state differs across the range. + */ let isValidJoinRange = (x >= skipJoinedCheckUntilX); let lastCharX = x; - // Process any joined character ranges as needed. Because of how the - // ranges are produced, we know that they are valid for the characters - // and attributes of our input. + /* + * Process any joined character ranges as needed. Because of how the + * ranges are produced, we know that they are valid for the characters + * and attributes of our input. + */ let cell: ICellData = this._workCell; if (joinedRanges.length > 0 && x === joinedRanges[0][0] && isValidJoinRange) { const range = joinedRanges.shift()!; - // If the ligature's selection state is not consistent, don't join it. This helps the - // selection render correctly regardless whether they should be joined. + /* + * If the ligature's selection state is not consistent, don't join it. This helps the + * selection render correctly regardless whether they should be joined. + */ const firstSelectionState = this._isCellInSelection(range[0], row); for (i = range[0] + 1; i < range[1]; i++) { isValidJoinRange &&= (firstSelectionState === this._isCellInSelection(i, row)); @@ -140,8 +146,10 @@ export class DomRendererRowFactory { } else { isJoined = true; - // We already know the exact start and end column of the joined range, - // so we get the string and width representing it directly + /* + * We already know the exact start and end column of the joined range, + * so we get the string and width representing it directly + */ cell = new JoinedCellData( this._workCell, lineData.translateToString(true, range[0], range[1]), @@ -243,9 +251,11 @@ export class DomRendererRowFactory { oldIsInSelection = isInSelection; if (isJoined) { - // The DOM renderer colors the background of the cursor but for ligatures all cells are - // joined. The workaround here is to show a cursor around the whole ligature so it shows up, - // the cursor looks the same when on any character of the ligature though + /* + * The DOM renderer colors the background of the cursor but for ligatures all cells are + * joined. The workaround here is to show a cursor around the whole ligature so it shows up, + * the cursor looks the same when on any character of the ligature though + */ if (cursorX >= x && cursorX <= lastCharX) { cursorX = x; } @@ -333,8 +343,10 @@ export class DomRendererRowFactory { classes.push(RowCss.STRIKETHROUGH_CLASS); } - // apply link hover underline late, effectively overrides any previous text-decoration - // settings + /* + * apply link hover underline late, effectively overrides any previous text-decoration + * settings + */ if (isLinkHover) { charElement.style.textDecoration = 'underline'; } @@ -353,8 +365,10 @@ export class DomRendererRowFactory { bgColorMode = temp2; } - // Apply any decoration foreground/background overrides, this must happen after inverse has - // been applied + /* + * Apply any decoration foreground/background overrides, this must happen after inverse has + * been applied + */ let bgOverride: IColor | undefined; let fgOverride: IColor | undefined; let isTop = false; @@ -377,15 +391,19 @@ export class DomRendererRowFactory { // Apply selection if (!isTop && isInSelection) { - // If in the selection, force the element to be above the selection to improve contrast and - // support opaque selections. The applies background is not actually needed here as - // selection is drawn in a seperate container, the main purpose of this to ensuring minimum - // contrast ratio + /* + * If in the selection, force the element to be above the selection to improve contrast and + * support opaque selections. The applies background is not actually needed here as + * selection is drawn in a seperate container, the main purpose of this to ensuring minimum + * contrast ratio + */ bgOverride = this._coreBrowserService.isFocused ? colors.selectionBackgroundOpaque : colors.selectionInactiveBackgroundOpaque; bg = bgOverride.rgba >> 8 & 0xFFFFFF; bgColorMode = Attributes.CM_RGB; - // Since an opaque selection is being rendered, the selection pretends to be a decoration to - // ensure text is drawn above the selection. + /* + * Since an opaque selection is being rendered, the selection pretends to be a decoration to + * ensure text is drawn above the selection. + */ isTop = true; // Apply selection foreground if applicable if (colors.selectionForeground) { @@ -459,9 +477,11 @@ export class DomRendererRowFactory { } } - // apply CSS classes - // slightly faster than using classList by omitting - // checks for doubled entries (code above should not have doublets) + /* + * apply CSS classes + * slightly faster than using classList by omitting + * checks for doubled entries (code above should not have doublets) + */ if (classes.length) { charElement.className = classes.join(' '); classes.length = 0; @@ -504,8 +524,10 @@ export class DomRendererRowFactory { // Calculate and store in cache if (adjustedColor === undefined) { - // Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from - // non-dim cells + /* + * Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from + * non-dim cells + */ const ratio = this._optionsService.rawOptions.minimumContrastRatio / (cell.isDim() ? 2 : 1); adjustedColor = color.ensureContrastRatio(bgOverride ?? bg, fgOverride ?? fg, ratio); cache.setColor((bgOverride ?? bg).rgba, (fgOverride ?? fg).rgba, adjustedColor ?? null); diff --git a/src/browser/renderer/dom/WidthCache.ts b/src/browser/renderer/dom/WidthCache.ts index b598bbd6f..1bf66204d 100644 --- a/src/browser/renderer/dom/WidthCache.ts +++ b/src/browser/renderer/dom/WidthCache.ts @@ -31,16 +31,20 @@ export interface IWidthCacheFontVariantCanvas { } export class WidthCache implements IDisposable { - // flat cache for regular variant up to CacheSettings.FLAT_SIZE - // NOTE: ~4x faster access than holey (serving >>80% of terminal content) - // It has a small memory footprint (only 1MB for full BMP caching), - // still the sweet spot is not reached before touching 32k different codepoints, - // thus we store the remaining <<20% of terminal data in a holey structure. + /* + * flat cache for regular variant up to CacheSettings.FLAT_SIZE + * NOTE: ~4x faster access than holey (serving >>80% of terminal content) + * It has a small memory footprint (only 1MB for full BMP caching), + * still the sweet spot is not reached before touching 32k different codepoints, + * thus we store the remaining <<20% of terminal data in a holey structure. + */ protected _flat = new Float32Array(WidthCacheSettings.FLAT_SIZE); - // holey cache for bold, italic and bold&italic for any string - // FIXME: can grow really big over time (~8.5 MB for full BMP caching), - // so a shared API across terminals is needed + /* + * holey cache for bold, italic and bold&italic for any string + * FIXME: can grow really big over time (~8.5 MB for full BMP caching), + * so a shared API across terminals is needed + */ protected _holey: Map | undefined; private _font = ''; diff --git a/src/browser/renderer/shared/RendererUtils.test.ts b/src/browser/renderer/shared/RendererUtils.test.ts index 4512cb6d9..87f6a3f20 100644 --- a/src/browser/renderer/shared/RendererUtils.test.ts +++ b/src/browser/renderer/shared/RendererUtils.test.ts @@ -13,8 +13,10 @@ describe('RendererUtils', () => { let line = 1; let variantOffset = 0; - // should line 1 - // =,_,=_,=_, + /* + * should line 1 + * =,_,=_,=_, + */ let cells = [cellWidth, cellWidth, doubleCellWidth, doubleCellWidth]; let result = [1, 0, 0, 0]; for (let index = 0; index < cells.length; index++) { @@ -23,8 +25,10 @@ describe('RendererUtils', () => { assert.equal(variantOffset, result[index]); } - // should line 2 - // ==__==__==_,_==__==__==,__==__==__==__==__==__,==__==__==__==__==__==, + /* + * should line 2 + * ==__==__==_,_==__==__==,__==__==__==__==__==__,==__==__==__==__==__==, + */ line = 2; variantOffset = 0; cells = [cellWidth, cellWidth, doubleCellWidth, doubleCellWidth]; @@ -35,8 +39,10 @@ describe('RendererUtils', () => { assert.equal(variantOffset, result[index]); } - // should line 3 - // ===___===__,_===___===_,__===___===___===___==,=___===___===___===___, + /* + * should line 3 + * ===___===__,_===___===_,__===___===___===___==,=___===___===___===___, + */ line = 3; variantOffset = 0; cells = [cellWidth, cellWidth, doubleCellWidth, doubleCellWidth]; diff --git a/src/browser/renderer/shared/RendererUtils.ts b/src/browser/renderer/shared/RendererUtils.ts index 010643648..30925f32f 100644 --- a/src/browser/renderer/shared/RendererUtils.ts +++ b/src/browser/renderer/shared/RendererUtils.ts @@ -13,9 +13,11 @@ export function throwIfFalsy(value: T | undefined | null): T { } export function isPowerlineGlyph(codepoint: number): boolean { - // Only return true for Powerline symbols which require - // different padding and should be excluded from minimum contrast - // ratio standards + /* + * Only return true for Powerline symbols which require + * different padding and should be excluded from minimum contrast + * ratio standards + */ return 0xE0A4 <= codepoint && codepoint <= 0xE0D6; } @@ -48,8 +50,10 @@ export function allowRescaling(codepoint: number | undefined, width: number, gly return ( // Is single cell width width === 1 && - // Glyph exceeds cell bounds, add 50% to avoid hurting readability by rescaling glyphs that - // barely overlap + /* + * Glyph exceeds cell bounds, add 50% to avoid hurting readability by rescaling glyphs that + * barely overlap + */ glyphSizeX > Math.ceil(deviceCellWidth * 1.5) && // Never rescale ascii codepoint !== undefined && codepoint > 0xFF && diff --git a/src/browser/scrollable/abstractScrollbar.ts b/src/browser/scrollable/abstractScrollbar.ts index 64c25d9d8..0463471b5 100644 --- a/src/browser/scrollable/abstractScrollbar.ts +++ b/src/browser/scrollable/abstractScrollbar.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import * as dom from '../Dom'; import { FastDomNode } from './fastDomNode'; diff --git a/src/browser/scrollable/fastDomNode.ts b/src/browser/scrollable/fastDomNode.ts index 1a795c2be..206416677 100644 --- a/src/browser/scrollable/fastDomNode.ts +++ b/src/browser/scrollable/fastDomNode.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ export class FastDomNode { diff --git a/src/browser/scrollable/globalPointerMoveMonitor.ts b/src/browser/scrollable/globalPointerMoveMonitor.ts index a80e0507f..b2e9b588a 100644 --- a/src/browser/scrollable/globalPointerMoveMonitor.ts +++ b/src/browser/scrollable/globalPointerMoveMonitor.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import * as dom from '../Dom'; import { DisposableStore, IDisposable, toDisposable } from 'common/Lifecycle'; diff --git a/src/browser/scrollable/horizontalScrollbar.ts b/src/browser/scrollable/horizontalScrollbar.ts index 299a3e473..8e48ea7ff 100644 --- a/src/browser/scrollable/horizontalScrollbar.ts +++ b/src/browser/scrollable/horizontalScrollbar.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { AbstractScrollbar, ISimplifiedPointerEvent, IScrollbarHost } from './abstractScrollbar'; import { IScrollableElementResolvedOptions } from './scrollableElementOptions'; diff --git a/src/browser/scrollable/mouseEvent.ts b/src/browser/scrollable/mouseEvent.ts index a12b63d52..482ba82aa 100644 --- a/src/browser/scrollable/mouseEvent.ts +++ b/src/browser/scrollable/mouseEvent.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import * as platform from 'common/Platform'; diff --git a/src/browser/scrollable/scrollable.ts b/src/browser/scrollable/scrollable.ts index 18275ffe5..9f8cbb6f7 100644 --- a/src/browser/scrollable/scrollable.ts +++ b/src/browser/scrollable/scrollable.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { Emitter, IEvent } from 'common/Event'; import { Disposable, IDisposable } from 'common/Lifecycle'; diff --git a/src/browser/scrollable/scrollableElement.ts b/src/browser/scrollable/scrollableElement.ts index 4039d0c4e..a41dde991 100644 --- a/src/browser/scrollable/scrollableElement.ts +++ b/src/browser/scrollable/scrollableElement.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import * as dom from '../Dom'; import { FastDomNode } from './fastDomNode'; diff --git a/src/browser/scrollable/scrollableElementOptions.ts b/src/browser/scrollable/scrollableElementOptions.ts index 037cd5c11..0e1d06226 100644 --- a/src/browser/scrollable/scrollableElementOptions.ts +++ b/src/browser/scrollable/scrollableElementOptions.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { ScrollbarVisibility } from './scrollable'; diff --git a/src/browser/scrollable/scrollbarArrow.ts b/src/browser/scrollable/scrollbarArrow.ts index d543669c1..5784dc8dc 100644 --- a/src/browser/scrollable/scrollbarArrow.ts +++ b/src/browser/scrollable/scrollbarArrow.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { GlobalPointerMoveMonitor } from './globalPointerMoveMonitor'; import { Widget } from './widget'; diff --git a/src/browser/scrollable/scrollbarState.ts b/src/browser/scrollable/scrollbarState.ts index f9129f1b0..7b7751f97 100644 --- a/src/browser/scrollable/scrollbarState.ts +++ b/src/browser/scrollable/scrollbarState.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ /** * The minimal size of the slider (such that it can still be clickable). diff --git a/src/browser/scrollable/scrollbarVisibilityController.ts b/src/browser/scrollable/scrollbarVisibilityController.ts index 59d172bce..b56e3524f 100644 --- a/src/browser/scrollable/scrollbarVisibilityController.ts +++ b/src/browser/scrollable/scrollbarVisibilityController.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { FastDomNode } from './fastDomNode'; import { TimeoutTimer } from 'common/Async'; diff --git a/src/browser/scrollable/touch.ts b/src/browser/scrollable/touch.ts index 8ffdfe699..a734a79a1 100644 --- a/src/browser/scrollable/touch.ts +++ b/src/browser/scrollable/touch.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import * as DomUtils from '../Dom'; import { Disposable, IDisposable, toDisposable } from 'common/Lifecycle'; diff --git a/src/browser/scrollable/verticalScrollbar.ts b/src/browser/scrollable/verticalScrollbar.ts index 615f443a0..9f5a090af 100644 --- a/src/browser/scrollable/verticalScrollbar.ts +++ b/src/browser/scrollable/verticalScrollbar.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { AbstractScrollbar, ISimplifiedPointerEvent, IScrollbarHost } from './abstractScrollbar'; import { IScrollableElementResolvedOptions } from './scrollableElementOptions'; diff --git a/src/browser/scrollable/widget.ts b/src/browser/scrollable/widget.ts index 80220c9b1..eae4ec185 100644 --- a/src/browser/scrollable/widget.ts +++ b/src/browser/scrollable/widget.ts @@ -1,7 +1,9 @@ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import * as dom from '../Dom'; import { IMouseEvent, StandardMouseEvent } from './mouseEvent'; diff --git a/src/browser/services/CharSizeService.ts b/src/browser/services/CharSizeService.ts index 6baf9b50a..8d97f233c 100644 --- a/src/browser/services/CharSizeService.ts +++ b/src/browser/services/CharSizeService.ts @@ -61,8 +61,10 @@ abstract class BaseMeasureStategy extends Disposable implements IMeasureStrategy protected _result: IMeasureResult = { width: 0, height: 0 }; protected _validateAndSet(width: number | undefined, height: number | undefined): void { - // If values are 0 then the element is likely currently display:none, in which case we should - // retain the previous value. + /* + * If values are 0 then the element is likely currently display:none, in which case we should + * retain the previous value. + */ if (width !== undefined && width > 0 && height !== undefined && height > 0) { this._result.width = width; this._result.height = height; diff --git a/src/browser/services/CharacterJoinerService.ts b/src/browser/services/CharacterJoinerService.ts index ca4f1984e..d0ded2623 100644 --- a/src/browser/services/CharacterJoinerService.ts +++ b/src/browser/services/CharacterJoinerService.ts @@ -13,8 +13,10 @@ import { ICharacterJoinerService } from 'browser/services/Services'; export class JoinedCellData extends AttributeData implements ICellData { private _width: number; - // .content carries no meaning for joined CellData, simply nullify it - // thus we have to overload all other .content accessors + /* + * .content carries no meaning for joined CellData, simply nullify it + * thus we have to overload all other .content accessors + */ public content: number = 0; public fg: number; public bg: number; @@ -42,8 +44,10 @@ export class JoinedCellData extends AttributeData implements ICellData { } public getCode(): number { - // code always gets the highest possible fake codepoint (read as -1) - // this is needed as code is used by caches as identifier + /* + * code always gets the highest possible fake codepoint (read as -1) + * this is needed as code is used by caches as identifier + */ return 0x1FFFFF; } @@ -101,10 +105,12 @@ export class CharacterJoinerService implements ICharacterJoinerService { const ranges: [number, number][] = []; const lineStr = line.translateToString(true); - // Because some cells can be represented by multiple javascript characters, - // we track the cell and the string indexes separately. This allows us to - // translate the string ranges we get from the joiners back into cell ranges - // for use when rendering + /* + * Because some cells can be represented by multiple javascript characters, + * we track the cell and the string indexes separately. This allows us to + * translate the string ranges we get from the joiners back into cell ranges + * for use when rendering + */ let rangeStartColumn = 0; let currentStringIndex = 0; let rangeStartStringIndex = 0; @@ -121,8 +127,10 @@ export class CharacterJoinerService implements ICharacterJoinerService { // End of range if (this._workCell.fg !== rangeAttrFG || this._workCell.bg !== rangeAttrBG) { - // If we ended up with a sequence of more than one character, - // look for ranges to join. + /* + * If we ended up with a sequence of more than one character, + * look for ranges to join. + */ if (x - rangeStartColumn > 1) { const joinedRanges = this._getJoinedRanges( lineStr, @@ -173,9 +181,11 @@ export class CharacterJoinerService implements ICharacterJoinerService { */ private _getJoinedRanges(line: string, startIndex: number, endIndex: number, lineData: IBufferLine, startCol: number): [number, number][] { const text = line.substring(startIndex, endIndex); - // At this point we already know that there is at least one joiner so - // we can just pull its value and assign it directly rather than - // merging it into an empty array, which incurs unnecessary writes. + /* + * At this point we already know that there is at least one joiner so + * we can just pull its value and assign it directly rather than + * merging it into an empty array, which incurs unnecessary writes. + */ let allJoinedRanges: [number, number][] = []; try { allJoinedRanges = this._characterJoiners[0].handler(text); @@ -220,8 +230,10 @@ export class CharacterJoinerService implements ICharacterJoinerService { const width = line.getWidth(x); const length = line.getString(x).length || WHITESPACE_CELL_CHAR.length; - // We skip zero-width characters when creating the string to join the text - // so we do the same here + /* + * We skip zero-width characters when creating the string to join the text + * so we do the same here + */ if (width === 0) { continue; } @@ -244,10 +256,12 @@ export class CharacterJoinerService implements ICharacterJoinerService { break; } - // Ranges can be on adjacent characters. Because the end index of the - // ranges are exclusive, this means that the index for the start of a - // range can be the same as the end index of the previous range. To - // account for the start of the next range, we check here just in case. + /* + * Ranges can be on adjacent characters. Because the end index of the + * ranges are exclusive, this means that the index for the start of a + * range can be the same as the end index of the previous range. To + * account for the start of the next range, we check here just in case. + */ if (currentRange[0] <= currentStringIndex) { currentRange[0] = x; currentRangeStarted = true; @@ -256,13 +270,17 @@ export class CharacterJoinerService implements ICharacterJoinerService { } } - // Adjust the string index based on the character length to line up with - // the column adjustment + /* + * Adjust the string index based on the character length to line up with + * the column adjustment + */ currentStringIndex += length; } - // If there is still a range left at the end, it must extend all the way to - // the end of the line. + /* + * If there is still a range left at the end, it must extend all the way to + * the end of the line. + */ if (currentRange) { currentRange[1] = this._bufferService.cols; } @@ -288,15 +306,19 @@ export class CharacterJoinerService implements ICharacterJoinerService { } if (newRange[1] <= range[1]) { - // Case 2: New range is either wholly contained within the - // search range or overlaps with the front of it + /* + * Case 2: New range is either wholly contained within the + * search range or overlaps with the front of it + */ range[0] = Math.min(newRange[0], range[0]); return ranges; } if (newRange[0] < range[1]) { - // Case 3: New range either wholly contains the search range - // or overlaps with the end of it + /* + * Case 3: New range either wholly contains the search range + * or overlaps with the end of it + */ range[0] = Math.min(newRange[0], range[0]); inRange = true; } @@ -305,22 +327,28 @@ export class CharacterJoinerService implements ICharacterJoinerService { continue; } else { if (newRange[1] <= range[0]) { - // Case 5: New range extends from previous range but doesn't - // reach the current one + /* + * Case 5: New range extends from previous range but doesn't + * reach the current one + */ ranges[i - 1][1] = newRange[1]; return ranges; } if (newRange[1] <= range[1]) { - // Case 6: New range extends from prvious range into the - // current range + /* + * Case 6: New range extends from prvious range into the + * current range + */ ranges[i - 1][1] = Math.max(newRange[1], range[1]); ranges.splice(i, 1); return ranges; } - // Case 7: New range extends from previous range past the - // end of the current range + /* + * Case 7: New range extends from previous range past the + * end of the current range + */ ranges.splice(i, 1); i--; } diff --git a/src/browser/services/MouseService.test.ts b/src/browser/services/MouseService.test.ts index f395083e3..63e14954a 100644 --- a/src/browser/services/MouseService.test.ts +++ b/src/browser/services/MouseService.test.ts @@ -127,8 +127,10 @@ describe('MouseService _triggerMouseEvent', () => { assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.RIGHT, action: CoreMouseAction.DOWN }), true); assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.WHEEL, action: CoreMouseAction.UP }), true); assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.NONE, action: CoreMouseAction.MOVE }), true); - // should not report in any case - // invalid button + action combinations + /* + * should not report in any case + * invalid button + action combinations + */ assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.WHEEL, action: CoreMouseAction.MOVE }), false); assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.NONE, action: CoreMouseAction.DOWN }), false); assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.NONE, action: CoreMouseAction.UP }), false); diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts index d701796c2..2f35957b7 100644 --- a/src/browser/services/MouseService.ts +++ b/src/browser/services/MouseService.ts @@ -147,8 +147,10 @@ export class MouseService implements IMouseService { return false; } - // exit if we cannot determine valid button/action values - // do nothing for higher buttons than wheel + /* + * exit if we cannot determine valid button/action values + * do nothing for higher buttons than wheel + */ if (action === undefined || but === undefined || but > CoreMouseButton.WHEEL) { return false; } @@ -204,19 +206,23 @@ export class MouseService implements IMouseService { ev.preventDefault(); ctx.focus(); - // Don't send the mouse button to the pty if mouse events are disabled or - // if the selection manager is having selection forced (ie. a modifier is - // held). + /* + * Don't send the mouse button to the pty if mouse events are disabled or + * if the selection manager is having selection forced (ie. a modifier is + * held). + */ if (!this._mouseStateService.areMouseEventsActive || this._selectionService.shouldForceSelection(ev)) { return; } this._sendEvent(ctx, ev); - // Register additional global handlers which should keep reporting outside - // of the terminal element. - // Note: Other emulators also do this for 'mousedown' while a button - // is held, we currently limit 'mousedown' to the terminal only. + /* + * Register additional global handlers which should keep reporting outside + * of the terminal element. + * Note: Other emulators also do this for 'mousedown' while a button + * is held, we currently limit 'mousedown' to the terminal only. + */ if (ctx.requestedEvents.mouseup) { ctx.target.document.addEventListener('mouseup', ctx.requestedEvents.mouseup); } @@ -236,12 +242,14 @@ export class MouseService implements IMouseService { } if (!this._bufferService.buffer.hasScrollback) { - // Convert wheel events into up/down events when the buffer does not have scrollback, this - // enables scrolling in apps hosted in the alt buffer such as vim or tmux even when mouse - // events are not enabled. - // This used implementation used get the actual lines/partial lines scrolled from the - // viewport but since moving to the new viewport implementation has been simplified to - // simply send a single up or down sequence. + /* + * Convert wheel events into up/down events when the buffer does not have scrollback, this + * enables scrolling in apps hosted in the alt buffer such as vim or tmux even when mouse + * events are not enabled. + * This used implementation used get the actual lines/partial lines scrolled from the + * viewport but since moving to the new viewport implementation has been simplified to + * simply send a single up or down sequence. + */ // Do nothing if there's no vertical scroll const deltaY = ev.deltaY; diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index e999b7eac..433a24eb5 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -88,9 +88,11 @@ export class RenderService extends Disposable implements IRenderService { this._register(this._optionsService.onOptionChange(() => this._handleOptionsChanged())); this._register(this._charSizeService.onCharSizeChange(() => this.handleCharSizeChanged())); - // Do a full refresh whenever any decoration is added or removed. This may not actually result - // in changes but since decorations should be used sparingly or added/removed all in the same - // frame this should have minimal performance impact. + /* + * Do a full refresh whenever any decoration is added or removed. This may not actually result + * in changes but since decorations should be used sparingly or added/removed all in the same + * frame this should have minimal performance impact. + */ this._register(decorationService.onDecorationRegistered(() => this._fullRefresh())); this._register(decorationService.onDecorationRemoved(() => this._fullRefresh())); @@ -124,8 +126,10 @@ export class RenderService extends Disposable implements IRenderService { } private _registerIntersectionObserver(w: Window & typeof globalThis, screenElement: HTMLElement): void { - // Detect whether IntersectionObserver is detected and enable renderer pause - // and resume based on terminal visibility if so + /* + * Detect whether IntersectionObserver is detected and enable renderer pause + * and resume based on terminal visibility if so + */ if ('IntersectionObserver' in w) { const observer = new w.IntersectionObserver(e => this._handleIntersectionChange(e[e.length - 1]), { threshold: 0 }); this._observerDisposable.value = toDisposable(() => { @@ -186,16 +190,20 @@ export class RenderService extends Disposable implements IRenderService { return; } - // Skip rendering if synchronized output mode is enabled. This check must happen here - // (in addition to refreshRows) to handle renders that were queued before the mode was enabled. + /* + * Skip rendering if synchronized output mode is enabled. This check must happen here + * (in addition to refreshRows) to handle renders that were queued before the mode was enabled. + */ if (this._coreService.decPrivateModes.synchronizedOutput) { this._syncOutputHandler.bufferRows(start, end); return; } - // Since this is debounced, a resize event could have happened between the time a refresh was - // requested and when this triggers. Clamp the values of start and end to ensure they're valid - // given the current viewport state. + /* + * Since this is debounced, a resize event could have happened between the time a refresh was + * requested and when this triggers. Clamp the values of start and end to ensure they're valid + * given the current viewport state. + */ start = Math.min(start, this._rowCount - 1); end = Math.min(end, this._rowCount - 1); @@ -277,8 +285,10 @@ export class RenderService extends Disposable implements IRenderService { } public handleDevicePixelRatioChange(): void { - // Force char size measurement as DomMeasureStrategy(getBoundingClientRect) is not stable - // when devicePixelRatio changes + /* + * Force char size measurement as DomMeasureStrategy(getBoundingClientRect) is not stable + * when devicePixelRatio changes + */ this._charSizeService.measure(); if (!this._renderer.value) { diff --git a/src/browser/services/SelectionService.test.ts b/src/browser/services/SelectionService.test.ts index be62ccc6b..822bb6b6d 100644 --- a/src/browser/services/SelectionService.test.ts +++ b/src/browser/services/SelectionService.test.ts @@ -257,8 +257,10 @@ describe('SelectionService', () => { ])); // The a is here to prevent the space being trimmed in selectionText selectionService.selectWordAt([0, 0]); assert.equal(selectionService.selectionText, ' '); - // ZWJ emojis do not combine in the terminal so the family emoji used here consumed 4 cells - // The selection text should retain ZWJ chars despite not combining on the terminal + /* + * ZWJ emojis do not combine in the terminal so the family emoji used here consumed 4 cells + * The selection text should retain ZWJ chars despite not combining on the terminal + */ selectionService.selectWordAt([1, 0]); assert.equal(selectionService.selectionText, '👨‍👩‍👧‍👦'); selectionService.selectWordAt([2, 0]); @@ -473,8 +475,10 @@ describe('SelectionService', () => { }); it('should select a column of text with double character emojis', () => { - // TODO the case this is testing works for me in the demo webapp, - // but doing it programmatically fails. + /* + * TODO the case this is testing works for me in the demo webapp, + * but doing it programmatically fails. + */ buffer.lines.length = 3; buffer.lines.set(0, stringToRow('a ')); buffer.lines.set(1, stringArrayToRow(['😁', ' '])); diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index 39692a871..29ed1b827 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -152,8 +152,10 @@ export class SelectionService extends Disposable implements ISelectionService { this._removeMouseDownListeners(); })); - // Clear selection when resizing vertically. This experience could be improved, this is the - // simple option to fix the buggy behavior. https://github.com/xtermjs/xterm.js/issues/5300 + /* + * Clear selection when resizing vertically. This experience could be improved, this is the + * simple option to fix the buggy behavior. https://github.com/xtermjs/xterm.js/issues/5300 + */ this._register(this._bufferService.onResize(e => { if (e.rowsChanged) { this.clearSelection(); @@ -215,8 +217,10 @@ export class SelectionService extends Disposable implements ISelectionService { return ''; } - // For column selection it's not enough to rely on final selection's swapping of reversed - // values, it also needs the x coordinates to swap independently of the y coordinate is needed + /* + * For column selection it's not enough to rely on final selection's swapping of reversed + * values, it also needs the x coordinates to swap independently of the y coordinate is needed + */ const startCol = start[0] < end[0] ? start[0] : end[0]; const endCol = start[0] < end[0] ? end[0] : start[0]; for (let i = start[1]; i <= end[1]; i++) { @@ -251,8 +255,10 @@ export class SelectionService extends Disposable implements ISelectionService { } } - // Format string by replacing non-breaking space chars with regular spaces - // and joining the array into a multi-line string. + /* + * Format string by replacing non-breaking space chars with regular spaces + * and joining the array into a multi-line string. + */ const formattedResult = result.map(line => { return line.replace(ALL_NON_BREAKING_SPACE_REGEX, ' '); }).join(Browser.isWindows ? '\r\n' : '\n'); @@ -281,8 +287,10 @@ export class SelectionService extends Disposable implements ISelectionService { this._refreshAnimationFrame = this._coreBrowserService.window.requestAnimationFrame(() => this._refresh()); } - // If the platform is Linux and the refresh call comes from a mouse event, - // we need to update the selection for middle click to paste selection. + /* + * If the platform is Linux and the refresh call comes from a mouse event, + * we need to update the selection for middle click to paste selection. + */ if (Browser.isLinux && isLinuxMouseSelection) { const selectionText = this.selectionText; if (selectionText.length) { @@ -447,8 +455,10 @@ export class SelectionService extends Disposable implements ISelectionService { */ public handleMouseDown(event: MouseEvent): void { this._mouseDownTimeStamp = event.timeStamp; - // If we have selection, we want the context menu on right click even if the - // terminal is in mouse mode. + /* + * If we have selection, we want the context menu on right click even if the + * terminal is in mouse mode. + */ if (event.button === 2 && this.hasSelection) { return; } @@ -561,8 +571,10 @@ export class SelectionService extends Disposable implements ISelectionService { return; } - // If the mouse is over the second half of a wide character, adjust the - // selection to cover the whole character + /* + * If the mouse is over the second half of a wide character, adjust the + * selection to cover the whole character + */ if (line.hasWidth(this._model.selectionStart[0]) === 0) { this._model.selectionStart[0]++; } @@ -605,19 +617,25 @@ export class SelectionService extends Disposable implements ISelectionService { * @param event The mousemove event. */ private _handleMouseMove(event: MouseEvent): void { - // If the mousemove listener is active it means that a selection is - // currently being made, we should stop propagation to prevent mouse events - // to be sent to the pty. + /* + * If the mousemove listener is active it means that a selection is + * currently being made, we should stop propagation to prevent mouse events + * to be sent to the pty. + */ event.stopImmediatePropagation(); - // Do nothing if there is no selection start, this can happen if the first - // click in the terminal is an incremental click + /* + * Do nothing if there is no selection start, this can happen if the first + * click in the terminal is an incremental click + */ if (!this._model.selectionStart) { return; } - // Record the previous position so we know whether to redraw the selection - // at the end. + /* + * Record the previous position so we know whether to redraw the selection + * at the end. + */ const previousSelectionEnd = this._model.selectionEnd ? [this._model.selectionEnd[0], this._model.selectionEnd[1]] : null; // Set the initial selection end based on the mouse coordinates @@ -641,9 +659,11 @@ export class SelectionService extends Disposable implements ISelectionService { // Determine the amount of scrolling that will happen. this._dragScrollAmount = this._getMouseEventScrollAmount(event); - // If the cursor was above or below the viewport, make sure it's at the - // start or end of the viewport respectively. This should only happen when - // NOT in column select mode. + /* + * If the cursor was above or below the viewport, make sure it's at the + * start or end of the viewport respectively. This should only happen when + * NOT in column select mode. + */ if (this._activeSelectionMode !== SelectionMode.COLUMN) { if (this._dragScrollAmount > 0) { this._model.selectionEnd[0] = this._bufferService.cols; @@ -652,9 +672,11 @@ export class SelectionService extends Disposable implements ISelectionService { } } - // If the character is a wide character include the cell to the right in the - // selection. Note that selections at the very end of the line will never - // have a character. + /* + * If the character is a wide character include the cell to the right in the + * selection. Note that selections at the very end of the line will never + * have a character. + */ const buffer = this._bufferService.buffer; if (this._model.selectionEnd[1] < buffer.lines.length) { const line = buffer.lines.get(this._model.selectionEnd[1]); @@ -683,10 +705,12 @@ export class SelectionService extends Disposable implements ISelectionService { } if (this._dragScrollAmount) { this._onRequestScrollLines.fire({ amount: this._dragScrollAmount, suppressScrollEvent: false }); - // Re-evaluate selection - // If the cursor was above or below the viewport, make sure it's at the - // start or end of the viewport respectively. This should only happen when - // NOT in column select mode. + /* + * Re-evaluate selection + * If the cursor was above or below the viewport, make sure it's at the + * start or end of the viewport respectively. This should only happen when + * NOT in column select mode. + */ const buffer = this._bufferService.buffer; if (this._dragScrollAmount > 0) { if (this._activeSelectionMode !== SelectionMode.COLUMN) { @@ -765,10 +789,12 @@ export class SelectionService extends Disposable implements ISelectionService { private _handleBufferActivate(e: {activeBuffer: IBuffer, inactiveBuffer: IBuffer}): void { this.clearSelection(); - // Only adjust the selection on trim, shiftElements is rarely used (only in - // reverseIndex) and delete in a splice is only ever used when the same - // number of elements was just added. Given this is could actually be - // beneficial to leave the selection as is for these cases. + /* + * Only adjust the selection on trim, shiftElements is rarely used (only in + * reverseIndex) and delete in a splice is only ever used when the same + * number of elements was just added. Given this is could actually be + * beneficial to leave the selection as is for these cases. + */ this._trimListener.dispose(); this._trimListener = e.activeBuffer.lines.onTrim(amount => this._handleTrim(amount)); } @@ -784,13 +810,17 @@ export class SelectionService extends Disposable implements ISelectionService { for (let i = 0; x >= i; i++) { const length = bufferLine.loadCell(i, this._workCell).getChars().length; if (this._workCell.getWidth() === 0) { - // Wide characters aren't included in the line string so decrement the - // index so the index is back on the wide character. + /* + * Wide characters aren't included in the line string so decrement the + * index so the index is back on the wide character. + */ charIndex--; } else if (length > 1 && x !== i) { - // Emojis take up multiple characters, so adjust accordingly. For these - // we don't want ot include the character at the column as we're - // returning the start index in the string, not the end index. + /* + * Emojis take up multiple characters, so adjust accordingly. For these + * we don't want ot include the character at the column as we're + * returning the start index in the string, not the end index. + */ charIndex += length - 1; } } @@ -853,15 +883,19 @@ export class SelectionService extends Disposable implements ISelectionService { endIndex++; } } else { - // Expand until whitespace is hit. This algorithm works by scanning left - // and right from the starting position, keeping both the index format - // (line) and the column format (bufferLine) in sync. When a wide - // character is hit, it is recorded and the column index is adjusted. + /* + * Expand until whitespace is hit. This algorithm works by scanning left + * and right from the starting position, keeping both the index format + * (line) and the column format (bufferLine) in sync. When a wide + * character is hit, it is recorded and the column index is adjusted. + */ let startCol = coords[0]; let endCol = coords[0]; - // Consider the initial position, skip it and increment the wide char - // variable + /* + * Consider the initial position, skip it and increment the wide char + * variable + */ if (bufferLine.getWidth(startCol) === 0) { leftWideCharCount++; startCol--; @@ -887,8 +921,10 @@ export class SelectionService extends Disposable implements ISelectionService { leftWideCharCount++; startCol--; } else if (length > 1) { - // If the next character's string is longer than 1 char (eg. emoji), - // adjust the index + /* + * If the next character's string is longer than 1 char (eg. emoji), + * adjust the index + */ leftLongCharOffset += length - 1; startIndex -= length - 1; } @@ -903,8 +939,10 @@ export class SelectionService extends Disposable implements ISelectionService { rightWideCharCount++; endCol++; } else if (length > 1) { - // If the next character's string is longer than 1 char (eg. emoji), - // adjust the index + /* + * If the next character's string is longer than 1 char (eg. emoji), + * adjust the index + */ rightLongCharOffset += length - 1; endIndex += length - 1; } @@ -916,16 +954,20 @@ export class SelectionService extends Disposable implements ISelectionService { // Incremenet the end index so it is at the start of the next character endIndex++; - // Calculate the start _column_, converting the the string indexes back to - // column coordinates. + /* + * Calculate the start _column_, converting the the string indexes back to + * column coordinates. + */ let start = startIndex // The index of the selection's start char in the line string + charOffset // The difference between the initial char's column and index - leftWideCharCount // The number of wide chars left of the initial char + leftLongCharOffset; // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis) - // Calculate the length in _columns_, converting the the string indexes back - // to column coordinates. + /* + * Calculate the length in _columns_, converting the the string indexes back + * to column coordinates. + */ let length = Math.min(this._bufferService.cols, // Disallow lengths larger than the terminal cols endIndex // The index of the selection's end char in the line string - startIndex // The index of the selection's start char in the line string @@ -1002,8 +1044,10 @@ export class SelectionService extends Disposable implements ISelectionService { endRow--; } - // Adjust wrapped length value, this only needs to happen when values are reversed as in that - // case we're interested in the start of the word, not the end + /* + * Adjust wrapped length value, this only needs to happen when values are reversed as in that + * case we're interested in the start of the word, not the end + */ if (!this._model.areSelectionValuesReversed()) { while (wordPosition.start + wordPosition.length > this._bufferService.cols) { wordPosition.length -= this._bufferService.cols; @@ -1021,8 +1065,10 @@ export class SelectionService extends Disposable implements ISelectionService { * @param cell The cell to check. */ private _isCharWordSeparator(cell: ICellData): boolean { - // Zero width characters are never separators as they are always to the - // right of wide characters + /* + * Zero width characters are never separators as they are always to the + * right of wide characters + */ if (cell.getWidth() === 0) { return false; } diff --git a/src/common/CircularList.ts b/src/common/CircularList.ts index 3663bfc17..9843a470c 100644 --- a/src/common/CircularList.ts +++ b/src/common/CircularList.ts @@ -52,8 +52,10 @@ export class CircularList extends Disposable implements ICircularList { return; } - // Reconstruct array, starting at index 0. Only transfer values from the - // indexes 0 to length. + /* + * Reconstruct array, starting at index 0. Only transfer values from the + * indexes 0 to length. + */ const newArray = new Array(newMaxLength); for (let i = 0; i < Math.min(newMaxLength, this.length); i++) { newArray[i] = this._array[this._getCyclicIndex(i)]; diff --git a/src/common/Color.ts b/src/common/Color.ts index b3d13fc99..eef573796 100644 --- a/src/common/Color.ts +++ b/src/common/Color.ts @@ -27,9 +27,11 @@ export namespace channels { } export function toRgba(r: number, g: number, b: number, a: number = 0xFF): number { - // Note: The aggregated number is RGBA32 (BE), thus needs to be converted to ABGR32 - // on LE systems, before it can be used for direct 32-bit buffer writes. - // >>> 0 forces an unsigned int + /* + * Note: The aggregated number is RGBA32 (BE), thus needs to be converted to ABGR32 + * on LE systems, before it can be used for direct 32-bit buffer writes. + * >>> 0 forces an unsigned int + */ return (r << 24 | g << 16 | b << 8 | a) >>> 0; } @@ -197,8 +199,10 @@ export namespace css { throw new Error('css.toColor: Unsupported css format'); } - // Validate the color using canvas fillStyle - // See https://html.spec.whatwg.org/multipage/canvas.html#fill-and-stroke-styles + /* + * Validate the color using canvas fillStyle + * See https://html.spec.whatwg.org/multipage/canvas.html#fill-and-stroke-styles + */ $ctx.fillStyle = $litmusColor; $ctx.fillStyle = css; if (typeof $ctx.fillStyle !== 'string') { @@ -213,9 +217,11 @@ export namespace css { throw new Error('css.toColor: Unsupported css format'); } - // Extract the color from the canvas' fillStyle property which exposes the color value in rgba() - // format - // See https://html.spec.whatwg.org/multipage/canvas.html#serialisation-of-a-color + /* + * Extract the color from the canvas' fillStyle property which exposes the color value in rgba() + * format + * See https://html.spec.whatwg.org/multipage/canvas.html#serialisation-of-a-color + */ return { rgba: channels.toRgba($r, $g, $b, $a), css @@ -321,8 +327,10 @@ export namespace rgba { } export function reduceLuminance(bgRgba: number, fgRgba: number, ratio: number): number { - // This is a naive but fast approach to reducing luminance as converting to - // HSL and back is expensive + /* + * This is a naive but fast approach to reducing luminance as converting to + * HSL and back is expensive + */ const bgR = (bgRgba >> 24) & 0xFF; const bgG = (bgRgba >> 16) & 0xFF; const bgB = (bgRgba >> 8) & 0xFF; @@ -341,8 +349,10 @@ export namespace rgba { } export function increaseLuminance(bgRgba: number, fgRgba: number, ratio: number): number { - // This is a naive but fast approach to increasing luminance as converting to - // HSL and back is expensive + /* + * This is a naive but fast approach to increasing luminance as converting to + * HSL and back is expensive + */ const bgR = (bgRgba >> 24) & 0xFF; const bgG = (bgRgba >> 16) & 0xFF; const bgB = (bgRgba >> 8) & 0xFF; diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index 9bd7faaf5..3e16fe0a2 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -176,8 +176,10 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal { x = Math.max(x, BufferServiceConstants.MINIMUM_COLS); y = Math.max(y, BufferServiceConstants.MINIMUM_ROWS); - // Flush pending writes before resize to avoid race conditions where async - // writes are processed with incorrect dimensions + /* + * Flush pending writes before resize to avoid race conditions where async + * writes are processed with incorrect dimensions + */ this._writeBuffer.flushSync(); this._bufferService.resize(x, y); diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index f9da81a25..cf41d5746 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -613,8 +613,10 @@ describe('InputHandler', () => { await inputHandler.parseP('a'.repeat(bufferService.cols + 9)); // line 1 and 2 for (let i = 3; i < bufferService.rows; ++i) await inputHandler.parseP('a'.repeat(bufferService.cols)); - // params[1] left and above with wrap - // confirm precondition that line 2 is wrapped + /* + * params[1] left and above with wrap + * confirm precondition that line 2 is wrapped + */ assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, true); bufferService.buffer.y = 2; bufferService.buffer.x = 40; @@ -628,8 +630,10 @@ describe('InputHandler', () => { await inputHandler.parseP('a'.repeat(bufferService.cols + 9)); // line 1 and 2 for (let i = 3; i < bufferService.rows; ++i) await inputHandler.parseP('a'.repeat(bufferService.cols)); - // params[1] left and above with wrap - // confirm precondition that line 2 is wrapped + /* + * params[1] left and above with wrap + * confirm precondition that line 2 is wrapped + */ assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, true); bufferService.buffer.y = 1; bufferService.buffer.x = 90; // Cursor is beyond last column diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 2b7142dd0..1fac29d91 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -33,10 +33,12 @@ const GLEVEL: { [key: string]: number } = { '(': 0, ')': 1, '*': 2, '+': 3, '-': /** * Document xterm VT features here that are currently unsupported */ -// @vt: #N DCS DECUDK "User Defined Keys" "DCS Ps ; Ps \| Pt ST" "Definitions for user-defined keys." -// @vt: #N DCS XTGETTCAP "Request Terminfo String" "DCS + q Pt ST" "Request Terminfo String." -// @vt: #N DCS XTSETTCAP "Set Terminfo Data" "DCS + p Pt ST" "Set Terminfo Data." -// @vt: #N OSC 1 "Set Icon Name" "OSC 1 ; Pt BEL" "Set icon name." +/* + * @vt: #N DCS DECUDK "User Defined Keys" "DCS Ps ; Ps \| Pt ST" "Definitions for user-defined keys." + * @vt: #N DCS XTGETTCAP "Request Terminfo String" "DCS + q Pt ST" "Request Terminfo String." + * @vt: #N DCS XTSETTCAP "Set Terminfo Data" "DCS + p Pt ST" "Set Terminfo Data." + * @vt: #N OSC 1 "Set Icon Name" "OSC 1 ; Pt BEL" "Set icon name." + */ /** * Max length of the UTF32 input buffer. Real memory consumption is 4 times higher. @@ -288,13 +290,17 @@ export class InputHandler extends Disposable implements IInputHandler { this._parser.registerOscHandler(1, new OscHandler(data => this.setIconName(data))); // 2 - title this._parser.registerOscHandler(2, new OscHandler(data => this.setTitle(data))); - // 3 - set property X in the form "prop=value" - // 4 - Change Color Number + /* + * 3 - set property X in the form "prop=value" + * 4 - Change Color Number + */ this._parser.registerOscHandler(4, new OscHandler(data => this.setOrReportIndexedColor(data))); - // 5 - Change Special Color Number - // 6 - Enable/disable Special Color Number c - // 7 - current directory? (not in xterm spec, see https://gitlab.com/gnachman/iterm2/issues/3939) - // 8 - create hyperlink (not in xterm spec, see https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda) + /* + * 5 - Change Special Color Number + * 6 - Enable/disable Special Color Number c + * 7 - current directory? (not in xterm spec, see https://gitlab.com/gnachman/iterm2/issues/3939) + * 8 - create hyperlink (not in xterm spec, see https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda) + */ this._parser.registerOscHandler(8, new OscHandler(data => this.setHyperlink(data))); // 10 - Change VT100 text foreground color to Pt. this._parser.registerOscHandler(10, new OscHandler(data => this.setOrReportFgColor(data))); @@ -302,34 +308,40 @@ export class InputHandler extends Disposable implements IInputHandler { this._parser.registerOscHandler(11, new OscHandler(data => this.setOrReportBgColor(data))); // 12 - Change text cursor color to Pt. this._parser.registerOscHandler(12, new OscHandler(data => this.setOrReportCursorColor(data))); - // 13 - Change mouse foreground color to Pt. - // 14 - Change mouse background color to Pt. - // 15 - Change Tektronix foreground color to Pt. - // 16 - Change Tektronix background color to Pt. - // 17 - Change highlight background color to Pt. - // 18 - Change Tektronix cursor color to Pt. - // 19 - Change highlight foreground color to Pt. - // 46 - Change Log File to Pt. - // 50 - Set Font to Pt. - // 51 - reserved for Emacs shell. - // 52 - Manipulate Selection Data. - // 104 ; c - Reset Color Number c. + /* + * 13 - Change mouse foreground color to Pt. + * 14 - Change mouse background color to Pt. + * 15 - Change Tektronix foreground color to Pt. + * 16 - Change Tektronix background color to Pt. + * 17 - Change highlight background color to Pt. + * 18 - Change Tektronix cursor color to Pt. + * 19 - Change highlight foreground color to Pt. + * 46 - Change Log File to Pt. + * 50 - Set Font to Pt. + * 51 - reserved for Emacs shell. + * 52 - Manipulate Selection Data. + * 104 ; c - Reset Color Number c. + */ this._parser.registerOscHandler(104, new OscHandler(data => this.restoreIndexedColor(data))); - // 105 ; c - Reset Special Color Number c. - // 106 ; c; f - Enable/disable Special Color Number c. - // 110 - Reset VT100 text foreground color. + /* + * 105 ; c - Reset Special Color Number c. + * 106 ; c; f - Enable/disable Special Color Number c. + * 110 - Reset VT100 text foreground color. + */ this._parser.registerOscHandler(110, new OscHandler(data => this.restoreFgColor(data))); // 111 - Reset VT100 text background color. this._parser.registerOscHandler(111, new OscHandler(data => this.restoreBgColor(data))); // 112 - Reset text cursor color. this._parser.registerOscHandler(112, new OscHandler(data => this.restoreCursorColor(data))); - // 113 - Reset mouse foreground color. - // 114 - Reset mouse background color. - // 115 - Reset Tektronix foreground color. - // 116 - Reset Tektronix background color. - // 117 - Reset highlight color. - // 118 - Reset Tektronix cursor color. - // 119 - Reset highlight foreground color. + /* + * 113 - Reset mouse foreground color. + * 114 - Reset mouse background color. + * 115 - Reset Tektronix foreground color. + * 116 - Reset Tektronix background color. + * 117 - Reset highlight color. + * 118 - Reset Tektronix cursor color. + * 119 - Reset highlight foreground color. + */ /** * ESC handlers @@ -466,8 +478,10 @@ export class InputHandler extends Disposable implements IInputHandler { } } - // Clear the dirty row service so we know which lines changed as a result of parsing - // Important: do not clear between async calls, otherwise we lost pending update information. + /* + * Clear the dirty row service so we know which lines changed as a result of parsing + * Important: do not clear between async calls, otherwise we lost pending update information. + */ if (!wasPaused) { this._dirtyRowTracker.clearRange(); } @@ -502,8 +516,10 @@ export class InputHandler extends Disposable implements IInputHandler { this._onCursorMove.fire(); } - // Refresh any dirty rows accumulated as part of parsing, fire only for rows within the - // _viewport_ which is relative to ydisp, not relative to ybase. + /* + * Refresh any dirty rows accumulated as part of parsing, fire only for rows within the + * _viewport_ which is relative to ydisp, not relative to ybase. + */ const viewportEnd = this._dirtyRowTracker.end + (this._bufferService.buffer.ybase - this._bufferService.buffer.ydisp); const viewportStart = this._dirtyRowTracker.start + (this._bufferService.buffer.ybase - this._bufferService.buffer.ydisp); if (viewportStart < this._bufferService.rows) { @@ -525,8 +541,10 @@ export class InputHandler extends Disposable implements IInputHandler { const curAttr = this._curAttrData; let bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y); - // Defensive check: bufferRow can be undefined if a resize occurred mid-write due to async - // scheduling gaps in WriteBuffer. See https://github.com/xtermjs/xterm.js/issues/5597 + /* + * Defensive check: bufferRow can be undefined if a resize occurred mid-write due to async + * scheduling gaps in WriteBuffer. See https://github.com/xtermjs/xterm.js/issues/5597 + */ if (!bufferRow) { return; } @@ -542,15 +560,19 @@ export class InputHandler extends Disposable implements IInputHandler { for (let pos = start; pos < end; ++pos) { code = data[pos]; - // Soft hyphen's (U+00AD) behavior is ambiguous and differs across terminals. We opt to treat - // it as a zero-width hint to text layout engines and simply ignore it. + /* + * Soft hyphen's (U+00AD) behavior is ambiguous and differs across terminals. We opt to treat + * it as a zero-width hint to text layout engines and simply ignore it. + */ if (code === 0xAD) { continue; } - // get charset replacement character - // charset is only defined for ASCII, therefore we only - // search for an replacement char if code < 127 + /* + * get charset replacement character + * charset is only defined for ASCII, therefore we only + * search for an replacement char if code < 127 + */ if (code < 127 && charset) { const ch = charset[String.fromCharCode(code)]; if (ch) { @@ -571,12 +593,16 @@ export class InputHandler extends Disposable implements IInputHandler { this._oscLinkService.addLineToLink(this._getCurrentLinkId(), this._activeBuffer.ybase + this._activeBuffer.y); } - // goto next line if ch would overflow - // NOTE: To avoid costly width checks here, - // the terminal does not allow a cols < 2. + /* + * goto next line if ch would overflow + * NOTE: To avoid costly width checks here, + * the terminal does not allow a cols < 2. + */ if (this._activeBuffer.x + chWidth - oldWidth > cols) { - // autowrap - DECAWM - // automatically wraps to the beginning of the next line + /* + * autowrap - DECAWM + * automatically wraps to the beginning of the next line + */ if (wraparoundMode) { const oldRow = bufferRow; let oldCol = this._activeBuffer.x - oldWidth; @@ -589,8 +615,10 @@ export class InputHandler extends Disposable implements IInputHandler { if (this._activeBuffer.y >= this._bufferService.rows) { this._activeBuffer.y = this._bufferService.rows - 1; } - // The line already exists (eg. the initial viewport), mark it as a - // wrapped line + /* + * The line already exists (eg. the initial viewport), mark it as a + * wrapped line + */ this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = true; } // row changed, get it again @@ -599,8 +627,10 @@ export class InputHandler extends Disposable implements IInputHandler { return; } if (oldWidth > 0 && bufferRow instanceof BufferLine) { - // Combining character widens 1 column to 2. - // Move old character to next line. + /* + * Combining character widens 1 column to 2. + * Move old character to next line. + */ bufferRow.copyCellsFrom(oldRow as BufferLine, oldCol, 0, oldWidth, false); } @@ -611,22 +641,28 @@ export class InputHandler extends Disposable implements IInputHandler { } else { this._activeBuffer.x = cols - 1; if (chWidth === 2) { - // FIXME: check for xterm behavior - // What to do here? We got a wide char that does not fit into last cell + /* + * FIXME: check for xterm behavior + * What to do here? We got a wide char that does not fit into last cell + */ continue; } } } - // insert combining char at last cursor position - // this._activeBuffer.x should never be 0 for a combining char - // since they always follow a cell consuming char - // therefore we can test for this._activeBuffer.x to avoid overflow left + /* + * insert combining char at last cursor position + * this._activeBuffer.x should never be 0 for a combining char + * since they always follow a cell consuming char + * therefore we can test for this._activeBuffer.x to avoid overflow left + */ if (shouldJoin && this._activeBuffer.x) { const offset = bufferRow.getWidth(this._activeBuffer.x - 1) ? 1 : 2; - // if empty cell after fullwidth, need to go 2 cells back - // it is save to step 2 cells back here - // since an empty cell is only set by fullwidth chars + /* + * if empty cell after fullwidth, need to go 2 cells back + * it is save to step 2 cells back here + * since an empty cell is only set by fullwidth chars + */ bufferRow.addCodepointToCell(this._activeBuffer.x - offset, code, chWidth); for (let delta = chWidth - oldWidth; --delta >= 0;) { @@ -639,9 +675,11 @@ export class InputHandler extends Disposable implements IInputHandler { if (insertMode) { // right shift cells according to the width bufferRow.insertCells(this._activeBuffer.x, chWidth - oldWidth, this._activeBuffer.getNullCell(curAttr)); - // test last cell - since the last cell has only room for - // a halfwidth char any fullwidth shifted there is lost - // and will be set to empty cell + /* + * test last cell - since the last cell has only room for + * a halfwidth char any fullwidth shifted there is lost + * and will be set to empty cell + */ if (bufferRow.getWidth(cols - 1) === 2) { bufferRow.setCellFromCodepoint(cols - 1, NULL_CELL_CODE, NULL_CELL_WIDTH, curAttr); } @@ -650,9 +688,11 @@ export class InputHandler extends Disposable implements IInputHandler { // write current char to buffer and advance cursor bufferRow.setCellFromCodepoint(this._activeBuffer.x++, code, chWidth, curAttr); - // fullwidth char - also set next cell to placeholder stub and advance cursor - // for graphemes bigger than fullwidth we can simply loop to zero - // we already made sure above, that this._activeBuffer.x + chWidth will not overflow right + /* + * fullwidth char - also set next cell to placeholder stub and advance cursor + * for graphemes bigger than fullwidth we can simply loop to zero + * we already made sure above, that this._activeBuffer.x + chWidth will not overflow right + */ if (chWidth > 0) { while (--chWidth) { // other than a regular empty cell a cell following a wide char has no width @@ -750,11 +790,13 @@ export class InputHandler extends Disposable implements IInputHandler { } 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. + /* + * 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. @@ -799,9 +841,11 @@ export class InputHandler extends Disposable implements IInputHandler { return true; } - // reverse wrap-around is enabled - // other than for normal operation mode, reverse wrap-around allows the cursor - // to be at x=cols to be able to address the last cell of a row by BS + /* + * reverse wrap-around is enabled + * other than for normal operation mode, reverse wrap-around allows the cursor + * to be at x=cols to be able to address the last cell of a row by BS + */ this._restrictCursor(this._bufferService.cols); if (this._activeBuffer.x > 0) { @@ -822,17 +866,21 @@ export class InputHandler extends Disposable implements IInputHandler { this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = false; this._activeBuffer.y--; this._activeBuffer.x = this._bufferService.cols - 1; - // find last taken cell - last cell can have 3 different states: - // - hasContent(true) + hasWidth(1): narrow char - we are done - // - hasWidth(0): second part of wide char - we are done - // - hasContent(false) + hasWidth(1): empty cell due to early wrapping wide char, go one - // cell further back + /* + * find last taken cell - last cell can have 3 different states: + * - hasContent(true) + hasWidth(1): narrow char - we are done + * - hasWidth(0): second part of wide char - we are done + * - hasContent(false) + hasWidth(1): empty cell due to early wrapping wide char, go one + * cell further back + */ const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!; if (line.hasWidth(this._activeBuffer.x) && !line.hasContent(this._activeBuffer.x)) { this._activeBuffer.x--; - // We do this only once, since width=1 + hasContent=false currently happens only once - // before early wrapping of a wide char. - // This needs to be fixed once we support graphemes taking more than 2 cells. + /* + * We do this only once, since width=1 + hasContent=false currently happens only once + * before early wrapping of a wide char. + * This needs to be fixed once we support graphemes taking more than 2 cells. + */ } } } @@ -913,8 +961,10 @@ export class InputHandler extends Disposable implements IInputHandler { * Set relative cursor position. */ private _moveCursor(x: number, y: number): void { - // for relative changes we have to make sure we are within 0 .. cols/rows - 1 - // before calculating the new position + /* + * for relative changes we have to make sure we are within 0 .. cols/rows - 1 + * before calculating the new position + */ this._restrictCursor(); this._setCursor(this._activeBuffer.x + x, this._activeBuffer.y + y); } @@ -1354,8 +1404,10 @@ export class InputHandler extends Disposable implements IInputHandler { const scrollBottomRowsOffset = this._bufferService.rows - 1 - this._activeBuffer.scrollBottom; const scrollBottomAbsolute = this._bufferService.rows - 1 + this._activeBuffer.ybase - scrollBottomRowsOffset + 1; while (param--) { - // test: echo -e '\e[44m\e[1L\e[0m' - // blankLine(true) - xterm/linux behavior + /* + * test: echo -e '\e[44m\e[1L\e[0m' + * blankLine(true) - xterm/linux behavior + */ this._activeBuffer.lines.splice(scrollBottomAbsolute - 1, 1); this._activeBuffer.lines.splice(row, 0, this._activeBuffer.getBlankLine(this._eraseAttrData())); } @@ -1388,8 +1440,10 @@ export class InputHandler extends Disposable implements IInputHandler { j = this._bufferService.rows - 1 - this._activeBuffer.scrollBottom; j = this._bufferService.rows - 1 + this._activeBuffer.ybase - j; while (param--) { - // test: echo -e '\e[44m\e[1M\e[0m' - // blankLine(true) - xterm/linux behavior + /* + * test: echo -e '\e[44m\e[1M\e[0m' + * blankLine(true) - xterm/linux behavior + */ this._activeBuffer.lines.splice(row, 1); this._activeBuffer.lines.splice(j, 0, this._activeBuffer.getBlankLine(this._eraseAttrData())); } @@ -1737,16 +1791,20 @@ export class InputHandler extends Disposable implements IInputHandler { if (params.params[0] > 0) { return true; } - // xterm and urxvt - // seem to spit this - // out around ~370 times (?). + /* + * xterm and urxvt + * seem to spit this + * out around ~370 times (?). + */ if (this._is('xterm')) { this._coreService.triggerDataEvent(C0.ESC + '[>0;276;0c'); } else if (this._is('rxvt-unicode')) { this._coreService.triggerDataEvent(C0.ESC + '[>85;95;0c'); } else if (this._is('linux')) { - // not supported by linux console. - // linux console echoes parameters. + /* + * not supported by linux console. + * linux console echoes parameters. + */ this._coreService.triggerDataEvent(params.params[0] + 'c'); } else if (this._is('screen')) { this._coreService.triggerDataEvent(C0.ESC + '[>83;40003;0c'); @@ -1979,13 +2037,17 @@ export class InputHandler extends Disposable implements IInputHandler { this._mouseStateService.activeProtocol = 'DRAG'; break; case 1003: // any event mouse - // any event - sends motion events, - // even if there is no button held down. + /* + * any event - sends motion events, + * even if there is no button held down. + */ this._mouseStateService.activeProtocol = 'ANY'; break; case 1004: // send focusin/focusout events - // focusin: ^[[I - // focusout: ^[[O + /* + * focusin: ^[[I + * focusout: ^[[O + */ this._coreService.decPrivateModes.sendFocus = true; this._onRequestSendFocus.fire(); break; @@ -2408,10 +2470,12 @@ export class InputHandler extends Disposable implements IInputHandler { * Returns advance for params index. */ private _extractColor(params: IParams, pos: number, attr: IAttributeData): number { - // normalize params - // meaning: [target, CM, ign, val, val, val] - // RGB : [ 38/48, 2, ign, r, g, b] - // P256 : [ 38/48, 5, ign, v, ign, ign] + /* + * normalize params + * meaning: [target, CM, ign, val, val, val] + * RGB : [ 38/48, 2, ign, r, g, b] + * P256 : [ 38/48, 5, ign, v, ign, ign] + */ const accu = [0, 0, -1, 0, 0, 0]; // alignment placeholder for non color space sequences @@ -2477,8 +2541,10 @@ export class InputHandler extends Disposable implements IInputHandler { * 4:5 - dashed underline */ private _processUnderline(style: number, attr: IAttributeData): void { - // treat extended attrs as immutable, thus always clone from old one - // this is needed since the buffer only holds references to it + /* + * treat extended attrs as immutable, thus always clone from old one + * this is needed since the buffer only holds references to it + */ attr.extended = attr.extended.clone(); // default to 1 == single underline @@ -2501,8 +2567,10 @@ export class InputHandler extends Disposable implements IInputHandler { attr.fg = DEFAULT_ATTR_DATA.fg; attr.bg = DEFAULT_ATTR_DATA.bg; attr.extended = attr.extended.clone(); - // Reset underline style and color. Note that we don't want to reset other - // fields such as the url id. + /* + * Reset underline style and color. Note that we don't want to reset other + * fields such as the url id. + */ attr.extended.underlineStyle = UnderlineStyle.NONE; attr.extended.underlineColor &= ~(Attributes.CM_MASK | Attributes.RGB_MASK); attr.updateExtended(); @@ -2641,8 +2709,10 @@ export class InputHandler extends Disposable implements IInputHandler { // blink attr.fg |= FgFlags.BLINK; } else if (p === 7) { - // inverse and positive - // test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m' + /* + * inverse and positive + * test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m' + */ attr.fg |= FgFlags.INVERSE; } else if (p === 8) { // invisible @@ -2756,8 +2826,10 @@ export class InputHandler extends Disposable implements IInputHandler { // @vt: #P[Only CPR is supported.] CSI DECDSR "DEC Device Status Report" "CSI ? Ps n" "Only CPR is supported (same as DSR)." public deviceStatusPrivate(params: IParams): boolean { - // modern xterm doesnt seem to - // respond to any of these except ?6, 6, and 5 + /* + * modern xterm doesnt seem to + * respond to any of these except ?6, 6, and 5 + */ switch (params.params[0]) { case 6: // cursor position @@ -2766,20 +2838,28 @@ export class InputHandler extends Disposable implements IInputHandler { this._coreService.triggerDataEvent(`${C0.ESC}[?${y};${x}R`); break; case 15: - // no printer - // this.handler(C0.ESC + '[?11n'); + /* + * no printer + * this.handler(C0.ESC + '[?11n'); + */ break; case 25: - // dont support user defined keys - // this.handler(C0.ESC + '[?21n'); + /* + * dont support user defined keys + * this.handler(C0.ESC + '[?21n'); + */ break; case 26: - // north american keyboard - // this.handler(C0.ESC + '[?27;1;0;0n'); + /* + * north american keyboard + * this.handler(C0.ESC + '[?27;1;0;0n'); + */ break; case 53: - // no dec locator/mouse - // this.handler(C0.ESC + '[?50n'); + /* + * no dec locator/mouse + * this.handler(C0.ESC + '[?50n'); + */ break; case 996: // color scheme query (https://contour-terminal.org/vt-extensions/color-palette-update-notifications/) @@ -3401,9 +3481,11 @@ export class InputHandler extends Disposable implements IInputHandler { public reverseIndex(): boolean { this._restrictCursor(); if (this._activeBuffer.y === this._activeBuffer.scrollTop) { - // possibly move the code below to term.reverseScroll(); - // test: echo -ne '\e[1;1H\e[44m\eM\e[0m' - // blankLine(true) is xterm/linux behavior + /* + * possibly move the code below to term.reverseScroll(); + * test: echo -ne '\e[1;1H\e[44m\eM\e[0m' + * blankLine(true) is xterm/linux behavior + */ const scrollRegionHeight = this._activeBuffer.scrollBottom - this._activeBuffer.scrollTop; this._activeBuffer.lines.shiftElements(this._activeBuffer.ybase + this._activeBuffer.y, scrollRegionHeight, 1); this._activeBuffer.lines.set(this._activeBuffer.ybase + this._activeBuffer.y, this._activeBuffer.getBlankLine(this._eraseAttrData())); diff --git a/src/common/Platform.ts b/src/common/Platform.ts index 09ba097e6..ad2d1424c 100644 --- a/src/common/Platform.ts +++ b/src/common/Platform.ts @@ -9,14 +9,18 @@ interface INavigator { platform: string; } -// We're declaring a navigator global here as we expect it in all runtimes (node and browser), but -// we want this module to live in common. +/* + * We're declaring a navigator global here as we expect it in all runtimes (node and browser), but + * we want this module to live in common. + */ declare const navigator: INavigator; declare const process: unknown; -// navigator.userAgent is also checked here because bundling with the process module can cause -// issues otherwise. Note that navigator exists in Node.js 21+ but the userAgent is -// "Node.js/". +/* + * navigator.userAgent is also checked here because bundling with the process module can cause + * issues otherwise. Note that navigator exists in Node.js 21+ but the userAgent is + * "Node.js/". + */ export const isNode = (typeof process !== 'undefined' && 'title' in (process as any) && (typeof navigator === 'undefined' || navigator.userAgent.startsWith('Node.js/'))) ? true : false; const userAgent = (isNode) ? 'node' : navigator.userAgent; const platform = (isNode) ? 'node' : navigator.platform; @@ -44,9 +48,11 @@ export function getSafariVersion(): number { return parseInt(majorVersion[1]); } -// Find the users platform. We use this to interpret the meta key -// and ISO third level shifts. -// http://stackoverflow.com/q/19877924/577598 +/* + * Find the users platform. We use this to interpret the meta key + * and ISO third level shifts. + * http://stackoverflow.com/q/19877924/577598 + */ export const isMac = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'].includes(platform); export const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].includes(platform); export const isLinux = platform.indexOf('Linux') >= 0; diff --git a/src/common/SortedList.ts b/src/common/SortedList.ts index c6dc62085..8ae39d3d8 100644 --- a/src/common/SortedList.ts +++ b/src/common/SortedList.ts @@ -191,8 +191,10 @@ export class SortedList { return mid; } } - // key not in list - // still return closest min (also used as insert position) + /* + * key not in list + * still return closest min (also used as insert position) + */ return min; } } diff --git a/src/common/TaskQueue.ts b/src/common/TaskQueue.ts index 3cad0a140..ae9041d3a 100644 --- a/src/common/TaskQueue.ts +++ b/src/common/TaskQueue.ts @@ -83,17 +83,23 @@ abstract class TaskQueue implements ITaskQueue { if (!this._tasks[this._i]()) { this._i++; } - // other than performance.now, performance.now might not be stable (changes on wall clock - // changes), this is not an issue here as a clock change during a short running task is very - // unlikely in case it still happened and leads to negative duration, simply assume 1 msec + /* + * other than performance.now, performance.now might not be stable (changes on wall clock + * changes), this is not an issue here as a clock change during a short running task is very + * unlikely in case it still happened and leads to negative duration, simply assume 1 msec + */ taskDuration = Math.max(1, performance.now() - taskDuration); longestTask = Math.max(taskDuration, longestTask); - // Guess the following task will take a similar time to the longest task in this batch, allow - // additional room to try avoid exceeding the deadline + /* + * Guess the following task will take a similar time to the longest task in this batch, allow + * additional room to try avoid exceeding the deadline + */ deadlineRemaining = deadline.timeRemaining(); if (longestTask * 1.5 > deadlineRemaining) { - // Warn when the time exceeding the deadline is over 20ms, if this happens in practice the - // task should be split into sub-tasks to ensure the UI remains responsive. + /* + * Warn when the time exceeding the deadline is over 20ms, if this happens in practice the + * task should be split into sub-tasks to ensure the UI remains responsive. + */ if (lastDeadlineRemaining - taskDuration < -20) { this._logService.warn(`task queue exceeded allotted deadline by ${Math.abs(Math.round(lastDeadlineRemaining - taskDuration))}ms`); } diff --git a/src/common/Types.ts b/src/common/Types.ts index 5edf63637..fb95f5fe7 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -310,8 +310,10 @@ export const enum CoreMouseButton { RIGHT = 2, NONE = 3, WHEEL = 4, - // additional buttons 1..8 - // untested! + /* + * additional buttons 1..8 + * untested! + */ AUX1 = 8, AUX2 = 9, AUX3 = 10, @@ -528,8 +530,10 @@ export interface IInputHandler { /** CSI ' } */ insertColumns(params: IParams): boolean; /** CSI ' ~ */ deleteColumns(params: IParams): boolean; - /** OSC 0 - OSC 2 */ setTitle(data: string): boolean; + /** + * OSC 0 + * OSC 2 + */ setTitle(data: string): boolean; /** OSC 4 */ setOrReportIndexedColor(data: string): boolean; /** OSC 10 */ setOrReportFgColor(data: string): boolean; /** OSC 11 */ setOrReportBgColor(data: string): boolean; @@ -542,24 +546,30 @@ export interface IInputHandler { /** ESC E */ nextLine(): boolean; /** ESC = */ keypadApplicationMode(): boolean; /** ESC > */ keypadNumericMode(): boolean; - /** ESC % G - ESC % @ */ selectDefaultCharset(): boolean; - /** ESC ( C - ESC ) C - ESC * C - ESC + C - ESC - C - ESC . C - ESC / C */ selectCharset(collectAndFlag: string): boolean; + /** + * ESC % G + * ESC % @ + */ selectDefaultCharset(): boolean; + /** + * ESC ( C + * ESC ) C + * ESC * C + * ESC + C + * ESC - C + * ESC . C + * ESC / C + */ selectCharset(collectAndFlag: string): boolean; /** ESC D */ index(): boolean; /** ESC H */ tabSet(): boolean; /** ESC M */ reverseIndex(): boolean; /** ESC c */ fullReset(): boolean; - /** ESC n - ESC o - ESC | - ESC } - ESC ~ */ setgLevel(level: number): boolean; + /** + * ESC n + * ESC o + * ESC | + * ESC } + * ESC ~ + */ setgLevel(level: number): boolean; /** ESC # 8 */ screenAlignmentPattern(): boolean; } diff --git a/src/common/WindowsMode.ts b/src/common/WindowsMode.ts index 7cff094b2..f59343286 100644 --- a/src/common/WindowsMode.ts +++ b/src/common/WindowsMode.ts @@ -7,16 +7,18 @@ import { CHAR_DATA_CODE_INDEX, NULL_CELL_CODE, WHITESPACE_CELL_CODE } from 'comm import { IBufferService } from 'common/services/Services'; export function updateWindowsModeWrappedState(bufferService: IBufferService): void { - // Winpty does not support wraparound mode which means that lines will never - // be marked as wrapped. This causes issues for things like copying a line - // retaining the wrapped new line characters or if consumers are listening - // in on the data stream. - // - // The workaround for this is to listen to every incoming line feed and mark - // the line as wrapped if the last character in the previous line is not a - // space. This is certainly not without its problems, but generally on - // Windows when text reaches the end of the terminal it's likely going to be - // wrapped. + /* + * Winpty does not support wraparound mode which means that lines will never + * be marked as wrapped. This causes issues for things like copying a line + * retaining the wrapped new line characters or if consumers are listening + * in on the data stream. + * + * The workaround for this is to listen to every incoming line feed and mark + * the line as wrapped if the last character in the previous line is not a + * space. This is certainly not without its problems, but generally on + * Windows when text reaches the end of the terminal it's likely going to be + * wrapped. + */ const line = bufferService.buffer.lines.get(bufferService.buffer.ybase + bufferService.buffer.y - 1); const lastChar = line?.get(bufferService.cols - 1); diff --git a/src/common/buffer/Buffer.test.ts b/src/common/buffer/Buffer.test.ts index abfea7b06..f26807a94 100644 --- a/src/common/buffer/Buffer.test.ts +++ b/src/common/buffer/Buffer.test.ts @@ -348,8 +348,10 @@ describe('Buffer', () => { assert.equal(buffer.lines.get(4)!.translateToString(), ' '); }); it('should remove the correct amount of rows when reflowing larger', () => { - // This is a regression test to ensure that successive wrapped lines that are getting - // 3+ lines removed on a reflow actually remove the right lines + /* + * This is a regression test to ensure that successive wrapped lines that are getting + * 3+ lines removed on a reflow actually remove the right lines + */ buffer.fillViewportRows(); buffer.resize(10, 10); buffer.y = 2; @@ -429,10 +431,12 @@ describe('Buffer', () => { buffer.lines.get(2)!.set(i, [0, char, 1, code]); } buffer.y = 3; - // Buffer: - // abcdefghij - // 0123456789 - // abcdefghij + /* + * Buffer: + * abcdefghij + * 0123456789 + * abcdefghij + */ const firstMarker = buffer.addMarker(0); const secondMarker = buffer.addMarker(1); const thirdMarker = buffer.addMarker(2); @@ -492,10 +496,12 @@ describe('Buffer', () => { buffer.lines.get(2)!.set(i, [0, char, 1, code]); } buffer.y = 10; - // Buffer: - // abcdefghij - // 0123456789 - // abcdefghij + /* + * Buffer: + * abcdefghij + * 0123456789 + * abcdefghij + */ const firstMarker = buffer.addMarker(0); const secondMarker = buffer.addMarker(1); const thirdMarker = buffer.addMarker(2); @@ -539,9 +545,11 @@ describe('Buffer', () => { buffer.lines.get(1)!.set(0, [0, 'c', 1, 'c'.charCodeAt(0)]); buffer.lines.get(1)!.set(1, [0, 'd', 1, 'd'.charCodeAt(0)]); buffer.lines.get(1)!.isWrapped = true; - // Buffer: - // "ab " (wrapped) - // "cd" + /* + * Buffer: + * "ab " (wrapped) + * "cd" + */ buffer.resize(5, 10); assert.equal(buffer.ybase, 0); assert.equal(buffer.lines.length, 10); @@ -570,9 +578,11 @@ describe('Buffer', () => { buffer.lines.get(1)!.set(i, [0, '', 0, 0]); } buffer.lines.get(1)!.isWrapped = true; - // Buffer: - // 汉语汉语汉语 (wrapped) - // 汉语汉语汉语 + /* + * Buffer: + * 汉语汉语汉语 (wrapped) + * 汉语汉语汉语 + */ assert.equal(buffer.lines.get(0)!.translateToString(true), '汉语汉语汉语'); assert.equal(buffer.lines.get(1)!.translateToString(true), '汉语汉语汉语'); buffer.resize(13, 10); @@ -597,9 +607,11 @@ describe('Buffer', () => { buffer.lines.get(1)!.set(0, [0, 'c', 1, 'c'.charCodeAt(0)]); buffer.lines.get(1)!.set(1, [0, 'd', 1, 'd'.charCodeAt(0)]); buffer.lines.get(1)!.isWrapped = true; - // Buffer: - // "ab " (wrapped) - // "cd" + /* + * Buffer: + * "ab " (wrapped) + * "cd" + */ buffer.resize(3, 10); assert.equal(buffer.y, 2); assert.equal(buffer.ybase, 0); @@ -631,9 +643,11 @@ describe('Buffer', () => { buffer.lines.get(1)!.set(i, [0, '', 0, 0]); } buffer.lines.get(1)!.isWrapped = true; - // Buffer: - // 汉语汉语汉语 (wrapped) - // 汉语汉语汉语 + /* + * Buffer: + * 汉语汉语汉语 (wrapped) + * 汉语汉语汉语 + */ assert.equal(buffer.lines.get(0)!.translateToString(true), '汉语汉语汉语'); assert.equal(buffer.lines.get(1)!.translateToString(true), '汉语汉语汉语'); buffer.resize(11, 10); @@ -668,17 +682,19 @@ describe('Buffer', () => { describe('reflowLarger cases', () => { beforeEach(() => { - // Setup buffer state: - // 'ab' - // 'cd' (wrapped) - // 'ef' - // 'gh' (wrapped) - // 'ij' - // 'kl' (wrapped) - // ' ' - // ' ' - // ' ' - // ' ' + /* + * Setup buffer state: + * 'ab' + * 'cd' (wrapped) + * 'ef' + * 'gh' (wrapped) + * 'ij' + * 'kl' (wrapped) + * ' ' + * ' ' + * ' ' + * ' ' + */ buffer.fillViewportRows(); buffer.resize(2, 10); buffer.lines.get(0)!.set(0, [0, 'a', 1, 'a'.charCodeAt(0)]); @@ -859,17 +875,19 @@ describe('Buffer', () => { }); describe('reflowSmaller cases', () => { beforeEach(() => { - // Setup buffer state: - // 'abcd' - // 'efgh' (wrapped) - // 'ijkl' - // ' ' - // ' ' - // ' ' - // ' ' - // ' ' - // ' ' - // ' ' + /* + * Setup buffer state: + * 'abcd' + * 'efgh' (wrapped) + * 'ijkl' + * ' ' + * ' ' + * ' ' + * ' ' + * ' ' + * ' ' + * ' ' + */ buffer.fillViewportRows(); buffer.resize(4, 10); buffer.lines.get(0)!.set(0, [0, 'a', 1, 'a'.charCodeAt(0)]); @@ -1301,8 +1319,10 @@ describe('Buffer', () => { assert.equal(line.length, INIT_COLS / 2 - 1); } - // wait for a bit to give IdleTaskQueue a chance to kick in - // and finish memory cleaning + /* + * wait for a bit to give IdleTaskQueue a chance to kick in + * and finish memory cleaning + */ await new Promise(r => setTimeout(r, 30)); // cleanup should have realigned memory with exact bytelength diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts index 34ef02f6a..a572418c4 100644 --- a/src/common/buffer/Buffer.ts +++ b/src/common/buffer/Buffer.ts @@ -169,19 +169,25 @@ export class Buffer extends Disposable implements IBuffer { // count bufferlines with overly big memory to be cleaned afterwards let dirtyMemoryLines = 0; - // Increase max length if needed before adjustments to allow space to fill - // as required. + /* + * Increase max length if needed before adjustments to allow space to fill + * as required. + */ const newMaxLength = this._getCorrectBufferLength(newRows); if (newMaxLength > this.lines.maxLength) { this.lines.maxLength = newMaxLength; } - // if (this._cols > newCols) { - // console.log('increase!'); - // } + /* + * if (this._cols > newCols) { + * console.log('increase!'); + * } + */ - // The following adjustments should only happen if the buffer has been - // initialized/filled. + /* + * The following adjustments should only happen if the buffer has been + * initialized/filled. + */ if (this.lines.length > 0) { // Deal with columns increasing (reducing needs to happen after reflow) if (this._cols < newCols) { @@ -197,13 +203,17 @@ export class Buffer extends Disposable implements IBuffer { for (let y = this._rows; y < newRows; y++) { if (this.lines.length < newRows + this.ybase) { if (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 + /* + * 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(this._stringCache, newCols, nullCell, false)); } else { if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) { - // There is room above the buffer and there are no empty elements below the line, - // scroll up + /* + * There is room above the buffer and there are no empty elements below the line, + * scroll up + */ this.ybase--; addToY++; if (this.ydisp > 0) { @@ -211,8 +221,10 @@ export class Buffer extends Disposable implements IBuffer { this.ydisp--; } } else { - // Add a blank line if there is no buffer left at the top to scroll to, or if there - // are blank lines after the cursor + /* + * Add a blank line if there is no buffer left at the top to scroll to, or if there + * are blank lines after the cursor + */ this.lines.push(new BufferLine(this._stringCache, newCols, nullCell, false)); } } @@ -233,8 +245,10 @@ export class Buffer extends Disposable implements IBuffer { } } - // Reduce max length if needed after adjustments, this is done after as it - // would otherwise cut data from the bottom of the buffer. + /* + * Reduce max length if needed after adjustments, this is done after as it + * would otherwise cut data from the bottom of the buffer. + */ if (newMaxLength < this.lines.maxLength) { // Trim from the top of the buffer and adjust ybase and ydisp. const amountToTrim = this.lines.length - newMaxLength; @@ -275,8 +289,10 @@ export class Buffer extends Disposable implements IBuffer { this._cols = newCols; this._rows = newRows; - // Ensure the cursor position invariant: ybase + y must be within buffer bounds - // This can be violated during reflow or when shrinking rows + /* + * Ensure the cursor position invariant: ybase + y must be within buffer bounds + * This can be violated during reflow or when shrinking rows + */ if (this.lines.length > 0) { const maxY = Math.max(0, this.lines.length - this.ybase - 1); this.y = Math.min(this.y, maxY); @@ -293,8 +309,10 @@ export class Buffer extends Disposable implements IBuffer { private _batchedMemoryCleanup(): boolean { let normalRun = true; if (this._memoryCleanupPosition >= this.lines.length) { - // cleanup made it once through all lines, thus rescan in loop below to also catch shifted - // lines, which should finish rather quick if there are no more cleanups pending + /* + * cleanup made it once through all lines, thus rescan in loop below to also catch shifted + * lines, which should finish rather quick if there are no more cleanups pending + */ this._memoryCleanupPosition = 0; normalRun = false; } @@ -306,9 +324,11 @@ export class Buffer extends Disposable implements IBuffer { return true; } } - // normal runs always need another rescan afterwards - // if we made it here with normalRun=false, we are in a final run - // and can end the cleanup task for sure + /* + * normal runs always need another rescan afterwards + * if we made it here with normalRun=false, we are in a final run + * and can end the cleanup task for sure + */ return normalRun; } @@ -369,8 +389,10 @@ export class Buffer extends Disposable implements IBuffer { private _reflowSmaller(newCols: number, newRows: number): void { const reflowCursorLine = this._optionsService.rawOptions.reflowCursorLine; const nullCell = this.getNullCell(DEFAULT_ATTR_DATA); - // Gather all BufferLines that need to be inserted into the Buffer here so that they can be - // batched up and only committed once + /* + * Gather all BufferLines that need to be inserted into the Buffer here so that they can be + * batched up and only committed once + */ const toInsert = []; let countToInsert = 0; // Go backwards as many lines may be trimmed and this will avoid considering them @@ -389,8 +411,10 @@ export class Buffer extends Disposable implements IBuffer { } if (!reflowCursorLine) { - // If these lines contain the cursor don't touch them, the program will handle fixing up - // wrapped lines with the cursor + /* + * If these lines contain the cursor don't touch them, the program will handle fixing up + * wrapped lines with the cursor + */ const absoluteY = this.ybase + this.y; if (absoluteY >= y && absoluteY < y + wrappedLines.length) { continue; @@ -416,8 +440,10 @@ export class Buffer extends Disposable implements IBuffer { } if (newLines.length > 0) { toInsert.push({ - // countToInsert here gets the actual index, taking into account other inserted items. - // using this we can iterate through the list forwards + /* + * countToInsert here gets the actual index, taking into account other inserted items. + * using this we can iterate through the list forwards + */ start: y + wrappedLines.length + countToInsert, newLines }); @@ -437,8 +463,10 @@ export class Buffer extends Disposable implements IBuffer { while (srcLineIndex >= 0) { const cellsToCopy = Math.min(srcCol, destCol); if (wrappedLines[destLineIndex] === undefined) { - // Sanity check that the line exists, this has been known to fail for an unknown reason - // which would stop the reflow from happening if an exception would throw. + /* + * Sanity check that the line exists, this has been known to fail for an unknown reason + * which would stop the reflow from happening if an exception would throw. + */ break; } wrappedLines[destLineIndex].copyCellsFrom(wrappedLines[srcLineIndex], srcCol - cellsToCopy, destCol - cellsToCopy, cellsToCopy, true); @@ -486,12 +514,16 @@ export class Buffer extends Disposable implements IBuffer { this.savedY = Math.min(this.savedY + linesToAdd, this.ybase + newRows - 1); } - // Rearrange lines in the buffer if there are any insertions, this is done at the end rather - // than earlier so that it's a single O(n) pass through the buffer, instead of O(n^2) from many - // costly calls to CircularList.splice. + /* + * Rearrange lines in the buffer if there are any insertions, this is done at the end rather + * than earlier so that it's a single O(n) pass through the buffer, instead of O(n^2) from many + * costly calls to CircularList.splice. + */ if (toInsert.length > 0) { - // Record buffer insert events and then play them back backwards so that the indexes are - // correct + /* + * Record buffer insert events and then play them back backwards so that the indexes are + * correct + */ const insertEvents: IInsertEvent[] = []; // Record original lines so they don't get overridden when we rearrange the list diff --git a/src/common/buffer/BufferLine.test.ts b/src/common/buffer/BufferLine.test.ts index 3aec28872..7e8f73f4c 100644 --- a/src/common/buffer/BufferLine.test.ts +++ b/src/common/buffer/BufferLine.test.ts @@ -288,9 +288,11 @@ describe('BufferLine', function(): void { assert.equal(line2.isWrapped, line.isWrapped); }); it('should support combining chars', function(): void { - // CHAR_DATA_CODE_INDEX resembles current behavior in InputHandler.print - // --> set code to the last charCodeAt value of the string - // Note: needs to be fixed once the string pointer is in place + /* + * CHAR_DATA_CODE_INDEX resembles current behavior in InputHandler.print + * --> set code to the last charCodeAt value of the string + * Note: needs to be fixed once the string pointer is in place + */ const line = new TestBufferLine(2, createCellData(1, 'e\u0301', 1)); assert.deepEqual(line.toArray(), [[1, 'e\u0301', 1, '\u0301'.charCodeAt(0)], [1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]]); const line2 = new TestBufferLine(5, createCellData(1, 'a', 1), true); @@ -478,9 +480,11 @@ describe('BufferLine', function(): void { }); it('should always return some sane value', function(): void { const columns: number[] = []; - // sanity check - broken line with invalid out of bound null width cells - // this can atm happen with deleting/inserting chars in inputhandler by "breaking" - // fullwidth pairs --> needs to be fixed after settling BufferLine impl + /* + * sanity check - broken line with invalid out of bound null width cells + * this can atm happen with deleting/inserting chars in inputhandler by "breaking" + * fullwidth pairs --> needs to be fixed after settling BufferLine impl + */ const line = new TestBufferLine(10, NULL_CELL_DATA, false); assert.equal(line.translateToString(false, undefined, undefined, columns), ' '); assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); @@ -500,8 +504,10 @@ describe('BufferLine', function(): void { const line = new TestBufferLine(3, NULL_CELL_DATA, false); line.addCodepointToCell(0, '\u0301'.charCodeAt(0), 0); const cell = line.loadCell(0, new CellData()); - // chars contains single combining char - // width is set to 1 + /* + * chars contains single combining char + * width is set to 1 + */ assert.deepEqual(cell.getAsCharData(), [DEFAULT_ATTR, '\u0301', 1, 0x0301]); // do not account a single combining char as combined assert.equal(cell.isCombined(), 0); @@ -513,8 +519,10 @@ describe('BufferLine', function(): void { line.setCell(0, cell); line.addCodepointToCell(0, '\u0301'.charCodeAt(0), 0); line.loadCell(0, cell); - // chars contains 3 chars - // width is set to 1 + /* + * chars contains 3 chars + * width is set to 1 + */ assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301\u0301', 1, 0x0301]); // do not account a single combining char as combined assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK); @@ -526,8 +534,10 @@ describe('BufferLine', function(): void { line.setCell(0, cell); line.addCodepointToCell(0, '\u0301'.charCodeAt(0), 0); line.loadCell(0, cell); - // chars contains 2 chars - // width is set to 1 + /* + * chars contains 2 chars + * width is set to 1 + */ assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301', 1, 0x0301]); // do not account a single combining char as combined assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK); diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts index be200f112..0cef299e1 100644 --- a/src/common/buffer/BufferLine.ts +++ b/src/common/buffer/BufferLine.ts @@ -9,11 +9,13 @@ import { CellData } from 'common/buffer/CellData'; import { Attributes, BgFlags, CHAR_DATA_ATTR_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, Content, NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, WHITESPACE_CELL_CHAR } from 'common/buffer/Constants'; import { stringFromCodePoint } from 'common/input/TextDecoder'; -// Buffer memory layout: -// -// [0]: content `uint32_t` - wcwidth(2) comb(1) codepoint(21) -// [1]: fg `uint32_t` - flags(8) r(8) g(8) b(8) -// [2]: bg `uint32_t` - flags(8) r(8) g(8) b(8) +/* + * Buffer memory layout: + * + * [0]: content `uint32_t` - wcwidth(2) comb(1) codepoint(21) + * [1]: fg `uint32_t` - flags(8) r(8) g(8) b(8) + * [2]: bg `uint32_t` - flags(8) r(8) g(8) b(8) + */ const enum Constants { /** The number of 32 bit array indices taken by one cell. */ @@ -255,15 +257,19 @@ export class BufferLine implements IBufferLine { this._combined[index] += stringFromCodePoint(codePoint); } else { if (content & Content.CODEPOINT_MASK) { - // normal case for combining chars: - // - move current leading char + new one into combined string - // - set combined flag + /* + * normal case for combining chars: + * - move current leading char + new one into combined string + * - set combined flag + */ this._combined[index] = stringFromCodePoint(content & Content.CODEPOINT_MASK) + stringFromCodePoint(codePoint); content &= ~Content.CODEPOINT_MASK; // set codepoint in buffer to 0 content |= Content.IS_COMBINED_MASK; } else { - // should not happen - we actually have no data in the cell yet - // simply set the data in the cell buffer with a width of 1 + /* + * should not happen - we actually have no data in the cell yet + * simply set the data in the cell buffer with a width of 1 + */ content = codePoint | (1 << Content.WIDTH_SHIFT); } } @@ -318,9 +324,11 @@ export class BufferLine implements IBufferLine { } } - // handle fullwidth at pos: - // - reset pos-1 if wide char - // - reset pos if width==0 (previous second cell of a wide char) + /* + * handle fullwidth at pos: + * - reset pos-1 if wide char + * - reset pos if width==0 (previous second cell of a wide char) + */ if (pos && this.getWidth(pos - 1) === 2) { this.setCellFromCodepoint(pos - 1, 0, 1, fillCellData); } diff --git a/src/common/buffer/BufferReflow.ts b/src/common/buffer/BufferReflow.ts index 44aa0976f..5eaeae406 100644 --- a/src/common/buffer/BufferReflow.ts +++ b/src/common/buffer/BufferReflow.ts @@ -23,8 +23,10 @@ export interface INewLayoutResult { * @param reflowCursorLine Whether to reflow the line containing the cursor. */ export function reflowLargerGetLinesToRemove(lines: CircularList, oldCols: number, newCols: number, bufferAbsoluteY: number, nullCell: ICellData, reflowCursorLine: boolean): number[] { - // Gather all BufferLines that need to be removed from the Buffer here so that they can be - // batched up and only committed once + /* + * Gather all BufferLines that need to be removed from the Buffer here so that they can be + * batched up and only committed once + */ const toRemove: number[] = []; for (let y = 0; y < lines.length - 1; y++) { @@ -43,8 +45,10 @@ export function reflowLargerGetLinesToRemove(lines: CircularList, o } if (!reflowCursorLine) { - // If these lines contain the cursor don't touch them, the program will handle fixing up - // wrapped lines with the cursor + /* + * If these lines contain the cursor don't touch them, the program will handle fixing up + * wrapped lines with the cursor + */ if (bufferAbsoluteY >= y && bufferAbsoluteY < i) { y += wrappedLines.length - 1; continue; @@ -180,8 +184,10 @@ export function reflowSmallerGetNewLineLengths(wrappedLines: BufferLine[], oldCo const newLineLengths: number[] = []; const cellsNeeded = wrappedLines.map((l, i) => getWrappedLineTrimmedLength(wrappedLines, i, oldCols)).reduce((p, c) => p + c); - // Use srcCol and srcLine to find the new wrapping point, use that to get the cellsAvailable and - // linesNeeded + /* + * Use srcCol and srcLine to find the new wrapping point, use that to get the cellsAvailable and + * linesNeeded + */ let srcCol = 0; let srcLine = 0; let cellsAvailable = 0; @@ -214,9 +220,11 @@ export function getWrappedLineTrimmedLength(lines: BufferLine[], i: number, cols if (i === lines.length - 1) { return lines[i].getTrimmedLength(); } - // Detect whether the following line starts with a wide character and the end of the current line - // is null, if so then we can be pretty sure the null character should be excluded from the line - // length] + /* + * Detect whether the following line starts with a wide character and the end of the current line + * is null, if so then we can be pretty sure the null character should be excluded from the line + * length] + */ const endsInNull = !(lines[i].hasContent(cols - 1)) && lines[i].getWidth(cols - 1) === 1; const followingLineStartsWithWide = lines[i + 1].getWidth(0) === 2; if (endsInNull && followingLineStartsWithWide) { diff --git a/src/common/buffer/BufferSet.ts b/src/common/buffer/BufferSet.ts index 32a4bf3b4..2bad2171f 100644 --- a/src/common/buffer/BufferSet.ts +++ b/src/common/buffer/BufferSet.ts @@ -43,8 +43,10 @@ export class BufferSet extends Disposable implements IBufferSet { this._normalBuffer.value = this._normal; this._normal.fillViewportRows(); - // The alt buffer should never have scrollback. - // See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer + /* + * The alt buffer should never have scrollback. + * See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer + */ this._alt = new Buffer(false, this._optionsService, this._bufferService, this._logService); this._altBuffer.value = this._alt; this._activeBuffer = this._normal; @@ -86,9 +88,11 @@ export class BufferSet extends Disposable implements IBufferSet { } this._normal.x = this._alt.x; this._normal.y = this._alt.y; - // The alt buffer should always be cleared when we switch to the normal - // buffer. This frees up memory since the alt buffer should always be new - // when activated. + /* + * The alt buffer should always be cleared when we switch to the normal + * buffer. This frees up memory since the alt buffer should always be new + * when activated. + */ this._alt.clearAllMarkers(); this._alt.clear(); this._activeBuffer = this._normal; @@ -105,8 +109,10 @@ export class BufferSet extends Disposable implements IBufferSet { if (this._activeBuffer === this._alt) { return; } - // Since the alt buffer is always cleared when the normal buffer is - // activated, we want to fill it when switching to it. + /* + * Since the alt buffer is always cleared when the normal buffer is + * activated, we want to fill it when switching to it. + */ this._alt.fillViewportRows(fillAttr); this._alt.x = this._normal.x; this._alt.y = this._normal.y; diff --git a/src/common/buffer/CellData.ts b/src/common/buffer/CellData.ts index 43c4c594b..29e4a0f50 100644 --- a/src/common/buffer/CellData.ts +++ b/src/common/buffer/CellData.ts @@ -65,8 +65,10 @@ export class CellData extends AttributeData implements ICellData { } else if (value[CHAR_DATA_CHAR_INDEX].length === 2) { const code = value[CHAR_DATA_CHAR_INDEX].charCodeAt(0); - // if the 2-char string is a surrogate create single codepoint - // everything else is combined + /* + * if the 2-char string is a surrogate create single codepoint + * everything else is combined + */ if (0xD800 <= code && code <= 0xDBFF) { const second = value[CHAR_DATA_CHAR_INDEX].charCodeAt(1); if (0xDC00 <= second && second <= 0xDFFF) { diff --git a/src/common/input/Keyboard.test.ts b/src/common/input/Keyboard.test.ts index 47f516f0f..4e47cbce0 100644 --- a/src/common/input/Keyboard.test.ts +++ b/src/common/input/Keyboard.test.ts @@ -123,8 +123,10 @@ describe('Keyboard', () => { }); describe('On non-macOS platforms', () => { - // Evalueate alt + arrow key movement, which is a feature of terminal emulators but not VT100 - // http://unix.stackexchange.com/a/108106 + /* + * Evalueate alt + arrow key movement, which is a feature of terminal emulators but not VT100 + * http://unix.stackexchange.com/a/108106 + */ it('should return \\x1b[1;3D for alt+left', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 37 }, { isMac: false }).key, '\x1b[1;3D'); // CSI 1;3 D }); diff --git a/src/common/input/Keyboard.ts b/src/common/input/Keyboard.ts index 545b436c8..54bc4a27e 100644 --- a/src/common/input/Keyboard.ts +++ b/src/common/input/Keyboard.ts @@ -43,8 +43,10 @@ export function evaluateKeyboardEvent( ): IKeyboardResult { const result: IKeyboardResult = { type: KeyboardResultType.SEND_KEY, - // Whether to cancel event propagation (NOTE: this may not be needed since the event is - // canceled at the end of keyDown + /* + * Whether to cancel event propagation (NOTE: this may not be needed since the event is + * canceled at the end of keyDown + */ cancel: false, // The new key even to emit key: undefined @@ -100,8 +102,10 @@ export function evaluateKeyboardEvent( case 13: // return/enter if (ev.key === 'c' && ev.ctrlKey) { - // HACK: Safari on iPad, iOS, AppleVisionPro sends key 13 when typing ctrl-c on hardware - // keyboard + /* + * HACK: Safari on iPad, iOS, AppleVisionPro sends key 13 when typing ctrl-c on hardware + * keyboard + */ result.key = C0.ETX; } else { result.key = ev.altKey ? C0.ESC + C0.CR : C0.CR; @@ -171,8 +175,10 @@ export function evaluateKeyboardEvent( case 45: // insert if (!ev.shiftKey && !ev.ctrlKey) { - // or + are used to - // copy-paste on some systems. + /* + * or + are used to + * copy-paste on some systems. + */ result.key = C0.ESC + '[2~'; } break; @@ -346,11 +352,13 @@ export function evaluateKeyboardEvent( } else if (ev.keyCode === 32) { result.key = C0.ESC + (ev.ctrlKey ? C0.NUL : ' '); } else if (ev.key === 'Dead' && ev.code.startsWith('Key')) { - // Reference: https://github.com/xtermjs/xterm.js/issues/3725 - // Alt will produce a "dead key" (initate composition) with some - // of the letters in US layout (e.g. N/E/U). - // It's safe to match against Key* since no other `code` values begin with "Key". - // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values#code_values_on_mac + /* + * Reference: https://github.com/xtermjs/xterm.js/issues/3725 + * Alt will produce a "dead key" (initate composition) with some + * of the letters in US layout (e.g. N/E/U). + * It's safe to match against Key* since no other `code` values begin with "Key". + * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values#code_values_on_mac + */ let keyString = ev.code.slice(3, 4); if (!ev.shiftKey) { keyString = keyString.toLowerCase(); @@ -363,8 +371,10 @@ export function evaluateKeyboardEvent( result.type = KeyboardResultType.SELECT_ALL; } } else if (ev.key && !ev.ctrlKey && !ev.altKey && !ev.metaKey && ev.keyCode >= 48 && ev.key.length === 1) { - // Include only keys that that result in a _single_ character; don't include num lock, - // volume up, etc. + /* + * Include only keys that that result in a _single_ character; don't include num lock, + * volume up, etc. + */ result.key = ev.key; } else if (ev.key && ev.ctrlKey && ev.shiftKey) { switch (ev.code) { diff --git a/src/common/input/KittyKeyboard.test.ts b/src/common/input/KittyKeyboard.test.ts index 2a832d75f..44c4ae2bb 100644 --- a/src/common/input/KittyKeyboard.test.ts +++ b/src/common/input/KittyKeyboard.test.ts @@ -40,8 +40,10 @@ describe('KittyKeyboard', () => { const flags = KittyKeyboardFlags.DISAMBIGUATE_ESCAPE_CODES; it('shift+letter sends plain character in DISAMBIGUATE mode', () => { - // Kitty spec: DISAMBIGUATE only encodes keys ambiguous in legacy encoding - // Shift+a → "A" is not ambiguous, so send plain "A" + /* + * Kitty spec: DISAMBIGUATE only encodes keys ambiguous in legacy encoding + * Shift+a → "A" is not ambiguous, so send plain "A" + */ const result = kitty.evaluate(createEvent({ key: 'A', shiftKey: true }), flags); assert.strictEqual(result.key, 'A'); }); @@ -554,8 +556,10 @@ describe('KittyKeyboard', () => { }); }); - // Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense and - // isn't specified in the spec, but press and repeat events shouldn't get swallowed. + /* + * Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense and + * isn't specified in the spec, but press and repeat events shouldn't get swallowed. + */ describe('REPORT_EVENT_TYPES flag without DISAMBIGUATE_ESCAPE_CODES', () => { const flags = KittyKeyboardFlags.REPORT_EVENT_TYPES; diff --git a/src/common/input/KittyKeyboard.ts b/src/common/input/KittyKeyboard.ts index ff57b4f89..658d4e0d9 100644 --- a/src/common/input/KittyKeyboard.ts +++ b/src/common/input/KittyKeyboard.ts @@ -437,10 +437,12 @@ export class KittyKeyboard { return result; } - // Spec § "Report all keys as escape codes": "Additionally, with this mode, - // events for pressing modifier keys are reported." — i.e. *without* this - // mode, modifier-key press events are suppressed. Kitty's is_modifier_key() - // treats CapsLock/NumLock/ScrollLock as modifier keys for this rule. + /* + * Spec § "Report all keys as escape codes": "Additionally, with this mode, + * events for pressing modifier keys are reported." — i.e. *without* this + * mode, modifier-key press events are suppressed. Kitty's is_modifier_key() + * treats CapsLock/NumLock/ScrollLock as modifier keys for this rule. + */ if (this._isLockKey(ev) && !(flags & KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES)) { return result; } @@ -474,8 +476,10 @@ export class KittyKeyboard { // Special handling for Enter/Tab/Backspace. const specialKey = keyCode === 13 || keyCode === 9 || keyCode === 127; - // Per spec, Enter/Tab/Backspace will not have release events unless "Report all keys as escape - // codes" is also set. + /* + * Per spec, Enter/Tab/Backspace will not have release events unless "Report all keys as escape + * codes" is also set. + */ if (specialKey && eventType === KittyKeyboardEventType.RELEASE && !(flags & KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES)) { return result; } @@ -485,14 +489,18 @@ export class KittyKeyboard { const useCsiU = !!( flags & KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES || (reportEventTypes && eventType === KittyKeyboardEventType.RELEASE) || - // Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense, so - // just make REPORT_EVENT_TYPES imply DISAMBIGUATE_ESCAPE_CODES here for simplicity. - // See: https://github.com/kovidgoyal/kitty/issues/9999 + /* + * Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense, so + * just make REPORT_EVENT_TYPES imply DISAMBIGUATE_ESCAPE_CODES here for simplicity. + * See: https://github.com/kovidgoyal/kitty/issues/9999 + */ ((flags & KittyKeyboardFlags.DISAMBIGUATE_ESCAPE_CODES || reportEventTypes) && ( - // Per spec, Enter/Tab/Backspace "still generate the same bytes as in legacy mode" and - // consider space to be a text-generating key, so these skip the isFunc fast-path and only - // get CSI u when modifiers are present (handled below). + /* + * Per spec, Enter/Tab/Backspace "still generate the same bytes as in legacy mode" and + * consider space to be a text-generating key, so these skip the isFunc fast-path and only + * get CSI u when modifiers are present (handled below). + */ (isFunc && !specialKey) || ( (modifiers > 0 && ev.key.length !== 1) || diff --git a/src/common/input/TextDecoder.ts b/src/common/input/TextDecoder.ts index 7ec9c7cd2..cc503166d 100644 --- a/src/common/input/TextDecoder.ts +++ b/src/common/input/TextDecoder.ts @@ -28,11 +28,13 @@ export function utf32ToString(data: Uint32Array, start: number = 0, end: number for (let i = start; i < end; ++i) { let codepoint = data[i]; if (codepoint > 0xFFFF) { - // JS strings are encoded as UTF16, thus a non BMP codepoint gets converted into a surrogate - // pair conversion rules: - // - subtract 0x10000 from code point, leaving a 20 bit number - // - add high 10 bits to 0xD800 --> first surrogate - // - add low 10 bits to 0xDC00 --> second surrogate + /* + * JS strings are encoded as UTF16, thus a non BMP codepoint gets converted into a surrogate + * pair conversion rules: + * - subtract 0x10000 from code point, leaving a 20 bit number + * - add high 10 bits to 0xD800 --> first surrogate + * - add low 10 bits to 0xDC00 --> second surrogate + */ codepoint -= 0x10000; result += String.fromCharCode((codepoint >> 10) + 0xD800) + String.fromCharCode((codepoint % 0x400) + 0xDC00); } else { diff --git a/src/common/input/UnicodeV6.test.ts b/src/common/input/UnicodeV6.test.ts index 98dc97dc2..95c2f6e2f 100644 --- a/src/common/input/UnicodeV6.test.ts +++ b/src/common/input/UnicodeV6.test.ts @@ -12,8 +12,10 @@ it('wcwidth should match all values from the old implementation', function(): vo // old implementation const wcwidthOld = (function(opts: {nul: number, control: number}): (ucs: number) => number { - // extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c - // combining characters + /* + * extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c + * combining characters + */ const COMBINING_BMP = [ [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], @@ -149,18 +151,20 @@ it('wcwidth should match all values from the old implementation', function(): vo } return table; } - // get width from lookup table - // position in container : num / CODEPOINTS_PER_ITEM - // ==> n = table[Math.floor(num / 16)] - // ==> n = table[num >> 4] - // 16 codepoints per number: FFEEDDCCBBAA99887766554433221100 - // position in number : (num % CODEPOINTS_PER_ITEM) * BITWIDTH - // ==> m = (n % 16) * 2 - // ==> m = (num & 15) << 1 - // right shift to position m - // ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766 - // we are only interested in 2 LSBs, cut off higher bits - // ==> n = n & 3 e.g. 000000000000000000000000000000XX + /* + * get width from lookup table + * position in container : num / CODEPOINTS_PER_ITEM + * ==> n = table[Math.floor(num / 16)] + * ==> n = table[num >> 4] + * 16 codepoints per number: FFEEDDCCBBAA99887766554433221100 + * position in number : (num % CODEPOINTS_PER_ITEM) * BITWIDTH + * ==> m = (n % 16) * 2 + * ==> m = (num & 15) << 1 + * right shift to position m + * ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766 + * we are only interested in 2 LSBs, cut off higher bits + * ==> n = n & 3 e.g. 000000000000000000000000000000XX + */ return (num: number): number => { num |= 0; // get asm.js like optimization under V8 if (num < 32) { diff --git a/src/common/input/UnicodeV6.ts b/src/common/input/UnicodeV6.ts index 83265f705..4b6bd55c0 100644 --- a/src/common/input/UnicodeV6.ts +++ b/src/common/input/UnicodeV6.ts @@ -94,8 +94,10 @@ export class UnicodeV6 implements IUnicodeVersionProvider { table.fill(0, 1, 32); table.fill(0, 0x7f, 0xa0); - // apply wide char rules first - // wide chars + /* + * apply wide char rules first + * wide chars + */ table.fill(2, 0x1100, 0x1160); table[0x2329] = 2; table[0x232a] = 2; @@ -109,11 +111,13 @@ export class UnicodeV6 implements IUnicodeVersionProvider { table.fill(2, 0xff00, 0xff61); table.fill(2, 0xffe0, 0xffe7); - // apply combining last to ensure we overwrite - // wrongly wide set chars: - // the original algo evals combining first and falls - // through to wide check so we simply do here the opposite - // combining 0 + /* + * apply combining last to ensure we overwrite + * wrongly wide set chars: + * the original algo evals combining first and falls + * through to wide check so we simply do here the opposite + * combining 0 + */ for (let r = 0; r < BMP_COMBINING.length; ++r) { table.fill(0, BMP_COMBINING[r][0], BMP_COMBINING[r][1] + 1); } diff --git a/src/common/input/Win32InputMode.ts b/src/common/input/Win32InputMode.ts index 72907836d..23f6ade0f 100644 --- a/src/common/input/Win32InputMode.ts +++ b/src/common/input/Win32InputMode.ts @@ -198,8 +198,10 @@ export class Win32InputMode { * Returns 0 for non-character keys. */ private _getUnicodeChar(ev: IKeyboardEvent): number { - // Handle special keys that produce control characters - // Ctrl modifies some of these: Ctrl+Enter=LF, Ctrl+Backspace=DEL + /* + * Handle special keys that produce control characters + * Ctrl modifies some of these: Ctrl+Enter=LF, Ctrl+Backspace=DEL + */ if (ev.ctrlKey && !ev.altKey && !ev.metaKey) { if (ev.key === 'Enter') { return 0x0A; // Line feed (Ctrl+Enter) @@ -245,9 +247,11 @@ export class Win32InputMode { state |= Win32ControlKeyState.SHIFT_PRESSED; } - // Note: We can't distinguish left/right for ctrl/alt in standard browser events, - // so we use the generic pressed flags. The right-side flags are used when - // we can detect them (e.g., via code property). + /* + * Note: We can't distinguish left/right for ctrl/alt in standard browser events, + * so we use the generic pressed flags. The right-side flags are used when + * we can detect them (e.g., via code property). + */ if (ev.ctrlKey) { if (ev.code === 'ControlRight') { state |= Win32ControlKeyState.RIGHT_CTRL_PRESSED; diff --git a/src/common/input/WriteBuffer.ts b/src/common/input/WriteBuffer.ts index b936c9dbf..888db9ac5 100644 --- a/src/common/input/WriteBuffer.ts +++ b/src/common/input/WriteBuffer.ts @@ -90,12 +90,16 @@ export class WriteBuffer extends Disposable { * @deprecated Unreliable, to be removed soon. */ public writeSync(data: string | Uint8Array, maxSubsequentCalls?: number): void { - // stop writeSync recursions with maxSubsequentCalls argument - // This is dangerous to use as it will lose the current data chunk - // and return immediately. + /* + * stop writeSync recursions with maxSubsequentCalls argument + * This is dangerous to use as it will lose the current data chunk + * and return immediately. + */ if (maxSubsequentCalls !== undefined && this._syncCalls > maxSubsequentCalls) { - // comment next line if a whole loop block should only contain x `writeSync` calls - // (total flat vs. deep nested limit) + /* + * comment next line if a whole loop block should only contain x `writeSync` calls + * (total flat vs. deep nested limit) + */ this._syncCalls = 0; return; } @@ -112,18 +116,22 @@ export class WriteBuffer extends Disposable { } this._isSyncWriting = true; - // force sync processing on pending data chunks to avoid in-band data scrambling - // does the same as innerWrite but without event loop - // we have to do it here as single loop steps to not corrupt loop subject - // by another writeSync call triggered from _action + /* + * force sync processing on pending data chunks to avoid in-band data scrambling + * does the same as innerWrite but without event loop + * we have to do it here as single loop steps to not corrupt loop subject + * by another writeSync call triggered from _action + */ let chunk: string | Uint8Array | undefined; while (chunk = this._writeBuffer.shift()) { this._action(chunk); const cb = this._callbacks.shift(); if (cb) cb(); } - // reset to avoid reprocessing of chunks with scheduled innerWrite call - // stopping scheduled innerWrite by offset > length condition + /* + * reset to avoid reprocessing of chunks with scheduled innerWrite call + * stopping scheduled innerWrite by offset > length condition + */ this._pendingData = 0; this._bufferOffset = 0x7FFFFFFF; @@ -141,9 +149,11 @@ export class WriteBuffer extends Disposable { if (!this._writeBuffer.length) { this._bufferOffset = 0; - // If this is the first write call after the user has done some input, - // parse it immediately to minimize input latency, - // otherwise schedule for the next event + /* + * If this is the first write call after the user has done some input, + * parse it immediately to minimize input latency, + * otherwise schedule for the next event + */ if (this._didUserInput) { this._didUserInput = false; this._pendingData += data.length; @@ -234,15 +244,19 @@ export class WriteBuffer extends Disposable { * throughput by eval'ing `startTime` upfront pulling at least one more chunk into the * current microtask queue (executed before setTimeout). */ - // const continuation: (r: boolean) => void = performance.now() - startTime >= - // Constants.WRITE_TIMEOUT_MS - // ? r => setTimeout(() => this._innerWrite(0, r)) - // : r => this._innerWrite(startTime, r); + /* + * const continuation: (r: boolean) => void = performance.now() - startTime >= + * Constants.WRITE_TIMEOUT_MS + * ? r => setTimeout(() => this._innerWrite(0, r)) + * : r => this._innerWrite(startTime, r); + */ - // Handle exceptions synchronously to current band position, idea: - // 1. spawn a single microtask which we allow to throw hard - // 2. spawn a promise immediately resolving to `true` - // (executed on the same queue, thus properly aligned before continuation happens) + /* + * Handle exceptions synchronously to current band position, idea: + * 1. spawn a single microtask which we allow to throw hard + * 2. spawn a promise immediately resolving to `true` + * (executed on the same queue, thus properly aligned before continuation happens) + */ result.catch(err => { queueMicrotask(() => {throw err;}); return Promise.resolve(false); @@ -260,8 +274,10 @@ export class WriteBuffer extends Disposable { } } if (this._writeBuffer.length > this._bufferOffset) { - // Allow renderer to catch up before processing the next batch - // trim already processed chunks if we are above threshold + /* + * Allow renderer to catch up before processing the next batch + * trim already processed chunks if we are above threshold + */ if (this._bufferOffset > Constants.WRITE_BUFFER_LENGTH_THRESHOLD) { this._writeBuffer = this._writeBuffer.slice(this._bufferOffset); this._callbacks = this._callbacks.slice(this._bufferOffset); diff --git a/src/common/input/XParseColor.ts b/src/common/input/XParseColor.ts index ae4a3bcae..2f4a79dc8 100644 --- a/src/common/input/XParseColor.ts +++ b/src/common/input/XParseColor.ts @@ -50,9 +50,11 @@ export function parseColor(data: string): [number, number, number] | undefined { } } - // Named colors are currently not supported due to the large addition to the xterm.js bundle size - // they would add. In order to support named colors, we would need some way of optionally loading - // additional payloads so startup/download time is not bloated (see #3530). + /* + * Named colors are currently not supported due to the large addition to the xterm.js bundle size + * they would add. In order to support named colors, we would need some way of optionally loading + * additional payloads so startup/download time is not bloated (see #3530). + */ } // pad hex output to requested bit width diff --git a/src/common/parser/ApcParser.ts b/src/common/parser/ApcParser.ts index 8f921f8cb..453c71110 100644 --- a/src/common/parser/ApcParser.ts +++ b/src/common/parser/ApcParser.ts @@ -181,8 +181,10 @@ export class ApcHandler implements IApcHandler { } else if (success) { ret = this._handler(this._data); if (ret instanceof Promise) { - // need to hold data until `ret` got resolved - // dont care for errors, data will be freed anyway on next start + /* + * need to hold data until `ret` got resolved + * dont care for errors, data will be freed anyway on next start + */ return ret.then(res => { this._data = ''; this._hitLimit = false; diff --git a/src/common/parser/DcsParser.ts b/src/common/parser/DcsParser.ts index 692200230..849b82709 100644 --- a/src/common/parser/DcsParser.ts +++ b/src/common/parser/DcsParser.ts @@ -147,10 +147,12 @@ export class DcsHandler implements IDcsHandler { constructor(private _handler: (data: string, params: IParams) => boolean | Promise) { } public hook(params: IParams): void { - // since we need to preserve params until `unhook`, we have to clone it - // (only borrowed from parser and spans multiple parser states) - // perf optimization: - // clone only, if we have non empty params, otherwise stick with default + /* + * since we need to preserve params until `unhook`, we have to clone it + * (only borrowed from parser and spans multiple parser states) + * perf optimization: + * clone only, if we have non empty params, otherwise stick with default + */ this._params = (params.length > 1 || params.params[0]) ? params.clone() : EMPTY_PARAMS; this._data = ''; this._hitLimit = false; @@ -174,8 +176,10 @@ export class DcsHandler implements IDcsHandler { } else if (success) { ret = this._handler(this._data, this._params); if (ret instanceof Promise) { - // need to hold data and params until `ret` got resolved - // dont care for errors, data will be freed anyway on next start + /* + * need to hold data and params until `ret` got resolved + * dont care for errors, data will be freed anyway on next start + */ return ret.then(res => { this._params = EMPTY_PARAMS; this._data = ''; diff --git a/src/common/parser/EscapeSequenceParser.test.ts b/src/common/parser/EscapeSequenceParser.test.ts index 2d5a04df7..17cc37ee3 100644 --- a/src/common/parser/EscapeSequenceParser.test.ts +++ b/src/common/parser/EscapeSequenceParser.test.ts @@ -2065,8 +2065,10 @@ async function throwsAsync(fn: () => Promise, message?: string | undefined) } describe('EscapeSequenceParser - async', () => { - // sequences: SGR 1;31 | hello SP | ESC %G | wor | ESC E | ld! | SGR 0 | EXE \r\n | $> | DCS 1;2 a [xyz] ST | OSC 1;foo=bar ST | APC X abc ST | FIN - // needed handlers: CSI m, PRINT, ESC %G, ESC E, EXE \r, EXE \n, OSC 1, APC X + /* + * sequences: SGR 1;31 | hello SP | ESC %G | wor | ESC E | ld! | SGR 0 | EXE \r\n | $> | DCS 1;2 a [xyz] ST | OSC 1;foo=bar ST | APC X abc ST | FIN + * needed handlers: CSI m, PRINT, ESC %G, ESC E, EXE \r, EXE \n, OSC 1, APC X + */ const INPUT = '\x1b[1;31mhello \x1b%Gwor\x1bEld!\x1b[0m\r\n$>\x1bP1;2axyz\x1b\\\x1b]1;foo=bar\x1b\\\x1b_Xabc\x1b\\FIN'; let RESULT: any[]; let parser: TestEscapeSequenceParser; @@ -2155,8 +2157,10 @@ describe('EscapeSequenceParser - async', () => { it('sync parse call does not work anymore', () => { assert.notEqual(!parseSync(parser, INPUT), true); assert.notDeepEqual(callstack, RESULT); - // due to sync calling we should save exactly one saved stack - // proper continuation is not possible anymore, as we lost the promise resolve value + /* + * due to sync calling we should save exactly one saved stack + * proper continuation is not possible anymore, as we lost the promise resolve value + */ assert.equal(parser.trackedStack.length, 1); }); it('improper continuation should throw', async () => { @@ -2222,8 +2226,10 @@ describe('EscapeSequenceParser - async', () => { // split to single char input for (let i = 0; i < INPUT.length; ++i) { - // Note: a single fully awaited parse call always ends in sync mode, - // which re-enables faster sync processing in the higher up callstack + /* + * Note: a single fully awaited parse call always ends in sync mode, + * which re-enables faster sync processing in the higher up callstack + */ await parseP(parser, INPUT[i]); } assert.deepEqual(callstack, RESULT); diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index d944112db..be4877766 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -15,20 +15,22 @@ import { ApcParser } from 'common/parser/ApcParser'; /** * VT commands done by the parser */ -// @vt: #Y ESC CSI "Control Sequence Introducer" "ESC [" "Start of a CSI sequence." -// @vt: #Y ESC OSC "Operating System Command" "ESC ]" "Start of an OSC sequence." -// @vt: #Y ESC DCS "Device Control String" "ESC P" "Start of a DCS sequence." -// @vt: #Y ESC ST "String Terminator" "ESC \" "Terminator used for string type sequences." -// @vt: #Y ESC PM "Privacy Message" "ESC ^" "Start of a privacy message." -// @vt: #Y ESC APC "Application Program Command" "ESC _" "Start of an APC sequence." -// @vt: #Y C1 CSI "Control Sequence Introducer" "\x9B" "Start of a CSI sequence." -// @vt: #Y C1 OSC "Operating System Command" "\x9D" "Start of an OSC sequence." -// @vt: #Y C1 DCS "Device Control String" "\x90" "Start of a DCS sequence." -// @vt: #Y C1 ST "String Terminator" "\x9C" "Terminator used for string type sequences." -// @vt: #Y C1 PM "Privacy Message" "\x9E" "Start of a privacy message." -// @vt: #Y C1 APC "Application Program Command" "\x9F" "Start of an APC sequence." -// @vt: #Y C0 NUL "Null" "\0, \x00" "NUL is ignored." -// @vt: #Y C0 ESC "Escape" "\e, \x1B" "Start of a sequence. Cancels any other sequence." +/* + * @vt: #Y ESC CSI "Control Sequence Introducer" "ESC [" "Start of a CSI sequence." + * @vt: #Y ESC OSC "Operating System Command" "ESC ]" "Start of an OSC sequence." + * @vt: #Y ESC DCS "Device Control String" "ESC P" "Start of a DCS sequence." + * @vt: #Y ESC ST "String Terminator" "ESC \" "Terminator used for string type sequences." + * @vt: #Y ESC PM "Privacy Message" "ESC ^" "Start of a privacy message." + * @vt: #Y ESC APC "Application Program Command" "ESC _" "Start of an APC sequence." + * @vt: #Y C1 CSI "Control Sequence Introducer" "\x9B" "Start of a CSI sequence." + * @vt: #Y C1 OSC "Operating System Command" "\x9D" "Start of an OSC sequence." + * @vt: #Y C1 DCS "Device Control String" "\x90" "Start of a DCS sequence." + * @vt: #Y C1 ST "String Terminator" "\x9C" "Terminator used for string type sequences." + * @vt: #Y C1 PM "Privacy Message" "\x9E" "Start of a privacy message." + * @vt: #Y C1 APC "Application Program Command" "\x9F" "Start of an APC sequence." + * @vt: #Y C0 NUL "Null" "\0, \x00" "NUL is ignored." + * @vt: #Y C0 ESC "Escape" "\e, \x1B" "Start of a sequence. Cancels any other sequence." + */ /** * Table values are generated like this: @@ -95,8 +97,10 @@ const NON_ASCII_PRINTABLE = 0xA0; * Taken from https://vt100.net/emu/dec_ansi_parser. */ export const VT500_TRANSITION_TABLE = (function (): TransitionTable { - // table size: - // (ParserState.STATE_LENGTH - 1) << TableAccess.INDEX_STATE_SHIFT | NON_ASCII_PRINTABLE + 1 + /* + * table size: + * (ParserState.STATE_LENGTH - 1) << TableAccess.INDEX_STATE_SHIFT | NON_ASCII_PRINTABLE + 1 + */ const table: TransitionTable = new TransitionTable(4257); // range macro for byte @@ -500,9 +504,11 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this._params.resetZdm(); this._collect = 0; this.precedingJoinState = 0; - // abort pending continuation from async handler - // Here the RESET type indicates, that the next parse call will - // ignore any saved stack, instead continues sync with next codepoint from GROUND + /* + * abort pending continuation from async handler + * Here the RESET type indicates, that the next parse call will + * ignore any saved stack, instead continues sync with next codepoint from GROUND + */ if (this._parseStack.state !== ParserStackType.NONE) { this._parseStack.state = ParserStackType.RESET; this._parseStack.handlers = []; // also release handlers ref @@ -579,8 +585,10 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP // resume from async handler if (this._parseStack.state) { - // allow sync parser reset even in continuation mode - // Note: can be used to recover parser from improper continuation error below + /* + * allow sync parser reset even in continuation mode + * Note: can be used to recover parser from improper continuation error below + */ if (this._parseStack.state === ParserStackType.RESET) { this._parseStack.state = ParserStackType.NONE; start = this._parseStack.chunkPos + 1; // continue with next codepoint in GROUND @@ -605,9 +613,11 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP throw new Error('improper continuation due to previous async handler, giving up parsing'); } - // we have to resume the old handler loop if: - // - return value of the promise was `false` - // - handlers are not exhausted yet + /* + * we have to resume the old handler loop if: + * - return value of the promise was `false` + * - handlers are not exhausted yet + */ const handlers = this._parseStack.handlers; let handlerPos = this._parseStack.handlerPos - 1; switch (this._parseStack.state) { @@ -794,8 +804,10 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP const handlers = this._csiHandlers[this._collect << 8 | code]; let j = handlers ? handlers.length - 1 : -1; for (; j >= 0; j--) { - // true means success and to stop bubbling - // a promise indicates an async handler that needs to finish before progressing + /* + * true means success and to stop bubbling + * a promise indicates an async handler that needs to finish before progressing + */ handlerResult = handlers[j](this._params); if (handlerResult === true) { break; @@ -833,8 +845,10 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP const handlersEsc = this._escHandlers[this._collect << 8 | code]; let jj = handlersEsc ? handlersEsc.length - 1 : -1; for (; jj >= 0; jj--) { - // true means success and to stop bubbling - // a promise indicates an async handler that needs to finish before progressing + /* + * true means success and to stop bubbling + * a promise indicates an async handler that needs to finish before progressing + */ handlerResult = handlersEsc[jj](); if (handlerResult === true) { break; @@ -856,8 +870,10 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this._dcsParser.hook(this._collect << 8 | code, this._params); break; case ParserAction.DCS_PUT: - // inner loop - exit DCS_PUT: 0x18, 0x1a, 0x1b, 0x7f, 0x80 - 0x9f - // unhook triggered by: 0x1b, 0x9c (success) and 0x18, 0x1a (abort) + /* + * inner loop - exit DCS_PUT: 0x18, 0x1a, 0x1b, 0x7f, 0x80 - 0x9f + * unhook triggered by: 0x1b, 0x9c (success) and 0x18, 0x1a (abort) + */ for (let j = i + 1; ; ++j) { if (j >= length || (code = data[j]) === 0x18 || code === 0x1a || code === 0x1b || (code > 0x7f && code < NON_ASCII_PRINTABLE)) { this._dcsParser.put(data, i, j); @@ -905,8 +921,10 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this._apcParser.start(this._collect << 8 | code); break; case ParserAction.APC_PUT: - // inner loop - exit APC_PUT: 0x18, 0x1a, 0x1b, 0x9c - // allowed: 00/08 .. 00/13, 02/00 .. 07/14 + NON_ASCII_PRINTABLE + /* + * inner loop - exit APC_PUT: 0x18, 0x1a, 0x1b, 0x9c + * allowed: 00/08 .. 00/13, 02/00 .. 07/14 + NON_ASCII_PRINTABLE + */ for (let j = i + 1; ; ++j) { if (j < length && ( (data[j] >= 0x20 && data[j] < 0x7f) || (data[j] >= 0x08 && data[j] < 0x0e) || data[j] >= NON_ASCII_PRINTABLE diff --git a/src/common/parser/OscParser.ts b/src/common/parser/OscParser.ts index 1f0c8cb23..d348f483b 100644 --- a/src/common/parser/OscParser.ts +++ b/src/common/parser/OscParser.ts @@ -133,9 +133,11 @@ export class OscParser implements IOscParser { } // do nothing if command was faulty if (this._state !== OscState.ABORT) { - // if we are still in ID state and get an early end - // means that the command has no payload thus we still have - // to announce START and send END right after + /* + * if we are still in ID state and get an early end + * means that the command has no payload thus we still have + * to announce START and send END right after + */ if (this._state === OscState.ID) { this._start(); } @@ -166,9 +168,11 @@ export class OscParser implements IOscParser { } j--; } - // cleanup left over handlers - // we always have to call .end for proper cleanup, - // here we use `success` to indicate whether a handler should execute + /* + * cleanup left over handlers + * we always have to call .end for proper cleanup, + * here we use `success` to indicate whether a handler should execute + */ for (; j >= 0; j--) { handlerResult = this._active[j].end(false); if (handlerResult instanceof Promise) { @@ -222,8 +226,10 @@ export class OscHandler implements IOscHandler { } else if (success) { ret = this._handler(this._data); if (ret instanceof Promise) { - // need to hold data until `ret` got resolved - // dont care for errors, data will be freed anyway on next start + /* + * need to hold data until `ret` got resolved + * dont care for errors, data will be freed anyway on next start + */ return ret.then(res => { this._data = ''; this._hitLimit = false; diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index 06ac6b10a..ecf71eaf8 100644 --- a/src/common/services/BufferService.ts +++ b/src/common/services/BufferService.ts @@ -103,22 +103,28 @@ export class BufferService extends Disposable implements IBufferService { buffer.ydisp++; } } else { - // When the buffer is full and the user has scrolled up, keep the text - // stable unless ydisp is right at the top + /* + * When the buffer is full and the user has scrolled up, keep the text + * stable unless ydisp is right at the top + */ if (this.isUserScrolling) { buffer.ydisp = Math.max(buffer.ydisp - 1, 0); } } } else { - // scrollTop is non-zero which means no line will be going to the - // scrollback, instead we can just shift them in-place. + /* + * scrollTop is non-zero which means no line will be going to the + * scrollback, instead we can just shift them in-place. + */ const scrollRegionHeight = bottomRow - topRow + 1 /* as it's zero-based */; buffer.lines.shiftElements(topRow + 1, scrollRegionHeight - 1, -1); buffer.lines.set(bottomRow, newLine.clone()); } - // Move the viewport to the bottom of the buffer unless the user is - // scrolling. + /* + * Move the viewport to the bottom of the buffer unless the user is + * scrolling. + */ if (!this.isUserScrolling) { buffer.ydisp = buffer.ybase; } diff --git a/src/common/services/InstantiationService.ts b/src/common/services/InstantiationService.ts index 7e7695485..f11ff7f6e 100644 --- a/src/common/services/InstantiationService.ts +++ b/src/common/services/InstantiationService.ts @@ -4,10 +4,12 @@ * * This was heavily inspired from microsoft/vscode's dependency injection system (MIT). */ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { IInstantiationService, IServiceIdentifier } from 'common/services/Services'; import { getServiceDependencies } from 'common/services/ServiceRegistry'; diff --git a/src/common/services/LogService.ts b/src/common/services/LogService.ts index 8bc1e0aaa..eea09f47d 100644 --- a/src/common/services/LogService.ts +++ b/src/common/services/LogService.ts @@ -16,8 +16,10 @@ interface IConsole { warn: LogType; } -// console is available on both node.js and browser contexts but the common -// module doesn't depend on them so we need to explicitly declare it. +/* + * console is available on both node.js and browser contexts but the common + * module doesn't depend on them so we need to explicitly declare it. + */ declare const console: IConsole; const optionsKeyToLogLevel: { [key: string]: LogLevelEnum } = { diff --git a/src/common/services/MouseStateService.ts b/src/common/services/MouseStateService.ts index 21420460a..6316dbfc9 100644 --- a/src/common/services/MouseStateService.ts +++ b/src/common/services/MouseStateService.ts @@ -105,8 +105,10 @@ function eventCode(e: ICoreMouseEvent, isSGR: boolean): number { if (e.action === CoreMouseAction.MOVE) { code |= CoreMouseAction.MOVE; } else if (e.action === CoreMouseAction.UP && !isSGR) { - // special case - only SGR can report button on release - // all others have to go with NONE + /* + * special case - only SGR can report button on release + * all others have to go with NONE + */ code |= CoreMouseButton.NONE; } } @@ -126,10 +128,12 @@ const DEFAULT_ENCODINGS: { [key: string]: CoreMouseEncoding } = { */ DEFAULT: (e: ICoreMouseEvent) => { const params = [eventCode(e, false) + 32, e.col + 32, e.row + 32]; - // supress mouse report if we exceed addressible range - // Note this is handled differently by emulators - // - xterm: sends 0;0 coords instead - // - vte, konsole: no report + /* + * supress mouse report if we exceed addressible range + * Note this is handled differently by emulators + * - xterm: sends 0;0 coords instead + * - vte, konsole: no report + */ if (params[0] > 255 || params[1] > 255 || params[2] > 255) { return ''; } diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index ec647aaa5..252b38ef9 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -90,8 +90,10 @@ export class OptionsService extends Disposable implements IOptionsService { this.options = { ... defaultOptions }; this._setupOptions(); - // Clear out options that could link outside xterm.js as they could easily cause an embedder - // memory leak + /* + * Clear out options that could link outside xterm.js as they could easily cause an embedder + * memory leak + */ this._register(toDisposable(() => { this.rawOptions.linkHandler = null; this.rawOptions.documentOverride = null; diff --git a/src/common/services/ServiceRegistry.ts b/src/common/services/ServiceRegistry.ts index af51d1641..fef07d54c 100644 --- a/src/common/services/ServiceRegistry.ts +++ b/src/common/services/ServiceRegistry.ts @@ -4,10 +4,12 @@ * * This was heavily inspired from microsoft/vscode's dependency injection system (MIT). */ -/*--------------------------------------------------------------------------------------------- +/* + *--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ + *-------------------------------------------------------------------------------------------- + */ import { IServiceIdentifier } from 'common/services/Services'; diff --git a/src/common/services/UnicodeService.ts b/src/common/services/UnicodeService.ts index d0dd8584d..a04bf00a1 100644 --- a/src/common/services/UnicodeService.ts +++ b/src/common/services/UnicodeService.ts @@ -78,16 +78,20 @@ export class UnicodeService implements IUnicodeService { // surrogate pair first if (0xD800 <= code && code <= 0xDBFF) { if (++i >= length) { - // this should not happen with strings retrieved from - // Buffer.translateToString as it converts from UTF-32 - // and therefore always should contain the second part - // for any other string we still have to handle it somehow: - // simply treat the lonely surrogate first as a single char (UCS-2 behavior) + /* + * this should not happen with strings retrieved from + * Buffer.translateToString as it converts from UTF-32 + * and therefore always should contain the second part + * for any other string we still have to handle it somehow: + * simply treat the lonely surrogate first as a single char (UCS-2 behavior) + */ return result + this.wcwidth(code); } const second = s.charCodeAt(i); - // convert surrogate pair to high codepoint only for valid second part (UTF-16) - // otherwise treat them independently (UCS-2 behavior) + /* + * convert surrogate pair to high codepoint only for valid second part (UTF-16) + * otherwise treat them independently (UCS-2 behavior) + */ if (0xDC00 <= second && second <= 0xDFFF) { code = (code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; } else { diff --git a/src/headless/public/Terminal.ts b/src/headless/public/Terminal.ts index 0d4e6e93b..d9d604917 100644 --- a/src/headless/public/Terminal.ts +++ b/src/headless/public/Terminal.ts @@ -58,9 +58,11 @@ export class Terminal extends Disposable implements ITerminalApi { } private _checkReadonlyOptions(propName: string): void { - // Throw an error if any constructor only option is modified - // from terminal.options - // Modifications from anywhere else are allowed + /* + * Throw an error if any constructor only option is modified + * from terminal.options + * Modifications from anywhere else are allowed + */ if (CONSTRUCTOR_ONLY_OPTIONS.includes(propName)) { throw new Error(`Option "${propName}" can only be set in the constructor`); } diff --git a/test/playwright/InputHandler.test.ts b/test/playwright/InputHandler.test.ts index eec2938a8..451346f3a 100644 --- a/test/playwright/InputHandler.test.ts +++ b/test/playwright/InputHandler.test.ts @@ -227,9 +227,11 @@ test.describe('InputHandler Integration Tests', () => { await ctx.proxy.write('\x1b[2S'); await pollFor(ctx.page, () => getLinesAsArray(5), ['3', '4', '5', '', '']); }); - // This is intentionally not implemented here are image support lives within an addon - // test.skip('CSI ? Pi ; Pa ; Pv S - XTSMGRAPHICS: Set or request graphics attribute, xterm', async () => { - // }); + /* + * This is intentionally not implemented here are image support lives within an addon + * test.skip('CSI ? Pi ; Pa ; Pv S - XTSMGRAPHICS: Set or request graphics attribute, xterm', async () => { + * }); + */ test('CSI Ps T - SD: Scroll down Ps lines (default = 1), VT420', async () => { await ctx.proxy.write('1\r\n2\r\n3\r\n4\r\n5'); await pollFor(ctx.page, () => getLinesAsArray(5), ['1', '2', '3', '4', '5']); @@ -349,8 +351,10 @@ test.describe('InputHandler Integration Tests', () => { await pollFor(ctx.page, () => getLinesAsArray(3), ['aoo', '', ' b']); }); test('CSI Ps g - TBC: Tab Clear (default = 0)', async () => { - // Default: Clear tab stop at cursor position - // Move to column 9 (first tab stop), clear it, go back to column 1, tab should skip to column 17 + /* + * Default: Clear tab stop at cursor position + * Move to column 9 (first tab stop), clear it, go back to column 1, tab should skip to column 17 + */ await ctx.proxy.write('\x1b[9G\x1b[g\x1b[1G\ta'); await pollFor(ctx.page, () => getLinesAsArray(1), [' a']); // Ps=3: Clear all tab stops @@ -523,8 +527,10 @@ test.describe('InputHandler Integration Tests', () => { test('Ps = 6 9 - Enable left and right margin mode (DECLRMM), VT420 and up', async () => { await assertNoModeChange('\x1b[?69h'); }); - // test.skip('Ps = 8 0 - Enable Sixel Display Mode (DECSDM), VT330, VT340, VT382', async () => { - // }); + /* + * test.skip('Ps = 8 0 - Enable Sixel Display Mode (DECSDM), VT330, VT340, VT382', async () => { + * }); + */ test('Ps = 9 5 - Do not clear screen when DECCOLM is set/reset (DECNCSM), VT510 and up', async () => { await assertNoModeChange('\x1b[?95h'); }); @@ -881,9 +887,11 @@ test.describe('InputHandler Integration Tests', () => { test('Ps = 6 9 - Disable left and right margin mode (DECLRMM), VT420 and up.', async () => { await assertNoModeChange('\x1b[?69l'); }); - // This is intentionally not implemented here are image support lives within an addon - // test.skip('Ps = 8 0 - Disable Sixel Display Mode (DECSDM), VT330, VT340, VT382. Turns on "Sixel Scrolling". See the section Sixel Graphics and mode 8 4 5 2 .', async () => { - // }); + /* + * This is intentionally not implemented here are image support lives within an addon + * test.skip('Ps = 8 0 - Disable Sixel Display Mode (DECSDM), VT330, VT340, VT382. Turns on "Sixel Scrolling". See the section Sixel Graphics and mode 8 4 5 2 .', async () => { + * }); + */ test('Ps = 9 5 - Clear screen when DECCOLM is set/reset (DECNCSM), VT510 and up.', async () => { await assertNoModeChange('\x1b[?95l'); }); diff --git a/test/playwright/MouseTracking.test.ts b/test/playwright/MouseTracking.test.ts index 355bac3ba..fc3a112e5 100644 --- a/test/playwright/MouseTracking.test.ts +++ b/test/playwright/MouseTracking.test.ts @@ -12,13 +12,17 @@ test.beforeAll(async ({ browser }) => { }); test.afterAll(async () => await ctx.page.close()); -// adjusted to work inside devcontainer -// see https://github.com/xtermjs/xterm.js/issues/2379 +/* + * adjusted to work inside devcontainer + * see https://github.com/xtermjs/xterm.js/issues/2379 + */ const width = 1280; const height = 960; -// adjust terminal row/col size so we can test -// >80 up to 223 and >255 +/* + * adjust terminal row/col size so we can test + * >80 up to 223 and >255 + */ const fontSize = 6; const cols = 260; const rows = 50; @@ -40,9 +44,11 @@ async function getReports(encoding: string): Promise { return reports.map((report: number[]) => parseReport(encoding, report)); } -// translate cell positions into pixel offset -// always adds +2 in each direction so we dont end up in the wrong cell -// due to rounding issues +/* + * translate cell positions into pixel offset + * always adds +2 in each direction so we dont end up in the wrong cell + * due to rounding issues + */ async function cellPos(col: number, row: number): Promise { const coords: any = await ctx.page.evaluate(` (function() { @@ -165,10 +171,12 @@ function parseReport(encoding: string, msg: number[]): { state: any, row: number test.describe('Mouse Tracking Tests', () => { test.beforeAll(async () => { await ctx.page.setViewportSize({ width, height }); - // patch terminal to get the onData calls - // we encode the msg here to an array of codes to not lose bytes - // (transmission strips non utf8 bytes) - // also resize so we can properly test the edge cases + /* + * patch terminal to get the onData calls + * we encode the msg here to an array of codes to not lose bytes + * (transmission strips non utf8 bytes) + * also resize so we can properly test the edge cases + */ await ctx.page.evaluate(` window.term.onData(e => window.calls.push( Array.from(e).map(el => el.charCodeAt(0)) )); window.term.onBinary(e => window.calls.push( Array.from(e).map(el => el.charCodeAt(0)) )); @@ -214,8 +222,10 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 51, row: 11, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - // test at max rows/cols - // capped at 223 (1-based) + /* + * test at max rows/cols + * capped at 223 (1-based) + */ await mouseMove(223 - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -227,24 +237,28 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), []); - // button press/move/release tests - // left button + /* + * button press/move/release tests + * left button + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); await mouseMove(44, 24); await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - // middle button - // bug: default action not cancelled (adds data to getReports from clipboard under X11) - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await mouseDown('middle'); - // await mouseMove(44, 24); - // await mouseUp('middle'); - // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - // right button - // bug: default action not cancelled (popup shown) + /* + * middle button + * bug: default action not cancelled (adds data to getReports from clipboard under X11) + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await mouseDown('middle'); + * await mouseMove(44, 24); + * await mouseUp('middle'); + * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + * right button + * bug: default action not cancelled (popup shown) + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -252,16 +266,20 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('right'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'right', modifier: { control: false, shift: false, meta: false } } }]); - // wheel - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await wheelUp(); - // await pollFor(ctx.page, () => getReports(encoding), []); - // await wheelDown(); - // await pollFor(ctx.page, () => getReports(encoding), []); + /* + * wheel + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await wheelUp(); + * await pollFor(ctx.page, () => getReports(encoding), []); + * await wheelDown(); + * await pollFor(ctx.page, () => getReports(encoding), []); + */ - // modifiers - // CTRL + /* + * modifiers + * CTRL + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -284,10 +302,12 @@ test.describe('Mouse Tracking Tests', () => { await ctx.page.keyboard.up('Alt'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - // SHIFT - // note: caught by selection manager - // bug? Why not caught by selection manger on macos? - // bug: no modifier reported + /* + * SHIFT + * note: caught by selection manager + * bug? Why not caught by selection manger on macos? + * bug: no modifier reported + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft @@ -304,8 +324,10 @@ test.describe('Mouse Tracking Tests', () => { ]); } - // all modifiers - // bug: Shift not working - reporting totally wrong coords and modifiers - selection manager again? + /* + * all modifiers + * bug: Shift not working - reporting totally wrong coords and modifiers - selection manager again? + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -351,24 +373,28 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), [{ col: cols, row: rows, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - // button press/move/release tests - // left button + /* + * button press/move/release tests + * left button + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); await mouseMove(44, 24); await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - // middle button - // bug: default action not cancelled (adds data to getReports from clipboard under X11) - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await mouseDown('middle'); - // await mouseMove(44, 24); - // await mouseUp('middle'); - // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - // right button - // bug: default action not cancelled (popup shown) + /* + * middle button + * bug: default action not cancelled (adds data to getReports from clipboard under X11) + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await mouseDown('middle'); + * await mouseMove(44, 24); + * await mouseUp('middle'); + * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + * right button + * bug: default action not cancelled (popup shown) + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -376,16 +402,20 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('right'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'right', modifier: { control: false, shift: false, meta: false } } }]); - // wheel - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await wheelUp(); - // await pollFor(ctx.page, () => getReports(encoding), []); - // await wheelDown(); - // await pollFor(ctx.page, () => getReports(encoding), []); + /* + * wheel + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await wheelUp(); + * await pollFor(ctx.page, () => getReports(encoding), []); + * await wheelDown(); + * await pollFor(ctx.page, () => getReports(encoding), []); + */ - // modifiers - // CTRL + /* + * modifiers + * CTRL + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -408,8 +438,10 @@ test.describe('Mouse Tracking Tests', () => { await ctx.page.keyboard.up('Alt'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - // SHIFT - // note: caught by selection manager + /* + * SHIFT + * note: caught by selection manager + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft @@ -426,8 +458,10 @@ test.describe('Mouse Tracking Tests', () => { ]); } - // all modifiers - // bug: Shift not working - reporting totally wrong coords and modifiers - selection manager again? + /* + * all modifiers + * bug: Shift not working - reporting totally wrong coords and modifiers - selection manager again? + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -483,8 +517,10 @@ test.describe('Mouse Tracking Tests', () => { { col: 51, row: 11, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // test at max rows/cols - // capped at 223 (1-based) + /* + * test at max rows/cols + * capped at 223 (1-based) + */ await mouseMove(223 - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -493,8 +529,10 @@ test.describe('Mouse Tracking Tests', () => { { col: 223, row: rows, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // button press/move/release tests - // left button + /* + * button press/move/release tests + * left button + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); @@ -504,16 +542,18 @@ test.describe('Mouse Tracking Tests', () => { { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // middle button - // bug: default action not cancelled (adds data to getReports from clipboard under X11) - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await mouseDown('middle'); - // await mouseMove(44, 24); - // await mouseUp('middle'); - // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - // right button - // bug: default action not cancelled (popup shown) + /* + * middle button + * bug: default action not cancelled (adds data to getReports from clipboard under X11) + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await mouseDown('middle'); + * await mouseMove(44, 24); + * await mouseUp('middle'); + * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + * right button + * bug: default action not cancelled (popup shown) + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -524,16 +564,20 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // wheel - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await wheelUp(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - // await wheelDown(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + /* + * wheel + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await wheelUp(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + * await wheelDown(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + */ - // modifiers - // CTRL + /* + * modifiers + * CTRL + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -563,31 +607,35 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - // SHIFT - // note: press/release caught by selection manager - // bug: modifier not reported for passed events - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft - // await mouseDown('left'); - // await mouseMove(44, 24); - // await mouseUp('left'); - // // await wheelDown(); - // await ctx.page.keyboard.up('Shift'); - // // if (noShift) { - // // await pollFor(ctx.page, () => getReports(encoding), [ - // // // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // // ]); - // // } else { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, - // // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // // } + /* + * SHIFT + * note: press/release caught by selection manager + * bug: modifier not reported for passed events + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft + * await mouseDown('left'); + * await mouseMove(44, 24); + * await mouseUp('left'); + * // await wheelDown(); + * await ctx.page.keyboard.up('Shift'); + * // if (noShift) { + * // await pollFor(ctx.page, () => getReports(encoding), [ + * // // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * // ]); + * // } else { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, + * // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * // } + */ - // all modifiers - // bug: Shift not working - selection manager? + /* + * all modifiers + * bug: Shift not working - selection manager? + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -647,8 +695,10 @@ test.describe('Mouse Tracking Tests', () => { { col: cols, row: rows, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - // button press/move/release tests - // left button + /* + * button press/move/release tests + * left button + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); @@ -658,16 +708,18 @@ test.describe('Mouse Tracking Tests', () => { { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - // middle button - // bug: default action not cancelled (adds data to getReports from clipboard under X11) - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await mouseDown('middle'); - // await mouseMove(44, 24); - // await mouseUp('middle'); - // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - // right button - // bug: default action not cancelled (popup shown) + /* + * middle button + * bug: default action not cancelled (adds data to getReports from clipboard under X11) + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await mouseDown('middle'); + * await mouseMove(44, 24); + * await mouseUp('middle'); + * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + * right button + * bug: default action not cancelled (popup shown) + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -678,16 +730,20 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: 'right', modifier: { control: false, shift: false, meta: false } } } ]); - // wheel - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await wheelUp(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - // await wheelDown(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + /* + * wheel + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await wheelUp(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + * await wheelDown(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + */ - // modifiers - // CTRL + /* + * modifiers + * CTRL + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -717,30 +773,34 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - // SHIFT - // note: press/release caught by selection manager - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft - // await mouseDown('left'); - // await mouseMove(44, 24); - // await mouseUp('left'); - // await wheelDown(); - // await ctx.page.keyboard.up('Shift'); - // if (noShift) { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } else { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } + /* + * SHIFT + * note: press/release caught by selection manager + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft + * await mouseDown('left'); + * await mouseMove(44, 24); + * await mouseUp('left'); + * await wheelDown(); + * await ctx.page.keyboard.up('Shift'); + * if (noShift) { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } else { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } + */ - // all modifiers - // bug: Shift not working - selection manager? + /* + * all modifiers + * bug: Shift not working - selection manager? + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -800,8 +860,10 @@ test.describe('Mouse Tracking Tests', () => { { col: 51, row: 11, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // test at max rows/cols - // capped at 223 (1-based) + /* + * test at max rows/cols + * capped at 223 (1-based) + */ await mouseMove(223 - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -810,8 +872,10 @@ test.describe('Mouse Tracking Tests', () => { { col: 223, row: rows, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // button press/move/release tests - // left button + /* + * button press/move/release tests + * left button + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); @@ -822,16 +886,18 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // middle button - // bug: default action not cancelled (adds data to getReports from clipboard under X11) - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await mouseDown('middle'); - // await mouseMove(44, 24); - // await mouseUp('middle'); - // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - // right button - // bug: default action not cancelled (popup shown) + /* + * middle button + * bug: default action not cancelled (adds data to getReports from clipboard under X11) + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await mouseDown('middle'); + * await mouseMove(44, 24); + * await mouseUp('middle'); + * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + * right button + * bug: default action not cancelled (popup shown) + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -843,16 +909,20 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // wheel - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await wheelUp(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - // await wheelDown(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + /* + * wheel + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await wheelUp(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + * await wheelDown(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + */ - // modifiers - // CTRL + /* + * modifiers + * CTRL + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -884,31 +954,35 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - // SHIFT - // note: press/release/drag caught by selection manager - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft - // await mouseDown('left'); - // await mouseMove(44, 24); - // await mouseUp('left'); - // await wheelDown(); - // await ctx.page.keyboard.up('Shift'); - // if (noShift) { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } else { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } + /* + * SHIFT + * note: press/release/drag caught by selection manager + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft + * await mouseDown('left'); + * await mouseMove(44, 24); + * await mouseUp('left'); + * await wheelDown(); + * await ctx.page.keyboard.up('Shift'); + * if (noShift) { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } else { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } + */ - // all modifiers - // bug: Shift not working + /* + * all modifiers + * bug: Shift not working + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -938,8 +1012,10 @@ test.describe('Mouse Tracking Tests', () => { await mouseMove(0, 0); await ctx.proxy.write('\x1b[?1002h\x1b[?1006h'); - // test at 0,0 - // bug: release is fired immediately + /* + * test at 0,0 + * bug: release is fired immediately + */ await mouseDown('left'); await pollFor(ctx.page, () => getReports(encoding), [ { col: 1, row: 1, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } } @@ -970,8 +1046,10 @@ test.describe('Mouse Tracking Tests', () => { { col: cols, row: rows, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - // button press/move/release tests - // left button + /* + * button press/move/release tests + * left button + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); @@ -982,16 +1060,18 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - // middle button - // bug: default action not cancelled (adds data to getReports from clipboard under X11) - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await mouseDown('middle'); - // await mouseMove(44, 24); - // await mouseUp('middle'); - // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - // right button - // bug: default action not cancelled (popup shown) + /* + * middle button + * bug: default action not cancelled (adds data to getReports from clipboard under X11) + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await mouseDown('middle'); + * await mouseMove(44, 24); + * await mouseUp('middle'); + * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + * right button + * bug: default action not cancelled (popup shown) + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -1003,16 +1083,20 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: 'right', modifier: { control: false, shift: false, meta: false } } } ]); - // wheel - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await wheelUp(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - // await wheelDown(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + /* + * wheel + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await wheelUp(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + * await wheelDown(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + */ - // modifiers - // CTRL + /* + * modifiers + * CTRL + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -1044,31 +1128,35 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - // SHIFT - // note: press/release/drag caught by selection manager - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft - // await mouseDown('left'); - // await mouseMove(44, 24); - // await mouseUp('left'); - // await wheelDown(); - // await ctx.page.keyboard.up('Shift'); - // if (noShift) { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } else { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } + /* + * SHIFT + * note: press/release/drag caught by selection manager + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft + * await mouseDown('left'); + * await mouseMove(44, 24); + * await mouseUp('left'); + * await wheelDown(); + * await ctx.page.keyboard.up('Shift'); + * if (noShift) { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } else { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } + */ - // all modifiers - // bug: this is totally broken with wrong coords and messed up modifiers + /* + * all modifiers + * bug: this is totally broken with wrong coords and messed up modifiers + */ await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -1128,8 +1216,10 @@ test.describe('Mouse Tracking Tests', () => { { col: 51, row: 11, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // test at max rows/cols - // capped at 223 (1-based) + /* + * test at max rows/cols + * capped at 223 (1-based) + */ await mouseMove(223 - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -1139,8 +1229,10 @@ test.describe('Mouse Tracking Tests', () => { { col: 223, row: rows, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // button press/move/release tests - // left button + /* + * button press/move/release tests + * left button + */ await mouseMove(43, 24); await mouseDown('left'); await mouseMove(44, 24); @@ -1151,16 +1243,18 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // middle button - // bug: default action not cancelled (adds data to getReports from clipboard under X11) - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await mouseDown('middle'); - // await mouseMove(44, 24); - // await mouseUp('middle'); - // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - // right button - // bug: default action not cancelled (popup shown) + /* + * middle button + * bug: default action not cancelled (adds data to getReports from clipboard under X11) + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await mouseDown('middle'); + * await mouseMove(44, 24); + * await mouseUp('middle'); + * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + * right button + * bug: default action not cancelled (popup shown) + */ await mouseMove(43, 24); await mouseDown('right'); await mouseMove(44, 24); @@ -1172,16 +1266,20 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - // wheel - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await wheelUp(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - // await wheelDown(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + /* + * wheel + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await wheelUp(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + * await wheelDown(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + */ - // modifiers - // CTRL + /* + * modifiers + * CTRL + */ await ctx.page.keyboard.down('Control'); await mouseMove(43, 24); await mouseDown('left'); @@ -1213,32 +1311,36 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - // SHIFT - // note: press/release/drag caught by selection manager - // await ctx.page.keyboard.down('Shift'); - // await mouseMove(43, 24); - // await mouseDown('left'); - // await mouseMove(44, 24); - // await mouseUp('left'); - // await wheelDown(); - // await ctx.page.keyboard.up('Shift'); - // if (noShift) { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } else { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, - // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } + /* + * SHIFT + * note: press/release/drag caught by selection manager + * await ctx.page.keyboard.down('Shift'); + * await mouseMove(43, 24); + * await mouseDown('left'); + * await mouseMove(44, 24); + * await mouseUp('left'); + * await wheelDown(); + * await ctx.page.keyboard.up('Shift'); + * if (noShift) { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } else { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, + * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } + */ - // all modifiers - // bug: Shift not working + /* + * all modifiers + * bug: Shift not working + */ await ctx.page.keyboard.down('Control'); await ctx.page.keyboard.down('Alt'); // await ctx.page.keyboard.down('Shift'); @@ -1292,9 +1394,11 @@ test.describe('Mouse Tracking Tests', () => { { col: 51, row: 11, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - // test at max rows/cols - // bug: we are capped at col 95 currently - // fix: allow values up to 223, any bigger should drop to 0 + /* + * test at max rows/cols + * bug: we are capped at col 95 currently + * fix: allow values up to 223, any bigger should drop to 0 + */ await mouseMove(cols - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -1304,8 +1408,10 @@ test.describe('Mouse Tracking Tests', () => { { col: cols, row: rows, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - // button press/move/release tests - // left button + /* + * button press/move/release tests + * left button + */ await mouseMove(43, 24); await mouseDown('left'); await mouseMove(44, 24); @@ -1316,16 +1422,18 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - // middle button - // bug: default action not cancelled (adds data to getReports from clipboard under X11) - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await mouseDown('middle'); - // await mouseMove(44, 24); - // await mouseUp('middle'); - // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - // right button - // bug: default action not cancelled (popup shown) + /* + * middle button + * bug: default action not cancelled (adds data to getReports from clipboard under X11) + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await mouseDown('middle'); + * await mouseMove(44, 24); + * await mouseUp('middle'); + * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + * right button + * bug: default action not cancelled (popup shown) + */ await mouseMove(43, 24); await mouseDown('right'); await mouseMove(44, 24); @@ -1337,16 +1445,20 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: 'right', modifier: { control: false, shift: false, meta: false } } } ]); - // wheel - // await mouseMove(43, 24); - // await getReports(encoding); // clear reports - // await wheelUp(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - // await wheelDown(); - // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + /* + * wheel + * await mouseMove(43, 24); + * await getReports(encoding); // clear reports + * await wheelUp(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + * await wheelDown(); + * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + */ - // modifiers - // CTRL + /* + * modifiers + * CTRL + */ await ctx.page.keyboard.down('Control'); await mouseMove(43, 24); await mouseDown('left'); @@ -1378,32 +1490,36 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - // SHIFT - // note: press/release/drag caught by selection manager - // await ctx.page.keyboard.down('Shift'); - // await mouseMove(43, 24); - // await mouseDown('left'); - // await mouseMove(44, 24); - // await mouseUp('left'); - // await wheelDown(); - // await ctx.page.keyboard.up('Shift'); - // if (noShift) { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } else { - // await pollFor(ctx.page, () => getReports(encoding), [ - // { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, - // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - // ]); - // } + /* + * SHIFT + * note: press/release/drag caught by selection manager + * await ctx.page.keyboard.down('Shift'); + * await mouseMove(43, 24); + * await mouseDown('left'); + * await mouseMove(44, 24); + * await mouseUp('left'); + * await wheelDown(); + * await ctx.page.keyboard.up('Shift'); + * if (noShift) { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } else { + * await pollFor(ctx.page, () => getReports(encoding), [ + * { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, + * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + * ]); + * } + */ - // all modifiers - // bug: Shift not working + /* + * all modifiers + * bug: Shift not working + */ await ctx.page.keyboard.down('Control'); await ctx.page.keyboard.down('Alt'); // await ctx.page.keyboard.down('Shift'); diff --git a/test/playwright/SharedRendererTests.ts b/test/playwright/SharedRendererTests.ts index db5c4c073..75b330abd 100644 --- a/test/playwright/SharedRendererTests.ts +++ b/test/playwright/SharedRendererTests.ts @@ -378,8 +378,10 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void 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 + /* + * It's difficult to assert the exact color due to rounding, just ensure the color differs + * to the regular color + */ await pollFor(ctx.value.page, async () => { const c = await getCellColor(ctx.value, x + 1, y + 1); return ( @@ -783,9 +785,11 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 2), [0xad, 0x7f, 0xa8, 255]); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 2), [0x34, 0xe2, 0xe2, 255]); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 2), [0xee, 0xee, 0xec, 255]); - // Setting and check for minimum contrast values, note that these are not - // exact to the contrast ratio, if the increase luminance algorithm - // changes then these will probably fail + /* + * Setting and check for minimum contrast values, note that these are not + * exact to the contrast ratio, if the increase luminance algorithm + * changes then these will probably fail + */ await ctx.value.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); frameDetails = undefined; await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [176, 180, 180, 255]); @@ -852,9 +856,11 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 2), [0xad, 0x7f, 0xa8, 255]); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 2), [0x34, 0xe2, 0xe2, 255]); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 2), [0xee, 0xee, 0xec, 255]); - // Setting and check for minimum contrast values, note that these are not - // exact to the contrast ratio, if the increase luminance algorithm - // changes then these will probably fail + /* + * Setting and check for minimum contrast values, note that these are not + * exact to the contrast ratio, if the increase luminance algorithm + * changes then these will probably fail + */ await ctx.value.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); frameDetails = undefined; await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [46, 52, 54, 255]); @@ -935,9 +941,11 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(6, 2), [Math.floor((255 + 0xad) / 2), Math.floor((255 + 0x7f) / 2), Math.floor((255 + 0xa8) / 2), 255]); await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(7, 2), [Math.floor((255 + 0x34) / 2), Math.floor((255 + 0xe2) / 2), Math.floor((255 + 0xe2) / 2), 255]); await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(8, 2), [Math.floor((255 + 0xee) / 2), Math.floor((255 + 0xee) / 2), Math.floor((255 + 0xec) / 2), 255]); - // Setting and check for minimum contrast values, note that these are not - // exact to the contrast ratio, if the increase luminance algorithm - // changes then these will probably fail + /* + * Setting and check for minimum contrast values, note that these are not + * exact to the contrast ratio, if the increase luminance algorithm + * changes then these will probably fail + */ await waitForRenderAfter(ctx.value, () => ctx.value.page.evaluate(`window.term.options.minimumContrastRatio = 10;`)); await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(1, 1), [150, 153, 154, 255]); await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(2, 1), [229, 127, 127, 255]); diff --git a/test/playwright/Terminal.test.ts b/test/playwright/Terminal.test.ts index 2d1133cce..387197c6d 100644 --- a/test/playwright/Terminal.test.ts +++ b/test/playwright/Terminal.test.ts @@ -837,8 +837,10 @@ test.describe('API Integration Tests', () => { test.describe('registerLinkProvider', () => { test('should fire provideLinks when hovering cells', async () => { await openTerminal(ctx); - // Focus the terminal as the cursor will show and trigger a rerender, which can clear the - // active link + /* + * Focus the terminal as the cursor will show and trigger a rerender, which can clear the + * active link + */ await ctx.proxy.focus(); await ctx.page.evaluate(` window.calls = []; @@ -859,8 +861,10 @@ test.describe('API Integration Tests', () => { test('should fire hover and leave events on the link', async () => { await openTerminal(ctx); - // Focus the terminal as the cursor will show and trigger a rerender, which can clear the - // active link + /* + * Focus the terminal as the cursor will show and trigger a rerender, which can clear the + * active link + */ await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('foo bar baz'); // Wait for renderer to catch up as links are cleared on render @@ -897,8 +901,10 @@ test.describe('API Integration Tests', () => { test('should work fine when hover and leave callbacks are not provided', async () => { await openTerminal(ctx); - // Focus the terminal as the cursor will show and trigger a rerender, which can clear the - // active link + /* + * Focus the terminal as the cursor will show and trigger a rerender, which can clear the + * active link + */ await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('foo bar baz'); // Wait for renderer to catch up as links are cleared on render @@ -940,8 +946,10 @@ test.describe('API Integration Tests', () => { test('should fire activate events when clicking the link', async () => { await openTerminal(ctx); - // Focus the terminal as the cursor will show and trigger a rerender, which can clear the - // active link + /* + * Focus the terminal as the cursor will show and trigger a rerender, which can clear the + * active link + */ await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('a b c'); // Wait for renderer to catch up as links are cleared on render @@ -982,8 +990,10 @@ test.describe('API Integration Tests', () => { test('should work when multiple links are provided on the same line', async () => { await openTerminal(ctx); - // Focus the terminal as the cursor will show and trigger a rerender, which can clear the - // active link + /* + * Focus the terminal as the cursor will show and trigger a rerender, which can clear the + * active link + */ await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('foo bar baz'); // Wait for renderer to catch up as links are cleared on render @@ -1031,8 +1041,10 @@ test.describe('API Integration Tests', () => { test('should dispose links when hovering away', async () => { await openTerminal(ctx); - // Focus the terminal as the cursor will show and trigger a rerender, which can clear the - // active link + /* + * Focus the terminal as the cursor will show and trigger a rerender, which can clear the + * active link + */ await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('foo bar baz'); // Wait for renderer to catch up as links are cleared on render diff --git a/test/playwright/TestUtils.ts b/test/playwright/TestUtils.ts index 8a76c8820..76a5eb200 100644 --- a/test/playwright/TestUtils.ts +++ b/test/playwright/TestUtils.ts @@ -459,8 +459,10 @@ export async function openTerminal( } catch {} } `); - // HACK: Tests may have side effects that could cause the terminal not to be removed. This - // assertion catches this case early. + /* + * HACK: Tests may have side effects that could cause the terminal not to be removed. This + * assertion catches this case early. + */ strictEqual(await ctx.page.evaluate(`document.querySelector('#terminal-container').children.length`), 0, 'there must be no terminals on the page'); let script = ` @@ -501,8 +503,10 @@ export async function openTerminal( await ctx.page.evaluate(script); - // HACK: This is a soft layer breaker that's temporarily included until unicode graphemes have - // more complete integration tests. See https://github.com/xtermjs/xterm.js/pull/4519#discussion_r1285234453 + /* + * HACK: This is a soft layer breaker that's temporarily included until unicode graphemes have + * more complete integration tests. See https://github.com/xtermjs/xterm.js/pull/4519#discussion_r1285234453 + */ if (testOptions.loadUnicodeGraphemesAddon) { await ctx.page.evaluate(` window.unicode = new UnicodeGraphemesAddon(); From df3422fb0bd193c895d0a25d6e6762d167ca700c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 26 May 2026 20:32:23 +0000 Subject: [PATCH 027/153] Use jsdoc/multiline-blocks instead of multiline-comment-style Replace multiline-comment-style (starred-block), which also forces multiline // comments into block comments, with jsdoc/multiline-blocks. That rule only enforces starred JSDoc layout for /** blocks (no text on the opening line) and leaves multiline // comments unchanged for internal documentation. Fix multiline JSDoc in Types.ts. Fixes #5899 Co-authored-by: Daniel Imms --- addons/addon-fit/test/FitAddon.test.ts | 32 +- addons/addon-image/src/IIPHeaderParser.ts | 6 +- addons/addon-image/src/IIPMetrics.ts | 6 +- addons/addon-image/src/ImageAddon.ts | 30 +- addons/addon-image/src/ImageRenderer.ts | 36 +- addons/addon-image/src/ImageStorage.ts | 48 +- addons/addon-image/src/SixelHandler.ts | 12 +- .../src/kitty/KittyGraphicsHandler.ts | 146 ++-- .../src/kitty/KittyGraphicsTypes.ts | 30 +- .../src/kitty/KittyImageStorage.ts | 46 +- addons/addon-image/test/ImageAddon.test.ts | 6 +- addons/addon-image/test/KittyGraphics.test.ts | 90 +- addons/addon-ligatures/src/font.ts | 16 +- .../src/fontLigatures/index.ts | 28 +- .../src/fontLigatures/merge.ts | 40 +- .../src/fontLigatures/mergeRange.ts | 30 +- .../src/fontLigatures/processors/6-2.ts | 6 +- .../src/fontLigatures/processors/6-3.ts | 6 +- .../src/fontLigatures/processors/helper.ts | 12 +- .../fontLigatures/processors/substitution.ts | 6 +- addons/addon-ligatures/src/index.ts | 24 +- addons/addon-ligatures/src/parse.ts | 18 +- addons/addon-search/src/SearchEngine.ts | 18 +- addons/addon-search/src/SearchLineCache.ts | 6 +- addons/addon-serialize/src/SerializeAddon.ts | 104 +-- .../test/SerializeAddon.test.ts | 28 +- .../src/UnicodeGraphemeProvider.ts | 8 +- .../test/WebFontsAddon.test.ts | 18 +- addons/addon-web-links/src/WebLinkProvider.ts | 18 +- addons/addon-web-links/src/WebLinksAddon.ts | 24 +- addons/addon-webgl/src/CellColorResolver.ts | 18 +- addons/addon-webgl/src/CharAtlasCache.ts | 6 +- addons/addon-webgl/src/CharAtlasUtils.ts | 6 +- addons/addon-webgl/src/Constants.ts | 8 +- .../src/CursorBlinkStateManager.ts | 22 +- addons/addon-webgl/src/DevicePixelObserver.ts | 10 +- addons/addon-webgl/src/GlyphRenderer.ts | 60 +- addons/addon-webgl/src/RectangleRenderer.ts | 8 +- addons/addon-webgl/src/TextureAtlas.ts | 206 ++--- addons/addon-webgl/src/WebglRenderer.ts | 164 ++-- .../customGlyphs/CustomGlyphDefinitions.ts | 68 +- .../src/customGlyphs/CustomGlyphRasterizer.ts | 18 +- addons/addon-webgl/test/WebglRenderer.test.ts | 8 +- demo/client/client.ts | 20 +- .../components/window/addonImageWindow.ts | 22 +- .../components/window/addonProgressWindow.ts | 12 +- demo/client/components/window/testWindow.ts | 76 +- demo/client/unicodeTable.ts | 6 +- demo/server/server.ts | 14 +- eslint.config.mjs | 2 +- src/browser/AccessibilityManager.ts | 20 +- src/browser/Clipboard.ts | 6 +- src/browser/CoreBrowserTerminal.ts | 140 ++- src/browser/Linkifier.ts | 40 +- src/browser/OscLinkProvider.ts | 12 +- src/browser/Terminal.test.ts | 24 +- src/browser/TimeBasedDebouncer.ts | 6 +- src/browser/Types.ts | 6 +- src/browser/Viewport.ts | 32 +- .../decorations/BufferDecorationRenderer.ts | 6 +- src/browser/decorations/ColorZoneStore.ts | 14 +- .../decorations/OverviewRulerRenderer.ts | 12 +- src/browser/input/CompositionHelper.test.ts | 8 +- src/browser/input/CompositionHelper.ts | 86 +- src/browser/input/Mouse.ts | 8 +- src/browser/public/Terminal.ts | 8 +- src/browser/renderer/dom/DomRenderer.ts | 22 +- .../dom/DomRendererRowFactory.test.ts | 6 +- .../renderer/dom/DomRendererRowFactory.ts | 76 +- src/browser/renderer/dom/WidthCache.ts | 20 +- .../renderer/shared/RendererUtils.test.ts | 18 +- src/browser/renderer/shared/RendererUtils.ts | 14 +- src/browser/scrollable/abstractScrollbar.ts | 6 +- src/browser/scrollable/fastDomNode.ts | 6 +- .../scrollable/globalPointerMoveMonitor.ts | 6 +- src/browser/scrollable/horizontalScrollbar.ts | 6 +- src/browser/scrollable/mouseEvent.ts | 6 +- src/browser/scrollable/scrollable.ts | 6 +- src/browser/scrollable/scrollableElement.ts | 6 +- .../scrollable/scrollableElementOptions.ts | 6 +- src/browser/scrollable/scrollbarArrow.ts | 6 +- src/browser/scrollable/scrollbarState.ts | 6 +- .../scrollbarVisibilityController.ts | 6 +- src/browser/scrollable/touch.ts | 6 +- src/browser/scrollable/verticalScrollbar.ts | 6 +- src/browser/scrollable/widget.ts | 6 +- src/browser/services/CharSizeService.ts | 6 +- .../services/CharacterJoinerService.ts | 94 +- src/browser/services/MouseService.test.ts | 6 +- src/browser/services/MouseService.ts | 38 +- src/browser/services/RenderService.ts | 34 +- src/browser/services/SelectionService.test.ts | 12 +- src/browser/services/SelectionService.ts | 158 ++-- src/common/CircularList.ts | 6 +- src/common/Color.ts | 34 +- src/common/CoreTerminal.ts | 6 +- src/common/InputHandler.test.ts | 12 +- src/common/InputHandler.ts | 324 +++---- src/common/Platform.ts | 22 +- src/common/SortedList.ts | 6 +- src/common/TaskQueue.ts | 20 +- src/common/Types.ts | 30 +- src/common/WindowsMode.ts | 22 +- src/common/buffer/Buffer.test.ts | 112 +-- src/common/buffer/Buffer.ts | 102 +-- src/common/buffer/BufferLine.test.ts | 34 +- src/common/buffer/BufferLine.ts | 34 +- src/common/buffer/BufferReflow.ts | 26 +- src/common/buffer/BufferSet.ts | 20 +- src/common/buffer/CellData.ts | 6 +- src/common/input/Keyboard.test.ts | 6 +- src/common/input/Keyboard.ts | 36 +- src/common/input/KittyKeyboard.test.ts | 12 +- src/common/input/KittyKeyboard.ts | 32 +- src/common/input/TextDecoder.ts | 12 +- src/common/input/UnicodeV6.test.ts | 32 +- src/common/input/UnicodeV6.ts | 18 +- src/common/input/Win32InputMode.ts | 14 +- src/common/input/WriteBuffer.ts | 64 +- src/common/input/XParseColor.ts | 8 +- src/common/parser/ApcParser.ts | 6 +- src/common/parser/DcsParser.ts | 16 +- .../parser/EscapeSequenceParser.test.ts | 18 +- src/common/parser/EscapeSequenceParser.ts | 82 +- src/common/parser/OscParser.ts | 22 +- src/common/services/BufferService.ts | 18 +- src/common/services/InstantiationService.ts | 6 +- src/common/services/LogService.ts | 6 +- src/common/services/MouseStateService.ts | 16 +- src/common/services/OptionsService.ts | 6 +- src/common/services/ServiceRegistry.ts | 6 +- src/common/services/UnicodeService.ts | 18 +- src/headless/public/Terminal.ts | 8 +- test/playwright/InputHandler.test.ts | 28 +- test/playwright/MouseTracking.test.ts | 810 ++++++++---------- test/playwright/SharedRendererTests.ts | 30 +- test/playwright/Terminal.test.ts | 36 +- test/playwright/TestUtils.ts | 12 +- 138 files changed, 1834 insertions(+), 3028 deletions(-) diff --git a/addons/addon-fit/test/FitAddon.test.ts b/addons/addon-fit/test/FitAddon.test.ts index d82d3640a..6a7c41fa8 100644 --- a/addons/addon-fit/test/FitAddon.test.ts +++ b/addons/addon-fit/test/FitAddon.test.ts @@ -26,24 +26,18 @@ test.describe('FitAddon', () => { `); }); - /* - * test.beforeEach(async function(): Promise { - * await ctx.page.evaluate(`document.querySelector('#terminal-container').style.display=''`); - * await openTerminal(page); - * }); - */ + // test.beforeEach(async function(): Promise { + // await ctx.page.evaluate(`document.querySelector('#terminal-container').style.display=''`); + // await openTerminal(page); + // }); - /* - * after(async () => { - * await browser.close(); - * }); - */ + // after(async () => { + // await browser.close(); + // }); - /* - * afterEach(async function(): Promise { - * await ctx.proxy.dispose(); - * }); - */ + // afterEach(async function(): Promise { + // await ctx.proxy.dispose(); + // }); test('no terminal', async function(): Promise { await ctx.page.evaluate(`window.fit2 = new FitAddon();`); @@ -85,10 +79,8 @@ test.describe('FitAddon', () => { await ctx.page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`); await setDimensions(); const dimensions: { cols: number, rows: number } | undefined = await ctx.page.evaluate(`window.fit.proposeDimensions()`); - /* - * The value of dims will be undefined if the char measure strategy falls back to the DOM - * method, so only assert if it's not undefined. - */ + // The value of dims will be undefined if the char measure strategy falls back to the DOM + // method, so only assert if it's not undefined. if (dimensions) { ok(dimensions.cols > 85); ok(dimensions.cols < 88); diff --git a/addons/addon-image/src/IIPHeaderParser.ts b/addons/addon-image/src/IIPHeaderParser.ts index 74e753289..af4e9a68d 100644 --- a/addons/addon-image/src/IIPHeaderParser.ts +++ b/addons/addon-image/src/IIPHeaderParser.ts @@ -24,10 +24,8 @@ export interface IHeaderFields { height?: string; // Optional, defaults to 1 respecting aspect ratio (width takes precedence). preserveAspectRatio?: number; - /* - * Optional, defaults to 0. If set to 1, the file will be displayed inline, else downloaded - * (download not supported). - */ + // Optional, defaults to 0. If set to 1, the file will be displayed inline, else downloaded + // (download not supported). inline?: number; } diff --git a/addons/addon-image/src/IIPMetrics.ts b/addons/addon-image/src/IIPMetrics.ts index 1937a2740..b377cf061 100644 --- a/addons/addon-image/src/IIPMetrics.ts +++ b/addons/addon-image/src/IIPMetrics.ts @@ -23,10 +23,8 @@ export function imageType(d: Uint8Array): IMetrics { return UNSUPPORTED_TYPE; } const d32 = new Uint32Array(d.buffer, d.byteOffset, 6); - /* - * PNG: 89 50 4E 47 0D 0A 1A 0A (8 first bytes == magic number for PNG) - * + first chunk must be IHDR - */ + // PNG: 89 50 4E 47 0D 0A 1A 0A (8 first bytes == magic number for PNG) + // + first chunk must be IHDR if (d32[0] === 0x474E5089 && d32[1] === 0x0A1A0A0D && d32[3] === 0x52444849) { return { mime: 'image/png', diff --git a/addons/addon-image/src/ImageAddon.ts b/addons/addon-image/src/ImageAddon.ts index 930a74bd8..7e0f29d3f 100644 --- a/addons/addon-image/src/ImageAddon.ts +++ b/addons/addon-image/src/ImageAddon.ts @@ -137,13 +137,11 @@ export class ImageAddon implements ITerminalAddon, IImageApi { // enable size reports if (this._opts.enableSizeReports) { - /* - * const windowOptions = terminal.getOption('windowOptions'); - * windowOptions.getWinSizePixels = true; - * windowOptions.getCellSizePixels = true; - * windowOptions.getWinSizeChars = true; - * terminal.setOption('windowOptions', windowOptions); - */ + // const windowOptions = terminal.getOption('windowOptions'); + // windowOptions.getWinSizePixels = true; + // windowOptions.getCellSizePixels = true; + // windowOptions.getWinSizeChars = true; + // terminal.setOption('windowOptions', windowOptions); const windowOps = terminal.options.windowOptions ?? {}; windowOps.getWinSizePixels = true; windowOps.getCellSizePixels = true; @@ -292,13 +290,11 @@ export class ImageAddon implements ITerminalAddon, IImageApi { if (params[0]) { return true; } - /* - * reported features: - * 62 - VT220 - * 4 - SIXEL support - * 9 - charsets - * 22 - ANSI colors - */ + // reported features: + // 62 - VT220 + // 4 - SIXEL support + // 9 - charsets + // 22 - ANSI colors if (this._opts.sixelSupport) { this._report(`\x1b[?62;4;9;22c`); return true; @@ -356,10 +352,8 @@ export class ImageAddon implements ITerminalAddon, IImageApi { let width = this._renderer?.dimensions?.css.canvas.width; let height = this._renderer?.dimensions?.css.canvas.height; if (!width || !height) { - /* - * for some reason we have no working image renderer - * --> fallback to default cell size - */ + // for some reason we have no working image renderer + // --> fallback to default cell size const cellSize = CELL_SIZE_DEFAULT; width = (this._terminal?.cols || 80) * cellSize.width; height = (this._terminal?.rows || 24) * cellSize.height; diff --git a/addons/addon-image/src/ImageRenderer.ts b/addons/addon-image/src/ImageRenderer.ts index b00002ca2..b36cdd004 100644 --- a/addons/addon-image/src/ImageRenderer.ts +++ b/addons/addon-image/src/ImageRenderer.ts @@ -197,12 +197,10 @@ export class ImageRenderer extends Disposable implements IDisposable { const finalWidth = count * width + sx > img.width ? img.width - sx : count * width; const finalHeight = sy + height > img.height ? img.height - sy : height; - /* - * Floor all pixel offsets to get stable tile mapping without any overflows. - * Note: For not pixel perfect aligned cells like in the DOM renderer - * this will move a tile slightly to the top/left (subpixel range, thus ignore it). - * FIX #34: avoid striping on displays with pixelDeviceRatio != 1 by ceiling height and width - */ + // Floor all pixel offsets to get stable tile mapping without any overflows. + // Note: For not pixel perfect aligned cells like in the DOM renderer + // this will move a tile slightly to the top/left (subpixel range, thus ignore it). + // FIX #34: avoid striping on displays with pixelDeviceRatio != 1 by ceiling height and width ctx.drawImage( img, Math.floor(sx), Math.floor(sy), Math.ceil(finalWidth), Math.ceil(finalHeight), @@ -344,27 +342,21 @@ export class ImageRenderer extends Disposable implements IDisposable { ); canvas.classList.add(`xterm-image-layer-${layer}`); const screenElement = this._terminal._core.screenElement; - /* - * Use isolation to create a stacking context without overriding z-index, - * which would conflict with integrators (e.g. VS Code) that set their - * own z-index on the screen element. - */ + // Use isolation to create a stacking context without overriding z-index, + // which would conflict with integrators (e.g. VS Code) that set their + // own z-index on the screen element. screenElement.style.isolation = 'isolate'; if (layer === 'bottom') { - /* - * Use z-index:-1 so it paints behind non-positioned text elements. - * The screen element needs to be a stacking context (via isolation) - * to contain the negative z-index, otherwise it would go behind the - * entire terminal. - */ + // Use z-index:-1 so it paints behind non-positioned text elements. + // The screen element needs to be a stacking context (via isolation) + // to contain the negative z-index, otherwise it would go behind the + // entire terminal. canvas.style.zIndex = '-1'; screenElement.insertBefore(canvas, screenElement.firstChild); } else { - /* - * Explicit z-index ensures the image canvas reliably stacks above - * the text layer (DOM renderer rows). z-index: 0 is below the - * selection overlay (z-index: 1). - */ + // Explicit z-index ensures the image canvas reliably stacks above + // the text layer (DOM renderer rows). z-index: 0 is below the + // selection overlay (z-index: 1). canvas.style.zIndex = '0'; screenElement.appendChild(canvas); } diff --git a/addons/addon-image/src/ImageStorage.ts b/addons/addon-image/src/ImageStorage.ts index 3e3a7c298..8c89a16b0 100644 --- a/addons/addon-image/src/ImageStorage.ts +++ b/addons/addon-image/src/ImageStorage.ts @@ -154,10 +154,8 @@ export class ImageStorage implements IDisposable { for (const spec of this._images.values()) { spec.marker?.dispose(); } - /* - * NOTE: marker.dispose above already calls ImageBitmap.close - * therefore we can just wipe the map here - */ + // NOTE: marker.dispose above already calls ImageBitmap.close + // therefore we can just wipe the map here this._images.clear(); this._renderer.clearAll(); } @@ -308,10 +306,8 @@ export class ImageStorage implements IDisposable { this._delImg(id); } - /* - * eviction marker: - * delete the image when the marker gets disposed - */ + // eviction marker: + // delete the image when the marker gets disposed const endMarker = this._terminal.registerMarker(0); endMarker?.onDispose(() => { const spec = this._images.get(imageId); @@ -320,10 +316,8 @@ export class ImageStorage implements IDisposable { } }); - /* - * since markers do not work on alternate for some reason, - * we evict images here manually - */ + // since markers do not work on alternate for some reason, + // we evict images here manually if (this._terminal.buffer.active.type === 'alternate') { this._evictOnAlternate(); } @@ -422,12 +416,10 @@ export class ImageStorage implements IDisposable { const drawCalls: { imgSpec: IImageSpec, tileId: number, col: number, row: number, count: number }[] = []; const placeholderCalls: { col: number, row: number, count: number }[] = []; - /* - * walk all cells in viewport and collect tiles found - * Note: We check _extendedAttrs directly (not just HAS_EXTENDED flag) - * because text writes clear the BG flag but leave image tile data intact. - * This lets top-layer images survive text overwrites (kitty C=1 behavior). - */ + // walk all cells in viewport and collect tiles found + // Note: We check _extendedAttrs directly (not just HAS_EXTENDED flag) + // because text writes clear the BG flag but leave image tile data intact. + // This lets top-layer images survive text overwrites (kitty C=1 behavior). for (let row = start; row <= end; ++row) { const line = buffer.lines.get(row + buffer.ydisp) as IBufferLineExt; if (!line) return; @@ -500,10 +492,8 @@ export class ImageStorage implements IDisposable { return; } - /* - * handle only viewport width enlargements, exit all other cases - * TODO: needs patch for tile counter - */ + // handle only viewport width enlargements, exit all other cases + // TODO: needs patch for tile counter if (this._viewportMetrics.cols >= metrics.cols) { this._viewportMetrics = metrics; return; @@ -591,10 +581,8 @@ export class ImageStorage implements IDisposable { } } - /* - * TODO: Do we need some blob offloading tricks here to avoid early eviction? - * also see https://stackoverflow.com/questions/28307789/is-there-any-limitation-on-javascript-max-blob-size - */ + // TODO: Do we need some blob offloading tricks here to avoid early eviction? + // also see https://stackoverflow.com/questions/28307789/is-there-any-limitation-on-javascript-max-blob-size private _evictOldest(room: number): number { const used = this._getStoredPixels(); let current = used; @@ -617,11 +605,9 @@ export class ImageStorage implements IDisposable { const old = line._extendedAttrs[x]; if (old) { if (old.imageId !== undefined) { - /* - * found an old ExtendedAttrsImage, since we know that - * they are always isolated instances (single cell usage), - * we can re-use it and just update their id entries - */ + // found an old ExtendedAttrsImage, since we know that + // they are always isolated instances (single cell usage), + // we can re-use it and just update their id entries const oldSpec = this._images.get(old.imageId); if (oldSpec) { // early eviction for in-viewport overwrites diff --git a/addons/addon-image/src/SixelHandler.ts b/addons/addon-image/src/SixelHandler.ts index da4798bf6..1af2d85bc 100644 --- a/addons/addon-image/src/SixelHandler.ts +++ b/addons/addon-image/src/SixelHandler.ts @@ -113,17 +113,13 @@ export class SixelHandler implements IDcsHandler, IResetHandler { * Some helpers to extract current terminal colors. */ -/* - * get currently active background color from terminal - * also respect INVERSE setting - */ +// get currently active background color from terminal +// also respect INVERSE setting function extractActiveBg(attr: AttributeData, colors: ReadonlyColorSet | undefined): RGBA8888 { let bg = 0; if (!colors) { - /* - * FIXME: theme service is prolly not available yet, - * happens if .open() was not called yet (bug in core?) - */ + // FIXME: theme service is prolly not available yet, + // happens if .open() was not called yet (bug in core?) return bg; } if (attr.isInverse()) { diff --git a/addons/addon-image/src/kitty/KittyGraphicsHandler.ts b/addons/addon-image/src/kitty/KittyGraphicsHandler.ts index 8ad8ef794..de889dfff 100644 --- a/addons/addon-image/src/kitty/KittyGraphicsHandler.ts +++ b/addons/addon-image/src/kitty/KittyGraphicsHandler.ts @@ -62,11 +62,9 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos // Storage related states private _pendingTransmissions: Map = new Map(); - /* - * Tracks the pending key of the most recently started chunked upload. - * Per spec, subsequent chunks only need m= (and optionally q=), without i=. - * When a chunk arrives with no i=, this key is used to find the pending upload. - */ + // Tracks the pending key of the most recently started chunked upload. + // Per spec, subsequent chunks only need m= (and optionally q=), without i=. + // When a chunk arrives with no i=, this key is used to find the pending upload. private _lastPendingKey: number | undefined; constructor( @@ -175,11 +173,9 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos private _streamPayload(data: Uint32Array, start: number, end: number): void { if (this._aborted) return; - /* - * Check size limit (compare encoded bytes against pre-calculated limit) - * Include cumulative size from pending transmission for multi-chunk images. - * Per spec, subsequent chunks may omit i=, so fall back to _lastPendingKey. - */ + // Check size limit (compare encoded bytes against pre-calculated limit) + // Include cumulative size from pending transmission for multi-chunk images. + // Per spec, subsequent chunks may omit i=, so fall back to _lastPendingKey. const pendingKey = this._parsedCommand?.id ?? this._lastPendingKey ?? 0; const pending = this._pendingTransmissions.get(pendingKey); const previousEncodedSize = pending?.totalEncodedSize ?? 0; @@ -289,11 +285,9 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos } this._activeDecoder = null; - /* - * Handle command first — handlers create Blob/ImageData from imageBytes, - * which copies the data. Only then is it safe to release the decoder's - * wasm memory that imageBytes points into. - */ + // Handle command first — handlers create Blob/ImageData from imageBytes, + // which copies the data. Only then is it safe to release the decoder's + // wasm memory that imageBytes points into. const result = this._handleCommandWithBytesAndCmd(finalCmd, imageBytes, decodeError); if (decoder) { decoder.release(); @@ -331,12 +325,10 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos case KittyAction.PLACEMENT: return this._handlePlacement(cmd); default: - /* - * TODO: Implement remaining actions when needed: - * - a=f (frame): animation frame operations - * - a=a (animation): animation control - * - a=c (compose): compose images - */ + // TODO: Implement remaining actions when needed: + // - a=f (frame): animation frame operations + // - a=a (animation): animation control + // - a=c (compose): compose images if (cmd.id !== undefined) { this._sendResponse(cmd.id, 'EINVAL:unsupported action', cmd.quiet ?? 0); } @@ -350,10 +342,8 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos switch (action) { case KittyAction.TRANSMIT: { const result = this._handleTransmit(cmd, bytes, decodeError); - /* - * Only send response when _handleTransmit didn't already respond - * (it handles unsupported transmission medium responses internally) - */ + // Only send response when _handleTransmit didn't already respond + // (it handles unsupported transmission medium responses internally) if ((cmd.transmission ?? 'd') === 'd' && cmd.id !== undefined) { if (decodeError) { this._sendResponse(cmd.id, 'EINVAL:invalid base64 data', cmd.quiet ?? 0); @@ -371,12 +361,10 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos // a=p ignores any payload — image data was already transmitted return this._handlePlacement(cmd); default: - /* - * TODO: Implement remaining actions when needed: - * - a=f (frame): animation frame operations - * - a=a (animation): animation control - * - a=c (compose): compose images - */ + // TODO: Implement remaining actions when needed: + // - a=f (frame): animation frame operations + // - a=a (animation): animation control + // - a=c (compose): compose images if (cmd.id !== undefined) { this._sendResponse(cmd.id, 'EINVAL:unsupported action', cmd.quiet ?? 0); } @@ -402,18 +390,16 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos } private _handleTransmit(cmd: IKittyCommand, bytes: Uint8Array, decodeError: boolean): boolean { - /* - * TODO: Support file-based transmission modes (t=f, t=t, t=s) - * Currently only supports direct transmission (t=d, the default). - * - t=f (file): Payload is base64-encoded file path. Terminal reads image from that path. - * - t=t (temp file): Payload is base64-encoded path in temp directory. Terminal reads, deletes. - * - t=s: Payload is base64-encoded POSIX shm name. Terminal reads from shared memory. - * These modes require filesystem/IPC access not available in browsers. For Node.js/Electron: - * 1. Check cmd.transmission (t key) before treating bytes as image data - * 2. For t=f/t/s: decode bytes as UTF-8 string (the path/name), then read file contents - * 3. For t=d: treat bytes as image data (current behavior) - * When implementing, also update _handleQuery to accept these transmission mediums. - */ + // TODO: Support file-based transmission modes (t=f, t=t, t=s) + // Currently only supports direct transmission (t=d, the default). + // - t=f (file): Payload is base64-encoded file path. Terminal reads image from that path. + // - t=t (temp file): Payload is base64-encoded path in temp directory. Terminal reads, deletes. + // - t=s: Payload is base64-encoded POSIX shm name. Terminal reads from shared memory. + // These modes require filesystem/IPC access not available in browsers. For Node.js/Electron: + // 1. Check cmd.transmission (t key) before treating bytes as image data + // 2. For t=f/t/s: decode bytes as UTF-8 string (the path/name), then read file contents + // 3. For t=d: treat bytes as image data (current behavior) + // When implementing, also update _handleQuery to accept these transmission mediums. const transmission = cmd.transmission ?? 'd'; if (transmission !== 'd') { if (cmd.id !== undefined) { @@ -463,11 +449,9 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos const id = cmd.id ?? 0; const quiet = cmd.quiet ?? 0; - /* - * Per spec: reject unsupported transmission mediums (only t=d is supported atm) - * TODO: When filesystem support is added (Node.js/Electron), update this to accept - * t=f (file), t=t (temp file), and t=s (shared memory) and respond OK for queries. - */ + // Per spec: reject unsupported transmission mediums (only t=d is supported atm) + // TODO: When filesystem support is added (Node.js/Electron), update this to accept + // t=f (file), t=t (temp file), and t=s (shared memory) and respond OK for queries. const transmission = cmd.transmission ?? 'd'; if (transmission !== 'd') { this._sendResponse(id, 'EINVAL:unsupported transmission medium', quiet); @@ -516,11 +500,9 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos // Per spec: default delete selector is 'a' (delete all visible placements) const selector = cmd.deleteSelector ?? 'a'; - /* - * TODO: Distinguish lowercase (delete placements only) from uppercase - * (delete placements + free stored image data). Currently both variants - * free everything since we don't separate stored data from placements. - */ + // TODO: Distinguish lowercase (delete placements only) from uppercase + // (delete placements + free stored image data). Currently both variants + // free everything since we don't separate stored data from placements. switch (selector) { case 'a': case 'A': @@ -529,12 +511,10 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos break; case 'i': case 'I': - /* - * TODO: When placement id tracking is implemented (see TODO in - * KittyImageStorage), d=i with p= should delete only that - * specific placement, while d=i without p should delete all - * placements for the image. - */ + // TODO: When placement id tracking is implemented (see TODO in + // KittyImageStorage), d=i with p= should delete only that + // specific placement, while d=i without p should delete all + // placements for the image. if (cmd.id !== undefined) { const pending = this._pendingTransmissions.get(cmd.id); if (pending) { @@ -596,10 +576,8 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos const cw = this._renderer.dimensions?.css.cell.width || CELL_SIZE_DEFAULT.width; const ch = this._renderer.dimensions?.css.cell.height || CELL_SIZE_DEFAULT.height; - /* - * Per spec: c/r default to image's natural cell dimensions. - * If only one of c/r is specified, compute the other from image aspect ratio. - */ + // Per spec: c/r default to image's natural cell dimensions. + // If only one of c/r is specified, compute the other from image aspect ratio. let imgCols: number; let imgRows: number; if (cmd.columns !== undefined && cmd.rows !== undefined) { @@ -635,12 +613,10 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos const savedY = buffer.y; const savedYbase = buffer.ybase; - /* - * Determine layer based on z-index: negative = behind text, 0+ = on top. - * When z<0 we always use the bottom layer even without allowTransparency — - * the image will simply be hidden behind the opaque text background, which - * is the correct behavior (client asked for "behind text"). - */ + // Determine layer based on z-index: negative = behind text, 0+ = on top. + // When z<0 we always use the bottom layer even without allowTransparency — + // the image will simply be hidden behind the opaque text background, which + // is the correct behavior (client asked for "behind text"). const wantsBottom = cmd.zIndex !== undefined && cmd.zIndex < 0; const layer: ImageLayer = wantsBottom ? 'bottom' : 'top'; @@ -654,12 +630,10 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos const xOffset = Math.min(Math.max(0, cmd.xOffset ?? 0), cw - 1); const yOffset = Math.min(Math.max(0, cmd.yOffset ?? 0), ch - 1); if (xOffset !== 0 || yOffset !== 0) { - /* - * Per spec: X/Y is not added to c/r area. When c/r are explicit, the - * total placement area remains c*cw × r*ch pixels and the offset image - * is clipped to fit. When c/r are unset, the padded canvas determines - * the natural cell dimensions. - */ + // Per spec: X/Y is not added to c/r area. When c/r are explicit, the + // total placement area remains c*cw × r*ch pixels and the offset image + // is clipped to fit. When c/r are unset, the padded canvas determines + // the natural cell dimensions. const canvasW = (cmd.columns !== undefined) ? Math.round(imgCols * cw) : bitmap.width + xOffset; const canvasH = (cmd.rows !== undefined) ? Math.round(imgRows * ch) : bitmap.height + yOffset; const offsetCanvas = ImageRenderer.createCanvas(window.document, canvasW, canvasH); @@ -690,11 +664,9 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos this._kittyStorage.addImage(image.id, bitmap, true, layer, zIndex); bitmap = undefined; // ownership transferred to storage - /* - * Kitty cursor movement - * Per spec: cursor placed at first column after last image column, - * on the last row of the image. C=1 means don't move cursor. - */ + // Kitty cursor movement + // Per spec: cursor placed at first column after last image column, + // on the last row of the image. C=1 means don't move cursor. if (cmd.cursorMovement === 1) { // C=1: restore cursor to position before image was placed const scrolled = buffer.ybase - savedYbase; @@ -702,10 +674,8 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos // Can't restore cursor to scrollback? buffer.y = Math.max(savedY - scrolled, 0); } else { - /* - * Default (C=0): advance cursor horizontally past the image - * addImage already positioned cursor on the last row via lineFeeds - */ + // Default (C=0): advance cursor horizontally past the image + // addImage already positioned cursor on the last row via lineFeeds buffer.x = Math.min(savedX + imgCols, this._coreTerminal.cols); } } catch (e) { @@ -766,11 +736,9 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos return createImageBitmap(new ImageData(new Uint8ClampedArray(bytes.buffer as ArrayBuffer, bytes.byteOffset, pixelCount * KittyPixelConstants.BYTES_PER_PIXEL_RGBA), width, height)); } - /* - * RGB→RGBA: interleave alpha using uint32 block processing (4 pixels per iteration). - * 3 uint32 reads + 4 uint32 writes per 4 pixels vs 28 byte reads/writes — ~6x faster. - * Assumes little-endian (all modern browsers/Node.js). - */ + // RGB→RGBA: interleave alpha using uint32 block processing (4 pixels per iteration). + // 3 uint32 reads + 4 uint32 writes per 4 pixels vs 28 byte reads/writes — ~6x faster. + // Assumes little-endian (all modern browsers/Node.js). const data = new Uint8ClampedArray(pixelCount * KittyPixelConstants.BYTES_PER_PIXEL_RGBA); const src32 = new Uint32Array(bytes.buffer, bytes.byteOffset, Math.floor(bytes.byteLength / 4)); const dst32 = new Uint32Array(data.buffer); diff --git a/addons/addon-image/src/kitty/KittyGraphicsTypes.ts b/addons/addon-image/src/kitty/KittyGraphicsTypes.ts index da94675d8..448ebcc05 100644 --- a/addons/addon-image/src/kitty/KittyGraphicsTypes.ts +++ b/addons/addon-image/src/kitty/KittyGraphicsTypes.ts @@ -7,10 +7,8 @@ import type Base64Decoder from 'xterm-wasm-parts/lib/base64/Base64Decoder.wasm'; -/* - * Kitty graphics protocol action types. - * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'a'. - */ +// Kitty graphics protocol action types. +// See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'a'. export const enum KittyAction { TRANSMIT = 't', TRANSMIT_DISPLAY = 'T', @@ -19,29 +17,23 @@ export const enum KittyAction { DELETE = 'd' } -/* - * Kitty graphics protocol format types. - * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference - */ +// Kitty graphics protocol format types. +// See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference export const enum KittyFormat { RGB = 24, RGBA = 32, PNG = 100 } -/* - * Kitty graphics protocol compression types. - * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'o'. - */ +// Kitty graphics protocol compression types. +// See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'o'. export const enum KittyCompression { NONE = '', ZLIB = 'z' } -/* - * Kitty graphics protocol control data keys. - * See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference - */ +// Kitty graphics protocol control data keys. +// See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference export const enum KittyKey { // Action to perform (t=transmit, T=transmit+display, q=query, p=placement, d=delete) ACTION = 'a', @@ -123,10 +115,8 @@ export interface IKittyCommand { payload?: string; } -/* - * Pending chunked transmission state. - * Stores metadata from the first chunk while accumulating decoded payload data. - */ +// Pending chunked transmission state. +// Stores metadata from the first chunk while accumulating decoded payload data. export interface IPendingTransmission { // The parsed command from the first chunk (contains action, format, dimensions, etc.) cmd: IKittyCommand; diff --git a/addons/addon-image/src/kitty/KittyImageStorage.ts b/addons/addon-image/src/kitty/KittyImageStorage.ts index 766806532..95ea594c7 100644 --- a/addons/addon-image/src/kitty/KittyImageStorage.ts +++ b/addons/addon-image/src/kitty/KittyImageStorage.ts @@ -8,31 +8,27 @@ import { ImageStorage } from '../ImageStorage'; import { ImageLayer } from '../Types'; import { IKittyImageData } from './KittyGraphicsTypes'; -/* - * Kitty-specific image storage controller. - * - * Wraps shared ImageStorage with kitty protocol semantics: - * - tracks transmitted image payloads by kitty image id - * - tracks kitty image id -> shared ImageStorage id mapping for displayed images - * - mirrors shared-storage evictions into kitty maps - * - applies protocol-level undisplayed-image eviction policy - */ +// Kitty-specific image storage controller. +// +// Wraps shared ImageStorage with kitty protocol semantics: +// - tracks transmitted image payloads by kitty image id +// - tracks kitty image id -> shared ImageStorage id mapping for displayed images +// - mirrors shared-storage evictions into kitty maps +// - applies protocol-level undisplayed-image eviction policy export class KittyImageStorage implements IDisposable { private static readonly _maxStoredImages = 256; private _nextImageId = 1; private readonly _images: Map = new Map(); - /* - * TODO: Support multiple placements per image. The kitty spec identifies - * placements by an (image id, placement id) pair — same i + different p - * values should coexist, and same i + same p should replace the prior - * placement. Currently we track only one storage entry per kitty image id, - * so multiple placements of the same image overwrite each other. Fixing - * this requires changing these maps to Map> - * (kittyId → placementId → storageId) and updating addImage/deleteById - * accordingly. The underlying shared ImageStorage would also need to - * support multiple entries per logical image. - */ + // TODO: Support multiple placements per image. The kitty spec identifies + // placements by an (image id, placement id) pair — same i + different p + // values should coexist, and same i + same p should replace the prior + // placement. Currently we track only one storage entry per kitty image id, + // so multiple placements of the same image overwrite each other. Fixing + // this requires changing these maps to Map> + // (kittyId → placementId → storageId) and updating addImage/deleteById + // accordingly. The underlying shared ImageStorage would also need to + // support multiple entries per logical image. private readonly _kittyIdToStorageId: Map = new Map(); private readonly _storageIdToKittyId: Map = new Map(); @@ -94,12 +90,10 @@ export class KittyImageStorage implements IDisposable { } public addImage(kittyId: number, image: HTMLCanvasElement | ImageBitmap, scrolling: boolean, layer: ImageLayer, zIndex: number): void { - /* - * Clean up stale reverse-mapping from a previous placement of the same - * kitty image. The old shared-storage entry is kept (it may still be - * visible on screen) but its reverse mapping is removed so that eviction - * of the old entry won't incorrectly delete the kitty image data. - */ + // Clean up stale reverse-mapping from a previous placement of the same + // kitty image. The old shared-storage entry is kept (it may still be + // visible on screen) but its reverse mapping is removed so that eviction + // of the old entry won't incorrectly delete the kitty image data. const oldStorageId = this._kittyIdToStorageId.get(kittyId); if (oldStorageId !== undefined) { this._storageIdToKittyId.delete(oldStorageId); diff --git a/addons/addon-image/test/ImageAddon.test.ts b/addons/addon-image/test/ImageAddon.test.ts index 9db26cd7f..dc729ec63 100644 --- a/addons/addon-image/test/ImageAddon.test.ts +++ b/addons/addon-image/test/ImageAddon.test.ts @@ -64,10 +64,8 @@ const TESTDATA: ITestData = (() => { }; })(); const SIXEL_SEQ_0 = introducer(0) + TESTDATA.sixel + FINALIZER; -/* - * const SIXEL_SEQ_1 = introducer(1) + TESTDATA.sixel + FINALIZER; - * const SIXEL_SEQ_2 = introducer(2) + TESTDATA.sixel + FINALIZER; - */ +// const SIXEL_SEQ_1 = introducer(1) + TESTDATA.sixel + FINALIZER; +// const SIXEL_SEQ_2 = introducer(2) + TESTDATA.sixel + FINALIZER; // NOTE: the data is loaded as string for easier transport through playwright const TESTDATA_IIP: [string, [number, number]][] = [ diff --git a/addons/addon-image/test/KittyGraphics.test.ts b/addons/addon-image/test/KittyGraphics.test.ts index 2822cce6b..a9b5d6845 100644 --- a/addons/addon-image/test/KittyGraphics.test.ts +++ b/addons/addon-image/test/KittyGraphics.test.ts @@ -101,14 +101,12 @@ test.beforeAll(async ({ browser }) => { test.afterAll(async () => await ctx.page.close()); test.describe('Kitty Graphics Protocol', () => { - /* - * TODO: Add tests for larger images with various dimensions - * TODO: Add tests for virtual placement (U=1) - * TODO: Add tests for animation frames - * TODO: Add performance tests for streaming large images - * TODO: Implement cursor movement per Kitty spec - cursor should move by cols/rows after placement (unless C=1) - * TODO: Distinguish lowercase delete selectors (placement only) from uppercase (placement + free data) - */ + // TODO: Add tests for larger images with various dimensions + // TODO: Add tests for virtual placement (U=1) + // TODO: Add tests for animation frames + // TODO: Add performance tests for streaming large images + // TODO: Implement cursor movement per Kitty spec - cursor should move by cols/rows after placement (unless C=1) + // TODO: Distinguish lowercase delete selectors (placement only) from uppercase (placement + free data) test.beforeEach(async ({}, testInfo) => { await ctx.page.evaluate(` @@ -211,10 +209,8 @@ test.describe('Kitty Graphics Protocol', () => { }); test('enforces size limit across chunked transmissions', async () => { - /* - * Create a custom addon with very small size limit (100 bytes) - * The 1x1 PNG is ~164 bytes base64, so 2 chunks should exceed 100 - */ + // Create a custom addon with very small size limit (100 bytes) + // The 1x1 PNG is ~164 bytes base64, so 2 chunks should exceed 100 await ctx.page.evaluate(() => { (window as any).smallLimitAddon = new ImageAddon({ kittySupport: true, @@ -1283,10 +1279,8 @@ test.describe('Kitty Graphics Protocol', () => { }); test('only c specified computes r from aspect ratio', async () => { - /* - * 200x100 image (2:1 aspect) with c=10. - * Per spec: r = ceil((h/w) * c * cw / ch) = ceil(0.5 * 10 * cw / ch) - */ + // 200x100 image (2:1 aspect) with c=10. + // Per spec: r = ceil((h/w) * c * cw / ch) = ceil(0.5 * 10 * cw / ch) await ctx.proxy.write(`\x1b_Ga=t,f=100,i=225;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(100); @@ -1303,10 +1297,8 @@ test.describe('Kitty Graphics Protocol', () => { }); test('only r specified computes c from aspect ratio', async () => { - /* - * 200x100 image (2:1 aspect) with r=5. - * Per spec: c = ceil((w/h) * r * ch / cw) = ceil(2 * 5 * ch / cw) - */ + // 200x100 image (2:1 aspect) with r=5. + // Per spec: c = ceil((w/h) * r * ch / cw) = ceil(2 * 5 * ch / cw) await ctx.proxy.write(`\x1b_Ga=t,f=100,i=226;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(100); @@ -1324,11 +1316,9 @@ test.describe('Kitty Graphics Protocol', () => { }); test.describe('Cursor positioning', () => { - /* - * NOTE: Current tests document ACTUAL behavior (MVP - cursor doesn't move) - * Per Kitty spec: cursor placed at first column after last image column, - * on the last row of the image. C=1 means don't move cursor. - */ + // NOTE: Current tests document ACTUAL behavior (MVP - cursor doesn't move) + // Per Kitty spec: cursor placed at first column after last image column, + // on the last row of the image. C=1 means don't move cursor. test('cursor advances past 1x1 image', async () => { const cursorBefore = await getCursor(); @@ -1386,10 +1376,8 @@ test.describe('Kitty Graphics Protocol', () => { }); test('cursor should move right by cols when c specified', async () => { - /* - * c=5, r computed from 1x1 aspect ratio: r = ceil((1/1) * (5*cw) / ch) - * With 1:1 aspect, image height in pixels = 5*cw, so r = ceil(5*cw/ch) - */ + // c=5, r computed from 1x1 aspect ratio: r = ceil((1/1) * (5*cw) / ch) + // With 1:1 aspect, image height in pixels = 5*cw, so r = ceil(5*cw/ch) await ctx.proxy.write(`\x1b_Ga=T,f=100,c=5;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); @@ -1646,12 +1634,10 @@ test.describe('Kitty Graphics Protocol', () => { }); test.describe('Pixel verification', () => { - /* - * The 200x100 image has 20 colored rectangles in a 10x2 grid. - * Each rectangle is 20px wide x 50px tall. - * Top row (y=0..49): Red, Orange, Yellow, Lime, Green, Cyan, SkyBlue, Blue, Purple, Magenta - * Bottom row (y=50..99): Pink, Brown, Maroon, Olive, Teal, Navy, Gray, DarkGray, LightGray, White - */ + // The 200x100 image has 20 colored rectangles in a 10x2 grid. + // Each rectangle is 20px wide x 50px tall. + // Top row (y=0..49): Red, Orange, Yellow, Lime, Green, Cyan, SkyBlue, Blue, Purple, Magenta + // Bottom row (y=50..99): Pink, Brown, Maroon, Olive, Teal, Navy, Gray, DarkGray, LightGray, White test('renders red rectangle at top-left origin (0,0)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); @@ -1664,11 +1650,9 @@ test.describe('Kitty Graphics Protocol', () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); - /* - * Sample center of each top-row rectangle (y=25, x=10,30,50,...,190) - * All within the first cell row, so we read from the canvas at cell (0,0) - * Red at x=10 - */ + // Sample center of each top-row rectangle (y=25, x=10,30,50,...,190) + // All within the first cell row, so we read from the canvas at cell (0,0) + // Red at x=10 deepStrictEqual(await getPixel(0, 0, 10, 25), [255, 0, 0, 255]); // Orange at x=30 deepStrictEqual(await getPixel(0, 0, 30, 25), [255, 128, 0, 255]); @@ -1684,10 +1668,8 @@ test.describe('Kitty Graphics Protocol', () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); - /* - * Bottom row starts at y=50. Center at y=75. - * Pink at x=10 - */ + // Bottom row starts at y=50. Center at y=75. + // Pink at x=10 deepStrictEqual(await getPixel(0, 0, 10, 75), [255, 192, 203, 255]); // Brown at x=30 deepStrictEqual(await getPixel(0, 0, 30, 75), [165, 42, 42, 255]); @@ -1743,10 +1725,8 @@ test.describe('Kitty Graphics Protocol', () => { }); test('scales cropped source region to c/r placement rectangle', async () => { - /* - * Firefox's createImageBitmap uses different resize sampling, producing - * slightly off pixel values compared to Chromium, so skip on Firefox. - */ + // Firefox's createImageBitmap uses different resize sampling, producing + // slightly off pixel values compared to Chromium, so skip on Firefox. if (ctx.browser.browserType().name() === 'firefox') { test.skip(); } @@ -2157,10 +2137,8 @@ test.describe('Kitty Graphics Protocol', () => { }); test('memory limit eviction cleans Kitty handler maps', async () => { - /* - * Resize terminal to fit 7 non-overlapping 200x100 images without scrolling. - * Each image ≈ 29 cols × 8 rows at default cell size. - */ + // Resize terminal to fit 7 non-overlapping 200x100 images without scrolling. + // Each image ≈ 29 cols × 8 rows at default cell size. await ctx.page.evaluate(` window.term.reset(); window.imageAddon?.dispose(); @@ -2169,11 +2147,9 @@ test.describe('Kitty Graphics Protocol', () => { window.term.loadAddon(window.imageAddon); `); - /* - * storageLimit 0.5 MB = 125,000 pixels. Each 200x100 image = 20,000 pixels. - * 6 images = 120K pixels (under limit). 7th triggers eviction (140K > 125K). - * Place non-overlapping so tile-count eviction doesn't interfere. - */ + // storageLimit 0.5 MB = 125,000 pixels. Each 200x100 image = 20,000 pixels. + // 6 images = 120K pixels (under limit). 7th triggers eviction (140K > 125K). + // Place non-overlapping so tile-count eviction doesn't interfere. const positions = [[1, 1], [30, 1], [1, 9], [30, 9], [1, 17], [30, 17]]; for (let n = 0; n < 6; n++) { const [c, r] = positions[n]; diff --git a/addons/addon-ligatures/src/font.ts b/addons/addon-ligatures/src/font.ts index 8e5f93dd5..60ab3d746 100644 --- a/addons/addon-ligatures/src/font.ts +++ b/addons/addon-ligatures/src/font.ts @@ -43,11 +43,9 @@ export default async function load(fontFamily: string, cacheSize: number): Promi throw new Error('Permission to access local fonts not granted.'); } } catch (err: unknown) { - /* - * A `TypeError` indicates the 'local-fonts' - * permission is not yet implemented, so - * only `throw` if this is _not_ the problem. - */ + // A `TypeError` indicates the 'local-fonts' + // permission is not yet implemented, so + // only `throw` if this is _not_ the problem. if (err instanceof Error && err.name !== 'TypeError') { throw err; } @@ -91,11 +89,9 @@ export default async function load(fontFamily: string, cacheSize: number): Promi const fonts = await fontsPromise; for (const family of parse(fontFamily)) { - /* - * If we reach one of the generic font families, the font resolution - * will end for the browser and we can't determine the specific font - * used. Throw. - */ + // If we reach one of the generic font families, the font resolution + // will end for the browser and we can't determine the specific font + // used. Throw. if (genericFontFamilies.includes(family)) { return undefined; } diff --git a/addons/addon-ligatures/src/fontLigatures/index.ts b/addons/addon-ligatures/src/fontLigatures/index.ts index 794b39ecc..d65e73bc4 100644 --- a/addons/addon-ligatures/src/fontLigatures/index.ts +++ b/addons/addon-ligatures/src/fontLigatures/index.ts @@ -88,11 +88,9 @@ class FontImpl implements IFont { glyphIds.push(this._font.charToGlyphIndex(char)); } - /* - * If there are no lookup groups, there's no point looking for - * replacements. This gives us a minor performance boost for fonts with - * no ligatures - */ + // If there are no lookup groups, there's no point looking for + // replacements. This gives us a minor performance boost for fonts with + // no ligatures if (this._lookupTrees.length === 0) { return { inputGlyphs: glyphIds, @@ -115,10 +113,8 @@ class FontImpl implements IFont { } public findLigatureRanges(text: string): [number, number][] { - /* - * Short circuit the process if there are no possible ligatures in the - * font - */ + // Short circuit the process if there are no possible ligatures in the + // font if (this._lookupTrees.length === 0) { return []; } @@ -174,11 +170,9 @@ class FontImpl implements IFont { } } } else { - /* - * We don't need to do the lastGlyphIndex tracking here because - * reverse processing isn't allowed to replace more than one - * character at a time. - */ + // We don't need to do the lastGlyphIndex tracking here because + // reverse processing isn't allowed to replace more than one + // character at a time. for (let i = nextLookup.last - 1; i >= nextLookup.first; i--) { const result = walkTree(lookup.tree, sequence, i, i); if (result) { @@ -230,10 +224,8 @@ class FontImpl implements IFont { for (let j = 0; j < lookups.length; j++) { const lookupIndex = lookups[j]; if (lookupIndex >= start) { - /* - * Update the lookup information if it's the one we're - * storing or earlier than it. - */ + // Update the lookup information if it's the one we're + // storing or earlier than it. if (result.index === null || lookupIndex <= result.index) { result.index = lookupIndex; diff --git a/addons/addon-ligatures/src/fontLigatures/merge.ts b/addons/addon-ligatures/src/fontLigatures/merge.ts index a16f748a9..262ea85ae 100644 --- a/addons/addon-ligatures/src/fontLigatures/merge.ts +++ b/addons/addon-ligatures/src/fontLigatures/merge.ts @@ -32,11 +32,9 @@ export default function mergeTrees(trees: ILookupTree[]): ILookupTree { function mergeSubtree(mainTree: ILookupTree, mergeTree: ILookupTree, mergedEntries: WeakMap>): void { // Need to fix this recursively (and handle lookups) for (const [glyphId, value] of Object.entries(mergeTree.individual)) { - /* - * The main tree is guaranteed to have no overlaps between the - * individual and range values, so if we match an invididual, there - * must not be a range - */ + // The main tree is guaranteed to have no overlaps between the + // individual and range values, so if we match an invididual, there + // must not be a range if (mainTree.individual[glyphId]) { mergeTreeEntry(mainTree.individual[glyphId], value, mergedEntries); } else { @@ -51,17 +49,13 @@ function mergeSubtree(mainTree: ILookupTree, mergeTree: ILookupTree, mergedEntri matched = true; - /* - * If they overlap, we have to split the range and then - * merge the overlap - */ + // If they overlap, we have to split the range and then + // merge the overlap mainTree.individual[glyphId] = value; mergeTreeEntry(mainTree.individual[glyphId], cloneEntry(entry), mergedEntries); - /* - * When there's an overlap, we also have to fix up the range - * that we had already processed - */ + // When there's an overlap, we also have to fix up the range + // that we had already processed mainTree.range.splice(index, 1); for (const glyph of overlap.second) { if (Array.isArray(glyph)) { @@ -82,11 +76,9 @@ function mergeSubtree(mainTree: ILookupTree, mergeTree: ILookupTree, mergedEntri } for (const { range, entry } of mergeTree.range) { - /* - * Ranges are more complicated, because they can overlap with - * multiple things, individual and range alike. We start by - * eliminating ranges that are already present in another range - */ + // Ranges are more complicated, because they can overlap with + // multiple things, individual and range alike. We start by + // eliminating ranges that are already present in another range let remainingRanges: (number | [number, number])[] = [range]; for (let index = 0; index < mainTree.range.length; index++) { @@ -131,17 +123,13 @@ function mergeSubtree(mainTree: ILookupTree, mergeTree: ILookupTree, mergedEntri continue; } - /* - * If they overlap, we have to split the range and then - * merge the overlap - */ + // If they overlap, we have to split the range and then + // merge the overlap mainTree.individual[remainingRange] = cloneEntry(entry); mergeTreeEntry(mainTree.individual[remainingRange], cloneEntry(resultEntry), mergedEntries); - /* - * When there's an overlap, we also have to fix up the range - * that we had already processed - */ + // When there's an overlap, we also have to fix up the range + // that we had already processed mainTree.range.splice(index, 1); index--; diff --git a/addons/addon-ligatures/src/fontLigatures/mergeRange.ts b/addons/addon-ligatures/src/fontLigatures/mergeRange.ts index 8058e077f..ec5305083 100644 --- a/addons/addon-ligatures/src/fontLigatures/mergeRange.ts +++ b/addons/addon-ligatures/src/fontLigatures/mergeRange.ts @@ -18,18 +18,14 @@ export default function mergeRange(ranges: [number, number][], newRangeStart: nu return ranges; } if (newRangeEnd <= range[1]) { - /* - * Case 2: New range is either wholly contained within the - * search range or overlaps with the front of it - */ + // Case 2: New range is either wholly contained within the + // search range or overlaps with the front of it range[0] = Math.min(newRangeStart, range[0]); return ranges; } if (newRangeStart < range[1]) { - /* - * Case 3: New range either wholly contains the search range - * or overlaps with the end of it - */ + // Case 3: New range either wholly contains the search range + // or overlaps with the end of it range[0] = Math.min(newRangeStart, range[0]); inRange = true; } else { @@ -38,27 +34,21 @@ export default function mergeRange(ranges: [number, number][], newRangeStart: nu } } else { if (newRangeEnd <= range[0]) { - /* - * Case 5: New range extends from previous range but doesn't - * reach the current one - */ + // Case 5: New range extends from previous range but doesn't + // reach the current one ranges[i - 1][1] = newRangeEnd; return ranges; } if (newRangeEnd <= range[1]) { - /* - * Case 6: New range extends from prvious range into the - * current range - */ + // Case 6: New range extends from prvious range into the + // current range ranges[i - 1][1] = Math.max(newRangeEnd, range[1]); ranges.splice(i, 1); inRange = false; return ranges; } - /* - * Case 7: New range extends from previous range past the - * end of the current range - */ + // Case 7: New range extends from previous range past the + // end of the current range ranges.splice(i, 1); i--; } diff --git a/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts b/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts index 890188508..f39682423 100644 --- a/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts +++ b/addons/addon-ligatures/src/fontLigatures/processors/6-2.ts @@ -72,10 +72,8 @@ export default function buildTree(table: ChainingContextualSubstitutionTable.IFo ); } - /* - * When we get to the end, all of the entries we've accumulated - * should have a lookup defined - */ + // When we get to the end, all of the entries we've accumulated + // should have a lookup defined for (const { entry, substitutions } of currentEntries) { entry.lookup = { substitutions, diff --git a/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts b/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts index 2f8290824..334e6a566 100644 --- a/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts +++ b/addons/addon-ligatures/src/fontLigatures/processors/6-3.ts @@ -53,10 +53,8 @@ export default function buildTree(table: ChainingContextualSubstitutionTable.IFo ); } - /* - * When we get to the end, all of the entries we've accumulated - * should have a lookup defined - */ + // When we get to the end, all of the entries we've accumulated + // should have a lookup defined for (const { entry, substitutions } of currentEntries) { entry.lookup = { substitutions, diff --git a/addons/addon-ligatures/src/fontLigatures/processors/helper.ts b/addons/addon-ligatures/src/fontLigatures/processors/helper.ts index 6bb6060d1..e6259b6d4 100644 --- a/addons/addon-ligatures/src/fontLigatures/processors/helper.ts +++ b/addons/addon-ligatures/src/fontLigatures/processors/helper.ts @@ -57,10 +57,8 @@ export function processLookaheadPosition( range: [] }; - /* - * All glyphs at this position share ONE entry - lookahead just needs to match, - * all paths lead to the same result - */ + // All glyphs at this position share ONE entry - lookahead just needs to match, + // all paths lead to the same result const sharedEntry: ILookupTreeEntry = {}; for (const glyph of glyphs) { @@ -102,10 +100,8 @@ export function processBacktrackPosition( range: [] }; - /* - * All glyphs at this position share ONE entry - backtrack just needs to match, - * all paths lead to the same result - */ + // All glyphs at this position share ONE entry - backtrack just needs to match, + // all paths lead to the same result const sharedEntry: ILookupTreeEntry = {}; for (const glyph of glyphs) { diff --git a/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts b/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts index 7e461b87c..3a41e936c 100644 --- a/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts +++ b/addons/addon-ligatures/src/fontLigatures/processors/substitution.ts @@ -49,10 +49,8 @@ export function getIndividualSubstitutionGlyph(table: SubstitutionTable, glyphId switch (table.substFormat) { // https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#11-single-substitution-format-1 case 1: - /* - * TODO: determine if there's a rhyme or reason to the 16-bit - * wraparound and if it can ever be a different number - */ + // TODO: determine if there's a rhyme or reason to the 16-bit + // wraparound and if it can ever be a different number return (glyphId + table.deltaGlyphId) % (2 ** 16); // https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#12-single-substitution-format-2 case 2: diff --git a/addons/addon-ligatures/src/index.ts b/addons/addon-ligatures/src/index.ts index 33990b773..fb8e85e73 100644 --- a/addons/addon-ligatures/src/index.ts +++ b/addons/addon-ligatures/src/index.ts @@ -15,10 +15,8 @@ const enum LoadingState { FAILED } -/* - * Caches 100K characters worth of ligatures. In practice this works out to - * about 650 KB worth of cache, when a moderate number of ligatures are present. - */ +// Caches 100K characters worth of ligatures. In practice this works out to +// about 650 KB worth of cache, when a moderate number of ligatures are present. const CACHE_SIZE = 100000; /** @@ -48,10 +46,8 @@ export function enableLigatures(term: Terminal, fallbackLigatures: string[] = [] load(currentCallFontName, CACHE_SIZE) .then(f => { - /* - * Another request may have come in while we were waiting, so make - * sure our font is still vaild. - */ + // Another request may have come in while we were waiting, so make + // sure our font is still vaild. if (currentCallFontName === term.options.fontFamily) { loadingState = LoadingState.LOADED; font = f; @@ -63,10 +59,8 @@ export function enableLigatures(term: Terminal, fallbackLigatures: string[] = [] } }) .catch(e => { - /* - * Another request may have come in while we were waiting, so make - * sure our font is still vaild. - */ + // Another request may have come in while we were waiting, so make + // sure our font is still vaild. if (currentCallFontName === term.options.fontFamily) { loadingState = LoadingState.FAILED; if (term.options.logLevel === 'debug') { @@ -79,10 +73,8 @@ export function enableLigatures(term: Terminal, fallbackLigatures: string[] = [] } if (font && loadingState === LoadingState.LOADED) { - /* - * We clone the entries to avoid the internal cache of the ligature finder - * getting messed up. - */ + // We clone the entries to avoid the internal cache of the ligature finder + // getting messed up. return font.findLigatureRanges(text).map<[number, number]>( range => [range[0], range[1]] ); diff --git a/addons/addon-ligatures/src/parse.ts b/addons/addon-ligatures/src/parse.ts index 7cf7ad2f8..6289e4681 100644 --- a/addons/addon-ligatures/src/parse.ts +++ b/addons/addon-ligatures/src/parse.ts @@ -27,10 +27,8 @@ export default function parse(family: string): string[] { const families = []; let currentFamily = ''; - /* - * Work through the input character by character until there are none left. - * This lexing and parsing in one pass. - */ + // Work through the input character by character until there are none left. + // This lexing and parsing in one pass. while (context.offset < context.input.length) { const char = context.input[context.offset++]; switch (char) { @@ -154,17 +152,13 @@ function parseUnicode(context: IParseContext): string { while (context.offset < context.input.length) { const char = context.input[context.offset++]; if (/\s/.test(char)) { - /* - * The first whitespace character after a unicode escape indicates the end - * of the escape and is swallowed. - */ + // The first whitespace character after a unicode escape indicates the end + // of the escape and is swallowed. return unicodeToString(str); } if (str.length >= 6 || !/[\dA-Fa-f]/.test(char)) { - /* - * If the next character is not a valid hex digit or we have reached the - * maximum of 6 digits in the escape, terminate the escape. - */ + // If the next character is not a valid hex digit or we have reached the + // maximum of 6 digits in the escape, terminate the escape. context.offset--; return unicodeToString(str); } diff --git a/addons/addon-search/src/SearchEngine.ts b/addons/addon-search/src/SearchEngine.ts index cbff3eb8e..b9991974d 100644 --- a/addons/addon-search/src/SearchEngine.ts +++ b/addons/addon-search/src/SearchEngine.ts @@ -261,10 +261,8 @@ export class SearchEngine { return; } - /* - * This will iterate until we find the line start. - * When we find it, we will search using the calculated start column. - */ + // This will iterate until we find the line start. + // When we find it, we will search using the calculated start column. searchPosition.startRow--; searchPosition.startCol += this._terminal.cols; return this._findInLine(term, searchPosition, searchOptions); @@ -317,10 +315,8 @@ export class SearchEngine { return; } - /* - * Adjust the row number and search index if needed since a "line" of text can span multiple - * rows - */ + // Adjust the row number and search index if needed since a "line" of text can span multiple + // rows let startRowOffset = 0; while (startRowOffset < offsets.length - 1 && resultIndex >= offsets[startRowOffset + 1]) { startRowOffset++; @@ -359,10 +355,8 @@ export class SearchEngine { if (char.length > 1) { offset -= char.length - 1; } - /* - * Adjust the searchIndex for empty characters following wide unicode - * chars (eg. CJK) - */ + // Adjust the searchIndex for empty characters following wide unicode + // chars (eg. CJK) const nextCell = line.getCell(i + 1); if (nextCell && nextCell.getWidth() === 0) { offset++; diff --git a/addons/addon-search/src/SearchLineCache.ts b/addons/addon-search/src/SearchLineCache.ts index d62f47cd8..526f4bfcc 100644 --- a/addons/addon-search/src/SearchLineCache.ts +++ b/addons/addon-search/src/SearchLineCache.ts @@ -38,10 +38,8 @@ export class SearchLineCache extends Disposable { private _linesCache: LineCacheEntry[] | undefined; private _linesCacheTimeout = this._register(new MutableDisposable()); private _linesCacheDisposables = this._register(new MutableDisposable()); - /* - * Track access to avoid recreating a timeout on every init call which occurs once per search - * result (findNext/findPrevious -> _highlightAllMatches -> find loop). - */ + // Track access to avoid recreating a timeout on every init call which occurs once per search + // result (findNext/findPrevious -> _highlightAllMatches -> find loop). private _lastAccessTimestamp = 0; constructor(private readonly _terminal: Terminal) { diff --git a/addons/addon-serialize/src/SerializeAddon.ts b/addons/addon-serialize/src/SerializeAddon.ts index 1d46fd918..4980c8eda 100644 --- a/addons/addon-serialize/src/SerializeAddon.ts +++ b/addons/addon-serialize/src/SerializeAddon.ts @@ -84,10 +84,8 @@ function equalBg(cell1: IBufferCell | IAttributeData, cell2: IBufferCell): boole } function equalUnderline(cell1: IBufferCell | IAttributeData, cell2: IBufferCell): boolean { - /* - * If neither cell has underline, consider them equal regardless of internal underline color - * values - */ + // If neither cell has underline, consider them equal regardless of internal underline color + // values if (!cell1.isUnderline() && !cell2.isUnderline()) { return true; } @@ -136,18 +134,14 @@ class StringSerializeHandler extends BaseSerializeHandler { private _currentRow: string = ''; private _nullCellCount: number = 0; - /* - * we can see a full colored cell and a null cell that only have background the same style - * but the information isn't preserved by null cell itself - * so wee need to record it when required. - */ + // we can see a full colored cell and a null cell that only have background the same style + // but the information isn't preserved by null cell itself + // so wee need to record it when required. private _cursorStyle: IBufferCell = this._buffer.getNullCell(); - /* - * where exact the cursor styles comes from - * because we can't copy the cell directly - * so we remember where the content comes from instead - */ + // where exact the cursor styles comes from + // because we can't copy the cell directly + // so we remember where the content comes from instead private _cursorStyleRow: number = 0; private _cursorStyleCol: number = 0; @@ -178,10 +172,8 @@ class StringSerializeHandler extends BaseSerializeHandler { private _thisRowLastSecondChar: IBufferCell = this._buffer.getNullCell(); private _nextRowFirstChar: IBufferCell = this._buffer.getNullCell(); protected _rowEnd(row: number, isLastRow: boolean): void { - /* - * if there is colorful empty cell at line end, whe must pad it back, or the the color block - * will missing - */ + // if there is colorful empty cell at line end, whe must pad it back, or the the color block + // will missing if (this._nullCellCount > 0 && !equalBg(this._cursorStyle, this._backgroundCell)) { // use clear right to set background. this._currentRow += `\u001b[${this._nullCellCount}X`; @@ -214,10 +206,8 @@ class StringSerializeHandler extends BaseSerializeHandler { const nextRowFirstChar = nextLine.getCell(0, this._nextRowFirstChar)!; const isNextRowFirstCharDoubleWidth = nextRowFirstChar.getWidth() > 1; - /* - * validate whether this line wrap is ever possible - * which mean whether cursor can placed at a overflow position (x === row) naturally - */ + // validate whether this line wrap is ever possible + // which mean whether cursor can placed at a overflow position (x === row) naturally let isValid = false; if ( @@ -226,31 +216,23 @@ class StringSerializeHandler extends BaseSerializeHandler { isNextRowFirstCharDoubleWidth ? this._nullCellCount <= 1 : this._nullCellCount <= 0 ) { if ( - /* - * the last character can't be null, - * you can't use control sequence to move cursor to (x === row) - */ + // the last character can't be null, + // you can't use control sequence to move cursor to (x === row) (thisRowLastChar.getChars() || thisRowLastChar.getWidth() === 0) && - /* - * change background of the first wrapped cell also affects BCE - * so we mark it as invalid to simply the process to determine line separator - */ + // change background of the first wrapped cell also affects BCE + // so we mark it as invalid to simply the process to determine line separator equalBg(thisRowLastChar, nextRowFirstChar) ) { isValid = true; } if ( - /* - * the second to last character can't be null if the next line starts with CJK, - * you can't use control sequence to move cursor to (x === row) - */ + // the second to last character can't be null if the next line starts with CJK, + // you can't use control sequence to move cursor to (x === row) isNextRowFirstCharDoubleWidth && (thisRowLastSecondChar.getChars() || thisRowLastSecondChar.getWidth() === 0) && - /* - * change background of the first wrapped cell also affects BCE - * so we mark it as invalid to simply the process to determine line separator - */ + // change background of the first wrapped cell also affects BCE + // so we mark it as invalid to simply the process to determine line separator equalBg(thisRowLastChar, nextRowFirstChar) && equalBg(thisRowLastSecondChar, nextRowFirstChar) ) { @@ -259,10 +241,8 @@ class StringSerializeHandler extends BaseSerializeHandler { } if (!isValid) { - /* - * force the wrap with magic - * insert enough character to force the wrap - */ + // force the wrap with magic + // insert enough character to force the wrap rowSeparator = '-'.repeat(this._nullCellCount + 1); // move back and erase next line head rowSeparator += '\u001b[1D\u001b[1X'; @@ -276,10 +256,8 @@ class StringSerializeHandler extends BaseSerializeHandler { rowSeparator += '\u001b[B'; } - /* - * This is content and need the be serialized even it is invisible. - * without this, wrap will be missing from outputs. - */ + // This is content and need the be serialized even it is invisible. + // without this, wrap will be missing from outputs. this._lastContentCursorRow = row + 1; this._lastContentCursorCol = 0; @@ -380,10 +358,8 @@ class StringSerializeHandler extends BaseSerializeHandler { const sgrSeq = this._diffStyle(cell, this._cursorStyle); - /* - * the empty cell style is only assumed to be changed when background changed, because - * foreground is always 0. - */ + // the empty cell style is only assumed to be changed when background changed, because + // foreground is always 0. const styleChanged = isEmptyCell ? !equalBg(this._cursorStyle, cell) : sgrSeq.length > 0; /** @@ -422,11 +398,9 @@ class StringSerializeHandler extends BaseSerializeHandler { this._nullCellCount += cell.getWidth(); } else { if (this._nullCellCount > 0) { - /* - * we can just assume we have same style with previous one here - * because style change is handled by previous stage - * use move right when background is empty, use clear right when there is background. - */ + // we can just assume we have same style with previous one here + // because style change is handled by previous stage + // use move right when background is empty, use clear right when there is background. if (equalBg(this._cursorStyle, this._backgroundCell)) { this._currentRow += `\u001b[${this._nullCellCount}C`; } else { @@ -447,10 +421,8 @@ class StringSerializeHandler extends BaseSerializeHandler { protected _serializeString(excludeFinalCursorPosition: boolean): string { let rowEnd = this._allRows.length; - /* - * the fixup is only required for data without scrollback - * because it will always be placed at last line otherwise - */ + // the fixup is only required for data without scrollback + // because it will always be placed at last line otherwise if (this._buffer.length - this._firstRow <= this._terminal.rows) { rowEnd = this._lastContentCursorRow + 1 - this._firstRow; this._lastCursorCol = this._lastContentCursorCol; @@ -494,11 +466,9 @@ class StringSerializeHandler extends BaseSerializeHandler { } } - /* - * Restore the cursor's current style, see https://github.com/xtermjs/xterm.js/issues/3677 - * HACK: Internal API access since it's awkward to expose this in the API and serialize will - * likely be the only consumer - */ + // Restore the cursor's current style, see https://github.com/xtermjs/xterm.js/issues/3677 + // HACK: Internal API access since it's awkward to expose this in the API and serialize will + // likely be the only consumer const curAttrData: IAttributeData = (this._terminal as any)._core._inputHandler._curAttrData; const sgrSeq = this._diffStyle(curAttrData, this._cursorStyle); if (sgrSeq.length > 0) { @@ -611,10 +581,8 @@ export class SerializeAddon implements ITerminalAddon, ISerializeApi { } } - /* - * Cursor visibility (DECTCEM) - * Default: visible - */ + // Cursor visibility (DECTCEM) + // Default: visible if (!modes.showCursor) { content += '\x1b[?25l'; } diff --git a/addons/addon-serialize/test/SerializeAddon.test.ts b/addons/addon-serialize/test/SerializeAddon.test.ts index 02188146d..fb42d807d 100644 --- a/addons/addon-serialize/test/SerializeAddon.test.ts +++ b/addons/addon-serialize/test/SerializeAddon.test.ts @@ -416,13 +416,11 @@ test.describe('SerializeAddon', () => { '中文中文', '12中文', '中文12', - /* - * This line is going to be wrapped at last character - * because it has line length of 11 (1+2*5). - * We concat it back without the null cell currently. - * But this may be incorrect. - * see also #3097 - */ + // This line is going to be wrapped at last character + // because it has line length of 11 (1+2*5). + // We concat it back without the null cell currently. + // But this may be incorrect. + // see also #3097 '1中文中文中' ]; await ctx.proxy.write(lines.join('\r\n')); @@ -505,11 +503,9 @@ test.describe('SerializeAddon', () => { const MOVE_DOWN = (l: number): string => `\u001b[${l}B`; const MOVE_LEFT = (l: number): string => `\u001b[${l}D`; - /* - * A line wrap happened after current line. - * But there is no content. - * so wrap shouldn't even be able to happen. - */ + // A line wrap happened after current line. + // But there is no content. + // so wrap shouldn't even be able to happen. const segments = [ `123456789012345`, MOVE_UP(1), @@ -535,11 +531,9 @@ test.describe('SerializeAddon', () => { 10 ); - /* - * A line wrap happened after current line. - * But there is no content. - * so wrap shouldn't even be able to happen. - */ + // A line wrap happened after current line. + // But there is no content. + // so wrap shouldn't even be able to happen. const lines = [ padLines.join('\r\n'), '\r\n', diff --git a/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts b/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts index e68185f76..3d5717a0f 100644 --- a/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts +++ b/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts @@ -22,11 +22,9 @@ export class UnicodeGraphemeProvider implements IUnicodeVersionProvider { = UnicodeService.createPropertyValue(UC.GRAPHEME_BREAK_Other, 1, false); public charProperties(codepoint: number, preceding: UnicodeCharProperties): UnicodeCharProperties { - /* - * Optimize the simple ASCII case, under the condition that - * UnicodeService.extractCharKind(preceding) === GRAPHEME_BREAK_Other - * (which also covers the case that preceding === 0). - */ + // Optimize the simple ASCII case, under the condition that + // UnicodeService.extractCharKind(preceding) === GRAPHEME_BREAK_Other + // (which also covers the case that preceding === 0). if ((codepoint >= 32 && codepoint < 127) && (preceding >> 3) === 0) { return UnicodeGraphemeProvider._plainNarrowProperties; } diff --git a/addons/addon-web-fonts/test/WebFontsAddon.test.ts b/addons/addon-web-fonts/test/WebFontsAddon.test.ts index 100e34f55..c94bff84d 100644 --- a/addons/addon-web-fonts/test/WebFontsAddon.test.ts +++ b/addons/addon-web-fonts/test/WebFontsAddon.test.ts @@ -21,10 +21,8 @@ test.describe('WebFontsAddon', () => { deepStrictEqual(empty, []); }); test.afterEach(async () => { - /* - * for font loading tests to work, we have to remove added rules and fonts - * to work around the quite aggressive font caching done by the browsers - */ + // for font loading tests to work, we have to remove added rules and fonts + // to work around the quite aggressive font caching done by the browsers await ctx.page.evaluate(` document.styleSheets[0].deleteRule(1); document.styleSheets[0].deleteRule(0); @@ -75,10 +73,8 @@ test.describe('WebFontsAddon', () => { }); test('autoload & relayout from ctor', async ({ browser }) => { - /* - * to make this test work, we exclude the default measurement char W (x57) by restricting unicode-range - * now the browser will postpone font loading until codepoint is hit --> wrong glyph metrics on first usage - */ + // to make this test work, we exclude the default measurement char W (x57) by restricting unicode-range + // now the browser will postpone font loading until codepoint is hit --> wrong glyph metrics on first usage const data = await ctx.page.evaluate(` document.styleSheets[0].insertRule("@font-face {font-family: Kongtext; src: url(/fonts/kongtext.regular.ttf) format('truetype'); unicode-range: U+00A0-00FF}", 0); `); @@ -101,10 +97,8 @@ test.describe('WebFontsAddon', () => { deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'unloaded' }]); } - /* - * good case: addon fixes layout for webfont in ctor - * the relayout happens async, so wait a bit with a promise - */ + // good case: addon fixes layout for webfont in ctor + // the relayout happens async, so wait a bit with a promise await ctx.page.evaluate(` window.helperTerm.dispose(); window.helperTerm = new Terminal({fontFamily: '"Kongtext", ' + term.options.fontFamily}); diff --git a/addons/addon-web-links/src/WebLinkProvider.ts b/addons/addon-web-links/src/WebLinkProvider.ts index 3f4a894dd..efaead5c4 100644 --- a/addons/addon-web-links/src/WebLinkProvider.ts +++ b/addons/addon-web-links/src/WebLinkProvider.ts @@ -73,10 +73,8 @@ export class LinkComputer { continue; } - /* - * map string positions back to buffer positions - * values are 0-based right side excluding - */ + // map string positions back to buffer positions + // values are 0-based right side excluding const [startY, startX] = LinkComputer._mapStrIdx(terminal, startLineIndex, 0, match.index); const [endY, endX] = LinkComputer._mapStrIdx(terminal, startY, startX, text.length); @@ -174,13 +172,11 @@ export class LinkComputer { if (width) { stringIndex -= chars.length || 1; - /* - * correct stringIndex for early wrapped wide chars: - * - currently only happens at last cell - * - cells to the right are reset with chars='' and width=1 in InputHandler.print - * - follow-up line must be wrapped and contain wide char at first cell - * --> if all these conditions are met, correct stringIndex by +1 - */ + // correct stringIndex for early wrapped wide chars: + // - currently only happens at last cell + // - cells to the right are reset with chars='' and width=1 in InputHandler.print + // - follow-up line must be wrapped and contain wide char at first cell + // --> if all these conditions are met, correct stringIndex by +1 if (i === line.length - 1 && chars === '') { const line = buf.getLine(lineIndex + 1); if (line && line.isWrapped) { diff --git a/addons/addon-web-links/src/WebLinksAddon.ts b/addons/addon-web-links/src/WebLinksAddon.ts index 0a7171987..3328a3c3f 100644 --- a/addons/addon-web-links/src/WebLinksAddon.ts +++ b/addons/addon-web-links/src/WebLinksAddon.ts @@ -7,19 +7,17 @@ import type { Terminal, ITerminalAddon, IDisposable } from '@xterm/xterm'; import type { WebLinksAddon as IWebLinksApi } from '@xterm/addon-web-links'; import { ILinkProviderOptions, WebLinkProvider } from './WebLinkProvider'; -/* - * consider everthing starting with http:// or https:// - * up to first whitespace, `"` or `'` as url - * NOTE: The repeated end clause is needed to not match a dangling `:` - * resembling the old (...)*([^:"\'\\s]) final path clause - * additionally exclude early + final: - * - unsafe from rfc3986: !*'() - * - unsafe chars from rfc1738: {}|\^~[]` (minus [] as we need them for ipv6 adresses, also allow ~) - * also exclude as finals: - * - final interpunction like ,.!? - * - any sort of brackets <>()[]{} (not spec conform, but often used to enclose urls) - * - unsafe chars from rfc1738: {}|\^~[]` - */ +// consider everthing starting with http:// or https:// +// up to first whitespace, `"` or `'` as url +// NOTE: The repeated end clause is needed to not match a dangling `:` +// resembling the old (...)*([^:"\'\\s]) final path clause +// additionally exclude early + final: +// - unsafe from rfc3986: !*'() +// - unsafe chars from rfc1738: {}|\^~[]` (minus [] as we need them for ipv6 adresses, also allow ~) +// also exclude as finals: +// - final interpunction like ,.!? +// - any sort of brackets <>()[]{} (not spec conform, but often used to enclose urls) +// - unsafe chars from rfc1738: {}|\^~[]` const strictUrlRegex = /(https?|HTTPS?):[/]{2}[^\s"'!*(){}|\\\^<>`]*[^\s"':,.!?{}|\\\^~\[\]`()<>]/; diff --git a/addons/addon-webgl/src/CellColorResolver.ts b/addons/addon-webgl/src/CellColorResolver.ts index fe8baa35a..3a4661cc3 100644 --- a/addons/addon-webgl/src/CellColorResolver.ts +++ b/addons/addon-webgl/src/CellColorResolver.ts @@ -47,10 +47,8 @@ export class CellColorResolver { this.result.bg = cell.bg; this.result.fg = cell.fg; this.result.ext = cell.bg & BgFlags.HAS_EXTENDED ? cell.extended.ext : 0; - /* - * Get any foreground/background overrides, this happens on the model to avoid spreading - * override logic throughout the different sub-renderers - */ + // Get any foreground/background overrides, this happens on the model to avoid spreading + // override logic throughout the different sub-renderers // Reset overrides work variables $bg = 0; @@ -188,10 +186,8 @@ export class CellColorResolver { } }); - /* - * Convert any overrides from rgba to the fg/bg packed format. This resolves the inverse flag - * ahead of time in order to use the correct cache key - */ + // Convert any overrides from rgba to the fg/bg packed format. This resolves the inverse flag + // ahead of time in order to use the correct cache key if ($hasBg) { if ($isSelected) { // Non-RGB attributes from model + force non-dim + override + force RGB color mode @@ -206,10 +202,8 @@ export class CellColorResolver { $fg = (cell.fg & ~Attributes.RGB_MASK & ~FgFlags.INVERSE) | $fg | Attributes.CM_RGB; } - /* - * Handle case where inverse was specified by only one of bg override or fg override was set, - * resolving the other inverse color and setting the inverse flag if needed. - */ + // Handle case where inverse was specified by only one of bg override or fg override was set, + // resolving the other inverse color and setting the inverse flag if needed. if (this.result.fg & FgFlags.INVERSE) { if ($hasBg && !$hasFg) { // Resolve bg color type (default color has a different meaning in fg vs bg) diff --git a/addons/addon-webgl/src/CharAtlasCache.ts b/addons/addon-webgl/src/CharAtlasCache.ts index 5581ea22d..823a74cc6 100644 --- a/addons/addon-webgl/src/CharAtlasCache.ts +++ b/addons/addon-webgl/src/CharAtlasCache.ts @@ -13,10 +13,8 @@ import type { ILogService } from 'common/services/Services'; interface ITextureAtlasCacheEntry { atlas: ITextureAtlas; config: ICharAtlasConfig; - /* - * N.B. This implementation potentially holds onto copies of the terminal forever, so - * this may cause memory leaks. - */ + // N.B. This implementation potentially holds onto copies of the terminal forever, so + // this may cause memory leaks. ownedBy: Terminal[]; } diff --git a/addons/addon-webgl/src/CharAtlasUtils.ts b/addons/addon-webgl/src/CharAtlasUtils.ts index 15e796256..4ce103e98 100644 --- a/addons/addon-webgl/src/CharAtlasUtils.ts +++ b/addons/addon-webgl/src/CharAtlasUtils.ts @@ -25,10 +25,8 @@ export function generateConfig(deviceCellWidth: number, deviceCellHeight: number scrollbarSliderBackground: NULL_COLOR, scrollbarSliderHoverBackground: NULL_COLOR, scrollbarSliderActiveBackground: NULL_COLOR, - /* - * For the static char atlas, we only use the first 16 colors, but we need all 256 for the - * dynamic character atlas. - */ + // For the static char atlas, we only use the first 16 colors, but we need all 256 for the + // dynamic character atlas. ansi: colors.ansi.slice(), contrastCache: colors.contrastCache, halfContrastCache: colors.halfContrastCache diff --git a/addons/addon-webgl/src/Constants.ts b/addons/addon-webgl/src/Constants.ts index af08fe8bb..a00434d61 100644 --- a/addons/addon-webgl/src/Constants.ts +++ b/addons/addon-webgl/src/Constants.ts @@ -6,9 +6,7 @@ import { isFirefox, isLegacyEdge } from 'common/Platform'; export const DIM_OPACITY = 0.5; -/* - * The text baseline is set conditionally by browser. Using 'ideographic' for Firefox or Legacy Edge - * would result in truncated text (Issue 3353). Using 'bottom' for Chrome would result in slightly - * unaligned Powerline fonts (PR 3356#issuecomment-850928179). - */ +// The text baseline is set conditionally by browser. Using 'ideographic' for Firefox or Legacy Edge +// would result in truncated text (Issue 3353). Using 'bottom' for Chrome would result in slightly +// unaligned Powerline fonts (PR 3356#issuecomment-850928179). export const TEXT_BASELINE: CanvasTextBaseline = isFirefox || isLegacyEdge ? 'bottom' : 'ideographic'; diff --git a/addons/addon-webgl/src/CursorBlinkStateManager.ts b/addons/addon-webgl/src/CursorBlinkStateManager.ts index 1e9695c94..688aee7b7 100644 --- a/addons/addon-webgl/src/CursorBlinkStateManager.ts +++ b/addons/addon-webgl/src/CursorBlinkStateManager.ts @@ -87,17 +87,13 @@ export class CursorBlinkStateManager { this._blinkInterval = undefined; } - /* - * Setup the initial timeout which will hide the cursor, this is done before - * the regular interval is setup in order to support restarting the blink - * animation in a lightweight way (without thrashing clearInterval and - * setInterval). - */ + // Setup the initial timeout which will hide the cursor, this is done before + // the regular interval is setup in order to support restarting the blink + // animation in a lightweight way (without thrashing clearInterval and + // setInterval). this._blinkStartTimeout = this._coreBrowserService.window.setTimeout(() => { - /* - * Check if another animation restart was requested while this was being - * started - */ + // Check if another animation restart was requested while this was being + // started if (this._animationTimeRestarted) { const time = Constants.BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted); this._animationTimeRestarted = undefined; @@ -118,10 +114,8 @@ export class CursorBlinkStateManager { this._blinkInterval = this._coreBrowserService.window.setInterval(() => { // Adjust the animation time if it was restarted if (this._animationTimeRestarted) { - /* - * calc time diff - * Make restart interval do a setTimeout initially? - */ + // calc time diff + // Make restart interval do a setTimeout initially? const time = Constants.BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted); this._animationTimeRestarted = undefined; this._restartInterval(time); diff --git a/addons/addon-webgl/src/DevicePixelObserver.ts b/addons/addon-webgl/src/DevicePixelObserver.ts index c7af6a1bb..dcd8de0a6 100644 --- a/addons/addon-webgl/src/DevicePixelObserver.ts +++ b/addons/addon-webgl/src/DevicePixelObserver.ts @@ -6,12 +6,10 @@ import { toDisposable, IDisposable } from 'common/Lifecycle'; export function observeDevicePixelDimensions(element: HTMLElement, parentWindow: Window & typeof globalThis, callback: (deviceWidth: number, deviceHeight: number) => void): IDisposable { - /* - * Observe any resizes to the element and extract the actual pixel size of the element if the - * devicePixelContentBoxSize API is supported. This allows correcting rounding errors when - * converting between CSS pixels and device pixels which causes blurry rendering when device - * pixel ratio is not a round number. - */ + // Observe any resizes to the element and extract the actual pixel size of the element if the + // devicePixelContentBoxSize API is supported. This allows correcting rounding errors when + // converting between CSS pixels and device pixels which causes blurry rendering when device + // pixel ratio is not a round number. let observer: ResizeObserver | undefined = new parentWindow.ResizeObserver((entries) => { const entry = entries.find((entry) => entry.target === element); if (!entry) { diff --git a/addons/addon-webgl/src/GlyphRenderer.ts b/addons/addon-webgl/src/GlyphRenderer.ts index 82ca01b63..46b8efeb9 100644 --- a/addons/addon-webgl/src/GlyphRenderer.ts +++ b/addons/addon-webgl/src/GlyphRenderer.ts @@ -148,11 +148,9 @@ export class GlyphRenderer extends Disposable { gl.enableVertexAttribArray(VertexAttribLocations.UNIT_QUAD); gl.vertexAttribPointer(VertexAttribLocations.UNIT_QUAD, 2, this._gl.FLOAT, false, 0, 0); - /* - * Setup the unit quad element array buffer, this points to indices in - * unitQuadVertices to allow is to draw 2 triangles from the vertices via a - * triangle strip - */ + // Setup the unit quad element array buffer, this points to indices in + // unitQuadVertices to allow is to draw 2 triangles from the vertices via a + // triangle strip const unitQuadElementIndices = new Uint8Array([0, 1, 2, 3]); const elementIndicesBuffer = gl.createBuffer(); this._register(toDisposable(() => gl.deleteBuffer(elementIndicesBuffer))); @@ -191,10 +189,8 @@ export class GlyphRenderer extends Disposable { gl.uniform1iv(this._textureLocation, textureUnits); gl.uniformMatrix4fv(this._projectionLocation, false, PROJECTION_MATRIX); - /* - * Setup 1x1 red pixel textures for all potential atlas pages, if one of these invalid textures - * is ever drawn it will show characters as red rectangles. - */ + // Setup 1x1 red pixel textures for all potential atlas pages, if one of these invalid textures + // is ever drawn it will show characters as red rectangles. this._atlasTextures = []; for (let i = 0; i < TextureAtlas.maxAtlasPages; i++) { const glTexture = new GLTexture(throwIfFalsy(gl.createTexture())); @@ -220,22 +216,18 @@ export class GlyphRenderer extends Disposable { } public updateCell(x: number, y: number, code: number, bg: number, fg: number, ext: number, chars: string, width: number, lastBg: number): void { - /* - * Since this function is called for every cell (`rows*cols`), it must be very optimized. It - * should not instantiate any variables unless a new glyph is drawn to the cache where the - * slight slowdown is acceptable for the developer ergonomics provided as it's a once of for - * each glyph. - */ + // Since this function is called for every cell (`rows*cols`), it must be very optimized. It + // should not instantiate any variables unless a new glyph is drawn to the cache where the + // slight slowdown is acceptable for the developer ergonomics provided as it's a once of for + // each glyph. this._updateCell(this._vertices.attributes, x, y, code, bg, fg, ext, chars, width, lastBg); } private _updateCell(array: Float32Array, x: number, y: number, code: number | undefined, bg: number, fg: number, ext: number, chars: string, width: number, lastBg: number): void { $i = (y * this._terminal.cols + x) * Constants.INDICES_PER_CELL; - /* - * Exit early if this is a null character, allow space character to continue as it may have - * underline/strikethrough styles - */ + // Exit early if this is a null character, allow space character to continue as it may have + // underline/strikethrough styles if (code === NULL_CELL_CODE || code === undefined/* This is used for the right side of wide chars */) { array.fill(0, $i, $i + Constants.INDICES_PER_CELL - 1 - Constants.CELL_POSITION_INDICES); return; @@ -287,10 +279,8 @@ export class GlyphRenderer extends Disposable { } // a_cellpos only changes on resize - /* - * Reduce scale horizontally for wide glyphs printed in cells that would overlap with the - * following cell (ie. the width is not 2). - */ + // Reduce scale horizontally for wide glyphs printed in cells that would overlap with the + // following cell (ie. the width is not 2). if (this._optionsService.rawOptions.rescaleOverlappingGlyphs) { if (allowRescaling(code, width, $glyph.size.x, this._dimensions.device.cell.width)) { array[$i + 2] = (this._dimensions.device.cell.width - 1) / this._dimensions.device.canvas.width; // - 1 to improve readability @@ -349,15 +339,13 @@ export class GlyphRenderer extends Disposable { this._activeBuffer = (this._activeBuffer + 1) % 2; const activeBuffer = this._vertices.attributesBuffers[this._activeBuffer]; - /* - * Copy data for each cell of each line up to its line length (the last non-whitespace cell) - * from the attributes buffer into activeBuffer, which is the one that gets bound to the GPU. - * The reasons for this are as follows: - * - So the active buffer can be alternated so we don't get blocked on rendering finishing - * - To copy either the normal attributes buffer or the selection attributes buffer when there - * is a selection - * - So we don't send vertices for all the line-ending whitespace to the GPU - */ + // Copy data for each cell of each line up to its line length (the last non-whitespace cell) + // from the attributes buffer into activeBuffer, which is the one that gets bound to the GPU. + // The reasons for this are as follows: + // - So the active buffer can be alternated so we don't get blocked on rendering finishing + // - To copy either the normal attributes buffer or the selection attributes buffer when there + // is a selection + // - So we don't send vertices for all the line-ending whitespace to the GPU let bufferLength = 0; for (let y = 0; y < renderModel.lineLengths.length; y++) { const si = y * this._terminal.cols * Constants.INDICES_PER_CELL; @@ -370,11 +358,9 @@ export class GlyphRenderer extends Disposable { gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer); gl.bufferData(gl.ARRAY_BUFFER, activeBuffer.subarray(0, bufferLength), gl.STREAM_DRAW); - /* - * Bind the atlas page texture if they have changed. AtlasPage.version is globally - * monotonic, so a page object swap at the same index (which happens after a page merge) - * is detected by the same comparison. - */ + // Bind the atlas page texture if they have changed. AtlasPage.version is globally + // monotonic, so a page object swap at the same index (which happens after a page merge) + // is detected by the same comparison. for (let i = 0; i < this._atlas.pages.length; i++) { if (this._atlas.pages[i].version !== this._atlasTextures[i].version) { this._bindAtlasPageTexture(gl, this._atlas, i); diff --git a/addons/addon-webgl/src/RectangleRenderer.ts b/addons/addon-webgl/src/RectangleRenderer.ts index e87bc690c..81e129e5c 100644 --- a/addons/addon-webgl/src/RectangleRenderer.ts +++ b/addons/addon-webgl/src/RectangleRenderer.ts @@ -114,11 +114,9 @@ export class RectangleRenderer extends Disposable { gl.enableVertexAttribArray(VertexAttribLocations.UNIT_QUAD); gl.vertexAttribPointer(VertexAttribLocations.UNIT_QUAD, 2, this._gl.FLOAT, false, 0, 0); - /* - * Setup the unit quad element array buffer, this points to indices in - * unitQuadVertices to allow is to draw 2 triangles from the vertices via a - * triangle strip - */ + // Setup the unit quad element array buffer, this points to indices in + // unitQuadVertices to allow is to draw 2 triangles from the vertices via a + // triangle strip const unitQuadElementIndices = new Uint8Array([0, 1, 2, 3]); const elementIndicesBuffer = gl.createBuffer(); this._register(toDisposable(() => gl.deleteBuffer(elementIndicesBuffer))); diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index 09293d4fa..34188235c 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -151,17 +151,13 @@ export class TextureAtlas implements ITextureAtlas { } private _createNewPage(): AtlasPage { - /* - * Try merge the set of the 4 most used pages of the largest size. This is is deferred to a - * microtask to ensure it does not interrupt textures that will be rendered in the current - * animation frame which would result in blank rendered areas. This is actually not that - * expensive relative to drawing the glyphs, so there is no need to wait for an idle callback. - */ + // Try merge the set of the 4 most used pages of the largest size. This is is deferred to a + // microtask to ensure it does not interrupt textures that will be rendered in the current + // animation frame which would result in blank rendered areas. This is actually not that + // expensive relative to drawing the glyphs, so there is no need to wait for an idle callback. if (TextureAtlas.maxAtlasPages && this._pages.length >= Math.max(4, TextureAtlas.maxAtlasPages)) { - /* - * Find the set of the largest 4 images, below the maximum size, with the highest - * percentages used - */ + // Find the set of the largest 4 images, below the maximum size, with the highest + // percentages used const pagesBySize = this._pages.filter(e => { return e.canvas.width * 2 <= (TextureAtlas.maxTextureSize || Constants.FORCED_MAX_TEXTURE_SIZE); }).sort((a, b) => { @@ -184,10 +180,8 @@ export class TextureAtlas implements ITextureAtlas { // Gather details of the merge const mergingPages = pagesBySize.slice(sameSizeI, sameSizeI + 4); - /* - * Only proceed with merge if we have exactly 4 same-sized pages. If not, we cannot - * effectively reduce page count and merging would cause issues. - */ + // Only proceed with merge if we have exactly 4 same-sized pages. If not, we cannot + // effectively reduce page count and merging would cause issues. if (mergingPages.length < 4 || mergingPages.some(p => p.canvas.width !== mergingPages[0].canvas.width)) { const newPage = new AtlasPage(this._document, this._textureSize); this._pages.push(newPage); @@ -300,11 +294,9 @@ export class TextureAtlas implements ITextureAtlas { private _getBackgroundColor(bgColorMode: number, bgColor: number, inverse: boolean, dim: boolean): IColor { if (this._config.allowTransparency) { - /* - * The background color might have some transparency, so we need to render it as fully - * transparent in the atlas. Otherwise we'd end up drawing the transparent background twice - * around the anti-aliased edges of the glyph, and it would look too dark. - */ + // The background color might have some transparency, so we need to render it as fully + // transparent in the atlas. Otherwise we'd end up drawing the transparent background twice + // around the anti-aliased edges of the glyph, and it would look too dark. return NULL_COLOR; } @@ -426,10 +418,8 @@ export class TextureAtlas implements ITextureAtlas { const bgRgba = this._resolveBackgroundRgba(bgColorMode, bgColor, inverse); const fgRgba = this._resolveForegroundRgba(fgColorMode, fgColor, inverse, bold); - /* - * Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from - * non-dim cells - */ + // Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from + // non-dim cells const result = rgba.ensureContrastRatio(bgRgba, fgRgba, this._config.minimumContrastRatio / (dim ? 2 : 1)); if (!result) { @@ -457,26 +447,20 @@ export class TextureAtlas implements ITextureAtlas { private _drawToCache(codeOrChars: number | string, bg: number, fg: number, ext: number, restrictToCellHeight: boolean, domContainer: HTMLElement | undefined): IRasterizedGlyph { const chars = typeof codeOrChars === 'number' ? String.fromCharCode(codeOrChars) : codeOrChars; - /* - * Uncomment for debugging - * console.log(`draw to cache "${chars}"`, bg, fg, ext); - */ + // Uncomment for debugging + // console.log(`draw to cache "${chars}"`, bg, fg, ext); - /* - * Attach the canvas to the DOM in order to inherit font-feature-settings - * from the parent elements. This is necessary for ligatures and variants to - * work. - */ + // Attach the canvas to the DOM in order to inherit font-feature-settings + // from the parent elements. This is necessary for ligatures and variants to + // work. if (domContainer && this._tmpCanvas.parentElement !== domContainer) { this._tmpCanvas.style.display = 'none'; domContainer.append(this._tmpCanvas); } - /* - * 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. - */ + // 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 = Math.min(this._config.deviceCellWidth * Math.max(chars.length, 2) + TMP_CANVAS_GLYPH_PADDING * 2, this._config.deviceMaxTextureSize); if (this._tmpCanvas.width < allowedWidth) { this._tmpCanvas.width = allowedWidth; @@ -519,10 +503,8 @@ export class TextureAtlas implements ITextureAtlas { // draw the background const backgroundColor = this._getBackgroundColor(bgColorMode, bgColor, inverse, dim); - /* - * Use a 'copy' composite operation to clear any existing glyph out of _tmpCtxWithAlpha, - * regardless of transparency in backgroundColor - */ + // Use a 'copy' composite operation to clear any existing glyph out of _tmpCtxWithAlpha, + // regardless of transparency in backgroundColor this._tmpCtx.globalCompositeOperation = 'copy'; this._tmpCtx.fillStyle = backgroundColor.css; this._tmpCtx.fillRect(0, 0, this._tmpCanvas.width, this._tmpCanvas.height); @@ -550,11 +532,9 @@ export class TextureAtlas implements ITextureAtlas { customGlyph = tryDrawCustomGlyph(this._tmpCtx, chars, padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight, this._config.deviceCharWidth, this._config.deviceCharHeight, this._config.fontSize, this._config.devicePixelRatio, backgroundColor.css, variantOffset); } - /* - * Whether to clear pixels based on a threshold difference between the glyph color and the - * background color. This should be disabled when the glyph contains multiple colors such as - * underline colors to prevent important colors could get cleared. - */ + // Whether to clear pixels based on a threshold difference between the glyph color and the + // background color. This should be disabled when the glyph contains multiple colors such as + // underline colors to prevent important colors could get cleared. let enableClearThresholdCheck = !powerlineGlyph; let chWidth: number; @@ -619,10 +599,8 @@ export class TextureAtlas implements ITextureAtlas { clipRegion.rect(xChLeft, yTop, this._config.deviceCellWidth, yBot - yTop); this._tmpCtx.clip(clipRegion); - /* - * Draw a zigzag pattern, this is derived from the SVG used in monaco for the same - * style. The viewbox is 6x3 so scale it using that. - */ + // Draw a zigzag pattern, this is derived from the SVG used in monaco for the same + // style. The viewbox is 6x3 so scale it using that. const cellW = this._config.deviceCellWidth; const curlyH = (yBot - yTop); const scaleX = cellW / 6; @@ -693,21 +671,15 @@ export class TextureAtlas implements ITextureAtlas { } this._tmpCtx.restore(); - /* - * Draw stroke in the background color for non custom characters in order to give an outline - * between the text and the underline. Only do this when font size is >= 12 as the underline - * looks odd when the font size is too small - */ + // Draw stroke in the background color for non custom characters in order to give an outline + // between the text and the underline. Only do this when font size is >= 12 as the underline + // looks odd when the font size is too small if (!customGlyph && this._config.fontSize >= 12) { - /* - * This only works when transparency is disabled because it's not clear how to clear stroked - * text - */ + // This only works when transparency is disabled because it's not clear how to clear stroked + // text if (!this._config.allowTransparency && chars !== ' ') { - /* - * Measure the text, only draw the stroke if there is a descent beyond an alphabetic text - * baseline - */ + // Measure the text, only draw the stroke if there is a descent beyond an alphabetic text + // baseline this._tmpCtx.save(); this._tmpCtx.textBaseline = 'alphabetic'; const metrics = this._tmpCtx.measureText(chars); @@ -715,11 +687,9 @@ export class TextureAtlas implements ITextureAtlas { if ('actualBoundingBoxDescent' in metrics && metrics.actualBoundingBoxDescent > 0) { // This translates to 1/2 the line width in either direction this._tmpCtx.save(); - /* - * Clip the region to only draw in valid pixels near the underline to avoid a slight - * outline around the whole glyph, as well as additional pixels in the glyph at the top - * which would increase GPU memory demands - */ + // Clip the region to only draw in valid pixels near the underline to avoid a slight + // outline around the whole glyph, as well as additional pixels in the glyph at the top + // which would increase GPU memory demands const clipRegion = new Path2D(); clipRegion.rect(xLeft, yTop - Math.ceil(lineWidth / 2), this._config.deviceCellWidth * chWidth, yBot - yTop + Math.ceil(lineWidth / 2)); this._tmpCtx.clip(clipRegion); @@ -749,10 +719,8 @@ export class TextureAtlas implements ITextureAtlas { this._tmpCtx.fillText(chars, padding, padding + this._config.deviceCharHeight); } - /* - * 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 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); if (isBeyondCellBounds) { @@ -784,10 +752,8 @@ export class TextureAtlas implements ITextureAtlas { this._tmpCtx.restore(); - /* - * clear the background from the character to avoid issues with drawing over the previous - * character if it extends past it's bounds - */ + // clear the background from the character to avoid issues with drawing over the previous + // character if it extends past it's bounds const imageData = this._tmpCtx.getImageData( 0, 0, this._tmpCanvas.width, this._tmpCanvas.height ); @@ -830,16 +796,12 @@ export class TextureAtlas implements ITextureAtlas { } } - /* - * TODO: This algorithm could be simplified: - * - Search for the page with ROW_PIXEL_THRESHOLD in mind - * - Keep track of current/fixed rows in a Map - */ + // TODO: This algorithm could be simplified: + // - Search for the page with ROW_PIXEL_THRESHOLD in mind + // - Keep track of current/fixed rows in a Map - /* - * Replace the best current row with a fixed row if there is one at least as good as the - * current row. Search in reverse to prioritize filling in older pages. - */ + // Replace the best current row with a fixed row if there is one at least as good as the + // current row. Search in reverse to prioritize filling in older pages. for (let i = this._activePages.length - 1; i >= 0; i--) { for (const row of this._activePages[i].fixedRows) { if (row.height <= activeRow.height && rasterizedGlyph.size.y <= row.height) { @@ -870,16 +832,12 @@ export class TextureAtlas implements ITextureAtlas { break; } - /* - * Create a new page if too much vertical space would be wasted or there is not enough room - * left in the page. The previous active row will become fixed in the process as it now has a - * fixed height - */ + // Create a new page if too much vertical space would be wasted or there is not enough room + // left in the page. The previous active row will become fixed in the process as it now has a + // fixed height if (activeRow.y + rasterizedGlyph.size.y >= activePage.canvas.height || activeRow.height > rasterizedGlyph.size.y + Constants.ROW_PIXEL_THRESHOLD) { - /* - * Create the new fixed height row, creating a new page if there isn't enough room on the - * current page - */ + // Create the new fixed height row, creating a new page if there isn't enough room on the + // current page let wasPageAndRowFound = false; if (activePage.currentRow.y + activePage.currentRow.height + rasterizedGlyph.size.y >= activePage.canvas.height) { // Find the first page with room to create the new row on @@ -894,12 +852,10 @@ export class TextureAtlas implements ITextureAtlas { if (candidatePage) { activePage = candidatePage; } else { - /* - * Before creating a new atlas page that would trigger a page merge, check if the - * current active row is sufficient when ignoring the ROW_PIXEL_THRESHOLD. This will - * improve texture utilization by using the available space before the page is merged - * and becomes static. - */ + // Before creating a new atlas page that would trigger a page merge, check if the + // current active row is sufficient when ignoring the ROW_PIXEL_THRESHOLD. This will + // improve texture utilization by using the available space before the page is merged + // and becomes static. if ( TextureAtlas.maxAtlasPages && this._pages.length >= TextureAtlas.maxAtlasPages && @@ -967,10 +923,8 @@ export class TextureAtlas implements ITextureAtlas { rasterizedGlyph.sizeClipSpace.x /= activePage.canvas.width; rasterizedGlyph.sizeClipSpace.y /= activePage.canvas.height; - /* - * Update atlas current row, for fixed rows the glyph height will never be larger than the row - * height - */ + // Update atlas current row, for fixed rows the glyph height will never be larger than the row + // height activeRow.height = Math.max(activeRow.height, rasterizedGlyph.size.y); activeRow.x += rasterizedGlyph.size.x; @@ -1103,18 +1057,16 @@ class AtlasPage { public static nextVersion: number = 0; public version = ++AtlasPage.nextVersion; - /* - * Texture atlas current positioning data. The texture packing strategy used is to fill from - * left-to-right and top-to-bottom. When the glyph being written is less than half of the current - * row's height, the following happens: - * - * - The current row becomes the fixed height row A - * - A new fixed height row B the exact size of the glyph is created below the current row - * - A new dynamic height current row is created below B - * - * This strategy does a good job preventing space being wasted for very short glyphs such as - * underscores, hyphens etc. or those with underlines rendered. - */ + // Texture atlas current positioning data. The texture packing strategy used is to fill from + // left-to-right and top-to-bottom. When the glyph being written is less than half of the current + // row's height, the following happens: + // + // - The current row becomes the fixed height row A + // - A new fixed height row B the exact size of the glyph is created below the current row + // - A new dynamic height current row is created below B + // + // This strategy does a good job preventing space being wasted for very short glyphs such as + // underscores, hyphens etc. or those with underlines rendered. public currentRow: ICharAtlasActiveRow = { x: 0, y: 0, @@ -1134,11 +1086,9 @@ class AtlasPage { } } this.canvas = createCanvas(document, size, size); - /* - * The canvas needs alpha because we use clearColor to convert the background color to alpha. - * It might also contain some characters with transparent backgrounds if allowTransparency is - * set. - */ + // The canvas needs alpha because we use clearColor to convert the background color to alpha. + // It might also contain some characters with transparent backgrounds if allowTransparency is + // set. this.ctx = throwIfFalsy(this.canvas.getContext('2d', { alpha: true })); } @@ -1166,14 +1116,12 @@ function clearColor(imageData: ImageData, bg: IColor, fg: IColor, enableThreshol const fgG = fg.rgba >>> 16 & 0xFF; const fgB = fg.rgba >>> 8 & 0xFF; - /* - * Calculate a threshold that when below a color will be treated as transpart when the sum of - * channel value differs. This helps improve rendering when glyphs overlap with others. This - * threshold is calculated relative to the difference between the background and foreground to - * ensure important details of the glyph are always shown, even when the contrast ratio is low. - * The number 12 is largely arbitrary to ensure the pixels that escape the cell in the test case - * were covered (fg=#8ae234, bg=#c4a000). - */ + // Calculate a threshold that when below a color will be treated as transpart when the sum of + // channel value differs. This helps improve rendering when glyphs overlap with others. This + // threshold is calculated relative to the difference between the background and foreground to + // ensure important details of the glyph are always shown, even when the contrast ratio is low. + // The number 12 is largely arbitrary to ensure the pixels that escape the cell in the test case + // were covered (fg=#8ae234, bg=#c4a000). const threshold = Math.floor((Math.abs(r - fgR) + Math.abs(g - fgG) + Math.abs(b - fgB)) / 12); // Set alpha channel of relevent pixels to 0 diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 2997129f2..500308bd1 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -84,11 +84,9 @@ export class WebglRenderer extends Disposable implements IRenderer { ) { super(); - /* - * IMPORTANT: Canvas initialization and fetching of the context must be first in order to - * prevent possible listeners leaking and continuing to operate after the WebglRenderer has been - * discarded. - */ + // IMPORTANT: Canvas initialization and fetching of the context must be first in order to + // prevent possible listeners leaking and continuing to operate after the WebglRenderer has been + // discarded. this._canvas = this._coreBrowserService.mainDocument.createElement('canvas'); const contextAttributes = { antialias: false, @@ -127,10 +125,8 @@ export class WebglRenderer extends Disposable implements IRenderer { console.log('webglcontextlost event received'); // Prevent the default behavior in order to enable WebGL context restoration. e.preventDefault(); - /* - * Wait a few seconds to see if the 'webglcontextrestored' event is fired. - * If not, dispatch the onContextLoss notification to observers. - */ + // Wait a few seconds to see if the 'webglcontextrestored' event is fired. + // If not, dispatch the onContextLoss notification to observers. this._contextRestorationTimeout = setTimeout(() => { this._contextRestorationTimeout = undefined; console.warn('webgl context not restored; firing onContextLoss'); @@ -141,10 +137,8 @@ export class WebglRenderer extends Disposable implements IRenderer { console.warn('webglcontextrestored event received'); clearTimeout(this._contextRestorationTimeout); this._contextRestorationTimeout = undefined; - /* - * The texture atlas and glyph renderer must be fully reinitialized - * because their contents have been lost. - */ + // The texture atlas and glyph renderer must be fully reinitialized + // because their contents have been lost. removeTerminalFromCache(this._terminal); this._initializeWebGLState(); this._requestRedrawViewport(); @@ -184,10 +178,8 @@ export class WebglRenderer extends Disposable implements IRenderer { } public handleDevicePixelRatioChange(): void { - /* - * If the device pixel ratio changed, the char atlas needs to be regenerated - * and the terminal needs to refreshed - */ + // If the device pixel ratio changed, the char atlas needs to be regenerated + // and the terminal needs to refreshed if (this._devicePixelRatio !== this._coreBrowserService.dpr) { this._devicePixelRatio = this._coreBrowserService.dpr; this.handleResize(this._terminal.cols, this._terminal.rows); @@ -223,10 +215,8 @@ export class WebglRenderer extends Disposable implements IRenderer { this._refreshCharAtlas(); - /* - * Force a full refresh. Resizing `_glyphRenderer` should clear it already, - * so there is no need to clear it again here. - */ + // Force a full refresh. Resizing `_glyphRenderer` should clear it already, + // so there is no need to clear it again here. this._clearModel(false); // Render synchronously to avoid flicker when the canvas is cleared @@ -378,11 +368,9 @@ export class WebglRenderer extends Disposable implements IRenderer { return; } - /* - * 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) - */ + // 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.value.beginFrame()) { this._clearModel(true); this._updateModel(0, this._terminal.rows - 1); @@ -391,10 +379,8 @@ export class WebglRenderer extends Disposable implements IRenderer { this._updateModel(start, end); } - /* - * A mid-update atlas page merge invalidates vertex data and may not bump the host - * page's version, so re-run the update and force a full texture rebind. - */ + // A mid-update atlas page merge invalidates vertex data and may not bump the host + // page's version, so re-run the update and force a full texture rebind. let merged = false; let mergeRetries = 0; while (this._charAtlas && this._glyphRenderer.value.beginFrame() && mergeRetries++ < Constants.MERGE_RETRY_LIMIT) { @@ -422,10 +408,8 @@ export class WebglRenderer extends Disposable implements IRenderer { } else { this._cursorBlinkStateManager.clear(); } - /* - * Request a refresh from the terminal as management of rendering is being - * moved back to the terminal - */ + // Request a refresh from the terminal as management of rendering is being + // moved back to the terminal this._requestRedrawCursor(); } @@ -486,26 +470,20 @@ export class WebglRenderer extends Disposable implements IRenderer { // If true, indicates that the current character(s) to draw were joined. isJoined = false; - /* - * Indicates whether this cell is part of a joined range that should be ignored as it cannot - * be rendered entirely, like the selection state differs across the range. - */ + // Indicates whether this cell is part of a joined range that should be ignored as it cannot + // be rendered entirely, like the selection state differs across the range. isValidJoinRange = (x >= skipJoinedCheckUntilX); lastCharX = x; - /* - * Process any joined character ranges as needed. Because of how the - * ranges are produced, we know that they are valid for the characters - * and attributes of our input. - */ + // Process any joined character ranges as needed. Because of how the + // ranges are produced, we know that they are valid for the characters + // and attributes of our input. if (joinedRanges.length > 0 && x === joinedRanges[0][0] && isValidJoinRange) { range = joinedRanges.shift()!; - /* - * If the ligature's selection state is not consistent, don't join it. This helps the - * selection render correctly regardless whether they should be joined. - */ + // If the ligature's selection state is not consistent, don't join it. This helps the + // selection render correctly regardless whether they should be joined. const firstSelectionState = this._model.selection.isCellSelected(this._terminal, range[0], row); for (i = range[0] + 1; i < range[1]; i++) { isValidJoinRange &&= (firstSelectionState === this._model.selection.isCellSelected(this._terminal, i, row)); @@ -517,10 +495,8 @@ export class WebglRenderer extends Disposable implements IRenderer { } else { isJoined = true; - /* - * We already know the exact start and end column of the joined range, - * so we get the string and width representing it directly. - */ + // We already know the exact start and end column of the joined range, + // so we get the string and width representing it directly. cell = new JoinedCellData( cell, line!.translateToString(true, range[0], range[1]), @@ -610,10 +586,8 @@ export class WebglRenderer extends Disposable implements IRenderer { j = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL; this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0, 0); this._model.cells[j] = NULL_CELL_CODE; - /* - * Don't re-resolve the cell color since multi-colored ligature backgrounds are not - * supported - */ + // Don't re-resolve the cell color since multi-colored ligature backgrounds are not + // supported this._model.cells[j + RenderModelConstants.BG_OFFSET] = this._cellColorResolver.result.bg; this._model.cells[j + RenderModelConstants.FG_OFFSET] = this._cellColorResolver.result.fg; this._model.cells[j + RenderModelConstants.EXT_OFFSET] = this._cellColorResolver.result.ext; @@ -657,64 +631,48 @@ export class WebglRenderer extends Disposable implements IRenderer { return; } - /* - * Calculate the device character width. Width is floored as it must be drawn to an integer grid - * in order for the char atlas glyphs to not be blurry. - */ + // Calculate the device character width. Width is floored as it must be drawn to an integer grid + // in order for the char atlas glyphs to not be blurry. this.dimensions.device.char.width = Math.floor(this._charSizeService.width * this._devicePixelRatio); - /* - * Calculate the device character height. Height is ceiled in case devicePixelRatio is a - * floating point number in order to ensure there is enough space to draw the character to the - * cell. - */ + // Calculate the device character height. Height is ceiled in case devicePixelRatio is a + // floating point number in order to ensure there is enough space to draw the character to the + // cell. this.dimensions.device.char.height = Math.ceil(this._charSizeService.height * this._devicePixelRatio); - /* - * Calculate the device cell height, if lineHeight is _not_ 1, the resulting value will be - * floored since lineHeight can never be lower then 1, this guarentees the device cell height - * will always be larger than device char height. - */ + // Calculate the device cell height, if lineHeight is _not_ 1, the resulting value will be + // floored since lineHeight can never be lower then 1, this guarentees the device cell height + // will always be larger than device char height. this.dimensions.device.cell.height = Math.floor(this.dimensions.device.char.height * this._optionsService.rawOptions.lineHeight); - /* - * Calculate the y offset within a cell that glyph should draw at in order for it to be centered - * correctly within the cell. - */ + // Calculate the y offset within a cell that glyph should draw at in order for it to be centered + // correctly within the cell. this.dimensions.device.char.top = this._optionsService.rawOptions.lineHeight === 1 ? 0 : Math.round((this.dimensions.device.cell.height - this.dimensions.device.char.height) / 2); // Calculate the device cell width, taking the letterSpacing into account. this.dimensions.device.cell.width = this.dimensions.device.char.width + Math.round(this._optionsService.rawOptions.letterSpacing); - /* - * Calculate the x offset with a cell that text should draw from in order for it to be centered - * correctly within the cell. - */ + // Calculate the x offset with a cell that text should draw from in order for it to be centered + // correctly within the cell. this.dimensions.device.char.left = Math.floor(this._optionsService.rawOptions.letterSpacing / 2); - /* - * Recalculate the canvas dimensions, the device dimensions define the actual number of pixel in - * the canvas - */ + // Recalculate the canvas dimensions, the device dimensions define the actual number of pixel in + // the canvas this.dimensions.device.canvas.height = this._terminal.rows * this.dimensions.device.cell.height; this.dimensions.device.canvas.width = this._terminal.cols * this.dimensions.device.cell.width; - /* - * The the size of the canvas on the page. It's important that this rounds to nearest integer - * and not ceils as browsers often have floating point precision issues where - * `window.devicePixelRatio` ends up being something like `1.100000023841858` for example, when - * it's actually 1.1. Ceiling may causes blurriness as the backing canvas image is 1 pixel too - * large for the canvas element size. - */ + // The the size of the canvas on the page. It's important that this rounds to nearest integer + // and not ceils as browsers often have floating point precision issues where + // `window.devicePixelRatio` ends up being something like `1.100000023841858` for example, when + // it's actually 1.1. Ceiling may causes blurriness as the backing canvas image is 1 pixel too + // large for the canvas element size. this.dimensions.css.canvas.height = Math.round(this.dimensions.device.canvas.height / this._devicePixelRatio); this.dimensions.css.canvas.width = Math.round(this.dimensions.device.canvas.width / this._devicePixelRatio); - /* - * Get the CSS dimensions of an individual cell. This needs to be derived from the calculated - * device pixel canvas value above. CharMeasure.width/height by itself is insufficient when the - * page is not at 100% zoom level as CharMeasure is measured in CSS pixels, but the actual char - * size on the canvas can differ. - */ + // Get the CSS dimensions of an individual cell. This needs to be derived from the calculated + // device pixel canvas value above. CharMeasure.width/height by itself is insufficient when the + // page is not at 100% zoom level as CharMeasure is measured in CSS pixels, but the actual char + // size on the canvas can differ. this.dimensions.css.cell.height = this.dimensions.device.cell.height / this._devicePixelRatio; this.dimensions.css.cell.width = this.dimensions.device.cell.width / this._devicePixelRatio; } @@ -723,10 +681,8 @@ export class WebglRenderer extends Disposable implements IRenderer { if (this._canvas.width === width && this._canvas.height === height) { return; } - /* - * While the actual canvas size has changed, keep device canvas dimensions as the value before - * the change as it's an exact multiple of the cell sizes. - */ + // While the actual canvas size has changed, keep device canvas dimensions as the value before + // the change as it's an exact multiple of the cell sizes. this._canvas.width = width; this._canvas.height = height; // Render synchronously to avoid flicker when the canvas is cleared @@ -746,10 +702,8 @@ export class WebglRenderer extends Disposable implements IRenderer { // TODO: Share impl with core export class JoinedCellData extends AttributeData implements ICellData { private _width: number; - /* - * .content carries no meaning for joined CellData, simply nullify it - * thus we have to overload all other .content accessors - */ + // .content carries no meaning for joined CellData, simply nullify it + // thus we have to overload all other .content accessors public content: number = 0; public fg: number; public bg: number; @@ -777,10 +731,8 @@ export class JoinedCellData extends AttributeData implements ICellData { } public getCode(): number { - /* - * code always gets the highest possible fake codepoint (read as -1) - * this is needed as code is used by caches as identifier - */ + // code always gets the highest possible fake codepoint (read as -1) + // this is needed as code is used by caches as identifier return 0x1FFFFF; } diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index d2c95723a..c35299809 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -274,17 +274,15 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // #region Powerline Symbols (E0A0-E0BF) - /* - * This contains the definitions of the primarily used box drawing characters as vector shapes. - * The reason these characters are defined specially is to avoid common problems if a user's font - * has not been patched with powerline characters and also to get pixel perfect rendering as - * rendering issues can occur around AA/SPAA. - * - * The line variants draw beyond the cell and get clipped to ensure the end of the line is not - * visible. - * - * Original symbols defined in https://github.com/powerline/fontpatcher - */ + // This contains the definitions of the primarily used box drawing characters as vector shapes. + // The reason these characters are defined specially is to avoid common problems if a user's font + // has not been patched with powerline characters and also to get pixel perfect rendering as + // rendering issues can occur around AA/SPAA. + // + // The line variants draw beyond the cell and get clipped to ensure the end of the line is not + // visible. + // + // Original symbols defined in https://github.com/powerline/fontpatcher // Git branch '\u{E0A0}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.3,1 L.03,1 L.03,.88 C.03,.82,.06,.78,.11,.73 C.15,.7,.2,.68,.28,.65 L.43,.6 C.49,.58,.53,.56,.56,.53 C.59,.5,.6,.47,.6,.43 L.6,.27 L.4,.27 L.69,.1 L.98,.27 L.78,.27 L.78,.46 C.78,.52,.76,.56,.72,.61 C.68,.66,.63,.67,.56,.7 L.48,.72 C.42,.74,.38,.76,.35,.78 C.32,.8,.31,.84,.31,.88 L.31,1 M.3,.5 L.03,.59 L.03,.09 L.3,.09 L.3,.655', type: CustomGlyphVectorType.FILL } }, @@ -357,10 +355,8 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // #region Progress Indicators (EE00-EE0B) - /* - * initially added in Fira Code and later Nerd Fonts - * https://github.com/ryanoasis/nerd-fonts/pull/1733 - */ + // initially added in Fira Code and later Nerd Fonts + // https://github.com/ryanoasis/nerd-fonts/pull/1733 // Progress bars (EE00-EE05) '\u{EE00}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,.05 L.1,.05 L.1,.95 L1,.95 L1,1 L0,1 Z' }, // PROGRESS BAR EMPTY START @@ -382,10 +378,8 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // #region Git Branch Symbols (F5D0-F60D) - /* - * Initially added in Kitty (https://github.com/kovidgoyal/kitty/pull/7681) - * then in Wezterm (https://github.com/wezterm/wezterm/issues/6328). - */ + // Initially added in Kitty (https://github.com/kovidgoyal/kitty/pull/7681) + // then in Wezterm (https://github.com/wezterm/wezterm/issues/6328). // Straight lines (F5D0-F5D9) '\u{F5D0}': GitBranchSymbolsParts.LINE_H, // Same as 2500 @@ -439,10 +433,8 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi '\u{F5FA}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_UP], '\u{F5FB}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_UP], - /* - * Extended Nodes (F5FC-F60D) - * These were added a little later https://github.com/kovidgoyal/kitty/pull/7805 - */ + // Extended Nodes (F5FC-F60D) + // These were added a little later https://github.com/kovidgoyal/kitty/pull/7805 '\u{F5FC}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F5FD}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F5FE}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_LEFT], @@ -468,10 +460,8 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // https://www.unicode.org/charts/PDF/U1FB00.pdf - /* - * Block mosaic terminal graphic characters (1FB00-1FB3B) - * The term "sextant" refers to block mosaics divided into six parts. - */ + // Block mosaic terminal graphic characters (1FB00-1FB3B) + // The term "sextant" refers to block mosaics divided into six parts. '\u{1FB00}': sextant(0b000001), // BLOCK SEXTANT-1 '\u{1FB01}': sextant(0b000010), // BLOCK SEXTANT-2 '\u{1FB02}': sextant(0b000011), // BLOCK SEXTANT-12 (upper one third block) @@ -734,10 +724,8 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi '\u{1FBB2}': { type: CustomGlyphDefinitionType.PATH, data: 'M.29,.27 L0.13,.56 L.22,.59 L.35,0.35 L.67,.35 L.57,.57 L.71,.76 L.22,.76 L.42,1 L.53,.98 L.43,.86 L.9,.86 L.71,.6 L1,.6 L1,.52 L.83,.52 L.92,.36 L1,.36 L1,.27Z M.99,.13 A.12,.12,0,1,1,.75,.13 A.12,.12,0,1,1,.99,.13' }, // LEFT HALF RUNNING MAN '\u{1FBB3}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,.27 L.3,.27 L.55,.12 L.63,.18 L.33,.36 L0,.36 M0,.52 L.33,.52 L.59,.89 L.73,.89 L.73,.98 L.53,.98 L.28,.6 L0,.6' }, // RIGHT HALF RUNNING MAN - /* - * Arrows (1FBB4-1FBB8) - * TODO: Improve all arrows, use hybrid approach - */ + // Arrows (1FBB4-1FBB8) + // TODO: Improve all arrows, use hybrid approach '\u{1FBB4}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.15,.55 L.5,.4 L.5,.45 L.65,.45 L.65,.35 L.85,.35 L.85,.625 L.5,.625 L.5,.7 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // INVERSE DOWNWARDS ARROW WITH TIP LEFTWARDS '\u{1FBB5}': [ // LEFTWARDS ARROW AND UPPER AND LOWER ONE EIGHTH BLOCK { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L1,0 L1,.0625 L0,.0625 Z M0,.9375 L1,.9375 L1,1 L0,1 Z', type: CustomGlyphVectorType.FILL } }, @@ -892,11 +880,9 @@ function sextant(pattern: number): { type: CustomGlyphDefinitionType.PATH_FUNCTI return { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: () => { - /* - * Sextant grid: 2 columns, 3 rows - * Row heights in 8ths: top=3, middle=2, bottom=3 - * Column widths: left=4, right=4 - */ + // Sextant grid: 2 columns, 3 rows + // Row heights in 8ths: top=3, middle=2, bottom=3 + // Column widths: left=4, right=4 const rects: string[] = []; const colW = 0.5; // Each column is half width const rowH = [3 / 8, 2 / 8, 3 / 8]; // Row heights as fractions @@ -946,12 +932,10 @@ function sextant(pattern: number): { type: CustomGlyphDefinitionType.PATH_FUNCTI function segmentedDigit(pattern: number): string { const paths: string[] = []; - /* - * Each segment should have approximately the same stroke width, this is somewhat difficult to be - * precise since coordinates are 0-1 of the whole cell (percentage-based). To handle this, the - * fact that terminal cells are typically sized at ~2:1 (height:width) is leveraged. - * for horizontal vs vertical to make segments appear the same thickness - */ + // Each segment should have approximately the same stroke width, this is somewhat difficult to be + // precise since coordinates are 0-1 of the whole cell (percentage-based). To handle this, the + // fact that terminal cells are typically sized at ~2:1 (height:width) is leveraged. + // for horizontal vs vertical to make segments appear the same thickness const segW = 0.15; // Width of vertical segments (fraction of cell width) const segH = 0.075; // Height of horizontal segments (fraction of cell height, ~half of segW for 2:1 cells) const padX = 0.05; // Horizontal padding from edge diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts index ad36378cd..ba8925727 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts @@ -489,10 +489,8 @@ function drawPatternChar( pattern = throwIfFalsy(ctx.createPattern(tmpCanvas, null)); patternSet.set(fillStyle, pattern); } - /* - * Apply pattern offset to ensure seamless tiling across cells when cell dimensions are odd. - * variantOffset encodes: bit 1 = x pixel shift, bit 0 = y pixel shift. - */ + // Apply pattern offset to ensure seamless tiling across cells when cell dimensions are odd. + // variantOffset encodes: bit 1 = x pixel shift, bit 0 = y pixel shift. const dx = (variantOffset >> 1) & 1; const dy = variantOffset & 1; if (dx !== 0 || dy !== 0) { @@ -715,10 +713,8 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO for (let x = 0; x < result.length; x += 2) { // Translate from 0-1 to 0-cellWidth result[x] *= cellWidth - (leftPadding * devicePixelRatio) - (rightPadding * devicePixelRatio); - /* - * Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally - * clamp to the cell bounds. - */ + // Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally + // clamp to the cell bounds. if (doClamp && result[x] !== 0) { const rounded = Math.round(result[x] + 0.5) - 0.5; result[x] = clampToCell ? clamp(rounded, cellWidth, 0) : rounded; @@ -730,10 +726,8 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO for (let y = 1; y < result.length; y += 2) { // Translate from 0-1 to 0-cellHeight result[y] *= cellHeight; - /* - * Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally - * clamp to the cell bounds. - */ + // Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally + // clamp to the cell bounds. if (doClamp && result[y] !== 0) { const rounded = Math.round(result[y] + 0.5) - 0.5; result[y] = clampToCell ? clamp(rounded, cellHeight, 0) : rounded; diff --git a/addons/addon-webgl/test/WebglRenderer.test.ts b/addons/addon-webgl/test/WebglRenderer.test.ts index b39521180..c425fa000 100644 --- a/addons/addon-webgl/test/WebglRenderer.test.ts +++ b/addons/addon-webgl/test/WebglRenderer.test.ts @@ -24,11 +24,9 @@ test.beforeAll(async ({ browser }) => { test.afterAll(async () => await ctx.page.close()); test.describe('WebGL Renderer Integration Tests', async () => { - /* - * HACK: webgl2 is often not supported in headless firefox on Linux - * https://github.com/microsoft/playwright/issues/11566 - * also disable safari due to #5852 - */ + // HACK: webgl2 is often not supported in headless firefox on Linux + // https://github.com/microsoft/playwright/issues/11566 + // also disable safari due to #5852 if (platform() === 'linux') { test.skip(({ browserName }) => browserName === 'firefox' || browserName === 'webkit'); } diff --git a/demo/client/client.ts b/demo/client/client.ts index b1718f64f..aab723b7e 100644 --- a/demo/client/client.ts +++ b/demo/client/client.ts @@ -173,11 +173,9 @@ const createNewWindowButtonHandler: () => void = () => { terminalContainer.id = 'terminal-container'; win.document.body.appendChild(terminalContainer); - /* - * Stylesheets are needed to get the terminal in the popout window to render - * correctly. We also need to wait for them to load before creating the - * terminal, otherwise we will not compute the correct metrics when rendering. - */ + // Stylesheets are needed to get the terminal in the popout window to render + // correctly. We also need to wait for them to load before creating the + // terminal, otherwise we will not compute the correct metrics when rendering. let pendingStylesheets = 0; for (const linkNode of document.querySelectorAll('head link[rel=stylesheet]')) { const newLink = document.createElement('link'); @@ -361,10 +359,8 @@ function createTerminal(): Terminal { const resizeObserver = new ResizeObserver(entries => { if (optionsWindow.autoResize) { - /* - * In general this should be debounced to avoid excessive work on the main - * thread by firing the expensive resize action repeatedly - */ + // In general this should be debounced to avoid excessive work on the main + // thread by firing the expensive resize action repeatedly addons.fit.instance!.fit(); } }); @@ -551,10 +547,8 @@ function initAddons(term: Terminal): void { addon.instance = undefined; } if (name === 'ligatures') { - /* - * Recreate webgl when ligatures are toggled so texture atlas picks up any font feature - * settings changes - */ + // Recreate webgl when ligatures are toggled so texture atlas picks up any font feature + // settings changes if (addons.webgl.instance) { preDisposeWebgl(); addons.webgl.instance.dispose(); diff --git a/demo/client/components/window/addonImageWindow.ts b/demo/client/components/window/addonImageWindow.ts index c089cf5f7..392b7177b 100644 --- a/demo/client/components/window/addonImageWindow.ts +++ b/demo/client/components/window/addonImageWindow.ts @@ -165,18 +165,16 @@ export class AddonImageWindow extends BaseWindow implements IControlWindow { this._terminal.element!.addEventListener('click', (ev: MouseEvent) => { if (!ev.ctrlKey || !imageAddon) return; - /* - * TODO... - * if (ev.altKey) { - * const sel = term.getSelectionPosition(); - * if (sel) { - * addons.image.instance - * .extractCanvasAtBufferRange(term.getSelectionPosition()) - * ?.toBlob(data => window.open(URL.createObjectURL(data), '_blank')); - * return; - * } - * } - */ + // TODO... + // if (ev.altKey) { + // const sel = term.getSelectionPosition(); + // if (sel) { + // addons.image.instance + // .extractCanvasAtBufferRange(term.getSelectionPosition()) + // ?.toBlob(data => window.open(URL.createObjectURL(data), '_blank')); + // return; + // } + // } const pos = (this._terminal as any)._core._mouseService!.getCoords(ev, (this._terminal as any)._core.screenElement!, this._terminal.cols, this._terminal.rows); const x = pos[0] - 1; diff --git a/demo/client/components/window/addonProgressWindow.ts b/demo/client/components/window/addonProgressWindow.ts index fe4e51ca7..167c9ca28 100644 --- a/demo/client/components/window/addonProgressWindow.ts +++ b/demo/client/components/window/addonProgressWindow.ts @@ -66,13 +66,11 @@ export class AddonProgressWindow extends BaseWindow implements IControlWindow { const colors = { 0: '', 1: 'green', 2: 'red', 3: '', 4: 'yellow' }; function progressHandler({ state, value }: IProgressState): void { - /* - * Simulate windows taskbar hack by windows terminal: - * Since the taskbar has no means to indicate error/pause state other than by coloring - * the current progress, we move 0 to 10% and distribute higher values in the remaining 90 % - * NOTE: This is most likely not what you want to do for other progress indicators, - * that have a proper visual state for error/paused. - */ + // Simulate windows taskbar hack by windows terminal: + // Since the taskbar has no means to indicate error/pause state other than by coloring + // the current progress, we move 0 to 10% and distribute higher values in the remaining 90 % + // NOTE: This is most likely not what you want to do for other progress indicators, + // that have a proper visual state for error/paused. value = Math.min(10 + value * 0.9, 100); document.getElementById('progress-percent')!.style.width = `${value}%`; document.getElementById('progress-percent')!.style.backgroundColor = colors[state]; diff --git a/demo/client/components/window/testWindow.ts b/demo/client/components/window/testWindow.ts index e66795ab1..1ea6b3e67 100644 --- a/demo/client/components/window/testWindow.ts +++ b/demo/client/components/window/testWindow.ts @@ -435,11 +435,9 @@ function customGlyphAlignmentHandler(term: Terminal): void { } function customGlyphRangesHandler(term: Terminal): void { - /* - * Box Drawing - * 2500-257F - * https://www.unicode.org/charts/PDF/U2500.pdf - */ + // Box Drawing + // 2500-257F + // https://www.unicode.org/charts/PDF/U2500.pdf writeUnicodeTable(term, 'Box Drawing', 0x2500, 0x257F, [ ['Light and heavy solid lines', 0x2500, 0x2503], ['Light and heavy dashed lines', 0x2504, 0x250B], @@ -452,49 +450,39 @@ function customGlyphRangesHandler(term: Terminal): void { ['Light and heavy half lines', 0x2574, 0x257B], ['Mixed light and heavy lines', 0x257C, 0x257F], ]); - /* - * Box Elements - * 2580-259F - * https://www.unicode.org/charts/PDF/U2580.pdf - */ + // Box Elements + // 2580-259F + // https://www.unicode.org/charts/PDF/U2580.pdf writeUnicodeTable(term, 'Box Elements', 0x2580, 0x259F, [ ['Block elements', 0x2580, 0x2590], ['Shade characters', 0x2591, 0x2593], ['Block elements', 0x2594, 0x2595], ['Terminal graphic characters', 0x2596, 0x259F], ]); - /* - * Braille Patterns - * 2800-28FF - * https://www.unicode.org/charts/PDF/U2800.pdf - */ + // Braille Patterns + // 2800-28FF + // https://www.unicode.org/charts/PDF/U2800.pdf writeUnicodeTable(term, 'Braille patterns', 0x2800, 0x28FF, [ ['Braille patterns', 0x2800, 0x28FF], ]); - /* - * Powerline Symbols - * Range: E0A0–E0D4 - * https://github.com/ryanoasis/nerd-fonts - */ + // Powerline Symbols + // Range: E0A0–E0D4 + // https://github.com/ryanoasis/nerd-fonts writeUnicodeTable(term, 'Powerline Symbols', 0xE0A0, 0xE0D4, [ ['Powerline symbols', 0xE0A0, 0xE0B3, [0xE0A4, 0xE0A5, 0xE0A6, 0xE0A7, 0xE0A8, 0xE0A9, 0xE0AA, 0xE0AB, 0xE0AC, 0xE0AD, 0xE0AE, 0xE0AF]], ['Powerline extra symbols', 0xE0B4, 0xE0D4, [0xE0C9, 0xE0CB, 0xE0D3]], ]); - /* - * Progress Indicators - * Range: EE00-EE0B - * https://github.com/tonsky/FiraCode - */ + // Progress Indicators + // Range: EE00-EE0B + // https://github.com/tonsky/FiraCode writeUnicodeTable(term, 'Progress Indicators', 0xEE00, 0xEE0B, [ ['Progress bars', 0xEE00, 0xEE05], ['Progress spinners', 0xEE06, 0xEE0B], ]); - /* - * https://github.com/ryanoasis/nerd-fonts/pull/1733 - * Git Branch Symbols - * F5D0-F60D - * https://github.com/xtermjs/xterm.js/issues/5477 - */ + // https://github.com/ryanoasis/nerd-fonts/pull/1733 + // Git Branch Symbols + // F5D0-F60D + // https://github.com/xtermjs/xterm.js/issues/5477 writeUnicodeTable(term, 'Git Branch Symbols', 0xF5D0, 0xF5FB, [ ['Straight lines', 0xF5D0, 0xF5D5], ['Curved lines', 0xF5D6, 0xF5D9], @@ -502,11 +490,9 @@ function customGlyphRangesHandler(term: Terminal): void { ['Nodes', 0xF5EE, 0xF5FB], ['Extended nodes', 0xF5FC, 0xF60D], ]); - /* - * Symbols for Legacy Computing - * Range: 1FB00–1FBFF - * https://www.unicode.org/charts/PDF/U1FB00.pdf - */ + // Symbols for Legacy Computing + // Range: 1FB00–1FBFF + // https://www.unicode.org/charts/PDF/U1FB00.pdf writeUnicodeTable(term, 'Symbols for Legacy Computing', 0x1FB00, 0x1FBFF, [ ['Block mosaic terminal graphic characters (Sextants)', 0x1FB00, 0x1FB3B], ['Smooth mosaic terminal graphic characters', 0x1FB3C, 0x1FB6F], @@ -704,10 +690,8 @@ function loadTest(term: Terminal, addons: AddonCollection): void { for (let i = 1; i < count + 1; i++) { data[i] = 0x61 + Math.floor(Math.random() * (0x7A - 0x61)); } - /* - * End each line with \r so the cursor remains constant, this is what ls/tree do and improves - * performance significantly due to the cursor DOM element not needing to change - */ + // End each line with \r so the cursor remains constant, this is what ls/tree do and improves + // performance significantly due to the cursor DOM element not needing to change data[data.length - 1] = 0x0D; // \r testData.push(data); } @@ -739,10 +723,8 @@ async function loadTestLongLines(term: Terminal, addons: AddonCollection): Promi for (let i = 1; i < count + 1; i++) { data[i] = 0x61 + Math.floor(Math.random() * (0x7A - 0x61)); } - /* - * End each line with \r so the cursor remains constant, this is what ls/tree do and improves - * performance significantly due to the cursor DOM element not needing to change - */ + // End each line with \r so the cursor remains constant, this is what ls/tree do and improves + // performance significantly due to the cursor DOM element not needing to change data[data.length - 1] = 0x0D; // \r testData.push(data); } @@ -752,10 +734,8 @@ async function loadTestLongLines(term: Terminal, addons: AddonCollection): Promi try { term.write(d); } catch { - /* - * Flush events when cap is hit and try again(workaround for not having flow control in - * demo) - */ + // Flush events when cap is hit and try again(workaround for not having flow control in + // demo) await new Promise(r => term.write('', () => r())); term.write(d); } diff --git a/demo/client/unicodeTable.ts b/demo/client/unicodeTable.ts index 9755b911a..61bba36f2 100644 --- a/demo/client/unicodeTable.ts +++ b/demo/client/unicodeTable.ts @@ -220,10 +220,8 @@ export function writeUnicodeTable(term: Terminal, name: string, start: number, e term.write('\n\r'); - /* - * Render reserved labels that appear after the first label (below the row) - * Show one label per non-contiguous reserved range - */ + // Render reserved labels that appear after the first label (below the row) + // Show one label per non-contiguous reserved range const lateReserved = rowLabels.length > 0 ? rowReserved.filter(r => r.col >= rowLabels[0].col) : rowReserved; diff --git a/demo/server/server.ts b/demo/server/server.ts index 5bd594dcb..ef98eb84c 100644 --- a/demo/server/server.ts +++ b/demo/server/server.ts @@ -180,11 +180,9 @@ function startServer(): void { } 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 - * the problem and how to implement flow control at https://xtermjs.org/docs/guides/flowcontrol/ - */ + // 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 + // the problem and how to implement flow control at https://xtermjs.org/docs/guides/flowcontrol/ term.onData(function(data) { try { send(data as string & Buffer); @@ -211,10 +209,8 @@ function startServer(): void { app.listen(port, host, 0); } -/* - * HACK: There is an EPIPE error thrown when reloading a page. This only seems to happen on Windows - * and it's unclear why it happens. Suppressing the error here since this is just the demo server. - */ +// HACK: There is an EPIPE error thrown when reloading a page. This only seems to happen on Windows +// and it's unclear why it happens. Suppressing the error here since this is just the demo server. process.on('uncaughtException', (error) => { if (process.platform === 'win32' && error.message === 'read EPIPE') { return; diff --git a/eslint.config.mjs b/eslint.config.mjs index 18a4dfb59..e3d8a4dba 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -86,9 +86,9 @@ export default tseslint.config( 'eqeqeq': ['warn', 'always'], 'jsdoc/check-alignment': 'warn', 'jsdoc/check-param-names': 'warn', + 'jsdoc/multiline-blocks': 'warn', 'jsdoc/no-multi-asterisks': 'warn', 'keyword-spacing': 'warn', - 'multiline-comment-style': ['warn', 'starred-block'], 'max-len': ['warn', { code: 1000, comments: 100, diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index 5960c8d4f..6fc421337 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -21,10 +21,8 @@ const enum BoundaryPosition { BOTTOM } -/* - * Turn this on to unhide the accessibility tree and display it under - * (instead of overlapping with) the terminal. - */ +// Turn this on to unhide the accessibility tree and display it under +// (instead of overlapping with) the terminal. const DEBUG = false; export class AccessibilityManager extends Disposable { @@ -225,10 +223,8 @@ export class AccessibilityManager extends Disposable { return; } - /* - * Don't scroll when the last focused item was not the second row (focus is going the other - * direction) - */ + // Don't scroll when the last focused item was not the second row (focus is going the other + // direction) if (e.relatedTarget !== beforeBoundaryElement) { return; } @@ -287,11 +283,9 @@ export class AccessibilityManager extends Disposable { } if (selection.isCollapsed) { - /* - * Only do something when the anchorNode is inside the row container. This - * behavior mirrors what we do with mouse --- if the mouse clicks - * somewhere outside of the terminal, we don't clear the selection. - */ + // Only do something when the anchorNode is inside the row container. This + // behavior mirrors what we do with mouse --- if the mouse clicks + // somewhere outside of the terminal, we don't clear the selection. if (this._rowContainer.contains(selection.anchorNode)) { this._terminal.clearSelection(); } diff --git a/src/browser/Clipboard.ts b/src/browser/Clipboard.ts index 513dfa60c..aaf8a48e4 100644 --- a/src/browser/Clipboard.ts +++ b/src/browser/Clipboard.ts @@ -22,10 +22,8 @@ export function bracketTextForPaste(text: string, bracketedPasteMode: boolean): if (!bracketedPasteMode) { return text; } - /* - * Sanitize pasted text to prevent injected escape sequences (e.g. exiting bracketed paste) - * by replacing ESC (\x1b) with its visible representation U+241B (␛). - */ + // Sanitize pasted text to prevent injected escape sequences (e.g. exiting bracketed paste) + // by replacing ESC (\x1b) with its visible representation U+241B (␛). const sanitizedText = text.replace(/\x1b/g, '\u241b'); return `\x1b[200~${sanitizedText}\x1b[201~`; } diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index 519d8b1dc..52eb00025 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -323,10 +323,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { * Binds the desired blur behavior on a given terminal object. */ private _handleTextAreaBlur(): void { - /* - * Text can safely be removed on blur. Doing it earlier could interfere with - * screen readers reading it out. - */ + // Text can safely be removed on blur. Doing it earlier could interfere with + // screen readers reading it out. this.textarea!.value = ''; this.refresh(this.buffer.y, this.buffer.y); if (this.coreService.decPrivateModes.sendFocus) { @@ -352,10 +350,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { const cursorTop = this.buffer.y * this._renderService.dimensions.css.cell.height; const cursorLeft = cursorX * this._renderService.dimensions.css.cell.width; - /* - * Sync the textarea to the exact position of the composition view so the IME knows where the - * text is. - */ + // Sync the textarea to the exact position of the composition view so the IME knows where the + // text is. this.textarea.style.left = cursorLeft + 'px'; this.textarea.style.top = cursorTop + 'px'; this.textarea.style.width = cellWidth + 'px'; @@ -372,10 +368,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { // Bind clipboard functionality this._register(addDisposableListener(this.element!, 'copy', (event: ClipboardEvent) => { - /* - * If mouse events are active it means the selection manager is disabled and - * copy should be handled by the host program. - */ + // If mouse events are active it means the selection manager is disabled and + // copy should be handled by the host program. if (!this.hasSelection()) { return; } @@ -399,16 +393,12 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { })); } - /* - * Move the textarea under the cursor when middle clicking on Linux to ensure - * middle click to paste selection works. This only appears to work in Chrome - * at the time is writing. - */ + // Move the textarea under the cursor when middle clicking on Linux to ensure + // middle click to paste selection works. This only appears to work in Chrome + // at the time is writing. if (Browser.isLinux) { - /* - * Use auxclick event over mousedown the latter doesn't seem to work. Note - * that the regular click event doesn't fire for the middle mouse button. - */ + // Use auxclick event over mousedown the latter doesn't seem to work. Note + // that the regular click event doesn't fire for the middle mouse button. this._register(addDisposableListener(this.element!, 'auxclick', (event: MouseEvent) => { if (event.button === 1) { moveTextAreaUnderMouseCursor(event, this.textarea!, this.screenElement!); @@ -425,13 +415,11 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._register(addDisposableListener(this.textarea!, 'keydown', (ev: KeyboardEvent) => this._keyDown(ev), true)); this._register(addDisposableListener(this.textarea!, 'keypress', (ev: KeyboardEvent) => this._keyPress(ev), true)); this._register(addDisposableListener(this.textarea!, 'compositionstart', () => { - /* - * Ensure the textarea is synced to the latest cursor location before composition begins. This - * is to workaround a problem where highly dynamic TUIs like agentic CLIs reprint agressively - * would cause the IME to appear in the wrong position. The theory is that when the IME is - * triggered during a partial render the textarea position becomes locked and will not move - * until it is hidden and a custom move occurs. - */ + // Ensure the textarea is synced to the latest cursor location before composition begins. This + // is to workaround a problem where highly dynamic TUIs like agentic CLIs reprint agressively + // would cause the IME to appear in the wrong position. The theory is that when the IME is + // triggered during a partial render the textarea position becomes locked and will not move + // until it is hidden and a custom move occurs. this._syncTextArea(); this._compositionHelper!.compositionstart(); this._compositionHelper!.updateCompositionElements(); @@ -479,10 +467,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._register(this.optionsService.onSpecificOptionChange('allowTransparency', value => this.element!.classList.toggle('allow-transparency', value))); parent.appendChild(this.element); - /* - * Performance: Use a document fragment to build the terminal - * viewport and helper elements detached from the DOM - */ + // Performance: Use a document fragment to build the terminal + // viewport and helper elements detached from the DOM const fragment = this._document.createDocumentFragment(); this._viewportElement = this._document.createElement('div'); this._viewportElement.classList.add('xterm-viewport'); @@ -491,10 +477,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this.screenElement = this._document.createElement('div'); this.screenElement.classList.add('xterm-screen'); this._register(addDisposableListener(this.screenElement, 'mousemove', (ev: MouseEvent) => this.updateCursorStyle(ev))); - /* - * Create the container that will hold helpers like the textarea for - * capturing DOM Events. Then produce the helpers. - */ + // Create the container that will hold helpers like the textarea for + // capturing DOM Events. Then produce the helpers. this._helperContainer = this._document.createElement('div'); this._helperContainer.classList.add('xterm-helpers'); this.screenElement.appendChild(this._helperContainer); @@ -504,10 +488,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this.textarea.classList.add('xterm-helper-textarea'); this.textarea.setAttribute('aria-label', Strings.promptLabel.get()); if (!Browser.isChromeOS) { - /* - * ChromeVox on ChromeOS does not like this. See - * https://issuetracker.google.com/issues/260170397 - */ + // ChromeVox on ChromeOS does not like this. See + // https://issuetracker.google.com/issues/260170397 this.textarea.setAttribute('aria-multiline', 'false'); } this.textarea.setAttribute('autocorrect', 'off'); @@ -517,10 +499,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._register(this.optionsService.onSpecificOptionChange('disableStdin', () => textarea.readOnly = this.optionsService.rawOptions.disableStdin)); this.textarea.readOnly = this.optionsService.rawOptions.disableStdin; - /* - * Register the core browser service before the generic textarea handlers are registered so it - * handles them first. Otherwise the renderers may use the wrong focus state. - */ + // Register the core browser service before the generic textarea handlers are registered so it + // handles them first. Otherwise the renderers may use the wrong focus state. this._coreBrowserService = this._register(this._instantiationService.createInstance(CoreBrowserService, this.textarea, parent.ownerDocument.defaultView ?? window, @@ -618,11 +598,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._register(this._selectionService.onSelectionChange(() => this._onSelectionChange.fire())); this._register(this._selectionService.onRequestRedraw(e => this._renderService!.handleSelectionChanged(e.start, e.end, e.columnSelectMode))); this._register(this._selectionService.onLinuxMouseSelection(text => { - /* - * If there's a new selection, put it into the textarea, focus and select it - * in order to register it as a selection on the OS. This event is fired - * only on Linux to enable middle click to paste selection. - */ + // If there's a new selection, put it into the textarea, focus and select it + // in order to register it as a selection on the OS. This event is fired + // only on Linux to enable middle click to paste selection. this.textarea!.value = text; this.textarea!.focus(); this.textarea!.select(); @@ -647,10 +625,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { } if (this.options.screenReaderMode) { - /* - * Note that this must be done *after* the renderer is created in order to - * ensure the correct order of the dprchange event - */ + // Note that this must be done *after* the renderer is created in order to + // ensure the correct order of the dprchange event this._accessibilityManager.value = this._instantiationService.createInstance(AccessibilityManager, this); } this._register(this.optionsService.onSpecificOptionChange('screenReaderMode', e => this._handleScreenReaderModeOptionChange(e))); @@ -675,10 +651,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { // Initialize global actions that need to be taken on the document. this._initGlobal(); - /* - * Listen for mouse events and translate - * them into terminal mouse protocols. - */ + // Listen for mouse events and translate + // them into terminal mouse protocols. this._mouseService.bindMouse({ element: this.element!, screenElement: this.screenElement!, @@ -918,11 +892,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { return true; } - /* - * HACK: Process A-Z in the keypress event to fix an issue with macOS IMEs where lower case - * letters cannot be input while caps lock is on. Skip this hack when using kitty protocol - * or Win32 input mode as they need to send proper sequences for all key events. - */ + // HACK: Process A-Z in the keypress event to fix an issue with macOS IMEs where lower case + // letters cannot be input while caps lock is on. Skip this hack when using kitty protocol + // or Win32 input mode as they need to send proper sequences for all key events. if (!this._keyboardService.useKitty && !this._keyboardService.useWin32InputMode && event.key && !event.ctrlKey && !event.altKey && !event.metaKey && event.key.length === 1) { if (event.key.charCodeAt(0) >= 65 && event.key.charCodeAt(0) <= 90) { return true; @@ -934,11 +906,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { return true; } - /* - * If ctrl+c or enter is being sent, clear out the textarea. This is done so that screen readers - * will announce deleted characters. This will not work 100% of the time but it should cover - * most scenarios. - */ + // If ctrl+c or enter is being sent, clear out the textarea. This is done so that screen readers + // will announce deleted characters. This will not work 100% of the time but it should cover + // most scenarios. if (result.key === C0.ETX || result.key === C0.CR) { this.textarea!.value = ''; } @@ -948,12 +918,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._showCursor(); this.coreService.triggerDataEvent(result.key, !wasModifierOnly); - /* - * Cancel events when not in screen reader mode so events don't get bubbled up and handled by - * other listeners. When screen reader mode is enabled, we don't cancel them (unless ctrl or alt - * is also depressed) so that the cursor textarea can be updated, which triggers the screen - * reader to read it. - */ + // Cancel events when not in screen reader mode so events don't get bubbled up and handled by + // other listeners. When screen reader mode is enabled, we don't cancel them (unless ctrl or alt + // is also depressed) so that the cursor textarea can be updated, which triggers the screen + // reader to read it. if (!this.optionsService.rawOptions.screenReaderMode || event.altKey || event.ctrlKey) { event.preventDefault(); event.stopPropagation(); @@ -1042,10 +1010,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._keyPressHandled = true; - /* - * The key was handled so clear the dead key state, otherwise certain keystrokes like arrow - * keys could be ignored - */ + // The key was handled so clear the dead key state, otherwise certain keystrokes like arrow + // keys could be ignored this._unprocessedDeadKey = false; return true; @@ -1058,20 +1024,16 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { * @param ev The input event to be handled. */ protected _inputEvent(ev: InputEvent): boolean { - /* - * Only support emoji IMEs when screen reader mode is disabled as the event must bubble up to - * support reading out character input which can doubling up input characters - * Based on these event traces: https://github.com/xtermjs/xterm.js/issues/3679 - */ + // Only support emoji IMEs when screen reader mode is disabled as the event must bubble up to + // support reading out character input which can doubling up input characters + // Based on these event traces: https://github.com/xtermjs/xterm.js/issues/3679 if (ev.data && ev.inputType === 'insertText' && (!ev.composed || !this._keyDownSeen) && !this.optionsService.rawOptions.screenReaderMode) { if (this._keyPressHandled) { return false; } - /* - * The key was handled so clear the dead key state, otherwise certain keystrokes like arrow - * keys could be ignored - */ + // The key was handled so clear the dead key state, otherwise certain keystrokes like arrow + // keys could be ignored this._unprocessedDeadKey = false; const text = ev.data; @@ -1121,10 +1083,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { for (let i = 1; i < this.rows; i++) { this.buffer.lines.push(this.buffer.getBlankLine(DEFAULT_ATTR_DATA)); } - /* - * IMPORTANT: Fire scroll event before viewport is reset. This ensures embedders get the clear - * scroll event and that the viewport's state will be valid for immediate writes. - */ + // IMPORTANT: Fire scroll event before viewport is reset. This ensures embedders get the clear + // scroll event and that the viewport's state will be valid for immediate writes. this._onScroll.fire({ position: this.buffer.ydisp }); this.refresh(0, this.rows - 1); } diff --git a/src/browser/Linkifier.ts b/src/browser/Linkifier.ts index e99c41dfa..31c91daee 100644 --- a/src/browser/Linkifier.ts +++ b/src/browser/Linkifier.ts @@ -87,11 +87,9 @@ export class Linkifier extends Disposable implements ILinkifier2 { } private _handleHover(position: IBufferCellPosition): void { - /* - * TODO: This currently does not cache link provider results across wrapped lines, activeLine - * should be something like `activeRange: {startY, endY}` - * Check if we need to clear the link - */ + // TODO: This currently does not cache link provider results across wrapped lines, activeLine + // should be something like `activeRange: {startY, endY}` + // Check if we need to clear the link if (this._activeLine !== position.y || this._wasResized) { this._clearCurrentLink(); this._askForLink(position, false); @@ -127,11 +125,9 @@ export class Linkifier extends Disposable implements ILinkifier2 { const existingReply = this._activeProviderReplies?.get(i); // If there isn't a reply, the provider hasn't responded yet. - /* - * TODO: If there isn't a reply yet it means that the provider is still resolving. Ensuring - * provideLinks isn't triggered again saves ILink.hover firing twice though. This probably - * needs promises to get fixed - */ + // TODO: If there isn't a reply yet it means that the provider is still resolving. Ensuring + // provideLinks isn't triggered again saves ILink.hover firing twice though. This probably + // needs promises to get fixed if (existingReply) { linkProvided = this._checkLinkProviderResult(i, position, linkProvided); } @@ -144,10 +140,8 @@ export class Linkifier extends Disposable implements ILinkifier2 { this._activeProviderReplies?.set(i, linksWithState); linkProvided = this._checkLinkProviderResult(i, position, linkProvided); - /* - * If all providers have responded, remove lower priority links that intersect ranges of - * higher priority links - */ + // If all providers have responded, remove lower priority links that intersect ranges of + // higher priority links if (this._activeProviderReplies?.size === this._linkProviderService.linkProviders.length) { this._removeIntersectingLinks(position.y, this._activeProviderReplies); } @@ -193,10 +187,8 @@ export class Linkifier extends Disposable implements ILinkifier2 { } } - /* - * If all providers with higher priority came back undefined, then this provider's link for - * the position should be used - */ + // If all providers with higher priority came back undefined, then this provider's link for + // the position should be used if (!hasLinkBefore && links) { const linkAtPosition = links.find(link => this._linkAtPosition(link.link, position)); if (linkAtPosition) { @@ -304,19 +296,15 @@ export class Linkifier extends Disposable implements ILinkifier2 { } }); - /* - * Listen to viewport changes to re-render the link under the cursor (only when the line the - * link is on changes) - */ + // Listen to viewport changes to re-render the link under the cursor (only when the line the + // link is on changes) this._linkCacheDisposables.push(this._renderService.onRenderedViewportChange(e => { // Sanity check, this shouldn't happen in practice as this listener would be disposed if (!this._currentLink) { return; } - /* - * When start is 0 a scroll most likely occurred, make sure links above the fold also get - * cleared. - */ + // When start is 0 a scroll most likely occurred, make sure links above the fold also get + // cleared. const start = e.start === 0 ? 0 : e.start + 1 + this._bufferService.buffer.ydisp; const end = this._bufferService.buffer.ydisp + 1 + e.end; // Only clear the link if the viewport change happened on this line diff --git a/src/browser/OscLinkProvider.ts b/src/browser/OscLinkProvider.ts index 8ed823203..b6a3cce0a 100644 --- a/src/browser/OscLinkProvider.ts +++ b/src/browser/OscLinkProvider.ts @@ -33,10 +33,8 @@ export class OscLinkProvider implements ILinkProvider { let currentStart = -1; let finishLink = false; for (let x = 0; x < lineLength; x++) { - /* - * Minor optimization, only check for content if there isn't a link in case the link ends with - * a null cell - */ + // Minor optimization, only check for content if there isn't a link in case the link ends with + // a null cell if (currentStart === -1 && !line.hasContent(x)) { continue; } @@ -109,10 +107,8 @@ export class OscLinkProvider implements ILinkProvider { } } - /* - * TODO: Handle fetching and returning other link ranges to underline other links with the same - * id - */ + // TODO: Handle fetching and returning other link ranges to underline other links with the same + // id callback(result); } } diff --git a/src/browser/Terminal.test.ts b/src/browser/Terminal.test.ts index 425955982..d359fa28c 100644 --- a/src/browser/Terminal.test.ts +++ b/src/browser/Terminal.test.ts @@ -576,16 +576,14 @@ describe('Terminal', () => { beforeEach(() => { term.clearSelection = () => { }; - /* - * term.compositionHelper = { - * isComposing: false, - * keydown: { - * bind: () => { - * return () => { return true; }; - * } - * } - * }; - */ + // term.compositionHelper = { + // isComposing: false, + // keydown: { + // bind: () => { + // return () => { return true; }; + // } + // } + // }; evKeyDown = { preventDefault: () => { }, stopPropagation: () => { }, @@ -1150,10 +1148,8 @@ describe('Terminal', () => { // FIXME: move to common/CoreTerminal.test once the trimming is moved over describe('marker lifecycle', () => { - /* - * create a 10x5 terminal with markers on every line - * to test marker lifecycle under various terminal actions - */ + // create a 10x5 terminal with markers on every line + // to test marker lifecycle under various terminal actions let markers: IMarker[]; let disposeStack: IMarker[]; let term: TestTerminal; diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index 8d609cb23..997312901 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -43,10 +43,8 @@ export class TimeBasedDebouncer implements IRenderDebouncer { this._rowStart = this._rowStart !== undefined ? Math.min(this._rowStart, rowStart) : rowStart; this._rowEnd = this._rowEnd !== undefined ? Math.max(this._rowEnd, rowEnd) : rowEnd; - /* - * Only refresh if the time since last refresh is above a threshold, otherwise wait for - * enough time to pass before refreshing again. - */ + // Only refresh if the time since last refresh is above a threshold, otherwise wait for + // enough time to pass before refreshing again. const refreshRequestTime: number = performance.now(); if (refreshRequestTime - this._lastRefreshMs >= this._debounceThresholdMS) { // Enough time has lapsed since the last refresh; refresh immediately diff --git a/src/browser/Types.ts b/src/browser/Types.ts index 9c76fb07d..fa08de2e3 100644 --- a/src/browser/Types.ts +++ b/src/browser/Types.ts @@ -201,10 +201,8 @@ export const DEFAULT_ANSI_COLORS = Object.freeze((() => { css.toColor('#eeeeec') ]; - /* - * Fill in the remaining 240 ANSI colors. - * Generate colors (16-231) - */ + // Fill in the remaining 240 ANSI colors. + // Generate colors (16-231) const v = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]; for (let i = 0; i < 216; i++) { const r = v[(i / 36) % 6 | 0]; diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index c629bc7a3..3b6ae8301 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -99,20 +99,16 @@ export class Viewport extends Disposable { this._register(this._bufferService.onResize(() => this.queueSync())); this._register(this._bufferService.buffers.onBufferActivate(() => { - /* - * Reset _latestYDisp when switching buffers to prevent stale scroll position - * from alt buffer contaminating normal buffer scroll position - */ + // Reset _latestYDisp when switching buffers to prevent stale scroll position + // from alt buffer contaminating normal buffer scroll position this._latestYDisp = undefined; this.queueSync(); })); this._register(this._bufferService.onScroll(() => this._sync())); - /* - * Flush deferred viewport sync after a render completes (e.g. after ESU ends - * synchronized output mode). This ensures DOM scroll position updates atomically - * with the canvas render. - */ + // Flush deferred viewport sync after a render completes (e.g. after ESU ends + // synchronized output mode). This ensures DOM scroll position updates atomically + // with the canvas render. this._register(this._renderService.onRender(() => { if (this._needsSyncOnRender) { this._needsSyncOnRender = false; @@ -177,20 +173,16 @@ export class Viewport extends Disposable { if (!this._renderService || this._isSyncing) { return; } - /* - * Defer DOM scroll updates during synchronized output to prevent visible - * scroll position flickering while the canvas content is frozen. - */ + // Defer DOM scroll updates during synchronized output to prevent visible + // scroll position flickering while the canvas content is frozen. if (this._coreService.decPrivateModes.synchronizedOutput) { this._needsSyncOnRender = true; return; } this._isSyncing = true; - /* - * Ignore any onScroll event that happens as a result of dimensions changing as this should - * never cause a scrollLines call, only setScrollPosition can do that. - */ + // Ignore any onScroll event that happens as a result of dimensions changing as this should + // never cause a scrollLines call, only setScrollPosition can do that. this._suppressOnScrollHandler = true; this._scrollableElement.setScrollDimensions({ height: this._renderService.dimensions.css.canvas.height, @@ -198,10 +190,8 @@ export class Viewport extends Disposable { }); this._suppressOnScrollHandler = false; - /* - * If ydisp has been changed by some other component (input/buffer), then stop animating smooth - * scroll and scroll there immediately. - */ + // If ydisp has been changed by some other component (input/buffer), then stop animating smooth + // scroll and scroll there immediately. if (ydisp !== this._latestYDisp) { this._scrollableElement.setScrollPosition({ scrollTop: ydisp * this._renderService.dimensions.css.cell.height diff --git a/src/browser/decorations/BufferDecorationRenderer.ts b/src/browser/decorations/BufferDecorationRenderer.ts index fb4e047a7..e374af260 100644 --- a/src/browser/decorations/BufferDecorationRenderer.ts +++ b/src/browser/decorations/BufferDecorationRenderer.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { ICoreBrowserService, IRenderService } from 'browser/services/Services'; import { Disposable, toDisposable } from 'common/Lifecycle'; diff --git a/src/browser/decorations/ColorZoneStore.ts b/src/browser/decorations/ColorZoneStore.ts index d1795ab8e..d066bedb8 100644 --- a/src/browser/decorations/ColorZoneStore.ts +++ b/src/browser/decorations/ColorZoneStore.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { IInternalDecoration } from 'common/services/Services'; @@ -34,11 +32,9 @@ interface IMinimalDecorationForColorZone { export class ColorZoneStore implements IColorZoneStore { private _zones: IColorZone[] = []; - /* - * The zone pool is used to keep zone objects from being freed between clearing the color zone - * store and fetching the zones. This helps reduce GC pressure since the color zones are - * accumulated on potentially every scroll event. - */ + // The zone pool is used to keep zone objects from being freed between clearing the color zone + // store and fetching the zones. This helps reduce GC pressure since the color zones are + // accumulated on potentially every scroll event. private _zonePool: IColorZone[] = []; private _zonePoolIndex = 0; diff --git a/src/browser/decorations/OverviewRulerRenderer.ts b/src/browser/decorations/OverviewRulerRenderer.ts index 8ea3000b3..b28b25965 100644 --- a/src/browser/decorations/OverviewRulerRenderer.ts +++ b/src/browser/decorations/OverviewRulerRenderer.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { ColorZoneStore, IColorZone, IColorZoneStore } from 'browser/decorations/ColorZoneStore'; import { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; @@ -14,10 +12,8 @@ const enum Constants { OVERVIEW_RULER_BORDER_WIDTH = 1 } -/* - * Helper objects to avoid excessive calculation and garbage collection during rendering. These are - * static values for each render and can be accessed using the decoration position as the key. - */ +// Helper objects to avoid excessive calculation and garbage collection during rendering. These are +// static values for each render and can be accessed using the decoration position as the key. const drawHeight = { full: 0, left: 0, diff --git a/src/browser/input/CompositionHelper.test.ts b/src/browser/input/CompositionHelper.test.ts index 512d5a773..94b5841af 100644 --- a/src/browser/input/CompositionHelper.test.ts +++ b/src/browser/input/CompositionHelper.test.ts @@ -236,11 +236,9 @@ describe('CompositionHelper', () => { it('Should insert middle composition and subsequent input without appending existing trailing text', (done) => { textarea.value = '一二'; - /* - * screenReaderMode keeps textarea content/selection for assistive technologies (eg. screen - * readers), so the caret can be moved within the textarea (eg. via arrow keys) before - * starting composition. - */ + // screenReaderMode keeps textarea content/selection for assistive technologies (eg. screen + // readers), so the caret can be moved within the textarea (eg. via arrow keys) before + // starting composition. textarea.selectionStart = 1; textarea.selectionEnd = 1; diff --git a/src/browser/input/CompositionHelper.ts b/src/browser/input/CompositionHelper.ts index f211e2553..283874139 100644 --- a/src/browser/input/CompositionHelper.ts +++ b/src/browser/input/CompositionHelper.ts @@ -72,10 +72,8 @@ export class CompositionHelper { */ public compositionstart(): void { this._isComposing = true; - /* - * It's important to use the selection here instead of textarea length to avoid conflicts with - * screen reader mode - */ + // It's important to use the selection here instead of textarea length to avoid conflicts with + // screen reader mode const start = this._textarea.selectionStart ?? this._textarea.value.length; const end = this._textarea.selectionEnd ?? start; this._compositionPosition.start = Math.min(start, end); @@ -91,10 +89,8 @@ export class CompositionHelper { * @param ev The event. */ public compositionupdate(ev: Pick): void { - /* - * Mark text as LTR, direction=rtl is used in CSS so the end of the text is followed for long - * compositions - */ + // Mark text as LTR, direction=rtl is used in CSS so the end of the text is followed for long + // compositions this._compositionView.textContent = `\u200E${ev.data}\u200E`; this.updateCompositionElements(); setTimeout(() => { @@ -119,28 +115,22 @@ export class CompositionHelper { public keydown(ev: KeyboardEvent): boolean { if (this._isComposing || this._isSendingComposition) { if (ev.keyCode === 20 || ev.keyCode === 229) { - /* - * 20 is CapsLock, 229 is Enter - * Continue composing if the keyCode is the "composition character" - */ + // 20 is CapsLock, 229 is Enter + // Continue composing if the keyCode is the "composition character" return false; } if (ev.keyCode === 16 || ev.keyCode === 17 || ev.keyCode === 18) { // Continue composing if the keyCode is a modifier key return false; } - /* - * Finish composition immediately. This is mainly here for the case where enter is - * pressed and the handler needs to be triggered before the command is executed. - */ + // Finish composition immediately. This is mainly here for the case where enter is + // pressed and the handler needs to be triggered before the command is executed. this._finalizeComposition(false); } if (ev.keyCode === 229) { - /* - * If the "composition character" is used but gets to this point it means a non-composition - * character (eg. numbers and punctuation) was pressed when the IME was active. - */ + // If the "composition character" is used but gets to this point it means a non-composition + // character (eg. numbers and punctuation) was pressed when the IME was active. this._handleAnyTextareaChanges(); return false; } @@ -166,49 +156,39 @@ export class CompositionHelper { const input = this._textarea.value.substring(this._compositionPosition.start, this._compositionPosition.end); this._coreService.triggerDataEvent(input, true); } else { - /* - * Make a deep copy of the composition position here as a new compositionstart event may - * fire before the setTimeout executes. - */ + // Make a deep copy of the composition position here as a new compositionstart event may + // fire before the setTimeout executes. const currentCompositionPosition = { start: this._compositionPosition.start, end: this._compositionPosition.end }; const currentCompositionSuffix = this._compositionSuffix; - /* - * Since composition* events happen before the changes take place in the textarea on most - * browsers, use a setTimeout with 0ms time to allow the native compositionend event to - * complete. This ensures the correct character is retrieved. - * This solution was used because: - * - The compositionend event's data property is unreliable, at least on Chromium - * - The last compositionupdate event's data property does not always accurately describe - * the character, a counter example being Korean where an ending consonsant can move to - * the following character if the following input is a vowel. - */ + // Since composition* events happen before the changes take place in the textarea on most + // browsers, use a setTimeout with 0ms time to allow the native compositionend event to + // complete. This ensures the correct character is retrieved. + // This solution was used because: + // - The compositionend event's data property is unreliable, at least on Chromium + // - The last compositionupdate event's data property does not always accurately describe + // the character, a counter example being Korean where an ending consonsant can move to + // the following character if the following input is a vowel. this._isSendingComposition = true; setTimeout(() => { // Ensure that the input has not already been sent if (this._isSendingComposition) { this._isSendingComposition = false; let input; - /* - * Add length of data already sent due to keydown event, - * otherwise input characters can be duplicated. (Issue #3191) - */ + // Add length of data already sent due to keydown event, + // otherwise input characters can be duplicated. (Issue #3191) currentCompositionPosition.start += this._dataAlreadySent.length; if (this._isComposing) { - /* - * Use the start position of the new composition to get the string - * if a new composition has started. - */ + // Use the start position of the new composition to get the string + // if a new composition has started. input = this._textarea.value.substring(currentCompositionPosition.start, this._compositionPosition.start); } else { - /* - * Keep support for non-composition characters typed immediately after composition end - * while avoiding re-sending the trailing text that was already present - * before composition started. - */ + // Keep support for non-composition characters typed immediately after composition end + // while avoiding re-sending the trailing text that was already present + // before composition started. const value = this._textarea.value; const valueEnd = currentCompositionSuffix.length > 0 && value.endsWith(currentCompositionSuffix) ? value.length - currentCompositionSuffix.length @@ -280,18 +260,14 @@ export class CompositionHelper { this._compositionView.style.lineHeight = cellHeight + 'px'; this._compositionView.style.fontFamily = this._optionsService.rawOptions.fontFamily; this._compositionView.style.fontSize = this._optionsService.rawOptions.fontSize + 'px'; - /* - * Limit the composition view width to the space between the cursor and - * the terminal's right edge, preventing it from overflowing the terminal. - */ + // Limit the composition view width to the space between the cursor and + // the terminal's right edge, preventing it from overflowing the terminal. const maxWidth = this._bufferService.cols * this._renderService.dimensions.css.cell.width - cursorLeft; this._compositionView.style.maxWidth = maxWidth + 'px'; this._compositionView.style.overflow = 'hidden'; this._compositionView.style.direction = 'rtl'; - /* - * Sync the textarea to the exact position of the composition view so the IME knows where the - * text is. - */ + // Sync the textarea to the exact position of the composition view so the IME knows where the + // text is. const compositionViewBounds = this._compositionView.getBoundingClientRect(); this._textarea.style.left = cursorLeft + 'px'; this._textarea.style.top = cursorTop + 'px'; diff --git a/src/browser/input/Mouse.ts b/src/browser/input/Mouse.ts index 1094cdcf4..c40a7cc78 100644 --- a/src/browser/input/Mouse.ts +++ b/src/browser/input/Mouse.ts @@ -44,11 +44,9 @@ export function getCoords(window: Pick, event: Pick< coords[0] = Math.ceil((coords[0] + (isSelection ? cssCellWidth / 2 : 0)) / cssCellWidth); coords[1] = Math.ceil(coords[1] / cssCellHeight); - /* - * Ensure coordinates are within the terminal viewport. Note that selections - * need an addition point of precision to cover the end point (as characters - * cover half of one char and half of the next). - */ + // Ensure coordinates are within the terminal viewport. Note that selections + // need an addition point of precision to cover the end point (as characters + // cover half of one char and half of the next). coords[0] = Math.min(Math.max(coords[0], 1), colCount + (isSelection ? 1 : 0)); coords[1] = Math.min(Math.max(coords[1], 1), rowCount); diff --git a/src/browser/public/Terminal.ts b/src/browser/public/Terminal.ts index 9255f67f2..75aada048 100644 --- a/src/browser/public/Terminal.ts +++ b/src/browser/public/Terminal.ts @@ -54,11 +54,9 @@ export class Terminal extends Disposable implements ITerminalApi { } private _checkReadonlyOptions(propName: string): void { - /* - * Throw an error if any constructor only option is modified - * from terminal.options - * Modifications from anywhere else are allowed - */ + // Throw an error if any constructor only option is modified + // from terminal.options + // Modifications from anywhere else are allowed if (CONSTRUCTOR_ONLY_OPTIONS.includes(propName)) { throw new Error(`Option "${propName}" can only be set in the constructor`); } diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index 943906524..6f873af7b 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -114,10 +114,8 @@ export class DomRenderer extends Disposable implements IRenderer { this._register(toDisposable(() => { this._element.classList.remove(Constants.TERMINAL_CLASS_PREFIX + this._terminalClass); - /* - * Outside influences such as React unmounts may manipulate the DOM before our disposal. - * https://github.com/xtermjs/xterm.js/issues/2960 - */ + // Outside influences such as React unmounts may manipulate the DOM before our disposal. + // https://github.com/xtermjs/xterm.js/issues/2960 this._rowContainer.remove(); this._selectionContainer.remove(); this._widthCache.dispose(); @@ -186,11 +184,9 @@ export class DomRenderer extends Disposable implements IRenderer { // Base CSS let styles = `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} {` + - /* - * Disabling pointer events circumvents a browser behavior that prevents `click` events from - * being delivered if the target element is replaced during the click. This happened due to - * refresh() being called during the mousedown handler to start a selection. - */ + // Disabling pointer events circumvents a browser behavior that prevents `click` events from + // being delivered if the target element is replaced during the click. This happened due to + // refresh() being called during the mousedown handler to start a selection. ` pointer-events: none;` + ` color: ${colors.foreground.css};` + `}`; @@ -261,11 +257,9 @@ export class DomRenderer extends Disposable implements IRenderer { `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS}.${Constants.CURSOR_BLINK_IDLE_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS} {` + ` animation: none !important;` + `}` + - /* - * !important helps fix an issue where the cursor will not render on top of the selection, - * however it's very hard to fix this issue and retain the blink animation without the use of - * !important. So this edge case fails when cursor blink is on. - */ + // !important helps fix an issue where the cursor will not render on top of the selection, + // however it's very hard to fix this issue and retain the blink animation without the use of + // !important. So this edge case fails when cursor blink is on. `${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` + ` background-color: ${colors.cursor.css};` + ` color: ${colors.cursorAccent.css};` + diff --git a/src/browser/renderer/dom/DomRendererRowFactory.test.ts b/src/browser/renderer/dom/DomRendererRowFactory.test.ts index 4bd5b8e7b..6ca84569a 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.test.ts @@ -359,10 +359,8 @@ describe('DomRendererRowFactory', () => { }); describe('createRow with merged spans', () => { - /* - * for test purpose assume all in codepoints 0..255 are merging - * const ALL_MERGING = new Uint8Array(FontMetrics.MAX); - */ + // for test purpose assume all in codepoints 0..255 are merging + // const ALL_MERGING = new Uint8Array(FontMetrics.MAX); beforeEach(() => { lineData = createEmptyLineData(10); diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts index fc03e27b3..0cff9c25f 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.ts @@ -115,26 +115,20 @@ export class DomRendererRowFactory { // If true, indicates that the current character(s) to draw were joined. let isJoined = false; - /* - * Indicates whether this cell is part of a joined range that should be ignored as it cannot - * be rendered entirely, like the selection state differs across the range. - */ + // Indicates whether this cell is part of a joined range that should be ignored as it cannot + // be rendered entirely, like the selection state differs across the range. let isValidJoinRange = (x >= skipJoinedCheckUntilX); let lastCharX = x; - /* - * Process any joined character ranges as needed. Because of how the - * ranges are produced, we know that they are valid for the characters - * and attributes of our input. - */ + // Process any joined character ranges as needed. Because of how the + // ranges are produced, we know that they are valid for the characters + // and attributes of our input. let cell: ICellData = this._workCell; if (joinedRanges.length > 0 && x === joinedRanges[0][0] && isValidJoinRange) { const range = joinedRanges.shift()!; - /* - * If the ligature's selection state is not consistent, don't join it. This helps the - * selection render correctly regardless whether they should be joined. - */ + // If the ligature's selection state is not consistent, don't join it. This helps the + // selection render correctly regardless whether they should be joined. const firstSelectionState = this._isCellInSelection(range[0], row); for (i = range[0] + 1; i < range[1]; i++) { isValidJoinRange &&= (firstSelectionState === this._isCellInSelection(i, row)); @@ -146,10 +140,8 @@ export class DomRendererRowFactory { } else { isJoined = true; - /* - * We already know the exact start and end column of the joined range, - * so we get the string and width representing it directly - */ + // We already know the exact start and end column of the joined range, + // so we get the string and width representing it directly cell = new JoinedCellData( this._workCell, lineData.translateToString(true, range[0], range[1]), @@ -251,11 +243,9 @@ export class DomRendererRowFactory { oldIsInSelection = isInSelection; if (isJoined) { - /* - * The DOM renderer colors the background of the cursor but for ligatures all cells are - * joined. The workaround here is to show a cursor around the whole ligature so it shows up, - * the cursor looks the same when on any character of the ligature though - */ + // The DOM renderer colors the background of the cursor but for ligatures all cells are + // joined. The workaround here is to show a cursor around the whole ligature so it shows up, + // the cursor looks the same when on any character of the ligature though if (cursorX >= x && cursorX <= lastCharX) { cursorX = x; } @@ -343,10 +333,8 @@ export class DomRendererRowFactory { classes.push(RowCss.STRIKETHROUGH_CLASS); } - /* - * apply link hover underline late, effectively overrides any previous text-decoration - * settings - */ + // apply link hover underline late, effectively overrides any previous text-decoration + // settings if (isLinkHover) { charElement.style.textDecoration = 'underline'; } @@ -365,10 +353,8 @@ export class DomRendererRowFactory { bgColorMode = temp2; } - /* - * Apply any decoration foreground/background overrides, this must happen after inverse has - * been applied - */ + // Apply any decoration foreground/background overrides, this must happen after inverse has + // been applied let bgOverride: IColor | undefined; let fgOverride: IColor | undefined; let isTop = false; @@ -391,19 +377,15 @@ export class DomRendererRowFactory { // Apply selection if (!isTop && isInSelection) { - /* - * If in the selection, force the element to be above the selection to improve contrast and - * support opaque selections. The applies background is not actually needed here as - * selection is drawn in a seperate container, the main purpose of this to ensuring minimum - * contrast ratio - */ + // If in the selection, force the element to be above the selection to improve contrast and + // support opaque selections. The applies background is not actually needed here as + // selection is drawn in a seperate container, the main purpose of this to ensuring minimum + // contrast ratio bgOverride = this._coreBrowserService.isFocused ? colors.selectionBackgroundOpaque : colors.selectionInactiveBackgroundOpaque; bg = bgOverride.rgba >> 8 & 0xFFFFFF; bgColorMode = Attributes.CM_RGB; - /* - * Since an opaque selection is being rendered, the selection pretends to be a decoration to - * ensure text is drawn above the selection. - */ + // Since an opaque selection is being rendered, the selection pretends to be a decoration to + // ensure text is drawn above the selection. isTop = true; // Apply selection foreground if applicable if (colors.selectionForeground) { @@ -477,11 +459,9 @@ export class DomRendererRowFactory { } } - /* - * apply CSS classes - * slightly faster than using classList by omitting - * checks for doubled entries (code above should not have doublets) - */ + // apply CSS classes + // slightly faster than using classList by omitting + // checks for doubled entries (code above should not have doublets) if (classes.length) { charElement.className = classes.join(' '); classes.length = 0; @@ -524,10 +504,8 @@ export class DomRendererRowFactory { // Calculate and store in cache if (adjustedColor === undefined) { - /* - * Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from - * non-dim cells - */ + // Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from + // non-dim cells const ratio = this._optionsService.rawOptions.minimumContrastRatio / (cell.isDim() ? 2 : 1); adjustedColor = color.ensureContrastRatio(bgOverride ?? bg, fgOverride ?? fg, ratio); cache.setColor((bgOverride ?? bg).rgba, (fgOverride ?? fg).rgba, adjustedColor ?? null); diff --git a/src/browser/renderer/dom/WidthCache.ts b/src/browser/renderer/dom/WidthCache.ts index 1bf66204d..b598bbd6f 100644 --- a/src/browser/renderer/dom/WidthCache.ts +++ b/src/browser/renderer/dom/WidthCache.ts @@ -31,20 +31,16 @@ export interface IWidthCacheFontVariantCanvas { } export class WidthCache implements IDisposable { - /* - * flat cache for regular variant up to CacheSettings.FLAT_SIZE - * NOTE: ~4x faster access than holey (serving >>80% of terminal content) - * It has a small memory footprint (only 1MB for full BMP caching), - * still the sweet spot is not reached before touching 32k different codepoints, - * thus we store the remaining <<20% of terminal data in a holey structure. - */ + // flat cache for regular variant up to CacheSettings.FLAT_SIZE + // NOTE: ~4x faster access than holey (serving >>80% of terminal content) + // It has a small memory footprint (only 1MB for full BMP caching), + // still the sweet spot is not reached before touching 32k different codepoints, + // thus we store the remaining <<20% of terminal data in a holey structure. protected _flat = new Float32Array(WidthCacheSettings.FLAT_SIZE); - /* - * holey cache for bold, italic and bold&italic for any string - * FIXME: can grow really big over time (~8.5 MB for full BMP caching), - * so a shared API across terminals is needed - */ + // holey cache for bold, italic and bold&italic for any string + // FIXME: can grow really big over time (~8.5 MB for full BMP caching), + // so a shared API across terminals is needed protected _holey: Map | undefined; private _font = ''; diff --git a/src/browser/renderer/shared/RendererUtils.test.ts b/src/browser/renderer/shared/RendererUtils.test.ts index 87f6a3f20..4512cb6d9 100644 --- a/src/browser/renderer/shared/RendererUtils.test.ts +++ b/src/browser/renderer/shared/RendererUtils.test.ts @@ -13,10 +13,8 @@ describe('RendererUtils', () => { let line = 1; let variantOffset = 0; - /* - * should line 1 - * =,_,=_,=_, - */ + // should line 1 + // =,_,=_,=_, let cells = [cellWidth, cellWidth, doubleCellWidth, doubleCellWidth]; let result = [1, 0, 0, 0]; for (let index = 0; index < cells.length; index++) { @@ -25,10 +23,8 @@ describe('RendererUtils', () => { assert.equal(variantOffset, result[index]); } - /* - * should line 2 - * ==__==__==_,_==__==__==,__==__==__==__==__==__,==__==__==__==__==__==, - */ + // should line 2 + // ==__==__==_,_==__==__==,__==__==__==__==__==__,==__==__==__==__==__==, line = 2; variantOffset = 0; cells = [cellWidth, cellWidth, doubleCellWidth, doubleCellWidth]; @@ -39,10 +35,8 @@ describe('RendererUtils', () => { assert.equal(variantOffset, result[index]); } - /* - * should line 3 - * ===___===__,_===___===_,__===___===___===___==,=___===___===___===___, - */ + // should line 3 + // ===___===__,_===___===_,__===___===___===___==,=___===___===___===___, line = 3; variantOffset = 0; cells = [cellWidth, cellWidth, doubleCellWidth, doubleCellWidth]; diff --git a/src/browser/renderer/shared/RendererUtils.ts b/src/browser/renderer/shared/RendererUtils.ts index 30925f32f..010643648 100644 --- a/src/browser/renderer/shared/RendererUtils.ts +++ b/src/browser/renderer/shared/RendererUtils.ts @@ -13,11 +13,9 @@ export function throwIfFalsy(value: T | undefined | null): T { } export function isPowerlineGlyph(codepoint: number): boolean { - /* - * Only return true for Powerline symbols which require - * different padding and should be excluded from minimum contrast - * ratio standards - */ + // Only return true for Powerline symbols which require + // different padding and should be excluded from minimum contrast + // ratio standards return 0xE0A4 <= codepoint && codepoint <= 0xE0D6; } @@ -50,10 +48,8 @@ export function allowRescaling(codepoint: number | undefined, width: number, gly return ( // Is single cell width width === 1 && - /* - * Glyph exceeds cell bounds, add 50% to avoid hurting readability by rescaling glyphs that - * barely overlap - */ + // Glyph exceeds cell bounds, add 50% to avoid hurting readability by rescaling glyphs that + // barely overlap glyphSizeX > Math.ceil(deviceCellWidth * 1.5) && // Never rescale ascii codepoint !== undefined && codepoint > 0xFF && diff --git a/src/browser/scrollable/abstractScrollbar.ts b/src/browser/scrollable/abstractScrollbar.ts index 0463471b5..64c25d9d8 100644 --- a/src/browser/scrollable/abstractScrollbar.ts +++ b/src/browser/scrollable/abstractScrollbar.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import * as dom from '../Dom'; import { FastDomNode } from './fastDomNode'; diff --git a/src/browser/scrollable/fastDomNode.ts b/src/browser/scrollable/fastDomNode.ts index 206416677..1a795c2be 100644 --- a/src/browser/scrollable/fastDomNode.ts +++ b/src/browser/scrollable/fastDomNode.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ export class FastDomNode { diff --git a/src/browser/scrollable/globalPointerMoveMonitor.ts b/src/browser/scrollable/globalPointerMoveMonitor.ts index b2e9b588a..a80e0507f 100644 --- a/src/browser/scrollable/globalPointerMoveMonitor.ts +++ b/src/browser/scrollable/globalPointerMoveMonitor.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import * as dom from '../Dom'; import { DisposableStore, IDisposable, toDisposable } from 'common/Lifecycle'; diff --git a/src/browser/scrollable/horizontalScrollbar.ts b/src/browser/scrollable/horizontalScrollbar.ts index 8e48ea7ff..299a3e473 100644 --- a/src/browser/scrollable/horizontalScrollbar.ts +++ b/src/browser/scrollable/horizontalScrollbar.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { AbstractScrollbar, ISimplifiedPointerEvent, IScrollbarHost } from './abstractScrollbar'; import { IScrollableElementResolvedOptions } from './scrollableElementOptions'; diff --git a/src/browser/scrollable/mouseEvent.ts b/src/browser/scrollable/mouseEvent.ts index 482ba82aa..a12b63d52 100644 --- a/src/browser/scrollable/mouseEvent.ts +++ b/src/browser/scrollable/mouseEvent.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import * as platform from 'common/Platform'; diff --git a/src/browser/scrollable/scrollable.ts b/src/browser/scrollable/scrollable.ts index 9f8cbb6f7..18275ffe5 100644 --- a/src/browser/scrollable/scrollable.ts +++ b/src/browser/scrollable/scrollable.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { Emitter, IEvent } from 'common/Event'; import { Disposable, IDisposable } from 'common/Lifecycle'; diff --git a/src/browser/scrollable/scrollableElement.ts b/src/browser/scrollable/scrollableElement.ts index a41dde991..4039d0c4e 100644 --- a/src/browser/scrollable/scrollableElement.ts +++ b/src/browser/scrollable/scrollableElement.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import * as dom from '../Dom'; import { FastDomNode } from './fastDomNode'; diff --git a/src/browser/scrollable/scrollableElementOptions.ts b/src/browser/scrollable/scrollableElementOptions.ts index 0e1d06226..037cd5c11 100644 --- a/src/browser/scrollable/scrollableElementOptions.ts +++ b/src/browser/scrollable/scrollableElementOptions.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { ScrollbarVisibility } from './scrollable'; diff --git a/src/browser/scrollable/scrollbarArrow.ts b/src/browser/scrollable/scrollbarArrow.ts index 5784dc8dc..d543669c1 100644 --- a/src/browser/scrollable/scrollbarArrow.ts +++ b/src/browser/scrollable/scrollbarArrow.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { GlobalPointerMoveMonitor } from './globalPointerMoveMonitor'; import { Widget } from './widget'; diff --git a/src/browser/scrollable/scrollbarState.ts b/src/browser/scrollable/scrollbarState.ts index 7b7751f97..f9129f1b0 100644 --- a/src/browser/scrollable/scrollbarState.ts +++ b/src/browser/scrollable/scrollbarState.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ /** * The minimal size of the slider (such that it can still be clickable). diff --git a/src/browser/scrollable/scrollbarVisibilityController.ts b/src/browser/scrollable/scrollbarVisibilityController.ts index b56e3524f..59d172bce 100644 --- a/src/browser/scrollable/scrollbarVisibilityController.ts +++ b/src/browser/scrollable/scrollbarVisibilityController.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { FastDomNode } from './fastDomNode'; import { TimeoutTimer } from 'common/Async'; diff --git a/src/browser/scrollable/touch.ts b/src/browser/scrollable/touch.ts index a734a79a1..8ffdfe699 100644 --- a/src/browser/scrollable/touch.ts +++ b/src/browser/scrollable/touch.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import * as DomUtils from '../Dom'; import { Disposable, IDisposable, toDisposable } from 'common/Lifecycle'; diff --git a/src/browser/scrollable/verticalScrollbar.ts b/src/browser/scrollable/verticalScrollbar.ts index 9f5a090af..615f443a0 100644 --- a/src/browser/scrollable/verticalScrollbar.ts +++ b/src/browser/scrollable/verticalScrollbar.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { AbstractScrollbar, ISimplifiedPointerEvent, IScrollbarHost } from './abstractScrollbar'; import { IScrollableElementResolvedOptions } from './scrollableElementOptions'; diff --git a/src/browser/scrollable/widget.ts b/src/browser/scrollable/widget.ts index eae4ec185..80220c9b1 100644 --- a/src/browser/scrollable/widget.ts +++ b/src/browser/scrollable/widget.ts @@ -1,9 +1,7 @@ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import * as dom from '../Dom'; import { IMouseEvent, StandardMouseEvent } from './mouseEvent'; diff --git a/src/browser/services/CharSizeService.ts b/src/browser/services/CharSizeService.ts index 8d97f233c..6baf9b50a 100644 --- a/src/browser/services/CharSizeService.ts +++ b/src/browser/services/CharSizeService.ts @@ -61,10 +61,8 @@ abstract class BaseMeasureStategy extends Disposable implements IMeasureStrategy protected _result: IMeasureResult = { width: 0, height: 0 }; protected _validateAndSet(width: number | undefined, height: number | undefined): void { - /* - * If values are 0 then the element is likely currently display:none, in which case we should - * retain the previous value. - */ + // If values are 0 then the element is likely currently display:none, in which case we should + // retain the previous value. if (width !== undefined && width > 0 && height !== undefined && height > 0) { this._result.width = width; this._result.height = height; diff --git a/src/browser/services/CharacterJoinerService.ts b/src/browser/services/CharacterJoinerService.ts index d0ded2623..ca4f1984e 100644 --- a/src/browser/services/CharacterJoinerService.ts +++ b/src/browser/services/CharacterJoinerService.ts @@ -13,10 +13,8 @@ import { ICharacterJoinerService } from 'browser/services/Services'; export class JoinedCellData extends AttributeData implements ICellData { private _width: number; - /* - * .content carries no meaning for joined CellData, simply nullify it - * thus we have to overload all other .content accessors - */ + // .content carries no meaning for joined CellData, simply nullify it + // thus we have to overload all other .content accessors public content: number = 0; public fg: number; public bg: number; @@ -44,10 +42,8 @@ export class JoinedCellData extends AttributeData implements ICellData { } public getCode(): number { - /* - * code always gets the highest possible fake codepoint (read as -1) - * this is needed as code is used by caches as identifier - */ + // code always gets the highest possible fake codepoint (read as -1) + // this is needed as code is used by caches as identifier return 0x1FFFFF; } @@ -105,12 +101,10 @@ export class CharacterJoinerService implements ICharacterJoinerService { const ranges: [number, number][] = []; const lineStr = line.translateToString(true); - /* - * Because some cells can be represented by multiple javascript characters, - * we track the cell and the string indexes separately. This allows us to - * translate the string ranges we get from the joiners back into cell ranges - * for use when rendering - */ + // Because some cells can be represented by multiple javascript characters, + // we track the cell and the string indexes separately. This allows us to + // translate the string ranges we get from the joiners back into cell ranges + // for use when rendering let rangeStartColumn = 0; let currentStringIndex = 0; let rangeStartStringIndex = 0; @@ -127,10 +121,8 @@ export class CharacterJoinerService implements ICharacterJoinerService { // End of range if (this._workCell.fg !== rangeAttrFG || this._workCell.bg !== rangeAttrBG) { - /* - * If we ended up with a sequence of more than one character, - * look for ranges to join. - */ + // If we ended up with a sequence of more than one character, + // look for ranges to join. if (x - rangeStartColumn > 1) { const joinedRanges = this._getJoinedRanges( lineStr, @@ -181,11 +173,9 @@ export class CharacterJoinerService implements ICharacterJoinerService { */ private _getJoinedRanges(line: string, startIndex: number, endIndex: number, lineData: IBufferLine, startCol: number): [number, number][] { const text = line.substring(startIndex, endIndex); - /* - * At this point we already know that there is at least one joiner so - * we can just pull its value and assign it directly rather than - * merging it into an empty array, which incurs unnecessary writes. - */ + // At this point we already know that there is at least one joiner so + // we can just pull its value and assign it directly rather than + // merging it into an empty array, which incurs unnecessary writes. let allJoinedRanges: [number, number][] = []; try { allJoinedRanges = this._characterJoiners[0].handler(text); @@ -230,10 +220,8 @@ export class CharacterJoinerService implements ICharacterJoinerService { const width = line.getWidth(x); const length = line.getString(x).length || WHITESPACE_CELL_CHAR.length; - /* - * We skip zero-width characters when creating the string to join the text - * so we do the same here - */ + // We skip zero-width characters when creating the string to join the text + // so we do the same here if (width === 0) { continue; } @@ -256,12 +244,10 @@ export class CharacterJoinerService implements ICharacterJoinerService { break; } - /* - * Ranges can be on adjacent characters. Because the end index of the - * ranges are exclusive, this means that the index for the start of a - * range can be the same as the end index of the previous range. To - * account for the start of the next range, we check here just in case. - */ + // Ranges can be on adjacent characters. Because the end index of the + // ranges are exclusive, this means that the index for the start of a + // range can be the same as the end index of the previous range. To + // account for the start of the next range, we check here just in case. if (currentRange[0] <= currentStringIndex) { currentRange[0] = x; currentRangeStarted = true; @@ -270,17 +256,13 @@ export class CharacterJoinerService implements ICharacterJoinerService { } } - /* - * Adjust the string index based on the character length to line up with - * the column adjustment - */ + // Adjust the string index based on the character length to line up with + // the column adjustment currentStringIndex += length; } - /* - * If there is still a range left at the end, it must extend all the way to - * the end of the line. - */ + // If there is still a range left at the end, it must extend all the way to + // the end of the line. if (currentRange) { currentRange[1] = this._bufferService.cols; } @@ -306,19 +288,15 @@ export class CharacterJoinerService implements ICharacterJoinerService { } if (newRange[1] <= range[1]) { - /* - * Case 2: New range is either wholly contained within the - * search range or overlaps with the front of it - */ + // Case 2: New range is either wholly contained within the + // search range or overlaps with the front of it range[0] = Math.min(newRange[0], range[0]); return ranges; } if (newRange[0] < range[1]) { - /* - * Case 3: New range either wholly contains the search range - * or overlaps with the end of it - */ + // Case 3: New range either wholly contains the search range + // or overlaps with the end of it range[0] = Math.min(newRange[0], range[0]); inRange = true; } @@ -327,28 +305,22 @@ export class CharacterJoinerService implements ICharacterJoinerService { continue; } else { if (newRange[1] <= range[0]) { - /* - * Case 5: New range extends from previous range but doesn't - * reach the current one - */ + // Case 5: New range extends from previous range but doesn't + // reach the current one ranges[i - 1][1] = newRange[1]; return ranges; } if (newRange[1] <= range[1]) { - /* - * Case 6: New range extends from prvious range into the - * current range - */ + // Case 6: New range extends from prvious range into the + // current range ranges[i - 1][1] = Math.max(newRange[1], range[1]); ranges.splice(i, 1); return ranges; } - /* - * Case 7: New range extends from previous range past the - * end of the current range - */ + // Case 7: New range extends from previous range past the + // end of the current range ranges.splice(i, 1); i--; } diff --git a/src/browser/services/MouseService.test.ts b/src/browser/services/MouseService.test.ts index 63e14954a..f395083e3 100644 --- a/src/browser/services/MouseService.test.ts +++ b/src/browser/services/MouseService.test.ts @@ -127,10 +127,8 @@ describe('MouseService _triggerMouseEvent', () => { assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.RIGHT, action: CoreMouseAction.DOWN }), true); assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.WHEEL, action: CoreMouseAction.UP }), true); assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.NONE, action: CoreMouseAction.MOVE }), true); - /* - * should not report in any case - * invalid button + action combinations - */ + // should not report in any case + // invalid button + action combinations assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.WHEEL, action: CoreMouseAction.MOVE }), false); assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.NONE, action: CoreMouseAction.DOWN }), false); assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.NONE, action: CoreMouseAction.UP }), false); diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts index 2f35957b7..d701796c2 100644 --- a/src/browser/services/MouseService.ts +++ b/src/browser/services/MouseService.ts @@ -147,10 +147,8 @@ export class MouseService implements IMouseService { return false; } - /* - * exit if we cannot determine valid button/action values - * do nothing for higher buttons than wheel - */ + // exit if we cannot determine valid button/action values + // do nothing for higher buttons than wheel if (action === undefined || but === undefined || but > CoreMouseButton.WHEEL) { return false; } @@ -206,23 +204,19 @@ export class MouseService implements IMouseService { ev.preventDefault(); ctx.focus(); - /* - * Don't send the mouse button to the pty if mouse events are disabled or - * if the selection manager is having selection forced (ie. a modifier is - * held). - */ + // Don't send the mouse button to the pty if mouse events are disabled or + // if the selection manager is having selection forced (ie. a modifier is + // held). if (!this._mouseStateService.areMouseEventsActive || this._selectionService.shouldForceSelection(ev)) { return; } this._sendEvent(ctx, ev); - /* - * Register additional global handlers which should keep reporting outside - * of the terminal element. - * Note: Other emulators also do this for 'mousedown' while a button - * is held, we currently limit 'mousedown' to the terminal only. - */ + // Register additional global handlers which should keep reporting outside + // of the terminal element. + // Note: Other emulators also do this for 'mousedown' while a button + // is held, we currently limit 'mousedown' to the terminal only. if (ctx.requestedEvents.mouseup) { ctx.target.document.addEventListener('mouseup', ctx.requestedEvents.mouseup); } @@ -242,14 +236,12 @@ export class MouseService implements IMouseService { } if (!this._bufferService.buffer.hasScrollback) { - /* - * Convert wheel events into up/down events when the buffer does not have scrollback, this - * enables scrolling in apps hosted in the alt buffer such as vim or tmux even when mouse - * events are not enabled. - * This used implementation used get the actual lines/partial lines scrolled from the - * viewport but since moving to the new viewport implementation has been simplified to - * simply send a single up or down sequence. - */ + // Convert wheel events into up/down events when the buffer does not have scrollback, this + // enables scrolling in apps hosted in the alt buffer such as vim or tmux even when mouse + // events are not enabled. + // This used implementation used get the actual lines/partial lines scrolled from the + // viewport but since moving to the new viewport implementation has been simplified to + // simply send a single up or down sequence. // Do nothing if there's no vertical scroll const deltaY = ev.deltaY; diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index 433a24eb5..e999b7eac 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -88,11 +88,9 @@ export class RenderService extends Disposable implements IRenderService { this._register(this._optionsService.onOptionChange(() => this._handleOptionsChanged())); this._register(this._charSizeService.onCharSizeChange(() => this.handleCharSizeChanged())); - /* - * Do a full refresh whenever any decoration is added or removed. This may not actually result - * in changes but since decorations should be used sparingly or added/removed all in the same - * frame this should have minimal performance impact. - */ + // Do a full refresh whenever any decoration is added or removed. This may not actually result + // in changes but since decorations should be used sparingly or added/removed all in the same + // frame this should have minimal performance impact. this._register(decorationService.onDecorationRegistered(() => this._fullRefresh())); this._register(decorationService.onDecorationRemoved(() => this._fullRefresh())); @@ -126,10 +124,8 @@ export class RenderService extends Disposable implements IRenderService { } private _registerIntersectionObserver(w: Window & typeof globalThis, screenElement: HTMLElement): void { - /* - * Detect whether IntersectionObserver is detected and enable renderer pause - * and resume based on terminal visibility if so - */ + // Detect whether IntersectionObserver is detected and enable renderer pause + // and resume based on terminal visibility if so if ('IntersectionObserver' in w) { const observer = new w.IntersectionObserver(e => this._handleIntersectionChange(e[e.length - 1]), { threshold: 0 }); this._observerDisposable.value = toDisposable(() => { @@ -190,20 +186,16 @@ export class RenderService extends Disposable implements IRenderService { return; } - /* - * Skip rendering if synchronized output mode is enabled. This check must happen here - * (in addition to refreshRows) to handle renders that were queued before the mode was enabled. - */ + // Skip rendering if synchronized output mode is enabled. This check must happen here + // (in addition to refreshRows) to handle renders that were queued before the mode was enabled. if (this._coreService.decPrivateModes.synchronizedOutput) { this._syncOutputHandler.bufferRows(start, end); return; } - /* - * Since this is debounced, a resize event could have happened between the time a refresh was - * requested and when this triggers. Clamp the values of start and end to ensure they're valid - * given the current viewport state. - */ + // Since this is debounced, a resize event could have happened between the time a refresh was + // requested and when this triggers. Clamp the values of start and end to ensure they're valid + // given the current viewport state. start = Math.min(start, this._rowCount - 1); end = Math.min(end, this._rowCount - 1); @@ -285,10 +277,8 @@ export class RenderService extends Disposable implements IRenderService { } public handleDevicePixelRatioChange(): void { - /* - * Force char size measurement as DomMeasureStrategy(getBoundingClientRect) is not stable - * when devicePixelRatio changes - */ + // Force char size measurement as DomMeasureStrategy(getBoundingClientRect) is not stable + // when devicePixelRatio changes this._charSizeService.measure(); if (!this._renderer.value) { diff --git a/src/browser/services/SelectionService.test.ts b/src/browser/services/SelectionService.test.ts index 822bb6b6d..be62ccc6b 100644 --- a/src/browser/services/SelectionService.test.ts +++ b/src/browser/services/SelectionService.test.ts @@ -257,10 +257,8 @@ describe('SelectionService', () => { ])); // The a is here to prevent the space being trimmed in selectionText selectionService.selectWordAt([0, 0]); assert.equal(selectionService.selectionText, ' '); - /* - * ZWJ emojis do not combine in the terminal so the family emoji used here consumed 4 cells - * The selection text should retain ZWJ chars despite not combining on the terminal - */ + // ZWJ emojis do not combine in the terminal so the family emoji used here consumed 4 cells + // The selection text should retain ZWJ chars despite not combining on the terminal selectionService.selectWordAt([1, 0]); assert.equal(selectionService.selectionText, '👨‍👩‍👧‍👦'); selectionService.selectWordAt([2, 0]); @@ -475,10 +473,8 @@ describe('SelectionService', () => { }); it('should select a column of text with double character emojis', () => { - /* - * TODO the case this is testing works for me in the demo webapp, - * but doing it programmatically fails. - */ + // TODO the case this is testing works for me in the demo webapp, + // but doing it programmatically fails. buffer.lines.length = 3; buffer.lines.set(0, stringToRow('a ')); buffer.lines.set(1, stringArrayToRow(['😁', ' '])); diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index 29ed1b827..39692a871 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -152,10 +152,8 @@ export class SelectionService extends Disposable implements ISelectionService { this._removeMouseDownListeners(); })); - /* - * Clear selection when resizing vertically. This experience could be improved, this is the - * simple option to fix the buggy behavior. https://github.com/xtermjs/xterm.js/issues/5300 - */ + // Clear selection when resizing vertically. This experience could be improved, this is the + // simple option to fix the buggy behavior. https://github.com/xtermjs/xterm.js/issues/5300 this._register(this._bufferService.onResize(e => { if (e.rowsChanged) { this.clearSelection(); @@ -217,10 +215,8 @@ export class SelectionService extends Disposable implements ISelectionService { return ''; } - /* - * For column selection it's not enough to rely on final selection's swapping of reversed - * values, it also needs the x coordinates to swap independently of the y coordinate is needed - */ + // For column selection it's not enough to rely on final selection's swapping of reversed + // values, it also needs the x coordinates to swap independently of the y coordinate is needed const startCol = start[0] < end[0] ? start[0] : end[0]; const endCol = start[0] < end[0] ? end[0] : start[0]; for (let i = start[1]; i <= end[1]; i++) { @@ -255,10 +251,8 @@ export class SelectionService extends Disposable implements ISelectionService { } } - /* - * Format string by replacing non-breaking space chars with regular spaces - * and joining the array into a multi-line string. - */ + // Format string by replacing non-breaking space chars with regular spaces + // and joining the array into a multi-line string. const formattedResult = result.map(line => { return line.replace(ALL_NON_BREAKING_SPACE_REGEX, ' '); }).join(Browser.isWindows ? '\r\n' : '\n'); @@ -287,10 +281,8 @@ export class SelectionService extends Disposable implements ISelectionService { this._refreshAnimationFrame = this._coreBrowserService.window.requestAnimationFrame(() => this._refresh()); } - /* - * If the platform is Linux and the refresh call comes from a mouse event, - * we need to update the selection for middle click to paste selection. - */ + // If the platform is Linux and the refresh call comes from a mouse event, + // we need to update the selection for middle click to paste selection. if (Browser.isLinux && isLinuxMouseSelection) { const selectionText = this.selectionText; if (selectionText.length) { @@ -455,10 +447,8 @@ export class SelectionService extends Disposable implements ISelectionService { */ public handleMouseDown(event: MouseEvent): void { this._mouseDownTimeStamp = event.timeStamp; - /* - * If we have selection, we want the context menu on right click even if the - * terminal is in mouse mode. - */ + // If we have selection, we want the context menu on right click even if the + // terminal is in mouse mode. if (event.button === 2 && this.hasSelection) { return; } @@ -571,10 +561,8 @@ export class SelectionService extends Disposable implements ISelectionService { return; } - /* - * If the mouse is over the second half of a wide character, adjust the - * selection to cover the whole character - */ + // If the mouse is over the second half of a wide character, adjust the + // selection to cover the whole character if (line.hasWidth(this._model.selectionStart[0]) === 0) { this._model.selectionStart[0]++; } @@ -617,25 +605,19 @@ export class SelectionService extends Disposable implements ISelectionService { * @param event The mousemove event. */ private _handleMouseMove(event: MouseEvent): void { - /* - * If the mousemove listener is active it means that a selection is - * currently being made, we should stop propagation to prevent mouse events - * to be sent to the pty. - */ + // If the mousemove listener is active it means that a selection is + // currently being made, we should stop propagation to prevent mouse events + // to be sent to the pty. event.stopImmediatePropagation(); - /* - * Do nothing if there is no selection start, this can happen if the first - * click in the terminal is an incremental click - */ + // Do nothing if there is no selection start, this can happen if the first + // click in the terminal is an incremental click if (!this._model.selectionStart) { return; } - /* - * Record the previous position so we know whether to redraw the selection - * at the end. - */ + // Record the previous position so we know whether to redraw the selection + // at the end. const previousSelectionEnd = this._model.selectionEnd ? [this._model.selectionEnd[0], this._model.selectionEnd[1]] : null; // Set the initial selection end based on the mouse coordinates @@ -659,11 +641,9 @@ export class SelectionService extends Disposable implements ISelectionService { // Determine the amount of scrolling that will happen. this._dragScrollAmount = this._getMouseEventScrollAmount(event); - /* - * If the cursor was above or below the viewport, make sure it's at the - * start or end of the viewport respectively. This should only happen when - * NOT in column select mode. - */ + // If the cursor was above or below the viewport, make sure it's at the + // start or end of the viewport respectively. This should only happen when + // NOT in column select mode. if (this._activeSelectionMode !== SelectionMode.COLUMN) { if (this._dragScrollAmount > 0) { this._model.selectionEnd[0] = this._bufferService.cols; @@ -672,11 +652,9 @@ export class SelectionService extends Disposable implements ISelectionService { } } - /* - * If the character is a wide character include the cell to the right in the - * selection. Note that selections at the very end of the line will never - * have a character. - */ + // If the character is a wide character include the cell to the right in the + // selection. Note that selections at the very end of the line will never + // have a character. const buffer = this._bufferService.buffer; if (this._model.selectionEnd[1] < buffer.lines.length) { const line = buffer.lines.get(this._model.selectionEnd[1]); @@ -705,12 +683,10 @@ export class SelectionService extends Disposable implements ISelectionService { } if (this._dragScrollAmount) { this._onRequestScrollLines.fire({ amount: this._dragScrollAmount, suppressScrollEvent: false }); - /* - * Re-evaluate selection - * If the cursor was above or below the viewport, make sure it's at the - * start or end of the viewport respectively. This should only happen when - * NOT in column select mode. - */ + // Re-evaluate selection + // If the cursor was above or below the viewport, make sure it's at the + // start or end of the viewport respectively. This should only happen when + // NOT in column select mode. const buffer = this._bufferService.buffer; if (this._dragScrollAmount > 0) { if (this._activeSelectionMode !== SelectionMode.COLUMN) { @@ -789,12 +765,10 @@ export class SelectionService extends Disposable implements ISelectionService { private _handleBufferActivate(e: {activeBuffer: IBuffer, inactiveBuffer: IBuffer}): void { this.clearSelection(); - /* - * Only adjust the selection on trim, shiftElements is rarely used (only in - * reverseIndex) and delete in a splice is only ever used when the same - * number of elements was just added. Given this is could actually be - * beneficial to leave the selection as is for these cases. - */ + // Only adjust the selection on trim, shiftElements is rarely used (only in + // reverseIndex) and delete in a splice is only ever used when the same + // number of elements was just added. Given this is could actually be + // beneficial to leave the selection as is for these cases. this._trimListener.dispose(); this._trimListener = e.activeBuffer.lines.onTrim(amount => this._handleTrim(amount)); } @@ -810,17 +784,13 @@ export class SelectionService extends Disposable implements ISelectionService { for (let i = 0; x >= i; i++) { const length = bufferLine.loadCell(i, this._workCell).getChars().length; if (this._workCell.getWidth() === 0) { - /* - * Wide characters aren't included in the line string so decrement the - * index so the index is back on the wide character. - */ + // Wide characters aren't included in the line string so decrement the + // index so the index is back on the wide character. charIndex--; } else if (length > 1 && x !== i) { - /* - * Emojis take up multiple characters, so adjust accordingly. For these - * we don't want ot include the character at the column as we're - * returning the start index in the string, not the end index. - */ + // Emojis take up multiple characters, so adjust accordingly. For these + // we don't want ot include the character at the column as we're + // returning the start index in the string, not the end index. charIndex += length - 1; } } @@ -883,19 +853,15 @@ export class SelectionService extends Disposable implements ISelectionService { endIndex++; } } else { - /* - * Expand until whitespace is hit. This algorithm works by scanning left - * and right from the starting position, keeping both the index format - * (line) and the column format (bufferLine) in sync. When a wide - * character is hit, it is recorded and the column index is adjusted. - */ + // Expand until whitespace is hit. This algorithm works by scanning left + // and right from the starting position, keeping both the index format + // (line) and the column format (bufferLine) in sync. When a wide + // character is hit, it is recorded and the column index is adjusted. let startCol = coords[0]; let endCol = coords[0]; - /* - * Consider the initial position, skip it and increment the wide char - * variable - */ + // Consider the initial position, skip it and increment the wide char + // variable if (bufferLine.getWidth(startCol) === 0) { leftWideCharCount++; startCol--; @@ -921,10 +887,8 @@ export class SelectionService extends Disposable implements ISelectionService { leftWideCharCount++; startCol--; } else if (length > 1) { - /* - * If the next character's string is longer than 1 char (eg. emoji), - * adjust the index - */ + // If the next character's string is longer than 1 char (eg. emoji), + // adjust the index leftLongCharOffset += length - 1; startIndex -= length - 1; } @@ -939,10 +903,8 @@ export class SelectionService extends Disposable implements ISelectionService { rightWideCharCount++; endCol++; } else if (length > 1) { - /* - * If the next character's string is longer than 1 char (eg. emoji), - * adjust the index - */ + // If the next character's string is longer than 1 char (eg. emoji), + // adjust the index rightLongCharOffset += length - 1; endIndex += length - 1; } @@ -954,20 +916,16 @@ export class SelectionService extends Disposable implements ISelectionService { // Incremenet the end index so it is at the start of the next character endIndex++; - /* - * Calculate the start _column_, converting the the string indexes back to - * column coordinates. - */ + // Calculate the start _column_, converting the the string indexes back to + // column coordinates. let start = startIndex // The index of the selection's start char in the line string + charOffset // The difference between the initial char's column and index - leftWideCharCount // The number of wide chars left of the initial char + leftLongCharOffset; // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis) - /* - * Calculate the length in _columns_, converting the the string indexes back - * to column coordinates. - */ + // Calculate the length in _columns_, converting the the string indexes back + // to column coordinates. let length = Math.min(this._bufferService.cols, // Disallow lengths larger than the terminal cols endIndex // The index of the selection's end char in the line string - startIndex // The index of the selection's start char in the line string @@ -1044,10 +1002,8 @@ export class SelectionService extends Disposable implements ISelectionService { endRow--; } - /* - * Adjust wrapped length value, this only needs to happen when values are reversed as in that - * case we're interested in the start of the word, not the end - */ + // Adjust wrapped length value, this only needs to happen when values are reversed as in that + // case we're interested in the start of the word, not the end if (!this._model.areSelectionValuesReversed()) { while (wordPosition.start + wordPosition.length > this._bufferService.cols) { wordPosition.length -= this._bufferService.cols; @@ -1065,10 +1021,8 @@ export class SelectionService extends Disposable implements ISelectionService { * @param cell The cell to check. */ private _isCharWordSeparator(cell: ICellData): boolean { - /* - * Zero width characters are never separators as they are always to the - * right of wide characters - */ + // Zero width characters are never separators as they are always to the + // right of wide characters if (cell.getWidth() === 0) { return false; } diff --git a/src/common/CircularList.ts b/src/common/CircularList.ts index 9843a470c..3663bfc17 100644 --- a/src/common/CircularList.ts +++ b/src/common/CircularList.ts @@ -52,10 +52,8 @@ export class CircularList extends Disposable implements ICircularList { return; } - /* - * Reconstruct array, starting at index 0. Only transfer values from the - * indexes 0 to length. - */ + // Reconstruct array, starting at index 0. Only transfer values from the + // indexes 0 to length. const newArray = new Array(newMaxLength); for (let i = 0; i < Math.min(newMaxLength, this.length); i++) { newArray[i] = this._array[this._getCyclicIndex(i)]; diff --git a/src/common/Color.ts b/src/common/Color.ts index eef573796..b3d13fc99 100644 --- a/src/common/Color.ts +++ b/src/common/Color.ts @@ -27,11 +27,9 @@ export namespace channels { } export function toRgba(r: number, g: number, b: number, a: number = 0xFF): number { - /* - * Note: The aggregated number is RGBA32 (BE), thus needs to be converted to ABGR32 - * on LE systems, before it can be used for direct 32-bit buffer writes. - * >>> 0 forces an unsigned int - */ + // Note: The aggregated number is RGBA32 (BE), thus needs to be converted to ABGR32 + // on LE systems, before it can be used for direct 32-bit buffer writes. + // >>> 0 forces an unsigned int return (r << 24 | g << 16 | b << 8 | a) >>> 0; } @@ -199,10 +197,8 @@ export namespace css { throw new Error('css.toColor: Unsupported css format'); } - /* - * Validate the color using canvas fillStyle - * See https://html.spec.whatwg.org/multipage/canvas.html#fill-and-stroke-styles - */ + // Validate the color using canvas fillStyle + // See https://html.spec.whatwg.org/multipage/canvas.html#fill-and-stroke-styles $ctx.fillStyle = $litmusColor; $ctx.fillStyle = css; if (typeof $ctx.fillStyle !== 'string') { @@ -217,11 +213,9 @@ export namespace css { throw new Error('css.toColor: Unsupported css format'); } - /* - * Extract the color from the canvas' fillStyle property which exposes the color value in rgba() - * format - * See https://html.spec.whatwg.org/multipage/canvas.html#serialisation-of-a-color - */ + // Extract the color from the canvas' fillStyle property which exposes the color value in rgba() + // format + // See https://html.spec.whatwg.org/multipage/canvas.html#serialisation-of-a-color return { rgba: channels.toRgba($r, $g, $b, $a), css @@ -327,10 +321,8 @@ export namespace rgba { } export function reduceLuminance(bgRgba: number, fgRgba: number, ratio: number): number { - /* - * This is a naive but fast approach to reducing luminance as converting to - * HSL and back is expensive - */ + // This is a naive but fast approach to reducing luminance as converting to + // HSL and back is expensive const bgR = (bgRgba >> 24) & 0xFF; const bgG = (bgRgba >> 16) & 0xFF; const bgB = (bgRgba >> 8) & 0xFF; @@ -349,10 +341,8 @@ export namespace rgba { } export function increaseLuminance(bgRgba: number, fgRgba: number, ratio: number): number { - /* - * This is a naive but fast approach to increasing luminance as converting to - * HSL and back is expensive - */ + // This is a naive but fast approach to increasing luminance as converting to + // HSL and back is expensive const bgR = (bgRgba >> 24) & 0xFF; const bgG = (bgRgba >> 16) & 0xFF; const bgB = (bgRgba >> 8) & 0xFF; diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index 3e16fe0a2..9bd7faaf5 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -176,10 +176,8 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal { x = Math.max(x, BufferServiceConstants.MINIMUM_COLS); y = Math.max(y, BufferServiceConstants.MINIMUM_ROWS); - /* - * Flush pending writes before resize to avoid race conditions where async - * writes are processed with incorrect dimensions - */ + // Flush pending writes before resize to avoid race conditions where async + // writes are processed with incorrect dimensions this._writeBuffer.flushSync(); this._bufferService.resize(x, y); diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index cf41d5746..f9da81a25 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -613,10 +613,8 @@ describe('InputHandler', () => { await inputHandler.parseP('a'.repeat(bufferService.cols + 9)); // line 1 and 2 for (let i = 3; i < bufferService.rows; ++i) await inputHandler.parseP('a'.repeat(bufferService.cols)); - /* - * params[1] left and above with wrap - * confirm precondition that line 2 is wrapped - */ + // params[1] left and above with wrap + // confirm precondition that line 2 is wrapped assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, true); bufferService.buffer.y = 2; bufferService.buffer.x = 40; @@ -630,10 +628,8 @@ describe('InputHandler', () => { await inputHandler.parseP('a'.repeat(bufferService.cols + 9)); // line 1 and 2 for (let i = 3; i < bufferService.rows; ++i) await inputHandler.parseP('a'.repeat(bufferService.cols)); - /* - * params[1] left and above with wrap - * confirm precondition that line 2 is wrapped - */ + // params[1] left and above with wrap + // confirm precondition that line 2 is wrapped assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, true); bufferService.buffer.y = 1; bufferService.buffer.x = 90; // Cursor is beyond last column diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 1fac29d91..2b7142dd0 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -33,12 +33,10 @@ const GLEVEL: { [key: string]: number } = { '(': 0, ')': 1, '*': 2, '+': 3, '-': /** * Document xterm VT features here that are currently unsupported */ -/* - * @vt: #N DCS DECUDK "User Defined Keys" "DCS Ps ; Ps \| Pt ST" "Definitions for user-defined keys." - * @vt: #N DCS XTGETTCAP "Request Terminfo String" "DCS + q Pt ST" "Request Terminfo String." - * @vt: #N DCS XTSETTCAP "Set Terminfo Data" "DCS + p Pt ST" "Set Terminfo Data." - * @vt: #N OSC 1 "Set Icon Name" "OSC 1 ; Pt BEL" "Set icon name." - */ +// @vt: #N DCS DECUDK "User Defined Keys" "DCS Ps ; Ps \| Pt ST" "Definitions for user-defined keys." +// @vt: #N DCS XTGETTCAP "Request Terminfo String" "DCS + q Pt ST" "Request Terminfo String." +// @vt: #N DCS XTSETTCAP "Set Terminfo Data" "DCS + p Pt ST" "Set Terminfo Data." +// @vt: #N OSC 1 "Set Icon Name" "OSC 1 ; Pt BEL" "Set icon name." /** * Max length of the UTF32 input buffer. Real memory consumption is 4 times higher. @@ -290,17 +288,13 @@ export class InputHandler extends Disposable implements IInputHandler { this._parser.registerOscHandler(1, new OscHandler(data => this.setIconName(data))); // 2 - title this._parser.registerOscHandler(2, new OscHandler(data => this.setTitle(data))); - /* - * 3 - set property X in the form "prop=value" - * 4 - Change Color Number - */ + // 3 - set property X in the form "prop=value" + // 4 - Change Color Number this._parser.registerOscHandler(4, new OscHandler(data => this.setOrReportIndexedColor(data))); - /* - * 5 - Change Special Color Number - * 6 - Enable/disable Special Color Number c - * 7 - current directory? (not in xterm spec, see https://gitlab.com/gnachman/iterm2/issues/3939) - * 8 - create hyperlink (not in xterm spec, see https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda) - */ + // 5 - Change Special Color Number + // 6 - Enable/disable Special Color Number c + // 7 - current directory? (not in xterm spec, see https://gitlab.com/gnachman/iterm2/issues/3939) + // 8 - create hyperlink (not in xterm spec, see https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda) this._parser.registerOscHandler(8, new OscHandler(data => this.setHyperlink(data))); // 10 - Change VT100 text foreground color to Pt. this._parser.registerOscHandler(10, new OscHandler(data => this.setOrReportFgColor(data))); @@ -308,40 +302,34 @@ export class InputHandler extends Disposable implements IInputHandler { this._parser.registerOscHandler(11, new OscHandler(data => this.setOrReportBgColor(data))); // 12 - Change text cursor color to Pt. this._parser.registerOscHandler(12, new OscHandler(data => this.setOrReportCursorColor(data))); - /* - * 13 - Change mouse foreground color to Pt. - * 14 - Change mouse background color to Pt. - * 15 - Change Tektronix foreground color to Pt. - * 16 - Change Tektronix background color to Pt. - * 17 - Change highlight background color to Pt. - * 18 - Change Tektronix cursor color to Pt. - * 19 - Change highlight foreground color to Pt. - * 46 - Change Log File to Pt. - * 50 - Set Font to Pt. - * 51 - reserved for Emacs shell. - * 52 - Manipulate Selection Data. - * 104 ; c - Reset Color Number c. - */ + // 13 - Change mouse foreground color to Pt. + // 14 - Change mouse background color to Pt. + // 15 - Change Tektronix foreground color to Pt. + // 16 - Change Tektronix background color to Pt. + // 17 - Change highlight background color to Pt. + // 18 - Change Tektronix cursor color to Pt. + // 19 - Change highlight foreground color to Pt. + // 46 - Change Log File to Pt. + // 50 - Set Font to Pt. + // 51 - reserved for Emacs shell. + // 52 - Manipulate Selection Data. + // 104 ; c - Reset Color Number c. this._parser.registerOscHandler(104, new OscHandler(data => this.restoreIndexedColor(data))); - /* - * 105 ; c - Reset Special Color Number c. - * 106 ; c; f - Enable/disable Special Color Number c. - * 110 - Reset VT100 text foreground color. - */ + // 105 ; c - Reset Special Color Number c. + // 106 ; c; f - Enable/disable Special Color Number c. + // 110 - Reset VT100 text foreground color. this._parser.registerOscHandler(110, new OscHandler(data => this.restoreFgColor(data))); // 111 - Reset VT100 text background color. this._parser.registerOscHandler(111, new OscHandler(data => this.restoreBgColor(data))); // 112 - Reset text cursor color. this._parser.registerOscHandler(112, new OscHandler(data => this.restoreCursorColor(data))); - /* - * 113 - Reset mouse foreground color. - * 114 - Reset mouse background color. - * 115 - Reset Tektronix foreground color. - * 116 - Reset Tektronix background color. - * 117 - Reset highlight color. - * 118 - Reset Tektronix cursor color. - * 119 - Reset highlight foreground color. - */ + // 113 - Reset mouse foreground color. + // 114 - Reset mouse background color. + // 115 - Reset Tektronix foreground color. + // 116 - Reset Tektronix background color. + // 117 - Reset highlight color. + // 118 - Reset Tektronix cursor color. + // 119 - Reset highlight foreground color. /** * ESC handlers @@ -478,10 +466,8 @@ export class InputHandler extends Disposable implements IInputHandler { } } - /* - * Clear the dirty row service so we know which lines changed as a result of parsing - * Important: do not clear between async calls, otherwise we lost pending update information. - */ + // Clear the dirty row service so we know which lines changed as a result of parsing + // Important: do not clear between async calls, otherwise we lost pending update information. if (!wasPaused) { this._dirtyRowTracker.clearRange(); } @@ -516,10 +502,8 @@ export class InputHandler extends Disposable implements IInputHandler { this._onCursorMove.fire(); } - /* - * Refresh any dirty rows accumulated as part of parsing, fire only for rows within the - * _viewport_ which is relative to ydisp, not relative to ybase. - */ + // Refresh any dirty rows accumulated as part of parsing, fire only for rows within the + // _viewport_ which is relative to ydisp, not relative to ybase. const viewportEnd = this._dirtyRowTracker.end + (this._bufferService.buffer.ybase - this._bufferService.buffer.ydisp); const viewportStart = this._dirtyRowTracker.start + (this._bufferService.buffer.ybase - this._bufferService.buffer.ydisp); if (viewportStart < this._bufferService.rows) { @@ -541,10 +525,8 @@ export class InputHandler extends Disposable implements IInputHandler { const curAttr = this._curAttrData; let bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y); - /* - * Defensive check: bufferRow can be undefined if a resize occurred mid-write due to async - * scheduling gaps in WriteBuffer. See https://github.com/xtermjs/xterm.js/issues/5597 - */ + // Defensive check: bufferRow can be undefined if a resize occurred mid-write due to async + // scheduling gaps in WriteBuffer. See https://github.com/xtermjs/xterm.js/issues/5597 if (!bufferRow) { return; } @@ -560,19 +542,15 @@ export class InputHandler extends Disposable implements IInputHandler { for (let pos = start; pos < end; ++pos) { code = data[pos]; - /* - * Soft hyphen's (U+00AD) behavior is ambiguous and differs across terminals. We opt to treat - * it as a zero-width hint to text layout engines and simply ignore it. - */ + // Soft hyphen's (U+00AD) behavior is ambiguous and differs across terminals. We opt to treat + // it as a zero-width hint to text layout engines and simply ignore it. if (code === 0xAD) { continue; } - /* - * get charset replacement character - * charset is only defined for ASCII, therefore we only - * search for an replacement char if code < 127 - */ + // get charset replacement character + // charset is only defined for ASCII, therefore we only + // search for an replacement char if code < 127 if (code < 127 && charset) { const ch = charset[String.fromCharCode(code)]; if (ch) { @@ -593,16 +571,12 @@ export class InputHandler extends Disposable implements IInputHandler { this._oscLinkService.addLineToLink(this._getCurrentLinkId(), this._activeBuffer.ybase + this._activeBuffer.y); } - /* - * goto next line if ch would overflow - * NOTE: To avoid costly width checks here, - * the terminal does not allow a cols < 2. - */ + // goto next line if ch would overflow + // NOTE: To avoid costly width checks here, + // the terminal does not allow a cols < 2. if (this._activeBuffer.x + chWidth - oldWidth > cols) { - /* - * autowrap - DECAWM - * automatically wraps to the beginning of the next line - */ + // autowrap - DECAWM + // automatically wraps to the beginning of the next line if (wraparoundMode) { const oldRow = bufferRow; let oldCol = this._activeBuffer.x - oldWidth; @@ -615,10 +589,8 @@ export class InputHandler extends Disposable implements IInputHandler { if (this._activeBuffer.y >= this._bufferService.rows) { this._activeBuffer.y = this._bufferService.rows - 1; } - /* - * The line already exists (eg. the initial viewport), mark it as a - * wrapped line - */ + // The line already exists (eg. the initial viewport), mark it as a + // wrapped line this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = true; } // row changed, get it again @@ -627,10 +599,8 @@ export class InputHandler extends Disposable implements IInputHandler { return; } if (oldWidth > 0 && bufferRow instanceof BufferLine) { - /* - * Combining character widens 1 column to 2. - * Move old character to next line. - */ + // Combining character widens 1 column to 2. + // Move old character to next line. bufferRow.copyCellsFrom(oldRow as BufferLine, oldCol, 0, oldWidth, false); } @@ -641,28 +611,22 @@ export class InputHandler extends Disposable implements IInputHandler { } else { this._activeBuffer.x = cols - 1; if (chWidth === 2) { - /* - * FIXME: check for xterm behavior - * What to do here? We got a wide char that does not fit into last cell - */ + // FIXME: check for xterm behavior + // What to do here? We got a wide char that does not fit into last cell continue; } } } - /* - * insert combining char at last cursor position - * this._activeBuffer.x should never be 0 for a combining char - * since they always follow a cell consuming char - * therefore we can test for this._activeBuffer.x to avoid overflow left - */ + // insert combining char at last cursor position + // this._activeBuffer.x should never be 0 for a combining char + // since they always follow a cell consuming char + // therefore we can test for this._activeBuffer.x to avoid overflow left if (shouldJoin && this._activeBuffer.x) { const offset = bufferRow.getWidth(this._activeBuffer.x - 1) ? 1 : 2; - /* - * if empty cell after fullwidth, need to go 2 cells back - * it is save to step 2 cells back here - * since an empty cell is only set by fullwidth chars - */ + // if empty cell after fullwidth, need to go 2 cells back + // it is save to step 2 cells back here + // since an empty cell is only set by fullwidth chars bufferRow.addCodepointToCell(this._activeBuffer.x - offset, code, chWidth); for (let delta = chWidth - oldWidth; --delta >= 0;) { @@ -675,11 +639,9 @@ export class InputHandler extends Disposable implements IInputHandler { if (insertMode) { // right shift cells according to the width bufferRow.insertCells(this._activeBuffer.x, chWidth - oldWidth, this._activeBuffer.getNullCell(curAttr)); - /* - * test last cell - since the last cell has only room for - * a halfwidth char any fullwidth shifted there is lost - * and will be set to empty cell - */ + // test last cell - since the last cell has only room for + // a halfwidth char any fullwidth shifted there is lost + // and will be set to empty cell if (bufferRow.getWidth(cols - 1) === 2) { bufferRow.setCellFromCodepoint(cols - 1, NULL_CELL_CODE, NULL_CELL_WIDTH, curAttr); } @@ -688,11 +650,9 @@ export class InputHandler extends Disposable implements IInputHandler { // write current char to buffer and advance cursor bufferRow.setCellFromCodepoint(this._activeBuffer.x++, code, chWidth, curAttr); - /* - * fullwidth char - also set next cell to placeholder stub and advance cursor - * for graphemes bigger than fullwidth we can simply loop to zero - * we already made sure above, that this._activeBuffer.x + chWidth will not overflow right - */ + // fullwidth char - also set next cell to placeholder stub and advance cursor + // for graphemes bigger than fullwidth we can simply loop to zero + // we already made sure above, that this._activeBuffer.x + chWidth will not overflow right if (chWidth > 0) { while (--chWidth) { // other than a regular empty cell a cell following a wide char has no width @@ -790,13 +750,11 @@ export class InputHandler extends Disposable implements IInputHandler { } 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. - */ + // 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. @@ -841,11 +799,9 @@ export class InputHandler extends Disposable implements IInputHandler { return true; } - /* - * reverse wrap-around is enabled - * other than for normal operation mode, reverse wrap-around allows the cursor - * to be at x=cols to be able to address the last cell of a row by BS - */ + // reverse wrap-around is enabled + // other than for normal operation mode, reverse wrap-around allows the cursor + // to be at x=cols to be able to address the last cell of a row by BS this._restrictCursor(this._bufferService.cols); if (this._activeBuffer.x > 0) { @@ -866,21 +822,17 @@ export class InputHandler extends Disposable implements IInputHandler { this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = false; this._activeBuffer.y--; this._activeBuffer.x = this._bufferService.cols - 1; - /* - * find last taken cell - last cell can have 3 different states: - * - hasContent(true) + hasWidth(1): narrow char - we are done - * - hasWidth(0): second part of wide char - we are done - * - hasContent(false) + hasWidth(1): empty cell due to early wrapping wide char, go one - * cell further back - */ + // find last taken cell - last cell can have 3 different states: + // - hasContent(true) + hasWidth(1): narrow char - we are done + // - hasWidth(0): second part of wide char - we are done + // - hasContent(false) + hasWidth(1): empty cell due to early wrapping wide char, go one + // cell further back const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!; if (line.hasWidth(this._activeBuffer.x) && !line.hasContent(this._activeBuffer.x)) { this._activeBuffer.x--; - /* - * We do this only once, since width=1 + hasContent=false currently happens only once - * before early wrapping of a wide char. - * This needs to be fixed once we support graphemes taking more than 2 cells. - */ + // We do this only once, since width=1 + hasContent=false currently happens only once + // before early wrapping of a wide char. + // This needs to be fixed once we support graphemes taking more than 2 cells. } } } @@ -961,10 +913,8 @@ export class InputHandler extends Disposable implements IInputHandler { * Set relative cursor position. */ private _moveCursor(x: number, y: number): void { - /* - * for relative changes we have to make sure we are within 0 .. cols/rows - 1 - * before calculating the new position - */ + // for relative changes we have to make sure we are within 0 .. cols/rows - 1 + // before calculating the new position this._restrictCursor(); this._setCursor(this._activeBuffer.x + x, this._activeBuffer.y + y); } @@ -1404,10 +1354,8 @@ export class InputHandler extends Disposable implements IInputHandler { const scrollBottomRowsOffset = this._bufferService.rows - 1 - this._activeBuffer.scrollBottom; const scrollBottomAbsolute = this._bufferService.rows - 1 + this._activeBuffer.ybase - scrollBottomRowsOffset + 1; while (param--) { - /* - * test: echo -e '\e[44m\e[1L\e[0m' - * blankLine(true) - xterm/linux behavior - */ + // test: echo -e '\e[44m\e[1L\e[0m' + // blankLine(true) - xterm/linux behavior this._activeBuffer.lines.splice(scrollBottomAbsolute - 1, 1); this._activeBuffer.lines.splice(row, 0, this._activeBuffer.getBlankLine(this._eraseAttrData())); } @@ -1440,10 +1388,8 @@ export class InputHandler extends Disposable implements IInputHandler { j = this._bufferService.rows - 1 - this._activeBuffer.scrollBottom; j = this._bufferService.rows - 1 + this._activeBuffer.ybase - j; while (param--) { - /* - * test: echo -e '\e[44m\e[1M\e[0m' - * blankLine(true) - xterm/linux behavior - */ + // test: echo -e '\e[44m\e[1M\e[0m' + // blankLine(true) - xterm/linux behavior this._activeBuffer.lines.splice(row, 1); this._activeBuffer.lines.splice(j, 0, this._activeBuffer.getBlankLine(this._eraseAttrData())); } @@ -1791,20 +1737,16 @@ export class InputHandler extends Disposable implements IInputHandler { if (params.params[0] > 0) { return true; } - /* - * xterm and urxvt - * seem to spit this - * out around ~370 times (?). - */ + // xterm and urxvt + // seem to spit this + // out around ~370 times (?). if (this._is('xterm')) { this._coreService.triggerDataEvent(C0.ESC + '[>0;276;0c'); } else if (this._is('rxvt-unicode')) { this._coreService.triggerDataEvent(C0.ESC + '[>85;95;0c'); } else if (this._is('linux')) { - /* - * not supported by linux console. - * linux console echoes parameters. - */ + // not supported by linux console. + // linux console echoes parameters. this._coreService.triggerDataEvent(params.params[0] + 'c'); } else if (this._is('screen')) { this._coreService.triggerDataEvent(C0.ESC + '[>83;40003;0c'); @@ -2037,17 +1979,13 @@ export class InputHandler extends Disposable implements IInputHandler { this._mouseStateService.activeProtocol = 'DRAG'; break; case 1003: // any event mouse - /* - * any event - sends motion events, - * even if there is no button held down. - */ + // any event - sends motion events, + // even if there is no button held down. this._mouseStateService.activeProtocol = 'ANY'; break; case 1004: // send focusin/focusout events - /* - * focusin: ^[[I - * focusout: ^[[O - */ + // focusin: ^[[I + // focusout: ^[[O this._coreService.decPrivateModes.sendFocus = true; this._onRequestSendFocus.fire(); break; @@ -2470,12 +2408,10 @@ export class InputHandler extends Disposable implements IInputHandler { * Returns advance for params index. */ private _extractColor(params: IParams, pos: number, attr: IAttributeData): number { - /* - * normalize params - * meaning: [target, CM, ign, val, val, val] - * RGB : [ 38/48, 2, ign, r, g, b] - * P256 : [ 38/48, 5, ign, v, ign, ign] - */ + // normalize params + // meaning: [target, CM, ign, val, val, val] + // RGB : [ 38/48, 2, ign, r, g, b] + // P256 : [ 38/48, 5, ign, v, ign, ign] const accu = [0, 0, -1, 0, 0, 0]; // alignment placeholder for non color space sequences @@ -2541,10 +2477,8 @@ export class InputHandler extends Disposable implements IInputHandler { * 4:5 - dashed underline */ private _processUnderline(style: number, attr: IAttributeData): void { - /* - * treat extended attrs as immutable, thus always clone from old one - * this is needed since the buffer only holds references to it - */ + // treat extended attrs as immutable, thus always clone from old one + // this is needed since the buffer only holds references to it attr.extended = attr.extended.clone(); // default to 1 == single underline @@ -2567,10 +2501,8 @@ export class InputHandler extends Disposable implements IInputHandler { attr.fg = DEFAULT_ATTR_DATA.fg; attr.bg = DEFAULT_ATTR_DATA.bg; attr.extended = attr.extended.clone(); - /* - * Reset underline style and color. Note that we don't want to reset other - * fields such as the url id. - */ + // Reset underline style and color. Note that we don't want to reset other + // fields such as the url id. attr.extended.underlineStyle = UnderlineStyle.NONE; attr.extended.underlineColor &= ~(Attributes.CM_MASK | Attributes.RGB_MASK); attr.updateExtended(); @@ -2709,10 +2641,8 @@ export class InputHandler extends Disposable implements IInputHandler { // blink attr.fg |= FgFlags.BLINK; } else if (p === 7) { - /* - * inverse and positive - * test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m' - */ + // inverse and positive + // test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m' attr.fg |= FgFlags.INVERSE; } else if (p === 8) { // invisible @@ -2826,10 +2756,8 @@ export class InputHandler extends Disposable implements IInputHandler { // @vt: #P[Only CPR is supported.] CSI DECDSR "DEC Device Status Report" "CSI ? Ps n" "Only CPR is supported (same as DSR)." public deviceStatusPrivate(params: IParams): boolean { - /* - * modern xterm doesnt seem to - * respond to any of these except ?6, 6, and 5 - */ + // modern xterm doesnt seem to + // respond to any of these except ?6, 6, and 5 switch (params.params[0]) { case 6: // cursor position @@ -2838,28 +2766,20 @@ export class InputHandler extends Disposable implements IInputHandler { this._coreService.triggerDataEvent(`${C0.ESC}[?${y};${x}R`); break; case 15: - /* - * no printer - * this.handler(C0.ESC + '[?11n'); - */ + // no printer + // this.handler(C0.ESC + '[?11n'); break; case 25: - /* - * dont support user defined keys - * this.handler(C0.ESC + '[?21n'); - */ + // dont support user defined keys + // this.handler(C0.ESC + '[?21n'); break; case 26: - /* - * north american keyboard - * this.handler(C0.ESC + '[?27;1;0;0n'); - */ + // north american keyboard + // this.handler(C0.ESC + '[?27;1;0;0n'); break; case 53: - /* - * no dec locator/mouse - * this.handler(C0.ESC + '[?50n'); - */ + // no dec locator/mouse + // this.handler(C0.ESC + '[?50n'); break; case 996: // color scheme query (https://contour-terminal.org/vt-extensions/color-palette-update-notifications/) @@ -3481,11 +3401,9 @@ export class InputHandler extends Disposable implements IInputHandler { public reverseIndex(): boolean { this._restrictCursor(); if (this._activeBuffer.y === this._activeBuffer.scrollTop) { - /* - * possibly move the code below to term.reverseScroll(); - * test: echo -ne '\e[1;1H\e[44m\eM\e[0m' - * blankLine(true) is xterm/linux behavior - */ + // possibly move the code below to term.reverseScroll(); + // test: echo -ne '\e[1;1H\e[44m\eM\e[0m' + // blankLine(true) is xterm/linux behavior const scrollRegionHeight = this._activeBuffer.scrollBottom - this._activeBuffer.scrollTop; this._activeBuffer.lines.shiftElements(this._activeBuffer.ybase + this._activeBuffer.y, scrollRegionHeight, 1); this._activeBuffer.lines.set(this._activeBuffer.ybase + this._activeBuffer.y, this._activeBuffer.getBlankLine(this._eraseAttrData())); diff --git a/src/common/Platform.ts b/src/common/Platform.ts index ad2d1424c..09ba097e6 100644 --- a/src/common/Platform.ts +++ b/src/common/Platform.ts @@ -9,18 +9,14 @@ interface INavigator { platform: string; } -/* - * We're declaring a navigator global here as we expect it in all runtimes (node and browser), but - * we want this module to live in common. - */ +// We're declaring a navigator global here as we expect it in all runtimes (node and browser), but +// we want this module to live in common. declare const navigator: INavigator; declare const process: unknown; -/* - * navigator.userAgent is also checked here because bundling with the process module can cause - * issues otherwise. Note that navigator exists in Node.js 21+ but the userAgent is - * "Node.js/". - */ +// navigator.userAgent is also checked here because bundling with the process module can cause +// issues otherwise. Note that navigator exists in Node.js 21+ but the userAgent is +// "Node.js/". export const isNode = (typeof process !== 'undefined' && 'title' in (process as any) && (typeof navigator === 'undefined' || navigator.userAgent.startsWith('Node.js/'))) ? true : false; const userAgent = (isNode) ? 'node' : navigator.userAgent; const platform = (isNode) ? 'node' : navigator.platform; @@ -48,11 +44,9 @@ export function getSafariVersion(): number { return parseInt(majorVersion[1]); } -/* - * Find the users platform. We use this to interpret the meta key - * and ISO third level shifts. - * http://stackoverflow.com/q/19877924/577598 - */ +// Find the users platform. We use this to interpret the meta key +// and ISO third level shifts. +// http://stackoverflow.com/q/19877924/577598 export const isMac = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'].includes(platform); export const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].includes(platform); export const isLinux = platform.indexOf('Linux') >= 0; diff --git a/src/common/SortedList.ts b/src/common/SortedList.ts index 8ae39d3d8..c6dc62085 100644 --- a/src/common/SortedList.ts +++ b/src/common/SortedList.ts @@ -191,10 +191,8 @@ export class SortedList { return mid; } } - /* - * key not in list - * still return closest min (also used as insert position) - */ + // key not in list + // still return closest min (also used as insert position) return min; } } diff --git a/src/common/TaskQueue.ts b/src/common/TaskQueue.ts index ae9041d3a..3cad0a140 100644 --- a/src/common/TaskQueue.ts +++ b/src/common/TaskQueue.ts @@ -83,23 +83,17 @@ abstract class TaskQueue implements ITaskQueue { if (!this._tasks[this._i]()) { this._i++; } - /* - * other than performance.now, performance.now might not be stable (changes on wall clock - * changes), this is not an issue here as a clock change during a short running task is very - * unlikely in case it still happened and leads to negative duration, simply assume 1 msec - */ + // other than performance.now, performance.now might not be stable (changes on wall clock + // changes), this is not an issue here as a clock change during a short running task is very + // unlikely in case it still happened and leads to negative duration, simply assume 1 msec taskDuration = Math.max(1, performance.now() - taskDuration); longestTask = Math.max(taskDuration, longestTask); - /* - * Guess the following task will take a similar time to the longest task in this batch, allow - * additional room to try avoid exceeding the deadline - */ + // Guess the following task will take a similar time to the longest task in this batch, allow + // additional room to try avoid exceeding the deadline deadlineRemaining = deadline.timeRemaining(); if (longestTask * 1.5 > deadlineRemaining) { - /* - * Warn when the time exceeding the deadline is over 20ms, if this happens in practice the - * task should be split into sub-tasks to ensure the UI remains responsive. - */ + // Warn when the time exceeding the deadline is over 20ms, if this happens in practice the + // task should be split into sub-tasks to ensure the UI remains responsive. if (lastDeadlineRemaining - taskDuration < -20) { this._logService.warn(`task queue exceeded allotted deadline by ${Math.abs(Math.round(lastDeadlineRemaining - taskDuration))}ms`); } diff --git a/src/common/Types.ts b/src/common/Types.ts index fb95f5fe7..1981a910f 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -310,10 +310,8 @@ export const enum CoreMouseButton { RIGHT = 2, NONE = 3, WHEEL = 4, - /* - * additional buttons 1..8 - * untested! - */ + // additional buttons 1..8 + // untested! AUX1 = 8, AUX2 = 9, AUX3 = 10, @@ -532,7 +530,7 @@ export interface IInputHandler { /** * OSC 0 - * OSC 2 + * OSC 2 */ setTitle(data: string): boolean; /** OSC 4 */ setOrReportIndexedColor(data: string): boolean; /** OSC 10 */ setOrReportFgColor(data: string): boolean; @@ -548,16 +546,16 @@ export interface IInputHandler { /** ESC > */ keypadNumericMode(): boolean; /** * ESC % G - * ESC % @ + * ESC % @ */ selectDefaultCharset(): boolean; /** * ESC ( C - * ESC ) C - * ESC * C - * ESC + C - * ESC - C - * ESC . C - * ESC / C + * ESC ) C + * ESC * C + * ESC + C + * ESC - C + * ESC . C + * ESC / C */ selectCharset(collectAndFlag: string): boolean; /** ESC D */ index(): boolean; /** ESC H */ tabSet(): boolean; @@ -565,10 +563,10 @@ export interface IInputHandler { /** ESC c */ fullReset(): boolean; /** * ESC n - * ESC o - * ESC | - * ESC } - * ESC ~ + * ESC o + * ESC | + * ESC } + * ESC ~ */ setgLevel(level: number): boolean; /** ESC # 8 */ screenAlignmentPattern(): boolean; } diff --git a/src/common/WindowsMode.ts b/src/common/WindowsMode.ts index f59343286..7cff094b2 100644 --- a/src/common/WindowsMode.ts +++ b/src/common/WindowsMode.ts @@ -7,18 +7,16 @@ import { CHAR_DATA_CODE_INDEX, NULL_CELL_CODE, WHITESPACE_CELL_CODE } from 'comm import { IBufferService } from 'common/services/Services'; export function updateWindowsModeWrappedState(bufferService: IBufferService): void { - /* - * Winpty does not support wraparound mode which means that lines will never - * be marked as wrapped. This causes issues for things like copying a line - * retaining the wrapped new line characters or if consumers are listening - * in on the data stream. - * - * The workaround for this is to listen to every incoming line feed and mark - * the line as wrapped if the last character in the previous line is not a - * space. This is certainly not without its problems, but generally on - * Windows when text reaches the end of the terminal it's likely going to be - * wrapped. - */ + // Winpty does not support wraparound mode which means that lines will never + // be marked as wrapped. This causes issues for things like copying a line + // retaining the wrapped new line characters or if consumers are listening + // in on the data stream. + // + // The workaround for this is to listen to every incoming line feed and mark + // the line as wrapped if the last character in the previous line is not a + // space. This is certainly not without its problems, but generally on + // Windows when text reaches the end of the terminal it's likely going to be + // wrapped. const line = bufferService.buffer.lines.get(bufferService.buffer.ybase + bufferService.buffer.y - 1); const lastChar = line?.get(bufferService.cols - 1); diff --git a/src/common/buffer/Buffer.test.ts b/src/common/buffer/Buffer.test.ts index f26807a94..abfea7b06 100644 --- a/src/common/buffer/Buffer.test.ts +++ b/src/common/buffer/Buffer.test.ts @@ -348,10 +348,8 @@ describe('Buffer', () => { assert.equal(buffer.lines.get(4)!.translateToString(), ' '); }); it('should remove the correct amount of rows when reflowing larger', () => { - /* - * This is a regression test to ensure that successive wrapped lines that are getting - * 3+ lines removed on a reflow actually remove the right lines - */ + // This is a regression test to ensure that successive wrapped lines that are getting + // 3+ lines removed on a reflow actually remove the right lines buffer.fillViewportRows(); buffer.resize(10, 10); buffer.y = 2; @@ -431,12 +429,10 @@ describe('Buffer', () => { buffer.lines.get(2)!.set(i, [0, char, 1, code]); } buffer.y = 3; - /* - * Buffer: - * abcdefghij - * 0123456789 - * abcdefghij - */ + // Buffer: + // abcdefghij + // 0123456789 + // abcdefghij const firstMarker = buffer.addMarker(0); const secondMarker = buffer.addMarker(1); const thirdMarker = buffer.addMarker(2); @@ -496,12 +492,10 @@ describe('Buffer', () => { buffer.lines.get(2)!.set(i, [0, char, 1, code]); } buffer.y = 10; - /* - * Buffer: - * abcdefghij - * 0123456789 - * abcdefghij - */ + // Buffer: + // abcdefghij + // 0123456789 + // abcdefghij const firstMarker = buffer.addMarker(0); const secondMarker = buffer.addMarker(1); const thirdMarker = buffer.addMarker(2); @@ -545,11 +539,9 @@ describe('Buffer', () => { buffer.lines.get(1)!.set(0, [0, 'c', 1, 'c'.charCodeAt(0)]); buffer.lines.get(1)!.set(1, [0, 'd', 1, 'd'.charCodeAt(0)]); buffer.lines.get(1)!.isWrapped = true; - /* - * Buffer: - * "ab " (wrapped) - * "cd" - */ + // Buffer: + // "ab " (wrapped) + // "cd" buffer.resize(5, 10); assert.equal(buffer.ybase, 0); assert.equal(buffer.lines.length, 10); @@ -578,11 +570,9 @@ describe('Buffer', () => { buffer.lines.get(1)!.set(i, [0, '', 0, 0]); } buffer.lines.get(1)!.isWrapped = true; - /* - * Buffer: - * 汉语汉语汉语 (wrapped) - * 汉语汉语汉语 - */ + // Buffer: + // 汉语汉语汉语 (wrapped) + // 汉语汉语汉语 assert.equal(buffer.lines.get(0)!.translateToString(true), '汉语汉语汉语'); assert.equal(buffer.lines.get(1)!.translateToString(true), '汉语汉语汉语'); buffer.resize(13, 10); @@ -607,11 +597,9 @@ describe('Buffer', () => { buffer.lines.get(1)!.set(0, [0, 'c', 1, 'c'.charCodeAt(0)]); buffer.lines.get(1)!.set(1, [0, 'd', 1, 'd'.charCodeAt(0)]); buffer.lines.get(1)!.isWrapped = true; - /* - * Buffer: - * "ab " (wrapped) - * "cd" - */ + // Buffer: + // "ab " (wrapped) + // "cd" buffer.resize(3, 10); assert.equal(buffer.y, 2); assert.equal(buffer.ybase, 0); @@ -643,11 +631,9 @@ describe('Buffer', () => { buffer.lines.get(1)!.set(i, [0, '', 0, 0]); } buffer.lines.get(1)!.isWrapped = true; - /* - * Buffer: - * 汉语汉语汉语 (wrapped) - * 汉语汉语汉语 - */ + // Buffer: + // 汉语汉语汉语 (wrapped) + // 汉语汉语汉语 assert.equal(buffer.lines.get(0)!.translateToString(true), '汉语汉语汉语'); assert.equal(buffer.lines.get(1)!.translateToString(true), '汉语汉语汉语'); buffer.resize(11, 10); @@ -682,19 +668,17 @@ describe('Buffer', () => { describe('reflowLarger cases', () => { beforeEach(() => { - /* - * Setup buffer state: - * 'ab' - * 'cd' (wrapped) - * 'ef' - * 'gh' (wrapped) - * 'ij' - * 'kl' (wrapped) - * ' ' - * ' ' - * ' ' - * ' ' - */ + // Setup buffer state: + // 'ab' + // 'cd' (wrapped) + // 'ef' + // 'gh' (wrapped) + // 'ij' + // 'kl' (wrapped) + // ' ' + // ' ' + // ' ' + // ' ' buffer.fillViewportRows(); buffer.resize(2, 10); buffer.lines.get(0)!.set(0, [0, 'a', 1, 'a'.charCodeAt(0)]); @@ -875,19 +859,17 @@ describe('Buffer', () => { }); describe('reflowSmaller cases', () => { beforeEach(() => { - /* - * Setup buffer state: - * 'abcd' - * 'efgh' (wrapped) - * 'ijkl' - * ' ' - * ' ' - * ' ' - * ' ' - * ' ' - * ' ' - * ' ' - */ + // Setup buffer state: + // 'abcd' + // 'efgh' (wrapped) + // 'ijkl' + // ' ' + // ' ' + // ' ' + // ' ' + // ' ' + // ' ' + // ' ' buffer.fillViewportRows(); buffer.resize(4, 10); buffer.lines.get(0)!.set(0, [0, 'a', 1, 'a'.charCodeAt(0)]); @@ -1319,10 +1301,8 @@ describe('Buffer', () => { assert.equal(line.length, INIT_COLS / 2 - 1); } - /* - * wait for a bit to give IdleTaskQueue a chance to kick in - * and finish memory cleaning - */ + // wait for a bit to give IdleTaskQueue a chance to kick in + // and finish memory cleaning await new Promise(r => setTimeout(r, 30)); // cleanup should have realigned memory with exact bytelength diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts index a572418c4..34ef02f6a 100644 --- a/src/common/buffer/Buffer.ts +++ b/src/common/buffer/Buffer.ts @@ -169,25 +169,19 @@ export class Buffer extends Disposable implements IBuffer { // count bufferlines with overly big memory to be cleaned afterwards let dirtyMemoryLines = 0; - /* - * Increase max length if needed before adjustments to allow space to fill - * as required. - */ + // Increase max length if needed before adjustments to allow space to fill + // as required. const newMaxLength = this._getCorrectBufferLength(newRows); if (newMaxLength > this.lines.maxLength) { this.lines.maxLength = newMaxLength; } - /* - * if (this._cols > newCols) { - * console.log('increase!'); - * } - */ + // if (this._cols > newCols) { + // console.log('increase!'); + // } - /* - * The following adjustments should only happen if the buffer has been - * initialized/filled. - */ + // The following adjustments should only happen if the buffer has been + // initialized/filled. if (this.lines.length > 0) { // Deal with columns increasing (reducing needs to happen after reflow) if (this._cols < newCols) { @@ -203,17 +197,13 @@ export class Buffer extends Disposable implements IBuffer { for (let y = this._rows; y < newRows; y++) { if (this.lines.length < newRows + this.ybase) { if (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 - */ + // 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(this._stringCache, newCols, nullCell, false)); } else { if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) { - /* - * There is room above the buffer and there are no empty elements below the line, - * scroll up - */ + // There is room above the buffer and there are no empty elements below the line, + // scroll up this.ybase--; addToY++; if (this.ydisp > 0) { @@ -221,10 +211,8 @@ export class Buffer extends Disposable implements IBuffer { this.ydisp--; } } else { - /* - * Add a blank line if there is no buffer left at the top to scroll to, or if there - * are blank lines after the cursor - */ + // Add a blank line if there is no buffer left at the top to scroll to, or if there + // are blank lines after the cursor this.lines.push(new BufferLine(this._stringCache, newCols, nullCell, false)); } } @@ -245,10 +233,8 @@ export class Buffer extends Disposable implements IBuffer { } } - /* - * Reduce max length if needed after adjustments, this is done after as it - * would otherwise cut data from the bottom of the buffer. - */ + // Reduce max length if needed after adjustments, this is done after as it + // would otherwise cut data from the bottom of the buffer. if (newMaxLength < this.lines.maxLength) { // Trim from the top of the buffer and adjust ybase and ydisp. const amountToTrim = this.lines.length - newMaxLength; @@ -289,10 +275,8 @@ export class Buffer extends Disposable implements IBuffer { this._cols = newCols; this._rows = newRows; - /* - * Ensure the cursor position invariant: ybase + y must be within buffer bounds - * This can be violated during reflow or when shrinking rows - */ + // Ensure the cursor position invariant: ybase + y must be within buffer bounds + // This can be violated during reflow or when shrinking rows if (this.lines.length > 0) { const maxY = Math.max(0, this.lines.length - this.ybase - 1); this.y = Math.min(this.y, maxY); @@ -309,10 +293,8 @@ export class Buffer extends Disposable implements IBuffer { private _batchedMemoryCleanup(): boolean { let normalRun = true; if (this._memoryCleanupPosition >= this.lines.length) { - /* - * cleanup made it once through all lines, thus rescan in loop below to also catch shifted - * lines, which should finish rather quick if there are no more cleanups pending - */ + // cleanup made it once through all lines, thus rescan in loop below to also catch shifted + // lines, which should finish rather quick if there are no more cleanups pending this._memoryCleanupPosition = 0; normalRun = false; } @@ -324,11 +306,9 @@ export class Buffer extends Disposable implements IBuffer { return true; } } - /* - * normal runs always need another rescan afterwards - * if we made it here with normalRun=false, we are in a final run - * and can end the cleanup task for sure - */ + // normal runs always need another rescan afterwards + // if we made it here with normalRun=false, we are in a final run + // and can end the cleanup task for sure return normalRun; } @@ -389,10 +369,8 @@ export class Buffer extends Disposable implements IBuffer { private _reflowSmaller(newCols: number, newRows: number): void { const reflowCursorLine = this._optionsService.rawOptions.reflowCursorLine; const nullCell = this.getNullCell(DEFAULT_ATTR_DATA); - /* - * Gather all BufferLines that need to be inserted into the Buffer here so that they can be - * batched up and only committed once - */ + // Gather all BufferLines that need to be inserted into the Buffer here so that they can be + // batched up and only committed once const toInsert = []; let countToInsert = 0; // Go backwards as many lines may be trimmed and this will avoid considering them @@ -411,10 +389,8 @@ export class Buffer extends Disposable implements IBuffer { } if (!reflowCursorLine) { - /* - * If these lines contain the cursor don't touch them, the program will handle fixing up - * wrapped lines with the cursor - */ + // If these lines contain the cursor don't touch them, the program will handle fixing up + // wrapped lines with the cursor const absoluteY = this.ybase + this.y; if (absoluteY >= y && absoluteY < y + wrappedLines.length) { continue; @@ -440,10 +416,8 @@ export class Buffer extends Disposable implements IBuffer { } if (newLines.length > 0) { toInsert.push({ - /* - * countToInsert here gets the actual index, taking into account other inserted items. - * using this we can iterate through the list forwards - */ + // countToInsert here gets the actual index, taking into account other inserted items. + // using this we can iterate through the list forwards start: y + wrappedLines.length + countToInsert, newLines }); @@ -463,10 +437,8 @@ export class Buffer extends Disposable implements IBuffer { while (srcLineIndex >= 0) { const cellsToCopy = Math.min(srcCol, destCol); if (wrappedLines[destLineIndex] === undefined) { - /* - * Sanity check that the line exists, this has been known to fail for an unknown reason - * which would stop the reflow from happening if an exception would throw. - */ + // Sanity check that the line exists, this has been known to fail for an unknown reason + // which would stop the reflow from happening if an exception would throw. break; } wrappedLines[destLineIndex].copyCellsFrom(wrappedLines[srcLineIndex], srcCol - cellsToCopy, destCol - cellsToCopy, cellsToCopy, true); @@ -514,16 +486,12 @@ export class Buffer extends Disposable implements IBuffer { this.savedY = Math.min(this.savedY + linesToAdd, this.ybase + newRows - 1); } - /* - * Rearrange lines in the buffer if there are any insertions, this is done at the end rather - * than earlier so that it's a single O(n) pass through the buffer, instead of O(n^2) from many - * costly calls to CircularList.splice. - */ + // Rearrange lines in the buffer if there are any insertions, this is done at the end rather + // than earlier so that it's a single O(n) pass through the buffer, instead of O(n^2) from many + // costly calls to CircularList.splice. if (toInsert.length > 0) { - /* - * Record buffer insert events and then play them back backwards so that the indexes are - * correct - */ + // Record buffer insert events and then play them back backwards so that the indexes are + // correct const insertEvents: IInsertEvent[] = []; // Record original lines so they don't get overridden when we rearrange the list diff --git a/src/common/buffer/BufferLine.test.ts b/src/common/buffer/BufferLine.test.ts index 7e8f73f4c..3aec28872 100644 --- a/src/common/buffer/BufferLine.test.ts +++ b/src/common/buffer/BufferLine.test.ts @@ -288,11 +288,9 @@ describe('BufferLine', function(): void { assert.equal(line2.isWrapped, line.isWrapped); }); it('should support combining chars', function(): void { - /* - * CHAR_DATA_CODE_INDEX resembles current behavior in InputHandler.print - * --> set code to the last charCodeAt value of the string - * Note: needs to be fixed once the string pointer is in place - */ + // CHAR_DATA_CODE_INDEX resembles current behavior in InputHandler.print + // --> set code to the last charCodeAt value of the string + // Note: needs to be fixed once the string pointer is in place const line = new TestBufferLine(2, createCellData(1, 'e\u0301', 1)); assert.deepEqual(line.toArray(), [[1, 'e\u0301', 1, '\u0301'.charCodeAt(0)], [1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]]); const line2 = new TestBufferLine(5, createCellData(1, 'a', 1), true); @@ -480,11 +478,9 @@ describe('BufferLine', function(): void { }); it('should always return some sane value', function(): void { const columns: number[] = []; - /* - * sanity check - broken line with invalid out of bound null width cells - * this can atm happen with deleting/inserting chars in inputhandler by "breaking" - * fullwidth pairs --> needs to be fixed after settling BufferLine impl - */ + // sanity check - broken line with invalid out of bound null width cells + // this can atm happen with deleting/inserting chars in inputhandler by "breaking" + // fullwidth pairs --> needs to be fixed after settling BufferLine impl const line = new TestBufferLine(10, NULL_CELL_DATA, false); assert.equal(line.translateToString(false, undefined, undefined, columns), ' '); assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); @@ -504,10 +500,8 @@ describe('BufferLine', function(): void { const line = new TestBufferLine(3, NULL_CELL_DATA, false); line.addCodepointToCell(0, '\u0301'.charCodeAt(0), 0); const cell = line.loadCell(0, new CellData()); - /* - * chars contains single combining char - * width is set to 1 - */ + // chars contains single combining char + // width is set to 1 assert.deepEqual(cell.getAsCharData(), [DEFAULT_ATTR, '\u0301', 1, 0x0301]); // do not account a single combining char as combined assert.equal(cell.isCombined(), 0); @@ -519,10 +513,8 @@ describe('BufferLine', function(): void { line.setCell(0, cell); line.addCodepointToCell(0, '\u0301'.charCodeAt(0), 0); line.loadCell(0, cell); - /* - * chars contains 3 chars - * width is set to 1 - */ + // chars contains 3 chars + // width is set to 1 assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301\u0301', 1, 0x0301]); // do not account a single combining char as combined assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK); @@ -534,10 +526,8 @@ describe('BufferLine', function(): void { line.setCell(0, cell); line.addCodepointToCell(0, '\u0301'.charCodeAt(0), 0); line.loadCell(0, cell); - /* - * chars contains 2 chars - * width is set to 1 - */ + // chars contains 2 chars + // width is set to 1 assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301', 1, 0x0301]); // do not account a single combining char as combined assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK); diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts index 0cef299e1..be200f112 100644 --- a/src/common/buffer/BufferLine.ts +++ b/src/common/buffer/BufferLine.ts @@ -9,13 +9,11 @@ import { CellData } from 'common/buffer/CellData'; import { Attributes, BgFlags, CHAR_DATA_ATTR_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, Content, NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, WHITESPACE_CELL_CHAR } from 'common/buffer/Constants'; import { stringFromCodePoint } from 'common/input/TextDecoder'; -/* - * Buffer memory layout: - * - * [0]: content `uint32_t` - wcwidth(2) comb(1) codepoint(21) - * [1]: fg `uint32_t` - flags(8) r(8) g(8) b(8) - * [2]: bg `uint32_t` - flags(8) r(8) g(8) b(8) - */ +// Buffer memory layout: +// +// [0]: content `uint32_t` - wcwidth(2) comb(1) codepoint(21) +// [1]: fg `uint32_t` - flags(8) r(8) g(8) b(8) +// [2]: bg `uint32_t` - flags(8) r(8) g(8) b(8) const enum Constants { /** The number of 32 bit array indices taken by one cell. */ @@ -257,19 +255,15 @@ export class BufferLine implements IBufferLine { this._combined[index] += stringFromCodePoint(codePoint); } else { if (content & Content.CODEPOINT_MASK) { - /* - * normal case for combining chars: - * - move current leading char + new one into combined string - * - set combined flag - */ + // normal case for combining chars: + // - move current leading char + new one into combined string + // - set combined flag this._combined[index] = stringFromCodePoint(content & Content.CODEPOINT_MASK) + stringFromCodePoint(codePoint); content &= ~Content.CODEPOINT_MASK; // set codepoint in buffer to 0 content |= Content.IS_COMBINED_MASK; } else { - /* - * should not happen - we actually have no data in the cell yet - * simply set the data in the cell buffer with a width of 1 - */ + // should not happen - we actually have no data in the cell yet + // simply set the data in the cell buffer with a width of 1 content = codePoint | (1 << Content.WIDTH_SHIFT); } } @@ -324,11 +318,9 @@ export class BufferLine implements IBufferLine { } } - /* - * handle fullwidth at pos: - * - reset pos-1 if wide char - * - reset pos if width==0 (previous second cell of a wide char) - */ + // handle fullwidth at pos: + // - reset pos-1 if wide char + // - reset pos if width==0 (previous second cell of a wide char) if (pos && this.getWidth(pos - 1) === 2) { this.setCellFromCodepoint(pos - 1, 0, 1, fillCellData); } diff --git a/src/common/buffer/BufferReflow.ts b/src/common/buffer/BufferReflow.ts index 5eaeae406..44aa0976f 100644 --- a/src/common/buffer/BufferReflow.ts +++ b/src/common/buffer/BufferReflow.ts @@ -23,10 +23,8 @@ export interface INewLayoutResult { * @param reflowCursorLine Whether to reflow the line containing the cursor. */ export function reflowLargerGetLinesToRemove(lines: CircularList, oldCols: number, newCols: number, bufferAbsoluteY: number, nullCell: ICellData, reflowCursorLine: boolean): number[] { - /* - * Gather all BufferLines that need to be removed from the Buffer here so that they can be - * batched up and only committed once - */ + // Gather all BufferLines that need to be removed from the Buffer here so that they can be + // batched up and only committed once const toRemove: number[] = []; for (let y = 0; y < lines.length - 1; y++) { @@ -45,10 +43,8 @@ export function reflowLargerGetLinesToRemove(lines: CircularList, o } if (!reflowCursorLine) { - /* - * If these lines contain the cursor don't touch them, the program will handle fixing up - * wrapped lines with the cursor - */ + // If these lines contain the cursor don't touch them, the program will handle fixing up + // wrapped lines with the cursor if (bufferAbsoluteY >= y && bufferAbsoluteY < i) { y += wrappedLines.length - 1; continue; @@ -184,10 +180,8 @@ export function reflowSmallerGetNewLineLengths(wrappedLines: BufferLine[], oldCo const newLineLengths: number[] = []; const cellsNeeded = wrappedLines.map((l, i) => getWrappedLineTrimmedLength(wrappedLines, i, oldCols)).reduce((p, c) => p + c); - /* - * Use srcCol and srcLine to find the new wrapping point, use that to get the cellsAvailable and - * linesNeeded - */ + // Use srcCol and srcLine to find the new wrapping point, use that to get the cellsAvailable and + // linesNeeded let srcCol = 0; let srcLine = 0; let cellsAvailable = 0; @@ -220,11 +214,9 @@ export function getWrappedLineTrimmedLength(lines: BufferLine[], i: number, cols if (i === lines.length - 1) { return lines[i].getTrimmedLength(); } - /* - * Detect whether the following line starts with a wide character and the end of the current line - * is null, if so then we can be pretty sure the null character should be excluded from the line - * length] - */ + // Detect whether the following line starts with a wide character and the end of the current line + // is null, if so then we can be pretty sure the null character should be excluded from the line + // length] const endsInNull = !(lines[i].hasContent(cols - 1)) && lines[i].getWidth(cols - 1) === 1; const followingLineStartsWithWide = lines[i + 1].getWidth(0) === 2; if (endsInNull && followingLineStartsWithWide) { diff --git a/src/common/buffer/BufferSet.ts b/src/common/buffer/BufferSet.ts index 2bad2171f..32a4bf3b4 100644 --- a/src/common/buffer/BufferSet.ts +++ b/src/common/buffer/BufferSet.ts @@ -43,10 +43,8 @@ export class BufferSet extends Disposable implements IBufferSet { this._normalBuffer.value = this._normal; this._normal.fillViewportRows(); - /* - * The alt buffer should never have scrollback. - * See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer - */ + // The alt buffer should never have scrollback. + // See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer this._alt = new Buffer(false, this._optionsService, this._bufferService, this._logService); this._altBuffer.value = this._alt; this._activeBuffer = this._normal; @@ -88,11 +86,9 @@ export class BufferSet extends Disposable implements IBufferSet { } this._normal.x = this._alt.x; this._normal.y = this._alt.y; - /* - * The alt buffer should always be cleared when we switch to the normal - * buffer. This frees up memory since the alt buffer should always be new - * when activated. - */ + // The alt buffer should always be cleared when we switch to the normal + // buffer. This frees up memory since the alt buffer should always be new + // when activated. this._alt.clearAllMarkers(); this._alt.clear(); this._activeBuffer = this._normal; @@ -109,10 +105,8 @@ export class BufferSet extends Disposable implements IBufferSet { if (this._activeBuffer === this._alt) { return; } - /* - * Since the alt buffer is always cleared when the normal buffer is - * activated, we want to fill it when switching to it. - */ + // Since the alt buffer is always cleared when the normal buffer is + // activated, we want to fill it when switching to it. this._alt.fillViewportRows(fillAttr); this._alt.x = this._normal.x; this._alt.y = this._normal.y; diff --git a/src/common/buffer/CellData.ts b/src/common/buffer/CellData.ts index 29e4a0f50..43c4c594b 100644 --- a/src/common/buffer/CellData.ts +++ b/src/common/buffer/CellData.ts @@ -65,10 +65,8 @@ export class CellData extends AttributeData implements ICellData { } else if (value[CHAR_DATA_CHAR_INDEX].length === 2) { const code = value[CHAR_DATA_CHAR_INDEX].charCodeAt(0); - /* - * if the 2-char string is a surrogate create single codepoint - * everything else is combined - */ + // if the 2-char string is a surrogate create single codepoint + // everything else is combined if (0xD800 <= code && code <= 0xDBFF) { const second = value[CHAR_DATA_CHAR_INDEX].charCodeAt(1); if (0xDC00 <= second && second <= 0xDFFF) { diff --git a/src/common/input/Keyboard.test.ts b/src/common/input/Keyboard.test.ts index 4e47cbce0..47f516f0f 100644 --- a/src/common/input/Keyboard.test.ts +++ b/src/common/input/Keyboard.test.ts @@ -123,10 +123,8 @@ describe('Keyboard', () => { }); describe('On non-macOS platforms', () => { - /* - * Evalueate alt + arrow key movement, which is a feature of terminal emulators but not VT100 - * http://unix.stackexchange.com/a/108106 - */ + // Evalueate alt + arrow key movement, which is a feature of terminal emulators but not VT100 + // http://unix.stackexchange.com/a/108106 it('should return \\x1b[1;3D for alt+left', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 37 }, { isMac: false }).key, '\x1b[1;3D'); // CSI 1;3 D }); diff --git a/src/common/input/Keyboard.ts b/src/common/input/Keyboard.ts index 54bc4a27e..545b436c8 100644 --- a/src/common/input/Keyboard.ts +++ b/src/common/input/Keyboard.ts @@ -43,10 +43,8 @@ export function evaluateKeyboardEvent( ): IKeyboardResult { const result: IKeyboardResult = { type: KeyboardResultType.SEND_KEY, - /* - * Whether to cancel event propagation (NOTE: this may not be needed since the event is - * canceled at the end of keyDown - */ + // Whether to cancel event propagation (NOTE: this may not be needed since the event is + // canceled at the end of keyDown cancel: false, // The new key even to emit key: undefined @@ -102,10 +100,8 @@ export function evaluateKeyboardEvent( case 13: // return/enter if (ev.key === 'c' && ev.ctrlKey) { - /* - * HACK: Safari on iPad, iOS, AppleVisionPro sends key 13 when typing ctrl-c on hardware - * keyboard - */ + // HACK: Safari on iPad, iOS, AppleVisionPro sends key 13 when typing ctrl-c on hardware + // keyboard result.key = C0.ETX; } else { result.key = ev.altKey ? C0.ESC + C0.CR : C0.CR; @@ -175,10 +171,8 @@ export function evaluateKeyboardEvent( case 45: // insert if (!ev.shiftKey && !ev.ctrlKey) { - /* - * or + are used to - * copy-paste on some systems. - */ + // or + are used to + // copy-paste on some systems. result.key = C0.ESC + '[2~'; } break; @@ -352,13 +346,11 @@ export function evaluateKeyboardEvent( } else if (ev.keyCode === 32) { result.key = C0.ESC + (ev.ctrlKey ? C0.NUL : ' '); } else if (ev.key === 'Dead' && ev.code.startsWith('Key')) { - /* - * Reference: https://github.com/xtermjs/xterm.js/issues/3725 - * Alt will produce a "dead key" (initate composition) with some - * of the letters in US layout (e.g. N/E/U). - * It's safe to match against Key* since no other `code` values begin with "Key". - * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values#code_values_on_mac - */ + // Reference: https://github.com/xtermjs/xterm.js/issues/3725 + // Alt will produce a "dead key" (initate composition) with some + // of the letters in US layout (e.g. N/E/U). + // It's safe to match against Key* since no other `code` values begin with "Key". + // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values#code_values_on_mac let keyString = ev.code.slice(3, 4); if (!ev.shiftKey) { keyString = keyString.toLowerCase(); @@ -371,10 +363,8 @@ export function evaluateKeyboardEvent( result.type = KeyboardResultType.SELECT_ALL; } } else if (ev.key && !ev.ctrlKey && !ev.altKey && !ev.metaKey && ev.keyCode >= 48 && ev.key.length === 1) { - /* - * Include only keys that that result in a _single_ character; don't include num lock, - * volume up, etc. - */ + // Include only keys that that result in a _single_ character; don't include num lock, + // volume up, etc. result.key = ev.key; } else if (ev.key && ev.ctrlKey && ev.shiftKey) { switch (ev.code) { diff --git a/src/common/input/KittyKeyboard.test.ts b/src/common/input/KittyKeyboard.test.ts index 44c4ae2bb..2a832d75f 100644 --- a/src/common/input/KittyKeyboard.test.ts +++ b/src/common/input/KittyKeyboard.test.ts @@ -40,10 +40,8 @@ describe('KittyKeyboard', () => { const flags = KittyKeyboardFlags.DISAMBIGUATE_ESCAPE_CODES; it('shift+letter sends plain character in DISAMBIGUATE mode', () => { - /* - * Kitty spec: DISAMBIGUATE only encodes keys ambiguous in legacy encoding - * Shift+a → "A" is not ambiguous, so send plain "A" - */ + // Kitty spec: DISAMBIGUATE only encodes keys ambiguous in legacy encoding + // Shift+a → "A" is not ambiguous, so send plain "A" const result = kitty.evaluate(createEvent({ key: 'A', shiftKey: true }), flags); assert.strictEqual(result.key, 'A'); }); @@ -556,10 +554,8 @@ describe('KittyKeyboard', () => { }); }); - /* - * Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense and - * isn't specified in the spec, but press and repeat events shouldn't get swallowed. - */ + // Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense and + // isn't specified in the spec, but press and repeat events shouldn't get swallowed. describe('REPORT_EVENT_TYPES flag without DISAMBIGUATE_ESCAPE_CODES', () => { const flags = KittyKeyboardFlags.REPORT_EVENT_TYPES; diff --git a/src/common/input/KittyKeyboard.ts b/src/common/input/KittyKeyboard.ts index 658d4e0d9..ff57b4f89 100644 --- a/src/common/input/KittyKeyboard.ts +++ b/src/common/input/KittyKeyboard.ts @@ -437,12 +437,10 @@ export class KittyKeyboard { return result; } - /* - * Spec § "Report all keys as escape codes": "Additionally, with this mode, - * events for pressing modifier keys are reported." — i.e. *without* this - * mode, modifier-key press events are suppressed. Kitty's is_modifier_key() - * treats CapsLock/NumLock/ScrollLock as modifier keys for this rule. - */ + // Spec § "Report all keys as escape codes": "Additionally, with this mode, + // events for pressing modifier keys are reported." — i.e. *without* this + // mode, modifier-key press events are suppressed. Kitty's is_modifier_key() + // treats CapsLock/NumLock/ScrollLock as modifier keys for this rule. if (this._isLockKey(ev) && !(flags & KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES)) { return result; } @@ -476,10 +474,8 @@ export class KittyKeyboard { // Special handling for Enter/Tab/Backspace. const specialKey = keyCode === 13 || keyCode === 9 || keyCode === 127; - /* - * Per spec, Enter/Tab/Backspace will not have release events unless "Report all keys as escape - * codes" is also set. - */ + // Per spec, Enter/Tab/Backspace will not have release events unless "Report all keys as escape + // codes" is also set. if (specialKey && eventType === KittyKeyboardEventType.RELEASE && !(flags & KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES)) { return result; } @@ -489,18 +485,14 @@ export class KittyKeyboard { const useCsiU = !!( flags & KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES || (reportEventTypes && eventType === KittyKeyboardEventType.RELEASE) || - /* - * Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense, so - * just make REPORT_EVENT_TYPES imply DISAMBIGUATE_ESCAPE_CODES here for simplicity. - * See: https://github.com/kovidgoyal/kitty/issues/9999 - */ + // Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense, so + // just make REPORT_EVENT_TYPES imply DISAMBIGUATE_ESCAPE_CODES here for simplicity. + // See: https://github.com/kovidgoyal/kitty/issues/9999 ((flags & KittyKeyboardFlags.DISAMBIGUATE_ESCAPE_CODES || reportEventTypes) && ( - /* - * Per spec, Enter/Tab/Backspace "still generate the same bytes as in legacy mode" and - * consider space to be a text-generating key, so these skip the isFunc fast-path and only - * get CSI u when modifiers are present (handled below). - */ + // Per spec, Enter/Tab/Backspace "still generate the same bytes as in legacy mode" and + // consider space to be a text-generating key, so these skip the isFunc fast-path and only + // get CSI u when modifiers are present (handled below). (isFunc && !specialKey) || ( (modifiers > 0 && ev.key.length !== 1) || diff --git a/src/common/input/TextDecoder.ts b/src/common/input/TextDecoder.ts index cc503166d..7ec9c7cd2 100644 --- a/src/common/input/TextDecoder.ts +++ b/src/common/input/TextDecoder.ts @@ -28,13 +28,11 @@ export function utf32ToString(data: Uint32Array, start: number = 0, end: number for (let i = start; i < end; ++i) { let codepoint = data[i]; if (codepoint > 0xFFFF) { - /* - * JS strings are encoded as UTF16, thus a non BMP codepoint gets converted into a surrogate - * pair conversion rules: - * - subtract 0x10000 from code point, leaving a 20 bit number - * - add high 10 bits to 0xD800 --> first surrogate - * - add low 10 bits to 0xDC00 --> second surrogate - */ + // JS strings are encoded as UTF16, thus a non BMP codepoint gets converted into a surrogate + // pair conversion rules: + // - subtract 0x10000 from code point, leaving a 20 bit number + // - add high 10 bits to 0xD800 --> first surrogate + // - add low 10 bits to 0xDC00 --> second surrogate codepoint -= 0x10000; result += String.fromCharCode((codepoint >> 10) + 0xD800) + String.fromCharCode((codepoint % 0x400) + 0xDC00); } else { diff --git a/src/common/input/UnicodeV6.test.ts b/src/common/input/UnicodeV6.test.ts index 95c2f6e2f..98dc97dc2 100644 --- a/src/common/input/UnicodeV6.test.ts +++ b/src/common/input/UnicodeV6.test.ts @@ -12,10 +12,8 @@ it('wcwidth should match all values from the old implementation', function(): vo // old implementation const wcwidthOld = (function(opts: {nul: number, control: number}): (ucs: number) => number { - /* - * extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c - * combining characters - */ + // extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c + // combining characters const COMBINING_BMP = [ [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], @@ -151,20 +149,18 @@ it('wcwidth should match all values from the old implementation', function(): vo } return table; } - /* - * get width from lookup table - * position in container : num / CODEPOINTS_PER_ITEM - * ==> n = table[Math.floor(num / 16)] - * ==> n = table[num >> 4] - * 16 codepoints per number: FFEEDDCCBBAA99887766554433221100 - * position in number : (num % CODEPOINTS_PER_ITEM) * BITWIDTH - * ==> m = (n % 16) * 2 - * ==> m = (num & 15) << 1 - * right shift to position m - * ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766 - * we are only interested in 2 LSBs, cut off higher bits - * ==> n = n & 3 e.g. 000000000000000000000000000000XX - */ + // get width from lookup table + // position in container : num / CODEPOINTS_PER_ITEM + // ==> n = table[Math.floor(num / 16)] + // ==> n = table[num >> 4] + // 16 codepoints per number: FFEEDDCCBBAA99887766554433221100 + // position in number : (num % CODEPOINTS_PER_ITEM) * BITWIDTH + // ==> m = (n % 16) * 2 + // ==> m = (num & 15) << 1 + // right shift to position m + // ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766 + // we are only interested in 2 LSBs, cut off higher bits + // ==> n = n & 3 e.g. 000000000000000000000000000000XX return (num: number): number => { num |= 0; // get asm.js like optimization under V8 if (num < 32) { diff --git a/src/common/input/UnicodeV6.ts b/src/common/input/UnicodeV6.ts index 4b6bd55c0..83265f705 100644 --- a/src/common/input/UnicodeV6.ts +++ b/src/common/input/UnicodeV6.ts @@ -94,10 +94,8 @@ export class UnicodeV6 implements IUnicodeVersionProvider { table.fill(0, 1, 32); table.fill(0, 0x7f, 0xa0); - /* - * apply wide char rules first - * wide chars - */ + // apply wide char rules first + // wide chars table.fill(2, 0x1100, 0x1160); table[0x2329] = 2; table[0x232a] = 2; @@ -111,13 +109,11 @@ export class UnicodeV6 implements IUnicodeVersionProvider { table.fill(2, 0xff00, 0xff61); table.fill(2, 0xffe0, 0xffe7); - /* - * apply combining last to ensure we overwrite - * wrongly wide set chars: - * the original algo evals combining first and falls - * through to wide check so we simply do here the opposite - * combining 0 - */ + // apply combining last to ensure we overwrite + // wrongly wide set chars: + // the original algo evals combining first and falls + // through to wide check so we simply do here the opposite + // combining 0 for (let r = 0; r < BMP_COMBINING.length; ++r) { table.fill(0, BMP_COMBINING[r][0], BMP_COMBINING[r][1] + 1); } diff --git a/src/common/input/Win32InputMode.ts b/src/common/input/Win32InputMode.ts index 23f6ade0f..72907836d 100644 --- a/src/common/input/Win32InputMode.ts +++ b/src/common/input/Win32InputMode.ts @@ -198,10 +198,8 @@ export class Win32InputMode { * Returns 0 for non-character keys. */ private _getUnicodeChar(ev: IKeyboardEvent): number { - /* - * Handle special keys that produce control characters - * Ctrl modifies some of these: Ctrl+Enter=LF, Ctrl+Backspace=DEL - */ + // Handle special keys that produce control characters + // Ctrl modifies some of these: Ctrl+Enter=LF, Ctrl+Backspace=DEL if (ev.ctrlKey && !ev.altKey && !ev.metaKey) { if (ev.key === 'Enter') { return 0x0A; // Line feed (Ctrl+Enter) @@ -247,11 +245,9 @@ export class Win32InputMode { state |= Win32ControlKeyState.SHIFT_PRESSED; } - /* - * Note: We can't distinguish left/right for ctrl/alt in standard browser events, - * so we use the generic pressed flags. The right-side flags are used when - * we can detect them (e.g., via code property). - */ + // Note: We can't distinguish left/right for ctrl/alt in standard browser events, + // so we use the generic pressed flags. The right-side flags are used when + // we can detect them (e.g., via code property). if (ev.ctrlKey) { if (ev.code === 'ControlRight') { state |= Win32ControlKeyState.RIGHT_CTRL_PRESSED; diff --git a/src/common/input/WriteBuffer.ts b/src/common/input/WriteBuffer.ts index 888db9ac5..b936c9dbf 100644 --- a/src/common/input/WriteBuffer.ts +++ b/src/common/input/WriteBuffer.ts @@ -90,16 +90,12 @@ export class WriteBuffer extends Disposable { * @deprecated Unreliable, to be removed soon. */ public writeSync(data: string | Uint8Array, maxSubsequentCalls?: number): void { - /* - * stop writeSync recursions with maxSubsequentCalls argument - * This is dangerous to use as it will lose the current data chunk - * and return immediately. - */ + // stop writeSync recursions with maxSubsequentCalls argument + // This is dangerous to use as it will lose the current data chunk + // and return immediately. if (maxSubsequentCalls !== undefined && this._syncCalls > maxSubsequentCalls) { - /* - * comment next line if a whole loop block should only contain x `writeSync` calls - * (total flat vs. deep nested limit) - */ + // comment next line if a whole loop block should only contain x `writeSync` calls + // (total flat vs. deep nested limit) this._syncCalls = 0; return; } @@ -116,22 +112,18 @@ export class WriteBuffer extends Disposable { } this._isSyncWriting = true; - /* - * force sync processing on pending data chunks to avoid in-band data scrambling - * does the same as innerWrite but without event loop - * we have to do it here as single loop steps to not corrupt loop subject - * by another writeSync call triggered from _action - */ + // force sync processing on pending data chunks to avoid in-band data scrambling + // does the same as innerWrite but without event loop + // we have to do it here as single loop steps to not corrupt loop subject + // by another writeSync call triggered from _action let chunk: string | Uint8Array | undefined; while (chunk = this._writeBuffer.shift()) { this._action(chunk); const cb = this._callbacks.shift(); if (cb) cb(); } - /* - * reset to avoid reprocessing of chunks with scheduled innerWrite call - * stopping scheduled innerWrite by offset > length condition - */ + // reset to avoid reprocessing of chunks with scheduled innerWrite call + // stopping scheduled innerWrite by offset > length condition this._pendingData = 0; this._bufferOffset = 0x7FFFFFFF; @@ -149,11 +141,9 @@ export class WriteBuffer extends Disposable { if (!this._writeBuffer.length) { this._bufferOffset = 0; - /* - * If this is the first write call after the user has done some input, - * parse it immediately to minimize input latency, - * otherwise schedule for the next event - */ + // If this is the first write call after the user has done some input, + // parse it immediately to minimize input latency, + // otherwise schedule for the next event if (this._didUserInput) { this._didUserInput = false; this._pendingData += data.length; @@ -244,19 +234,15 @@ export class WriteBuffer extends Disposable { * throughput by eval'ing `startTime` upfront pulling at least one more chunk into the * current microtask queue (executed before setTimeout). */ - /* - * const continuation: (r: boolean) => void = performance.now() - startTime >= - * Constants.WRITE_TIMEOUT_MS - * ? r => setTimeout(() => this._innerWrite(0, r)) - * : r => this._innerWrite(startTime, r); - */ + // const continuation: (r: boolean) => void = performance.now() - startTime >= + // Constants.WRITE_TIMEOUT_MS + // ? r => setTimeout(() => this._innerWrite(0, r)) + // : r => this._innerWrite(startTime, r); - /* - * Handle exceptions synchronously to current band position, idea: - * 1. spawn a single microtask which we allow to throw hard - * 2. spawn a promise immediately resolving to `true` - * (executed on the same queue, thus properly aligned before continuation happens) - */ + // Handle exceptions synchronously to current band position, idea: + // 1. spawn a single microtask which we allow to throw hard + // 2. spawn a promise immediately resolving to `true` + // (executed on the same queue, thus properly aligned before continuation happens) result.catch(err => { queueMicrotask(() => {throw err;}); return Promise.resolve(false); @@ -274,10 +260,8 @@ export class WriteBuffer extends Disposable { } } if (this._writeBuffer.length > this._bufferOffset) { - /* - * Allow renderer to catch up before processing the next batch - * trim already processed chunks if we are above threshold - */ + // Allow renderer to catch up before processing the next batch + // trim already processed chunks if we are above threshold if (this._bufferOffset > Constants.WRITE_BUFFER_LENGTH_THRESHOLD) { this._writeBuffer = this._writeBuffer.slice(this._bufferOffset); this._callbacks = this._callbacks.slice(this._bufferOffset); diff --git a/src/common/input/XParseColor.ts b/src/common/input/XParseColor.ts index 2f4a79dc8..ae4a3bcae 100644 --- a/src/common/input/XParseColor.ts +++ b/src/common/input/XParseColor.ts @@ -50,11 +50,9 @@ export function parseColor(data: string): [number, number, number] | undefined { } } - /* - * Named colors are currently not supported due to the large addition to the xterm.js bundle size - * they would add. In order to support named colors, we would need some way of optionally loading - * additional payloads so startup/download time is not bloated (see #3530). - */ + // Named colors are currently not supported due to the large addition to the xterm.js bundle size + // they would add. In order to support named colors, we would need some way of optionally loading + // additional payloads so startup/download time is not bloated (see #3530). } // pad hex output to requested bit width diff --git a/src/common/parser/ApcParser.ts b/src/common/parser/ApcParser.ts index 453c71110..8f921f8cb 100644 --- a/src/common/parser/ApcParser.ts +++ b/src/common/parser/ApcParser.ts @@ -181,10 +181,8 @@ export class ApcHandler implements IApcHandler { } else if (success) { ret = this._handler(this._data); if (ret instanceof Promise) { - /* - * need to hold data until `ret` got resolved - * dont care for errors, data will be freed anyway on next start - */ + // need to hold data until `ret` got resolved + // dont care for errors, data will be freed anyway on next start return ret.then(res => { this._data = ''; this._hitLimit = false; diff --git a/src/common/parser/DcsParser.ts b/src/common/parser/DcsParser.ts index 849b82709..692200230 100644 --- a/src/common/parser/DcsParser.ts +++ b/src/common/parser/DcsParser.ts @@ -147,12 +147,10 @@ export class DcsHandler implements IDcsHandler { constructor(private _handler: (data: string, params: IParams) => boolean | Promise) { } public hook(params: IParams): void { - /* - * since we need to preserve params until `unhook`, we have to clone it - * (only borrowed from parser and spans multiple parser states) - * perf optimization: - * clone only, if we have non empty params, otherwise stick with default - */ + // since we need to preserve params until `unhook`, we have to clone it + // (only borrowed from parser and spans multiple parser states) + // perf optimization: + // clone only, if we have non empty params, otherwise stick with default this._params = (params.length > 1 || params.params[0]) ? params.clone() : EMPTY_PARAMS; this._data = ''; this._hitLimit = false; @@ -176,10 +174,8 @@ export class DcsHandler implements IDcsHandler { } else if (success) { ret = this._handler(this._data, this._params); if (ret instanceof Promise) { - /* - * need to hold data and params until `ret` got resolved - * dont care for errors, data will be freed anyway on next start - */ + // need to hold data and params until `ret` got resolved + // dont care for errors, data will be freed anyway on next start return ret.then(res => { this._params = EMPTY_PARAMS; this._data = ''; diff --git a/src/common/parser/EscapeSequenceParser.test.ts b/src/common/parser/EscapeSequenceParser.test.ts index 17cc37ee3..2d5a04df7 100644 --- a/src/common/parser/EscapeSequenceParser.test.ts +++ b/src/common/parser/EscapeSequenceParser.test.ts @@ -2065,10 +2065,8 @@ async function throwsAsync(fn: () => Promise, message?: string | undefined) } describe('EscapeSequenceParser - async', () => { - /* - * sequences: SGR 1;31 | hello SP | ESC %G | wor | ESC E | ld! | SGR 0 | EXE \r\n | $> | DCS 1;2 a [xyz] ST | OSC 1;foo=bar ST | APC X abc ST | FIN - * needed handlers: CSI m, PRINT, ESC %G, ESC E, EXE \r, EXE \n, OSC 1, APC X - */ + // sequences: SGR 1;31 | hello SP | ESC %G | wor | ESC E | ld! | SGR 0 | EXE \r\n | $> | DCS 1;2 a [xyz] ST | OSC 1;foo=bar ST | APC X abc ST | FIN + // needed handlers: CSI m, PRINT, ESC %G, ESC E, EXE \r, EXE \n, OSC 1, APC X const INPUT = '\x1b[1;31mhello \x1b%Gwor\x1bEld!\x1b[0m\r\n$>\x1bP1;2axyz\x1b\\\x1b]1;foo=bar\x1b\\\x1b_Xabc\x1b\\FIN'; let RESULT: any[]; let parser: TestEscapeSequenceParser; @@ -2157,10 +2155,8 @@ describe('EscapeSequenceParser - async', () => { it('sync parse call does not work anymore', () => { assert.notEqual(!parseSync(parser, INPUT), true); assert.notDeepEqual(callstack, RESULT); - /* - * due to sync calling we should save exactly one saved stack - * proper continuation is not possible anymore, as we lost the promise resolve value - */ + // due to sync calling we should save exactly one saved stack + // proper continuation is not possible anymore, as we lost the promise resolve value assert.equal(parser.trackedStack.length, 1); }); it('improper continuation should throw', async () => { @@ -2226,10 +2222,8 @@ describe('EscapeSequenceParser - async', () => { // split to single char input for (let i = 0; i < INPUT.length; ++i) { - /* - * Note: a single fully awaited parse call always ends in sync mode, - * which re-enables faster sync processing in the higher up callstack - */ + // Note: a single fully awaited parse call always ends in sync mode, + // which re-enables faster sync processing in the higher up callstack await parseP(parser, INPUT[i]); } assert.deepEqual(callstack, RESULT); diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index be4877766..d944112db 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -15,22 +15,20 @@ import { ApcParser } from 'common/parser/ApcParser'; /** * VT commands done by the parser */ -/* - * @vt: #Y ESC CSI "Control Sequence Introducer" "ESC [" "Start of a CSI sequence." - * @vt: #Y ESC OSC "Operating System Command" "ESC ]" "Start of an OSC sequence." - * @vt: #Y ESC DCS "Device Control String" "ESC P" "Start of a DCS sequence." - * @vt: #Y ESC ST "String Terminator" "ESC \" "Terminator used for string type sequences." - * @vt: #Y ESC PM "Privacy Message" "ESC ^" "Start of a privacy message." - * @vt: #Y ESC APC "Application Program Command" "ESC _" "Start of an APC sequence." - * @vt: #Y C1 CSI "Control Sequence Introducer" "\x9B" "Start of a CSI sequence." - * @vt: #Y C1 OSC "Operating System Command" "\x9D" "Start of an OSC sequence." - * @vt: #Y C1 DCS "Device Control String" "\x90" "Start of a DCS sequence." - * @vt: #Y C1 ST "String Terminator" "\x9C" "Terminator used for string type sequences." - * @vt: #Y C1 PM "Privacy Message" "\x9E" "Start of a privacy message." - * @vt: #Y C1 APC "Application Program Command" "\x9F" "Start of an APC sequence." - * @vt: #Y C0 NUL "Null" "\0, \x00" "NUL is ignored." - * @vt: #Y C0 ESC "Escape" "\e, \x1B" "Start of a sequence. Cancels any other sequence." - */ +// @vt: #Y ESC CSI "Control Sequence Introducer" "ESC [" "Start of a CSI sequence." +// @vt: #Y ESC OSC "Operating System Command" "ESC ]" "Start of an OSC sequence." +// @vt: #Y ESC DCS "Device Control String" "ESC P" "Start of a DCS sequence." +// @vt: #Y ESC ST "String Terminator" "ESC \" "Terminator used for string type sequences." +// @vt: #Y ESC PM "Privacy Message" "ESC ^" "Start of a privacy message." +// @vt: #Y ESC APC "Application Program Command" "ESC _" "Start of an APC sequence." +// @vt: #Y C1 CSI "Control Sequence Introducer" "\x9B" "Start of a CSI sequence." +// @vt: #Y C1 OSC "Operating System Command" "\x9D" "Start of an OSC sequence." +// @vt: #Y C1 DCS "Device Control String" "\x90" "Start of a DCS sequence." +// @vt: #Y C1 ST "String Terminator" "\x9C" "Terminator used for string type sequences." +// @vt: #Y C1 PM "Privacy Message" "\x9E" "Start of a privacy message." +// @vt: #Y C1 APC "Application Program Command" "\x9F" "Start of an APC sequence." +// @vt: #Y C0 NUL "Null" "\0, \x00" "NUL is ignored." +// @vt: #Y C0 ESC "Escape" "\e, \x1B" "Start of a sequence. Cancels any other sequence." /** * Table values are generated like this: @@ -97,10 +95,8 @@ const NON_ASCII_PRINTABLE = 0xA0; * Taken from https://vt100.net/emu/dec_ansi_parser. */ export const VT500_TRANSITION_TABLE = (function (): TransitionTable { - /* - * table size: - * (ParserState.STATE_LENGTH - 1) << TableAccess.INDEX_STATE_SHIFT | NON_ASCII_PRINTABLE + 1 - */ + // table size: + // (ParserState.STATE_LENGTH - 1) << TableAccess.INDEX_STATE_SHIFT | NON_ASCII_PRINTABLE + 1 const table: TransitionTable = new TransitionTable(4257); // range macro for byte @@ -504,11 +500,9 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this._params.resetZdm(); this._collect = 0; this.precedingJoinState = 0; - /* - * abort pending continuation from async handler - * Here the RESET type indicates, that the next parse call will - * ignore any saved stack, instead continues sync with next codepoint from GROUND - */ + // abort pending continuation from async handler + // Here the RESET type indicates, that the next parse call will + // ignore any saved stack, instead continues sync with next codepoint from GROUND if (this._parseStack.state !== ParserStackType.NONE) { this._parseStack.state = ParserStackType.RESET; this._parseStack.handlers = []; // also release handlers ref @@ -585,10 +579,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP // resume from async handler if (this._parseStack.state) { - /* - * allow sync parser reset even in continuation mode - * Note: can be used to recover parser from improper continuation error below - */ + // allow sync parser reset even in continuation mode + // Note: can be used to recover parser from improper continuation error below if (this._parseStack.state === ParserStackType.RESET) { this._parseStack.state = ParserStackType.NONE; start = this._parseStack.chunkPos + 1; // continue with next codepoint in GROUND @@ -613,11 +605,9 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP throw new Error('improper continuation due to previous async handler, giving up parsing'); } - /* - * we have to resume the old handler loop if: - * - return value of the promise was `false` - * - handlers are not exhausted yet - */ + // we have to resume the old handler loop if: + // - return value of the promise was `false` + // - handlers are not exhausted yet const handlers = this._parseStack.handlers; let handlerPos = this._parseStack.handlerPos - 1; switch (this._parseStack.state) { @@ -804,10 +794,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP const handlers = this._csiHandlers[this._collect << 8 | code]; let j = handlers ? handlers.length - 1 : -1; for (; j >= 0; j--) { - /* - * true means success and to stop bubbling - * a promise indicates an async handler that needs to finish before progressing - */ + // true means success and to stop bubbling + // a promise indicates an async handler that needs to finish before progressing handlerResult = handlers[j](this._params); if (handlerResult === true) { break; @@ -845,10 +833,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP const handlersEsc = this._escHandlers[this._collect << 8 | code]; let jj = handlersEsc ? handlersEsc.length - 1 : -1; for (; jj >= 0; jj--) { - /* - * true means success and to stop bubbling - * a promise indicates an async handler that needs to finish before progressing - */ + // true means success and to stop bubbling + // a promise indicates an async handler that needs to finish before progressing handlerResult = handlersEsc[jj](); if (handlerResult === true) { break; @@ -870,10 +856,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this._dcsParser.hook(this._collect << 8 | code, this._params); break; case ParserAction.DCS_PUT: - /* - * inner loop - exit DCS_PUT: 0x18, 0x1a, 0x1b, 0x7f, 0x80 - 0x9f - * unhook triggered by: 0x1b, 0x9c (success) and 0x18, 0x1a (abort) - */ + // inner loop - exit DCS_PUT: 0x18, 0x1a, 0x1b, 0x7f, 0x80 - 0x9f + // unhook triggered by: 0x1b, 0x9c (success) and 0x18, 0x1a (abort) for (let j = i + 1; ; ++j) { if (j >= length || (code = data[j]) === 0x18 || code === 0x1a || code === 0x1b || (code > 0x7f && code < NON_ASCII_PRINTABLE)) { this._dcsParser.put(data, i, j); @@ -921,10 +905,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this._apcParser.start(this._collect << 8 | code); break; case ParserAction.APC_PUT: - /* - * inner loop - exit APC_PUT: 0x18, 0x1a, 0x1b, 0x9c - * allowed: 00/08 .. 00/13, 02/00 .. 07/14 + NON_ASCII_PRINTABLE - */ + // inner loop - exit APC_PUT: 0x18, 0x1a, 0x1b, 0x9c + // allowed: 00/08 .. 00/13, 02/00 .. 07/14 + NON_ASCII_PRINTABLE for (let j = i + 1; ; ++j) { if (j < length && ( (data[j] >= 0x20 && data[j] < 0x7f) || (data[j] >= 0x08 && data[j] < 0x0e) || data[j] >= NON_ASCII_PRINTABLE diff --git a/src/common/parser/OscParser.ts b/src/common/parser/OscParser.ts index d348f483b..1f0c8cb23 100644 --- a/src/common/parser/OscParser.ts +++ b/src/common/parser/OscParser.ts @@ -133,11 +133,9 @@ export class OscParser implements IOscParser { } // do nothing if command was faulty if (this._state !== OscState.ABORT) { - /* - * if we are still in ID state and get an early end - * means that the command has no payload thus we still have - * to announce START and send END right after - */ + // if we are still in ID state and get an early end + // means that the command has no payload thus we still have + // to announce START and send END right after if (this._state === OscState.ID) { this._start(); } @@ -168,11 +166,9 @@ export class OscParser implements IOscParser { } j--; } - /* - * cleanup left over handlers - * we always have to call .end for proper cleanup, - * here we use `success` to indicate whether a handler should execute - */ + // cleanup left over handlers + // we always have to call .end for proper cleanup, + // here we use `success` to indicate whether a handler should execute for (; j >= 0; j--) { handlerResult = this._active[j].end(false); if (handlerResult instanceof Promise) { @@ -226,10 +222,8 @@ export class OscHandler implements IOscHandler { } else if (success) { ret = this._handler(this._data); if (ret instanceof Promise) { - /* - * need to hold data until `ret` got resolved - * dont care for errors, data will be freed anyway on next start - */ + // need to hold data until `ret` got resolved + // dont care for errors, data will be freed anyway on next start return ret.then(res => { this._data = ''; this._hitLimit = false; diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index ecf71eaf8..06ac6b10a 100644 --- a/src/common/services/BufferService.ts +++ b/src/common/services/BufferService.ts @@ -103,28 +103,22 @@ export class BufferService extends Disposable implements IBufferService { buffer.ydisp++; } } else { - /* - * When the buffer is full and the user has scrolled up, keep the text - * stable unless ydisp is right at the top - */ + // When the buffer is full and the user has scrolled up, keep the text + // stable unless ydisp is right at the top if (this.isUserScrolling) { buffer.ydisp = Math.max(buffer.ydisp - 1, 0); } } } else { - /* - * scrollTop is non-zero which means no line will be going to the - * scrollback, instead we can just shift them in-place. - */ + // scrollTop is non-zero which means no line will be going to the + // scrollback, instead we can just shift them in-place. const scrollRegionHeight = bottomRow - topRow + 1 /* as it's zero-based */; buffer.lines.shiftElements(topRow + 1, scrollRegionHeight - 1, -1); buffer.lines.set(bottomRow, newLine.clone()); } - /* - * Move the viewport to the bottom of the buffer unless the user is - * scrolling. - */ + // Move the viewport to the bottom of the buffer unless the user is + // scrolling. if (!this.isUserScrolling) { buffer.ydisp = buffer.ybase; } diff --git a/src/common/services/InstantiationService.ts b/src/common/services/InstantiationService.ts index f11ff7f6e..7e7695485 100644 --- a/src/common/services/InstantiationService.ts +++ b/src/common/services/InstantiationService.ts @@ -4,12 +4,10 @@ * * This was heavily inspired from microsoft/vscode's dependency injection system (MIT). */ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { IInstantiationService, IServiceIdentifier } from 'common/services/Services'; import { getServiceDependencies } from 'common/services/ServiceRegistry'; diff --git a/src/common/services/LogService.ts b/src/common/services/LogService.ts index eea09f47d..8bc1e0aaa 100644 --- a/src/common/services/LogService.ts +++ b/src/common/services/LogService.ts @@ -16,10 +16,8 @@ interface IConsole { warn: LogType; } -/* - * console is available on both node.js and browser contexts but the common - * module doesn't depend on them so we need to explicitly declare it. - */ +// console is available on both node.js and browser contexts but the common +// module doesn't depend on them so we need to explicitly declare it. declare const console: IConsole; const optionsKeyToLogLevel: { [key: string]: LogLevelEnum } = { diff --git a/src/common/services/MouseStateService.ts b/src/common/services/MouseStateService.ts index 6316dbfc9..21420460a 100644 --- a/src/common/services/MouseStateService.ts +++ b/src/common/services/MouseStateService.ts @@ -105,10 +105,8 @@ function eventCode(e: ICoreMouseEvent, isSGR: boolean): number { if (e.action === CoreMouseAction.MOVE) { code |= CoreMouseAction.MOVE; } else if (e.action === CoreMouseAction.UP && !isSGR) { - /* - * special case - only SGR can report button on release - * all others have to go with NONE - */ + // special case - only SGR can report button on release + // all others have to go with NONE code |= CoreMouseButton.NONE; } } @@ -128,12 +126,10 @@ const DEFAULT_ENCODINGS: { [key: string]: CoreMouseEncoding } = { */ DEFAULT: (e: ICoreMouseEvent) => { const params = [eventCode(e, false) + 32, e.col + 32, e.row + 32]; - /* - * supress mouse report if we exceed addressible range - * Note this is handled differently by emulators - * - xterm: sends 0;0 coords instead - * - vte, konsole: no report - */ + // supress mouse report if we exceed addressible range + // Note this is handled differently by emulators + // - xterm: sends 0;0 coords instead + // - vte, konsole: no report if (params[0] > 255 || params[1] > 255 || params[2] > 255) { return ''; } diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 252b38ef9..ec647aaa5 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -90,10 +90,8 @@ export class OptionsService extends Disposable implements IOptionsService { this.options = { ... defaultOptions }; this._setupOptions(); - /* - * Clear out options that could link outside xterm.js as they could easily cause an embedder - * memory leak - */ + // Clear out options that could link outside xterm.js as they could easily cause an embedder + // memory leak this._register(toDisposable(() => { this.rawOptions.linkHandler = null; this.rawOptions.documentOverride = null; diff --git a/src/common/services/ServiceRegistry.ts b/src/common/services/ServiceRegistry.ts index fef07d54c..af51d1641 100644 --- a/src/common/services/ServiceRegistry.ts +++ b/src/common/services/ServiceRegistry.ts @@ -4,12 +4,10 @@ * * This was heavily inspired from microsoft/vscode's dependency injection system (MIT). */ -/* - *--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. - *-------------------------------------------------------------------------------------------- - */ + *--------------------------------------------------------------------------------------------*/ import { IServiceIdentifier } from 'common/services/Services'; diff --git a/src/common/services/UnicodeService.ts b/src/common/services/UnicodeService.ts index a04bf00a1..d0dd8584d 100644 --- a/src/common/services/UnicodeService.ts +++ b/src/common/services/UnicodeService.ts @@ -78,20 +78,16 @@ export class UnicodeService implements IUnicodeService { // surrogate pair first if (0xD800 <= code && code <= 0xDBFF) { if (++i >= length) { - /* - * this should not happen with strings retrieved from - * Buffer.translateToString as it converts from UTF-32 - * and therefore always should contain the second part - * for any other string we still have to handle it somehow: - * simply treat the lonely surrogate first as a single char (UCS-2 behavior) - */ + // this should not happen with strings retrieved from + // Buffer.translateToString as it converts from UTF-32 + // and therefore always should contain the second part + // for any other string we still have to handle it somehow: + // simply treat the lonely surrogate first as a single char (UCS-2 behavior) return result + this.wcwidth(code); } const second = s.charCodeAt(i); - /* - * convert surrogate pair to high codepoint only for valid second part (UTF-16) - * otherwise treat them independently (UCS-2 behavior) - */ + // convert surrogate pair to high codepoint only for valid second part (UTF-16) + // otherwise treat them independently (UCS-2 behavior) if (0xDC00 <= second && second <= 0xDFFF) { code = (code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; } else { diff --git a/src/headless/public/Terminal.ts b/src/headless/public/Terminal.ts index d9d604917..0d4e6e93b 100644 --- a/src/headless/public/Terminal.ts +++ b/src/headless/public/Terminal.ts @@ -58,11 +58,9 @@ export class Terminal extends Disposable implements ITerminalApi { } private _checkReadonlyOptions(propName: string): void { - /* - * Throw an error if any constructor only option is modified - * from terminal.options - * Modifications from anywhere else are allowed - */ + // Throw an error if any constructor only option is modified + // from terminal.options + // Modifications from anywhere else are allowed if (CONSTRUCTOR_ONLY_OPTIONS.includes(propName)) { throw new Error(`Option "${propName}" can only be set in the constructor`); } diff --git a/test/playwright/InputHandler.test.ts b/test/playwright/InputHandler.test.ts index 451346f3a..eec2938a8 100644 --- a/test/playwright/InputHandler.test.ts +++ b/test/playwright/InputHandler.test.ts @@ -227,11 +227,9 @@ test.describe('InputHandler Integration Tests', () => { await ctx.proxy.write('\x1b[2S'); await pollFor(ctx.page, () => getLinesAsArray(5), ['3', '4', '5', '', '']); }); - /* - * This is intentionally not implemented here are image support lives within an addon - * test.skip('CSI ? Pi ; Pa ; Pv S - XTSMGRAPHICS: Set or request graphics attribute, xterm', async () => { - * }); - */ + // This is intentionally not implemented here are image support lives within an addon + // test.skip('CSI ? Pi ; Pa ; Pv S - XTSMGRAPHICS: Set or request graphics attribute, xterm', async () => { + // }); test('CSI Ps T - SD: Scroll down Ps lines (default = 1), VT420', async () => { await ctx.proxy.write('1\r\n2\r\n3\r\n4\r\n5'); await pollFor(ctx.page, () => getLinesAsArray(5), ['1', '2', '3', '4', '5']); @@ -351,10 +349,8 @@ test.describe('InputHandler Integration Tests', () => { await pollFor(ctx.page, () => getLinesAsArray(3), ['aoo', '', ' b']); }); test('CSI Ps g - TBC: Tab Clear (default = 0)', async () => { - /* - * Default: Clear tab stop at cursor position - * Move to column 9 (first tab stop), clear it, go back to column 1, tab should skip to column 17 - */ + // Default: Clear tab stop at cursor position + // Move to column 9 (first tab stop), clear it, go back to column 1, tab should skip to column 17 await ctx.proxy.write('\x1b[9G\x1b[g\x1b[1G\ta'); await pollFor(ctx.page, () => getLinesAsArray(1), [' a']); // Ps=3: Clear all tab stops @@ -527,10 +523,8 @@ test.describe('InputHandler Integration Tests', () => { test('Ps = 6 9 - Enable left and right margin mode (DECLRMM), VT420 and up', async () => { await assertNoModeChange('\x1b[?69h'); }); - /* - * test.skip('Ps = 8 0 - Enable Sixel Display Mode (DECSDM), VT330, VT340, VT382', async () => { - * }); - */ + // test.skip('Ps = 8 0 - Enable Sixel Display Mode (DECSDM), VT330, VT340, VT382', async () => { + // }); test('Ps = 9 5 - Do not clear screen when DECCOLM is set/reset (DECNCSM), VT510 and up', async () => { await assertNoModeChange('\x1b[?95h'); }); @@ -887,11 +881,9 @@ test.describe('InputHandler Integration Tests', () => { test('Ps = 6 9 - Disable left and right margin mode (DECLRMM), VT420 and up.', async () => { await assertNoModeChange('\x1b[?69l'); }); - /* - * This is intentionally not implemented here are image support lives within an addon - * test.skip('Ps = 8 0 - Disable Sixel Display Mode (DECSDM), VT330, VT340, VT382. Turns on "Sixel Scrolling". See the section Sixel Graphics and mode 8 4 5 2 .', async () => { - * }); - */ + // This is intentionally not implemented here are image support lives within an addon + // test.skip('Ps = 8 0 - Disable Sixel Display Mode (DECSDM), VT330, VT340, VT382. Turns on "Sixel Scrolling". See the section Sixel Graphics and mode 8 4 5 2 .', async () => { + // }); test('Ps = 9 5 - Clear screen when DECCOLM is set/reset (DECNCSM), VT510 and up.', async () => { await assertNoModeChange('\x1b[?95l'); }); diff --git a/test/playwright/MouseTracking.test.ts b/test/playwright/MouseTracking.test.ts index fc3a112e5..355bac3ba 100644 --- a/test/playwright/MouseTracking.test.ts +++ b/test/playwright/MouseTracking.test.ts @@ -12,17 +12,13 @@ test.beforeAll(async ({ browser }) => { }); test.afterAll(async () => await ctx.page.close()); -/* - * adjusted to work inside devcontainer - * see https://github.com/xtermjs/xterm.js/issues/2379 - */ +// adjusted to work inside devcontainer +// see https://github.com/xtermjs/xterm.js/issues/2379 const width = 1280; const height = 960; -/* - * adjust terminal row/col size so we can test - * >80 up to 223 and >255 - */ +// adjust terminal row/col size so we can test +// >80 up to 223 and >255 const fontSize = 6; const cols = 260; const rows = 50; @@ -44,11 +40,9 @@ async function getReports(encoding: string): Promise { return reports.map((report: number[]) => parseReport(encoding, report)); } -/* - * translate cell positions into pixel offset - * always adds +2 in each direction so we dont end up in the wrong cell - * due to rounding issues - */ +// translate cell positions into pixel offset +// always adds +2 in each direction so we dont end up in the wrong cell +// due to rounding issues async function cellPos(col: number, row: number): Promise { const coords: any = await ctx.page.evaluate(` (function() { @@ -171,12 +165,10 @@ function parseReport(encoding: string, msg: number[]): { state: any, row: number test.describe('Mouse Tracking Tests', () => { test.beforeAll(async () => { await ctx.page.setViewportSize({ width, height }); - /* - * patch terminal to get the onData calls - * we encode the msg here to an array of codes to not lose bytes - * (transmission strips non utf8 bytes) - * also resize so we can properly test the edge cases - */ + // patch terminal to get the onData calls + // we encode the msg here to an array of codes to not lose bytes + // (transmission strips non utf8 bytes) + // also resize so we can properly test the edge cases await ctx.page.evaluate(` window.term.onData(e => window.calls.push( Array.from(e).map(el => el.charCodeAt(0)) )); window.term.onBinary(e => window.calls.push( Array.from(e).map(el => el.charCodeAt(0)) )); @@ -222,10 +214,8 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 51, row: 11, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - /* - * test at max rows/cols - * capped at 223 (1-based) - */ + // test at max rows/cols + // capped at 223 (1-based) await mouseMove(223 - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -237,28 +227,24 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), []); - /* - * button press/move/release tests - * left button - */ + // button press/move/release tests + // left button await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); await mouseMove(44, 24); await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - /* - * middle button - * bug: default action not cancelled (adds data to getReports from clipboard under X11) - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await mouseDown('middle'); - * await mouseMove(44, 24); - * await mouseUp('middle'); - * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - * right button - * bug: default action not cancelled (popup shown) - */ + // middle button + // bug: default action not cancelled (adds data to getReports from clipboard under X11) + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await mouseDown('middle'); + // await mouseMove(44, 24); + // await mouseUp('middle'); + // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // right button + // bug: default action not cancelled (popup shown) await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -266,20 +252,16 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('right'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'right', modifier: { control: false, shift: false, meta: false } } }]); - /* - * wheel - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await wheelUp(); - * await pollFor(ctx.page, () => getReports(encoding), []); - * await wheelDown(); - * await pollFor(ctx.page, () => getReports(encoding), []); - */ + // wheel + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await wheelUp(); + // await pollFor(ctx.page, () => getReports(encoding), []); + // await wheelDown(); + // await pollFor(ctx.page, () => getReports(encoding), []); - /* - * modifiers - * CTRL - */ + // modifiers + // CTRL await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -302,12 +284,10 @@ test.describe('Mouse Tracking Tests', () => { await ctx.page.keyboard.up('Alt'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - /* - * SHIFT - * note: caught by selection manager - * bug? Why not caught by selection manger on macos? - * bug: no modifier reported - */ + // SHIFT + // note: caught by selection manager + // bug? Why not caught by selection manger on macos? + // bug: no modifier reported await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft @@ -324,10 +304,8 @@ test.describe('Mouse Tracking Tests', () => { ]); } - /* - * all modifiers - * bug: Shift not working - reporting totally wrong coords and modifiers - selection manager again? - */ + // all modifiers + // bug: Shift not working - reporting totally wrong coords and modifiers - selection manager again? await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -373,28 +351,24 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), [{ col: cols, row: rows, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - /* - * button press/move/release tests - * left button - */ + // button press/move/release tests + // left button await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); await mouseMove(44, 24); await mouseUp('left'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - /* - * middle button - * bug: default action not cancelled (adds data to getReports from clipboard under X11) - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await mouseDown('middle'); - * await mouseMove(44, 24); - * await mouseUp('middle'); - * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - * right button - * bug: default action not cancelled (popup shown) - */ + // middle button + // bug: default action not cancelled (adds data to getReports from clipboard under X11) + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await mouseDown('middle'); + // await mouseMove(44, 24); + // await mouseUp('middle'); + // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // right button + // bug: default action not cancelled (popup shown) await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -402,20 +376,16 @@ test.describe('Mouse Tracking Tests', () => { await mouseUp('right'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'right', modifier: { control: false, shift: false, meta: false } } }]); - /* - * wheel - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await wheelUp(); - * await pollFor(ctx.page, () => getReports(encoding), []); - * await wheelDown(); - * await pollFor(ctx.page, () => getReports(encoding), []); - */ + // wheel + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await wheelUp(); + // await pollFor(ctx.page, () => getReports(encoding), []); + // await wheelDown(); + // await pollFor(ctx.page, () => getReports(encoding), []); - /* - * modifiers - * CTRL - */ + // modifiers + // CTRL await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -438,10 +408,8 @@ test.describe('Mouse Tracking Tests', () => { await ctx.page.keyboard.up('Alt'); await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }]); - /* - * SHIFT - * note: caught by selection manager - */ + // SHIFT + // note: caught by selection manager await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft @@ -458,10 +426,8 @@ test.describe('Mouse Tracking Tests', () => { ]); } - /* - * all modifiers - * bug: Shift not working - reporting totally wrong coords and modifiers - selection manager again? - */ + // all modifiers + // bug: Shift not working - reporting totally wrong coords and modifiers - selection manager again? await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -517,10 +483,8 @@ test.describe('Mouse Tracking Tests', () => { { col: 51, row: 11, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * test at max rows/cols - * capped at 223 (1-based) - */ + // test at max rows/cols + // capped at 223 (1-based) await mouseMove(223 - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -529,10 +493,8 @@ test.describe('Mouse Tracking Tests', () => { { col: 223, row: rows, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * button press/move/release tests - * left button - */ + // button press/move/release tests + // left button await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); @@ -542,18 +504,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * middle button - * bug: default action not cancelled (adds data to getReports from clipboard under X11) - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await mouseDown('middle'); - * await mouseMove(44, 24); - * await mouseUp('middle'); - * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - * right button - * bug: default action not cancelled (popup shown) - */ + // middle button + // bug: default action not cancelled (adds data to getReports from clipboard under X11) + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await mouseDown('middle'); + // await mouseMove(44, 24); + // await mouseUp('middle'); + // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // right button + // bug: default action not cancelled (popup shown) await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -564,20 +524,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * wheel - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await wheelUp(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - * await wheelDown(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - */ + // wheel + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await wheelUp(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + // await wheelDown(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - /* - * modifiers - * CTRL - */ + // modifiers + // CTRL await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -607,35 +563,31 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - /* - * SHIFT - * note: press/release caught by selection manager - * bug: modifier not reported for passed events - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft - * await mouseDown('left'); - * await mouseMove(44, 24); - * await mouseUp('left'); - * // await wheelDown(); - * await ctx.page.keyboard.up('Shift'); - * // if (noShift) { - * // await pollFor(ctx.page, () => getReports(encoding), [ - * // // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * // ]); - * // } else { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, - * // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * // } - */ + // SHIFT + // note: press/release caught by selection manager + // bug: modifier not reported for passed events + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft + // await mouseDown('left'); + // await mouseMove(44, 24); + // await mouseUp('left'); + // // await wheelDown(); + // await ctx.page.keyboard.up('Shift'); + // // if (noShift) { + // // await pollFor(ctx.page, () => getReports(encoding), [ + // // // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // // ]); + // // } else { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, + // // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // // } - /* - * all modifiers - * bug: Shift not working - selection manager? - */ + // all modifiers + // bug: Shift not working - selection manager? await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -695,10 +647,8 @@ test.describe('Mouse Tracking Tests', () => { { col: cols, row: rows, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * button press/move/release tests - * left button - */ + // button press/move/release tests + // left button await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); @@ -708,18 +658,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * middle button - * bug: default action not cancelled (adds data to getReports from clipboard under X11) - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await mouseDown('middle'); - * await mouseMove(44, 24); - * await mouseUp('middle'); - * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - * right button - * bug: default action not cancelled (popup shown) - */ + // middle button + // bug: default action not cancelled (adds data to getReports from clipboard under X11) + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await mouseDown('middle'); + // await mouseMove(44, 24); + // await mouseUp('middle'); + // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // right button + // bug: default action not cancelled (popup shown) await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -730,20 +678,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: 'right', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * wheel - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await wheelUp(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - * await wheelDown(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - */ + // wheel + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await wheelUp(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + // await wheelDown(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - /* - * modifiers - * CTRL - */ + // modifiers + // CTRL await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -773,34 +717,30 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - /* - * SHIFT - * note: press/release caught by selection manager - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft - * await mouseDown('left'); - * await mouseMove(44, 24); - * await mouseUp('left'); - * await wheelDown(); - * await ctx.page.keyboard.up('Shift'); - * if (noShift) { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } else { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } - */ + // SHIFT + // note: press/release caught by selection manager + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft + // await mouseDown('left'); + // await mouseMove(44, 24); + // await mouseUp('left'); + // await wheelDown(); + // await ctx.page.keyboard.up('Shift'); + // if (noShift) { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } else { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } - /* - * all modifiers - * bug: Shift not working - selection manager? - */ + // all modifiers + // bug: Shift not working - selection manager? await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -860,10 +800,8 @@ test.describe('Mouse Tracking Tests', () => { { col: 51, row: 11, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * test at max rows/cols - * capped at 223 (1-based) - */ + // test at max rows/cols + // capped at 223 (1-based) await mouseMove(223 - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -872,10 +810,8 @@ test.describe('Mouse Tracking Tests', () => { { col: 223, row: rows, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * button press/move/release tests - * left button - */ + // button press/move/release tests + // left button await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); @@ -886,18 +822,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * middle button - * bug: default action not cancelled (adds data to getReports from clipboard under X11) - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await mouseDown('middle'); - * await mouseMove(44, 24); - * await mouseUp('middle'); - * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - * right button - * bug: default action not cancelled (popup shown) - */ + // middle button + // bug: default action not cancelled (adds data to getReports from clipboard under X11) + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await mouseDown('middle'); + // await mouseMove(44, 24); + // await mouseUp('middle'); + // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // right button + // bug: default action not cancelled (popup shown) await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -909,20 +843,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * wheel - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await wheelUp(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - * await wheelDown(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - */ + // wheel + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await wheelUp(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + // await wheelDown(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - /* - * modifiers - * CTRL - */ + // modifiers + // CTRL await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -954,35 +884,31 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - /* - * SHIFT - * note: press/release/drag caught by selection manager - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft - * await mouseDown('left'); - * await mouseMove(44, 24); - * await mouseUp('left'); - * await wheelDown(); - * await ctx.page.keyboard.up('Shift'); - * if (noShift) { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } else { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } - */ + // SHIFT + // note: press/release/drag caught by selection manager + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft + // await mouseDown('left'); + // await mouseMove(44, 24); + // await mouseUp('left'); + // await wheelDown(); + // await ctx.page.keyboard.up('Shift'); + // if (noShift) { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } else { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } - /* - * all modifiers - * bug: Shift not working - */ + // all modifiers + // bug: Shift not working await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -1012,10 +938,8 @@ test.describe('Mouse Tracking Tests', () => { await mouseMove(0, 0); await ctx.proxy.write('\x1b[?1002h\x1b[?1006h'); - /* - * test at 0,0 - * bug: release is fired immediately - */ + // test at 0,0 + // bug: release is fired immediately await mouseDown('left'); await pollFor(ctx.page, () => getReports(encoding), [ { col: 1, row: 1, state: { action: 'press', button: 'left', modifier: { control: false, shift: false, meta: false } } } @@ -1046,10 +970,8 @@ test.describe('Mouse Tracking Tests', () => { { col: cols, row: rows, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * button press/move/release tests - * left button - */ + // button press/move/release tests + // left button await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('left'); @@ -1060,18 +982,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * middle button - * bug: default action not cancelled (adds data to getReports from clipboard under X11) - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await mouseDown('middle'); - * await mouseMove(44, 24); - * await mouseUp('middle'); - * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - * right button - * bug: default action not cancelled (popup shown) - */ + // middle button + // bug: default action not cancelled (adds data to getReports from clipboard under X11) + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await mouseDown('middle'); + // await mouseMove(44, 24); + // await mouseUp('middle'); + // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // right button + // bug: default action not cancelled (popup shown) await mouseMove(43, 24); await getReports(encoding); // clear reports await mouseDown('right'); @@ -1083,20 +1003,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: 'right', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * wheel - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await wheelUp(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - * await wheelDown(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - */ + // wheel + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await wheelUp(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + // await wheelDown(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - /* - * modifiers - * CTRL - */ + // modifiers + // CTRL await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -1128,35 +1044,31 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - /* - * SHIFT - * note: press/release/drag caught by selection manager - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft - * await mouseDown('left'); - * await mouseMove(44, 24); - * await mouseUp('left'); - * await wheelDown(); - * await ctx.page.keyboard.up('Shift'); - * if (noShift) { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } else { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } - */ + // SHIFT + // note: press/release/drag caught by selection manager + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await ctx.page.keyboard.down('Shift'); // defaults to ShiftLeft + // await mouseDown('left'); + // await mouseMove(44, 24); + // await mouseUp('left'); + // await wheelDown(); + // await ctx.page.keyboard.up('Shift'); + // if (noShift) { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } else { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } - /* - * all modifiers - * bug: this is totally broken with wrong coords and messed up modifiers - */ + // all modifiers + // bug: this is totally broken with wrong coords and messed up modifiers await mouseMove(43, 24); await getReports(encoding); // clear reports await ctx.page.keyboard.down('Control'); @@ -1216,10 +1128,8 @@ test.describe('Mouse Tracking Tests', () => { { col: 51, row: 11, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * test at max rows/cols - * capped at 223 (1-based) - */ + // test at max rows/cols + // capped at 223 (1-based) await mouseMove(223 - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -1229,10 +1139,8 @@ test.describe('Mouse Tracking Tests', () => { { col: 223, row: rows, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * button press/move/release tests - * left button - */ + // button press/move/release tests + // left button await mouseMove(43, 24); await mouseDown('left'); await mouseMove(44, 24); @@ -1243,18 +1151,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * middle button - * bug: default action not cancelled (adds data to getReports from clipboard under X11) - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await mouseDown('middle'); - * await mouseMove(44, 24); - * await mouseUp('middle'); - * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - * right button - * bug: default action not cancelled (popup shown) - */ + // middle button + // bug: default action not cancelled (adds data to getReports from clipboard under X11) + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await mouseDown('middle'); + // await mouseMove(44, 24); + // await mouseUp('middle'); + // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // right button + // bug: default action not cancelled (popup shown) await mouseMove(43, 24); await mouseDown('right'); await mouseMove(44, 24); @@ -1266,20 +1172,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * wheel - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await wheelUp(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - * await wheelDown(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - */ + // wheel + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await wheelUp(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + // await wheelDown(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - /* - * modifiers - * CTRL - */ + // modifiers + // CTRL await ctx.page.keyboard.down('Control'); await mouseMove(43, 24); await mouseDown('left'); @@ -1311,36 +1213,32 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - /* - * SHIFT - * note: press/release/drag caught by selection manager - * await ctx.page.keyboard.down('Shift'); - * await mouseMove(43, 24); - * await mouseDown('left'); - * await mouseMove(44, 24); - * await mouseUp('left'); - * await wheelDown(); - * await ctx.page.keyboard.up('Shift'); - * if (noShift) { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } else { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, - * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } - */ + // SHIFT + // note: press/release/drag caught by selection manager + // await ctx.page.keyboard.down('Shift'); + // await mouseMove(43, 24); + // await mouseDown('left'); + // await mouseMove(44, 24); + // await mouseUp('left'); + // await wheelDown(); + // await ctx.page.keyboard.up('Shift'); + // if (noShift) { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } else { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, + // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'release', button: '', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } - /* - * all modifiers - * bug: Shift not working - */ + // all modifiers + // bug: Shift not working await ctx.page.keyboard.down('Control'); await ctx.page.keyboard.down('Alt'); // await ctx.page.keyboard.down('Shift'); @@ -1394,11 +1292,9 @@ test.describe('Mouse Tracking Tests', () => { { col: 51, row: 11, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * test at max rows/cols - * bug: we are capped at col 95 currently - * fix: allow values up to 223, any bigger should drop to 0 - */ + // test at max rows/cols + // bug: we are capped at col 95 currently + // fix: allow values up to 223, any bigger should drop to 0 await mouseMove(cols - 1, rows - 1); await mouseDown('left'); await mouseUp('left'); @@ -1408,10 +1304,8 @@ test.describe('Mouse Tracking Tests', () => { { col: cols, row: rows, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * button press/move/release tests - * left button - */ + // button press/move/release tests + // left button await mouseMove(43, 24); await mouseDown('left'); await mouseMove(44, 24); @@ -1422,18 +1316,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: false, meta: false } } }, { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * middle button - * bug: default action not cancelled (adds data to getReports from clipboard under X11) - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await mouseDown('middle'); - * await mouseMove(44, 24); - * await mouseUp('middle'); - * await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); - * right button - * bug: default action not cancelled (popup shown) - */ + // middle button + // bug: default action not cancelled (adds data to getReports from clipboard under X11) + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await mouseDown('middle'); + // await mouseMove(44, 24); + // await mouseUp('middle'); + // await pollFor(ctx.page, () => getReports(encoding), [{col: 44, row: 25, state: {action: 'press', button: 'middle', modifier: {control: false, shift: false, meta: false}}}]); + // right button + // bug: default action not cancelled (popup shown) await mouseMove(43, 24); await mouseDown('right'); await mouseMove(44, 24); @@ -1445,20 +1337,16 @@ test.describe('Mouse Tracking Tests', () => { { col: 45, row: 25, state: { action: 'release', button: 'right', modifier: { control: false, shift: false, meta: false } } } ]); - /* - * wheel - * await mouseMove(43, 24); - * await getReports(encoding); // clear reports - * await wheelUp(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - * await wheelDown(); - * await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - */ + // wheel + // await mouseMove(43, 24); + // await getReports(encoding); // clear reports + // await wheelUp(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'up', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); + // await wheelDown(); + // await pollFor(ctx.page, () => getReports(encoding), [{ col: 44, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: false } } }]); - /* - * modifiers - * CTRL - */ + // modifiers + // CTRL await ctx.page.keyboard.down('Control'); await mouseMove(43, 24); await mouseDown('left'); @@ -1490,36 +1378,32 @@ test.describe('Mouse Tracking Tests', () => { // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: false, meta: true } } } ]); - /* - * SHIFT - * note: press/release/drag caught by selection manager - * await ctx.page.keyboard.down('Shift'); - * await mouseMove(43, 24); - * await mouseDown('left'); - * await mouseMove(44, 24); - * await mouseUp('left'); - * await wheelDown(); - * await ctx.page.keyboard.up('Shift'); - * if (noShift) { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } else { - * await pollFor(ctx.page, () => getReports(encoding), [ - * { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, - * { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, - * { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } - * ]); - * } - */ + // SHIFT + // note: press/release/drag caught by selection manager + // await ctx.page.keyboard.down('Shift'); + // await mouseMove(43, 24); + // await mouseDown('left'); + // await mouseMove(44, 24); + // await mouseUp('left'); + // await wheelDown(); + // await ctx.page.keyboard.up('Shift'); + // if (noShift) { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } else { + // await pollFor(ctx.page, () => getReports(encoding), [ + // { col: 44, row: 25, state: { action: 'move', button: '', modifier: { control: false, shift: true, meta: false } } }, + // { col: 44, row: 25, state: { action: 'press', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'move', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'release', button: 'left', modifier: { control: false, shift: true, meta: false } } }, + // { col: 45, row: 25, state: { action: 'down', button: 'wheel', modifier: { control: false, shift: true, meta: false } } } + // ]); + // } - /* - * all modifiers - * bug: Shift not working - */ + // all modifiers + // bug: Shift not working await ctx.page.keyboard.down('Control'); await ctx.page.keyboard.down('Alt'); // await ctx.page.keyboard.down('Shift'); diff --git a/test/playwright/SharedRendererTests.ts b/test/playwright/SharedRendererTests.ts index 75b330abd..db5c4c073 100644 --- a/test/playwright/SharedRendererTests.ts +++ b/test/playwright/SharedRendererTests.ts @@ -378,10 +378,8 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void 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 - */ + // It's difficult to assert the exact color due to rounding, just ensure the color differs + // to the regular color await pollFor(ctx.value.page, async () => { const c = await getCellColor(ctx.value, x + 1, y + 1); return ( @@ -785,11 +783,9 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 2), [0xad, 0x7f, 0xa8, 255]); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 2), [0x34, 0xe2, 0xe2, 255]); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 2), [0xee, 0xee, 0xec, 255]); - /* - * Setting and check for minimum contrast values, note that these are not - * exact to the contrast ratio, if the increase luminance algorithm - * changes then these will probably fail - */ + // Setting and check for minimum contrast values, note that these are not + // exact to the contrast ratio, if the increase luminance algorithm + // changes then these will probably fail await ctx.value.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); frameDetails = undefined; await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [176, 180, 180, 255]); @@ -856,11 +852,9 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 2), [0xad, 0x7f, 0xa8, 255]); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 2), [0x34, 0xe2, 0xe2, 255]); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 2), [0xee, 0xee, 0xec, 255]); - /* - * Setting and check for minimum contrast values, note that these are not - * exact to the contrast ratio, if the increase luminance algorithm - * changes then these will probably fail - */ + // Setting and check for minimum contrast values, note that these are not + // exact to the contrast ratio, if the increase luminance algorithm + // changes then these will probably fail await ctx.value.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); frameDetails = undefined; await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [46, 52, 54, 255]); @@ -941,11 +935,9 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(6, 2), [Math.floor((255 + 0xad) / 2), Math.floor((255 + 0x7f) / 2), Math.floor((255 + 0xa8) / 2), 255]); await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(7, 2), [Math.floor((255 + 0x34) / 2), Math.floor((255 + 0xe2) / 2), Math.floor((255 + 0xe2) / 2), 255]); await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(8, 2), [Math.floor((255 + 0xee) / 2), Math.floor((255 + 0xee) / 2), Math.floor((255 + 0xec) / 2), 255]); - /* - * Setting and check for minimum contrast values, note that these are not - * exact to the contrast ratio, if the increase luminance algorithm - * changes then these will probably fail - */ + // Setting and check for minimum contrast values, note that these are not + // exact to the contrast ratio, if the increase luminance algorithm + // changes then these will probably fail await waitForRenderAfter(ctx.value, () => ctx.value.page.evaluate(`window.term.options.minimumContrastRatio = 10;`)); await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(1, 1), [150, 153, 154, 255]); await pollForApproximate(ctx.value.page, marginOfError, () => getFgColor(2, 1), [229, 127, 127, 255]); diff --git a/test/playwright/Terminal.test.ts b/test/playwright/Terminal.test.ts index 387197c6d..2d1133cce 100644 --- a/test/playwright/Terminal.test.ts +++ b/test/playwright/Terminal.test.ts @@ -837,10 +837,8 @@ test.describe('API Integration Tests', () => { test.describe('registerLinkProvider', () => { test('should fire provideLinks when hovering cells', async () => { await openTerminal(ctx); - /* - * Focus the terminal as the cursor will show and trigger a rerender, which can clear the - * active link - */ + // Focus the terminal as the cursor will show and trigger a rerender, which can clear the + // active link await ctx.proxy.focus(); await ctx.page.evaluate(` window.calls = []; @@ -861,10 +859,8 @@ test.describe('API Integration Tests', () => { test('should fire hover and leave events on the link', async () => { await openTerminal(ctx); - /* - * Focus the terminal as the cursor will show and trigger a rerender, which can clear the - * active link - */ + // Focus the terminal as the cursor will show and trigger a rerender, which can clear the + // active link await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('foo bar baz'); // Wait for renderer to catch up as links are cleared on render @@ -901,10 +897,8 @@ test.describe('API Integration Tests', () => { test('should work fine when hover and leave callbacks are not provided', async () => { await openTerminal(ctx); - /* - * Focus the terminal as the cursor will show and trigger a rerender, which can clear the - * active link - */ + // Focus the terminal as the cursor will show and trigger a rerender, which can clear the + // active link await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('foo bar baz'); // Wait for renderer to catch up as links are cleared on render @@ -946,10 +940,8 @@ test.describe('API Integration Tests', () => { test('should fire activate events when clicking the link', async () => { await openTerminal(ctx); - /* - * Focus the terminal as the cursor will show and trigger a rerender, which can clear the - * active link - */ + // Focus the terminal as the cursor will show and trigger a rerender, which can clear the + // active link await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('a b c'); // Wait for renderer to catch up as links are cleared on render @@ -990,10 +982,8 @@ test.describe('API Integration Tests', () => { test('should work when multiple links are provided on the same line', async () => { await openTerminal(ctx); - /* - * Focus the terminal as the cursor will show and trigger a rerender, which can clear the - * active link - */ + // Focus the terminal as the cursor will show and trigger a rerender, which can clear the + // active link await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('foo bar baz'); // Wait for renderer to catch up as links are cleared on render @@ -1041,10 +1031,8 @@ test.describe('API Integration Tests', () => { test('should dispose links when hovering away', async () => { await openTerminal(ctx); - /* - * Focus the terminal as the cursor will show and trigger a rerender, which can clear the - * active link - */ + // Focus the terminal as the cursor will show and trigger a rerender, which can clear the + // active link await ctx.page.evaluate('window.term.focus()'); await ctx.proxy.write('foo bar baz'); // Wait for renderer to catch up as links are cleared on render diff --git a/test/playwright/TestUtils.ts b/test/playwright/TestUtils.ts index 76a5eb200..8a76c8820 100644 --- a/test/playwright/TestUtils.ts +++ b/test/playwright/TestUtils.ts @@ -459,10 +459,8 @@ export async function openTerminal( } catch {} } `); - /* - * HACK: Tests may have side effects that could cause the terminal not to be removed. This - * assertion catches this case early. - */ + // HACK: Tests may have side effects that could cause the terminal not to be removed. This + // assertion catches this case early. strictEqual(await ctx.page.evaluate(`document.querySelector('#terminal-container').children.length`), 0, 'there must be no terminals on the page'); let script = ` @@ -503,10 +501,8 @@ export async function openTerminal( await ctx.page.evaluate(script); - /* - * HACK: This is a soft layer breaker that's temporarily included until unicode graphemes have - * more complete integration tests. See https://github.com/xtermjs/xterm.js/pull/4519#discussion_r1285234453 - */ + // HACK: This is a soft layer breaker that's temporarily included until unicode graphemes have + // more complete integration tests. See https://github.com/xtermjs/xterm.js/pull/4519#discussion_r1285234453 if (testOptions.loadUnicodeGraphemesAddon) { await ctx.page.evaluate(` window.unicode = new UnicodeGraphemesAddon(); From 0e823004e6199eb415709672204c3dbdd07e1db6 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 03:59:42 +0000 Subject: [PATCH 028/153] Use single-line JSDoc comments per handler in Types.ts Document multi-variant escape handlers with separate /** ... */ lines instead of multiline blocks. Co-authored-by: Daniel Imms --- src/common/Types.ts | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/src/common/Types.ts b/src/common/Types.ts index 1981a910f..744446af2 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -528,10 +528,9 @@ export interface IInputHandler { /** CSI ' } */ insertColumns(params: IParams): boolean; /** CSI ' ~ */ deleteColumns(params: IParams): boolean; - /** - * OSC 0 - * OSC 2 - */ setTitle(data: string): boolean; + /** OSC 0 */ + /** OSC 2 */ + setTitle(data: string): boolean; /** OSC 4 */ setOrReportIndexedColor(data: string): boolean; /** OSC 10 */ setOrReportFgColor(data: string): boolean; /** OSC 11 */ setOrReportBgColor(data: string): boolean; @@ -544,30 +543,27 @@ export interface IInputHandler { /** ESC E */ nextLine(): boolean; /** ESC = */ keypadApplicationMode(): boolean; /** ESC > */ keypadNumericMode(): boolean; - /** - * ESC % G - * ESC % @ - */ selectDefaultCharset(): boolean; - /** - * ESC ( C - * ESC ) C - * ESC * C - * ESC + C - * ESC - C - * ESC . C - * ESC / C - */ selectCharset(collectAndFlag: string): boolean; + /** ESC % G */ + /** ESC % @ */ + selectDefaultCharset(): boolean; + /** ESC ( C */ + /** ESC ) C */ + /** ESC * C */ + /** ESC + C */ + /** ESC - C */ + /** ESC . C */ + /** ESC / C */ + selectCharset(collectAndFlag: string): boolean; /** ESC D */ index(): boolean; /** ESC H */ tabSet(): boolean; /** ESC M */ reverseIndex(): boolean; /** ESC c */ fullReset(): boolean; - /** - * ESC n - * ESC o - * ESC | - * ESC } - * ESC ~ - */ setgLevel(level: number): boolean; + /** ESC n */ + /** ESC o */ + /** ESC | */ + /** ESC } */ + /** ESC ~ */ + setgLevel(level: number): boolean; /** ESC # 8 */ screenAlignmentPattern(): boolean; } From 88cef9704935cae508307215fd6ba6141925df84 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 04:01:00 +0000 Subject: [PATCH 029/153] Attach method signatures to last handler JSDoc line in Types.ts Co-authored-by: Daniel Imms --- src/common/Types.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/common/Types.ts b/src/common/Types.ts index 744446af2..07aebbe8f 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -529,8 +529,7 @@ export interface IInputHandler { /** CSI ' ~ */ deleteColumns(params: IParams): boolean; /** OSC 0 */ - /** OSC 2 */ - setTitle(data: string): boolean; + /** OSC 2 */ setTitle(data: string): boolean; /** OSC 4 */ setOrReportIndexedColor(data: string): boolean; /** OSC 10 */ setOrReportFgColor(data: string): boolean; /** OSC 11 */ setOrReportBgColor(data: string): boolean; @@ -544,16 +543,14 @@ export interface IInputHandler { /** ESC = */ keypadApplicationMode(): boolean; /** ESC > */ keypadNumericMode(): boolean; /** ESC % G */ - /** ESC % @ */ - selectDefaultCharset(): boolean; + /** ESC % @ */ selectDefaultCharset(): boolean; /** ESC ( C */ /** ESC ) C */ /** ESC * C */ /** ESC + C */ /** ESC - C */ /** ESC . C */ - /** ESC / C */ - selectCharset(collectAndFlag: string): boolean; + /** ESC / C */ selectCharset(collectAndFlag: string): boolean; /** ESC D */ index(): boolean; /** ESC H */ tabSet(): boolean; /** ESC M */ reverseIndex(): boolean; @@ -562,8 +559,7 @@ export interface IInputHandler { /** ESC o */ /** ESC | */ /** ESC } */ - /** ESC ~ */ - setgLevel(level: number): boolean; + /** ESC ~ */ setgLevel(level: number): boolean; /** ESC # 8 */ screenAlignmentPattern(): boolean; } From c62e6dd765a9afd2214a1267006345aadee5a719 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 26 May 2026 21:39:27 -0700 Subject: [PATCH 030/153] Register BufferNamespaceApi onBufferActivate listener with disposables (#5906) Wrap the onBufferActivate subscription in this._register so it is disposed when BufferNamespaceApi is disposed, matching the pattern used elsewhere in the codebase. Co-authored-by: Cursor Agent Co-authored-by: Daniel Imms --- src/common/public/BufferNamespaceApi.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/public/BufferNamespaceApi.ts b/src/common/public/BufferNamespaceApi.ts index 425b1798c..b9744f943 100644 --- a/src/common/public/BufferNamespaceApi.ts +++ b/src/common/public/BufferNamespaceApi.ts @@ -20,7 +20,7 @@ export class BufferNamespaceApi extends Disposable implements IBufferNamespaceAp super(); this._normal = new BufferApiView(this._core.buffers.normal, 'normal'); this._alternate = new BufferApiView(this._core.buffers.alt, 'alternate'); - this._core.buffers.onBufferActivate(() => this._onBufferChange.fire(this.active)); + this._register(this._core.buffers.onBufferActivate(() => this._onBufferChange.fire(this.active))); } public get active(): IBufferApi { if (this._core.buffers.active === this._core.buffers.normal) { return this.normal; } From c5ab83d6978c7d5fd046b6ea1bf924f421a2bf34 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:33:03 +0000 Subject: [PATCH 031/153] Remove stale FIXME comments from charAttributes JSDoc Fixes #5913. Blinking is supported in renderers via TextBlinkStateManager, and bright background colors (100-107) are handled in the SGR loop. Co-authored-by: Daniel Imms --- src/common/InputHandler.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 2b7142dd0..33e954811 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -2590,10 +2590,6 @@ export class InputHandler extends Disposable implements IInputHandler { * | 3 | CMY color. | #N | * | 4 | CMYK color. | #N | * | 5 | Indexed (256 colors) as `Ps ; 5 ; INDEX` or `Ps : 5 : INDEX`. | #Y | - * - * - * FIXME: blinking is implemented in attrs, but not working in renderers? - * FIXME: remove dead branch for p=100 */ public charAttributes(params: IParams): boolean { // Optimize a single SGR0. From d565a2750e4511db0cfd5882460513f2f61b3d3c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:33:20 +0000 Subject: [PATCH 032/153] Fix SelectionService trim listener leak with MutableDisposable Register _trimListener via _register(MutableDisposable) so it is disposed when SelectionService is torn down. Use .value assignment on buffer activate instead of manual dispose/swap. Fixes #5914 Co-authored-by: Daniel Imms --- src/browser/services/SelectionService.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index 39692a871..356cd77fe 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -9,7 +9,7 @@ import { moveToCellSequence } from 'browser/input/MoveToCell'; import { SelectionModel } from 'browser/selection/SelectionModel'; import { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types'; import { ICoreBrowserService, IMouseCoordsService, IRenderService, ISelectionService } from 'browser/services/Services'; -import { Disposable, toDisposable } from 'common/Lifecycle'; +import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; import * as Browser from 'common/Platform'; import { IBufferLine, ICellData, IDisposable } from 'common/Types'; import { getRangeLength } from 'common/buffer/BufferRange'; @@ -102,7 +102,7 @@ export class SelectionService extends Disposable implements ISelectionService { private _mouseMoveListener: EventListener; private _mouseUpListener: EventListener; - private _trimListener: IDisposable; + private readonly _trimListener = this._register(new MutableDisposable()); private _workCell: CellData = new CellData(); private _mouseDownTimeStamp: number = 0; @@ -140,7 +140,7 @@ export class SelectionService extends Disposable implements ISelectionService { this.clearSelection(); } }); - this._trimListener = this._bufferService.buffer.lines.onTrim(amount => this._handleTrim(amount)); + this._trimListener.value = this._bufferService.buffer.lines.onTrim(amount => this._handleTrim(amount)); this._register(this._bufferService.buffers.onBufferActivate(e => this._handleBufferActivate(e))); this.enable(); @@ -769,8 +769,7 @@ export class SelectionService extends Disposable implements ISelectionService { // reverseIndex) and delete in a splice is only ever used when the same // number of elements was just added. Given this is could actually be // beneficial to leave the selection as is for these cases. - this._trimListener.dispose(); - this._trimListener = e.activeBuffer.lines.onTrim(amount => this._handleTrim(amount)); + this._trimListener.value = e.activeBuffer.lines.onTrim(amount => this._handleTrim(amount)); } /** From adc9dea083d11c7738401f5c33314697d1a55e3c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:33:28 +0000 Subject: [PATCH 033/153] Fix TextureAtlas remove-emitter leak on dispose Dispose _onRemoveTextureAtlasCanvas in TextureAtlas.dispose() alongside _onAddTextureAtlasCanvas to avoid leaking listeners when the atlas is torn down. Fixes #5917 Co-authored-by: Daniel Imms --- addons/addon-webgl/src/TextureAtlas.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index 34188235c..a2857e0a4 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -109,6 +109,7 @@ export class TextureAtlas implements ITextureAtlas { page.canvas.remove(); } this._onAddTextureAtlasCanvas.dispose(); + this._onRemoveTextureAtlasCanvas.dispose(); } public warmUp(): void { From 7b6c814566754993d0259976da93e0bc17ce79e0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:33:48 +0000 Subject: [PATCH 034/153] Log errors from onWillOpen handlers instead of swallowing them Fixes #5920 Co-authored-by: Daniel Imms --- src/browser/CoreBrowserTerminal.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index 52eb00025..e44b1717e 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -563,8 +563,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { try { this._onWillOpen.fire(this.element); + } catch (e) { + this._logService.error('onWillOpen handler threw an exception', e); } - catch { /* fails to load addon for some reason */ } if (!this._renderService.hasRenderer()) { this._renderService.setRenderer(this._createRenderer()); } From 8f2bb32c9cbc04a1e753f038384d846a9fa7da0e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:34:00 +0000 Subject: [PATCH 035/153] fix(headless): clear markers when clear() is called Headless clear() now calls buffer.clearAllMarkers() to match the browser implementation, preventing markers from surviving clear() and pointing at wrong lines. Fixes #5910 Co-authored-by: Daniel Imms --- src/headless/Terminal.ts | 1 + src/headless/public/Terminal.test.ts | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/headless/Terminal.ts b/src/headless/Terminal.ts index cc8b152b2..2df76c61e 100644 --- a/src/headless/Terminal.ts +++ b/src/headless/Terminal.ts @@ -108,6 +108,7 @@ export class Terminal extends CoreTerminal { // Don't clear if it's already clear return; } + this.buffer.clearAllMarkers(); this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)!); this.buffer.lines.length = 1; this.buffer.ydisp = 0; diff --git a/src/headless/public/Terminal.test.ts b/src/headless/public/Terminal.test.ts index 0b35fb0e7..0c4739744 100644 --- a/src/headless/public/Terminal.test.ts +++ b/src/headless/public/Terminal.test.ts @@ -114,6 +114,29 @@ describe('Headless API Tests', function (): void { } }); + it('clear disposes markers', async () => { + term = new Terminal({ rows: 5, allowProposedApi: true }); + for (let i = 0; i < 10; i++) { + await writeSync('\n\rtest' + i); + } + const markers = [ + term.registerMarker(1)!, + term.registerMarker(2)!, + term.registerMarker(3)!, + term.registerMarker(4)! + ]; + let disposeCount = 0; + for (const marker of markers) { + marker.onDispose(() => disposeCount++); + } + term.clear(); + strictEqual(disposeCount, markers.length); + for (const marker of markers) { + strictEqual(marker.isDisposed, true); + } + strictEqual(term.markers.length, 0); + }); + describe('options', () => { const termOptions = { cols: 80, From 306c3d73ec64ec8bf6ca7634923f32a9eccb95af Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:34:26 +0000 Subject: [PATCH 036/153] Fix flushSync() to fire onWriteParsed flushSync() processes pending writes synchronously (e.g. during CoreTerminal.resize()) but did not emit onWriteParsed, unlike the async _innerWrite path. Addons listening for parse completion could miss updates after resize. Fixes #5912 Co-authored-by: Daniel Imms --- src/common/input/WriteBuffer.test.ts | 15 +++++++++++++++ src/common/input/WriteBuffer.ts | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/src/common/input/WriteBuffer.test.ts b/src/common/input/WriteBuffer.test.ts index f366a6320..cca6bd0be 100644 --- a/src/common/input/WriteBuffer.test.ts +++ b/src/common/input/WriteBuffer.test.ts @@ -125,5 +125,20 @@ describe('WriteBuffer', () => { assert.deepEqual(stack, []); assert.deepEqual(cbStack, []); }); + it('flushSync fires onWriteParsed', () => { + let parsed = 0; + wb.onWriteParsed(() => parsed++); + wb.write('a'); + wb.write('b'); + assert.equal(parsed, 0); + wb.flushSync(); + assert.equal(parsed, 1); + }); + it('flushSync with no pending writes does not fire onWriteParsed', () => { + let parsed = 0; + wb.onWriteParsed(() => parsed++); + wb.flushSync(); + assert.equal(parsed, 0); + }); }); }); diff --git a/src/common/input/WriteBuffer.ts b/src/common/input/WriteBuffer.ts index b936c9dbf..7c6d5d866 100644 --- a/src/common/input/WriteBuffer.ts +++ b/src/common/input/WriteBuffer.ts @@ -71,7 +71,9 @@ export class WriteBuffer extends Disposable { // Process all pending chunks synchronously let chunk: string | Uint8Array | undefined; + let didProcess = false; while (chunk = this._writeBuffer.shift()) { + didProcess = true; this._action(chunk); const cb = this._callbacks.shift(); if (cb) cb(); @@ -84,6 +86,9 @@ export class WriteBuffer extends Disposable { this._callbacks.length = 0; this._isSyncWriting = false; + if (didProcess) { + this._onWriteParsed.fire(); + } } /** From fad4821fd953ebe19e65b7e81da5d972fd1a01c7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:34:48 +0000 Subject: [PATCH 037/153] =?UTF-8?q?Fix=20O(n=C2=B2)=20payload=20accumulati?= =?UTF-8?q?on=20in=20DCS/OSC/APC=20handlers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace repeated string concatenation with a chunk buffer that joins once on completion. Fixes #5916. Co-authored-by: Daniel Imms --- src/common/parser/ApcParser.ts | 15 +++++------ src/common/parser/DcsParser.ts | 15 +++++------ src/common/parser/OscParser.ts | 15 +++++------ src/common/parser/PayloadStringBuffer.ts | 34 ++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 24 deletions(-) create mode 100644 src/common/parser/PayloadStringBuffer.ts diff --git a/src/common/parser/ApcParser.ts b/src/common/parser/ApcParser.ts index 8f921f8cb..d3f5b8e2e 100644 --- a/src/common/parser/ApcParser.ts +++ b/src/common/parser/ApcParser.ts @@ -7,6 +7,7 @@ import { IApcHandler, IHandlerCollection, ApcFallbackHandlerType, IApcParser, IS import { ParserConstants } from 'common/parser/Constants'; import { utf32ToString } from 'common/input/TextDecoder'; import { IDisposable } from 'common/Types'; +import { PayloadStringBuffer } from 'common/parser/PayloadStringBuffer'; const EMPTY_HANDLERS: IApcHandler[] = []; @@ -153,13 +154,13 @@ export class ApcParser implements IApcParser { export class ApcHandler implements IApcHandler { private static _payloadLimit = ParserConstants.PAYLOAD_LIMIT; - private _data = ''; + private _data = new PayloadStringBuffer(); private _hitLimit: boolean = false; constructor(private _handler: (data: string) => boolean | Promise) { } public start(): void { - this._data = ''; + this._data.reset(); this._hitLimit = false; } @@ -167,9 +168,7 @@ export class ApcHandler implements IApcHandler { if (this._hitLimit) { return; } - this._data += utf32ToString(data, start, end); - if (this._data.length > ApcHandler._payloadLimit) { - this._data = ''; + if (this._data.add(utf32ToString(data, start, end), ApcHandler._payloadLimit)) { this._hitLimit = true; } } @@ -179,18 +178,18 @@ export class ApcHandler implements IApcHandler { if (this._hitLimit) { ret = false; } else if (success) { - ret = this._handler(this._data); + ret = this._handler(this._data.toString()); if (ret instanceof Promise) { // need to hold data until `ret` got resolved // dont care for errors, data will be freed anyway on next start return ret.then(res => { - this._data = ''; + this._data.reset(); this._hitLimit = false; return res; }); } } - this._data = ''; + this._data.reset(); this._hitLimit = false; return ret; } diff --git a/src/common/parser/DcsParser.ts b/src/common/parser/DcsParser.ts index 692200230..be2fa39d5 100644 --- a/src/common/parser/DcsParser.ts +++ b/src/common/parser/DcsParser.ts @@ -8,6 +8,7 @@ import { IDcsHandler, IParams, IHandlerCollection, IDcsParser, DcsFallbackHandle import { utf32ToString } from 'common/input/TextDecoder'; import { Params } from 'common/parser/Params'; import { ParserConstants } from 'common/parser/Constants'; +import { PayloadStringBuffer } from 'common/parser/PayloadStringBuffer'; const EMPTY_HANDLERS: IDcsHandler[] = []; @@ -140,7 +141,7 @@ EMPTY_PARAMS.addParam(0); export class DcsHandler implements IDcsHandler { private static _payloadLimit = ParserConstants.PAYLOAD_LIMIT; - private _data = ''; + private _data = new PayloadStringBuffer(); private _params: IParams = EMPTY_PARAMS; private _hitLimit: boolean = false; @@ -152,7 +153,7 @@ export class DcsHandler implements IDcsHandler { // perf optimization: // clone only, if we have non empty params, otherwise stick with default this._params = (params.length > 1 || params.params[0]) ? params.clone() : EMPTY_PARAMS; - this._data = ''; + this._data.reset(); this._hitLimit = false; } @@ -160,9 +161,7 @@ export class DcsHandler implements IDcsHandler { if (this._hitLimit) { return; } - this._data += utf32ToString(data, start, end); - if (this._data.length > DcsHandler._payloadLimit) { - this._data = ''; + if (this._data.add(utf32ToString(data, start, end), DcsHandler._payloadLimit)) { this._hitLimit = true; } } @@ -172,20 +171,20 @@ export class DcsHandler implements IDcsHandler { if (this._hitLimit) { ret = false; } else if (success) { - ret = this._handler(this._data, this._params); + ret = this._handler(this._data.toString(), this._params); if (ret instanceof Promise) { // need to hold data and params until `ret` got resolved // dont care for errors, data will be freed anyway on next start return ret.then(res => { this._params = EMPTY_PARAMS; - this._data = ''; + this._data.reset(); this._hitLimit = false; return res; }); } } this._params = EMPTY_PARAMS; - this._data = ''; + this._data.reset(); this._hitLimit = false; return ret; } diff --git a/src/common/parser/OscParser.ts b/src/common/parser/OscParser.ts index 1f0c8cb23..6a17af817 100644 --- a/src/common/parser/OscParser.ts +++ b/src/common/parser/OscParser.ts @@ -7,6 +7,7 @@ import { IOscHandler, IHandlerCollection, OscFallbackHandlerType, IOscParser, IS import { OscState, ParserConstants } from 'common/parser/Constants'; import { utf32ToString } from 'common/input/TextDecoder'; import { IDisposable } from 'common/Types'; +import { PayloadStringBuffer } from 'common/parser/PayloadStringBuffer'; const EMPTY_HANDLERS: IOscHandler[] = []; @@ -194,13 +195,13 @@ export class OscParser implements IOscParser { export class OscHandler implements IOscHandler { private static _payloadLimit = ParserConstants.PAYLOAD_LIMIT; - private _data = ''; + private _data = new PayloadStringBuffer(); private _hitLimit: boolean = false; constructor(private _handler: (data: string) => boolean | Promise) { } public start(): void { - this._data = ''; + this._data.reset(); this._hitLimit = false; } @@ -208,9 +209,7 @@ export class OscHandler implements IOscHandler { if (this._hitLimit) { return; } - this._data += utf32ToString(data, start, end); - if (this._data.length > OscHandler._payloadLimit) { - this._data = ''; + if (this._data.add(utf32ToString(data, start, end), OscHandler._payloadLimit)) { this._hitLimit = true; } } @@ -220,18 +219,18 @@ export class OscHandler implements IOscHandler { if (this._hitLimit) { ret = false; } else if (success) { - ret = this._handler(this._data); + ret = this._handler(this._data.toString()); if (ret instanceof Promise) { // need to hold data until `ret` got resolved // dont care for errors, data will be freed anyway on next start return ret.then(res => { - this._data = ''; + this._data.reset(); this._hitLimit = false; return res; }); } } - this._data = ''; + this._data.reset(); this._hitLimit = false; return ret; } diff --git a/src/common/parser/PayloadStringBuffer.ts b/src/common/parser/PayloadStringBuffer.ts new file mode 100644 index 000000000..9caef25ea --- /dev/null +++ b/src/common/parser/PayloadStringBuffer.ts @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2026 The xterm.js authors. All rights reserved. + * @license MIT + */ + +/** + * Accumulates string payloads from multiple chunks without O(n²) string concatenation. + */ +export class PayloadStringBuffer { + private _chunks: string[] = []; + private _length = 0; + + public reset(): void { + this._chunks.length = 0; + this._length = 0; + } + + /** + * @returns true if the payload limit was exceeded (buffer is cleared in that case) + */ + public add(chunk: string, limit: number): boolean { + this._chunks.push(chunk); + this._length += chunk.length; + if (this._length > limit) { + this.reset(); + return true; + } + return false; + } + + public toString(): string { + return this._chunks.join(''); + } +} From c06c3025f28e351935fbc6da7058b0169c9eb9e8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:35:04 +0000 Subject: [PATCH 038/153] fix(addon-webgl): delete VAOs on renderer dispose Register vertex array objects for disposal in GlyphRenderer and RectangleRenderer, matching existing buffer and program cleanup. Fixes #5918 Co-authored-by: Daniel Imms --- addons/addon-webgl/src/GlyphRenderer.ts | 1 + addons/addon-webgl/src/RectangleRenderer.ts | 1 + addons/addon-webgl/src/Types.ts | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/addons/addon-webgl/src/GlyphRenderer.ts b/addons/addon-webgl/src/GlyphRenderer.ts index 46b8efeb9..7a7e98496 100644 --- a/addons/addon-webgl/src/GlyphRenderer.ts +++ b/addons/addon-webgl/src/GlyphRenderer.ts @@ -137,6 +137,7 @@ export class GlyphRenderer extends Disposable { // Create and set the vertex array object this._vertexArrayObject = gl.createVertexArray(); + this._register(toDisposable(() => gl.deleteVertexArray(this._vertexArrayObject))); gl.bindVertexArray(this._vertexArrayObject); // Setup a_unitquad, this defines the 4 vertices of a rectangle diff --git a/addons/addon-webgl/src/RectangleRenderer.ts b/addons/addon-webgl/src/RectangleRenderer.ts index 81e129e5c..b2225de3a 100644 --- a/addons/addon-webgl/src/RectangleRenderer.ts +++ b/addons/addon-webgl/src/RectangleRenderer.ts @@ -103,6 +103,7 @@ export class RectangleRenderer extends Disposable { // Create and set the vertex array object this._vertexArrayObject = gl.createVertexArray(); + this._register(toDisposable(() => gl.deleteVertexArray(this._vertexArrayObject))); gl.bindVertexArray(this._vertexArrayObject); // Setup a_unitquad, this defines the 4 vertices of a rectangle diff --git a/addons/addon-webgl/src/Types.ts b/addons/addon-webgl/src/Types.ts index 67c9408d6..b3c51e842 100644 --- a/addons/addon-webgl/src/Types.ts +++ b/addons/addon-webgl/src/Types.ts @@ -28,7 +28,8 @@ export interface ICursorRenderModel { export interface IWebGL2RenderingContext extends WebGLRenderingContext { vertexAttribDivisor(index: number, divisor: number): void; createVertexArray(): IWebGLVertexArrayObject; - bindVertexArray(vao: IWebGLVertexArrayObject): void; + deleteVertexArray(vao: IWebGLVertexArrayObject | null): void; + bindVertexArray(vao: IWebGLVertexArrayObject | null): void; drawElementsInstanced(mode: number, count: number, type: number, offset: number, instanceCount: number): void; } From f43273b3f0c589b063c5e4a772abae173def3679 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:35:05 +0000 Subject: [PATCH 039/153] fix(webgl): include texture size and cell dimensions in atlas cache equality configEquals omitted deviceMaxTextureSize, deviceCellWidth, and deviceCellHeight, which could cause the char atlas cache to reuse an atlas built for a different GPU texture limit or cell size. Fixes #5915 Co-authored-by: Daniel Imms --- addons/addon-webgl/src/CharAtlasUtils.test.ts | 87 +++++++++++++++++++ addons/addon-webgl/src/CharAtlasUtils.ts | 3 + 2 files changed, 90 insertions(+) create mode 100644 addons/addon-webgl/src/CharAtlasUtils.test.ts diff --git a/addons/addon-webgl/src/CharAtlasUtils.test.ts b/addons/addon-webgl/src/CharAtlasUtils.test.ts new file mode 100644 index 000000000..0a135746e --- /dev/null +++ b/addons/addon-webgl/src/CharAtlasUtils.test.ts @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2026 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { assert } from 'chai'; +import { configEquals } from './CharAtlasUtils'; +import { ICharAtlasConfig } from './Types'; +import { NULL_COLOR } from 'common/Color'; +import { IColor } from 'common/Types'; + +function createTestConfig(overrides: Partial = {}): ICharAtlasConfig { + const color: IColor = { css: '#ffffff', rgba: 0xffffffff }; + const contrastCache = { + clear: () => {}, + setCss: () => {}, + getCss: () => undefined, + setColor: () => {}, + getColor: () => undefined + }; + const colors = { + foreground: color, + background: color, + cursor: NULL_COLOR, + cursorAccent: NULL_COLOR, + selectionForeground: undefined, + selectionBackgroundTransparent: NULL_COLOR, + selectionBackgroundOpaque: NULL_COLOR, + selectionInactiveBackgroundTransparent: NULL_COLOR, + selectionInactiveBackgroundOpaque: NULL_COLOR, + overviewRulerBorder: NULL_COLOR, + scrollbarSliderBackground: NULL_COLOR, + scrollbarSliderHoverBackground: NULL_COLOR, + scrollbarSliderActiveBackground: NULL_COLOR, + ansi: new Array(256).fill(color), + contrastCache, + halfContrastCache: contrastCache + }; + return { + customGlyphs: true, + devicePixelRatio: 1, + deviceMaxTextureSize: 4096, + letterSpacing: 0, + lineHeight: 1, + fontSize: 15, + fontFamily: 'monospace', + fontWeight: 'normal', + fontWeightBold: 'bold', + deviceCellWidth: 10, + deviceCellHeight: 20, + deviceCharWidth: 8, + deviceCharHeight: 16, + allowTransparency: false, + drawBoldTextInBrightColors: true, + minimumContrastRatio: 1, + colors, + ...overrides + }; +} + +describe('CharAtlasUtils', () => { + describe('configEquals', () => { + it('should return true for identical configs', () => { + const a = createTestConfig(); + const b = createTestConfig(); + assert.ok(configEquals(a, b)); + }); + + it('should return false when deviceMaxTextureSize differs', () => { + const a = createTestConfig(); + const b = createTestConfig({ deviceMaxTextureSize: 8192 }); + assert.ok(!configEquals(a, b)); + }); + + it('should return false when deviceCellWidth differs', () => { + const a = createTestConfig(); + const b = createTestConfig({ deviceCellWidth: 11 }); + assert.ok(!configEquals(a, b)); + }); + + it('should return false when deviceCellHeight differs', () => { + const a = createTestConfig(); + const b = createTestConfig({ deviceCellHeight: 21 }); + assert.ok(!configEquals(a, b)); + }); + }); +}); diff --git a/addons/addon-webgl/src/CharAtlasUtils.ts b/addons/addon-webgl/src/CharAtlasUtils.ts index 4ce103e98..d9ca69b39 100644 --- a/addons/addon-webgl/src/CharAtlasUtils.ts +++ b/addons/addon-webgl/src/CharAtlasUtils.ts @@ -59,6 +59,7 @@ export function configEquals(a: ICharAtlasConfig, b: ICharAtlasConfig): boolean } } return a.devicePixelRatio === b.devicePixelRatio && + a.deviceMaxTextureSize === b.deviceMaxTextureSize && a.customGlyphs === b.customGlyphs && a.lineHeight === b.lineHeight && a.letterSpacing === b.letterSpacing && @@ -67,6 +68,8 @@ export function configEquals(a: ICharAtlasConfig, b: ICharAtlasConfig): boolean a.fontWeight === b.fontWeight && a.fontWeightBold === b.fontWeightBold && a.allowTransparency === b.allowTransparency && + a.deviceCellWidth === b.deviceCellWidth && + a.deviceCellHeight === b.deviceCellHeight && a.deviceCharWidth === b.deviceCharWidth && a.deviceCharHeight === b.deviceCharHeight && a.drawBoldTextInBrightColors === b.drawBoldTextInBrightColors && From 4aa02f3fd947838b94ec82e68d248d4d20e9c6f7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:37:09 +0000 Subject: [PATCH 040/153] fix(addon-webgl): route logging through ILogService instead of console Replace direct console.log/warn/error calls in the WebGL addon with ILogService so messages respect the terminal logLevel and custom logger. Fixes xtermjs/xterm.js#5921 Co-authored-by: Daniel Imms --- addons/addon-webgl/src/GlyphRenderer.ts | 7 ++++--- addons/addon-webgl/src/RectangleRenderer.ts | 6 ++++-- addons/addon-webgl/src/TextureAtlas.ts | 2 +- addons/addon-webgl/src/WebglAddon.ts | 4 +++- addons/addon-webgl/src/WebglRenderer.ts | 13 +++++++------ addons/addon-webgl/src/WebglUtils.ts | 13 +++++++------ .../src/customGlyphs/CustomGlyphRasterizer.ts | 17 +++++++++++------ 7 files changed, 37 insertions(+), 25 deletions(-) diff --git a/addons/addon-webgl/src/GlyphRenderer.ts b/addons/addon-webgl/src/GlyphRenderer.ts index 46b8efeb9..1efde442e 100644 --- a/addons/addon-webgl/src/GlyphRenderer.ts +++ b/addons/addon-webgl/src/GlyphRenderer.ts @@ -9,7 +9,7 @@ import { Disposable, toDisposable } from 'common/Lifecycle'; import { Terminal } from '@xterm/xterm'; import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject, type IRasterizedGlyph, type ITextureAtlas } from './Types'; import { createProgram, GLTexture, PROJECTION_MATRIX } from './WebglUtils'; -import type { IOptionsService } from 'common/services/Services'; +import type { ILogService, IOptionsService } from 'common/services/Services'; import { allowRescaling, throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; interface IVertices { @@ -114,7 +114,8 @@ export class GlyphRenderer extends Disposable { private readonly _terminal: Terminal, private readonly _gl: IWebGL2RenderingContext, private _dimensions: IRenderDimensions, - private readonly _optionsService: IOptionsService + private readonly _optionsService: IOptionsService, + private readonly _logService: ILogService ) { super(); @@ -127,7 +128,7 @@ export class GlyphRenderer extends Disposable { TextureAtlas.maxTextureSize = throwIfFalsy(gl.getParameter(gl.MAX_TEXTURE_SIZE) as number | null); } - this._program = throwIfFalsy(createProgram(gl, vertexShaderSource, createFragmentShaderSource(TextureAtlas.maxAtlasPages))); + this._program = throwIfFalsy(createProgram(gl, vertexShaderSource, createFragmentShaderSource(TextureAtlas.maxAtlasPages), this._logService)); this._register(toDisposable(() => gl.deleteProgram(this._program))); // Uniform locations diff --git a/addons/addon-webgl/src/RectangleRenderer.ts b/addons/addon-webgl/src/RectangleRenderer.ts index 81e129e5c..6da92be13 100644 --- a/addons/addon-webgl/src/RectangleRenderer.ts +++ b/addons/addon-webgl/src/RectangleRenderer.ts @@ -14,6 +14,7 @@ import { RenderModelConstants } from './RenderModel'; import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject } from './Types'; import { createProgram, expandFloat32Array, PROJECTION_MATRIX } from './WebglUtils'; import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; +import type { ILogService } from 'common/services/Services'; const enum VertexAttribLocations { POSITION = 0, @@ -89,13 +90,14 @@ export class RectangleRenderer extends Disposable { private _terminal: Terminal, private _gl: IWebGL2RenderingContext, private _dimensions: IRenderDimensions, - private readonly _themeService: IThemeService + private readonly _themeService: IThemeService, + private readonly _logService: ILogService ) { super(); const gl = this._gl; - this._program = throwIfFalsy(createProgram(gl, vertexShaderSource, fragmentShaderSource)); + this._program = throwIfFalsy(createProgram(gl, vertexShaderSource, fragmentShaderSource, this._logService)); this._register(toDisposable(() => gl.deleteProgram(this._program))); // Uniform locations diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index 34188235c..ba7db27d3 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -529,7 +529,7 @@ export class TextureAtlas implements ITextureAtlas { let customGlyph = false; if (this._config.customGlyphs !== false) { const variantOffset = this._workAttributeData.getUnderlineVariantOffset(); - customGlyph = tryDrawCustomGlyph(this._tmpCtx, chars, padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight, this._config.deviceCharWidth, this._config.deviceCharHeight, this._config.fontSize, this._config.devicePixelRatio, backgroundColor.css, variantOffset); + customGlyph = tryDrawCustomGlyph(this._tmpCtx, chars, padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight, this._config.deviceCharWidth, this._config.deviceCharHeight, this._config.fontSize, this._config.devicePixelRatio, this._logService, backgroundColor.css, variantOffset); } // Whether to clear pixels based on a threshold difference between the glyph color and the diff --git a/addons/addon-webgl/src/WebglAddon.ts b/addons/addon-webgl/src/WebglAddon.ts index f21fb704e..624c9ea38 100644 --- a/addons/addon-webgl/src/WebglAddon.ts +++ b/addons/addon-webgl/src/WebglAddon.ts @@ -9,7 +9,7 @@ import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IRender import { ITerminal } from 'browser/Types'; import { Disposable, toDisposable } from 'common/Lifecycle'; import { getSafariVersion, isSafari } from 'common/Platform'; -import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; +import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services'; import { IWebGL2RenderingContext } from './Types'; import { WebglRenderer } from './WebglRenderer'; import { Emitter, EventUtils } from 'common/Event'; @@ -65,6 +65,7 @@ export class WebglAddon extends Disposable implements ITerminalAddon, IWebglApi const charSizeService: ICharSizeService = unsafeCore._charSizeService; const coreBrowserService: ICoreBrowserService = unsafeCore._coreBrowserService; const decorationService: IDecorationService = unsafeCore._decorationService; + const logService: ILogService = unsafeCore._logService; const themeService: IThemeService = unsafeCore._themeService; this._renderer = this._register(new WebglRenderer( @@ -74,6 +75,7 @@ export class WebglAddon extends Disposable implements ITerminalAddon, IWebglApi coreBrowserService, coreService, decorationService, + logService, optionsService, themeService, this._customGlyphs, diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 500308bd1..2c20accc7 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -15,7 +15,7 @@ import { AttributeData } from 'common/buffer/AttributeData'; import { CellData } from 'common/buffer/CellData'; import { Attributes, Content, FgFlags, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants'; import { TextBlinkStateManager } from 'browser/renderer/shared/TextBlinkStateManager'; -import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; +import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services'; import { Terminal } from '@xterm/xterm'; import { GlyphRenderer } from './GlyphRenderer'; import { RectangleRenderer } from './RectangleRenderer'; @@ -77,6 +77,7 @@ export class WebglRenderer extends Disposable implements IRenderer { private readonly _coreBrowserService: ICoreBrowserService, private readonly _coreService: ICoreService, private readonly _decorationService: IDecorationService, + private readonly _logService: ILogService, private readonly _optionsService: IOptionsService, private readonly _themeService: IThemeService, private readonly _customGlyphs: boolean = true, @@ -122,19 +123,19 @@ export class WebglRenderer extends Disposable implements IRenderer { this._deviceMaxTextureSize = this._gl.getParameter(this._gl.MAX_TEXTURE_SIZE); this._register(addDisposableListener(this._canvas, 'webglcontextlost', (e) => { - console.log('webglcontextlost event received'); + this._logService.debug('webglcontextlost event received'); // Prevent the default behavior in order to enable WebGL context restoration. e.preventDefault(); // Wait a few seconds to see if the 'webglcontextrestored' event is fired. // If not, dispatch the onContextLoss notification to observers. this._contextRestorationTimeout = setTimeout(() => { this._contextRestorationTimeout = undefined; - console.warn('webgl context not restored; firing onContextLoss'); + this._logService.warn('webgl context not restored; firing onContextLoss'); this._onContextLoss.fire(e); }, 3000 /* ms */); })); this._register(addDisposableListener(this._canvas, 'webglcontextrestored', (e) => { - console.warn('webglcontextrestored event received'); + this._logService.warn('webglcontextrestored event received'); clearTimeout(this._contextRestorationTimeout); this._contextRestorationTimeout = undefined; // The texture atlas and glyph renderer must be fully reinitialized @@ -274,8 +275,8 @@ export class WebglRenderer extends Disposable implements IRenderer { * Initializes members dependent on WebGL context state. */ private _initializeWebGLState(): [RectangleRenderer, GlyphRenderer] { - this._rectangleRenderer.value = new RectangleRenderer(this._terminal, this._gl, this.dimensions, this._themeService); - this._glyphRenderer.value = new GlyphRenderer(this._terminal, this._gl, this.dimensions, this._optionsService); + this._rectangleRenderer.value = new RectangleRenderer(this._terminal, this._gl, this.dimensions, this._themeService, this._logService); + this._glyphRenderer.value = new GlyphRenderer(this._terminal, this._gl, this.dimensions, this._optionsService, this._logService); // Update dimensions and acquire char atlas this.handleCharSizeChanged(); diff --git a/addons/addon-webgl/src/WebglUtils.ts b/addons/addon-webgl/src/WebglUtils.ts index da765c802..9944c6bd3 100644 --- a/addons/addon-webgl/src/WebglUtils.ts +++ b/addons/addon-webgl/src/WebglUtils.ts @@ -4,6 +4,7 @@ */ import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; +import type { ILogService } from 'common/services/Services'; /** * A matrix that when multiplies will translate 0-1 coordinates (left to right, @@ -16,21 +17,21 @@ export const PROJECTION_MATRIX = new Float32Array([ -1, 1, 0, 1 ]); -export function createProgram(gl: WebGLRenderingContext, vertexSource: string, fragmentSource: string): WebGLProgram | undefined { +export function createProgram(gl: WebGLRenderingContext, vertexSource: string, fragmentSource: string, logService: ILogService): WebGLProgram | undefined { const program = throwIfFalsy(gl.createProgram()); - gl.attachShader(program, throwIfFalsy(createShader(gl, gl.VERTEX_SHADER, vertexSource))); - gl.attachShader(program, throwIfFalsy(createShader(gl, gl.FRAGMENT_SHADER, fragmentSource))); + gl.attachShader(program, throwIfFalsy(createShader(gl, gl.VERTEX_SHADER, vertexSource, logService))); + gl.attachShader(program, throwIfFalsy(createShader(gl, gl.FRAGMENT_SHADER, fragmentSource, logService))); gl.linkProgram(program); const success = gl.getProgramParameter(program, gl.LINK_STATUS); if (success) { return program; } - console.error(gl.getProgramInfoLog(program)); + logService.error(gl.getProgramInfoLog(program)); gl.deleteProgram(program); } -export function createShader(gl: WebGLRenderingContext, type: number, source: string): WebGLShader | undefined { +export function createShader(gl: WebGLRenderingContext, type: number, source: string, logService: ILogService): WebGLShader | undefined { const shader = throwIfFalsy(gl.createShader(type)); gl.shaderSource(shader, source); gl.compileShader(shader); @@ -39,7 +40,7 @@ export function createShader(gl: WebGLRenderingContext, type: number, source: st return shader; } - console.error(gl.getShaderInfoLog(shader)); + logService.error(gl.getShaderInfoLog(shader)); gl.deleteShader(shader); } diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts index ba8925727..357d512d2 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts @@ -4,6 +4,7 @@ */ import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; +import type { ILogService } from 'common/services/Services'; import { customGlyphDefinitions } from './CustomGlyphDefinitions'; import { CustomGlyphDefinitionType, CustomGlyphScaleType, CustomGlyphVectorType, type CustomGlyphDefinitionPart, type CustomGlyphPathDrawFunctionDefinition, type CustomGlyphPatternDefinition, type ICustomGlyphSolidOctantBlockVector, type ICustomGlyphVectorShape } from './Types'; @@ -22,6 +23,7 @@ export function tryDrawCustomGlyph( deviceCharHeight: number, fontSize: number, devicePixelRatio: number, + logService: ILogService, backgroundColor?: string, variantOffset: number = 0 ): boolean { @@ -30,7 +32,7 @@ export function tryDrawCustomGlyph( // Normalize to array for uniform handling const parts = Array.isArray(unifiedCharDefinition) ? unifiedCharDefinition : [unifiedCharDefinition]; for (const part of parts) { - drawDefinitionPart(ctx, part, xOffset, yOffset, deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, fontSize, devicePixelRatio, backgroundColor, variantOffset); + drawDefinitionPart(ctx, part, xOffset, yOffset, deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, fontSize, devicePixelRatio, logService, backgroundColor, variantOffset); } return true; } @@ -49,6 +51,7 @@ function drawDefinitionPart( deviceCharHeight: number, fontSize: number, devicePixelRatio: number, + logService: ILogService, backgroundColor?: string, variantOffset: number = 0 ): void { @@ -79,7 +82,7 @@ function drawDefinitionPart( drawPatternChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, variantOffset); break; case CustomGlyphDefinitionType.PATH_FUNCTION: - drawPathFunctionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, part.strokeWidth); + drawPathFunctionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, logService, part.strokeWidth); break; case CustomGlyphDefinitionType.PATH: drawPathDefinitionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, part.strokeWidth); @@ -88,7 +91,7 @@ function drawDefinitionPart( drawPathNegativeDefinitionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, backgroundColor); break; case CustomGlyphDefinitionType.VECTOR_SHAPE: - drawVectorShape(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, fontSize, devicePixelRatio); + drawVectorShape(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, fontSize, devicePixelRatio, logService); break; case CustomGlyphDefinitionType.BRAILLE: drawBrailleCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight); @@ -510,6 +513,7 @@ function drawPathFunctionCharacter( deviceCellWidth: number, deviceCellHeight: number, devicePixelRatio: number, + logService: ILogService, strokeWidth?: number ): void { ctx.save(); @@ -536,7 +540,7 @@ function drawPathFunctionCharacter( } const f = svgToCanvasInstructionMap[type]; if (!f) { - console.error(`Could not find drawing instructions for "${type}"`); + logService.error(`Could not find drawing instructions for "${type}"`); continue; } const args: string[] = instruction.substring(1).split(','); @@ -598,7 +602,8 @@ function drawVectorShape( deviceCellWidth: number, deviceCellHeight: number, fontSize: number, - devicePixelRatio: number + devicePixelRatio: number, + logService: ILogService ): void { // Clip the cell to make sure drawing doesn't occur beyond bounds const clipRegion = new Path2D(); @@ -619,7 +624,7 @@ function drawVectorShape( } const f = svgToCanvasInstructionMap[type]; if (!f) { - console.error(`Could not find drawing instructions for "${type}"`); + logService.error(`Could not find drawing instructions for "${type}"`); continue; } const args: string[] = instruction.substring(1).split(','); From f757c965ee0abd33dbc9577ef60a52748f3bdfd9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:38:58 +0000 Subject: [PATCH 041/153] fix(addon-webgl): keep VAO WebGL types non-nullable Co-authored-by: Daniel Imms --- addons/addon-webgl/src/GlyphRenderer.ts | 6 +++--- addons/addon-webgl/src/RectangleRenderer.ts | 6 +++--- addons/addon-webgl/src/Types.ts | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/addons/addon-webgl/src/GlyphRenderer.ts b/addons/addon-webgl/src/GlyphRenderer.ts index 7a7e98496..cfa156037 100644 --- a/addons/addon-webgl/src/GlyphRenderer.ts +++ b/addons/addon-webgl/src/GlyphRenderer.ts @@ -136,9 +136,9 @@ export class GlyphRenderer extends Disposable { this._textureLocation = throwIfFalsy(gl.getUniformLocation(this._program, 'u_texture')); // Create and set the vertex array object - this._vertexArrayObject = gl.createVertexArray(); - this._register(toDisposable(() => gl.deleteVertexArray(this._vertexArrayObject))); - gl.bindVertexArray(this._vertexArrayObject); + const vertexArrayObject = this._vertexArrayObject = gl.createVertexArray(); + this._register(toDisposable(() => gl.deleteVertexArray(vertexArrayObject))); + gl.bindVertexArray(vertexArrayObject); // Setup a_unitquad, this defines the 4 vertices of a rectangle const unitQuadVertices = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]); diff --git a/addons/addon-webgl/src/RectangleRenderer.ts b/addons/addon-webgl/src/RectangleRenderer.ts index b2225de3a..55926ed14 100644 --- a/addons/addon-webgl/src/RectangleRenderer.ts +++ b/addons/addon-webgl/src/RectangleRenderer.ts @@ -102,9 +102,9 @@ export class RectangleRenderer extends Disposable { this._projectionLocation = throwIfFalsy(gl.getUniformLocation(this._program, 'u_projection')); // Create and set the vertex array object - this._vertexArrayObject = gl.createVertexArray(); - this._register(toDisposable(() => gl.deleteVertexArray(this._vertexArrayObject))); - gl.bindVertexArray(this._vertexArrayObject); + const vertexArrayObject = this._vertexArrayObject = gl.createVertexArray(); + this._register(toDisposable(() => gl.deleteVertexArray(vertexArrayObject))); + gl.bindVertexArray(vertexArrayObject); // Setup a_unitquad, this defines the 4 vertices of a rectangle const unitQuadVertices = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]); diff --git a/addons/addon-webgl/src/Types.ts b/addons/addon-webgl/src/Types.ts index b3c51e842..294582e37 100644 --- a/addons/addon-webgl/src/Types.ts +++ b/addons/addon-webgl/src/Types.ts @@ -28,8 +28,8 @@ export interface ICursorRenderModel { export interface IWebGL2RenderingContext extends WebGLRenderingContext { vertexAttribDivisor(index: number, divisor: number): void; createVertexArray(): IWebGLVertexArrayObject; - deleteVertexArray(vao: IWebGLVertexArrayObject | null): void; - bindVertexArray(vao: IWebGLVertexArrayObject | null): void; + deleteVertexArray(vao: IWebGLVertexArrayObject): void; + bindVertexArray(vao: IWebGLVertexArrayObject): void; drawElementsInstanced(mode: number, count: number, type: number, offset: number, instanceCount: number): void; } From 534ce1365b1570bd0b8112dbc9892ea058be3ae0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:40:10 +0000 Subject: [PATCH 042/153] Clear WebGL context-loss timeout on WebglRenderer dispose Fixes #5919 Co-authored-by: Daniel Imms --- addons/addon-webgl/src/WebglRenderer.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 500308bd1..8988d9fc8 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -158,6 +158,8 @@ export class WebglRenderer extends Disposable implements IRenderer { this._isAttached = this._core.screenElement!.isConnected; this._register(toDisposable(() => { + clearTimeout(this._contextRestorationTimeout); + this._contextRestorationTimeout = undefined; for (const l of this._renderLayers) { l.dispose(); } From 090b77f354e684fc6434029656bc37aafc4dd0d9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 15:41:00 +0000 Subject: [PATCH 043/153] Replace custom clone utility with structuredClone Remove common/Clone.ts and its tests. Use the native structuredClone API for cloning default modes, options, and test fixtures. Fixes #5911 Co-authored-by: Daniel Imms --- src/common/Clone.test.ts | 129 ----------------------------- src/common/Clone.ts | 23 ----- src/common/InputHandler.test.ts | 13 ++- src/common/TestUtils.test.ts | 3 +- src/common/services/CoreService.ts | 9 +- 5 files changed, 11 insertions(+), 166 deletions(-) delete mode 100644 src/common/Clone.test.ts delete mode 100644 src/common/Clone.ts diff --git a/src/common/Clone.test.ts b/src/common/Clone.test.ts deleted file mode 100644 index 5fbe4bf9b..000000000 --- a/src/common/Clone.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Copyright (c) 2016 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { assert } from 'chai'; -import { clone } from 'common/Clone'; - -describe('clone', () => { - it('should clone simple objects', () => { - const test = { - a: 1, - b: 2 - }; - - assert.deepEqual(clone(test), { a: 1, b: 2 }); - }); - - it('should clone nested objects', () => { - const test = { - bar: { - a: 1, - b: 2, - c: { - foo: 'bar' - } - } - }; - - assert.deepEqual(clone(test), { - bar: { - a: 1, - b: 2, - c: { - foo: 'bar' - } - } - }); - }); - - it('should clone array values', () => { - const test = { - a: [1, 2, 3], - b: [1, null, 'test', { foo: 'bar' }] - }; - - assert.deepEqual(clone(test), { - a: [1, 2, 3], - b: [1, null, 'test', { foo: 'bar' }] - }); - }); - - it('should stop mutation from occuring on the original object', () => { - const test = { - a: 1, - b: 2, - c: { - foo: 'bar' - } - }; - - const cloned = clone(test); - - test.a = 5; - test.c.foo = 'barbaz'; - - assert.deepEqual(cloned, { - a: 1, - b: 2, - c: { - foo: 'bar' - } - }); - }); - - it('should clone to a maximum depth of 5 by default', () => { - const test = { - a: { - b: { - c: { - d: { - e: { - f: 'foo' - } - } - } - } - } - }; - - const cloned = clone(test); - - test.a.b.c.d.e.f = 'bar'; - - // The values at a greater depth then 5 should not be cloned - assert.equal((cloned as any).a.b.c.d.e.f, 'bar'); - }); - - it('should allow an optional maximum depth to be set', () => { - const test = { - a: { - b: { - c: 'foo' - } - } - }; - - const cloned = clone(test, 2); - - test.a.b.c = 'bar'; - - // The values at a greater depth then 2 should not be cloned - assert.equal((cloned as any).a.b.c, 'bar'); - }); - - it('should not throw when cloning a recursive reference', () => { - const test = { - a: { - b: { - c: {} - } - } - }; - - test.a.b.c = test; - - assert.doesNotThrow(() => clone(test)); - }); -}); diff --git a/src/common/Clone.ts b/src/common/Clone.ts deleted file mode 100644 index 37821fe02..000000000 --- a/src/common/Clone.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2016 The xterm.js authors. All rights reserved. - * @license MIT - */ - -/* - * A simple utility for cloning values - */ -export function clone(val: T, depth: number = 5): T { - if (typeof val !== 'object') { - return val; - } - - // If we're cloning an array, use an array as the base, otherwise use an object - const clonedObject: any = Array.isArray(val) ? [] : {}; - - for (const key in val) { - // Recursively clone eack item unless we're at the maximum depth - clonedObject[key] = depth <= 1 ? val[key] : (val[key] && clone(val[key], depth - 1)); - } - - return clonedObject as T; -} diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index f9da81a25..5c59b062b 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -14,7 +14,6 @@ import { Params } from 'common/parser/Params'; import { MockCoreService, MockBufferService, MockOptionsService, MockLogService, MockMouseStateService, MockCharsetService, MockUnicodeService, MockOscLinkService, extendedAttributes } from 'common/TestUtils.test'; import { IBufferService, ICoreService, type IOscLinkService } from 'common/services/Services'; import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; -import { clone } from 'common/Clone'; import { BufferService } from 'common/services/BufferService'; import { CoreService } from 'common/services/CoreService'; import { OscLinkService } from 'common/services/OscLinkService'; @@ -227,32 +226,32 @@ describe('InputHandler', () => { assert.equal(coreService.decPrivateModes.cursorStyle, undefined); assert.equal(coreService.decPrivateModes.cursorBlink, undefined); - optionsService.options = clone(DEFAULT_OPTIONS); + optionsService.options = structuredClone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([1])); assert.equal(coreService.decPrivateModes.cursorStyle, 'block'); assert.equal(coreService.decPrivateModes.cursorBlink, true); - optionsService.options = clone(DEFAULT_OPTIONS); + optionsService.options = structuredClone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([2])); assert.equal(coreService.decPrivateModes.cursorStyle, 'block'); assert.equal(coreService.decPrivateModes.cursorBlink, false); - optionsService.options = clone(DEFAULT_OPTIONS); + optionsService.options = structuredClone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([3])); assert.equal(coreService.decPrivateModes.cursorStyle, 'underline'); assert.equal(coreService.decPrivateModes.cursorBlink, true); - optionsService.options = clone(DEFAULT_OPTIONS); + optionsService.options = structuredClone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([4])); assert.equal(coreService.decPrivateModes.cursorStyle, 'underline'); assert.equal(coreService.decPrivateModes.cursorBlink, false); - optionsService.options = clone(DEFAULT_OPTIONS); + optionsService.options = structuredClone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([5])); assert.equal(coreService.decPrivateModes.cursorStyle, 'bar'); assert.equal(coreService.decPrivateModes.cursorBlink, true); - optionsService.options = clone(DEFAULT_OPTIONS); + optionsService.options = structuredClone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([6])); assert.equal(coreService.decPrivateModes.cursorStyle, 'bar'); assert.equal(coreService.decPrivateModes.cursorBlink, false); diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index 5940fa659..69c2d499d 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -5,7 +5,6 @@ import { IBufferService, ICoreService, ILogService, IOptionsService, ITerminalOptions, IMouseStateService, ICharsetService, UnicodeCharProperties, UnicodeCharWidth, IUnicodeService, IUnicodeVersionProvider, LogLevelEnum, IDecorationService, IInternalDecoration, IOscLinkService, type IBufferResizeEvent } from 'common/services/Services'; import { UnicodeService } from 'common/services/UnicodeService'; -import { clone } from 'common/Clone'; import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; import { IBufferSet, IBuffer } from 'common/buffer/Types'; import { BufferSet } from 'common/buffer/BufferSet'; @@ -157,7 +156,7 @@ export class MockLogService implements ILogService { export class MockOptionsService implements IOptionsService { public serviceBrand: any; - public readonly rawOptions: Required = clone(DEFAULT_OPTIONS); + public readonly rawOptions: Required = structuredClone(DEFAULT_OPTIONS); public options: Required = this.rawOptions; public onOptionChange: IEvent = new Emitter().event; constructor(testOptions?: Partial) { diff --git a/src/common/services/CoreService.ts b/src/common/services/CoreService.ts index d8962ec88..f5094f5a9 100644 --- a/src/common/services/CoreService.ts +++ b/src/common/services/CoreService.ts @@ -3,7 +3,6 @@ * @license MIT */ -import { clone } from 'common/Clone'; import { Disposable } from 'common/Lifecycle'; import { IDecPrivateModes, IKittyKeyboardState, IModes } from 'common/Types'; import { IBufferService, ICoreService, ILogService, IOptionsService } from 'common/services/Services'; @@ -61,14 +60,14 @@ export class CoreService extends Disposable implements ICoreService { ) { super(); this.isCursorInitialized = _optionsService.rawOptions.showCursorImmediately ?? false; - this.modes = clone(DEFAULT_MODES); - this.decPrivateModes = clone(DEFAULT_DEC_PRIVATE_MODES); + this.modes = structuredClone(DEFAULT_MODES); + this.decPrivateModes = structuredClone(DEFAULT_DEC_PRIVATE_MODES); this.kittyKeyboard = DEFAULT_KITTY_KEYBOARD_STATE(); } public reset(): void { - this.modes = clone(DEFAULT_MODES); - this.decPrivateModes = clone(DEFAULT_DEC_PRIVATE_MODES); + this.modes = structuredClone(DEFAULT_MODES); + this.decPrivateModes = structuredClone(DEFAULT_DEC_PRIVATE_MODES); this.kittyKeyboard = DEFAULT_KITTY_KEYBOARD_STATE(); } From 30b42838fd0e0674c86970df72f57b314c30bc0b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 16:27:26 +0000 Subject: [PATCH 044/153] Extract StringBuilder and LimitedStringBuilder with unit tests Replace PayloadStringBuffer with LimitedStringBuilder from common. Add thorough StringBuilder and LimitedStringBuilder test coverage. Co-authored-by: Daniel Imms --- src/common/StringBuilder.test.ts | 143 +++++++++++++++++++++++ src/common/StringBuilder.ts | 67 +++++++++++ src/common/parser/ApcParser.ts | 6 +- src/common/parser/DcsParser.ts | 6 +- src/common/parser/OscParser.ts | 6 +- src/common/parser/PayloadStringBuffer.ts | 34 ------ 6 files changed, 219 insertions(+), 43 deletions(-) create mode 100644 src/common/StringBuilder.test.ts create mode 100644 src/common/StringBuilder.ts delete mode 100644 src/common/parser/PayloadStringBuffer.ts diff --git a/src/common/StringBuilder.test.ts b/src/common/StringBuilder.test.ts new file mode 100644 index 000000000..1794de0f7 --- /dev/null +++ b/src/common/StringBuilder.test.ts @@ -0,0 +1,143 @@ +/** + * Copyright (c) 2026 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { assert } from 'chai'; +import { LimitedStringBuilder, StringBuilder } from 'common/StringBuilder'; + +describe('StringBuilder', () => { + it('should start empty', () => { + const builder = new StringBuilder(); + assert.strictEqual(builder.length, 0); + assert.strictEqual(builder.toString(), ''); + }); + + it('should append a single chunk', () => { + const builder = new StringBuilder(); + builder.append('hello'); + assert.strictEqual(builder.length, 5); + assert.strictEqual(builder.toString(), 'hello'); + }); + + it('should join multiple chunks in order', () => { + const builder = new StringBuilder(); + builder.append('foo'); + builder.append('bar'); + builder.append('baz'); + assert.strictEqual(builder.length, 9); + assert.strictEqual(builder.toString(), 'foobarbaz'); + }); + + it('should handle empty chunks', () => { + const builder = new StringBuilder(); + builder.append(''); + builder.append('a'); + builder.append(''); + assert.strictEqual(builder.length, 1); + assert.strictEqual(builder.toString(), 'a'); + }); + + it('should reset accumulated data', () => { + const builder = new StringBuilder(); + builder.append('hello'); + builder.reset(); + assert.strictEqual(builder.length, 0); + assert.strictEqual(builder.toString(), ''); + }); + + it('should allow appending after reset', () => { + const builder = new StringBuilder(); + builder.append('old'); + builder.reset(); + builder.append('new'); + assert.strictEqual(builder.toString(), 'new'); + }); + + it('should accumulate many small chunks without quadratic concatenation', () => { + const builder = new StringBuilder(); + const chunk = 'x'; + const count = 10000; + for (let i = 0; i < count; i++) { + builder.append(chunk); + } + assert.strictEqual(builder.length, count); + assert.strictEqual(builder.toString(), 'x'.repeat(count)); + }); +}); + +describe('LimitedStringBuilder', () => { + it('should expose the configured limit', () => { + const builder = new LimitedStringBuilder(42); + assert.strictEqual(builder.limit, 42); + }); + + it('should start empty', () => { + const builder = new LimitedStringBuilder(10); + assert.strictEqual(builder.length, 0); + assert.strictEqual(builder.toString(), ''); + }); + + it('should accept data up to the limit', () => { + const builder = new LimitedStringBuilder(10); + assert.strictEqual(builder.append('12345'), false); + assert.strictEqual(builder.append('67890'), false); + assert.strictEqual(builder.length, 10); + assert.strictEqual(builder.toString(), '1234567890'); + }); + + it('should accept a single chunk exactly at the limit', () => { + const builder = new LimitedStringBuilder(5); + assert.strictEqual(builder.append('abcde'), false); + assert.strictEqual(builder.length, 5); + assert.strictEqual(builder.toString(), 'abcde'); + }); + + it('should reject data exceeding the limit and clear the buffer', () => { + const builder = new LimitedStringBuilder(5); + builder.append('abc'); + assert.strictEqual(builder.append('def'), true); + assert.strictEqual(builder.length, 0); + assert.strictEqual(builder.toString(), ''); + }); + + it('should reject a single chunk larger than the limit', () => { + const builder = new LimitedStringBuilder(3); + assert.strictEqual(builder.append('toolong'), true); + assert.strictEqual(builder.length, 0); + assert.strictEqual(builder.toString(), ''); + }); + + it('should allow appending again after reset following a limit breach', () => { + const builder = new LimitedStringBuilder(3); + assert.strictEqual(builder.append('abcd'), true); + builder.reset(); + assert.strictEqual(builder.append('ab'), false); + assert.strictEqual(builder.toString(), 'ab'); + }); + + it('should accumulate many chunks before hitting the limit', () => { + const limit = 100; + const builder = new LimitedStringBuilder(limit); + const chunk = 'A'; + for (let i = 0; i < limit; i++) { + assert.strictEqual(builder.append(chunk), false); + } + assert.strictEqual(builder.toString(), 'A'.repeat(limit)); + assert.strictEqual(builder.append('B'), true); + assert.strictEqual(builder.toString(), ''); + }); + + it('should reject when limit is zero and any data is appended', () => { + const builder = new LimitedStringBuilder(0); + assert.strictEqual(builder.append('a'), true); + assert.strictEqual(builder.length, 0); + }); + + it('should allow zero-length appends at the limit', () => { + const builder = new LimitedStringBuilder(0); + assert.strictEqual(builder.append(''), false); + assert.strictEqual(builder.length, 0); + assert.strictEqual(builder.toString(), ''); + }); +}); diff --git a/src/common/StringBuilder.ts b/src/common/StringBuilder.ts new file mode 100644 index 000000000..d9184ce31 --- /dev/null +++ b/src/common/StringBuilder.ts @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2026 The xterm.js authors. All rights reserved. + * @license MIT + */ + +/** + * Accumulates string data from multiple chunks without O(n²) string concatenation. + */ +export class StringBuilder { + private _chunks: string[] = []; + private _length = 0; + + public get length(): number { + return this._length; + } + + public reset(): void { + this._chunks.length = 0; + this._length = 0; + } + + public append(chunk: string): void { + this._chunks.push(chunk); + this._length += chunk.length; + } + + public toString(): string { + return this._chunks.join(''); + } +} + +/** + * String builder that rejects payloads larger than a fixed limit. + */ +export class LimitedStringBuilder { + private readonly _builder = new StringBuilder(); + + constructor(private readonly _limit: number) { } + + public get length(): number { + return this._builder.length; + } + + public get limit(): number { + return this._limit; + } + + public reset(): void { + this._builder.reset(); + } + + /** + * @returns true if the limit was exceeded (buffer is cleared in that case) + */ + public append(chunk: string): boolean { + this._builder.append(chunk); + if (this._builder.length > this._limit) { + this._builder.reset(); + return true; + } + return false; + } + + public toString(): string { + return this._builder.toString(); + } +} diff --git a/src/common/parser/ApcParser.ts b/src/common/parser/ApcParser.ts index d3f5b8e2e..4e647d037 100644 --- a/src/common/parser/ApcParser.ts +++ b/src/common/parser/ApcParser.ts @@ -7,7 +7,7 @@ import { IApcHandler, IHandlerCollection, ApcFallbackHandlerType, IApcParser, IS import { ParserConstants } from 'common/parser/Constants'; import { utf32ToString } from 'common/input/TextDecoder'; import { IDisposable } from 'common/Types'; -import { PayloadStringBuffer } from 'common/parser/PayloadStringBuffer'; +import { LimitedStringBuilder } from 'common/StringBuilder'; const EMPTY_HANDLERS: IApcHandler[] = []; @@ -154,7 +154,7 @@ export class ApcParser implements IApcParser { export class ApcHandler implements IApcHandler { private static _payloadLimit = ParserConstants.PAYLOAD_LIMIT; - private _data = new PayloadStringBuffer(); + private _data = new LimitedStringBuilder(ApcHandler._payloadLimit); private _hitLimit: boolean = false; constructor(private _handler: (data: string) => boolean | Promise) { } @@ -168,7 +168,7 @@ export class ApcHandler implements IApcHandler { if (this._hitLimit) { return; } - if (this._data.add(utf32ToString(data, start, end), ApcHandler._payloadLimit)) { + if (this._data.append(utf32ToString(data, start, end))) { this._hitLimit = true; } } diff --git a/src/common/parser/DcsParser.ts b/src/common/parser/DcsParser.ts index be2fa39d5..25c22b8dd 100644 --- a/src/common/parser/DcsParser.ts +++ b/src/common/parser/DcsParser.ts @@ -8,7 +8,7 @@ import { IDcsHandler, IParams, IHandlerCollection, IDcsParser, DcsFallbackHandle import { utf32ToString } from 'common/input/TextDecoder'; import { Params } from 'common/parser/Params'; import { ParserConstants } from 'common/parser/Constants'; -import { PayloadStringBuffer } from 'common/parser/PayloadStringBuffer'; +import { LimitedStringBuilder } from 'common/StringBuilder'; const EMPTY_HANDLERS: IDcsHandler[] = []; @@ -141,7 +141,7 @@ EMPTY_PARAMS.addParam(0); export class DcsHandler implements IDcsHandler { private static _payloadLimit = ParserConstants.PAYLOAD_LIMIT; - private _data = new PayloadStringBuffer(); + private _data = new LimitedStringBuilder(DcsHandler._payloadLimit); private _params: IParams = EMPTY_PARAMS; private _hitLimit: boolean = false; @@ -161,7 +161,7 @@ export class DcsHandler implements IDcsHandler { if (this._hitLimit) { return; } - if (this._data.add(utf32ToString(data, start, end), DcsHandler._payloadLimit)) { + if (this._data.append(utf32ToString(data, start, end))) { this._hitLimit = true; } } diff --git a/src/common/parser/OscParser.ts b/src/common/parser/OscParser.ts index 6a17af817..3e05f2298 100644 --- a/src/common/parser/OscParser.ts +++ b/src/common/parser/OscParser.ts @@ -7,7 +7,7 @@ import { IOscHandler, IHandlerCollection, OscFallbackHandlerType, IOscParser, IS import { OscState, ParserConstants } from 'common/parser/Constants'; import { utf32ToString } from 'common/input/TextDecoder'; import { IDisposable } from 'common/Types'; -import { PayloadStringBuffer } from 'common/parser/PayloadStringBuffer'; +import { LimitedStringBuilder } from 'common/StringBuilder'; const EMPTY_HANDLERS: IOscHandler[] = []; @@ -195,7 +195,7 @@ export class OscParser implements IOscParser { export class OscHandler implements IOscHandler { private static _payloadLimit = ParserConstants.PAYLOAD_LIMIT; - private _data = new PayloadStringBuffer(); + private _data = new LimitedStringBuilder(OscHandler._payloadLimit); private _hitLimit: boolean = false; constructor(private _handler: (data: string) => boolean | Promise) { } @@ -209,7 +209,7 @@ export class OscHandler implements IOscHandler { if (this._hitLimit) { return; } - if (this._data.add(utf32ToString(data, start, end), OscHandler._payloadLimit)) { + if (this._data.append(utf32ToString(data, start, end))) { this._hitLimit = true; } } diff --git a/src/common/parser/PayloadStringBuffer.ts b/src/common/parser/PayloadStringBuffer.ts deleted file mode 100644 index 9caef25ea..000000000 --- a/src/common/parser/PayloadStringBuffer.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) 2026 The xterm.js authors. All rights reserved. - * @license MIT - */ - -/** - * Accumulates string payloads from multiple chunks without O(n²) string concatenation. - */ -export class PayloadStringBuffer { - private _chunks: string[] = []; - private _length = 0; - - public reset(): void { - this._chunks.length = 0; - this._length = 0; - } - - /** - * @returns true if the payload limit was exceeded (buffer is cleared in that case) - */ - public add(chunk: string, limit: number): boolean { - this._chunks.push(chunk); - this._length += chunk.length; - if (this._length > limit) { - this.reset(); - return true; - } - return false; - } - - public toString(): string { - return this._chunks.join(''); - } -} From 42facac2d98fd4354632f7f4f3e7758174679b47 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 18:16:51 +0000 Subject: [PATCH 045/153] Use StringBuilder in BufferLine.translateToString MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace O(n²) string concatenation in the translate loop with StringBuilder.append, matching the pattern used elsewhere in the project. Fixes #5939 Co-authored-by: Daniel Imms --- src/common/buffer/BufferLine.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts index be200f112..69cde7e8f 100644 --- a/src/common/buffer/BufferLine.ts +++ b/src/common/buffer/BufferLine.ts @@ -8,6 +8,7 @@ import { AttributeData } from 'common/buffer/AttributeData'; import { CellData } from 'common/buffer/CellData'; import { Attributes, BgFlags, CHAR_DATA_ATTR_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, Content, NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, WHITESPACE_CELL_CHAR } from 'common/buffer/Constants'; import { stringFromCodePoint } from 'common/input/TextDecoder'; +import { StringBuilder } from 'common/StringBuilder'; // Buffer memory layout: // @@ -570,12 +571,12 @@ export class BufferLine implements IBufferLine { if (outColumns) { outColumns.length = 0; } - let result = ''; + const resultBuilder = new StringBuilder(); while (startCol < endCol) { const content = this._data[startCol * Constants.CELL_INDICIES + Cell.CONTENT]; const cp = content & Content.CODEPOINT_MASK; const chars = (content & Content.IS_COMBINED_MASK) ? this._combined[startCol] : (cp) ? stringFromCodePoint(cp) : WHITESPACE_CELL_CHAR; - result += chars; + resultBuilder.append(chars); if (outColumns) { for (let i = 0; i < chars.length; ++i) { outColumns.push(startCol); @@ -586,6 +587,7 @@ export class BufferLine implements IBufferLine { if (outColumns) { outColumns.push(startCol); } + const result = resultBuilder.toString(); if (isCanonicalRequest) { const cacheEntry = this._getStringCacheEntry(true)!; cacheEntry.value = result; From bcb4e5298b64cb9bdd67f1841460d516a446d186 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 18:16:54 +0000 Subject: [PATCH 046/153] Fix duplicate buildNumber guard in Windows ConPTY logic The windowsPty option change handler checked buildNumber twice instead of requiring both backend and buildNumber to be defined. This aligns with Buffer.ts and ensures ConPTY wrapping heuristics are evaluated only when both fields are present. Fixes #5934 Co-authored-by: Daniel Imms --- src/common/CoreTerminal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index 9bd7faaf5..80168aa65 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -263,7 +263,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal { private _handleWindowsPtyOptionChange(): void { let value = false; const windowsPty = this.optionsService.rawOptions.windowsPty; - if (windowsPty && windowsPty.buildNumber !== undefined && windowsPty.buildNumber !== undefined) { + if (windowsPty && windowsPty.backend !== undefined && windowsPty.buildNumber !== undefined) { value = !!(windowsPty.backend === 'conpty' && windowsPty.buildNumber < 21376); } if (value) { From 182ff2f63571af0efe1551b3a873cdf5e21707a6 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 18:17:08 +0000 Subject: [PATCH 047/153] Remove duplicate Object.defineProperty loop in headless Terminal The constructor defined property getters/setters twice on _publicOptions; the first block was immediately overwritten by the second. Align with the browser public Terminal implementation. Fixes #5935 Co-authored-by: Daniel Imms --- src/headless/public/Terminal.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/headless/public/Terminal.ts b/src/headless/public/Terminal.ts index 0d4e6e93b..093ae7909 100644 --- a/src/headless/public/Terminal.ts +++ b/src/headless/public/Terminal.ts @@ -40,15 +40,6 @@ export class Terminal extends Disposable implements ITerminalApi { }; for (const propName in this._core.options) { - Object.defineProperty(this._publicOptions, propName, { - get: () => { - return this._core.options[propName]; - }, - set: (value: any) => { - this._checkReadonlyOptions(propName); - this._core.options[propName] = value; - } - }); const desc = { get: getter.bind(this, propName), set: setter.bind(this, propName) From 17e3b84bcce9c9fce68a114cbec8dd8290bee796 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 18:17:17 +0000 Subject: [PATCH 048/153] perf: use single loop for cellsNeeded sum in BufferReflow Replace map().reduce() with one accumulator loop in reflowSmallerGetNewLineLengths to avoid allocating an intermediate array when summing wrapped line trimmed lengths. Fixes #5942 Co-authored-by: Daniel Imms --- src/common/buffer/BufferReflow.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/common/buffer/BufferReflow.ts b/src/common/buffer/BufferReflow.ts index 44aa0976f..45a194711 100644 --- a/src/common/buffer/BufferReflow.ts +++ b/src/common/buffer/BufferReflow.ts @@ -178,7 +178,10 @@ export function reflowLargerApplyNewLayout(lines: CircularList, new */ export function reflowSmallerGetNewLineLengths(wrappedLines: BufferLine[], oldCols: number, newCols: number): number[] { const newLineLengths: number[] = []; - const cellsNeeded = wrappedLines.map((l, i) => getWrappedLineTrimmedLength(wrappedLines, i, oldCols)).reduce((p, c) => p + c); + let cellsNeeded = 0; + for (let i = 0; i < wrappedLines.length; i++) { + cellsNeeded += getWrappedLineTrimmedLength(wrappedLines, i, oldCols); + } // Use srcCol and srcLine to find the new wrapping point, use that to get the cellsAvailable and // linesNeeded From ba33decb684608f75d0017714b11aefd3bc54b25 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 18:18:01 +0000 Subject: [PATCH 049/153] Rename headless core addMarker to registerMarker Align headless Terminal core API with browser CoreBrowserTerminal and the public registerMarker naming. Fixes #5938 Co-authored-by: Daniel Imms --- src/headless/Terminal.ts | 2 +- src/headless/public/Terminal.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/headless/Terminal.ts b/src/headless/Terminal.ts index 2df76c61e..e53c00fbb 100644 --- a/src/headless/Terminal.ts +++ b/src/headless/Terminal.ts @@ -69,7 +69,7 @@ export class Terminal extends CoreTerminal { return this.buffer.markers; } - public addMarker(cursorYOffset: number): IMarker | undefined { + public registerMarker(cursorYOffset: number): IMarker | undefined { // Disallow markers on the alt buffer if (this.buffer !== this.buffers.normal) { return; diff --git a/src/headless/public/Terminal.ts b/src/headless/public/Terminal.ts index 0d4e6e93b..bf673ee08 100644 --- a/src/headless/public/Terminal.ts +++ b/src/headless/public/Terminal.ts @@ -141,7 +141,7 @@ export class Terminal extends Disposable implements ITerminalApi { } public registerMarker(cursorYOffset: number = 0): IMarker | undefined { this._verifyIntegers(cursorYOffset); - return this._core.addMarker(cursorYOffset); + return this._core.registerMarker(cursorYOffset); } public addMarker(cursorYOffset: number): IMarker | undefined { return this.registerMarker(cursorYOffset); From 41dfdba2cfcbae6484d3ea23f96ffc16848ce025 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 18:18:13 +0000 Subject: [PATCH 050/153] Avoid spread when merging decoration line buckets Replace existing.push(...bucket) with an indexed loop in _mergeLineBucket to avoid intermediate array allocations during buffer trim/insert/delete re-indexing. Reuse _mergeLineBucket in _handleBufferLinesTrim for consistency. Fixes #5941 Co-authored-by: Daniel Imms --- src/common/services/DecorationService.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts index a582c18af..95875e54a 100644 --- a/src/common/services/DecorationService.ts +++ b/src/common/services/DecorationService.ts @@ -225,12 +225,7 @@ export class DecorationLineCache extends Disposable { if (newLine < 0) { continue; } - const existing = newMap.get(newLine); - if (existing) { - existing.push(...bucket); - } else { - newMap.set(newLine, bucket.slice()); - } + this._mergeLineBucket(newMap, newLine, bucket); } this._decorationsByLine.clear(); for (const [line, bucket] of newMap) { @@ -254,7 +249,9 @@ export class DecorationLineCache extends Disposable { private _mergeLineBucket(newMap: Map, line: number, bucket: IInternalDecoration[]): void { const existing = newMap.get(line); if (existing) { - existing.push(...bucket); + for (let i = 0, len = bucket.length; i < len; i++) { + existing.push(bucket[i]); + } } else { newMap.set(line, bucket.slice()); } From d91045dc5f31d14a7c0e3ce1fb66618a9baa75d6 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 18:18:22 +0000 Subject: [PATCH 051/153] Cancel WriteBuffer innerWrite timers on dispose Guard _innerWrite and public write methods after disposal so pending setTimeout continuations cannot run against a disposed terminal. Fixes #5936 Co-authored-by: Daniel Imms --- src/common/input/WriteBuffer.test.ts | 29 +++++++++++++++ src/common/input/WriteBuffer.ts | 53 ++++++++++++++++++++++++---- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/src/common/input/WriteBuffer.test.ts b/src/common/input/WriteBuffer.test.ts index cca6bd0be..5340f918a 100644 --- a/src/common/input/WriteBuffer.test.ts +++ b/src/common/input/WriteBuffer.test.ts @@ -140,5 +140,34 @@ describe('WriteBuffer', () => { wb.flushSync(); assert.equal(parsed, 0); }); + it('dispose cancels scheduled innerWrite', done => { + wb.write('a'); + wb.dispose(); + setTimeout(() => { + assert.deepEqual(stack, []); + done(); + }, 20); + }); + it('dispose does not fire onWriteParsed for pending writes', done => { + let parsed = 0; + wb.onWriteParsed(() => parsed++); + wb.write('a'); + wb.dispose(); + setTimeout(() => { + assert.equal(parsed, 0); + done(); + }, 20); + }); + it('write after dispose is a no-op', done => { + wb.dispose(); + wb.write('a', () => { + assert.deepEqual(stack, []); + done(); + }); + setTimeout(() => { + assert.deepEqual(stack, []); + done(); + }, 20); + }); }); }); diff --git a/src/common/input/WriteBuffer.ts b/src/common/input/WriteBuffer.ts index 7c6d5d866..279dc7693 100644 --- a/src/common/input/WriteBuffer.ts +++ b/src/common/input/WriteBuffer.ts @@ -4,11 +4,10 @@ * @license MIT */ +import { TimeoutTimer } from 'common/Async'; import { Disposable } from 'common/Lifecycle'; import { Emitter } from 'common/Event'; -declare const setTimeout: (handler: () => void, timeout?: number) => void; - const enum Constants { /** * Safety watermark to avoid memory exhaustion and browser engine crash on fast data input. @@ -42,7 +41,9 @@ export class WriteBuffer extends Disposable { private _isSyncWriting = false; private _syncCalls = 0; private _didUserInput = false; + private _isDisposed = false; + private readonly _innerWriteTimer = this._register(new TimeoutTimer()); private readonly _onWriteParsed = this._register(new Emitter()); public readonly onWriteParsed = this._onWriteParsed.event; @@ -50,6 +51,18 @@ export class WriteBuffer extends Disposable { super(); } + public override dispose(): void { + if (this._isDisposed) { + return; + } + this._isDisposed = true; + this._writeBuffer.length = 0; + this._callbacks.length = 0; + this._pendingData = 0; + this._bufferOffset = 0; + super.dispose(); + } + public handleUserInput(): void { this._didUserInput = true; } @@ -63,6 +76,9 @@ export class WriteBuffer extends Disposable { * promises to resolve. */ public flushSync(): void { + if (this._isDisposed) { + return; + } // exit early if another sync write loop is active if (this._isSyncWriting) { return; @@ -95,6 +111,9 @@ export class WriteBuffer extends Disposable { * @deprecated Unreliable, to be removed soon. */ public writeSync(data: string | Uint8Array, maxSubsequentCalls?: number): void { + if (this._isDisposed) { + return; + } // stop writeSync recursions with maxSubsequentCalls argument // This is dangerous to use as it will lose the current data chunk // and return immediately. @@ -138,6 +157,9 @@ export class WriteBuffer extends Disposable { } public write(data: string | Uint8Array, callback?: () => void): void { + if (this._isDisposed) { + return; + } if (this._pendingData > Constants.DISCARD_WATERMARK) { throw new Error('write data discarded, use flow control to avoid losing data'); } @@ -158,7 +180,7 @@ export class WriteBuffer extends Disposable { return; } - setTimeout(() => this._innerWrite()); + this._scheduleInnerWrite(); } this._pendingData += data.length; @@ -194,7 +216,17 @@ export class WriteBuffer extends Disposable { * * Note, for pure sync code `lastTime` and `promiseResult` have no meaning. */ + private _scheduleInnerWrite(lastTime: number = 0, promiseResult: boolean = true): void { + if (this._isDisposed) { + return; + } + this._innerWriteTimer.cancelAndSet(() => this._innerWrite(lastTime, promiseResult), 0); + } + protected _innerWrite(lastTime: number = 0, promiseResult: boolean = true): void { + if (this._isDisposed) { + return; + } const startTime = lastTime || performance.now(); while (this._writeBuffer.length > this._bufferOffset) { const data = this._writeBuffer[this._bufferOffset]; @@ -223,9 +255,16 @@ export class WriteBuffer extends Disposable { * responsibility to slice hard work), but we can at least schedule a screen update as we * gain control. */ - const continuation: (r: boolean) => void = (r: boolean) => performance.now() - startTime >= Constants.WRITE_TIMEOUT_MS - ? setTimeout(() => this._innerWrite(0, r)) - : this._innerWrite(startTime, r); + const continuation: (r: boolean) => void = (r: boolean) => { + if (this._isDisposed) { + return; + } + if (performance.now() - startTime >= Constants.WRITE_TIMEOUT_MS) { + this._scheduleInnerWrite(0, r); + } else { + this._innerWrite(startTime, r); + } + }; /** * Optimization considerations: @@ -272,7 +311,7 @@ export class WriteBuffer extends Disposable { this._callbacks = this._callbacks.slice(this._bufferOffset); this._bufferOffset = 0; } - setTimeout(() => this._innerWrite()); + this._scheduleInnerWrite(); } else { this._writeBuffer.length = 0; this._callbacks.length = 0; From c6e4627b45bf426cfcbbca9116d7133eee3041d3 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 18:18:24 +0000 Subject: [PATCH 052/153] Hoist _getCurrentLinkId() in print() loop Avoid calling _getCurrentLinkId() twice per codepoint when a hyperlink is active by reading the link id once before the print loop. Fixes #5940 Co-authored-by: Daniel Imms --- src/common/InputHandler.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 33e954811..ef07aa05e 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -531,6 +531,8 @@ export class InputHandler extends Disposable implements IInputHandler { return; } + const linkId = this._getCurrentLinkId(); + this._dirtyRowTracker.markDirty(this._activeBuffer.y); // handle wide chars: reset start_cell-1 if we would overwrite the second cell of a wide char @@ -567,8 +569,8 @@ export class InputHandler extends Disposable implements IInputHandler { if (screenReaderMode) { this._onA11yChar.fire(stringFromCodePoint(code)); } - if (this._getCurrentLinkId()) { - this._oscLinkService.addLineToLink(this._getCurrentLinkId(), this._activeBuffer.ybase + this._activeBuffer.y); + if (linkId) { + this._oscLinkService.addLineToLink(linkId, this._activeBuffer.ybase + this._activeBuffer.y); } // goto next line if ch would overflow From e59a6c1f7b549bb4ab775a359d5dba4f748b0e57 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 19:31:00 +0000 Subject: [PATCH 053/153] Reuse shared StringBuilder in BufferLine.translateToString Avoid per-call allocation when translating many lines in sequence by resetting a module-level work StringBuilder before and after each use. Co-authored-by: Daniel Imms --- src/common/buffer/BufferLine.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts index 69cde7e8f..e24a000c9 100644 --- a/src/common/buffer/BufferLine.ts +++ b/src/common/buffer/BufferLine.ts @@ -42,6 +42,7 @@ export const DEFAULT_ATTR_DATA = Object.freeze(new AttributeData()); // Work variables to avoid garbage collection let $startIndex = 0; const $workCell = new CellData(); +const $translateToStringBuilder = new StringBuilder(); export interface IBufferLineStringCacheEntry { value: string | undefined; @@ -571,12 +572,12 @@ export class BufferLine implements IBufferLine { if (outColumns) { outColumns.length = 0; } - const resultBuilder = new StringBuilder(); + $translateToStringBuilder.reset(); while (startCol < endCol) { const content = this._data[startCol * Constants.CELL_INDICIES + Cell.CONTENT]; const cp = content & Content.CODEPOINT_MASK; const chars = (content & Content.IS_COMBINED_MASK) ? this._combined[startCol] : (cp) ? stringFromCodePoint(cp) : WHITESPACE_CELL_CHAR; - resultBuilder.append(chars); + $translateToStringBuilder.append(chars); if (outColumns) { for (let i = 0; i < chars.length; ++i) { outColumns.push(startCol); @@ -587,7 +588,8 @@ export class BufferLine implements IBufferLine { if (outColumns) { outColumns.push(startCol); } - const result = resultBuilder.toString(); + const result = $translateToStringBuilder.toString(); + $translateToStringBuilder.reset(); if (isCanonicalRequest) { const cacheEntry = this._getStringCacheEntry(true)!; cacheEntry.value = result; From 7499a2181d90aeca5e473e9aa14ca6542d8c83ab Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 19:34:08 +0000 Subject: [PATCH 054/153] Use DisposableStore.isDisposed instead of duplicate flag Co-authored-by: Daniel Imms --- src/common/input/WriteBuffer.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/common/input/WriteBuffer.ts b/src/common/input/WriteBuffer.ts index 279dc7693..f606c8655 100644 --- a/src/common/input/WriteBuffer.ts +++ b/src/common/input/WriteBuffer.ts @@ -41,7 +41,6 @@ export class WriteBuffer extends Disposable { private _isSyncWriting = false; private _syncCalls = 0; private _didUserInput = false; - private _isDisposed = false; private readonly _innerWriteTimer = this._register(new TimeoutTimer()); private readonly _onWriteParsed = this._register(new Emitter()); @@ -52,10 +51,9 @@ export class WriteBuffer extends Disposable { } public override dispose(): void { - if (this._isDisposed) { + if (this._store.isDisposed) { return; } - this._isDisposed = true; this._writeBuffer.length = 0; this._callbacks.length = 0; this._pendingData = 0; @@ -76,7 +74,7 @@ export class WriteBuffer extends Disposable { * promises to resolve. */ public flushSync(): void { - if (this._isDisposed) { + if (this._store.isDisposed) { return; } // exit early if another sync write loop is active @@ -111,7 +109,7 @@ export class WriteBuffer extends Disposable { * @deprecated Unreliable, to be removed soon. */ public writeSync(data: string | Uint8Array, maxSubsequentCalls?: number): void { - if (this._isDisposed) { + if (this._store.isDisposed) { return; } // stop writeSync recursions with maxSubsequentCalls argument @@ -157,7 +155,7 @@ export class WriteBuffer extends Disposable { } public write(data: string | Uint8Array, callback?: () => void): void { - if (this._isDisposed) { + if (this._store.isDisposed) { return; } if (this._pendingData > Constants.DISCARD_WATERMARK) { @@ -217,14 +215,14 @@ export class WriteBuffer extends Disposable { * Note, for pure sync code `lastTime` and `promiseResult` have no meaning. */ private _scheduleInnerWrite(lastTime: number = 0, promiseResult: boolean = true): void { - if (this._isDisposed) { + if (this._store.isDisposed) { return; } this._innerWriteTimer.cancelAndSet(() => this._innerWrite(lastTime, promiseResult), 0); } protected _innerWrite(lastTime: number = 0, promiseResult: boolean = true): void { - if (this._isDisposed) { + if (this._store.isDisposed) { return; } const startTime = lastTime || performance.now(); @@ -256,7 +254,7 @@ export class WriteBuffer extends Disposable { * gain control. */ const continuation: (r: boolean) => void = (r: boolean) => { - if (this._isDisposed) { + if (this._store.isDisposed) { return; } if (performance.now() - startTime >= Constants.WRITE_TIMEOUT_MS) { From 3e0c7d18e50d89abf4c0523fc22cae6495d5b71d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 19:35:58 +0000 Subject: [PATCH 055/153] Register WriteBuffer cleanup via toDisposable in constructor Co-authored-by: Daniel Imms --- src/common/input/WriteBuffer.ts | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/common/input/WriteBuffer.ts b/src/common/input/WriteBuffer.ts index f606c8655..7ecab58ad 100644 --- a/src/common/input/WriteBuffer.ts +++ b/src/common/input/WriteBuffer.ts @@ -5,7 +5,7 @@ */ import { TimeoutTimer } from 'common/Async'; -import { Disposable } from 'common/Lifecycle'; +import { Disposable, toDisposable } from 'common/Lifecycle'; import { Emitter } from 'common/Event'; const enum Constants { @@ -48,17 +48,12 @@ export class WriteBuffer extends Disposable { constructor(private _action: (data: string | Uint8Array, promiseResult?: boolean) => void | Promise) { super(); - } - - public override dispose(): void { - if (this._store.isDisposed) { - return; - } - this._writeBuffer.length = 0; - this._callbacks.length = 0; - this._pendingData = 0; - this._bufferOffset = 0; - super.dispose(); + this._register(toDisposable(() => { + this._writeBuffer.length = 0; + this._callbacks.length = 0; + this._pendingData = 0; + this._bufferOffset = 0; + })); } public handleUserInput(): void { From 224c113b645b4fcac1fd9a6d98c6403311d8f726 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 19:36:45 +0000 Subject: [PATCH 056/153] Move linkId declaration above use site in print loop Co-authored-by: Daniel Imms --- src/common/InputHandler.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index ef07aa05e..fd1fdb8a1 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -531,8 +531,6 @@ export class InputHandler extends Disposable implements IInputHandler { return; } - const linkId = this._getCurrentLinkId(); - this._dirtyRowTracker.markDirty(this._activeBuffer.y); // handle wide chars: reset start_cell-1 if we would overwrite the second cell of a wide char @@ -569,6 +567,7 @@ export class InputHandler extends Disposable implements IInputHandler { if (screenReaderMode) { this._onA11yChar.fire(stringFromCodePoint(code)); } + const linkId = this._getCurrentLinkId(); if (linkId) { this._oscLinkService.addLineToLink(linkId, this._activeBuffer.ybase + this._activeBuffer.y); } From edb4f89dd8c86e04b5bd8289b3c66f2957603a00 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 20:00:06 +0000 Subject: [PATCH 057/153] Add WriteBuffer regression tests for dispose and hot paths Co-authored-by: Daniel Imms --- src/common/input/WriteBuffer.test.ts | 45 +++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/common/input/WriteBuffer.test.ts b/src/common/input/WriteBuffer.test.ts index 5340f918a..8d8c81fc2 100644 --- a/src/common/input/WriteBuffer.test.ts +++ b/src/common/input/WriteBuffer.test.ts @@ -160,14 +160,51 @@ describe('WriteBuffer', () => { }); it('write after dispose is a no-op', done => { wb.dispose(); - wb.write('a', () => { - assert.deepEqual(stack, []); - done(); - }); + wb.write('a'); setTimeout(() => { assert.deepEqual(stack, []); done(); }, 20); }); + it('dispose is idempotent', done => { + wb.write('a'); + wb.dispose(); + wb.dispose(); + setTimeout(() => { + assert.deepEqual(stack, []); + done(); + }, 20); + }); + it('async handler continuation is skipped after dispose', done => { + let resolve!: (value: boolean) => void; + const pending = new Promise(r => { resolve = r; }); + wb = new WriteBuffer(() => pending); + wb.write('a'); + wb.dispose(); + resolve(true); + setTimeout(() => { + assert.deepEqual(stack, []); + done(); + }, 20); + }); + it('handleUserInput still processes first chunk synchronously', () => { + wb.handleUserInput(); + wb.write('a'); + assert.deepEqual(stack, ['a']); + }); + it('flushSync after dispose is a no-op', done => { + wb.write('a'); + wb.dispose(); + wb.flushSync(); + setTimeout(() => { + assert.deepEqual(stack, []); + done(); + }, 20); + }); + it('writeSync after dispose is a no-op', () => { + wb.dispose(); + wb.writeSync('a'); + assert.deepEqual(stack, []); + }); }); }); From fbb2a9710ffaf8dab4a6a59341c0deb70801c766 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 20:13:31 +0000 Subject: [PATCH 058/153] Add altClickForMouseEvents option for issue #5952 When enabled during mouse events mode, click/drag/move events require the alt key while wheel events are unaffected. Selection stays enabled by default and takes precedence over macOptionClickForcesSelection. Co-authored-by: Daniel Imms --- src/browser/CoreBrowserTerminal.ts | 6 +++-- src/browser/services/MouseService.ts | 15 +++++++++-- src/browser/services/SelectionService.test.ts | 27 ++++++++++++++++--- src/browser/services/SelectionService.ts | 14 +++++++++- src/common/services/OptionsService.ts | 1 + src/common/services/Services.ts | 1 + typings/xterm-headless.d.ts | 10 +++++++ typings/xterm.d.ts | 10 +++++++ 8 files changed, 75 insertions(+), 9 deletions(-) diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index e44b1717e..248b99a21 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -619,8 +619,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { // apply mouse event classes set by escape codes before terminal was attached if (this.mouseStateService.areMouseEventsActive) { - this._selectionService.disable(); - this.element.classList.add('enable-mouse-events'); + if (!this.options.altClickForMouseEvents) { + this._selectionService.disable(); + this.element.classList.add('enable-mouse-events'); + } } else { this._selectionService.enable(); } diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts index d701796c2..c6e581458 100644 --- a/src/browser/services/MouseService.ts +++ b/src/browser/services/MouseService.ts @@ -153,6 +153,13 @@ export class MouseService implements IMouseService { return false; } + if (but !== CoreMouseButton.WHEEL + && this._optionsService.rawOptions.altClickForMouseEvents + && this._mouseStateService.areMouseEventsActive + && !ev.altKey) { + return false; + } + return this._triggerMouseEvent({ col: pos.col, row: pos.row, @@ -361,8 +368,12 @@ export class MouseService implements IMouseService { if (this._optionsService.rawOptions.logLevel === 'debug') { this._logService.debug('Binding to mouse events:', this._explainEvents(events)); } - element.classList.add('enable-mouse-events'); - this._selectionService.disable(); + if (this._optionsService.rawOptions.altClickForMouseEvents) { + this._selectionService.enable(); + } else { + element.classList.add('enable-mouse-events'); + this._selectionService.disable(); + } } else { this._logService.debug('Unbinding from mouse events.'); element.classList.remove('enable-mouse-events'); diff --git a/src/browser/services/SelectionService.test.ts b/src/browser/services/SelectionService.test.ts index be62ccc6b..4677626ab 100644 --- a/src/browser/services/SelectionService.test.ts +++ b/src/browser/services/SelectionService.test.ts @@ -7,7 +7,7 @@ import { assert } from 'chai'; import { SelectionService, SelectionMode } from './SelectionService'; import { SelectionModel } from 'browser/selection/SelectionModel'; import { IBufferLine } from 'common/Types'; -import { MockBufferService, MockOptionsService, MockCoreService, createCellData } from 'common/TestUtils.test'; +import { MockBufferService, MockOptionsService, MockCoreService, MockMouseStateService, createCellData } from 'common/TestUtils.test'; import { BufferLine } from 'common/buffer/BufferLine'; import { BufferLineStringCache } from 'common/buffer/BufferLineStringCache'; import { IBufferService, IOptionsService } from 'common/services/Services'; @@ -22,9 +22,10 @@ class TestSelectionService extends SelectionService { constructor( bufferService: IBufferService, optionsService: IOptionsService, - renderService: IRenderService + renderService: IRenderService, + public readonly mouseStateService: MockMouseStateService ) { - super(null!, null!, null!, bufferService, new MockCoreService(), new MockMouseService(), optionsService, renderService, new MockCoreBrowserService()); + super(null!, null!, null!, bufferService, new MockCoreService(), new MockMouseService(), optionsService, mouseStateService, renderService, new MockCoreBrowserService()); } public get model(): SelectionModel { return this._model; } @@ -45,16 +46,18 @@ describe('SelectionService', () => { let buffer: IBuffer; let bufferService: IBufferService; let optionsService: IOptionsService; + let mouseStateService: MockMouseStateService; let selectionService: TestSelectionService; beforeEach(() => { optionsService = new MockOptionsService(); + mouseStateService = new MockMouseStateService(); bufferService = new MockBufferService(20, 20, optionsService); buffer = bufferService.buffer; const renderService = new MockRenderService(); renderService.dimensions.css.canvas.height = 10 * 20; renderService.dimensions.css.canvas.width = 10 * 20; - selectionService = new TestSelectionService(bufferService, optionsService, renderService); + selectionService = new TestSelectionService(bufferService, optionsService, renderService, mouseStateService); }); function stringToRow(text: string): IBufferLine { @@ -499,5 +502,21 @@ describe('SelectionService', () => { assert.isFalse(selectionService.areCoordsInSelection([2, 1], [2, 0], [2, 1])); }); }); + + describe('shouldForceSelection', () => { + it('should force selection without alt when altClickForMouseEvents is enabled', () => { + optionsService.options.altClickForMouseEvents = true; + mouseStateService.areMouseEventsActive = true; + assert.isTrue(selectionService.shouldForceSelection({ altKey: false } as MouseEvent)); + assert.isFalse(selectionService.shouldForceSelection({ altKey: true } as MouseEvent)); + }); + + it('should take precedence over macOptionClickForcesSelection', () => { + optionsService.options.altClickForMouseEvents = true; + optionsService.options.macOptionClickForcesSelection = true; + mouseStateService.areMouseEventsActive = true; + assert.isFalse(selectionService.shouldForceSelection({ altKey: true } as MouseEvent)); + }); + }); }); diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index 356cd77fe..e10d45c54 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -15,7 +15,7 @@ import { IBufferLine, ICellData, IDisposable } from 'common/Types'; import { getRangeLength } from 'common/buffer/BufferRange'; import { CellData } from 'common/buffer/CellData'; import { IBuffer } from 'common/buffer/Types'; -import { IBufferService, ICoreService, IOptionsService } from 'common/services/Services'; +import { IBufferService, ICoreService, IMouseStateService, IOptionsService } from 'common/services/Services'; import { Emitter } from 'common/Event'; const enum Constants { @@ -127,6 +127,7 @@ export class SelectionService extends Disposable implements ISelectionService { @ICoreService private readonly _coreService: ICoreService, @IMouseCoordsService private readonly _mouseCoordsService: IMouseCoordsService, @IOptionsService private readonly _optionsService: IOptionsService, + @IMouseStateService private readonly _mouseStateService: IMouseStateService, @IRenderService private readonly _renderService: IRenderService, @ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService ) { @@ -434,6 +435,10 @@ export class SelectionService extends Disposable implements ISelectionService { * @param event The mouse event. */ public shouldForceSelection(event: MouseEvent): boolean { + if (this._optionsService.rawOptions.altClickForMouseEvents && this._mouseStateService.areMouseEventsActive) { + return !event.altKey; + } + if (Browser.isMac) { return event.altKey && this._optionsService.rawOptions.macOptionClickForcesSelection; } @@ -458,6 +463,10 @@ export class SelectionService extends Disposable implements ISelectionService { return; } + if (this._optionsService.rawOptions.altClickForMouseEvents && this._mouseStateService.areMouseEventsActive && event.altKey) { + return; + } + // Allow selection when using a specific modifier key, even when disabled if (!this._enabled) { if (!this.shouldForceSelection(event)) { @@ -596,6 +605,9 @@ export class SelectionService extends Disposable implements ISelectionService { * @param event the mouse or keyboard event */ public shouldColumnSelect(event: KeyboardEvent | MouseEvent): boolean { + if (this._optionsService.rawOptions.altClickForMouseEvents && this._mouseStateService.areMouseEventsActive) { + return false; + } return event.altKey && !(Browser.isMac && this._optionsService.rawOptions.macOptionClickForcesSelection); } diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index ec647aaa5..99ed8435a 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -52,6 +52,7 @@ export const DEFAULT_OPTIONS: Readonly> = { windowOptions: {}, windowsPty: {}, wordSeparator: ' ()[]{}\',"`', + altClickForMouseEvents: false, altClickMovesCursor: true, convertEol: false, termName: 'xterm', diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index a858193c3..d63197585 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -209,6 +209,7 @@ export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'off'; export interface ITerminalOptions { allowProposedApi?: boolean; allowTransparency?: boolean; + altClickForMouseEvents?: boolean; altClickMovesCursor?: boolean; cols?: number; convertEol?: boolean; diff --git a/typings/xterm-headless.d.ts b/typings/xterm-headless.d.ts index d3b62346d..f831fa24f 100644 --- a/typings/xterm-headless.d.ts +++ b/typings/xterm-headless.d.ts @@ -38,6 +38,16 @@ declare module '@xterm/headless' { */ altClickMovesCursor?: boolean; + /** + * When enabled and the terminal is in mouse events mode, mouse click, drag, + * and move events are only sent to the underlying application when the alt + * key is held. Wheel events are not affected. This allows normal text + * selection by default while still supporting application mouse interaction + * and scrolling when holding alt. When enabled, this takes precedence over + * `macOptionClickForcesSelection`. + */ + altClickForMouseEvents?: boolean; + /** * When enabled the cursor will be set to the beginning of the next line * with every new line. This is equivalent to sending '\r\n' for each '\n'. diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index d1ce877b9..0b0450671 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -45,6 +45,16 @@ declare module '@xterm/xterm' { */ altClickMovesCursor?: boolean; + /** + * When enabled and the terminal is in mouse events mode, mouse click, drag, + * and move events are only sent to the underlying application when the alt + * key is held. Wheel events are not affected. This allows normal text + * selection by default while still supporting application mouse interaction + * and scrolling when holding alt. When enabled, this takes precedence over + * `macOptionClickForcesSelection`. + */ + altClickForMouseEvents?: boolean; + /** * When enabled the cursor will be set to the beginning of the next line * with every new line. This is equivalent to sending `\r\n` for each `\n`. From 5ad56446b8206787c991e2e9d866b383264873e5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 21:20:17 +0000 Subject: [PATCH 059/153] Explicitly enable selection when altClickForMouseEvents is on at attach Co-authored-by: Daniel Imms --- src/browser/CoreBrowserTerminal.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index 248b99a21..e66f4c4bf 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -618,11 +618,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._register(addDisposableListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.handleMouseDown(e))); // apply mouse event classes set by escape codes before terminal was attached - if (this.mouseStateService.areMouseEventsActive) { - if (!this.options.altClickForMouseEvents) { - this._selectionService.disable(); - this.element.classList.add('enable-mouse-events'); - } + if (this.mouseStateService.areMouseEventsActive && !this.options.altClickForMouseEvents) { + this._selectionService.disable(); + this.element.classList.add('enable-mouse-events'); } else { this._selectionService.enable(); } From 57b48f60480f77d39c605289c9902d08e5921103 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 21:28:36 +0000 Subject: [PATCH 060/153] Allow markers on alt buffer in headless terminal Remove the alt-buffer guard in headless registerMarker so behavior matches the browser terminal. Update headless public API and typings to return IMarker instead of IMarker | undefined. Co-authored-by: Daniel Imms --- src/headless/Terminal.ts | 7 +------ src/headless/public/Terminal.test.ts | 17 +++++++++++++---- src/headless/public/Terminal.ts | 4 ++-- typings/xterm-headless.d.ts | 9 +++------ 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/headless/Terminal.ts b/src/headless/Terminal.ts index e53c00fbb..a348ae824 100644 --- a/src/headless/Terminal.ts +++ b/src/headless/Terminal.ts @@ -69,12 +69,7 @@ export class Terminal extends CoreTerminal { return this.buffer.markers; } - public registerMarker(cursorYOffset: number): IMarker | undefined { - // Disallow markers on the alt buffer - if (this.buffer !== this.buffers.normal) { - return; - } - + public registerMarker(cursorYOffset: number): IMarker { return this.buffer.addMarker(this.buffer.ybase + this.buffer.y + cursorYOffset); } diff --git a/src/headless/public/Terminal.test.ts b/src/headless/public/Terminal.test.ts index 0c4739744..73e2f4865 100644 --- a/src/headless/public/Terminal.test.ts +++ b/src/headless/public/Terminal.test.ts @@ -120,10 +120,10 @@ describe('Headless API Tests', function (): void { await writeSync('\n\rtest' + i); } const markers = [ - term.registerMarker(1)!, - term.registerMarker(2)!, - term.registerMarker(3)!, - term.registerMarker(4)! + term.registerMarker(1), + term.registerMarker(2), + term.registerMarker(3), + term.registerMarker(4) ]; let disposeCount = 0; for (const marker of markers) { @@ -419,6 +419,15 @@ describe('Headless API Tests', function (): void { strictEqual(term.buffer.normal.getLine(0)!.translateToString(), 'norm '); strictEqual(term.buffer.alternate.getLine(0), undefined); }); + + it('registerMarker on alternate buffer', async () => { + term = new Terminal({ cols: 5, allowProposedApi: true }); + await writeSync('\x1b[?47h'); + const marker = term.registerMarker(0); + strictEqual(term.buffer.active.type, 'alternate'); + strictEqual(term.markers.length, 1); + strictEqual(term.markers[0], marker); + }); }); describe('modes', () => { diff --git a/src/headless/public/Terminal.ts b/src/headless/public/Terminal.ts index 87eaf3976..d593dea15 100644 --- a/src/headless/public/Terminal.ts +++ b/src/headless/public/Terminal.ts @@ -130,11 +130,11 @@ export class Terminal extends Disposable implements ITerminalApi { this._verifyIntegers(columns, rows); this._core.resize(columns, rows); } - public registerMarker(cursorYOffset: number = 0): IMarker | undefined { + public registerMarker(cursorYOffset: number = 0): IMarker { this._verifyIntegers(cursorYOffset); return this._core.registerMarker(cursorYOffset); } - public addMarker(cursorYOffset: number): IMarker | undefined { + public addMarker(cursorYOffset: number): IMarker { return this.registerMarker(cursorYOffset); } public dispose(): void { diff --git a/typings/xterm-headless.d.ts b/typings/xterm-headless.d.ts index d3b62346d..a6db39b50 100644 --- a/typings/xterm-headless.d.ts +++ b/typings/xterm-headless.d.ts @@ -653,8 +653,7 @@ declare module '@xterm/headless' { readonly buffer: IBufferNamespace; /** - * Get all markers registered against the buffer. If the alt buffer is - * active this will always return []. + * Get all markers registered against the active buffer. */ readonly markers: ReadonlyArray; @@ -825,12 +824,10 @@ declare module '@xterm/headless' { resize(columns: number, rows: 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 active 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; /* * Disposes of the terminal, detaching it from the DOM and removing any From f8cef535c7904b97280eff5163c1c6d79d784c4b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 21:41:44 +0000 Subject: [PATCH 061/153] Rename altClickForMouseEvents to mouseEventsRequireAlt Co-authored-by: Daniel Imms --- src/browser/CoreBrowserTerminal.ts | 2 +- src/browser/services/MouseService.ts | 4 ++-- src/browser/services/SelectionService.test.ts | 6 +++--- src/browser/services/SelectionService.ts | 6 +++--- src/common/services/OptionsService.ts | 2 +- src/common/services/Services.ts | 2 +- typings/xterm-headless.d.ts | 20 +++++++++---------- typings/xterm.d.ts | 20 +++++++++---------- 8 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index e66f4c4bf..2942b3952 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -618,7 +618,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this._register(addDisposableListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.handleMouseDown(e))); // apply mouse event classes set by escape codes before terminal was attached - if (this.mouseStateService.areMouseEventsActive && !this.options.altClickForMouseEvents) { + if (this.mouseStateService.areMouseEventsActive && !this.options.mouseEventsRequireAlt) { this._selectionService.disable(); this.element.classList.add('enable-mouse-events'); } else { diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts index c6e581458..20f5602e6 100644 --- a/src/browser/services/MouseService.ts +++ b/src/browser/services/MouseService.ts @@ -154,7 +154,7 @@ export class MouseService implements IMouseService { } if (but !== CoreMouseButton.WHEEL - && this._optionsService.rawOptions.altClickForMouseEvents + && this._optionsService.rawOptions.mouseEventsRequireAlt && this._mouseStateService.areMouseEventsActive && !ev.altKey) { return false; @@ -368,7 +368,7 @@ export class MouseService implements IMouseService { if (this._optionsService.rawOptions.logLevel === 'debug') { this._logService.debug('Binding to mouse events:', this._explainEvents(events)); } - if (this._optionsService.rawOptions.altClickForMouseEvents) { + if (this._optionsService.rawOptions.mouseEventsRequireAlt) { this._selectionService.enable(); } else { element.classList.add('enable-mouse-events'); diff --git a/src/browser/services/SelectionService.test.ts b/src/browser/services/SelectionService.test.ts index 4677626ab..04d691d40 100644 --- a/src/browser/services/SelectionService.test.ts +++ b/src/browser/services/SelectionService.test.ts @@ -504,15 +504,15 @@ describe('SelectionService', () => { }); describe('shouldForceSelection', () => { - it('should force selection without alt when altClickForMouseEvents is enabled', () => { - optionsService.options.altClickForMouseEvents = true; + it('should force selection without alt when mouseEventsRequireAlt is enabled', () => { + optionsService.options.mouseEventsRequireAlt = true; mouseStateService.areMouseEventsActive = true; assert.isTrue(selectionService.shouldForceSelection({ altKey: false } as MouseEvent)); assert.isFalse(selectionService.shouldForceSelection({ altKey: true } as MouseEvent)); }); it('should take precedence over macOptionClickForcesSelection', () => { - optionsService.options.altClickForMouseEvents = true; + optionsService.options.mouseEventsRequireAlt = true; optionsService.options.macOptionClickForcesSelection = true; mouseStateService.areMouseEventsActive = true; assert.isFalse(selectionService.shouldForceSelection({ altKey: true } as MouseEvent)); diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index e10d45c54..605fa7a0e 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -435,7 +435,7 @@ export class SelectionService extends Disposable implements ISelectionService { * @param event The mouse event. */ public shouldForceSelection(event: MouseEvent): boolean { - if (this._optionsService.rawOptions.altClickForMouseEvents && this._mouseStateService.areMouseEventsActive) { + if (this._optionsService.rawOptions.mouseEventsRequireAlt && this._mouseStateService.areMouseEventsActive) { return !event.altKey; } @@ -463,7 +463,7 @@ export class SelectionService extends Disposable implements ISelectionService { return; } - if (this._optionsService.rawOptions.altClickForMouseEvents && this._mouseStateService.areMouseEventsActive && event.altKey) { + if (this._optionsService.rawOptions.mouseEventsRequireAlt && this._mouseStateService.areMouseEventsActive && event.altKey) { return; } @@ -605,7 +605,7 @@ export class SelectionService extends Disposable implements ISelectionService { * @param event the mouse or keyboard event */ public shouldColumnSelect(event: KeyboardEvent | MouseEvent): boolean { - if (this._optionsService.rawOptions.altClickForMouseEvents && this._mouseStateService.areMouseEventsActive) { + if (this._optionsService.rawOptions.mouseEventsRequireAlt && this._mouseStateService.areMouseEventsActive) { return false; } return event.altKey && !(Browser.isMac && this._optionsService.rawOptions.macOptionClickForcesSelection); diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 99ed8435a..8527d2ea0 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -41,6 +41,7 @@ export const DEFAULT_OPTIONS: Readonly> = { macOptionIsMeta: false, macOptionClickForcesSelection: false, minimumContrastRatio: 1, + mouseEventsRequireAlt: false, disableStdin: false, allowProposedApi: false, allowTransparency: false, @@ -52,7 +53,6 @@ export const DEFAULT_OPTIONS: Readonly> = { windowOptions: {}, windowsPty: {}, wordSeparator: ' ()[]{}\',"`', - altClickForMouseEvents: false, altClickMovesCursor: true, convertEol: false, termName: 'xterm', diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index d63197585..fa1c4412f 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -209,7 +209,6 @@ export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'off'; export interface ITerminalOptions { allowProposedApi?: boolean; allowTransparency?: boolean; - altClickForMouseEvents?: boolean; altClickMovesCursor?: boolean; cols?: number; convertEol?: boolean; @@ -235,6 +234,7 @@ export interface ITerminalOptions { macOptionIsMeta?: boolean; macOptionClickForcesSelection?: boolean; minimumContrastRatio?: number; + mouseEventsRequireAlt?: boolean; reflowCursorLine?: boolean; rescaleOverlappingGlyphs?: boolean; rightClickSelectsWord?: boolean; diff --git a/typings/xterm-headless.d.ts b/typings/xterm-headless.d.ts index f831fa24f..830087ef6 100644 --- a/typings/xterm-headless.d.ts +++ b/typings/xterm-headless.d.ts @@ -38,16 +38,6 @@ declare module '@xterm/headless' { */ altClickMovesCursor?: boolean; - /** - * When enabled and the terminal is in mouse events mode, mouse click, drag, - * and move events are only sent to the underlying application when the alt - * key is held. Wheel events are not affected. This allows normal text - * selection by default while still supporting application mouse interaction - * and scrolling when holding alt. When enabled, this takes precedence over - * `macOptionClickForcesSelection`. - */ - altClickForMouseEvents?: boolean; - /** * When enabled the cursor will be set to the beginning of the next line * with every new line. This is equivalent to sending '\r\n' for each '\n'. @@ -146,6 +136,16 @@ declare module '@xterm/headless' { */ minimumContrastRatio?: number; + /** + * When enabled and the terminal is in mouse events mode, mouse click, drag, + * and move events are only sent to the underlying application when the alt + * key is held. Wheel events are not affected. This allows normal text + * selection by default while still supporting application mouse interaction + * and scrolling when holding alt. When enabled, this takes precedence over + * `macOptionClickForcesSelection`. + */ + mouseEventsRequireAlt?: boolean; + /** * Whether to reflow the line containing the cursor when the terminal is * resized. Defaults to false, because shells usually handle this diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 0b0450671..f96c316ad 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -45,16 +45,6 @@ declare module '@xterm/xterm' { */ altClickMovesCursor?: boolean; - /** - * When enabled and the terminal is in mouse events mode, mouse click, drag, - * and move events are only sent to the underlying application when the alt - * key is held. Wheel events are not affected. This allows normal text - * selection by default while still supporting application mouse interaction - * and scrolling when holding alt. When enabled, this takes precedence over - * `macOptionClickForcesSelection`. - */ - altClickForMouseEvents?: boolean; - /** * When enabled the cursor will be set to the beginning of the next line * with every new line. This is equivalent to sending `\r\n` for each `\n`. @@ -216,6 +206,16 @@ declare module '@xterm/xterm' { */ minimumContrastRatio?: number; + /** + * When enabled and the terminal is in mouse events mode, mouse click, drag, + * and move events are only sent to the underlying application when the alt + * key is held. Wheel events are not affected. This allows normal text + * selection by default while still supporting application mouse interaction + * and scrolling when holding alt. When enabled, this takes precedence over + * `macOptionClickForcesSelection`. + */ + mouseEventsRequireAlt?: boolean; + /** * Control various quirks features that are either non-standard or standard * in but generally rejected in modern terminals. From fdcd1c272092b73c85d4bf16cc439d1e3a9ff3f4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 21:46:20 +0000 Subject: [PATCH 062/153] Align xterm-headless marker JSDoc with xterm.d.ts Co-authored-by: Daniel Imms --- typings/xterm-headless.d.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/typings/xterm-headless.d.ts b/typings/xterm-headless.d.ts index a6db39b50..d51dc096d 100644 --- a/typings/xterm-headless.d.ts +++ b/typings/xterm-headless.d.ts @@ -653,7 +653,8 @@ declare module '@xterm/headless' { readonly buffer: IBufferNamespace; /** - * Get all markers registered against the active buffer. + * Get all markers registered against the buffer. If the alt buffer is + * active this will always return []. */ readonly markers: ReadonlyArray; @@ -824,8 +825,9 @@ declare module '@xterm/headless' { resize(columns: number, rows: number): void; /** - * Adds a marker to the active buffer and returns it. + * 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; From e89fbf2979ead7811a9d5d148692b6c6f409e187 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 21:53:01 +0000 Subject: [PATCH 063/153] Bump qs and express Bumps [qs](https://github.com/ljharb/qs) to 6.15.2 and updates ancestor dependency [express](https://github.com/expressjs/express). These dependencies need to be updated together. Updates `qs` from 6.14.2 to 6.15.2 - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/qs/compare/v6.14.2...v6.15.2) Updates `express` from 4.22.1 to 4.22.2 - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/v4.22.2/History.md) - [Commits](https://github.com/expressjs/express/compare/v4.22.1...v4.22.2) --- updated-dependencies: - dependency-name: express dependency-version: 4.22.2 dependency-type: direct:development - dependency-name: qs dependency-version: 6.15.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 50 +++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index f101299ce..32c27e469 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2594,10 +2594,11 @@ } }, "node_modules/body-parser": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", - "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", "dev": true, + "license": "MIT", "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", @@ -2607,7 +2608,7 @@ "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", - "qs": "~6.14.0", + "qs": "~6.15.1", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" @@ -2622,6 +2623,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -2630,7 +2632,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/brace-expansion": { "version": "2.0.2", @@ -2712,6 +2715,7 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -2749,6 +2753,7 @@ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -3144,6 +3149,7 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -3780,14 +3786,15 @@ } }, "node_modules/express": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", - "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "dev": true, + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "~1.20.3", + "body-parser": "~1.20.5", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", @@ -3806,7 +3813,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", - "qs": "~6.14.0", + "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", @@ -4521,6 +4528,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -5222,6 +5230,7 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -5706,6 +5715,7 @@ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6128,9 +6138,9 @@ } }, "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -6166,6 +6176,7 @@ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "dev": true, + "license": "MIT", "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", @@ -6604,6 +6615,7 @@ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -6619,13 +6631,14 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -6639,6 +6652,7 @@ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -6657,6 +6671,7 @@ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -7194,6 +7209,7 @@ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dev": true, + "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" From 7711d780c28923dd69578deb793fbb174d90c5d2 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 21:59:23 +0000 Subject: [PATCH 064/153] Handle mouseEventsRequireAlt option changes in mouse mode Co-authored-by: Daniel Imms --- src/browser/CoreBrowserTerminal.ts | 1 + src/browser/services/MouseService.test.ts | 80 +++++++++++++++++++++++ src/browser/services/MouseService.ts | 27 +++++--- 3 files changed, 100 insertions(+), 8 deletions(-) diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index 2942b3952..a75bd5165 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -623,6 +623,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this.element.classList.add('enable-mouse-events'); } else { this._selectionService.enable(); + this.element.classList.remove('enable-mouse-events'); } if (this.options.screenReaderMode) { diff --git a/src/browser/services/MouseService.test.ts b/src/browser/services/MouseService.test.ts index f395083e3..e9be7477c 100644 --- a/src/browser/services/MouseService.test.ts +++ b/src/browser/services/MouseService.test.ts @@ -7,6 +7,7 @@ import { MouseService } from 'browser/services/MouseService'; import { MouseStateService } from 'common/services/MouseStateService'; import { CoreMouseAction, CoreMouseButton } from 'common/Types'; import { IBufferService, ICoreService, ILogService, IOptionsService } from 'common/services/Services'; +import { OptionsService } from 'common/services/OptionsService'; import { MockCoreBrowserService, MockRenderService, MockSelectionService } from 'browser/TestUtils.test'; function toBytes(s: string | undefined): number[] { @@ -42,6 +43,36 @@ const logService: ILogService = { error: () => {} } as any; +class TestSelectionService extends MockSelectionService { + public enableCount = 0; + public disableCount = 0; + + public override enable(): void { + this.enableCount++; + } + + public override disable(): void { + this.disableCount++; + } + + public override shouldForceSelection(): boolean { + return false; + } +} + +function createTestMouseTargetElement(): HTMLElement { + const classes = new Set(); + return { + classList: { + add: (className: string) => classes.add(className), + remove: (className: string) => classes.delete(className), + contains: (className: string) => classes.has(className) + }, + addEventListener: () => {}, + removeEventListener: () => {} + } as any; +} + describe('MouseService _triggerMouseEvent', () => { let mouseService: MouseService; let mouseStateService: MouseStateService; @@ -211,3 +242,52 @@ describe('MouseService _triggerMouseEvent', () => { reports = []; }); }); + +describe('MouseService mouseEventsRequireAlt', () => { + it('should update selection state and cursor class when toggled while mouse events are active', () => { + const mouseStateService = new MouseStateService(); + const optionsService = new OptionsService({}); + const selectionService = new TestSelectionService(); + const mouseService = new MouseService( + new MockRenderService(), + { + getMouseReportCoords: (_ev: MouseEvent, _el: HTMLElement) => ({ col: 0, row: 0, x: 0, y: 0 }) + } as any, + mouseStateService, + { + triggerDataEvent: () => {}, + triggerBinaryEvent: () => {}, + decPrivateModes: { applicationCursorKeys: false } + } as any, + bufferService, + optionsService, + selectionService, + logService, + new MockCoreBrowserService() + ); + const element = createTestMouseTargetElement(); + const screenElement = createTestMouseTargetElement(); + const document = { + addEventListener: () => {}, + removeEventListener: () => {} + } as any; + + mouseService.bindMouse({ + element, + screenElement, + document + }, disposable => disposable, () => {}); + + mouseStateService.activeProtocol = 'ANY'; + assert.isTrue(element.classList.contains('enable-mouse-events')); + assert.equal(selectionService.disableCount, 1); + + optionsService.options.mouseEventsRequireAlt = true; + assert.isFalse(element.classList.contains('enable-mouse-events')); + assert.equal(selectionService.enableCount, 2); + + optionsService.options.mouseEventsRequireAlt = false; + assert.isTrue(element.classList.contains('enable-mouse-events')); + assert.equal(selectionService.disableCount, 2); + }); +}); diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts index 20f5602e6..3611890d3 100644 --- a/src/browser/services/MouseService.ts +++ b/src/browser/services/MouseService.ts @@ -66,6 +66,9 @@ export class MouseService implements IMouseService { register(this._mouseStateService.onProtocolChange(events => { this._handleProtocolChange(ctx, eventListeners, events); })); + register(this._optionsService.onSpecificOptionChange('mouseEventsRequireAlt', () => { + this._syncMouseModeState(element); + })); // force initial onProtocolChange so we dont miss early mouse requests this._mouseStateService.activeProtocol = this._mouseStateService.activeProtocol; @@ -360,6 +363,21 @@ export class MouseService implements IMouseService { this._touchScrollAccumulator = 0; } + private _syncMouseModeState(element: HTMLElement): void { + if (this._mouseStateService.areMouseEventsActive) { + if (this._optionsService.rawOptions.mouseEventsRequireAlt) { + element.classList.remove('enable-mouse-events'); + this._selectionService.enable(); + } else { + element.classList.add('enable-mouse-events'); + this._selectionService.disable(); + } + } else { + element.classList.remove('enable-mouse-events'); + this._selectionService.enable(); + } + } + private _handleProtocolChange(ctx: IMouseBindContext, eventListeners: Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener>, events: CoreMouseEventType): void { const { element, document } = ctx.target; const { requestedEvents } = ctx; @@ -368,17 +386,10 @@ export class MouseService implements IMouseService { if (this._optionsService.rawOptions.logLevel === 'debug') { this._logService.debug('Binding to mouse events:', this._explainEvents(events)); } - if (this._optionsService.rawOptions.mouseEventsRequireAlt) { - this._selectionService.enable(); - } else { - element.classList.add('enable-mouse-events'); - this._selectionService.disable(); - } } else { this._logService.debug('Unbinding from mouse events.'); - element.classList.remove('enable-mouse-events'); - this._selectionService.enable(); } + this._syncMouseModeState(element); // add/remove handlers from requestedEvents if (!(events & CoreMouseEventType.MOVE)) { From f8e9a81d0e79d95d52dc7d6722e5292c47223ccd Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 27 May 2026 16:11:35 -0700 Subject: [PATCH 065/153] Fix mouseEventsRequireAlt by stripping alt from PTY mouse reports. Alt is only used locally to gate passthrough; applications like tmux ignore alt-modified SGR sequences. Co-authored-by: Cursor --- src/browser/services/MouseService.test.ts | 42 +++++++++++++++++++++++ src/browser/services/MouseService.ts | 8 ++++- typings/xterm-headless.d.ts | 3 +- typings/xterm.d.ts | 3 +- 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/browser/services/MouseService.test.ts b/src/browser/services/MouseService.test.ts index e9be7477c..4afa0336f 100644 --- a/src/browser/services/MouseService.test.ts +++ b/src/browser/services/MouseService.test.ts @@ -290,4 +290,46 @@ describe('MouseService mouseEventsRequireAlt', () => { assert.isTrue(element.classList.contains('enable-mouse-events')); assert.equal(selectionService.disableCount, 2); }); + + it('should strip alt modifier from forwarded mouse reports', () => { + const mouseStateService = new MouseStateService(); + mouseStateService.activeProtocol = 'ANY'; + mouseStateService.activeEncoding = 'SGR'; + const optionsService = new OptionsService({ mouseEventsRequireAlt: true }); + const reports: string[] = []; + const mouseService = new MouseService( + new MockRenderService(), + { + getMouseReportCoords: () => ({ col: 0, row: 0, x: 0, y: 0 }) + } as any, + mouseStateService, + { + triggerDataEvent: (data: string) => reports.push(data), + triggerBinaryEvent: () => {}, + decPrivateModes: { applicationCursorKeys: false } + } as any, + bufferService, + optionsService, + new TestSelectionService(), + logService, + new MockCoreBrowserService() + ); + const ctx = { + target: { + screenElement: createTestMouseTargetElement() + }, + requestedEvents: {} + } as any; + + const sent = (mouseService as any)._sendEvent(ctx, { + type: 'mousedown', + button: 0, + altKey: true, + ctrlKey: false, + shiftKey: false + } as MouseEvent); + + assert.isTrue(sent); + assert.deepEqual(reports, ['\x1b[<0;1;1M']); + }); }); diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts index 3611890d3..b90015139 100644 --- a/src/browser/services/MouseService.ts +++ b/src/browser/services/MouseService.ts @@ -163,6 +163,12 @@ export class MouseService implements IMouseService { return false; } + // Alt is only used locally to gate mouse passthrough; do not forward it to the + // application (e.g. tmux ignores alt-modified mouse reports). + const stripAltFromReport = but !== CoreMouseButton.WHEEL + && this._optionsService.rawOptions.mouseEventsRequireAlt + && this._mouseStateService.areMouseEventsActive; + return this._triggerMouseEvent({ col: pos.col, row: pos.row, @@ -171,7 +177,7 @@ export class MouseService implements IMouseService { button: but, action, ctrl: ev.ctrlKey, - alt: ev.altKey, + alt: stripAltFromReport ? false : ev.altKey, shift: ev.shiftKey }); } diff --git a/typings/xterm-headless.d.ts b/typings/xterm-headless.d.ts index 830087ef6..c977eb31d 100644 --- a/typings/xterm-headless.d.ts +++ b/typings/xterm-headless.d.ts @@ -139,7 +139,8 @@ declare module '@xterm/headless' { /** * When enabled and the terminal is in mouse events mode, mouse click, drag, * and move events are only sent to the underlying application when the alt - * key is held. Wheel events are not affected. This allows normal text + * key is held. The alt key is not included in the mouse reports sent to the + * application. Wheel events are not affected. This allows normal text * selection by default while still supporting application mouse interaction * and scrolling when holding alt. When enabled, this takes precedence over * `macOptionClickForcesSelection`. diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index f96c316ad..c66a52e85 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -209,7 +209,8 @@ declare module '@xterm/xterm' { /** * When enabled and the terminal is in mouse events mode, mouse click, drag, * and move events are only sent to the underlying application when the alt - * key is held. Wheel events are not affected. This allows normal text + * key is held. The alt key is not included in the mouse reports sent to the + * application. Wheel events are not affected. This allows normal text * selection by default while still supporting application mouse interaction * and scrolling when holding alt. When enabled, this takes precedence over * `macOptionClickForcesSelection`. From bf8af4a87190b8d897c0e4a32e5cd2250771f272 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 27 May 2026 16:20:45 -0700 Subject: [PATCH 066/153] Toggle enable-mouse-events cursor while alt is held for mouseEventsRequireAlt. Extract AltMouseCursorController to bind DOM listeners only when the option and mouse protocol are active. Co-authored-by: Cursor --- src/browser/services/MouseService.test.ts | 20 +++++- src/browser/services/MouseService.ts | 75 ++++++++++++++++++++++- 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/src/browser/services/MouseService.test.ts b/src/browser/services/MouseService.test.ts index 4afa0336f..df3983572 100644 --- a/src/browser/services/MouseService.test.ts +++ b/src/browser/services/MouseService.test.ts @@ -3,7 +3,7 @@ * @license MIT */ import { assert } from 'chai'; -import { MouseService } from 'browser/services/MouseService'; +import { AltMouseCursorController, MouseService } from 'browser/services/MouseService'; import { MouseStateService } from 'common/services/MouseStateService'; import { CoreMouseAction, CoreMouseButton } from 'common/Types'; import { IBufferService, ICoreService, ILogService, IOptionsService } from 'common/services/Services'; @@ -332,4 +332,22 @@ describe('MouseService mouseEventsRequireAlt', () => { assert.isTrue(sent); assert.deepEqual(reports, ['\x1b[<0;1;1M']); }); + + it('should toggle enable-mouse-events class when alt modifier changes', () => { + const element = createTestMouseTargetElement(); + const altMouseCursor = new AltMouseCursorController(element, { + addEventListener: () => {}, + removeEventListener: () => {} + } as any, () => true); + + const sync = (altHeld: boolean) => altMouseCursor.syncFromModifier({ + getModifierState: (key: string) => key === 'Alt' && altHeld + } as KeyboardEvent); + + assert.isFalse(element.classList.contains('enable-mouse-events')); + sync(true); + assert.isTrue(element.classList.contains('enable-mouse-events')); + sync(false); + assert.isFalse(element.classList.contains('enable-mouse-events')); + }); }); diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts index b90015139..64fe75ffe 100644 --- a/src/browser/services/MouseService.ts +++ b/src/browser/services/MouseService.ts @@ -7,7 +7,7 @@ import { addDisposableListener } from 'browser/Dom'; import { IBufferService, IMouseStateService, ICoreService, ILogService, IOptionsService } from 'common/services/Services'; import { CoreMouseAction, CoreMouseButton, CoreMouseEventType, ICoreMouseEvent, IDisposable } from 'common/Types'; import { C0 } from 'common/data/EscapeSequences'; -import { toDisposable } from 'common/Lifecycle'; +import { DisposableStore, MutableDisposable, toDisposable } from 'common/Lifecycle'; import { ICoreBrowserService, IMouseCoordsService, IMouseService, IMouseServiceTarget, IRenderService, ISelectionService } from './Services'; import { Gesture, EventType as GestureEventType, IGestureEvent } from 'browser/scrollable/touch'; @@ -25,6 +25,7 @@ export class MouseService implements IMouseService { private _lastEvent: ICoreMouseEvent | null = null; private _wheelPartialScroll: number = 0; private _touchScrollAccumulator: number = 0; + private _altMouseCursor: AltMouseCursorController | undefined; constructor( @IRenderService private readonly _renderService: IRenderService, @@ -63,11 +64,19 @@ export class MouseService implements IMouseService { mousedrag: (ev: Event) => this._handleMouseDrag(ctx, ev as MouseEvent), mousemove: (ev: Event) => this._handleMouseMove(ctx, ev as MouseEvent) }; + this._altMouseCursor = new AltMouseCursorController( + element, + document, + () => this._mouseStateService.areMouseEventsActive + && !!this._optionsService.rawOptions.mouseEventsRequireAlt + ); + register(this._altMouseCursor); register(this._mouseStateService.onProtocolChange(events => { this._handleProtocolChange(ctx, eventListeners, events); })); register(this._optionsService.onSpecificOptionChange('mouseEventsRequireAlt', () => { this._syncMouseModeState(element); + this._altMouseCursor?.sync(); })); // force initial onProtocolChange so we dont miss early mouse requests this._mouseStateService.activeProtocol = this._mouseStateService.activeProtocol; @@ -372,7 +381,7 @@ export class MouseService implements IMouseService { private _syncMouseModeState(element: HTMLElement): void { if (this._mouseStateService.areMouseEventsActive) { if (this._optionsService.rawOptions.mouseEventsRequireAlt) { - element.classList.remove('enable-mouse-events'); + this._altMouseCursor?.resetClass(); this._selectionService.enable(); } else { element.classList.add('enable-mouse-events'); @@ -396,6 +405,7 @@ export class MouseService implements IMouseService { this._logService.debug('Unbinding from mouse events.'); } this._syncMouseModeState(element); + this._altMouseCursor?.sync(); // add/remove handlers from requestedEvents if (!(events & CoreMouseEventType.MOVE)) { @@ -565,3 +575,64 @@ export class MouseService implements IMouseService { } } + +/** + * Toggles `enable-mouse-events` on the terminal element while alt is held when + * `mouseEventsRequireAlt` is active. DOM listeners are only registered while active. + */ +export class AltMouseCursorController implements IDisposable { + private readonly _listeners = new MutableDisposable(); + + constructor( + private readonly _element: HTMLElement, + private readonly _document: Document, + private readonly _isActive: () => boolean + ) { + } + + public dispose(): void { + this._listeners.dispose(); + } + + public sync(): void { + this._listeners.clear(); + + if (!this._isActive()) { + return; + } + + const store = new DisposableStore(); + const syncFromModifier = (ev: KeyboardEvent | MouseEvent) => this.syncFromModifier(ev); + store.add(addDisposableListener(this._document, 'keydown', syncFromModifier)); + store.add(addDisposableListener(this._document, 'keyup', syncFromModifier)); + store.add(addDisposableListener(this._element, 'mousemove', syncFromModifier)); + const targetWindow = this._element.ownerDocument?.defaultView; + if (targetWindow) { + store.add(addDisposableListener(targetWindow, 'blur', () => { + if (this._isActive()) { + this.resetClass(); + } + })); + } + this._listeners.value = store; + } + + public resetClass(): void { + this._updateClass(false); + } + + public syncFromModifier(ev: KeyboardEvent | MouseEvent): void { + if (!this._isActive()) { + return; + } + this._updateClass(ev.getModifierState('Alt')); + } + + private _updateClass(altHeld: boolean): void { + if (altHeld) { + this._element.classList.add('enable-mouse-events'); + } else { + this._element.classList.remove('enable-mouse-events'); + } + } +} From 1d569f873d52df402f2e0642c3e1aac922afb713 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 23:22:32 +0000 Subject: [PATCH 067/153] Ignore .cursor/*.log files in git Co-authored-by: Daniel Imms --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 81fe7a147..aa46b451f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ test-results/ # Tracing/debug files .cursor/nal-trace/ +.cursor/*.log # Keep bundled code out of Git dist/ From ce1f5f96081f803dc49b9146fac7ba49c01abcdc Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 27 May 2026 16:23:17 -0700 Subject: [PATCH 068/153] Add MouseEventCssClasses const enum for enable-mouse-events class. Centralize the CSS class name in MouseService.ts and use it across mouse mode handling and tests. Co-authored-by: Cursor --- src/browser/CoreBrowserTerminal.ts | 6 +++--- src/browser/services/MouseService.test.ts | 14 +++++++------- src/browser/services/MouseService.ts | 14 +++++++++----- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index a75bd5165..72f38c9d0 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -37,7 +37,7 @@ import { CharacterJoinerService } from 'browser/services/CharacterJoinerService' import { CoreBrowserService } from 'browser/services/CoreBrowserService'; import { LinkProviderService } from 'browser/services/LinkProviderService'; import { MouseCoordsService } from 'browser/services/MouseCoordsService'; -import { MouseService } from 'browser/services/MouseService'; +import { MouseEventCssClasses, MouseService } from 'browser/services/MouseService'; import { RenderService } from 'browser/services/RenderService'; import { SelectionService } from 'browser/services/SelectionService'; import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IKeyboardService, ILinkProviderService, IMouseCoordsService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services'; @@ -620,10 +620,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { // apply mouse event classes set by escape codes before terminal was attached if (this.mouseStateService.areMouseEventsActive && !this.options.mouseEventsRequireAlt) { this._selectionService.disable(); - this.element.classList.add('enable-mouse-events'); + this.element.classList.add(MouseEventCssClasses.ENABLE_MOUSE_EVENTS); } else { this._selectionService.enable(); - this.element.classList.remove('enable-mouse-events'); + this.element.classList.remove(MouseEventCssClasses.ENABLE_MOUSE_EVENTS); } if (this.options.screenReaderMode) { diff --git a/src/browser/services/MouseService.test.ts b/src/browser/services/MouseService.test.ts index df3983572..d7aa7308f 100644 --- a/src/browser/services/MouseService.test.ts +++ b/src/browser/services/MouseService.test.ts @@ -3,7 +3,7 @@ * @license MIT */ import { assert } from 'chai'; -import { AltMouseCursorController, MouseService } from 'browser/services/MouseService'; +import { AltMouseCursorController, MouseEventCssClasses, MouseService } from 'browser/services/MouseService'; import { MouseStateService } from 'common/services/MouseStateService'; import { CoreMouseAction, CoreMouseButton } from 'common/Types'; import { IBufferService, ICoreService, ILogService, IOptionsService } from 'common/services/Services'; @@ -279,15 +279,15 @@ describe('MouseService mouseEventsRequireAlt', () => { }, disposable => disposable, () => {}); mouseStateService.activeProtocol = 'ANY'; - assert.isTrue(element.classList.contains('enable-mouse-events')); + assert.isTrue(element.classList.contains(MouseEventCssClasses.ENABLE_MOUSE_EVENTS)); assert.equal(selectionService.disableCount, 1); optionsService.options.mouseEventsRequireAlt = true; - assert.isFalse(element.classList.contains('enable-mouse-events')); + assert.isFalse(element.classList.contains(MouseEventCssClasses.ENABLE_MOUSE_EVENTS)); assert.equal(selectionService.enableCount, 2); optionsService.options.mouseEventsRequireAlt = false; - assert.isTrue(element.classList.contains('enable-mouse-events')); + assert.isTrue(element.classList.contains(MouseEventCssClasses.ENABLE_MOUSE_EVENTS)); assert.equal(selectionService.disableCount, 2); }); @@ -344,10 +344,10 @@ describe('MouseService mouseEventsRequireAlt', () => { getModifierState: (key: string) => key === 'Alt' && altHeld } as KeyboardEvent); - assert.isFalse(element.classList.contains('enable-mouse-events')); + assert.isFalse(element.classList.contains(MouseEventCssClasses.ENABLE_MOUSE_EVENTS)); sync(true); - assert.isTrue(element.classList.contains('enable-mouse-events')); + assert.isTrue(element.classList.contains(MouseEventCssClasses.ENABLE_MOUSE_EVENTS)); sync(false); - assert.isFalse(element.classList.contains('enable-mouse-events')); + assert.isFalse(element.classList.contains(MouseEventCssClasses.ENABLE_MOUSE_EVENTS)); }); }); diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts index 64fe75ffe..164b6de9d 100644 --- a/src/browser/services/MouseService.ts +++ b/src/browser/services/MouseService.ts @@ -13,6 +13,10 @@ import { Gesture, EventType as GestureEventType, IGestureEvent } from 'browser/s type RequestedMouseEvents = Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener | null>; +export const enum MouseEventCssClasses { + ENABLE_MOUSE_EVENTS = 'enable-mouse-events' +} + interface IMouseBindContext { readonly target: IMouseServiceTarget; readonly focus: () => void; @@ -384,11 +388,11 @@ export class MouseService implements IMouseService { this._altMouseCursor?.resetClass(); this._selectionService.enable(); } else { - element.classList.add('enable-mouse-events'); + element.classList.add(MouseEventCssClasses.ENABLE_MOUSE_EVENTS); this._selectionService.disable(); } } else { - element.classList.remove('enable-mouse-events'); + element.classList.remove(MouseEventCssClasses.ENABLE_MOUSE_EVENTS); this._selectionService.enable(); } } @@ -577,7 +581,7 @@ export class MouseService implements IMouseService { } /** - * Toggles `enable-mouse-events` on the terminal element while alt is held when + * Toggles MouseEventCssClasses.ENABLE_MOUSE_EVENTS on the terminal element while alt is held when * `mouseEventsRequireAlt` is active. DOM listeners are only registered while active. */ export class AltMouseCursorController implements IDisposable { @@ -630,9 +634,9 @@ export class AltMouseCursorController implements IDisposable { private _updateClass(altHeld: boolean): void { if (altHeld) { - this._element.classList.add('enable-mouse-events'); + this._element.classList.add(MouseEventCssClasses.ENABLE_MOUSE_EVENTS); } else { - this._element.classList.remove('enable-mouse-events'); + this._element.classList.remove(MouseEventCssClasses.ENABLE_MOUSE_EVENTS); } } } From 1b95efa8c8207d532b237ab48410c8261fdfd15a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 27 May 2026 16:54:23 -0700 Subject: [PATCH 069/153] Add explicit void return type to alt cursor event listener. Fixes explicit-function-return-type lint in AltMouseCursorController. Co-authored-by: Cursor --- src/browser/services/MouseService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts index 164b6de9d..f9d68f42e 100644 --- a/src/browser/services/MouseService.ts +++ b/src/browser/services/MouseService.ts @@ -606,7 +606,7 @@ export class AltMouseCursorController implements IDisposable { } const store = new DisposableStore(); - const syncFromModifier = (ev: KeyboardEvent | MouseEvent) => this.syncFromModifier(ev); + const syncFromModifier = (ev: KeyboardEvent | MouseEvent): void => this.syncFromModifier(ev); store.add(addDisposableListener(this._document, 'keydown', syncFromModifier)); store.add(addDisposableListener(this._document, 'keyup', syncFromModifier)); store.add(addDisposableListener(this._element, 'mousemove', syncFromModifier)); From 38174907f30ca307be978cd9e3e274831f1c870b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 28 May 2026 00:24:06 +0000 Subject: [PATCH 070/153] fix(addon-progress): stabilize WebKit integration test harness Remove unnecessary unawaited setViewportSize from ProgressAddon tests, which was the line reported in flaky CI failures. Use serial mode and scoped hooks per Playwright shared-page guidance, and guard afterAll teardown when setup fails. Fixes #5956 Co-authored-by: Daniel Imms --- .../addon-progress/test/ProgressAddon.test.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/addons/addon-progress/test/ProgressAddon.test.ts b/addons/addon-progress/test/ProgressAddon.test.ts index 792c0445d..12ff7a38f 100644 --- a/addons/addon-progress/test/ProgressAddon.test.ts +++ b/addons/addon-progress/test/ProgressAddon.test.ts @@ -9,15 +9,18 @@ import { ITestContext, createTestContext, openTerminal } from '../../../test/pla let ctx: ITestContext; -test.beforeAll(async ({ browser }) => { - ctx = await createTestContext(browser); - ctx.page.setViewportSize({ width: 1024, height: 768 }); - await openTerminal(ctx); -}); -test.afterAll(async () => await ctx.page.close()); - test.describe('ProgressAddon', () => { + test.describe.configure({ mode: 'serial' }); + + test.beforeAll(async ({ browser }) => { + ctx = await createTestContext(browser); + await openTerminal(ctx); + }); + test.afterAll(async () => { + await ctx?.page?.close(); + }); + test.beforeEach(async function(): Promise { await ctx.page.evaluate(` window.progressStack = []; From 206917652835a7ef18b173bfa0774291d866e945 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 27 May 2026 19:58:21 -0700 Subject: [PATCH 071/153] Fix lint --- src/common/services/DecorationService.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts index 95875e54a..6db9bcf78 100644 --- a/src/common/services/DecorationService.ts +++ b/src/common/services/DecorationService.ts @@ -88,12 +88,10 @@ export class DecorationService extends Disposable implements IDecorationService if (!bucket) { return; } - let xmin = 0; - let xmax = 0; for (const d of bucket) { - xmin = d.options.x ?? 0; - xmax = xmin + (d.options.width ?? 1); - if (x >= xmin && x < xmax && (!layer || (d.options.layer ?? 'bottom') === layer)) { + $xmin = d.options.x ?? 0; + $xmax = $xmin + (d.options.width ?? 1); + if (x >= $xmin && x < $xmax && (!layer || (d.options.layer ?? 'bottom') === layer)) { yield d; } } From 743852b60cd6f1e089357706c171c4fd550348d9 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 27 May 2026 19:58:52 -0700 Subject: [PATCH 072/153] Remove unneeded comment --- addons/addon-image/src/ImageStorage.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/addon-image/src/ImageStorage.ts b/addons/addon-image/src/ImageStorage.ts index a3f714571..e5585c527 100644 --- a/addons/addon-image/src/ImageStorage.ts +++ b/addons/addon-image/src/ImageStorage.ts @@ -455,7 +455,6 @@ export class ImageStorage implements IDisposable { if (!nextE || nextE.imageId !== imageId || nextE.tileId !== startTile + count) { break; } - // e = nextE; count++; } col--; From 0c4ec34df81f50f3d81c28bb75c81e9c66fa8480 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 27 May 2026 20:11:20 -0700 Subject: [PATCH 073/153] Remove unnecessary @types/lru-cache --- addons/addon-ligatures/package.json | 1 - package-lock.json | 11 ----------- 2 files changed, 12 deletions(-) diff --git a/addons/addon-ligatures/package.json b/addons/addon-ligatures/package.json index d85c5fa7a..024ebaf4c 100644 --- a/addons/addon-ligatures/package.json +++ b/addons/addon-ligatures/package.json @@ -35,7 +35,6 @@ "opentype.js": "^2.0.0" }, "devDependencies": { - "@types/lru-cache": "^7.10.9", "@types/opentype.js": "^1.3.9", "font-finder": "^1.1.0" } diff --git a/package-lock.json b/package-lock.json index c77929d5d..4c3c6f552 100644 --- a/package-lock.json +++ b/package-lock.json @@ -83,7 +83,6 @@ "opentype.js": "^2.0.0" }, "devDependencies": { - "@types/lru-cache": "^7.10.9", "@types/opentype.js": "^1.3.9", "font-finder": "^1.1.0" }, @@ -1687,16 +1686,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/lru-cache": { - "version": "7.10.9", - "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-7.10.9.tgz", - "integrity": "sha512-wrwgkdJ0xr8AbzKhVaRI8SXZN9saapPwwLoydBEr4HqMZET1LUTi1gdoaj82XmRJ9atqN7MtB0aja29iiK+7ag==", - "dev": true, - "license": "MIT", - "dependencies": { - "lru-cache": "*" - } - }, "node_modules/@types/mocha": { "version": "10.0.10", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", From e96133603bb72b8fb49f4df5d64a6dfb7bf010e8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 28 May 2026 05:10:34 +0000 Subject: [PATCH 074/153] Add Cursor Cloud specific instructions to AGENTS.md Add development environment guidance for cloud agents including: - Demo server startup notes - Build-before-test requirements - Note about no external services needed - tsgo compiler note - Lint optimization tip Co-authored-by: Daniel Imms --- AGENTS.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 0d5d03588..5b07ce824 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -98,3 +98,15 @@ const cell = line?.getCell(0); ## Writing unit tests - Unit tests live alongside the source code file of the thing it's testing with a .test.ts suffix. + +## Cursor Cloud specific instructions + +**Demo server**: Start with `npm start` (port 3000). The demo server uses node-pty to spawn real shell sessions over WebSocket. Integration tests auto-start it via Playwright's `webServer` config, so you don't need to start it manually for `npm run test-integration`. + +**Build before testing**: Always run `npm run build && npm run esbuild` before `npm run test-unit`. Integration tests also need `npm run esbuild-demo-client` and `npm run esbuild-demo-server`. The update script handles this automatically on session start. + +**No external services**: This project has zero external dependencies (no databases, Docker, or APIs). Everything runs locally with Node.js. + +**TypeScript compiler**: The project uses `tsgo` (native TypeScript compiler preview) rather than standard `tsc`. It's installed via the `@typescript/native-preview` package. + +**Lint only changed files**: Prefer `npm run lint-changes` over `npm run lint` when iterating on code changes — it's significantly faster. From c7a94fc0bc723f719fd6c223642736855e3e37c5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 28 May 2026 14:01:12 +0000 Subject: [PATCH 075/153] Move fixtures to test/fixtures and update references Co-authored-by: Daniel Imms --- src/browser/Terminal2.test.ts | 2 +- {fixtures => test/fixtures}/escape_sequence_files/NOTES | 0 {fixtures => test/fixtures}/escape_sequence_files/run_tests.py | 0 .../fixtures}/escape_sequence_files/t0001-all_printable.in | 0 .../fixtures}/escape_sequence_files/t0001-all_printable.text | 0 .../fixtures}/escape_sequence_files/t0002-history.in | 0 .../fixtures}/escape_sequence_files/t0002-history.text | 0 .../fixtures}/escape_sequence_files/t0002j-simple_string.in | 0 .../fixtures}/escape_sequence_files/t0002j-simple_string.text | 0 .../fixtures}/escape_sequence_files/t0003-line_wrap.in | 0 .../fixtures}/escape_sequence_files/t0003-line_wrap.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0003j-LF.in | 0 .../fixtures}/escape_sequence_files/t0003j-LF.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0004-LF.in | 0 {fixtures => test/fixtures}/escape_sequence_files/t0004-LF.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0004j-CR.in | 0 .../fixtures}/escape_sequence_files/t0004j-CR.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0005-CR.in | 0 {fixtures => test/fixtures}/escape_sequence_files/t0005-CR.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0006-IND.in | 0 .../fixtures}/escape_sequence_files/t0006-IND.text | 0 .../fixtures}/escape_sequence_files/t0007-space_at_end.in | 0 .../fixtures}/escape_sequence_files/t0007-space_at_end.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0008-BS.in | 0 {fixtures => test/fixtures}/escape_sequence_files/t0008-BS.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0009-NEL.in | 0 .../fixtures}/escape_sequence_files/t0009-NEL.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0010-RI.in | 0 {fixtures => test/fixtures}/escape_sequence_files/t0010-RI.text | 0 .../fixtures}/escape_sequence_files/t0011-RI_scroll.in | 0 .../fixtures}/escape_sequence_files/t0011-RI_scroll.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0012-VT.in | 0 {fixtures => test/fixtures}/escape_sequence_files/t0012-VT.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0013-FF.in | 0 {fixtures => test/fixtures}/escape_sequence_files/t0013-FF.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0014-CAN.in | 0 .../fixtures}/escape_sequence_files/t0014-CAN.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0015-SUB.in | 0 .../fixtures}/escape_sequence_files/t0015-SUB.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0016-SU.in | 0 {fixtures => test/fixtures}/escape_sequence_files/t0016-SU.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0017-SD.in | 0 {fixtures => test/fixtures}/escape_sequence_files/t0017-SD.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0020-CUF.in | 0 .../fixtures}/escape_sequence_files/t0020-CUF.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0021-CUB.in | 0 .../fixtures}/escape_sequence_files/t0021-CUB.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0022-CUU.in | 0 .../fixtures}/escape_sequence_files/t0022-CUU.text | 0 .../fixtures}/escape_sequence_files/t0023-CUU_scroll.in | 0 .../fixtures}/escape_sequence_files/t0023-CUU_scroll.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0024-CUD.in | 0 .../fixtures}/escape_sequence_files/t0024-CUD.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0025-CUP.in | 0 .../fixtures}/escape_sequence_files/t0025-CUP.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0026-CNL.in | 0 .../fixtures}/escape_sequence_files/t0026-CNL.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0027-CPL.in | 0 .../fixtures}/escape_sequence_files/t0027-CPL.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0030-HPR.in | 0 .../fixtures}/escape_sequence_files/t0030-HPR.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0031-HPB.in_ | 0 .../fixtures}/escape_sequence_files/t0031-HPB.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0032-VPB.in | 0 .../fixtures}/escape_sequence_files/t0032-VPB.text | 0 .../fixtures}/escape_sequence_files/t0033-VPB_scroll.in | 0 .../fixtures}/escape_sequence_files/t0033-VPB_scroll.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0034-VPR.in | 0 .../fixtures}/escape_sequence_files/t0034-VPR.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0035-HVP.in | 0 .../fixtures}/escape_sequence_files/t0035-HVP.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0040-REP.in | 0 .../fixtures}/escape_sequence_files/t0040-REP.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0050-ICH.in | 0 .../fixtures}/escape_sequence_files/t0050-ICH.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0051-IL.in | 0 {fixtures => test/fixtures}/escape_sequence_files/t0051-IL.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0052-DL.in | 0 {fixtures => test/fixtures}/escape_sequence_files/t0052-DL.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0053-DCH.in | 0 .../fixtures}/escape_sequence_files/t0053-DCH.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0054-ECH.in | 0 .../fixtures}/escape_sequence_files/t0054-ECH.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0055-EL.in | 0 {fixtures => test/fixtures}/escape_sequence_files/t0055-EL.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0056-ED.in | 0 {fixtures => test/fixtures}/escape_sequence_files/t0056-ED.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0057-ED3.in | 0 .../fixtures}/escape_sequence_files/t0057-ED3.note | 0 .../fixtures}/escape_sequence_files/t0057-ED3.text | 0 .../fixtures}/escape_sequence_files/t0060-DECSC.in | 0 .../fixtures}/escape_sequence_files/t0060-DECSC.text | 0 .../fixtures}/escape_sequence_files/t0061-CSI_s.in | 0 .../fixtures}/escape_sequence_files/t0061-CSI_s.text | 0 .../fixtures}/escape_sequence_files/t0070-DECSTBM_LF.in | 0 .../fixtures}/escape_sequence_files/t0070-DECSTBM_LF.text | 0 .../fixtures}/escape_sequence_files/t0071-DECSTBM_IND.in | 0 .../fixtures}/escape_sequence_files/t0071-DECSTBM_IND.text | 0 .../fixtures}/escape_sequence_files/t0072-DECSTBM_NEL.in | 0 .../fixtures}/escape_sequence_files/t0072-DECSTBM_NEL.text | 0 .../fixtures}/escape_sequence_files/t0073-DECSTBM_RI.in | 0 .../fixtures}/escape_sequence_files/t0073-DECSTBM_RI.text | 0 .../fixtures}/escape_sequence_files/t0074-DECSTBM_SU_SD.in | 0 .../fixtures}/escape_sequence_files/t0074-DECSTBM_SU_SD.text | 0 .../fixtures}/escape_sequence_files/t0075-DECSTBM_CUU_CUD.in | 0 .../fixtures}/escape_sequence_files/t0075-DECSTBM_CUU_CUD.text | 0 .../fixtures}/escape_sequence_files/t0076-DECSTBM_IL_DL.in | 0 .../fixtures}/escape_sequence_files/t0076-DECSTBM_IL_DL.text | 0 .../fixtures}/escape_sequence_files/t0077-DECSTBM_quirks.in | 0 .../fixtures}/escape_sequence_files/t0077-DECSTBM_quirks.text | 0 .../fixtures}/escape_sequence_files/t0078-DECSTBM_CPL_CNL.in | 0 .../fixtures}/escape_sequence_files/t0078-DECSTBM_CPL_CNL.text | 0 .../fixtures}/escape_sequence_files/t0079-DECSTBM_VPR.in | 0 .../fixtures}/escape_sequence_files/t0079-DECSTBM_VPR.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0080-HT.in | 0 {fixtures => test/fixtures}/escape_sequence_files/t0080-HT.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0081-TBC.in | 0 .../fixtures}/escape_sequence_files/t0081-TBC.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0082-HTS.in | 0 .../fixtures}/escape_sequence_files/t0082-HTS.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0083-CHT.in | 0 .../fixtures}/escape_sequence_files/t0083-CHT.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0084-CBT.in | 0 .../fixtures}/escape_sequence_files/t0084-CBT.text | 0 .../fixtures}/escape_sequence_files/t0084-CBT.text-xterm | 0 .../fixtures}/escape_sequence_files/t0090-alt_screen.in | 0 .../fixtures}/escape_sequence_files/t0090-alt_screen.text | 0 .../fixtures}/escape_sequence_files/t0091-alt_screen_ED3.in | 0 .../fixtures}/escape_sequence_files/t0091-alt_screen_ED3.text | 0 .../fixtures}/escape_sequence_files/t0092-alt_screen_DECSC.in | 0 .../fixtures}/escape_sequence_files/t0092-alt_screen_DECSC.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0100-IRM.in | 0 .../fixtures}/escape_sequence_files/t0100-IRM.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0101-NLM.in | 0 .../fixtures}/escape_sequence_files/t0101-NLM.text | 0 .../fixtures}/escape_sequence_files/t0102-DECAWM.in | 0 .../fixtures}/escape_sequence_files/t0102-DECAWM.text | 0 .../fixtures}/escape_sequence_files/t0103-reverse_wrap.in | 0 .../fixtures}/escape_sequence_files/t0103-reverse_wrap.text | 0 .../fixtures}/escape_sequence_files/t0200-SGR.html | 0 {fixtures => test/fixtures}/escape_sequence_files/t0200-SGR.in_ | 0 .../fixtures}/escape_sequence_files/t0220-SGR_inverse.html | 0 .../fixtures}/escape_sequence_files/t0220-SGR_inverse.in_ | 0 .../fixtures}/escape_sequence_files/t0300-vttest1.in | 0 .../fixtures}/escape_sequence_files/t0300-vttest1.text | 0 .../fixtures}/escape_sequence_files/t0500-bash_long_line.in | 0 .../fixtures}/escape_sequence_files/t0500-bash_long_line.text | 0 .../fixtures}/escape_sequence_files/t0501-bash_ls.in | 0 .../fixtures}/escape_sequence_files/t0501-bash_ls.text | 0 .../fixtures}/escape_sequence_files/t0502-bash_ls_color.in | 0 .../fixtures}/escape_sequence_files/t0502-bash_ls_color.text | 0 .../fixtures}/escape_sequence_files/t0503-zsh_ls_color.in | 0 .../fixtures}/escape_sequence_files/t0503-zsh_ls_color.text | 0 {fixtures => test/fixtures}/escape_sequence_files/t0504-vim.in | 0 .../fixtures}/escape_sequence_files/t0504-vim.text | 0 .../fixtures}/escape_sequence_files/t600-DECSTBM_SR.in | 0 .../fixtures}/escape_sequence_files/t600-DECSTBM_SR.text | 0 .../fixtures}/escape_sequence_files/t601-DECSTBM_SL.in | 0 .../fixtures}/escape_sequence_files/t601-DECSTBM_SL.text | 0 .../fixtures}/escape_sequence_files/t602-DECSTBM_DECIC.in | 0 .../fixtures}/escape_sequence_files/t602-DECSTBM_DECIC.text | 0 .../fixtures}/escape_sequence_files/t603-DECSTBM_DECDC.in | 0 .../fixtures}/escape_sequence_files/t603-DECSTBM_DECDC.text | 0 163 files changed, 1 insertion(+), 1 deletion(-) rename {fixtures => test/fixtures}/escape_sequence_files/NOTES (100%) rename {fixtures => test/fixtures}/escape_sequence_files/run_tests.py (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0001-all_printable.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0001-all_printable.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0002-history.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0002-history.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0002j-simple_string.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0002j-simple_string.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0003-line_wrap.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0003-line_wrap.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0003j-LF.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0003j-LF.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0004-LF.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0004-LF.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0004j-CR.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0004j-CR.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0005-CR.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0005-CR.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0006-IND.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0006-IND.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0007-space_at_end.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0007-space_at_end.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0008-BS.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0008-BS.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0009-NEL.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0009-NEL.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0010-RI.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0010-RI.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0011-RI_scroll.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0011-RI_scroll.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0012-VT.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0012-VT.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0013-FF.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0013-FF.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0014-CAN.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0014-CAN.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0015-SUB.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0015-SUB.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0016-SU.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0016-SU.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0017-SD.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0017-SD.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0020-CUF.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0020-CUF.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0021-CUB.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0021-CUB.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0022-CUU.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0022-CUU.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0023-CUU_scroll.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0023-CUU_scroll.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0024-CUD.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0024-CUD.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0025-CUP.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0025-CUP.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0026-CNL.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0026-CNL.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0027-CPL.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0027-CPL.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0030-HPR.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0030-HPR.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0031-HPB.in_ (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0031-HPB.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0032-VPB.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0032-VPB.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0033-VPB_scroll.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0033-VPB_scroll.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0034-VPR.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0034-VPR.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0035-HVP.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0035-HVP.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0040-REP.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0040-REP.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0050-ICH.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0050-ICH.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0051-IL.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0051-IL.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0052-DL.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0052-DL.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0053-DCH.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0053-DCH.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0054-ECH.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0054-ECH.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0055-EL.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0055-EL.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0056-ED.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0056-ED.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0057-ED3.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0057-ED3.note (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0057-ED3.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0060-DECSC.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0060-DECSC.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0061-CSI_s.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0061-CSI_s.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0070-DECSTBM_LF.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0070-DECSTBM_LF.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0071-DECSTBM_IND.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0071-DECSTBM_IND.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0072-DECSTBM_NEL.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0072-DECSTBM_NEL.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0073-DECSTBM_RI.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0073-DECSTBM_RI.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0074-DECSTBM_SU_SD.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0074-DECSTBM_SU_SD.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0075-DECSTBM_CUU_CUD.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0075-DECSTBM_CUU_CUD.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0076-DECSTBM_IL_DL.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0076-DECSTBM_IL_DL.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0077-DECSTBM_quirks.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0077-DECSTBM_quirks.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0078-DECSTBM_CPL_CNL.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0078-DECSTBM_CPL_CNL.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0079-DECSTBM_VPR.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0079-DECSTBM_VPR.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0080-HT.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0080-HT.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0081-TBC.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0081-TBC.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0082-HTS.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0082-HTS.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0083-CHT.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0083-CHT.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0084-CBT.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0084-CBT.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0084-CBT.text-xterm (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0090-alt_screen.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0090-alt_screen.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0091-alt_screen_ED3.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0091-alt_screen_ED3.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0092-alt_screen_DECSC.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0092-alt_screen_DECSC.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0100-IRM.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0100-IRM.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0101-NLM.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0101-NLM.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0102-DECAWM.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0102-DECAWM.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0103-reverse_wrap.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0103-reverse_wrap.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0200-SGR.html (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0200-SGR.in_ (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0220-SGR_inverse.html (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0220-SGR_inverse.in_ (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0300-vttest1.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0300-vttest1.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0500-bash_long_line.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0500-bash_long_line.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0501-bash_ls.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0501-bash_ls.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0502-bash_ls_color.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0502-bash_ls_color.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0503-zsh_ls_color.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0503-zsh_ls_color.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0504-vim.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t0504-vim.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t600-DECSTBM_SR.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t600-DECSTBM_SR.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t601-DECSTBM_SL.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t601-DECSTBM_SL.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t602-DECSTBM_DECIC.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t602-DECSTBM_DECIC.text (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t603-DECSTBM_DECDC.in (100%) rename {fixtures => test/fixtures}/escape_sequence_files/t603-DECSTBM_DECDC.text (100%) diff --git a/src/browser/Terminal2.test.ts b/src/browser/Terminal2.test.ts index a405d0602..270b2e871 100644 --- a/src/browser/Terminal2.test.ts +++ b/src/browser/Terminal2.test.ts @@ -14,7 +14,7 @@ import { IDisposable } from '@xterm/xterm'; const COLS = 80; const ROWS = 25; -const escapeSequenceFilesDir = path.join(__dirname, '../../fixtures/escape_sequence_files'); +const escapeSequenceFilesDir = path.join(__dirname, '../../test/fixtures/escape_sequence_files'); const TESTFILES = fs.readdirSync(escapeSequenceFilesDir) .filter(f => f.endsWith('.in')) .map(f => path.join(escapeSequenceFilesDir, f)); diff --git a/fixtures/escape_sequence_files/NOTES b/test/fixtures/escape_sequence_files/NOTES similarity index 100% rename from fixtures/escape_sequence_files/NOTES rename to test/fixtures/escape_sequence_files/NOTES diff --git a/fixtures/escape_sequence_files/run_tests.py b/test/fixtures/escape_sequence_files/run_tests.py similarity index 100% rename from fixtures/escape_sequence_files/run_tests.py rename to test/fixtures/escape_sequence_files/run_tests.py diff --git a/fixtures/escape_sequence_files/t0001-all_printable.in b/test/fixtures/escape_sequence_files/t0001-all_printable.in similarity index 100% rename from fixtures/escape_sequence_files/t0001-all_printable.in rename to test/fixtures/escape_sequence_files/t0001-all_printable.in diff --git a/fixtures/escape_sequence_files/t0001-all_printable.text b/test/fixtures/escape_sequence_files/t0001-all_printable.text similarity index 100% rename from fixtures/escape_sequence_files/t0001-all_printable.text rename to test/fixtures/escape_sequence_files/t0001-all_printable.text diff --git a/fixtures/escape_sequence_files/t0002-history.in b/test/fixtures/escape_sequence_files/t0002-history.in similarity index 100% rename from fixtures/escape_sequence_files/t0002-history.in rename to test/fixtures/escape_sequence_files/t0002-history.in diff --git a/fixtures/escape_sequence_files/t0002-history.text b/test/fixtures/escape_sequence_files/t0002-history.text similarity index 100% rename from fixtures/escape_sequence_files/t0002-history.text rename to test/fixtures/escape_sequence_files/t0002-history.text diff --git a/fixtures/escape_sequence_files/t0002j-simple_string.in b/test/fixtures/escape_sequence_files/t0002j-simple_string.in similarity index 100% rename from fixtures/escape_sequence_files/t0002j-simple_string.in rename to test/fixtures/escape_sequence_files/t0002j-simple_string.in diff --git a/fixtures/escape_sequence_files/t0002j-simple_string.text b/test/fixtures/escape_sequence_files/t0002j-simple_string.text similarity index 100% rename from fixtures/escape_sequence_files/t0002j-simple_string.text rename to test/fixtures/escape_sequence_files/t0002j-simple_string.text diff --git a/fixtures/escape_sequence_files/t0003-line_wrap.in b/test/fixtures/escape_sequence_files/t0003-line_wrap.in similarity index 100% rename from fixtures/escape_sequence_files/t0003-line_wrap.in rename to test/fixtures/escape_sequence_files/t0003-line_wrap.in diff --git a/fixtures/escape_sequence_files/t0003-line_wrap.text b/test/fixtures/escape_sequence_files/t0003-line_wrap.text similarity index 100% rename from fixtures/escape_sequence_files/t0003-line_wrap.text rename to test/fixtures/escape_sequence_files/t0003-line_wrap.text diff --git a/fixtures/escape_sequence_files/t0003j-LF.in b/test/fixtures/escape_sequence_files/t0003j-LF.in similarity index 100% rename from fixtures/escape_sequence_files/t0003j-LF.in rename to test/fixtures/escape_sequence_files/t0003j-LF.in diff --git a/fixtures/escape_sequence_files/t0003j-LF.text b/test/fixtures/escape_sequence_files/t0003j-LF.text similarity index 100% rename from fixtures/escape_sequence_files/t0003j-LF.text rename to test/fixtures/escape_sequence_files/t0003j-LF.text diff --git a/fixtures/escape_sequence_files/t0004-LF.in b/test/fixtures/escape_sequence_files/t0004-LF.in similarity index 100% rename from fixtures/escape_sequence_files/t0004-LF.in rename to test/fixtures/escape_sequence_files/t0004-LF.in diff --git a/fixtures/escape_sequence_files/t0004-LF.text b/test/fixtures/escape_sequence_files/t0004-LF.text similarity index 100% rename from fixtures/escape_sequence_files/t0004-LF.text rename to test/fixtures/escape_sequence_files/t0004-LF.text diff --git a/fixtures/escape_sequence_files/t0004j-CR.in b/test/fixtures/escape_sequence_files/t0004j-CR.in similarity index 100% rename from fixtures/escape_sequence_files/t0004j-CR.in rename to test/fixtures/escape_sequence_files/t0004j-CR.in diff --git a/fixtures/escape_sequence_files/t0004j-CR.text b/test/fixtures/escape_sequence_files/t0004j-CR.text similarity index 100% rename from fixtures/escape_sequence_files/t0004j-CR.text rename to test/fixtures/escape_sequence_files/t0004j-CR.text diff --git a/fixtures/escape_sequence_files/t0005-CR.in b/test/fixtures/escape_sequence_files/t0005-CR.in similarity index 100% rename from fixtures/escape_sequence_files/t0005-CR.in rename to test/fixtures/escape_sequence_files/t0005-CR.in diff --git a/fixtures/escape_sequence_files/t0005-CR.text b/test/fixtures/escape_sequence_files/t0005-CR.text similarity index 100% rename from fixtures/escape_sequence_files/t0005-CR.text rename to test/fixtures/escape_sequence_files/t0005-CR.text diff --git a/fixtures/escape_sequence_files/t0006-IND.in b/test/fixtures/escape_sequence_files/t0006-IND.in similarity index 100% rename from fixtures/escape_sequence_files/t0006-IND.in rename to test/fixtures/escape_sequence_files/t0006-IND.in diff --git a/fixtures/escape_sequence_files/t0006-IND.text b/test/fixtures/escape_sequence_files/t0006-IND.text similarity index 100% rename from fixtures/escape_sequence_files/t0006-IND.text rename to test/fixtures/escape_sequence_files/t0006-IND.text diff --git a/fixtures/escape_sequence_files/t0007-space_at_end.in b/test/fixtures/escape_sequence_files/t0007-space_at_end.in similarity index 100% rename from fixtures/escape_sequence_files/t0007-space_at_end.in rename to test/fixtures/escape_sequence_files/t0007-space_at_end.in diff --git a/fixtures/escape_sequence_files/t0007-space_at_end.text b/test/fixtures/escape_sequence_files/t0007-space_at_end.text similarity index 100% rename from fixtures/escape_sequence_files/t0007-space_at_end.text rename to test/fixtures/escape_sequence_files/t0007-space_at_end.text diff --git a/fixtures/escape_sequence_files/t0008-BS.in b/test/fixtures/escape_sequence_files/t0008-BS.in similarity index 100% rename from fixtures/escape_sequence_files/t0008-BS.in rename to test/fixtures/escape_sequence_files/t0008-BS.in diff --git a/fixtures/escape_sequence_files/t0008-BS.text b/test/fixtures/escape_sequence_files/t0008-BS.text similarity index 100% rename from fixtures/escape_sequence_files/t0008-BS.text rename to test/fixtures/escape_sequence_files/t0008-BS.text diff --git a/fixtures/escape_sequence_files/t0009-NEL.in b/test/fixtures/escape_sequence_files/t0009-NEL.in similarity index 100% rename from fixtures/escape_sequence_files/t0009-NEL.in rename to test/fixtures/escape_sequence_files/t0009-NEL.in diff --git a/fixtures/escape_sequence_files/t0009-NEL.text b/test/fixtures/escape_sequence_files/t0009-NEL.text similarity index 100% rename from fixtures/escape_sequence_files/t0009-NEL.text rename to test/fixtures/escape_sequence_files/t0009-NEL.text diff --git a/fixtures/escape_sequence_files/t0010-RI.in b/test/fixtures/escape_sequence_files/t0010-RI.in similarity index 100% rename from fixtures/escape_sequence_files/t0010-RI.in rename to test/fixtures/escape_sequence_files/t0010-RI.in diff --git a/fixtures/escape_sequence_files/t0010-RI.text b/test/fixtures/escape_sequence_files/t0010-RI.text similarity index 100% rename from fixtures/escape_sequence_files/t0010-RI.text rename to test/fixtures/escape_sequence_files/t0010-RI.text diff --git a/fixtures/escape_sequence_files/t0011-RI_scroll.in b/test/fixtures/escape_sequence_files/t0011-RI_scroll.in similarity index 100% rename from fixtures/escape_sequence_files/t0011-RI_scroll.in rename to test/fixtures/escape_sequence_files/t0011-RI_scroll.in diff --git a/fixtures/escape_sequence_files/t0011-RI_scroll.text b/test/fixtures/escape_sequence_files/t0011-RI_scroll.text similarity index 100% rename from fixtures/escape_sequence_files/t0011-RI_scroll.text rename to test/fixtures/escape_sequence_files/t0011-RI_scroll.text diff --git a/fixtures/escape_sequence_files/t0012-VT.in b/test/fixtures/escape_sequence_files/t0012-VT.in similarity index 100% rename from fixtures/escape_sequence_files/t0012-VT.in rename to test/fixtures/escape_sequence_files/t0012-VT.in diff --git a/fixtures/escape_sequence_files/t0012-VT.text b/test/fixtures/escape_sequence_files/t0012-VT.text similarity index 100% rename from fixtures/escape_sequence_files/t0012-VT.text rename to test/fixtures/escape_sequence_files/t0012-VT.text diff --git a/fixtures/escape_sequence_files/t0013-FF.in b/test/fixtures/escape_sequence_files/t0013-FF.in similarity index 100% rename from fixtures/escape_sequence_files/t0013-FF.in rename to test/fixtures/escape_sequence_files/t0013-FF.in diff --git a/fixtures/escape_sequence_files/t0013-FF.text b/test/fixtures/escape_sequence_files/t0013-FF.text similarity index 100% rename from fixtures/escape_sequence_files/t0013-FF.text rename to test/fixtures/escape_sequence_files/t0013-FF.text diff --git a/fixtures/escape_sequence_files/t0014-CAN.in b/test/fixtures/escape_sequence_files/t0014-CAN.in similarity index 100% rename from fixtures/escape_sequence_files/t0014-CAN.in rename to test/fixtures/escape_sequence_files/t0014-CAN.in diff --git a/fixtures/escape_sequence_files/t0014-CAN.text b/test/fixtures/escape_sequence_files/t0014-CAN.text similarity index 100% rename from fixtures/escape_sequence_files/t0014-CAN.text rename to test/fixtures/escape_sequence_files/t0014-CAN.text diff --git a/fixtures/escape_sequence_files/t0015-SUB.in b/test/fixtures/escape_sequence_files/t0015-SUB.in similarity index 100% rename from fixtures/escape_sequence_files/t0015-SUB.in rename to test/fixtures/escape_sequence_files/t0015-SUB.in diff --git a/fixtures/escape_sequence_files/t0015-SUB.text b/test/fixtures/escape_sequence_files/t0015-SUB.text similarity index 100% rename from fixtures/escape_sequence_files/t0015-SUB.text rename to test/fixtures/escape_sequence_files/t0015-SUB.text diff --git a/fixtures/escape_sequence_files/t0016-SU.in b/test/fixtures/escape_sequence_files/t0016-SU.in similarity index 100% rename from fixtures/escape_sequence_files/t0016-SU.in rename to test/fixtures/escape_sequence_files/t0016-SU.in diff --git a/fixtures/escape_sequence_files/t0016-SU.text b/test/fixtures/escape_sequence_files/t0016-SU.text similarity index 100% rename from fixtures/escape_sequence_files/t0016-SU.text rename to test/fixtures/escape_sequence_files/t0016-SU.text diff --git a/fixtures/escape_sequence_files/t0017-SD.in b/test/fixtures/escape_sequence_files/t0017-SD.in similarity index 100% rename from fixtures/escape_sequence_files/t0017-SD.in rename to test/fixtures/escape_sequence_files/t0017-SD.in diff --git a/fixtures/escape_sequence_files/t0017-SD.text b/test/fixtures/escape_sequence_files/t0017-SD.text similarity index 100% rename from fixtures/escape_sequence_files/t0017-SD.text rename to test/fixtures/escape_sequence_files/t0017-SD.text diff --git a/fixtures/escape_sequence_files/t0020-CUF.in b/test/fixtures/escape_sequence_files/t0020-CUF.in similarity index 100% rename from fixtures/escape_sequence_files/t0020-CUF.in rename to test/fixtures/escape_sequence_files/t0020-CUF.in diff --git a/fixtures/escape_sequence_files/t0020-CUF.text b/test/fixtures/escape_sequence_files/t0020-CUF.text similarity index 100% rename from fixtures/escape_sequence_files/t0020-CUF.text rename to test/fixtures/escape_sequence_files/t0020-CUF.text diff --git a/fixtures/escape_sequence_files/t0021-CUB.in b/test/fixtures/escape_sequence_files/t0021-CUB.in similarity index 100% rename from fixtures/escape_sequence_files/t0021-CUB.in rename to test/fixtures/escape_sequence_files/t0021-CUB.in diff --git a/fixtures/escape_sequence_files/t0021-CUB.text b/test/fixtures/escape_sequence_files/t0021-CUB.text similarity index 100% rename from fixtures/escape_sequence_files/t0021-CUB.text rename to test/fixtures/escape_sequence_files/t0021-CUB.text diff --git a/fixtures/escape_sequence_files/t0022-CUU.in b/test/fixtures/escape_sequence_files/t0022-CUU.in similarity index 100% rename from fixtures/escape_sequence_files/t0022-CUU.in rename to test/fixtures/escape_sequence_files/t0022-CUU.in diff --git a/fixtures/escape_sequence_files/t0022-CUU.text b/test/fixtures/escape_sequence_files/t0022-CUU.text similarity index 100% rename from fixtures/escape_sequence_files/t0022-CUU.text rename to test/fixtures/escape_sequence_files/t0022-CUU.text diff --git a/fixtures/escape_sequence_files/t0023-CUU_scroll.in b/test/fixtures/escape_sequence_files/t0023-CUU_scroll.in similarity index 100% rename from fixtures/escape_sequence_files/t0023-CUU_scroll.in rename to test/fixtures/escape_sequence_files/t0023-CUU_scroll.in diff --git a/fixtures/escape_sequence_files/t0023-CUU_scroll.text b/test/fixtures/escape_sequence_files/t0023-CUU_scroll.text similarity index 100% rename from fixtures/escape_sequence_files/t0023-CUU_scroll.text rename to test/fixtures/escape_sequence_files/t0023-CUU_scroll.text diff --git a/fixtures/escape_sequence_files/t0024-CUD.in b/test/fixtures/escape_sequence_files/t0024-CUD.in similarity index 100% rename from fixtures/escape_sequence_files/t0024-CUD.in rename to test/fixtures/escape_sequence_files/t0024-CUD.in diff --git a/fixtures/escape_sequence_files/t0024-CUD.text b/test/fixtures/escape_sequence_files/t0024-CUD.text similarity index 100% rename from fixtures/escape_sequence_files/t0024-CUD.text rename to test/fixtures/escape_sequence_files/t0024-CUD.text diff --git a/fixtures/escape_sequence_files/t0025-CUP.in b/test/fixtures/escape_sequence_files/t0025-CUP.in similarity index 100% rename from fixtures/escape_sequence_files/t0025-CUP.in rename to test/fixtures/escape_sequence_files/t0025-CUP.in diff --git a/fixtures/escape_sequence_files/t0025-CUP.text b/test/fixtures/escape_sequence_files/t0025-CUP.text similarity index 100% rename from fixtures/escape_sequence_files/t0025-CUP.text rename to test/fixtures/escape_sequence_files/t0025-CUP.text diff --git a/fixtures/escape_sequence_files/t0026-CNL.in b/test/fixtures/escape_sequence_files/t0026-CNL.in similarity index 100% rename from fixtures/escape_sequence_files/t0026-CNL.in rename to test/fixtures/escape_sequence_files/t0026-CNL.in diff --git a/fixtures/escape_sequence_files/t0026-CNL.text b/test/fixtures/escape_sequence_files/t0026-CNL.text similarity index 100% rename from fixtures/escape_sequence_files/t0026-CNL.text rename to test/fixtures/escape_sequence_files/t0026-CNL.text diff --git a/fixtures/escape_sequence_files/t0027-CPL.in b/test/fixtures/escape_sequence_files/t0027-CPL.in similarity index 100% rename from fixtures/escape_sequence_files/t0027-CPL.in rename to test/fixtures/escape_sequence_files/t0027-CPL.in diff --git a/fixtures/escape_sequence_files/t0027-CPL.text b/test/fixtures/escape_sequence_files/t0027-CPL.text similarity index 100% rename from fixtures/escape_sequence_files/t0027-CPL.text rename to test/fixtures/escape_sequence_files/t0027-CPL.text diff --git a/fixtures/escape_sequence_files/t0030-HPR.in b/test/fixtures/escape_sequence_files/t0030-HPR.in similarity index 100% rename from fixtures/escape_sequence_files/t0030-HPR.in rename to test/fixtures/escape_sequence_files/t0030-HPR.in diff --git a/fixtures/escape_sequence_files/t0030-HPR.text b/test/fixtures/escape_sequence_files/t0030-HPR.text similarity index 100% rename from fixtures/escape_sequence_files/t0030-HPR.text rename to test/fixtures/escape_sequence_files/t0030-HPR.text diff --git a/fixtures/escape_sequence_files/t0031-HPB.in_ b/test/fixtures/escape_sequence_files/t0031-HPB.in_ similarity index 100% rename from fixtures/escape_sequence_files/t0031-HPB.in_ rename to test/fixtures/escape_sequence_files/t0031-HPB.in_ diff --git a/fixtures/escape_sequence_files/t0031-HPB.text b/test/fixtures/escape_sequence_files/t0031-HPB.text similarity index 100% rename from fixtures/escape_sequence_files/t0031-HPB.text rename to test/fixtures/escape_sequence_files/t0031-HPB.text diff --git a/fixtures/escape_sequence_files/t0032-VPB.in b/test/fixtures/escape_sequence_files/t0032-VPB.in similarity index 100% rename from fixtures/escape_sequence_files/t0032-VPB.in rename to test/fixtures/escape_sequence_files/t0032-VPB.in diff --git a/fixtures/escape_sequence_files/t0032-VPB.text b/test/fixtures/escape_sequence_files/t0032-VPB.text similarity index 100% rename from fixtures/escape_sequence_files/t0032-VPB.text rename to test/fixtures/escape_sequence_files/t0032-VPB.text diff --git a/fixtures/escape_sequence_files/t0033-VPB_scroll.in b/test/fixtures/escape_sequence_files/t0033-VPB_scroll.in similarity index 100% rename from fixtures/escape_sequence_files/t0033-VPB_scroll.in rename to test/fixtures/escape_sequence_files/t0033-VPB_scroll.in diff --git a/fixtures/escape_sequence_files/t0033-VPB_scroll.text b/test/fixtures/escape_sequence_files/t0033-VPB_scroll.text similarity index 100% rename from fixtures/escape_sequence_files/t0033-VPB_scroll.text rename to test/fixtures/escape_sequence_files/t0033-VPB_scroll.text diff --git a/fixtures/escape_sequence_files/t0034-VPR.in b/test/fixtures/escape_sequence_files/t0034-VPR.in similarity index 100% rename from fixtures/escape_sequence_files/t0034-VPR.in rename to test/fixtures/escape_sequence_files/t0034-VPR.in diff --git a/fixtures/escape_sequence_files/t0034-VPR.text b/test/fixtures/escape_sequence_files/t0034-VPR.text similarity index 100% rename from fixtures/escape_sequence_files/t0034-VPR.text rename to test/fixtures/escape_sequence_files/t0034-VPR.text diff --git a/fixtures/escape_sequence_files/t0035-HVP.in b/test/fixtures/escape_sequence_files/t0035-HVP.in similarity index 100% rename from fixtures/escape_sequence_files/t0035-HVP.in rename to test/fixtures/escape_sequence_files/t0035-HVP.in diff --git a/fixtures/escape_sequence_files/t0035-HVP.text b/test/fixtures/escape_sequence_files/t0035-HVP.text similarity index 100% rename from fixtures/escape_sequence_files/t0035-HVP.text rename to test/fixtures/escape_sequence_files/t0035-HVP.text diff --git a/fixtures/escape_sequence_files/t0040-REP.in b/test/fixtures/escape_sequence_files/t0040-REP.in similarity index 100% rename from fixtures/escape_sequence_files/t0040-REP.in rename to test/fixtures/escape_sequence_files/t0040-REP.in diff --git a/fixtures/escape_sequence_files/t0040-REP.text b/test/fixtures/escape_sequence_files/t0040-REP.text similarity index 100% rename from fixtures/escape_sequence_files/t0040-REP.text rename to test/fixtures/escape_sequence_files/t0040-REP.text diff --git a/fixtures/escape_sequence_files/t0050-ICH.in b/test/fixtures/escape_sequence_files/t0050-ICH.in similarity index 100% rename from fixtures/escape_sequence_files/t0050-ICH.in rename to test/fixtures/escape_sequence_files/t0050-ICH.in diff --git a/fixtures/escape_sequence_files/t0050-ICH.text b/test/fixtures/escape_sequence_files/t0050-ICH.text similarity index 100% rename from fixtures/escape_sequence_files/t0050-ICH.text rename to test/fixtures/escape_sequence_files/t0050-ICH.text diff --git a/fixtures/escape_sequence_files/t0051-IL.in b/test/fixtures/escape_sequence_files/t0051-IL.in similarity index 100% rename from fixtures/escape_sequence_files/t0051-IL.in rename to test/fixtures/escape_sequence_files/t0051-IL.in diff --git a/fixtures/escape_sequence_files/t0051-IL.text b/test/fixtures/escape_sequence_files/t0051-IL.text similarity index 100% rename from fixtures/escape_sequence_files/t0051-IL.text rename to test/fixtures/escape_sequence_files/t0051-IL.text diff --git a/fixtures/escape_sequence_files/t0052-DL.in b/test/fixtures/escape_sequence_files/t0052-DL.in similarity index 100% rename from fixtures/escape_sequence_files/t0052-DL.in rename to test/fixtures/escape_sequence_files/t0052-DL.in diff --git a/fixtures/escape_sequence_files/t0052-DL.text b/test/fixtures/escape_sequence_files/t0052-DL.text similarity index 100% rename from fixtures/escape_sequence_files/t0052-DL.text rename to test/fixtures/escape_sequence_files/t0052-DL.text diff --git a/fixtures/escape_sequence_files/t0053-DCH.in b/test/fixtures/escape_sequence_files/t0053-DCH.in similarity index 100% rename from fixtures/escape_sequence_files/t0053-DCH.in rename to test/fixtures/escape_sequence_files/t0053-DCH.in diff --git a/fixtures/escape_sequence_files/t0053-DCH.text b/test/fixtures/escape_sequence_files/t0053-DCH.text similarity index 100% rename from fixtures/escape_sequence_files/t0053-DCH.text rename to test/fixtures/escape_sequence_files/t0053-DCH.text diff --git a/fixtures/escape_sequence_files/t0054-ECH.in b/test/fixtures/escape_sequence_files/t0054-ECH.in similarity index 100% rename from fixtures/escape_sequence_files/t0054-ECH.in rename to test/fixtures/escape_sequence_files/t0054-ECH.in diff --git a/fixtures/escape_sequence_files/t0054-ECH.text b/test/fixtures/escape_sequence_files/t0054-ECH.text similarity index 100% rename from fixtures/escape_sequence_files/t0054-ECH.text rename to test/fixtures/escape_sequence_files/t0054-ECH.text diff --git a/fixtures/escape_sequence_files/t0055-EL.in b/test/fixtures/escape_sequence_files/t0055-EL.in similarity index 100% rename from fixtures/escape_sequence_files/t0055-EL.in rename to test/fixtures/escape_sequence_files/t0055-EL.in diff --git a/fixtures/escape_sequence_files/t0055-EL.text b/test/fixtures/escape_sequence_files/t0055-EL.text similarity index 100% rename from fixtures/escape_sequence_files/t0055-EL.text rename to test/fixtures/escape_sequence_files/t0055-EL.text diff --git a/fixtures/escape_sequence_files/t0056-ED.in b/test/fixtures/escape_sequence_files/t0056-ED.in similarity index 100% rename from fixtures/escape_sequence_files/t0056-ED.in rename to test/fixtures/escape_sequence_files/t0056-ED.in diff --git a/fixtures/escape_sequence_files/t0056-ED.text b/test/fixtures/escape_sequence_files/t0056-ED.text similarity index 100% rename from fixtures/escape_sequence_files/t0056-ED.text rename to test/fixtures/escape_sequence_files/t0056-ED.text diff --git a/fixtures/escape_sequence_files/t0057-ED3.in b/test/fixtures/escape_sequence_files/t0057-ED3.in similarity index 100% rename from fixtures/escape_sequence_files/t0057-ED3.in rename to test/fixtures/escape_sequence_files/t0057-ED3.in diff --git a/fixtures/escape_sequence_files/t0057-ED3.note b/test/fixtures/escape_sequence_files/t0057-ED3.note similarity index 100% rename from fixtures/escape_sequence_files/t0057-ED3.note rename to test/fixtures/escape_sequence_files/t0057-ED3.note diff --git a/fixtures/escape_sequence_files/t0057-ED3.text b/test/fixtures/escape_sequence_files/t0057-ED3.text similarity index 100% rename from fixtures/escape_sequence_files/t0057-ED3.text rename to test/fixtures/escape_sequence_files/t0057-ED3.text diff --git a/fixtures/escape_sequence_files/t0060-DECSC.in b/test/fixtures/escape_sequence_files/t0060-DECSC.in similarity index 100% rename from fixtures/escape_sequence_files/t0060-DECSC.in rename to test/fixtures/escape_sequence_files/t0060-DECSC.in diff --git a/fixtures/escape_sequence_files/t0060-DECSC.text b/test/fixtures/escape_sequence_files/t0060-DECSC.text similarity index 100% rename from fixtures/escape_sequence_files/t0060-DECSC.text rename to test/fixtures/escape_sequence_files/t0060-DECSC.text diff --git a/fixtures/escape_sequence_files/t0061-CSI_s.in b/test/fixtures/escape_sequence_files/t0061-CSI_s.in similarity index 100% rename from fixtures/escape_sequence_files/t0061-CSI_s.in rename to test/fixtures/escape_sequence_files/t0061-CSI_s.in diff --git a/fixtures/escape_sequence_files/t0061-CSI_s.text b/test/fixtures/escape_sequence_files/t0061-CSI_s.text similarity index 100% rename from fixtures/escape_sequence_files/t0061-CSI_s.text rename to test/fixtures/escape_sequence_files/t0061-CSI_s.text diff --git a/fixtures/escape_sequence_files/t0070-DECSTBM_LF.in b/test/fixtures/escape_sequence_files/t0070-DECSTBM_LF.in similarity index 100% rename from fixtures/escape_sequence_files/t0070-DECSTBM_LF.in rename to test/fixtures/escape_sequence_files/t0070-DECSTBM_LF.in diff --git a/fixtures/escape_sequence_files/t0070-DECSTBM_LF.text b/test/fixtures/escape_sequence_files/t0070-DECSTBM_LF.text similarity index 100% rename from fixtures/escape_sequence_files/t0070-DECSTBM_LF.text rename to test/fixtures/escape_sequence_files/t0070-DECSTBM_LF.text diff --git a/fixtures/escape_sequence_files/t0071-DECSTBM_IND.in b/test/fixtures/escape_sequence_files/t0071-DECSTBM_IND.in similarity index 100% rename from fixtures/escape_sequence_files/t0071-DECSTBM_IND.in rename to test/fixtures/escape_sequence_files/t0071-DECSTBM_IND.in diff --git a/fixtures/escape_sequence_files/t0071-DECSTBM_IND.text b/test/fixtures/escape_sequence_files/t0071-DECSTBM_IND.text similarity index 100% rename from fixtures/escape_sequence_files/t0071-DECSTBM_IND.text rename to test/fixtures/escape_sequence_files/t0071-DECSTBM_IND.text diff --git a/fixtures/escape_sequence_files/t0072-DECSTBM_NEL.in b/test/fixtures/escape_sequence_files/t0072-DECSTBM_NEL.in similarity index 100% rename from fixtures/escape_sequence_files/t0072-DECSTBM_NEL.in rename to test/fixtures/escape_sequence_files/t0072-DECSTBM_NEL.in diff --git a/fixtures/escape_sequence_files/t0072-DECSTBM_NEL.text b/test/fixtures/escape_sequence_files/t0072-DECSTBM_NEL.text similarity index 100% rename from fixtures/escape_sequence_files/t0072-DECSTBM_NEL.text rename to test/fixtures/escape_sequence_files/t0072-DECSTBM_NEL.text diff --git a/fixtures/escape_sequence_files/t0073-DECSTBM_RI.in b/test/fixtures/escape_sequence_files/t0073-DECSTBM_RI.in similarity index 100% rename from fixtures/escape_sequence_files/t0073-DECSTBM_RI.in rename to test/fixtures/escape_sequence_files/t0073-DECSTBM_RI.in diff --git a/fixtures/escape_sequence_files/t0073-DECSTBM_RI.text b/test/fixtures/escape_sequence_files/t0073-DECSTBM_RI.text similarity index 100% rename from fixtures/escape_sequence_files/t0073-DECSTBM_RI.text rename to test/fixtures/escape_sequence_files/t0073-DECSTBM_RI.text diff --git a/fixtures/escape_sequence_files/t0074-DECSTBM_SU_SD.in b/test/fixtures/escape_sequence_files/t0074-DECSTBM_SU_SD.in similarity index 100% rename from fixtures/escape_sequence_files/t0074-DECSTBM_SU_SD.in rename to test/fixtures/escape_sequence_files/t0074-DECSTBM_SU_SD.in diff --git a/fixtures/escape_sequence_files/t0074-DECSTBM_SU_SD.text b/test/fixtures/escape_sequence_files/t0074-DECSTBM_SU_SD.text similarity index 100% rename from fixtures/escape_sequence_files/t0074-DECSTBM_SU_SD.text rename to test/fixtures/escape_sequence_files/t0074-DECSTBM_SU_SD.text diff --git a/fixtures/escape_sequence_files/t0075-DECSTBM_CUU_CUD.in b/test/fixtures/escape_sequence_files/t0075-DECSTBM_CUU_CUD.in similarity index 100% rename from fixtures/escape_sequence_files/t0075-DECSTBM_CUU_CUD.in rename to test/fixtures/escape_sequence_files/t0075-DECSTBM_CUU_CUD.in diff --git a/fixtures/escape_sequence_files/t0075-DECSTBM_CUU_CUD.text b/test/fixtures/escape_sequence_files/t0075-DECSTBM_CUU_CUD.text similarity index 100% rename from fixtures/escape_sequence_files/t0075-DECSTBM_CUU_CUD.text rename to test/fixtures/escape_sequence_files/t0075-DECSTBM_CUU_CUD.text diff --git a/fixtures/escape_sequence_files/t0076-DECSTBM_IL_DL.in b/test/fixtures/escape_sequence_files/t0076-DECSTBM_IL_DL.in similarity index 100% rename from fixtures/escape_sequence_files/t0076-DECSTBM_IL_DL.in rename to test/fixtures/escape_sequence_files/t0076-DECSTBM_IL_DL.in diff --git a/fixtures/escape_sequence_files/t0076-DECSTBM_IL_DL.text b/test/fixtures/escape_sequence_files/t0076-DECSTBM_IL_DL.text similarity index 100% rename from fixtures/escape_sequence_files/t0076-DECSTBM_IL_DL.text rename to test/fixtures/escape_sequence_files/t0076-DECSTBM_IL_DL.text diff --git a/fixtures/escape_sequence_files/t0077-DECSTBM_quirks.in b/test/fixtures/escape_sequence_files/t0077-DECSTBM_quirks.in similarity index 100% rename from fixtures/escape_sequence_files/t0077-DECSTBM_quirks.in rename to test/fixtures/escape_sequence_files/t0077-DECSTBM_quirks.in diff --git a/fixtures/escape_sequence_files/t0077-DECSTBM_quirks.text b/test/fixtures/escape_sequence_files/t0077-DECSTBM_quirks.text similarity index 100% rename from fixtures/escape_sequence_files/t0077-DECSTBM_quirks.text rename to test/fixtures/escape_sequence_files/t0077-DECSTBM_quirks.text diff --git a/fixtures/escape_sequence_files/t0078-DECSTBM_CPL_CNL.in b/test/fixtures/escape_sequence_files/t0078-DECSTBM_CPL_CNL.in similarity index 100% rename from fixtures/escape_sequence_files/t0078-DECSTBM_CPL_CNL.in rename to test/fixtures/escape_sequence_files/t0078-DECSTBM_CPL_CNL.in diff --git a/fixtures/escape_sequence_files/t0078-DECSTBM_CPL_CNL.text b/test/fixtures/escape_sequence_files/t0078-DECSTBM_CPL_CNL.text similarity index 100% rename from fixtures/escape_sequence_files/t0078-DECSTBM_CPL_CNL.text rename to test/fixtures/escape_sequence_files/t0078-DECSTBM_CPL_CNL.text diff --git a/fixtures/escape_sequence_files/t0079-DECSTBM_VPR.in b/test/fixtures/escape_sequence_files/t0079-DECSTBM_VPR.in similarity index 100% rename from fixtures/escape_sequence_files/t0079-DECSTBM_VPR.in rename to test/fixtures/escape_sequence_files/t0079-DECSTBM_VPR.in diff --git a/fixtures/escape_sequence_files/t0079-DECSTBM_VPR.text b/test/fixtures/escape_sequence_files/t0079-DECSTBM_VPR.text similarity index 100% rename from fixtures/escape_sequence_files/t0079-DECSTBM_VPR.text rename to test/fixtures/escape_sequence_files/t0079-DECSTBM_VPR.text diff --git a/fixtures/escape_sequence_files/t0080-HT.in b/test/fixtures/escape_sequence_files/t0080-HT.in similarity index 100% rename from fixtures/escape_sequence_files/t0080-HT.in rename to test/fixtures/escape_sequence_files/t0080-HT.in diff --git a/fixtures/escape_sequence_files/t0080-HT.text b/test/fixtures/escape_sequence_files/t0080-HT.text similarity index 100% rename from fixtures/escape_sequence_files/t0080-HT.text rename to test/fixtures/escape_sequence_files/t0080-HT.text diff --git a/fixtures/escape_sequence_files/t0081-TBC.in b/test/fixtures/escape_sequence_files/t0081-TBC.in similarity index 100% rename from fixtures/escape_sequence_files/t0081-TBC.in rename to test/fixtures/escape_sequence_files/t0081-TBC.in diff --git a/fixtures/escape_sequence_files/t0081-TBC.text b/test/fixtures/escape_sequence_files/t0081-TBC.text similarity index 100% rename from fixtures/escape_sequence_files/t0081-TBC.text rename to test/fixtures/escape_sequence_files/t0081-TBC.text diff --git a/fixtures/escape_sequence_files/t0082-HTS.in b/test/fixtures/escape_sequence_files/t0082-HTS.in similarity index 100% rename from fixtures/escape_sequence_files/t0082-HTS.in rename to test/fixtures/escape_sequence_files/t0082-HTS.in diff --git a/fixtures/escape_sequence_files/t0082-HTS.text b/test/fixtures/escape_sequence_files/t0082-HTS.text similarity index 100% rename from fixtures/escape_sequence_files/t0082-HTS.text rename to test/fixtures/escape_sequence_files/t0082-HTS.text diff --git a/fixtures/escape_sequence_files/t0083-CHT.in b/test/fixtures/escape_sequence_files/t0083-CHT.in similarity index 100% rename from fixtures/escape_sequence_files/t0083-CHT.in rename to test/fixtures/escape_sequence_files/t0083-CHT.in diff --git a/fixtures/escape_sequence_files/t0083-CHT.text b/test/fixtures/escape_sequence_files/t0083-CHT.text similarity index 100% rename from fixtures/escape_sequence_files/t0083-CHT.text rename to test/fixtures/escape_sequence_files/t0083-CHT.text diff --git a/fixtures/escape_sequence_files/t0084-CBT.in b/test/fixtures/escape_sequence_files/t0084-CBT.in similarity index 100% rename from fixtures/escape_sequence_files/t0084-CBT.in rename to test/fixtures/escape_sequence_files/t0084-CBT.in diff --git a/fixtures/escape_sequence_files/t0084-CBT.text b/test/fixtures/escape_sequence_files/t0084-CBT.text similarity index 100% rename from fixtures/escape_sequence_files/t0084-CBT.text rename to test/fixtures/escape_sequence_files/t0084-CBT.text diff --git a/fixtures/escape_sequence_files/t0084-CBT.text-xterm b/test/fixtures/escape_sequence_files/t0084-CBT.text-xterm similarity index 100% rename from fixtures/escape_sequence_files/t0084-CBT.text-xterm rename to test/fixtures/escape_sequence_files/t0084-CBT.text-xterm diff --git a/fixtures/escape_sequence_files/t0090-alt_screen.in b/test/fixtures/escape_sequence_files/t0090-alt_screen.in similarity index 100% rename from fixtures/escape_sequence_files/t0090-alt_screen.in rename to test/fixtures/escape_sequence_files/t0090-alt_screen.in diff --git a/fixtures/escape_sequence_files/t0090-alt_screen.text b/test/fixtures/escape_sequence_files/t0090-alt_screen.text similarity index 100% rename from fixtures/escape_sequence_files/t0090-alt_screen.text rename to test/fixtures/escape_sequence_files/t0090-alt_screen.text diff --git a/fixtures/escape_sequence_files/t0091-alt_screen_ED3.in b/test/fixtures/escape_sequence_files/t0091-alt_screen_ED3.in similarity index 100% rename from fixtures/escape_sequence_files/t0091-alt_screen_ED3.in rename to test/fixtures/escape_sequence_files/t0091-alt_screen_ED3.in diff --git a/fixtures/escape_sequence_files/t0091-alt_screen_ED3.text b/test/fixtures/escape_sequence_files/t0091-alt_screen_ED3.text similarity index 100% rename from fixtures/escape_sequence_files/t0091-alt_screen_ED3.text rename to test/fixtures/escape_sequence_files/t0091-alt_screen_ED3.text diff --git a/fixtures/escape_sequence_files/t0092-alt_screen_DECSC.in b/test/fixtures/escape_sequence_files/t0092-alt_screen_DECSC.in similarity index 100% rename from fixtures/escape_sequence_files/t0092-alt_screen_DECSC.in rename to test/fixtures/escape_sequence_files/t0092-alt_screen_DECSC.in diff --git a/fixtures/escape_sequence_files/t0092-alt_screen_DECSC.text b/test/fixtures/escape_sequence_files/t0092-alt_screen_DECSC.text similarity index 100% rename from fixtures/escape_sequence_files/t0092-alt_screen_DECSC.text rename to test/fixtures/escape_sequence_files/t0092-alt_screen_DECSC.text diff --git a/fixtures/escape_sequence_files/t0100-IRM.in b/test/fixtures/escape_sequence_files/t0100-IRM.in similarity index 100% rename from fixtures/escape_sequence_files/t0100-IRM.in rename to test/fixtures/escape_sequence_files/t0100-IRM.in diff --git a/fixtures/escape_sequence_files/t0100-IRM.text b/test/fixtures/escape_sequence_files/t0100-IRM.text similarity index 100% rename from fixtures/escape_sequence_files/t0100-IRM.text rename to test/fixtures/escape_sequence_files/t0100-IRM.text diff --git a/fixtures/escape_sequence_files/t0101-NLM.in b/test/fixtures/escape_sequence_files/t0101-NLM.in similarity index 100% rename from fixtures/escape_sequence_files/t0101-NLM.in rename to test/fixtures/escape_sequence_files/t0101-NLM.in diff --git a/fixtures/escape_sequence_files/t0101-NLM.text b/test/fixtures/escape_sequence_files/t0101-NLM.text similarity index 100% rename from fixtures/escape_sequence_files/t0101-NLM.text rename to test/fixtures/escape_sequence_files/t0101-NLM.text diff --git a/fixtures/escape_sequence_files/t0102-DECAWM.in b/test/fixtures/escape_sequence_files/t0102-DECAWM.in similarity index 100% rename from fixtures/escape_sequence_files/t0102-DECAWM.in rename to test/fixtures/escape_sequence_files/t0102-DECAWM.in diff --git a/fixtures/escape_sequence_files/t0102-DECAWM.text b/test/fixtures/escape_sequence_files/t0102-DECAWM.text similarity index 100% rename from fixtures/escape_sequence_files/t0102-DECAWM.text rename to test/fixtures/escape_sequence_files/t0102-DECAWM.text diff --git a/fixtures/escape_sequence_files/t0103-reverse_wrap.in b/test/fixtures/escape_sequence_files/t0103-reverse_wrap.in similarity index 100% rename from fixtures/escape_sequence_files/t0103-reverse_wrap.in rename to test/fixtures/escape_sequence_files/t0103-reverse_wrap.in diff --git a/fixtures/escape_sequence_files/t0103-reverse_wrap.text b/test/fixtures/escape_sequence_files/t0103-reverse_wrap.text similarity index 100% rename from fixtures/escape_sequence_files/t0103-reverse_wrap.text rename to test/fixtures/escape_sequence_files/t0103-reverse_wrap.text diff --git a/fixtures/escape_sequence_files/t0200-SGR.html b/test/fixtures/escape_sequence_files/t0200-SGR.html similarity index 100% rename from fixtures/escape_sequence_files/t0200-SGR.html rename to test/fixtures/escape_sequence_files/t0200-SGR.html diff --git a/fixtures/escape_sequence_files/t0200-SGR.in_ b/test/fixtures/escape_sequence_files/t0200-SGR.in_ similarity index 100% rename from fixtures/escape_sequence_files/t0200-SGR.in_ rename to test/fixtures/escape_sequence_files/t0200-SGR.in_ diff --git a/fixtures/escape_sequence_files/t0220-SGR_inverse.html b/test/fixtures/escape_sequence_files/t0220-SGR_inverse.html similarity index 100% rename from fixtures/escape_sequence_files/t0220-SGR_inverse.html rename to test/fixtures/escape_sequence_files/t0220-SGR_inverse.html diff --git a/fixtures/escape_sequence_files/t0220-SGR_inverse.in_ b/test/fixtures/escape_sequence_files/t0220-SGR_inverse.in_ similarity index 100% rename from fixtures/escape_sequence_files/t0220-SGR_inverse.in_ rename to test/fixtures/escape_sequence_files/t0220-SGR_inverse.in_ diff --git a/fixtures/escape_sequence_files/t0300-vttest1.in b/test/fixtures/escape_sequence_files/t0300-vttest1.in similarity index 100% rename from fixtures/escape_sequence_files/t0300-vttest1.in rename to test/fixtures/escape_sequence_files/t0300-vttest1.in diff --git a/fixtures/escape_sequence_files/t0300-vttest1.text b/test/fixtures/escape_sequence_files/t0300-vttest1.text similarity index 100% rename from fixtures/escape_sequence_files/t0300-vttest1.text rename to test/fixtures/escape_sequence_files/t0300-vttest1.text diff --git a/fixtures/escape_sequence_files/t0500-bash_long_line.in b/test/fixtures/escape_sequence_files/t0500-bash_long_line.in similarity index 100% rename from fixtures/escape_sequence_files/t0500-bash_long_line.in rename to test/fixtures/escape_sequence_files/t0500-bash_long_line.in diff --git a/fixtures/escape_sequence_files/t0500-bash_long_line.text b/test/fixtures/escape_sequence_files/t0500-bash_long_line.text similarity index 100% rename from fixtures/escape_sequence_files/t0500-bash_long_line.text rename to test/fixtures/escape_sequence_files/t0500-bash_long_line.text diff --git a/fixtures/escape_sequence_files/t0501-bash_ls.in b/test/fixtures/escape_sequence_files/t0501-bash_ls.in similarity index 100% rename from fixtures/escape_sequence_files/t0501-bash_ls.in rename to test/fixtures/escape_sequence_files/t0501-bash_ls.in diff --git a/fixtures/escape_sequence_files/t0501-bash_ls.text b/test/fixtures/escape_sequence_files/t0501-bash_ls.text similarity index 100% rename from fixtures/escape_sequence_files/t0501-bash_ls.text rename to test/fixtures/escape_sequence_files/t0501-bash_ls.text diff --git a/fixtures/escape_sequence_files/t0502-bash_ls_color.in b/test/fixtures/escape_sequence_files/t0502-bash_ls_color.in similarity index 100% rename from fixtures/escape_sequence_files/t0502-bash_ls_color.in rename to test/fixtures/escape_sequence_files/t0502-bash_ls_color.in diff --git a/fixtures/escape_sequence_files/t0502-bash_ls_color.text b/test/fixtures/escape_sequence_files/t0502-bash_ls_color.text similarity index 100% rename from fixtures/escape_sequence_files/t0502-bash_ls_color.text rename to test/fixtures/escape_sequence_files/t0502-bash_ls_color.text diff --git a/fixtures/escape_sequence_files/t0503-zsh_ls_color.in b/test/fixtures/escape_sequence_files/t0503-zsh_ls_color.in similarity index 100% rename from fixtures/escape_sequence_files/t0503-zsh_ls_color.in rename to test/fixtures/escape_sequence_files/t0503-zsh_ls_color.in diff --git a/fixtures/escape_sequence_files/t0503-zsh_ls_color.text b/test/fixtures/escape_sequence_files/t0503-zsh_ls_color.text similarity index 100% rename from fixtures/escape_sequence_files/t0503-zsh_ls_color.text rename to test/fixtures/escape_sequence_files/t0503-zsh_ls_color.text diff --git a/fixtures/escape_sequence_files/t0504-vim.in b/test/fixtures/escape_sequence_files/t0504-vim.in similarity index 100% rename from fixtures/escape_sequence_files/t0504-vim.in rename to test/fixtures/escape_sequence_files/t0504-vim.in diff --git a/fixtures/escape_sequence_files/t0504-vim.text b/test/fixtures/escape_sequence_files/t0504-vim.text similarity index 100% rename from fixtures/escape_sequence_files/t0504-vim.text rename to test/fixtures/escape_sequence_files/t0504-vim.text diff --git a/fixtures/escape_sequence_files/t600-DECSTBM_SR.in b/test/fixtures/escape_sequence_files/t600-DECSTBM_SR.in similarity index 100% rename from fixtures/escape_sequence_files/t600-DECSTBM_SR.in rename to test/fixtures/escape_sequence_files/t600-DECSTBM_SR.in diff --git a/fixtures/escape_sequence_files/t600-DECSTBM_SR.text b/test/fixtures/escape_sequence_files/t600-DECSTBM_SR.text similarity index 100% rename from fixtures/escape_sequence_files/t600-DECSTBM_SR.text rename to test/fixtures/escape_sequence_files/t600-DECSTBM_SR.text diff --git a/fixtures/escape_sequence_files/t601-DECSTBM_SL.in b/test/fixtures/escape_sequence_files/t601-DECSTBM_SL.in similarity index 100% rename from fixtures/escape_sequence_files/t601-DECSTBM_SL.in rename to test/fixtures/escape_sequence_files/t601-DECSTBM_SL.in diff --git a/fixtures/escape_sequence_files/t601-DECSTBM_SL.text b/test/fixtures/escape_sequence_files/t601-DECSTBM_SL.text similarity index 100% rename from fixtures/escape_sequence_files/t601-DECSTBM_SL.text rename to test/fixtures/escape_sequence_files/t601-DECSTBM_SL.text diff --git a/fixtures/escape_sequence_files/t602-DECSTBM_DECIC.in b/test/fixtures/escape_sequence_files/t602-DECSTBM_DECIC.in similarity index 100% rename from fixtures/escape_sequence_files/t602-DECSTBM_DECIC.in rename to test/fixtures/escape_sequence_files/t602-DECSTBM_DECIC.in diff --git a/fixtures/escape_sequence_files/t602-DECSTBM_DECIC.text b/test/fixtures/escape_sequence_files/t602-DECSTBM_DECIC.text similarity index 100% rename from fixtures/escape_sequence_files/t602-DECSTBM_DECIC.text rename to test/fixtures/escape_sequence_files/t602-DECSTBM_DECIC.text diff --git a/fixtures/escape_sequence_files/t603-DECSTBM_DECDC.in b/test/fixtures/escape_sequence_files/t603-DECSTBM_DECDC.in similarity index 100% rename from fixtures/escape_sequence_files/t603-DECSTBM_DECDC.in rename to test/fixtures/escape_sequence_files/t603-DECSTBM_DECDC.in diff --git a/fixtures/escape_sequence_files/t603-DECSTBM_DECDC.text b/test/fixtures/escape_sequence_files/t603-DECSTBM_DECDC.text similarity index 100% rename from fixtures/escape_sequence_files/t603-DECSTBM_DECDC.text rename to test/fixtures/escape_sequence_files/t603-DECSTBM_DECDC.text From f76c7aa8ea72b851fecb2447074dc10063511351 Mon Sep 17 00:00:00 2001 From: Per Bothner Date: Sat, 30 May 2026 14:07:03 -0700 Subject: [PATCH 076/153] Avoid "fake markers" in DecorationService.test.ts. Instead we use an actual Marker in an actual Buffer. --- src/common/services/DecorationService.test.ts | 43 ++++++------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/src/common/services/DecorationService.test.ts b/src/common/services/DecorationService.test.ts index e3a6c2f04..ec86a85f3 100644 --- a/src/common/services/DecorationService.test.ts +++ b/src/common/services/DecorationService.test.ts @@ -12,27 +12,18 @@ import { MockLogService, MockBufferService, MockOptionsService } from 'common/Te import { Buffer } from 'common/buffer/Buffer'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; -function createFakeMarker(line: number): IMarker { - return Object.freeze(new class extends Disposable { - public readonly id = 1; - public readonly line = line; - public readonly isDisposed = false; - public readonly onDispose = new Emitter().event; - }()); -} - -function createDecorationService(): DecorationService { - const bufferService = new MockBufferService(80, 24, new MockOptionsService()); - return new DecorationService(new MockLogService(), bufferService); -} - -const fakeMarker: IMarker = createFakeMarker(1); - describe('DecorationService', () => { + let bufferService: MockBufferService; + let service: DecorationService; + + beforeEach(() => { + bufferService = new MockBufferService(80, 24, new MockOptionsService()); + service = new DecorationService(new MockLogService(), bufferService); + }); + it('should set isDisposed to true after dispose', () => { - const service = createDecorationService(); const decoration = service.registerDecoration({ - marker: fakeMarker + marker: bufferService.buffer.addMarker(1) }); assert.ok(decoration); assert.isFalse(decoration!.isDisposed); @@ -42,22 +33,19 @@ describe('DecorationService', () => { describe('forEachDecorationAtCell', () => { it('should find decoration at its marker line', () => { - const service = createDecorationService(); const decoration = service.registerDecoration({ - marker: createFakeMarker(5), + marker: bufferService.buffer.addMarker(5), width: 10 }); assert.ok(decoration); - const found: typeof decoration[] = []; service.forEachDecorationAtCell(0, 5, undefined, d => found.push(d)); assert.strictEqual(found.length, 1); }); it('should find decoration with height > 1 on subsequent lines', () => { - const service = createDecorationService(); const decoration = service.registerDecoration({ - marker: createFakeMarker(5), + marker: bufferService.buffer.addMarker(5), width: 10, height: 3 }); @@ -81,15 +69,13 @@ describe('DecorationService', () => { }); it('should not find decoration outside its x range', () => { - const service = createDecorationService(); const decoration = service.registerDecoration({ - marker: createFakeMarker(5), + marker: bufferService.buffer.addMarker(5), x: 5, width: 3, height: 2 }); assert.ok(decoration); - const foundAtX4: typeof decoration[] = []; service.forEachDecorationAtCell(4, 5, undefined, d => foundAtX4.push(d)); assert.strictEqual(foundAtX4.length, 0); @@ -113,7 +99,7 @@ describe('DecorationService', () => { const buffer = bufferService.buffer; (buffer as Buffer).fillViewportRows(); - for (let i = 0; i < 100; i++) { + for (let i = 0; i < buffer.lines.length; i++) { serviceWithBuffer.registerDecoration({ marker: buffer.addMarker(i), width: 5 @@ -134,9 +120,8 @@ describe('DecorationService', () => { describe('getDecorationsAtCell', () => { it('should find decoration with height > 1 on subsequent lines', () => { - const service = createDecorationService(); const decoration = service.registerDecoration({ - marker: createFakeMarker(5), + marker: bufferService.buffer.addMarker(5), width: 10, height: 3 }); From 17ad33dc3df129d62657933bf3279abc44b1419d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 09:16:10 -0700 Subject: [PATCH 077/153] Remove TypeScript path mappings in favor of relative imports Fixes https://github.com/xtermjs/xterm.js/issues/5896 TypeScript path mappings for common/*, browser/*, and headless/* were not rewritten in emitted JavaScript, which forced webpack resolve aliases and NODE_PATH workarounds so runtime could resolve modules. That added build complexity and broke tooling such as ESLint import/no-cycle. Changes: - Convert ~750 imports across src/, addons/, and test/ to relative paths - Remove common/browser/headless paths and src catch-all mappings from tsconfigs (keep @xterm/addon-* demo and addon typings paths) - Remove webpack resolve.alias for common, browser, headless, and vs - Drop NODE_PATH from unit tests, benchmarks, and VS Code launch config - Fix addon-image IIPImageStorage to import ./Types after removing catch-all * Verified: npm run build, esbuild, package, package-headless Co-authored-by: Cursor --- .vscode/launch.json | 3 - addons/addon-attach/test/tsconfig.json | 6 -- addons/addon-clipboard/src/tsconfig.json | 3 - addons/addon-clipboard/test/tsconfig.json | 6 -- addons/addon-fit/src/FitAddon.ts | 2 +- addons/addon-fit/src/tsconfig.json | 3 - addons/addon-fit/test/tsconfig.json | 6 -- addons/addon-image/src/IIPImageStorage.ts | 2 +- addons/addon-image/src/ImageAddon.ts | 2 +- addons/addon-image/src/ImageRenderer.ts | 2 +- addons/addon-image/src/Types.ts | 14 ++-- addons/addon-image/src/tsconfig.json | 15 ++-- addons/addon-image/test/tsconfig.json | 6 -- addons/addon-image/webpack.config.js | 5 -- addons/addon-ligatures/test/tsconfig.json | 5 -- addons/addon-progress/src/ProgressAddon.ts | 2 +- addons/addon-progress/src/tsconfig.json | 6 -- addons/addon-progress/test/tsconfig.json | 6 -- addons/addon-progress/webpack.config.js | 5 -- addons/addon-search/src/DecorationManager.ts | 2 +- addons/addon-search/src/SearchAddon.ts | 6 +- addons/addon-search/src/SearchEngine.test.ts | 4 +- .../addon-search/src/SearchLineCache.test.ts | 4 +- addons/addon-search/src/SearchLineCache.ts | 4 +- .../addon-search/src/SearchResultTracker.ts | 4 +- addons/addon-search/src/tsconfig.json | 6 -- addons/addon-search/test/tsconfig.json | 6 -- addons/addon-search/webpack.config.js | 4 -- .../benchmark/SerializeAddon.benchmark.ts | 4 +- .../addon-serialize/benchmark/tsconfig.json | 28 +++++--- addons/addon-serialize/package.json | 6 +- .../src/SerializeAddon.test.ts | 8 +-- addons/addon-serialize/src/SerializeAddon.ts | 6 +- addons/addon-serialize/src/tsconfig.json | 6 -- addons/addon-serialize/test/tsconfig.json | 8 --- addons/addon-serialize/webpack.config.js | 4 -- .../UnicodeGraphemeAddon.benchmark.ts | 4 +- .../benchmark/tsconfig.json | 33 ++++++--- addons/addon-unicode-graphemes/package.json | 6 +- .../src/UnicodeGraphemeProvider.ts | 4 +- .../addon-unicode-graphemes/src/tsconfig.json | 3 - .../test/tsconfig.json | 8 --- .../addon-unicode-graphemes/webpack.config.js | 4 -- addons/addon-unicode11/src/UnicodeV11.ts | 4 +- addons/addon-unicode11/src/tsconfig.json | 6 -- addons/addon-unicode11/test/tsconfig.json | 6 -- addons/addon-unicode11/webpack.config.js | 4 -- addons/addon-web-fonts/test/tsconfig.json | 6 -- addons/addon-web-links/test/tsconfig.json | 6 -- addons/addon-webgl/src/CellColorResolver.ts | 16 ++--- addons/addon-webgl/src/CharAtlasCache.ts | 4 +- addons/addon-webgl/src/CharAtlasUtils.test.ts | 4 +- addons/addon-webgl/src/CharAtlasUtils.ts | 6 +- addons/addon-webgl/src/Constants.ts | 2 +- .../src/CursorBlinkStateManager.ts | 4 +- addons/addon-webgl/src/DevicePixelObserver.ts | 2 +- addons/addon-webgl/src/GlyphRenderer.ts | 10 +-- addons/addon-webgl/src/RectangleRenderer.ts | 16 ++--- addons/addon-webgl/src/RenderModel.ts | 4 +- addons/addon-webgl/src/TextureAtlas.ts | 20 +++--- addons/addon-webgl/src/Types.ts | 8 +-- addons/addon-webgl/src/WebglAddon.ts | 12 ++-- addons/addon-webgl/src/WebglRenderer.ts | 26 +++---- addons/addon-webgl/src/WebglUtils.ts | 4 +- .../src/customGlyphs/CustomGlyphRasterizer.ts | 4 +- .../src/renderLayer/BaseRenderLayer.ts | 14 ++-- .../src/renderLayer/LinkRenderLayer.ts | 10 +-- addons/addon-webgl/src/renderLayer/Types.ts | 2 +- addons/addon-webgl/src/tsconfig.json | 9 --- addons/addon-webgl/test/tsconfig.json | 6 -- addons/addon-webgl/webpack.config.js | 5 -- bin/test_unit.js | 8 +-- demo/client/tsconfig.json | 56 +++++++++++---- package.json | 6 +- src/browser/AccessibilityManager.ts | 16 ++--- src/browser/Clipboard.test.ts | 2 +- src/browser/Clipboard.ts | 4 +- src/browser/ColorContrastCache.test.ts | 2 +- src/browser/ColorContrastCache.ts | 6 +- src/browser/CoreBrowserTerminal.ts | 68 +++++++++---------- src/browser/Dom.ts | 4 +- src/browser/Linkifier.test.ts | 8 +-- src/browser/Linkifier.ts | 12 ++-- src/browser/OscLinkProvider.test.ts | 10 +-- src/browser/OscLinkProvider.ts | 10 +-- src/browser/RenderDebouncer.ts | 4 +- src/browser/Terminal.test.ts | 12 ++-- src/browser/Terminal2.test.ts | 2 +- src/browser/TestUtils.test.ts | 30 ++++---- src/browser/TimeBasedDebouncer.ts | 2 +- src/browser/Types.ts | 8 +-- src/browser/Viewport.ts | 20 +++--- .../decorations/BufferDecorationRenderer.ts | 6 +- .../decorations/ColorZoneStore.test.ts | 2 +- src/browser/decorations/ColorZoneStore.ts | 2 +- .../decorations/OverviewRulerRenderer.ts | 8 +-- src/browser/input/CompositionHelper.test.ts | 6 +- src/browser/input/CompositionHelper.ts | 6 +- src/browser/input/Mouse.test.ts | 2 +- src/browser/input/MoveToCell.test.ts | 4 +- src/browser/input/MoveToCell.ts | 4 +- src/browser/public/Terminal.ts | 20 +++--- src/browser/renderer/dom/DomRenderer.ts | 28 ++++---- .../dom/DomRendererRowFactory.test.ts | 18 ++--- .../renderer/dom/DomRendererRowFactory.ts | 24 +++---- src/browser/renderer/dom/WidthCache.test.ts | 2 +- src/browser/renderer/dom/WidthCache.ts | 6 +- .../renderer/shared/RendererUtils.test.ts | 2 +- src/browser/renderer/shared/RendererUtils.ts | 2 +- .../renderer/shared/SelectionRenderModel.ts | 4 +- .../shared/TextBlinkStateManager.test.ts | 8 +-- .../renderer/shared/TextBlinkStateManager.ts | 6 +- src/browser/renderer/shared/Types.ts | 6 +- src/browser/scrollable/abstractScrollbar.ts | 2 +- .../scrollable/globalPointerMoveMonitor.ts | 2 +- src/browser/scrollable/mouseEvent.ts | 2 +- src/browser/scrollable/scrollable.ts | 4 +- src/browser/scrollable/scrollableElement.ts | 8 +-- src/browser/scrollable/scrollbarArrow.ts | 2 +- .../scrollbarVisibilityController.ts | 4 +- src/browser/scrollable/touch.ts | 2 +- src/browser/scrollable/widget.ts | 2 +- src/browser/selection/SelectionModel.test.ts | 2 +- src/browser/selection/SelectionModel.ts | 2 +- src/browser/services/CharSizeService.ts | 8 +-- .../services/CharacterJoinerService.test.ts | 14 ++-- .../services/CharacterJoinerService.ts | 14 ++-- src/browser/services/CoreBrowserService.ts | 6 +- src/browser/services/KeyboardService.ts | 14 ++-- src/browser/services/LinkProviderService.ts | 6 +- src/browser/services/MouseCoordsService.ts | 6 +- src/browser/services/MouseService.test.ts | 12 ++-- src/browser/services/MouseService.ts | 12 ++-- src/browser/services/RenderService.ts | 16 ++--- src/browser/services/SelectionService.test.ts | 20 +++--- src/browser/services/SelectionService.ts | 28 ++++---- src/browser/services/Services.ts | 12 ++-- src/browser/services/ThemeService.test.ts | 6 +- src/browser/services/ThemeService.ts | 16 ++--- src/browser/tsconfig.json | 10 ++- src/common/Async.ts | 2 +- src/common/CircularList.test.ts | 2 +- src/common/CircularList.ts | 6 +- src/common/Color.test.ts | 2 +- src/common/Color.ts | 2 +- src/common/CoreTerminal.ts | 36 +++++----- src/common/Event.test.ts | 2 +- src/common/Event.ts | 2 +- src/common/InputHandler.test.ts | 26 +++---- src/common/InputHandler.ts | 40 +++++------ src/common/MultiKeyMap.test.ts | 2 +- src/common/SortedList.test.ts | 4 +- src/common/SortedList.ts | 4 +- src/common/StringBuilder.test.ts | 2 +- src/common/TaskQueue.ts | 2 +- src/common/TestUtils.test.ts | 20 +++--- src/common/Types.ts | 12 ++-- src/common/WindowsMode.ts | 4 +- src/common/buffer/AttributeData.ts | 4 +- src/common/buffer/Buffer.test.ts | 14 ++-- src/common/buffer/Buffer.ts | 28 ++++---- src/common/buffer/BufferLine.test.ts | 12 ++-- src/common/buffer/BufferLine.ts | 12 ++-- src/common/buffer/BufferLineStringCache.ts | 6 +- src/common/buffer/BufferRange.test.ts | 2 +- src/common/buffer/BufferReflow.test.ts | 8 +-- src/common/buffer/BufferReflow.ts | 6 +- src/common/buffer/BufferSet.test.ts | 6 +- src/common/buffer/BufferSet.ts | 12 ++-- src/common/buffer/CellData.test.ts | 4 +- src/common/buffer/CellData.ts | 8 +-- src/common/buffer/Marker.ts | 6 +- src/common/buffer/Types.ts | 4 +- src/common/data/Charsets.ts | 2 +- src/common/input/Keyboard.test.ts | 4 +- src/common/input/Keyboard.ts | 4 +- src/common/input/KittyKeyboard.test.ts | 4 +- src/common/input/KittyKeyboard.ts | 4 +- src/common/input/TextDecoder.test.ts | 2 +- src/common/input/UnicodeV6.test.ts | 2 +- src/common/input/UnicodeV6.ts | 4 +- src/common/input/Win32InputMode.test.ts | 4 +- src/common/input/Win32InputMode.ts | 4 +- src/common/input/WriteBuffer.ts | 6 +- src/common/input/XParseColor.test.ts | 2 +- src/common/parser/ApcParser.test.ts | 6 +- src/common/parser/ApcParser.ts | 10 +-- src/common/parser/DcsParser.test.ts | 8 +-- src/common/parser/DcsParser.ts | 12 ++-- .../parser/EscapeSequenceParser.test.ts | 18 ++--- src/common/parser/EscapeSequenceParser.ts | 16 ++--- src/common/parser/OscParser.test.ts | 6 +- src/common/parser/OscParser.ts | 10 +-- src/common/parser/Params.test.ts | 4 +- src/common/parser/Params.ts | 2 +- src/common/parser/Types.ts | 4 +- src/common/public/BufferApiView.ts | 6 +- src/common/public/BufferLineApiView.ts | 4 +- src/common/public/BufferNamespaceApi.ts | 8 +-- src/common/public/ParserApi.ts | 4 +- src/common/public/UnicodeApi.ts | 2 +- src/common/services/BufferService.ts | 12 ++-- src/common/services/CharsetService.ts | 4 +- src/common/services/CoreService.test.ts | 6 +- src/common/services/CoreService.ts | 8 +-- src/common/services/DecorationService.test.ts | 12 ++-- src/common/services/DecorationService.ts | 16 ++--- src/common/services/InstantiationService.ts | 4 +- src/common/services/LogService.ts | 4 +- src/common/services/MouseStateService.test.ts | 4 +- src/common/services/MouseStateService.ts | 8 +-- src/common/services/OptionsService.test.ts | 4 +- src/common/services/OptionsService.ts | 10 +-- src/common/services/OscLinkService.test.ts | 12 ++-- src/common/services/OscLinkService.ts | 4 +- src/common/services/ServiceRegistry.ts | 2 +- src/common/services/Services.ts | 8 +-- src/common/services/UnicodeService.test.ts | 4 +- src/common/services/UnicodeService.ts | 6 +- src/common/tsconfig.json | 6 +- src/headless/Terminal.ts | 10 +-- src/headless/public/Terminal.test.ts | 2 +- src/headless/public/Terminal.ts | 16 ++--- src/headless/tsconfig.json | 6 +- test/benchmark/DecorationService.benchmark.ts | 4 +- .../EscapeSequenceParser.benchmark.ts | 12 ++-- test/benchmark/Event.benchmark.ts | 2 +- test/benchmark/Terminal.benchmark.ts | 4 +- test/benchmark/tsconfig.json | 7 +- test/playwright/Terminal.test.ts | 2 +- test/playwright/TestUtils.ts | 6 +- test/playwright/tsconfig.json | 8 --- webpack.config.headless.js | 8 +-- webpack.config.js | 8 +-- 234 files changed, 867 insertions(+), 1005 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 07c424e5d..facfa4f4d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -15,9 +15,6 @@ "--recursive", "${workspaceRoot}/out/**/*.test.js" ], - "env": { - "NODE_PATH": "${workspaceRoot}/out" - }, "sourceMaps": true, "outFiles": [ "${workspaceRoot}/out/**/*.js" ], "internalConsoleOptions": "openOnSessionStart" diff --git a/addons/addon-attach/test/tsconfig.json b/addons/addon-attach/test/tsconfig.json index 900a727ed..73f17c2a5 100644 --- a/addons/addon-attach/test/tsconfig.json +++ b/addons/addon-attach/test/tsconfig.json @@ -10,12 +10,6 @@ "sourceMap": true, "removeComments": true, "paths": { - "common/*": [ - "../../../src/common/*" - ], - "browser/*": [ - "../../../src/browser/*" - ], "*": [ "./*" ] diff --git a/addons/addon-clipboard/src/tsconfig.json b/addons/addon-clipboard/src/tsconfig.json index b61072808..b1192735c 100644 --- a/addons/addon-clipboard/src/tsconfig.json +++ b/addons/addon-clipboard/src/tsconfig.json @@ -15,9 +15,6 @@ "../../../node_modules/@types/mocha" ], "paths": { - "browser/*": [ - "../../../src/browser/*" - ], "@xterm/addon-clipboard": [ "../typings/addon-clipboard.d.ts" ] diff --git a/addons/addon-clipboard/test/tsconfig.json b/addons/addon-clipboard/test/tsconfig.json index 900a727ed..73f17c2a5 100644 --- a/addons/addon-clipboard/test/tsconfig.json +++ b/addons/addon-clipboard/test/tsconfig.json @@ -10,12 +10,6 @@ "sourceMap": true, "removeComments": true, "paths": { - "common/*": [ - "../../../src/common/*" - ], - "browser/*": [ - "../../../src/browser/*" - ], "*": [ "./*" ] diff --git a/addons/addon-fit/src/FitAddon.ts b/addons/addon-fit/src/FitAddon.ts index 3db988acd..812d31d3f 100644 --- a/addons/addon-fit/src/FitAddon.ts +++ b/addons/addon-fit/src/FitAddon.ts @@ -5,7 +5,7 @@ import type { Terminal, ITerminalAddon, IRenderDimensions } from '@xterm/xterm'; import type { FitAddon as IFitApi } from '@xterm/addon-fit'; -import { ViewportConstants } from 'browser/shared/Constants'; +import { ViewportConstants } from '../../../src/browser/shared/Constants'; interface ITerminalDimensions { /** diff --git a/addons/addon-fit/src/tsconfig.json b/addons/addon-fit/src/tsconfig.json index 068b43370..98bf0cf98 100644 --- a/addons/addon-fit/src/tsconfig.json +++ b/addons/addon-fit/src/tsconfig.json @@ -15,9 +15,6 @@ "../../../node_modules/@types/mocha" ], "paths": { - "browser/*": [ - "../../../src/browser/*" - ], "@xterm/addon-fit": [ "../typings/addon-fit.d.ts" ] diff --git a/addons/addon-fit/test/tsconfig.json b/addons/addon-fit/test/tsconfig.json index 900a727ed..73f17c2a5 100644 --- a/addons/addon-fit/test/tsconfig.json +++ b/addons/addon-fit/test/tsconfig.json @@ -10,12 +10,6 @@ "sourceMap": true, "removeComments": true, "paths": { - "common/*": [ - "../../../src/common/*" - ], - "browser/*": [ - "../../../src/browser/*" - ], "*": [ "./*" ] diff --git a/addons/addon-image/src/IIPImageStorage.ts b/addons/addon-image/src/IIPImageStorage.ts index 282fea9f0..28037a897 100644 --- a/addons/addon-image/src/IIPImageStorage.ts +++ b/addons/addon-image/src/IIPImageStorage.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { IAddImageOpts } from 'Types'; +import { IAddImageOpts } from './Types'; import { ImageStorage } from './ImageStorage'; /** diff --git a/addons/addon-image/src/ImageAddon.ts b/addons/addon-image/src/ImageAddon.ts index 8fd395431..07aea05c5 100644 --- a/addons/addon-image/src/ImageAddon.ts +++ b/addons/addon-image/src/ImageAddon.ts @@ -5,7 +5,7 @@ import type { ITerminalAddon, IDisposable } from '@xterm/xterm'; import type { ImageAddon as IImageApi } from '@xterm/addon-image'; -import { Emitter, type IEvent } from 'common/Event'; +import { Emitter, type IEvent } from '../../../src/common/Event'; import { IIPHandler } from './IIPHandler'; import { ImageRenderer } from './ImageRenderer'; import { ImageStorage, CELL_SIZE_DEFAULT } from './ImageStorage'; diff --git a/addons/addon-image/src/ImageRenderer.ts b/addons/addon-image/src/ImageRenderer.ts index 5854efaec..285f13724 100644 --- a/addons/addon-image/src/ImageRenderer.ts +++ b/addons/addon-image/src/ImageRenderer.ts @@ -6,7 +6,7 @@ import { toRGBA8888 } from 'sixel/lib/Colors'; import { IDisposable } from '@xterm/xterm'; import { ICellSize, ImageLayer, ITerminalExt, IImageSpec, IRenderDimensions, IRenderService } from './Types'; -import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; +import { Disposable, MutableDisposable, toDisposable } from '../../../src/common/Lifecycle'; const enum Constants { PLACEHOLDER_LENGTH = 4096, diff --git a/addons/addon-image/src/Types.ts b/addons/addon-image/src/Types.ts index 19a1acca1..9a70f4399 100644 --- a/addons/addon-image/src/Types.ts +++ b/addons/addon-image/src/Types.ts @@ -6,13 +6,13 @@ import { IDisposable, IMarker, Terminal } from '@xterm/xterm'; // private imports from base repo we build against -import { Attributes, BgFlags, Content, ExtFlags, UnderlineStyle } from 'common/buffer/Constants'; -import type { AttributeData } from 'common/buffer/AttributeData'; -import type { IParams, IDcsHandler, IOscHandler, IApcHandler, IEscapeSequenceParser } from 'common/parser/Types'; -import type { IBufferLine, IExtendedAttrs, IInputHandler } from 'common/Types'; -import type { ITerminal, ReadonlyColorSet } from 'browser/Types'; -import type { IRenderDimensions } from 'browser/renderer/shared/Types'; -import type { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; +import { Attributes, BgFlags, Content, ExtFlags, UnderlineStyle } from '../../../src/common/buffer/Constants'; +import type { AttributeData } from '../../../src/common/buffer/AttributeData'; +import type { IParams, IDcsHandler, IOscHandler, IApcHandler, IEscapeSequenceParser } from '../../../src/common/parser/Types'; +import type { IBufferLine, IExtendedAttrs, IInputHandler } from '../../../src/common/Types'; +import type { ITerminal, ReadonlyColorSet } from '../../../src/browser/Types'; +import type { IRenderDimensions } from '../../../src/browser/renderer/shared/Types'; +import type { ICoreBrowserService, IRenderService, IThemeService } from '../../../src/browser/services/Services'; export const enum Cell { CONTENT = 0, // codepoint and wcwidth information (enum Content) diff --git a/addons/addon-image/src/tsconfig.json b/addons/addon-image/src/tsconfig.json index 9a5d24c1f..18e276a35 100644 --- a/addons/addon-image/src/tsconfig.json +++ b/addons/addon-image/src/tsconfig.json @@ -12,10 +12,9 @@ "../../../node_modules/@types/mocha" ], "paths": { - "browser/*": [ "../../../src/browser/*" ], - "common/*": [ "../../../src/common/*" ], - "@xterm/addon-image": [ "../typings/addon-image.d.ts" ], - "*": [ "./*" ] + "@xterm/addon-image": [ + "../typings/addon-image.d.ts" + ] } }, "include": [ @@ -23,7 +22,11 @@ "../../../typings/xterm.d.ts" ], "references": [ - { "path": "../../../src/browser" }, - { "path": "../../../src/common" } + { + "path": "../../../src/browser" + }, + { + "path": "../../../src/common" + } ] } diff --git a/addons/addon-image/test/tsconfig.json b/addons/addon-image/test/tsconfig.json index 900a727ed..73f17c2a5 100644 --- a/addons/addon-image/test/tsconfig.json +++ b/addons/addon-image/test/tsconfig.json @@ -10,12 +10,6 @@ "sourceMap": true, "removeComments": true, "paths": { - "common/*": [ - "../../../src/common/*" - ], - "browser/*": [ - "../../../src/browser/*" - ], "*": [ "./*" ] diff --git a/addons/addon-image/webpack.config.js b/addons/addon-image/webpack.config.js index 01147d7e7..ba2fa35a4 100644 --- a/addons/addon-image/webpack.config.js +++ b/addons/addon-image/webpack.config.js @@ -24,11 +24,6 @@ const addon = { resolve: { modules: ['./node_modules'], extensions: [ '.js' ], - alias: { - common: path.resolve('../../out/common'), - browser: path.resolve('../../out/browser'), - vs: path.resolve('../../out/vs') - } }, output: { filename: mainFile, diff --git a/addons/addon-ligatures/test/tsconfig.json b/addons/addon-ligatures/test/tsconfig.json index 6986e2b46..25a92c8e4 100644 --- a/addons/addon-ligatures/test/tsconfig.json +++ b/addons/addon-ligatures/test/tsconfig.json @@ -10,11 +10,6 @@ "sourceMap": true, "removeComments": true, "strict": true, - "paths": { - "*": [ - "./*" - ] - }, "types": [ "../../../node_modules/@types/mocha", "../../../node_modules/@types/node" diff --git a/addons/addon-progress/src/ProgressAddon.ts b/addons/addon-progress/src/ProgressAddon.ts index 9d71179e1..e474edca3 100644 --- a/addons/addon-progress/src/ProgressAddon.ts +++ b/addons/addon-progress/src/ProgressAddon.ts @@ -5,7 +5,7 @@ import type { Terminal, ITerminalAddon, IDisposable } from '@xterm/xterm'; import type { ProgressAddon as IProgressApi, IProgressState } from '@xterm/addon-progress'; -import type { Emitter, IEvent } from 'common/Event'; +import type { Emitter, IEvent } from '../../../src/common/Event'; const enum ProgressType { diff --git a/addons/addon-progress/src/tsconfig.json b/addons/addon-progress/src/tsconfig.json index bd437f5b6..3d64b7791 100644 --- a/addons/addon-progress/src/tsconfig.json +++ b/addons/addon-progress/src/tsconfig.json @@ -15,12 +15,6 @@ "../../../node_modules/@types/mocha" ], "paths": { - "common/*": [ - "../../../src/common/*" - ], - "browser/*": [ - "../../../src/browser/*" - ], "@xterm/addon-progress": [ "../typings/addon-progress.d.ts" ] diff --git a/addons/addon-progress/test/tsconfig.json b/addons/addon-progress/test/tsconfig.json index 900a727ed..73f17c2a5 100644 --- a/addons/addon-progress/test/tsconfig.json +++ b/addons/addon-progress/test/tsconfig.json @@ -10,12 +10,6 @@ "sourceMap": true, "removeComments": true, "paths": { - "common/*": [ - "../../../src/common/*" - ], - "browser/*": [ - "../../../src/browser/*" - ], "*": [ "./*" ] diff --git a/addons/addon-progress/webpack.config.js b/addons/addon-progress/webpack.config.js index 1bf29bfd2..250b04a49 100644 --- a/addons/addon-progress/webpack.config.js +++ b/addons/addon-progress/webpack.config.js @@ -24,11 +24,6 @@ module.exports = { resolve: { modules: ['./node_modules'], extensions: [ '.js' ], - alias: { - common: path.resolve('../../out/common'), - browser: path.resolve('../../out/browser'), - vs: path.resolve('../../out/vs') - } }, output: { filename: mainFile, diff --git a/addons/addon-search/src/DecorationManager.ts b/addons/addon-search/src/DecorationManager.ts index a8ec12017..1675ef10f 100644 --- a/addons/addon-search/src/DecorationManager.ts +++ b/addons/addon-search/src/DecorationManager.ts @@ -5,7 +5,7 @@ import type { Terminal, IDisposable, IDecoration } from '@xterm/xterm'; import type { ISearchDecorationOptions } from '@xterm/addon-search'; -import { dispose, Disposable, toDisposable } from 'common/Lifecycle'; +import { dispose, Disposable, toDisposable } from '../../../src/common/Lifecycle'; import type { ISearchResult } from './SearchEngine'; /** diff --git a/addons/addon-search/src/SearchAddon.ts b/addons/addon-search/src/SearchAddon.ts index cb4094743..720ed26f0 100644 --- a/addons/addon-search/src/SearchAddon.ts +++ b/addons/addon-search/src/SearchAddon.ts @@ -5,9 +5,9 @@ import type { Terminal, IDisposable, ITerminalAddon } from '@xterm/xterm'; import type { SearchAddon as ISearchApi, ISearchOptions, ISearchAddonOptions, ISearchResultChangeEvent, ISearchDecorationOptions } from '@xterm/addon-search'; -import { Emitter, type IEvent } from 'common/Event'; -import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; -import { disposableTimeout } from 'common/Async'; +import { Emitter, type IEvent } from '../../../src/common/Event'; +import { Disposable, MutableDisposable, toDisposable } from '../../../src/common/Lifecycle'; +import { disposableTimeout } from '../../../src/common/Async'; import { SearchLineCache } from './SearchLineCache'; import { SearchState } from './SearchState'; import { SearchEngine, type ISearchResult } from './SearchEngine'; diff --git a/addons/addon-search/src/SearchEngine.test.ts b/addons/addon-search/src/SearchEngine.test.ts index b3cea8f54..e2ab9939b 100644 --- a/addons/addon-search/src/SearchEngine.test.ts +++ b/addons/addon-search/src/SearchEngine.test.ts @@ -5,9 +5,9 @@ import { assert } from 'chai'; import { SearchEngine } from './SearchEngine'; import { SearchLineCache } from './SearchLineCache'; -import { Terminal } from 'browser/public/Terminal'; +import { Terminal } from '../../../src/browser/public/Terminal'; import type { ISearchOptions } from '@xterm/addon-search'; -import { DisposableStore } from 'common/Lifecycle'; +import { DisposableStore } from '../../../src/common/Lifecycle'; function writeP(terminal: Terminal, data: string): Promise { return new Promise(r => terminal.write(data, r)); diff --git a/addons/addon-search/src/SearchLineCache.test.ts b/addons/addon-search/src/SearchLineCache.test.ts index 77f64c81e..1a465858c 100644 --- a/addons/addon-search/src/SearchLineCache.test.ts +++ b/addons/addon-search/src/SearchLineCache.test.ts @@ -4,8 +4,8 @@ */ import { assert } from 'chai'; import { SearchLineCache, LineCacheEntry } from './SearchLineCache'; -import { Terminal } from 'browser/public/Terminal'; -import { timeout } from 'common/Async'; +import { Terminal } from '../../../src/browser/public/Terminal'; +import { timeout } from '../../../src/common/Async'; function writeP(terminal: Terminal, data: string): Promise { return new Promise(r => terminal.write(data, r)); diff --git a/addons/addon-search/src/SearchLineCache.ts b/addons/addon-search/src/SearchLineCache.ts index 526f4bfcc..1b590ec74 100644 --- a/addons/addon-search/src/SearchLineCache.ts +++ b/addons/addon-search/src/SearchLineCache.ts @@ -4,8 +4,8 @@ */ import type { Terminal } from '@xterm/xterm'; -import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; -import { disposableTimeout } from 'common/Async'; +import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from '../../../src/common/Lifecycle'; +import { disposableTimeout } from '../../../src/common/Async'; export type LineCacheEntry = [ /** diff --git a/addons/addon-search/src/SearchResultTracker.ts b/addons/addon-search/src/SearchResultTracker.ts index 285345b7a..9c7997741 100644 --- a/addons/addon-search/src/SearchResultTracker.ts +++ b/addons/addon-search/src/SearchResultTracker.ts @@ -5,8 +5,8 @@ import type { ISearchResultChangeEvent } from '@xterm/addon-search'; import type { IDisposable } from '@xterm/xterm'; -import { Emitter, type IEvent } from 'common/Event'; -import { Disposable } from 'common/Lifecycle'; +import { Emitter, type IEvent } from '../../../src/common/Event'; +import { Disposable } from '../../../src/common/Lifecycle'; import type { ISearchResult } from './SearchEngine'; /** diff --git a/addons/addon-search/src/tsconfig.json b/addons/addon-search/src/tsconfig.json index 9817710fa..7e05af738 100644 --- a/addons/addon-search/src/tsconfig.json +++ b/addons/addon-search/src/tsconfig.json @@ -15,12 +15,6 @@ "../../../node_modules/@types/mocha" ], "paths": { - "common/*": [ - "../../../src/common/*" - ], - "browser/*": [ - "../../../src/browser/*" - ], "@xterm/addon-search" : [ "../typings/addon-search.d.ts" ] diff --git a/addons/addon-search/test/tsconfig.json b/addons/addon-search/test/tsconfig.json index 900a727ed..73f17c2a5 100644 --- a/addons/addon-search/test/tsconfig.json +++ b/addons/addon-search/test/tsconfig.json @@ -10,12 +10,6 @@ "sourceMap": true, "removeComments": true, "paths": { - "common/*": [ - "../../../src/common/*" - ], - "browser/*": [ - "../../../src/browser/*" - ], "*": [ "./*" ] diff --git a/addons/addon-search/webpack.config.js b/addons/addon-search/webpack.config.js index adc3d4bdb..9410c84d0 100644 --- a/addons/addon-search/webpack.config.js +++ b/addons/addon-search/webpack.config.js @@ -24,10 +24,6 @@ module.exports = { resolve: { modules: ['./node_modules'], extensions: [ '.js' ], - alias: { - common: path.resolve('../../out/common'), - vs: path.resolve('../../out/vs') - } }, output: { filename: mainFile, diff --git a/addons/addon-serialize/benchmark/SerializeAddon.benchmark.ts b/addons/addon-serialize/benchmark/SerializeAddon.benchmark.ts index b60dba1dd..0b0130cca 100644 --- a/addons/addon-serialize/benchmark/SerializeAddon.benchmark.ts +++ b/addons/addon-serialize/benchmark/SerializeAddon.benchmark.ts @@ -6,8 +6,8 @@ import { perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark'; import { spawn } from 'node-pty'; -import { Utf8ToUtf32, stringFromCodePoint } from 'common/input/TextDecoder'; -import { Terminal } from 'browser/public/Terminal'; +import { Utf8ToUtf32, stringFromCodePoint } from '../../../src/common/input/TextDecoder'; +import { Terminal } from '../../../src/browser/public/Terminal'; import { SerializeAddon } from 'SerializeAddon'; class TestTerminal extends Terminal { diff --git a/addons/addon-serialize/benchmark/tsconfig.json b/addons/addon-serialize/benchmark/tsconfig.json index ad7ee6d04..f187b3c8f 100644 --- a/addons/addon-serialize/benchmark/tsconfig.json +++ b/addons/addon-serialize/benchmark/tsconfig.json @@ -6,24 +6,36 @@ ], "rootDir": "..", "outDir": "../out-benchmark", - "types": ["../../../node_modules/@types/node"], + "types": [ + "../../../node_modules/@types/node" + ], "moduleResolution": "nodenext", "strict": false, "target": "es2021", "module": "nodenext", "paths": { - "common/*": ["../../../src/common/*"], - "browser/*": ["../../../src/browser/*"], - "SerializeAddon": ["../src/SerializeAddon"], + "SerializeAddon": [ + "../src/SerializeAddon" + ], "@xterm/addon-serialize": [ "../typings/addon-serialize.d.ts" ] } }, - "include": ["../**/*", "../../../typings/xterm.d.ts"], - "exclude": ["../../../**/*test.ts", "../../**/*api.ts"], + "include": [ + "../**/*", + "../../../typings/xterm.d.ts" + ], + "exclude": [ + "../../../**/*test.ts", + "../../**/*api.ts" + ], "references": [ - { "path": "../../../src/common" }, - { "path": "../../../src/browser" } + { + "path": "../../../src/common" + }, + { + "path": "../../../src/browser" + } ] } diff --git a/addons/addon-serialize/package.json b/addons/addon-serialize/package.json index 6fa91ffee..3258043ef 100644 --- a/addons/addon-serialize/package.json +++ b/addons/addon-serialize/package.json @@ -21,8 +21,8 @@ "package": "../../node_modules/.bin/webpack", "prepublishOnly": "npm run package", "start": "node ../../demo/start", - "benchmark": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json", - "benchmark-baseline": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --baseline out-benchmark/benchmark/*benchmark.js", - "benchmark-eval": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --eval out-benchmark/benchmark/*benchmark.js" + "benchmark": "../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json", + "benchmark-baseline": "../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --baseline out-benchmark/benchmark/*benchmark.js", + "benchmark-eval": "../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --eval out-benchmark/benchmark/*benchmark.js" } } diff --git a/addons/addon-serialize/src/SerializeAddon.test.ts b/addons/addon-serialize/src/SerializeAddon.test.ts index 4f4fd5461..0cfc83258 100644 --- a/addons/addon-serialize/src/SerializeAddon.test.ts +++ b/addons/addon-serialize/src/SerializeAddon.test.ts @@ -6,10 +6,10 @@ import jsdom = require('jsdom'); import { assert } from 'chai'; import { SerializeAddon } from './SerializeAddon'; -import { Terminal } from 'browser/public/Terminal'; -import { SelectionModel } from 'browser/selection/SelectionModel'; -import { IBufferService } from 'common/services/Services'; -import { ThemeService } from 'browser/services/ThemeService'; +import { Terminal } from '../../../src/browser/public/Terminal'; +import { SelectionModel } from '../../../src/browser/selection/SelectionModel'; +import { IBufferService } from '../../../src/common/services/Services'; +import { ThemeService } from '../../../src/browser/services/ThemeService'; function sgr(...seq: string[]): string { return `\x1b[${seq.join(';')}m`; diff --git a/addons/addon-serialize/src/SerializeAddon.ts b/addons/addon-serialize/src/SerializeAddon.ts index 4980c8eda..fbcc65cfa 100644 --- a/addons/addon-serialize/src/SerializeAddon.ts +++ b/addons/addon-serialize/src/SerializeAddon.ts @@ -7,9 +7,9 @@ import type { IBuffer, IBufferCell, IBufferRange, ITerminalAddon, Terminal } from '@xterm/xterm'; import type { IHTMLSerializeOptions, SerializeAddon as ISerializeApi, ISerializeOptions, ISerializeRange } from '@xterm/addon-serialize'; -import { IAttributeData, IColor } from 'common/Types'; -import { DEFAULT_ANSI_COLORS } from 'browser/Types'; -import { UnderlineStyle } from 'common/buffer/Constants'; +import { IAttributeData, IColor } from '../../../src/common/Types'; +import { DEFAULT_ANSI_COLORS } from '../../../src/browser/Types'; +import { UnderlineStyle } from '../../../src/common/buffer/Constants'; function constrain(value: number, low: number, high: number): number { return Math.max(low, Math.min(value, high)); diff --git a/addons/addon-serialize/src/tsconfig.json b/addons/addon-serialize/src/tsconfig.json index 725647366..8176e660e 100644 --- a/addons/addon-serialize/src/tsconfig.json +++ b/addons/addon-serialize/src/tsconfig.json @@ -11,12 +11,6 @@ "sourceMap": true, "removeComments": true, "paths": { - "common/*": [ - "../../../src/common/*" - ], - "browser/*": [ - "../../../src/browser/*" - ], "@xterm/addon-serialize": [ "../typings/addon-serialize.d.ts" ] diff --git a/addons/addon-serialize/test/tsconfig.json b/addons/addon-serialize/test/tsconfig.json index 769909b19..9d3f4722c 100644 --- a/addons/addon-serialize/test/tsconfig.json +++ b/addons/addon-serialize/test/tsconfig.json @@ -9,14 +9,6 @@ "outDir": "../out-test", "sourceMap": true, "removeComments": true, - "paths": { - "common/*": [ - "../../../src/common/*" - ], - "browser/*": [ - "../../../src/browser/*" - ] - }, "strict": true, "types": [ "../../../node_modules/@types/node" diff --git a/addons/addon-serialize/webpack.config.js b/addons/addon-serialize/webpack.config.js index 837a73a38..c38ab9f2b 100644 --- a/addons/addon-serialize/webpack.config.js +++ b/addons/addon-serialize/webpack.config.js @@ -24,10 +24,6 @@ module.exports = { resolve: { modules: ['./node_modules'], extensions: [ '.js' ], - alias: { - common: path.resolve('../../out/common'), - browser: path.resolve('../../out/browser') - } }, output: { filename: mainFile, diff --git a/addons/addon-unicode-graphemes/benchmark/UnicodeGraphemeAddon.benchmark.ts b/addons/addon-unicode-graphemes/benchmark/UnicodeGraphemeAddon.benchmark.ts index 7538b9344..74b434814 100644 --- a/addons/addon-unicode-graphemes/benchmark/UnicodeGraphemeAddon.benchmark.ts +++ b/addons/addon-unicode-graphemes/benchmark/UnicodeGraphemeAddon.benchmark.ts @@ -6,8 +6,8 @@ import { perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark'; import { spawn } from 'node-pty'; -import { Utf8ToUtf32, stringFromCodePoint } from 'common/input/TextDecoder'; -import { CoreBrowserTerminal } from 'browser/CoreBrowserTerminal'; +import { Utf8ToUtf32, stringFromCodePoint } from '../../../src/common/input/TextDecoder'; +import { CoreBrowserTerminal } from '../../../src/browser/CoreBrowserTerminal'; import { UnicodeGraphemeProvider } from 'UnicodeGraphemeProvider'; diff --git a/addons/addon-unicode-graphemes/benchmark/tsconfig.json b/addons/addon-unicode-graphemes/benchmark/tsconfig.json index 1781ca318..75fda6d08 100644 --- a/addons/addon-unicode-graphemes/benchmark/tsconfig.json +++ b/addons/addon-unicode-graphemes/benchmark/tsconfig.json @@ -1,26 +1,41 @@ { "compilerOptions": { - "lib": ["dom", "es6"], + "lib": [ + "dom", + "es6" + ], "rootDir": "..", "outDir": "../out-benchmark", - "types": ["../../../node_modules/@types/node"], + "types": [ + "../../../node_modules/@types/node" + ], "moduleResolution": "nodenext", "strict": false, "target": "es2015", "module": "nodenext", "paths": { - "common/*": ["../../../src/common/*"], - "browser/*": ["../../../src/browser/*"], - "UnicodeGraphemeProvider": ["../src/UnicodeGraphemeProvider"], + "UnicodeGraphemeProvider": [ + "../src/UnicodeGraphemeProvider" + ], "@xterm/addon-unicode-graphemes": [ "../typings/addon-unicode-graphemes.d.ts" ] } }, - "include": ["../**/*", "../../../typings/xterm.d.ts"], - "exclude": ["../../../**/*test.ts", "../../**/*api.ts"], + "include": [ + "../**/*", + "../../../typings/xterm.d.ts" + ], + "exclude": [ + "../../../**/*test.ts", + "../../**/*api.ts" + ], "references": [ - { "path": "../../../src/common" }, - { "path": "../../../src/browser" } + { + "path": "../../../src/common" + }, + { + "path": "../../../src/browser" + } ] } diff --git a/addons/addon-unicode-graphemes/package.json b/addons/addon-unicode-graphemes/package.json index 640a61a7d..1848bebfc 100644 --- a/addons/addon-unicode-graphemes/package.json +++ b/addons/addon-unicode-graphemes/package.json @@ -21,8 +21,8 @@ "package": "../../node_modules/.bin/webpack", "prepublishOnly": "npm run package", "start": "node ../../demo/start", - "benchmark": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json out-benchmark/benchmark/*benchmark.js", - "benchmark-baseline": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --baseline out-benchmark/benchmark/*benchmark.js", - "benchmark-eval": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --eval out-benchmark/benchmark/*benchmark.js" + "benchmark": "../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json out-benchmark/benchmark/*benchmark.js", + "benchmark-baseline": "../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --baseline out-benchmark/benchmark/*benchmark.js", + "benchmark-eval": "../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --eval out-benchmark/benchmark/*benchmark.js" } } diff --git a/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts b/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts index 3d5717a0f..ed258805d 100644 --- a/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts +++ b/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts @@ -4,8 +4,8 @@ */ import { IUnicodeVersionProvider } from '@xterm/xterm'; -import { UnicodeCharProperties, UnicodeCharWidth } from 'common/services/Services'; -import { UnicodeService } from 'common/services/UnicodeService'; +import { UnicodeCharProperties, UnicodeCharWidth } from '../../../src/common/services/Services'; +import { UnicodeService } from '../../../src/common/services/UnicodeService'; import * as UC from './third-party/UnicodeProperties'; export class UnicodeGraphemeProvider implements IUnicodeVersionProvider { diff --git a/addons/addon-unicode-graphemes/src/tsconfig.json b/addons/addon-unicode-graphemes/src/tsconfig.json index 824909dbd..de62f95cb 100644 --- a/addons/addon-unicode-graphemes/src/tsconfig.json +++ b/addons/addon-unicode-graphemes/src/tsconfig.json @@ -12,9 +12,6 @@ "removeComments": true, "strict": true, "paths": { - "common/*": [ - "../../../src/common/*" - ], "@xterm/addon-unicode-graphemes": [ "../typings/addon-unicode-graphemes.d.ts" ] diff --git a/addons/addon-unicode-graphemes/test/tsconfig.json b/addons/addon-unicode-graphemes/test/tsconfig.json index 769909b19..9d3f4722c 100644 --- a/addons/addon-unicode-graphemes/test/tsconfig.json +++ b/addons/addon-unicode-graphemes/test/tsconfig.json @@ -9,14 +9,6 @@ "outDir": "../out-test", "sourceMap": true, "removeComments": true, - "paths": { - "common/*": [ - "../../../src/common/*" - ], - "browser/*": [ - "../../../src/browser/*" - ] - }, "strict": true, "types": [ "../../../node_modules/@types/node" diff --git a/addons/addon-unicode-graphemes/webpack.config.js b/addons/addon-unicode-graphemes/webpack.config.js index 35e111d53..b3934006e 100644 --- a/addons/addon-unicode-graphemes/webpack.config.js +++ b/addons/addon-unicode-graphemes/webpack.config.js @@ -24,10 +24,6 @@ module.exports = { resolve: { modules: ['./node_modules'], extensions: [ '.js' ], - alias: { - common: path.resolve('../../out/common'), - vs: path.resolve('../../out/vs') - } }, output: { filename: mainFile, diff --git a/addons/addon-unicode11/src/UnicodeV11.ts b/addons/addon-unicode11/src/UnicodeV11.ts index 79568960a..d80b60b80 100644 --- a/addons/addon-unicode11/src/UnicodeV11.ts +++ b/addons/addon-unicode11/src/UnicodeV11.ts @@ -4,8 +4,8 @@ */ import { IUnicodeVersionProvider } from '@xterm/xterm'; -import { UnicodeCharProperties, UnicodeCharWidth } from 'common/services/Services'; -import { UnicodeService } from 'common/services/UnicodeService'; +import { UnicodeCharProperties, UnicodeCharWidth } from '../../../src/common/services/Services'; +import { UnicodeService } from '../../../src/common/services/UnicodeService'; const BMP_COMBINING = [ [0x0300, 0x036F], [0x0483, 0x0489], [0x0591, 0x05BD], diff --git a/addons/addon-unicode11/src/tsconfig.json b/addons/addon-unicode11/src/tsconfig.json index 31286fb0f..a5b97d43e 100644 --- a/addons/addon-unicode11/src/tsconfig.json +++ b/addons/addon-unicode11/src/tsconfig.json @@ -12,14 +12,8 @@ "removeComments": true, "strict": true, "paths": { - "common/*": [ - "../../../src/common/*" - ], "@xterm/addon-unicode11": [ "../typings/addon-unicode11.d.ts" - ], - "*": [ - "./*" ] }, "types": [ diff --git a/addons/addon-unicode11/test/tsconfig.json b/addons/addon-unicode11/test/tsconfig.json index 900a727ed..73f17c2a5 100644 --- a/addons/addon-unicode11/test/tsconfig.json +++ b/addons/addon-unicode11/test/tsconfig.json @@ -10,12 +10,6 @@ "sourceMap": true, "removeComments": true, "paths": { - "common/*": [ - "../../../src/common/*" - ], - "browser/*": [ - "../../../src/browser/*" - ], "*": [ "./*" ] diff --git a/addons/addon-unicode11/webpack.config.js b/addons/addon-unicode11/webpack.config.js index 9b66049fd..f3ae10eef 100644 --- a/addons/addon-unicode11/webpack.config.js +++ b/addons/addon-unicode11/webpack.config.js @@ -24,10 +24,6 @@ module.exports = { resolve: { modules: ['./node_modules'], extensions: [ '.js' ], - alias: { - common: path.resolve('../../out/common'), - vs: path.resolve('../../out/vs') - } }, output: { filename: mainFile, diff --git a/addons/addon-web-fonts/test/tsconfig.json b/addons/addon-web-fonts/test/tsconfig.json index 59664e32b..66097910d 100644 --- a/addons/addon-web-fonts/test/tsconfig.json +++ b/addons/addon-web-fonts/test/tsconfig.json @@ -10,12 +10,6 @@ "sourceMap": true, "removeComments": true, "paths": { - "common/*": [ - "../../../src/common/*" - ], - "browser/*": [ - "../../../src/browser/*" - ], "*": [ "./*" ] diff --git a/addons/addon-web-links/test/tsconfig.json b/addons/addon-web-links/test/tsconfig.json index 59664e32b..66097910d 100644 --- a/addons/addon-web-links/test/tsconfig.json +++ b/addons/addon-web-links/test/tsconfig.json @@ -10,12 +10,6 @@ "sourceMap": true, "removeComments": true, "paths": { - "common/*": [ - "../../../src/common/*" - ], - "browser/*": [ - "../../../src/browser/*" - ], "*": [ "./*" ] diff --git a/addons/addon-webgl/src/CellColorResolver.ts b/addons/addon-webgl/src/CellColorResolver.ts index 3a4661cc3..52e19a823 100644 --- a/addons/addon-webgl/src/CellColorResolver.ts +++ b/addons/addon-webgl/src/CellColorResolver.ts @@ -1,12 +1,12 @@ -import { ISelectionRenderModel } from 'browser/renderer/shared/Types'; -import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; -import { ReadonlyColorSet } from 'browser/Types'; -import { Attributes, BgFlags, ExtFlags, FgFlags, NULL_CELL_CODE, UnderlineStyle } from 'common/buffer/Constants'; -import { IDecorationService, IOptionsService } from 'common/services/Services'; -import { ICellData } from 'common/Types'; +import { ISelectionRenderModel } from '../../../src/browser/renderer/shared/Types'; +import { ICoreBrowserService, IThemeService } from '../../../src/browser/services/Services'; +import { ReadonlyColorSet } from '../../../src/browser/Types'; +import { Attributes, BgFlags, ExtFlags, FgFlags, NULL_CELL_CODE, UnderlineStyle } from '../../../src/common/buffer/Constants'; +import { IDecorationService, IOptionsService } from '../../../src/common/services/Services'; +import { ICellData } from '../../../src/common/Types'; import { Terminal } from '@xterm/xterm'; -import { rgba } from 'common/Color'; -import { treatGlyphAsBackgroundColor } from 'browser/renderer/shared/RendererUtils'; +import { rgba } from '../../../src/common/Color'; +import { treatGlyphAsBackgroundColor } from '../../../src/browser/renderer/shared/RendererUtils'; import { blockPatternCodepoints } from './customGlyphs/CustomGlyphDefinitions'; // Work variables to avoid garbage collection diff --git a/addons/addon-webgl/src/CharAtlasCache.ts b/addons/addon-webgl/src/CharAtlasCache.ts index 823a74cc6..9aa165f12 100644 --- a/addons/addon-webgl/src/CharAtlasCache.ts +++ b/addons/addon-webgl/src/CharAtlasCache.ts @@ -5,10 +5,10 @@ import { TextureAtlas } from './TextureAtlas'; import { ITerminalOptions, Terminal } from '@xterm/xterm'; -import { ITerminal, ReadonlyColorSet } from 'browser/Types'; +import { ITerminal, ReadonlyColorSet } from '../../../src/browser/Types'; import { ICharAtlasConfig, ITextureAtlas } from './Types'; import { generateConfig, configEquals } from './CharAtlasUtils'; -import type { ILogService } from 'common/services/Services'; +import type { ILogService } from '../../../src/common/services/Services'; interface ITextureAtlasCacheEntry { atlas: ITextureAtlas; diff --git a/addons/addon-webgl/src/CharAtlasUtils.test.ts b/addons/addon-webgl/src/CharAtlasUtils.test.ts index 0a135746e..572989410 100644 --- a/addons/addon-webgl/src/CharAtlasUtils.test.ts +++ b/addons/addon-webgl/src/CharAtlasUtils.test.ts @@ -6,8 +6,8 @@ import { assert } from 'chai'; import { configEquals } from './CharAtlasUtils'; import { ICharAtlasConfig } from './Types'; -import { NULL_COLOR } from 'common/Color'; -import { IColor } from 'common/Types'; +import { NULL_COLOR } from '../../../src/common/Color'; +import { IColor } from '../../../src/common/Types'; function createTestConfig(overrides: Partial = {}): ICharAtlasConfig { const color: IColor = { css: '#ffffff', rgba: 0xffffffff }; diff --git a/addons/addon-webgl/src/CharAtlasUtils.ts b/addons/addon-webgl/src/CharAtlasUtils.ts index d9ca69b39..6a98c9a8d 100644 --- a/addons/addon-webgl/src/CharAtlasUtils.ts +++ b/addons/addon-webgl/src/CharAtlasUtils.ts @@ -4,10 +4,10 @@ */ import { ICharAtlasConfig } from './Types'; -import { Attributes } from 'common/buffer/Constants'; +import { Attributes } from '../../../src/common/buffer/Constants'; import { ITerminalOptions } from '@xterm/xterm'; -import { IColorSet, ReadonlyColorSet } from 'browser/Types'; -import { NULL_COLOR } from 'common/Color'; +import { IColorSet, ReadonlyColorSet } from '../../../src/browser/Types'; +import { NULL_COLOR } from '../../../src/common/Color'; export function generateConfig(deviceCellWidth: number, deviceCellHeight: number, deviceCharWidth: number, deviceCharHeight: number, options: Required, colors: ReadonlyColorSet, devicePixelRatio: number, deviceMaxTextureSize: number, customGlyphs: boolean = true): ICharAtlasConfig { // null out some fields that don't matter diff --git a/addons/addon-webgl/src/Constants.ts b/addons/addon-webgl/src/Constants.ts index a00434d61..e37aecdb4 100644 --- a/addons/addon-webgl/src/Constants.ts +++ b/addons/addon-webgl/src/Constants.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { isFirefox, isLegacyEdge } from 'common/Platform'; +import { isFirefox, isLegacyEdge } from '../../../src/common/Platform'; export const DIM_OPACITY = 0.5; // The text baseline is set conditionally by browser. Using 'ideographic' for Firefox or Legacy Edge diff --git a/addons/addon-webgl/src/CursorBlinkStateManager.ts b/addons/addon-webgl/src/CursorBlinkStateManager.ts index 688aee7b7..1339dae03 100644 --- a/addons/addon-webgl/src/CursorBlinkStateManager.ts +++ b/addons/addon-webgl/src/CursorBlinkStateManager.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { RendererConstants } from 'browser/renderer/shared/Constants'; -import { ICoreBrowserService } from 'browser/services/Services'; +import { RendererConstants } from '../../../src/browser/renderer/shared/Constants'; +import { ICoreBrowserService } from '../../../src/browser/services/Services'; const enum Constants { /** diff --git a/addons/addon-webgl/src/DevicePixelObserver.ts b/addons/addon-webgl/src/DevicePixelObserver.ts index dcd8de0a6..50ed09941 100644 --- a/addons/addon-webgl/src/DevicePixelObserver.ts +++ b/addons/addon-webgl/src/DevicePixelObserver.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { toDisposable, IDisposable } from 'common/Lifecycle'; +import { toDisposable, IDisposable } from '../../../src/common/Lifecycle'; export function observeDevicePixelDimensions(element: HTMLElement, parentWindow: Window & typeof globalThis, callback: (deviceWidth: number, deviceHeight: number) => void): IDisposable { // Observe any resizes to the element and extract the actual pixel size of the element if the diff --git a/addons/addon-webgl/src/GlyphRenderer.ts b/addons/addon-webgl/src/GlyphRenderer.ts index 650fc451a..915979206 100644 --- a/addons/addon-webgl/src/GlyphRenderer.ts +++ b/addons/addon-webgl/src/GlyphRenderer.ts @@ -3,14 +3,14 @@ * @license MIT */ import { TextureAtlas } from './TextureAtlas'; -import { IRenderDimensions } from 'browser/renderer/shared/Types'; -import { NULL_CELL_CODE } from 'common/buffer/Constants'; -import { Disposable, toDisposable } from 'common/Lifecycle'; +import { IRenderDimensions } from '../../../src/browser/renderer/shared/Types'; +import { NULL_CELL_CODE } from '../../../src/common/buffer/Constants'; +import { Disposable, toDisposable } from '../../../src/common/Lifecycle'; import { Terminal } from '@xterm/xterm'; import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject, type IRasterizedGlyph, type ITextureAtlas } from './Types'; import { createProgram, GLTexture, PROJECTION_MATRIX } from './WebglUtils'; -import type { ILogService, IOptionsService } from 'common/services/Services'; -import { allowRescaling, throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; +import type { ILogService, IOptionsService } from '../../../src/common/services/Services'; +import { allowRescaling, throwIfFalsy } from '../../../src/browser/renderer/shared/RendererUtils'; interface IVertices { attributes: Float32Array; diff --git a/addons/addon-webgl/src/RectangleRenderer.ts b/addons/addon-webgl/src/RectangleRenderer.ts index eebfda3b3..0eb45a11c 100644 --- a/addons/addon-webgl/src/RectangleRenderer.ts +++ b/addons/addon-webgl/src/RectangleRenderer.ts @@ -3,18 +3,18 @@ * @license MIT */ -import { IRenderDimensions } from 'browser/renderer/shared/Types'; -import { IThemeService } from 'browser/services/Services'; -import { ReadonlyColorSet } from 'browser/Types'; -import { Attributes, FgFlags } from 'common/buffer/Constants'; -import { Disposable, toDisposable } from 'common/Lifecycle'; -import { IColor } from 'common/Types'; +import { IRenderDimensions } from '../../../src/browser/renderer/shared/Types'; +import { IThemeService } from '../../../src/browser/services/Services'; +import { ReadonlyColorSet } from '../../../src/browser/Types'; +import { Attributes, FgFlags } from '../../../src/common/buffer/Constants'; +import { Disposable, toDisposable } from '../../../src/common/Lifecycle'; +import { IColor } from '../../../src/common/Types'; import { Terminal } from '@xterm/xterm'; import { RenderModelConstants } from './RenderModel'; import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject } from './Types'; import { createProgram, expandFloat32Array, PROJECTION_MATRIX } from './WebglUtils'; -import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; -import type { ILogService } from 'common/services/Services'; +import { throwIfFalsy } from '../../../src/browser/renderer/shared/RendererUtils'; +import type { ILogService } from '../../../src/common/services/Services'; const enum VertexAttribLocations { POSITION = 0, diff --git a/addons/addon-webgl/src/RenderModel.ts b/addons/addon-webgl/src/RenderModel.ts index 6113a9bdc..e1fe54628 100644 --- a/addons/addon-webgl/src/RenderModel.ts +++ b/addons/addon-webgl/src/RenderModel.ts @@ -4,8 +4,8 @@ */ import { ICursorRenderModel, IRenderModel } from './Types'; -import { ISelectionRenderModel } from 'browser/renderer/shared/Types'; -import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel'; +import { ISelectionRenderModel } from '../../../src/browser/renderer/shared/Types'; +import { createSelectionRenderModel } from '../../../src/browser/renderer/shared/SelectionRenderModel'; export const enum RenderModelConstants { INDICIES_PER_CELL = 4, diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index dab0b1c85..a6dda7b71 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -3,19 +3,19 @@ * @license MIT */ -import { IColorContrastCache } from 'browser/Types'; +import { IColorContrastCache } from '../../../src/browser/Types'; import { DIM_OPACITY, TEXT_BASELINE } from './Constants'; import { tryDrawCustomGlyph } from './customGlyphs/CustomGlyphRasterizer'; -import { computeNextVariantOffset, treatGlyphAsBackgroundColor, isPowerlineGlyph, isRestrictedPowerlineGlyph, throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; +import { computeNextVariantOffset, treatGlyphAsBackgroundColor, isPowerlineGlyph, isRestrictedPowerlineGlyph, throwIfFalsy } from '../../../src/browser/renderer/shared/RendererUtils'; import { IBoundingBox, ICharAtlasConfig, IRasterizedGlyph, ITextureAtlas } from './Types'; -import { NULL_COLOR, channels, color, rgba } from 'common/Color'; -import { FourKeyMap } from 'common/MultiKeyMap'; -import { IdleTaskQueue } from 'common/TaskQueue'; -import { IColor } from 'common/Types'; -import { AttributeData } from 'common/buffer/AttributeData'; -import { Attributes, DEFAULT_COLOR, DEFAULT_EXT, UnderlineStyle } from 'common/buffer/Constants'; -import { ILogService, IUnicodeService } from 'common/services/Services'; -import { Emitter } from 'common/Event'; +import { NULL_COLOR, channels, color, rgba } from '../../../src/common/Color'; +import { FourKeyMap } from '../../../src/common/MultiKeyMap'; +import { IdleTaskQueue } from '../../../src/common/TaskQueue'; +import { IColor } from '../../../src/common/Types'; +import { AttributeData } from '../../../src/common/buffer/AttributeData'; +import { Attributes, DEFAULT_COLOR, DEFAULT_EXT, UnderlineStyle } from '../../../src/common/buffer/Constants'; +import { ILogService, IUnicodeService } from '../../../src/common/services/Services'; +import { Emitter } from '../../../src/common/Event'; /** * A shared object which is used to draw nothing for a particular cell. diff --git a/addons/addon-webgl/src/Types.ts b/addons/addon-webgl/src/Types.ts index 294582e37..913d9c8ab 100644 --- a/addons/addon-webgl/src/Types.ts +++ b/addons/addon-webgl/src/Types.ts @@ -4,10 +4,10 @@ */ import { FontWeight } from '@xterm/xterm'; -import { IColorSet } from 'browser/Types'; -import { ISelectionRenderModel } from 'browser/renderer/shared/Types'; -import { CursorInactiveStyle, CursorStyle, type IDisposable } from 'common/Types'; -import type { IEvent } from 'common/Event'; +import { IColorSet } from '../../../src/browser/Types'; +import { ISelectionRenderModel } from '../../../src/browser/renderer/shared/Types'; +import { CursorInactiveStyle, CursorStyle, type IDisposable } from '../../../src/common/Types'; +import type { IEvent } from '../../../src/common/Event'; export interface IRenderModel { cells: Uint32Array; diff --git a/addons/addon-webgl/src/WebglAddon.ts b/addons/addon-webgl/src/WebglAddon.ts index 624c9ea38..b370d2955 100644 --- a/addons/addon-webgl/src/WebglAddon.ts +++ b/addons/addon-webgl/src/WebglAddon.ts @@ -5,14 +5,14 @@ import type { ITerminalAddon, Terminal } from '@xterm/xterm'; import type { IWebglAddonOptions, WebglAddon as IWebglApi } from '@xterm/addon-webgl'; -import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; -import { ITerminal } from 'browser/Types'; -import { Disposable, toDisposable } from 'common/Lifecycle'; -import { getSafariVersion, isSafari } from 'common/Platform'; -import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services'; +import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from '../../../src/browser/services/Services'; +import { ITerminal } from '../../../src/browser/Types'; +import { Disposable, toDisposable } from '../../../src/common/Lifecycle'; +import { getSafariVersion, isSafari } from '../../../src/common/Platform'; +import { ICoreService, IDecorationService, ILogService, IOptionsService } from '../../../src/common/services/Services'; import { IWebGL2RenderingContext } from './Types'; import { WebglRenderer } from './WebglRenderer'; -import { Emitter, EventUtils } from 'common/Event'; +import { Emitter, EventUtils } from '../../../src/common/Event'; export class WebglAddon extends Disposable implements ITerminalAddon, IWebglApi { private _terminal?: Terminal; diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 9c2f459df..1302d6be2 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -3,19 +3,19 @@ * @license MIT */ -import { ITerminal } from 'browser/Types'; +import { ITerminal } from '../../../src/browser/Types'; import { CellColorResolver } from './CellColorResolver'; import { acquireTextureAtlas, removeTerminalFromCache } from './CharAtlasCache'; import { CursorBlinkStateManager } from './CursorBlinkStateManager'; import { observeDevicePixelDimensions } from './DevicePixelObserver'; -import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types'; -import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services'; -import { CharData, IBufferLine, ICellData } from 'common/Types'; -import { AttributeData } from 'common/buffer/AttributeData'; -import { CellData } from 'common/buffer/CellData'; -import { Attributes, Content, FgFlags, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants'; -import { TextBlinkStateManager } from 'browser/renderer/shared/TextBlinkStateManager'; -import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services'; +import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from '../../../src/browser/renderer/shared/Types'; +import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IThemeService } from '../../../src/browser/services/Services'; +import { CharData, IBufferLine, ICellData } from '../../../src/common/Types'; +import { AttributeData } from '../../../src/common/buffer/AttributeData'; +import { CellData } from '../../../src/common/buffer/CellData'; +import { Attributes, Content, FgFlags, NULL_CELL_CHAR, NULL_CELL_CODE } from '../../../src/common/buffer/Constants'; +import { TextBlinkStateManager } from '../../../src/browser/renderer/shared/TextBlinkStateManager'; +import { ICoreService, IDecorationService, ILogService, IOptionsService } from '../../../src/common/services/Services'; import { Terminal } from '@xterm/xterm'; import { GlyphRenderer } from './GlyphRenderer'; import { RectangleRenderer } from './RectangleRenderer'; @@ -23,10 +23,10 @@ import { COMBINED_CHAR_BIT_MASK, RenderModel, RenderModelConstants } from './Ren import { IWebGL2RenderingContext, type ITextureAtlas } from './Types'; import { LinkRenderLayer } from './renderLayer/LinkRenderLayer'; import { IRenderLayer } from './renderLayer/Types'; -import { Emitter, EventUtils } from 'common/Event'; -import { addDisposableListener } from 'browser/Dom'; -import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; -import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils'; +import { Emitter, EventUtils } from '../../../src/common/Event'; +import { addDisposableListener } from '../../../src/browser/Dom'; +import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from '../../../src/common/Lifecycle'; +import { createRenderDimensions } from '../../../src/browser/renderer/shared/RendererUtils'; const enum Constants { MERGE_RETRY_LIMIT = 32 diff --git a/addons/addon-webgl/src/WebglUtils.ts b/addons/addon-webgl/src/WebglUtils.ts index 9944c6bd3..b106f7d56 100644 --- a/addons/addon-webgl/src/WebglUtils.ts +++ b/addons/addon-webgl/src/WebglUtils.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; -import type { ILogService } from 'common/services/Services'; +import { throwIfFalsy } from '../../../src/browser/renderer/shared/RendererUtils'; +import type { ILogService } from '../../../src/common/services/Services'; /** * A matrix that when multiplies will translate 0-1 coordinates (left to right, diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts index 357d512d2..d1c9fd83c 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; -import type { ILogService } from 'common/services/Services'; +import { throwIfFalsy } from '../../../../src/browser/renderer/shared/RendererUtils'; +import type { ILogService } from '../../../../src/common/services/Services'; import { customGlyphDefinitions } from './CustomGlyphDefinitions'; import { CustomGlyphDefinitionType, CustomGlyphScaleType, CustomGlyphVectorType, type CustomGlyphDefinitionPart, type CustomGlyphPathDrawFunctionDefinition, type CustomGlyphPatternDefinition, type ICustomGlyphSolidOctantBlockVector, type ICustomGlyphVectorShape } from './Types'; diff --git a/addons/addon-webgl/src/renderLayer/BaseRenderLayer.ts b/addons/addon-webgl/src/renderLayer/BaseRenderLayer.ts index 5b1161532..4c286284d 100644 --- a/addons/addon-webgl/src/renderLayer/BaseRenderLayer.ts +++ b/addons/addon-webgl/src/renderLayer/BaseRenderLayer.ts @@ -3,16 +3,16 @@ * @license MIT */ -import { ReadonlyColorSet } from 'browser/Types'; +import { ReadonlyColorSet } from '../../../../src/browser/Types'; import { acquireTextureAtlas } from '../CharAtlasCache'; -import { IRenderDimensions } from 'browser/renderer/shared/Types'; -import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; -import { Disposable, toDisposable } from 'common/Lifecycle'; -import { CellData } from 'common/buffer/CellData'; -import { IOptionsService } from 'common/services/Services'; +import { IRenderDimensions } from '../../../../src/browser/renderer/shared/Types'; +import { ICoreBrowserService, IThemeService } from '../../../../src/browser/services/Services'; +import { Disposable, toDisposable } from '../../../../src/common/Lifecycle'; +import { CellData } from '../../../../src/common/buffer/CellData'; +import { IOptionsService } from '../../../../src/common/services/Services'; import { Terminal } from '@xterm/xterm'; import { IRenderLayer } from './Types'; -import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; +import { throwIfFalsy } from '../../../../src/browser/renderer/shared/RendererUtils'; import { TEXT_BASELINE } from '../Constants'; import type { ITextureAtlas } from '../Types'; diff --git a/addons/addon-webgl/src/renderLayer/LinkRenderLayer.ts b/addons/addon-webgl/src/renderLayer/LinkRenderLayer.ts index faceac44f..e076b9bd7 100644 --- a/addons/addon-webgl/src/renderLayer/LinkRenderLayer.ts +++ b/addons/addon-webgl/src/renderLayer/LinkRenderLayer.ts @@ -4,11 +4,11 @@ */ import { is256Color } from '../CharAtlasUtils'; -import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants'; -import { IRenderDimensions } from 'browser/renderer/shared/Types'; -import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; -import { ILinkifier2, ILinkifierEvent } from 'browser/Types'; -import { IOptionsService } from 'common/services/Services'; +import { INVERTED_DEFAULT_COLOR } from '../../../../src/browser/renderer/shared/Constants'; +import { IRenderDimensions } from '../../../../src/browser/renderer/shared/Types'; +import { ICoreBrowserService, IThemeService } from '../../../../src/browser/services/Services'; +import { ILinkifier2, ILinkifierEvent } from '../../../../src/browser/Types'; +import { IOptionsService } from '../../../../src/common/services/Services'; import { Terminal } from '@xterm/xterm'; import { BaseRenderLayer } from './BaseRenderLayer'; diff --git a/addons/addon-webgl/src/renderLayer/Types.ts b/addons/addon-webgl/src/renderLayer/Types.ts index feeceb145..74cd5e27b 100644 --- a/addons/addon-webgl/src/renderLayer/Types.ts +++ b/addons/addon-webgl/src/renderLayer/Types.ts @@ -4,7 +4,7 @@ */ import { IDisposable, Terminal } from '@xterm/xterm'; -import { IRenderDimensions } from 'browser/renderer/shared/Types'; +import { IRenderDimensions } from '../../../../src/browser/renderer/shared/Types'; export interface IRenderLayer extends IDisposable { /** diff --git a/addons/addon-webgl/src/tsconfig.json b/addons/addon-webgl/src/tsconfig.json index 7035daa86..99154a1ab 100644 --- a/addons/addon-webgl/src/tsconfig.json +++ b/addons/addon-webgl/src/tsconfig.json @@ -11,17 +11,8 @@ "sourceMap": true, "removeComments": true, "paths": { - "common/*": [ - "../../../src/common/*" - ], - "browser/*": [ - "../../../src/browser/*" - ], "@xterm/addon-webgl": [ "../typings/addon-webgl.d.ts" - ], - "*": [ - "./*" ] }, "strict": true, diff --git a/addons/addon-webgl/test/tsconfig.json b/addons/addon-webgl/test/tsconfig.json index 73439ddc1..3f3ca3dbc 100644 --- a/addons/addon-webgl/test/tsconfig.json +++ b/addons/addon-webgl/test/tsconfig.json @@ -10,12 +10,6 @@ "sourceMap": true, "removeComments": true, "paths": { - "common/*": [ - "../../../src/common/*" - ], - "browser/*": [ - "../../../src/browser/*" - ], "*": [ "./*" ] diff --git a/addons/addon-webgl/webpack.config.js b/addons/addon-webgl/webpack.config.js index 2eab992e7..81e2dc3a8 100644 --- a/addons/addon-webgl/webpack.config.js +++ b/addons/addon-webgl/webpack.config.js @@ -24,11 +24,6 @@ module.exports = { resolve: { modules: ['./node_modules'], extensions: [ '.js' ], - alias: { - common: path.resolve('../../out/common'), - browser: path.resolve('../../out/browser'), - vs: path.resolve('../../out/vs') - } }, output: { filename: mainFile, diff --git a/bin/test_unit.js b/bin/test_unit.js index 7ec61a814..e7270e6b1 100644 --- a/bin/test_unit.js +++ b/bin/test_unit.js @@ -8,10 +8,6 @@ const path = require('path'); const COVERAGE_LINES_THRESHOLD = 40; -// Add `out` to the NODE_PATH so absolute paths can be resolved. -const env = { ...process.env }; -env.NODE_PATH = path.resolve(__dirname, '../out-esbuild'); - let testFiles = ['**/out-esbuild/**/*.test.js']; let flagArgs = []; @@ -46,7 +42,7 @@ if (checkCoverage) { args, { cwd: path.resolve(__dirname, '..'), - env, + env: process.env, shell: true, stdio: 'inherit' } @@ -59,7 +55,7 @@ const run = cp.spawnSync( [...testFiles, ...flagArgs], { cwd: path.resolve(__dirname, '..'), - env, + env: process.env, shell: true, stdio: 'inherit' } diff --git a/demo/client/tsconfig.json b/demo/client/tsconfig.json index 8490ceb3f..de1ba6eb9 100644 --- a/demo/client/tsconfig.json +++ b/demo/client/tsconfig.json @@ -2,26 +2,54 @@ "compilerOptions": { "module": "commonjs", "target": "es2021", - "types": ["node"], + "types": [ + "node" + ], "outDir": "../out-demo/client", "rootDir": ".", "sourceMap": true, "strict": true, "skipLibCheck": true, "paths": { - "@xterm/addon-attach": ["../../addons/addon-attach"], - "@xterm/addon-clipboard": ["../../addons/addon-clipboard"], - "@xterm/addon-fit": ["../../addons/addon-fit"], - "@xterm/addon-image": ["../../addons/addon-image"], - "@xterm/addon-progress": ["../../addons/addon-progress"], - "@xterm/addon-search": ["../../addons/addon-search"], - "@xterm/addon-serialize": ["../../addons/addon-serialize"], - "@xterm/addon-web-fonts": ["../../addons/addon-web-fonts"], - "@xterm/addon-web-links": ["../../addons/addon-web-links"], - "@xterm/addon-webgl": ["../../addons/addon-webgl"], - "@xterm/addon-unicode11": ["../../addons/addon-unicode11"], - "@xterm/addon-unicode-graphemes": ["../../addons/addon-unicode-graphemes"], - "@xterm/addon-ligatures": ["../../addons/addon-ligatures"], + "@xterm/addon-attach": [ + "../../addons/addon-attach" + ], + "@xterm/addon-clipboard": [ + "../../addons/addon-clipboard" + ], + "@xterm/addon-fit": [ + "../../addons/addon-fit" + ], + "@xterm/addon-image": [ + "../../addons/addon-image" + ], + "@xterm/addon-progress": [ + "../../addons/addon-progress" + ], + "@xterm/addon-search": [ + "../../addons/addon-search" + ], + "@xterm/addon-serialize": [ + "../../addons/addon-serialize" + ], + "@xterm/addon-web-fonts": [ + "../../addons/addon-web-fonts" + ], + "@xterm/addon-web-links": [ + "../../addons/addon-web-links" + ], + "@xterm/addon-webgl": [ + "../../addons/addon-webgl" + ], + "@xterm/addon-unicode11": [ + "../../addons/addon-unicode11" + ], + "@xterm/addon-unicode-graphemes": [ + "../../addons/addon-unicode-graphemes" + ], + "@xterm/addon-ligatures": [ + "../../addons/addon-ligatures" + ], "*": ["./*"] } }, diff --git a/package.json b/package.json index 66600b5cd..ae4e9fb87 100644 --- a/package.json +++ b/package.json @@ -61,9 +61,9 @@ "test-integration-firefox": "node ./bin/test_integration.js --workers=75% \"--project=FirefoxStable\"", "test-integration-webkit": "node ./bin/test_integration.js --workers=75% \"--project=WebKit\"", "test-integration-debug": "node ./bin/test_integration.js --workers=1 --headed --timeout=30000", - "benchmark": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json", - "benchmark-baseline": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --baseline out-test/benchmark/*benchmark.js", - "benchmark-eval": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --eval out-test/benchmark/*benchmark.js", + "benchmark": "xterm-benchmark -r 5 -c test/benchmark/benchmark.json", + "benchmark-baseline": "xterm-benchmark -r 5 -c test/benchmark/benchmark.json --baseline out-test/benchmark/*benchmark.js", + "benchmark-eval": "xterm-benchmark -r 5 -c test/benchmark/benchmark.json --eval out-test/benchmark/*benchmark.js", "clean": "rm -rf lib out addons/*/lib addons/*/out", "vtfeatures": "node bin/extract_vtfeatures.js src/*/*.ts src/*/*/*.ts src/*.ts addons/**/src/*.ts", "prepackage": "npm run build", diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index 6fc421337..04b969afe 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -3,14 +3,14 @@ * @license MIT */ -import * as Strings from 'browser/LocalizableStrings'; -import { ITerminal, IRenderDebouncer } from 'browser/Types'; -import { TimeBasedDebouncer } from 'browser/TimeBasedDebouncer'; -import { Disposable, toDisposable } from 'common/Lifecycle'; -import { ICoreBrowserService, IRenderService } from 'browser/services/Services'; -import { IBuffer } from 'common/buffer/Types'; -import { IInstantiationService } from 'common/services/Services'; -import { addDisposableListener } from 'browser/Dom'; +import * as Strings from './LocalizableStrings'; +import { ITerminal, IRenderDebouncer } from './Types'; +import { TimeBasedDebouncer } from './TimeBasedDebouncer'; +import { Disposable, toDisposable } from '../common/Lifecycle'; +import { ICoreBrowserService, IRenderService } from './services/Services'; +import { IBuffer } from '../common/buffer/Types'; +import { IInstantiationService } from '../common/services/Services'; +import { addDisposableListener } from './Dom'; const enum Constants { MAX_ROWS_TO_READ = 20 diff --git a/src/browser/Clipboard.test.ts b/src/browser/Clipboard.test.ts index 2028504ec..cd61e4b8d 100644 --- a/src/browser/Clipboard.test.ts +++ b/src/browser/Clipboard.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import * as Clipboard from 'browser/Clipboard'; +import * as Clipboard from './Clipboard'; describe('evaluatePastedTextProcessing', () => { it('should replace carriage return and/or line feed with carriage return', () => { diff --git a/src/browser/Clipboard.ts b/src/browser/Clipboard.ts index aaf8a48e4..3a7ae14ab 100644 --- a/src/browser/Clipboard.ts +++ b/src/browser/Clipboard.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { ISelectionService } from 'browser/services/Services'; -import { ICoreService, IOptionsService } from 'common/services/Services'; +import { ISelectionService } from './services/Services'; +import { ICoreService, IOptionsService } from '../common/services/Services'; /** * Prepares text to be pasted into the terminal by normalizing the line endings diff --git a/src/browser/ColorContrastCache.test.ts b/src/browser/ColorContrastCache.test.ts index 17e5df383..f3d437ee8 100644 --- a/src/browser/ColorContrastCache.test.ts +++ b/src/browser/ColorContrastCache.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { ColorContrastCache } from 'browser/ColorContrastCache'; +import { ColorContrastCache } from './ColorContrastCache'; describe('ColorContrastCache', () => { let cache: ColorContrastCache; diff --git a/src/browser/ColorContrastCache.ts b/src/browser/ColorContrastCache.ts index 0c60e8db4..fdcd9d133 100644 --- a/src/browser/ColorContrastCache.ts +++ b/src/browser/ColorContrastCache.ts @@ -3,9 +3,9 @@ * @license MIT */ -import { IColorContrastCache } from 'browser/Types'; -import { IColor } from 'common/Types'; -import { TwoKeyMap } from 'common/MultiKeyMap'; +import { IColorContrastCache } from './Types'; +import { IColor } from '../common/Types'; +import { TwoKeyMap } from '../common/MultiKeyMap'; export class ColorContrastCache implements IColorContrastCache { private _color: TwoKeyMap = new TwoKeyMap(); diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index a9461581e..f7e20b914 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -22,43 +22,43 @@ */ import { IDecoration, IDecorationOptions, IDisposable, ILinkProvider, IMarker, IRenderDimensions as IRenderDimensionsApi } from '@xterm/xterm'; -import { copyHandler, handlePasteEvent, moveTextAreaUnderMouseCursor, paste, rightClickHandler } from 'browser/Clipboard'; -import * as Strings from 'browser/LocalizableStrings'; -import { OscLinkProvider } from 'browser/OscLinkProvider'; -import { CharacterJoinerHandler, CustomKeyEventHandler, CustomWheelEventHandler, IBrowser, IBufferRange, ICompositionHelper, ILinkifier2, ITerminal } from 'browser/Types'; -import { Viewport } from 'browser/Viewport'; -import { BufferDecorationRenderer } from 'browser/decorations/BufferDecorationRenderer'; -import { OverviewRulerRenderer } from 'browser/decorations/OverviewRulerRenderer'; -import { CompositionHelper } from 'browser/input/CompositionHelper'; -import { DomRenderer } from 'browser/renderer/dom/DomRenderer'; -import { IRenderer } from 'browser/renderer/shared/Types'; -import { CharSizeService } from 'browser/services/CharSizeService'; -import { CharacterJoinerService } from 'browser/services/CharacterJoinerService'; -import { CoreBrowserService } from 'browser/services/CoreBrowserService'; -import { LinkProviderService } from 'browser/services/LinkProviderService'; -import { MouseCoordsService } from 'browser/services/MouseCoordsService'; -import { MouseEventCssClasses, MouseService } from 'browser/services/MouseService'; -import { RenderService } from 'browser/services/RenderService'; -import { SelectionService } from 'browser/services/SelectionService'; -import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IKeyboardService, ILinkProviderService, IMouseCoordsService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services'; -import { ThemeService } from 'browser/services/ThemeService'; -import { KeyboardService } from 'browser/services/KeyboardService'; -import { channels, color, rgb } from 'common/Color'; -import { CoreTerminal } from 'common/CoreTerminal'; -import * as Browser from 'common/Platform'; -import { ColorRequestType, IColorEvent, ITerminalOptions, KeyboardResultType, SpecialColorIndex } from 'common/Types'; -import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; -import { IBuffer } from 'common/buffer/Types'; -import { C0, C1ESCAPED } from 'common/data/EscapeSequences'; -import { toRgbString } from 'common/input/XParseColor'; -import { DecorationService } from 'common/services/DecorationService'; -import { IDecorationService } from 'common/services/Services'; +import { copyHandler, handlePasteEvent, moveTextAreaUnderMouseCursor, paste, rightClickHandler } from './Clipboard'; +import * as Strings from './LocalizableStrings'; +import { OscLinkProvider } from './OscLinkProvider'; +import { CharacterJoinerHandler, CustomKeyEventHandler, CustomWheelEventHandler, IBrowser, IBufferRange, ICompositionHelper, ILinkifier2, ITerminal } from './Types'; +import { Viewport } from './Viewport'; +import { BufferDecorationRenderer } from './decorations/BufferDecorationRenderer'; +import { OverviewRulerRenderer } from './decorations/OverviewRulerRenderer'; +import { CompositionHelper } from './input/CompositionHelper'; +import { DomRenderer } from './renderer/dom/DomRenderer'; +import { IRenderer } from './renderer/shared/Types'; +import { CharSizeService } from './services/CharSizeService'; +import { CharacterJoinerService } from './services/CharacterJoinerService'; +import { CoreBrowserService } from './services/CoreBrowserService'; +import { LinkProviderService } from './services/LinkProviderService'; +import { MouseCoordsService } from './services/MouseCoordsService'; +import { MouseEventCssClasses, MouseService } from './services/MouseService'; +import { RenderService } from './services/RenderService'; +import { SelectionService } from './services/SelectionService'; +import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IKeyboardService, ILinkProviderService, IMouseCoordsService, IMouseService, IRenderService, ISelectionService, IThemeService } from './services/Services'; +import { ThemeService } from './services/ThemeService'; +import { KeyboardService } from './services/KeyboardService'; +import { channels, color, rgb } from '../common/Color'; +import { CoreTerminal } from '../common/CoreTerminal'; +import * as Browser from '../common/Platform'; +import { ColorRequestType, IColorEvent, ITerminalOptions, KeyboardResultType, SpecialColorIndex } from '../common/Types'; +import { DEFAULT_ATTR_DATA } from '../common/buffer/BufferLine'; +import { IBuffer } from '../common/buffer/Types'; +import { C0, C1ESCAPED } from '../common/data/EscapeSequences'; +import { toRgbString } from '../common/input/XParseColor'; +import { DecorationService } from '../common/services/DecorationService'; +import { IDecorationService } from '../common/services/Services'; import { WindowsOptionsReportType } from '../common/InputHandler'; import { AccessibilityManager } from './AccessibilityManager'; import { Linkifier } from './Linkifier'; -import { Emitter, EventUtils, type IEvent } from 'common/Event'; -import { addDisposableListener } from 'browser/Dom'; -import { MutableDisposable, toDisposable } from 'common/Lifecycle'; +import { Emitter, EventUtils, type IEvent } from '../common/Event'; +import { addDisposableListener } from './Dom'; +import { MutableDisposable, toDisposable } from '../common/Lifecycle'; export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { public textarea: HTMLTextAreaElement | undefined; diff --git a/src/browser/Dom.ts b/src/browser/Dom.ts index 56e7c0417..4d174270b 100644 --- a/src/browser/Dom.ts +++ b/src/browser/Dom.ts @@ -5,8 +5,8 @@ * Minimal DOM helpers for xterm.js browser code. */ -import { IntervalTimer } from 'common/Async'; -import { IDisposable } from 'common/Lifecycle'; +import { IntervalTimer } from '../common/Async'; +import { IDisposable } from '../common/Lifecycle'; export function getWindow(e: Node | UIEvent | undefined | null): Window { const candidateNode = e as Node | undefined | null; diff --git a/src/browser/Linkifier.test.ts b/src/browser/Linkifier.test.ts index 685c404f4..5f8b07531 100644 --- a/src/browser/Linkifier.test.ts +++ b/src/browser/Linkifier.test.ts @@ -4,11 +4,11 @@ */ import { assert } from 'chai'; -import { IBufferService } from 'common/services/Services'; +import { IBufferService } from '../common/services/Services'; import { Linkifier } from './Linkifier'; -import { MockBufferService } from 'common/TestUtils.test'; -import { ILink } from 'browser/Types'; -import { LinkProviderService } from 'browser/services/LinkProviderService'; +import { MockBufferService } from '../common/TestUtils.test'; +import { ILink } from './Types'; +import { LinkProviderService } from './services/LinkProviderService'; import jsdom = require('jsdom'); class TestLinkifier2 extends Linkifier { diff --git a/src/browser/Linkifier.ts b/src/browser/Linkifier.ts index 31c91daee..9581fb299 100644 --- a/src/browser/Linkifier.ts +++ b/src/browser/Linkifier.ts @@ -3,13 +3,13 @@ * @license MIT */ -import { IBufferCellPosition, ILink, ILinkDecorations, ILinkWithState, ILinkifier2, ILinkifierEvent } from 'browser/Types'; -import { Disposable, dispose, toDisposable } from 'common/Lifecycle'; -import { IDisposable } from 'common/Types'; -import { IBufferService } from 'common/services/Services'; +import { IBufferCellPosition, ILink, ILinkDecorations, ILinkWithState, ILinkifier2, ILinkifierEvent } from './Types'; +import { Disposable, dispose, toDisposable } from '../common/Lifecycle'; +import { IDisposable } from '../common/Types'; +import { IBufferService } from '../common/services/Services'; import { ILinkProviderService, IMouseCoordsService, IRenderService } from './services/Services'; -import { Emitter } from 'common/Event'; -import { addDisposableListener } from 'browser/Dom'; +import { Emitter } from '../common/Event'; +import { addDisposableListener } from './Dom'; export class Linkifier extends Disposable implements ILinkifier2 { public get currentLink(): ILinkWithState | undefined { return this._currentLink; } diff --git a/src/browser/OscLinkProvider.test.ts b/src/browser/OscLinkProvider.test.ts index 9e844cd16..2693f9ddd 100644 --- a/src/browser/OscLinkProvider.test.ts +++ b/src/browser/OscLinkProvider.test.ts @@ -4,11 +4,11 @@ */ import { assert } from 'chai'; -import { OscLinkProvider } from 'browser/OscLinkProvider'; -import { ILink } from 'browser/Types'; -import { createCellData, MockBufferService, MockOptionsService } from 'common/TestUtils.test'; -import { IBufferService, IOscLinkService } from 'common/services/Services'; -import { IBufferLine, IOscLinkData } from 'common/Types'; +import { OscLinkProvider } from './OscLinkProvider'; +import { ILink } from './Types'; +import { createCellData, MockBufferService, MockOptionsService } from '../common/TestUtils.test'; +import { IBufferService, IOscLinkService } from '../common/services/Services'; +import { IBufferLine, IOscLinkData } from '../common/Types'; class TestOscLinkService implements IOscLinkService { public serviceBrand: any; diff --git a/src/browser/OscLinkProvider.ts b/src/browser/OscLinkProvider.ts index d01a20b35..d52d68c08 100644 --- a/src/browser/OscLinkProvider.ts +++ b/src/browser/OscLinkProvider.ts @@ -3,11 +3,11 @@ * @license MIT */ -import { IBufferRange, ILink } from 'browser/Types'; -import { ILinkProvider } from 'browser/services/Services'; -import { CellData } from 'common/buffer/CellData'; -import { IBufferLine } from 'common/Types'; -import { IBufferService, IOptionsService, IOscLinkService } from 'common/services/Services'; +import { IBufferRange, ILink } from './Types'; +import { ILinkProvider } from './services/Services'; +import { CellData } from '../common/buffer/CellData'; +import { IBufferLine } from '../common/Types'; +import { IBufferService, IOptionsService, IOscLinkService } from '../common/services/Services'; export class OscLinkProvider implements ILinkProvider { private readonly _workCell = new CellData(); diff --git a/src/browser/RenderDebouncer.ts b/src/browser/RenderDebouncer.ts index c7ddb6cec..f6ea59bf6 100644 --- a/src/browser/RenderDebouncer.ts +++ b/src/browser/RenderDebouncer.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { IRenderDebouncerWithCallback } from 'browser/Types'; -import { ICoreBrowserService } from 'browser/services/Services'; +import { IRenderDebouncerWithCallback } from './Types'; +import { ICoreBrowserService } from './services/Services'; /** * Debounces calls to render terminal rows using animation frames. diff --git a/src/browser/Terminal.test.ts b/src/browser/Terminal.test.ts index d359fa28c..38165b84f 100644 --- a/src/browser/Terminal.test.ts +++ b/src/browser/Terminal.test.ts @@ -3,13 +3,13 @@ * @license MIT */ -import { MockCompositionHelper, MockRenderer, MockViewport, TestTerminal } from 'browser/TestUtils.test'; -import type { IBrowser } from 'browser/Types'; +import { MockCompositionHelper, MockRenderer, MockViewport, TestTerminal } from './TestUtils.test'; +import type { IBrowser } from './Types'; import { assert } from 'chai'; -import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; -import { CellData } from 'common/buffer/CellData'; -import { MockUnicodeService, createCellData } from 'common/TestUtils.test'; -import { IMarker } from 'common/Types'; +import { DEFAULT_ATTR_DATA } from '../common/buffer/BufferLine'; +import { CellData } from '../common/buffer/CellData'; +import { MockUnicodeService, createCellData } from '../common/TestUtils.test'; +import { IMarker } from '../common/Types'; const INIT_COLS = 80; const INIT_ROWS = 24; diff --git a/src/browser/Terminal2.test.ts b/src/browser/Terminal2.test.ts index 270b2e871..8274a47e5 100644 --- a/src/browser/Terminal2.test.ts +++ b/src/browser/Terminal2.test.ts @@ -7,7 +7,7 @@ import * as path from 'path'; import * as os from 'os'; import * as fs from 'fs'; import * as pty from 'node-pty'; -import { CoreBrowserTerminal } from 'browser/CoreBrowserTerminal'; +import { CoreBrowserTerminal } from './CoreBrowserTerminal'; import { IDisposable } from '@xterm/xterm'; // all test files expect terminal in 80x25 diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts index 65a18da72..ee5423eea 100644 --- a/src/browser/TestUtils.test.ts +++ b/src/browser/TestUtils.test.ts @@ -4,21 +4,21 @@ */ import { IDisposable, IMarker, ILinkProvider, IDecorationOptions, IDecoration, IRenderDimensions as IRenderDimensionsApi } from '@xterm/xterm'; -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, 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'; -import * as Browser from 'common/Platform'; -import { CoreBrowserTerminal } from 'browser/CoreBrowserTerminal'; -import { IUnicodeService, IOptionsService, ICoreService, IMouseStateService } from 'common/services/Services'; -import { IFunctionIdentifier, IParams } from 'common/parser/Types'; -import { AttributeData } from 'common/buffer/AttributeData'; -import { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types'; -import { css } from 'common/Color'; -import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils'; -import { Emitter, type IEvent } from 'common/Event'; +import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IMouseService, IRenderService, ISelectionService, IThemeService } from './services/Services'; +import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from './renderer/shared/Types'; +import { IColorSet, ITerminal, ILinkifier2, IBrowser, IViewport, ICompositionHelper, CharacterJoinerHandler, IBufferRange, ReadonlyColorSet, IBufferElementProvider } from './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'; +import * as Browser from '../common/Platform'; +import { CoreBrowserTerminal } from './CoreBrowserTerminal'; +import { IUnicodeService, IOptionsService, ICoreService, IMouseStateService } from '../common/services/Services'; +import { IFunctionIdentifier, IParams } from '../common/parser/Types'; +import { AttributeData } from '../common/buffer/AttributeData'; +import { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from './selection/Types'; +import { css } from '../common/Color'; +import { createRenderDimensions } from './renderer/shared/RendererUtils'; +import { Emitter, type IEvent } from '../common/Event'; export class TestTerminal extends CoreBrowserTerminal { public get curAttrData(): IAttributeData { return (this as any)._inputHandler._curAttrData; } diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index 997312901..0860e7927 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -5,7 +5,7 @@ const RENDER_DEBOUNCE_THRESHOLD_MS = 1000; // 1 Second -import { IRenderDebouncer } from 'browser/Types'; +import { IRenderDebouncer } from './Types'; /** * Debounces calls to update screen readers to update at most once configurable interval of time. diff --git a/src/browser/Types.ts b/src/browser/Types.ts index fa08de2e3..126857e17 100644 --- a/src/browser/Types.ts +++ b/src/browser/Types.ts @@ -3,11 +3,11 @@ * @license MIT */ -import { CharData, IColor, ICoreTerminal, ITerminalOptions } from 'common/Types'; -import { IBuffer } from 'common/buffer/Types'; +import { CharData, IColor, ICoreTerminal, ITerminalOptions } from '../common/Types'; +import { IBuffer } from '../common/buffer/Types'; import { IDisposable, IRenderDimensions as IRenderDimensionsApi, Terminal as ITerminalApi } from '@xterm/xterm'; -import { channels, css } from 'common/Color'; -import type { IEvent } from 'common/Event'; +import { channels, css } from '../common/Color'; +import type { IEvent } from '../common/Event'; /** * A portion of the public API that are implemented identially internally and simply passed through. diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index 3b6ae8301..18c275ccb 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -3,16 +3,16 @@ * @license MIT */ -import { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; -import { ViewportConstants } from 'browser/shared/Constants'; -import { Disposable, toDisposable } from 'common/Lifecycle'; -import { IBufferService, ICoreService, IMouseStateService, IOptionsService } from 'common/services/Services'; -import { CoreMouseEventType } from 'common/Types'; -import { scheduleAtNextAnimationFrame } from 'browser/Dom'; -import { SmoothScrollableElement } from 'browser/scrollable/scrollableElement'; -import type { IScrollableElementChangeOptions } from 'browser/scrollable/scrollableElementOptions'; -import { Emitter, EventUtils } from 'common/Event'; -import { Scrollable, ScrollbarVisibility, type IScrollEvent } from 'browser/scrollable/scrollable'; +import { ICoreBrowserService, IRenderService, IThemeService } from './services/Services'; +import { ViewportConstants } from './shared/Constants'; +import { Disposable, toDisposable } from '../common/Lifecycle'; +import { IBufferService, ICoreService, IMouseStateService, IOptionsService } from '../common/services/Services'; +import { CoreMouseEventType } from '../common/Types'; +import { scheduleAtNextAnimationFrame } from './Dom'; +import { SmoothScrollableElement } from './scrollable/scrollableElement'; +import type { IScrollableElementChangeOptions } from './scrollable/scrollableElementOptions'; +import { Emitter, EventUtils } from '../common/Event'; +import { Scrollable, ScrollbarVisibility, type IScrollEvent } from './scrollable/scrollable'; export class Viewport extends Disposable { diff --git a/src/browser/decorations/BufferDecorationRenderer.ts b/src/browser/decorations/BufferDecorationRenderer.ts index e374af260..fd356808e 100644 --- a/src/browser/decorations/BufferDecorationRenderer.ts +++ b/src/browser/decorations/BufferDecorationRenderer.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ICoreBrowserService, IRenderService } from 'browser/services/Services'; -import { Disposable, toDisposable } from 'common/Lifecycle'; -import { IBufferService, IDecorationService, IInternalDecoration } from 'common/services/Services'; +import { ICoreBrowserService, IRenderService } from '../services/Services'; +import { Disposable, toDisposable } from '../../common/Lifecycle'; +import { IBufferService, IDecorationService, IInternalDecoration } from '../../common/services/Services'; export class BufferDecorationRenderer extends Disposable { private readonly _container: HTMLElement; diff --git a/src/browser/decorations/ColorZoneStore.test.ts b/src/browser/decorations/ColorZoneStore.test.ts index 4759e3c8b..360ad6570 100644 --- a/src/browser/decorations/ColorZoneStore.test.ts +++ b/src/browser/decorations/ColorZoneStore.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { ColorZoneStore } from 'browser/decorations/ColorZoneStore'; +import { ColorZoneStore } from './ColorZoneStore'; const optionsRedFull = { overviewRulerOptions: { diff --git a/src/browser/decorations/ColorZoneStore.ts b/src/browser/decorations/ColorZoneStore.ts index d066bedb8..b63c865f8 100644 --- a/src/browser/decorations/ColorZoneStore.ts +++ b/src/browser/decorations/ColorZoneStore.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IInternalDecoration } from 'common/services/Services'; +import { IInternalDecoration } from '../../common/services/Services'; export interface IColorZoneStore { readonly zones: IColorZone[]; diff --git a/src/browser/decorations/OverviewRulerRenderer.ts b/src/browser/decorations/OverviewRulerRenderer.ts index b28b25965..2ec441bad 100644 --- a/src/browser/decorations/OverviewRulerRenderer.ts +++ b/src/browser/decorations/OverviewRulerRenderer.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ColorZoneStore, IColorZone, IColorZoneStore } from 'browser/decorations/ColorZoneStore'; -import { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; -import { Disposable, toDisposable } from 'common/Lifecycle'; -import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services'; +import { ColorZoneStore, IColorZone, IColorZoneStore } from './ColorZoneStore'; +import { ICoreBrowserService, IRenderService, IThemeService } from '../services/Services'; +import { Disposable, toDisposable } from '../../common/Lifecycle'; +import { IBufferService, IDecorationService, IOptionsService } from '../../common/services/Services'; const enum Constants { OVERVIEW_RULER_BORDER_WIDTH = 1 diff --git a/src/browser/input/CompositionHelper.test.ts b/src/browser/input/CompositionHelper.test.ts index 94b5841af..5a1e6c38c 100644 --- a/src/browser/input/CompositionHelper.test.ts +++ b/src/browser/input/CompositionHelper.test.ts @@ -4,9 +4,9 @@ */ import { assert } from 'chai'; -import { CompositionHelper } from 'browser/input/CompositionHelper'; -import { MockRenderService } from 'browser/TestUtils.test'; -import { MockCoreService, MockBufferService, MockOptionsService } from 'common/TestUtils.test'; +import { CompositionHelper } from './CompositionHelper'; +import { MockRenderService } from '../TestUtils.test'; +import { MockCoreService, MockBufferService, MockOptionsService } from '../../common/TestUtils.test'; describe('CompositionHelper', () => { let compositionHelper: CompositionHelper; diff --git a/src/browser/input/CompositionHelper.ts b/src/browser/input/CompositionHelper.ts index 283874139..c9ec396ab 100644 --- a/src/browser/input/CompositionHelper.ts +++ b/src/browser/input/CompositionHelper.ts @@ -3,9 +3,9 @@ * @license MIT */ -import { IRenderService } from 'browser/services/Services'; -import { IBufferService, ICoreService, IOptionsService } from 'common/services/Services'; -import { C0 } from 'common/data/EscapeSequences'; +import { IRenderService } from '../services/Services'; +import { IBufferService, ICoreService, IOptionsService } from '../../common/services/Services'; +import { C0 } from '../../common/data/EscapeSequences'; interface IPosition { start: number; diff --git a/src/browser/input/Mouse.test.ts b/src/browser/input/Mouse.test.ts index 002603612..8333d13f8 100644 --- a/src/browser/input/Mouse.test.ts +++ b/src/browser/input/Mouse.test.ts @@ -5,7 +5,7 @@ import jsdom = require('jsdom'); import { assert } from 'chai'; -import { getCoords } from 'browser/input/Mouse'; +import { getCoords } from './Mouse'; const CHAR_WIDTH = 10; const CHAR_HEIGHT = 20; diff --git a/src/browser/input/MoveToCell.test.ts b/src/browser/input/MoveToCell.test.ts index 61e5f90b9..8200fa24e 100644 --- a/src/browser/input/MoveToCell.test.ts +++ b/src/browser/input/MoveToCell.test.ts @@ -4,8 +4,8 @@ */ import { assert } from 'chai'; -import { IBufferService } from 'common/services/Services'; -import { MockBufferService } from 'common/TestUtils.test'; +import { IBufferService } from '../../common/services/Services'; +import { MockBufferService } from '../../common/TestUtils.test'; import { moveToCellSequence } from './MoveToCell'; describe('MoveToCell', () => { diff --git a/src/browser/input/MoveToCell.ts b/src/browser/input/MoveToCell.ts index ce4c59224..b5ddc7ca2 100644 --- a/src/browser/input/MoveToCell.ts +++ b/src/browser/input/MoveToCell.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { C0 } from 'common/data/EscapeSequences'; -import { IBufferService } from 'common/services/Services'; +import { C0 } from '../../common/data/EscapeSequences'; +import { IBufferService } from '../../common/services/Services'; const enum Direction { UP = 'A', diff --git a/src/browser/public/Terminal.ts b/src/browser/public/Terminal.ts index 75aada048..69ab09bf1 100644 --- a/src/browser/public/Terminal.ts +++ b/src/browser/public/Terminal.ts @@ -3,17 +3,17 @@ * @license MIT */ -import * as Strings from 'browser/LocalizableStrings'; -import { CoreBrowserTerminal as TerminalCore } from 'browser/CoreBrowserTerminal'; -import { IBufferRange, ITerminal } from 'browser/Types'; -import { Disposable } from 'common/Lifecycle'; -import { ITerminalOptions } from 'common/Types'; -import { AddonManager } from 'common/public/AddonManager'; -import { BufferNamespaceApi } from 'common/public/BufferNamespaceApi'; -import { ParserApi } from 'common/public/ParserApi'; -import { UnicodeApi } from 'common/public/UnicodeApi'; +import * as Strings from '../LocalizableStrings'; +import { CoreBrowserTerminal as TerminalCore } from '../CoreBrowserTerminal'; +import { IBufferRange, ITerminal } from '../Types'; +import { Disposable } from '../../common/Lifecycle'; +import { ITerminalOptions } from '../../common/Types'; +import { AddonManager } from '../../common/public/AddonManager'; +import { BufferNamespaceApi } from '../../common/public/BufferNamespaceApi'; +import { ParserApi } from '../../common/public/ParserApi'; +import { UnicodeApi } from '../../common/public/UnicodeApi'; import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOptions, IDisposable, ILinkProvider, ILocalizableStrings, IMarker, IModes, IParser, IRenderDimensions, ITerminalAddon, Terminal as ITerminalApi, ITerminalInitOnlyOptions, IUnicodeHandling } from '@xterm/xterm'; -import type { IEvent } from 'common/Event'; +import type { IEvent } from '../../common/Event'; /** * The set of options that only have an effect when set in the Terminal constructor. diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index 6f873af7b..de08ac0ac 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -3,20 +3,20 @@ * @license MIT */ -import { DomRendererRowFactory, RowCss } from 'browser/renderer/dom/DomRendererRowFactory'; -import { WidthCache } from 'browser/renderer/dom/WidthCache'; -import { INVERTED_DEFAULT_COLOR, RendererConstants } from 'browser/renderer/shared/Constants'; -import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils'; -import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel'; -import { TextBlinkStateManager } from 'browser/renderer/shared/TextBlinkStateManager'; -import { IRenderDimensions, IRenderer, IRequestRedrawEvent, ISelectionRenderModel } from 'browser/renderer/shared/Types'; -import { ICharSizeService, ICoreBrowserService, IThemeService } from 'browser/services/Services'; -import { ILinkifier2, ILinkifierEvent, ITerminal, ReadonlyColorSet } from 'browser/Types'; -import { color } from 'common/Color'; -import { Disposable, toDisposable } from 'common/Lifecycle'; -import { IBufferService, ICoreService, IInstantiationService, IOptionsService } from 'common/services/Services'; -import { Emitter } from 'common/Event'; -import { addDisposableListener } from 'browser/Dom'; +import { DomRendererRowFactory, RowCss } from './DomRendererRowFactory'; +import { WidthCache } from './WidthCache'; +import { INVERTED_DEFAULT_COLOR, RendererConstants } from '../shared/Constants'; +import { createRenderDimensions } from '../shared/RendererUtils'; +import { createSelectionRenderModel } from '../shared/SelectionRenderModel'; +import { TextBlinkStateManager } from '../shared/TextBlinkStateManager'; +import { IRenderDimensions, IRenderer, IRequestRedrawEvent, ISelectionRenderModel } from '../shared/Types'; +import { ICharSizeService, ICoreBrowserService, IThemeService } from '../../services/Services'; +import { ILinkifier2, ILinkifierEvent, ITerminal, ReadonlyColorSet } from '../../Types'; +import { color } from '../../../common/Color'; +import { Disposable, toDisposable } from '../../../common/Lifecycle'; +import { IBufferService, ICoreService, IInstantiationService, IOptionsService } from '../../../common/services/Services'; +import { Emitter } from '../../../common/Event'; +import { addDisposableListener } from '../../Dom'; const enum Constants { diff --git a/src/browser/renderer/dom/DomRendererRowFactory.test.ts b/src/browser/renderer/dom/DomRendererRowFactory.test.ts index 6ca84569a..732766ccc 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.test.ts @@ -5,15 +5,15 @@ import jsdom = require('jsdom'); import { assert } from 'chai'; -import { DomRendererRowFactory } from 'browser/renderer/dom/DomRendererRowFactory'; -import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, DEFAULT_ATTR, FgFlags, BgFlags, Attributes, UnderlineStyle } from 'common/buffer/Constants'; -import { BufferLine, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; -import { BufferLineStringCache } from 'common/buffer/BufferLineStringCache'; -import { IBufferLine } from 'common/Types'; -import { CellData } from 'common/buffer/CellData'; -import { MockCoreService, MockDecorationService, MockOptionsService, createCellData, NULL_CELL_DATA } from 'common/TestUtils.test'; -import { MockCharacterJoinerService, MockCoreBrowserService, MockThemeService } from 'browser/TestUtils.test'; -import { TestWidthCache } from 'browser/renderer/dom/WidthCache.test'; +import { DomRendererRowFactory } from './DomRendererRowFactory'; +import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, DEFAULT_ATTR, FgFlags, BgFlags, Attributes, UnderlineStyle } from '../../../common/buffer/Constants'; +import { BufferLine, DEFAULT_ATTR_DATA } from '../../../common/buffer/BufferLine'; +import { BufferLineStringCache } from '../../../common/buffer/BufferLineStringCache'; +import { IBufferLine } from '../../../common/Types'; +import { CellData } from '../../../common/buffer/CellData'; +import { MockCoreService, MockDecorationService, MockOptionsService, createCellData, NULL_CELL_DATA } from '../../../common/TestUtils.test'; +import { MockCharacterJoinerService, MockCoreBrowserService, MockThemeService } from '../../TestUtils.test'; +import { TestWidthCache } from './WidthCache.test'; const TEST_STRING_CACHE = new BufferLineStringCache(); diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts index e5bf31139..7d2e8ed9b 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.ts @@ -3,18 +3,18 @@ * @license MIT */ -import { IBufferLine, ICellData, IColor } from 'common/Types'; -import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants'; -import { WHITESPACE_CELL_CHAR, Attributes } from 'common/buffer/Constants'; -import { CellData } from 'common/buffer/CellData'; -import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; -import { channels, color } from 'common/Color'; -import { ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services'; -import { JoinedCellData } from 'browser/services/CharacterJoinerService'; -import { treatGlyphAsBackgroundColor } from 'browser/renderer/shared/RendererUtils'; -import { AttributeData } from 'common/buffer/AttributeData'; -import { WidthCache } from 'browser/renderer/dom/WidthCache'; -import { IColorContrastCache } from 'browser/Types'; +import { IBufferLine, ICellData, IColor } from '../../../common/Types'; +import { INVERTED_DEFAULT_COLOR } from '../shared/Constants'; +import { WHITESPACE_CELL_CHAR, Attributes } from '../../../common/buffer/Constants'; +import { CellData } from '../../../common/buffer/CellData'; +import { ICoreService, IDecorationService, IOptionsService } from '../../../common/services/Services'; +import { channels, color } from '../../../common/Color'; +import { ICharacterJoinerService, ICoreBrowserService, IThemeService } from '../../services/Services'; +import { JoinedCellData } from '../../services/CharacterJoinerService'; +import { treatGlyphAsBackgroundColor } from '../shared/RendererUtils'; +import { AttributeData } from '../../../common/buffer/AttributeData'; +import { WidthCache } from './WidthCache'; +import { IColorContrastCache } from '../../Types'; export const enum RowCss { diff --git a/src/browser/renderer/dom/WidthCache.test.ts b/src/browser/renderer/dom/WidthCache.test.ts index c1e511c94..f8bc5961a 100644 --- a/src/browser/renderer/dom/WidthCache.test.ts +++ b/src/browser/renderer/dom/WidthCache.test.ts @@ -4,7 +4,7 @@ */ import * as assert from 'assert'; -import { IWidthCacheFontVariantCanvas, WidthCache, WidthCacheSettings } from 'browser/renderer/dom/WidthCache'; +import { IWidthCacheFontVariantCanvas, WidthCache, WidthCacheSettings } from './WidthCache'; class MockWidthCacheFontVariantCanvas implements IWidthCacheFontVariantCanvas { diff --git a/src/browser/renderer/dom/WidthCache.ts b/src/browser/renderer/dom/WidthCache.ts index ddacfd6a6..d45938515 100644 --- a/src/browser/renderer/dom/WidthCache.ts +++ b/src/browser/renderer/dom/WidthCache.ts @@ -3,9 +3,9 @@ * @license MIT */ -import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; -import { IDisposable } from 'common/Types'; -import { FontWeight } from 'common/services/Services'; +import { throwIfFalsy } from '../shared/RendererUtils'; +import { IDisposable } from '../../../common/Types'; +import { FontWeight } from '../../../common/services/Services'; export const enum WidthCacheSettings { diff --git a/src/browser/renderer/shared/RendererUtils.test.ts b/src/browser/renderer/shared/RendererUtils.test.ts index 4512cb6d9..4f4e843af 100644 --- a/src/browser/renderer/shared/RendererUtils.test.ts +++ b/src/browser/renderer/shared/RendererUtils.test.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { computeNextVariantOffset } from 'browser/renderer/shared/RendererUtils'; +import { computeNextVariantOffset } from './RendererUtils'; import { assert } from 'chai'; describe('RendererUtils', () => { diff --git a/src/browser/renderer/shared/RendererUtils.ts b/src/browser/renderer/shared/RendererUtils.ts index 010643648..da8914abe 100644 --- a/src/browser/renderer/shared/RendererUtils.ts +++ b/src/browser/renderer/shared/RendererUtils.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { IDimensions, IRenderDimensions } from 'browser/renderer/shared/Types'; +import { IDimensions, IRenderDimensions } from './Types'; export function throwIfFalsy(value: T | undefined | null): T { if (!value) { diff --git a/src/browser/renderer/shared/SelectionRenderModel.ts b/src/browser/renderer/shared/SelectionRenderModel.ts index d3b7a99c4..1df166c28 100644 --- a/src/browser/renderer/shared/SelectionRenderModel.ts +++ b/src/browser/renderer/shared/SelectionRenderModel.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { ITerminal } from 'browser/Types'; -import { ISelectionRenderModel } from 'browser/renderer/shared/Types'; +import { ITerminal } from '../../Types'; +import { ISelectionRenderModel } from './Types'; import { Terminal } from '@xterm/xterm'; class SelectionRenderModel implements ISelectionRenderModel { diff --git a/src/browser/renderer/shared/TextBlinkStateManager.test.ts b/src/browser/renderer/shared/TextBlinkStateManager.test.ts index 5c8ce448a..3af39cac8 100644 --- a/src/browser/renderer/shared/TextBlinkStateManager.test.ts +++ b/src/browser/renderer/shared/TextBlinkStateManager.test.ts @@ -4,10 +4,10 @@ */ import { assert } from 'chai'; -import { TextBlinkStateManager } from 'browser/renderer/shared/TextBlinkStateManager'; -import { MockOptionsService } from 'common/TestUtils.test'; -import type { ICoreBrowserService } from 'browser/services/Services'; -import { Emitter } from 'common/Event'; +import { TextBlinkStateManager } from './TextBlinkStateManager'; +import { MockOptionsService } from '../../../common/TestUtils.test'; +import type { ICoreBrowserService } from '../../services/Services'; +import { Emitter } from '../../../common/Event'; class FakeWindow { public nextId = 1; diff --git a/src/browser/renderer/shared/TextBlinkStateManager.ts b/src/browser/renderer/shared/TextBlinkStateManager.ts index 028cde898..058abf73b 100644 --- a/src/browser/renderer/shared/TextBlinkStateManager.ts +++ b/src/browser/renderer/shared/TextBlinkStateManager.ts @@ -3,9 +3,9 @@ * @license MIT */ -import { ICoreBrowserService } from 'browser/services/Services'; -import { Disposable, toDisposable } from 'common/Lifecycle'; -import { IOptionsService } from 'common/services/Services'; +import { ICoreBrowserService } from '../../services/Services'; +import { Disposable, toDisposable } from '../../../common/Lifecycle'; +import { IOptionsService } from '../../../common/services/Services'; export class TextBlinkStateManager extends Disposable { private _intervalDuration: number = 0; diff --git a/src/browser/renderer/shared/Types.ts b/src/browser/renderer/shared/Types.ts index c78e7794f..d664d1611 100644 --- a/src/browser/renderer/shared/Types.ts +++ b/src/browser/renderer/shared/Types.ts @@ -4,9 +4,9 @@ */ import { Terminal } from '@xterm/xterm'; -import { ITerminal } from 'browser/Types'; -import { IDisposable } from 'common/Types'; -import type { IEvent } from 'common/Event'; +import { ITerminal } from '../../Types'; +import { IDisposable } from '../../../common/Types'; +import type { IEvent } from '../../../common/Event'; export interface IDimensions { width: number; diff --git a/src/browser/scrollable/abstractScrollbar.ts b/src/browser/scrollable/abstractScrollbar.ts index 64c25d9d8..906190f76 100644 --- a/src/browser/scrollable/abstractScrollbar.ts +++ b/src/browser/scrollable/abstractScrollbar.ts @@ -11,7 +11,7 @@ import { ScrollbarArrow, IScrollbarArrowOptions } from './scrollbarArrow'; import { ScrollbarState } from './scrollbarState'; import { ScrollbarVisibilityController } from './scrollbarVisibilityController'; import { Widget } from './widget'; -import * as platform from 'common/Platform'; +import * as platform from '../../common/Platform'; import { INewScrollPosition, Scrollable, ScrollbarVisibility } from './scrollable'; /** diff --git a/src/browser/scrollable/globalPointerMoveMonitor.ts b/src/browser/scrollable/globalPointerMoveMonitor.ts index a80e0507f..56ad16699 100644 --- a/src/browser/scrollable/globalPointerMoveMonitor.ts +++ b/src/browser/scrollable/globalPointerMoveMonitor.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from '../Dom'; -import { DisposableStore, IDisposable, toDisposable } from 'common/Lifecycle'; +import { DisposableStore, IDisposable, toDisposable } from '../../common/Lifecycle'; type PointerMoveCallback = (event: PointerEvent) => void; type OnStopCallback = () => void; diff --git a/src/browser/scrollable/mouseEvent.ts b/src/browser/scrollable/mouseEvent.ts index a12b63d52..6be8963e2 100644 --- a/src/browser/scrollable/mouseEvent.ts +++ b/src/browser/scrollable/mouseEvent.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as platform from 'common/Platform'; +import * as platform from '../../common/Platform'; interface IWindowChainElement { readonly window: WeakRef; diff --git a/src/browser/scrollable/scrollable.ts b/src/browser/scrollable/scrollable.ts index 18275ffe5..29435ad8f 100644 --- a/src/browser/scrollable/scrollable.ts +++ b/src/browser/scrollable/scrollable.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter, IEvent } from 'common/Event'; -import { Disposable, IDisposable } from 'common/Lifecycle'; +import { Emitter, IEvent } from '../../common/Event'; +import { Disposable, IDisposable } from '../../common/Lifecycle'; export const enum ScrollbarVisibility { AUTO = 1, diff --git a/src/browser/scrollable/scrollableElement.ts b/src/browser/scrollable/scrollableElement.ts index 4039d0c4e..df3628e10 100644 --- a/src/browser/scrollable/scrollableElement.ts +++ b/src/browser/scrollable/scrollableElement.ts @@ -11,10 +11,10 @@ import { HorizontalScrollbar } from './horizontalScrollbar'; import { IScrollableElementChangeOptions, IScrollableElementCreationOptions, IScrollableElementResolvedOptions } from './scrollableElementOptions'; import { VerticalScrollbar } from './verticalScrollbar'; import { Widget } from './widget'; -import { TimeoutTimer } from 'common/Async'; -import { Emitter, IEvent } from 'common/Event'; -import { IDisposable, dispose } from 'common/Lifecycle'; -import * as platform from 'common/Platform'; +import { TimeoutTimer } from '../../common/Async'; +import { Emitter, IEvent } from '../../common/Event'; +import { IDisposable, dispose } from '../../common/Lifecycle'; +import * as platform from '../../common/Platform'; import { INewScrollDimensions, INewScrollPosition, IScrollDimensions, IScrollPosition, IScrollEvent, Scrollable, ScrollbarVisibility } from './scrollable'; // import 'vs/css!./media/scrollbars'; diff --git a/src/browser/scrollable/scrollbarArrow.ts b/src/browser/scrollable/scrollbarArrow.ts index d543669c1..2f1f9e9fe 100644 --- a/src/browser/scrollable/scrollbarArrow.ts +++ b/src/browser/scrollable/scrollbarArrow.ts @@ -5,7 +5,7 @@ import { GlobalPointerMoveMonitor } from './globalPointerMoveMonitor'; import { Widget } from './widget'; -import { TimeoutTimer } from 'common/Async'; +import { TimeoutTimer } from '../../common/Async'; import * as dom from '../Dom'; export interface IScrollbarArrowOptions { diff --git a/src/browser/scrollable/scrollbarVisibilityController.ts b/src/browser/scrollable/scrollbarVisibilityController.ts index 59d172bce..84a8b460f 100644 --- a/src/browser/scrollable/scrollbarVisibilityController.ts +++ b/src/browser/scrollable/scrollbarVisibilityController.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { FastDomNode } from './fastDomNode'; -import { TimeoutTimer } from 'common/Async'; -import { Disposable } from 'common/Lifecycle'; +import { TimeoutTimer } from '../../common/Async'; +import { Disposable } from '../../common/Lifecycle'; import { ScrollbarVisibility } from './scrollable'; export class ScrollbarVisibilityController extends Disposable { diff --git a/src/browser/scrollable/touch.ts b/src/browser/scrollable/touch.ts index 475784cf6..5c58eac3b 100644 --- a/src/browser/scrollable/touch.ts +++ b/src/browser/scrollable/touch.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as DomUtils from '../Dom'; -import { Disposable, IDisposable, toDisposable } from 'common/Lifecycle'; +import { Disposable, IDisposable, toDisposable } from '../../common/Lifecycle'; const mainWindow = (typeof window === 'object' ? window : globalThis) as Window & typeof globalThis; diff --git a/src/browser/scrollable/widget.ts b/src/browser/scrollable/widget.ts index 80220c9b1..578492853 100644 --- a/src/browser/scrollable/widget.ts +++ b/src/browser/scrollable/widget.ts @@ -5,7 +5,7 @@ import * as dom from '../Dom'; import { IMouseEvent, StandardMouseEvent } from './mouseEvent'; -import { Disposable } from 'common/Lifecycle'; +import { Disposable } from '../../common/Lifecycle'; export abstract class Widget extends Disposable { diff --git a/src/browser/selection/SelectionModel.test.ts b/src/browser/selection/SelectionModel.test.ts index 79ff372b3..5f813d7ee 100644 --- a/src/browser/selection/SelectionModel.test.ts +++ b/src/browser/selection/SelectionModel.test.ts @@ -5,7 +5,7 @@ import { assert } from 'chai'; import { SelectionModel } from './SelectionModel'; -import { MockBufferService } from 'common/TestUtils.test'; +import { MockBufferService } from '../../common/TestUtils.test'; describe('SelectionModel', () => { let model: SelectionModel; diff --git a/src/browser/selection/SelectionModel.ts b/src/browser/selection/SelectionModel.ts index b26cf944f..7b62b230b 100644 --- a/src/browser/selection/SelectionModel.ts +++ b/src/browser/selection/SelectionModel.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { IBufferService } from 'common/services/Services'; +import { IBufferService } from '../../common/services/Services'; /** * Represents a selection within the buffer. This model only cares about column diff --git a/src/browser/services/CharSizeService.ts b/src/browser/services/CharSizeService.ts index 6baf9b50a..002ca5c18 100644 --- a/src/browser/services/CharSizeService.ts +++ b/src/browser/services/CharSizeService.ts @@ -3,10 +3,10 @@ * @license MIT */ -import { IOptionsService } from 'common/services/Services'; -import { ICharSizeService } from 'browser/services/Services'; -import { Disposable } from 'common/Lifecycle'; -import { Emitter } from 'common/Event'; +import { IOptionsService } from '../../common/services/Services'; +import { ICharSizeService } from './Services'; +import { Disposable } from '../../common/Lifecycle'; +import { Emitter } from '../../common/Event'; export class CharSizeService extends Disposable implements ICharSizeService { public serviceBrand: undefined; diff --git a/src/browser/services/CharacterJoinerService.test.ts b/src/browser/services/CharacterJoinerService.test.ts index 42301015c..1dc3db334 100644 --- a/src/browser/services/CharacterJoinerService.test.ts +++ b/src/browser/services/CharacterJoinerService.test.ts @@ -4,13 +4,13 @@ */ import { assert } from 'chai'; -import { ICharacterJoinerService } from 'browser/services/Services'; -import { CharacterJoinerService } from 'browser/services/CharacterJoinerService'; -import { BufferLine } from 'common/buffer/BufferLine'; -import { BufferLineStringCache } from 'common/buffer/BufferLineStringCache'; -import { IBufferLine } from 'common/Types'; -import { CellData } from 'common/buffer/CellData'; -import { MockBufferService, createCellData } from 'common/TestUtils.test'; +import { ICharacterJoinerService } from './Services'; +import { CharacterJoinerService } from './CharacterJoinerService'; +import { BufferLine } from '../../common/buffer/BufferLine'; +import { BufferLineStringCache } from '../../common/buffer/BufferLineStringCache'; +import { IBufferLine } from '../../common/Types'; +import { CellData } from '../../common/buffer/CellData'; +import { MockBufferService, createCellData } from '../../common/TestUtils.test'; const TEST_STRING_CACHE = new BufferLineStringCache(); diff --git a/src/browser/services/CharacterJoinerService.ts b/src/browser/services/CharacterJoinerService.ts index ca4f1984e..67ee70748 100644 --- a/src/browser/services/CharacterJoinerService.ts +++ b/src/browser/services/CharacterJoinerService.ts @@ -3,13 +3,13 @@ * @license MIT */ -import { IBufferLine, ICellData, CharData } from 'common/Types'; -import { ICharacterJoiner } from 'browser/Types'; -import { AttributeData } from 'common/buffer/AttributeData'; -import { WHITESPACE_CELL_CHAR, Content } from 'common/buffer/Constants'; -import { CellData } from 'common/buffer/CellData'; -import { IBufferService } from 'common/services/Services'; -import { ICharacterJoinerService } from 'browser/services/Services'; +import { IBufferLine, ICellData, CharData } from '../../common/Types'; +import { ICharacterJoiner } from '../Types'; +import { AttributeData } from '../../common/buffer/AttributeData'; +import { WHITESPACE_CELL_CHAR, Content } from '../../common/buffer/Constants'; +import { CellData } from '../../common/buffer/CellData'; +import { IBufferService } from '../../common/services/Services'; +import { ICharacterJoinerService } from './Services'; export class JoinedCellData extends AttributeData implements ICellData { private _width: number; diff --git a/src/browser/services/CoreBrowserService.ts b/src/browser/services/CoreBrowserService.ts index 658caaeb1..bcc39544e 100644 --- a/src/browser/services/CoreBrowserService.ts +++ b/src/browser/services/CoreBrowserService.ts @@ -4,9 +4,9 @@ */ import { ICoreBrowserService } from './Services'; -import { Emitter, EventUtils } from 'common/Event'; -import { addDisposableListener } from 'browser/Dom'; -import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; +import { Emitter, EventUtils } from '../../common/Event'; +import { addDisposableListener } from '../Dom'; +import { Disposable, MutableDisposable, toDisposable } from '../../common/Lifecycle'; export class CoreBrowserService extends Disposable implements ICoreBrowserService { public serviceBrand: undefined; diff --git a/src/browser/services/KeyboardService.ts b/src/browser/services/KeyboardService.ts index 493f30653..a85eb2339 100644 --- a/src/browser/services/KeyboardService.ts +++ b/src/browser/services/KeyboardService.ts @@ -3,13 +3,13 @@ * @license MIT */ -import { IKeyboardService } from 'browser/services/Services'; -import { evaluateKeyboardEvent } from 'common/input/Keyboard'; -import { KittyKeyboard, KittyKeyboardEventType, KittyKeyboardFlags } from 'common/input/KittyKeyboard'; -import { Win32InputMode } from 'common/input/Win32InputMode'; -import { isMac } from 'common/Platform'; -import { ICoreService, IOptionsService } from 'common/services/Services'; -import { IKeyboardResult } from 'common/Types'; +import { IKeyboardService } from './Services'; +import { evaluateKeyboardEvent } from '../../common/input/Keyboard'; +import { KittyKeyboard, KittyKeyboardEventType, KittyKeyboardFlags } from '../../common/input/KittyKeyboard'; +import { Win32InputMode } from '../../common/input/Win32InputMode'; +import { isMac } from '../../common/Platform'; +import { ICoreService, IOptionsService } from '../../common/services/Services'; +import { IKeyboardResult } from '../../common/Types'; export class KeyboardService implements IKeyboardService { public serviceBrand: undefined; diff --git a/src/browser/services/LinkProviderService.ts b/src/browser/services/LinkProviderService.ts index 7a7b530d0..fe4b564a2 100644 --- a/src/browser/services/LinkProviderService.ts +++ b/src/browser/services/LinkProviderService.ts @@ -1,6 +1,6 @@ -import { ILinkProvider, ILinkProviderService } from 'browser/services/Services'; -import { Disposable, toDisposable } from 'common/Lifecycle'; -import { IDisposable } from 'common/Types'; +import { ILinkProvider, ILinkProviderService } from './Services'; +import { Disposable, toDisposable } from '../../common/Lifecycle'; +import { IDisposable } from '../../common/Types'; export class LinkProviderService extends Disposable implements ILinkProviderService { declare public serviceBrand: undefined; diff --git a/src/browser/services/MouseCoordsService.ts b/src/browser/services/MouseCoordsService.ts index 60f0a6ab9..7e110ea98 100644 --- a/src/browser/services/MouseCoordsService.ts +++ b/src/browser/services/MouseCoordsService.ts @@ -3,9 +3,9 @@ * @license MIT */ -import { getWindow } from 'browser/Dom'; -import { getCoords, getCoordsRelativeToElement } from 'browser/input/Mouse'; -import { ICharSizeService, IMouseCoordsService, IRenderService } from 'browser/services/Services'; +import { getWindow } from '../Dom'; +import { getCoords, getCoordsRelativeToElement } from '../input/Mouse'; +import { ICharSizeService, IMouseCoordsService, IRenderService } from './Services'; export class MouseCoordsService implements IMouseCoordsService { public serviceBrand: undefined; diff --git a/src/browser/services/MouseService.test.ts b/src/browser/services/MouseService.test.ts index d7aa7308f..6d760ff0a 100644 --- a/src/browser/services/MouseService.test.ts +++ b/src/browser/services/MouseService.test.ts @@ -3,12 +3,12 @@ * @license MIT */ import { assert } from 'chai'; -import { AltMouseCursorController, MouseEventCssClasses, MouseService } from 'browser/services/MouseService'; -import { MouseStateService } from 'common/services/MouseStateService'; -import { CoreMouseAction, CoreMouseButton } from 'common/Types'; -import { IBufferService, ICoreService, ILogService, IOptionsService } from 'common/services/Services'; -import { OptionsService } from 'common/services/OptionsService'; -import { MockCoreBrowserService, MockRenderService, MockSelectionService } from 'browser/TestUtils.test'; +import { AltMouseCursorController, MouseEventCssClasses, MouseService } from './MouseService'; +import { MouseStateService } from '../../common/services/MouseStateService'; +import { CoreMouseAction, CoreMouseButton } from '../../common/Types'; +import { IBufferService, ICoreService, ILogService, IOptionsService } from '../../common/services/Services'; +import { OptionsService } from '../../common/services/OptionsService'; +import { MockCoreBrowserService, MockRenderService, MockSelectionService } from '../TestUtils.test'; function toBytes(s: string | undefined): number[] { if (!s) { diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts index f9d68f42e..61cbfa86c 100644 --- a/src/browser/services/MouseService.ts +++ b/src/browser/services/MouseService.ts @@ -3,13 +3,13 @@ * @license MIT */ -import { addDisposableListener } from 'browser/Dom'; -import { IBufferService, IMouseStateService, ICoreService, ILogService, IOptionsService } from 'common/services/Services'; -import { CoreMouseAction, CoreMouseButton, CoreMouseEventType, ICoreMouseEvent, IDisposable } from 'common/Types'; -import { C0 } from 'common/data/EscapeSequences'; -import { DisposableStore, MutableDisposable, toDisposable } from 'common/Lifecycle'; +import { addDisposableListener } from '../Dom'; +import { IBufferService, IMouseStateService, ICoreService, ILogService, IOptionsService } from '../../common/services/Services'; +import { CoreMouseAction, CoreMouseButton, CoreMouseEventType, ICoreMouseEvent, IDisposable } from '../../common/Types'; +import { C0 } from '../../common/data/EscapeSequences'; +import { DisposableStore, MutableDisposable, toDisposable } from '../../common/Lifecycle'; import { ICoreBrowserService, IMouseCoordsService, IMouseService, IMouseServiceTarget, IRenderService, ISelectionService } from './Services'; -import { Gesture, EventType as GestureEventType, IGestureEvent } from 'browser/scrollable/touch'; +import { Gesture, EventType as GestureEventType, IGestureEvent } from '../scrollable/touch'; type RequestedMouseEvents = Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener | null>; diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index e999b7eac..75cd55418 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -3,14 +3,14 @@ * @license MIT */ -import { RenderDebouncer } from 'browser/RenderDebouncer'; -import { IRenderDebouncerWithCallback } from 'browser/Types'; -import { IRenderDimensions, IRenderer } from 'browser/renderer/shared/Types'; -import { ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; -import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; -import { DebouncedIdleTask } from 'common/TaskQueue'; -import { IBufferService, ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services'; -import { Emitter } from 'common/Event'; +import { RenderDebouncer } from '../RenderDebouncer'; +import { IRenderDebouncerWithCallback } from '../Types'; +import { IRenderDimensions, IRenderer } from '../renderer/shared/Types'; +import { ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from './Services'; +import { Disposable, MutableDisposable, toDisposable } from '../../common/Lifecycle'; +import { DebouncedIdleTask } from '../../common/TaskQueue'; +import { IBufferService, ICoreService, IDecorationService, ILogService, IOptionsService } from '../../common/services/Services'; +import { Emitter } from '../../common/Event'; interface ISelectionState { start: [number, number] | undefined; diff --git a/src/browser/services/SelectionService.test.ts b/src/browser/services/SelectionService.test.ts index 04d691d40..b48a4ee38 100644 --- a/src/browser/services/SelectionService.test.ts +++ b/src/browser/services/SelectionService.test.ts @@ -5,16 +5,16 @@ import { assert } from 'chai'; import { SelectionService, SelectionMode } from './SelectionService'; -import { SelectionModel } from 'browser/selection/SelectionModel'; -import { IBufferLine } from 'common/Types'; -import { MockBufferService, MockOptionsService, MockCoreService, MockMouseStateService, createCellData } from 'common/TestUtils.test'; -import { BufferLine } from 'common/buffer/BufferLine'; -import { BufferLineStringCache } from 'common/buffer/BufferLineStringCache'; -import { IBufferService, IOptionsService } from 'common/services/Services'; -import { MockCoreBrowserService, MockMouseService, MockRenderService } from 'browser/TestUtils.test'; -import { CellData } from 'common/buffer/CellData'; -import { IBuffer } from 'common/buffer/Types'; -import { IRenderService } from 'browser/services/Services'; +import { SelectionModel } from '../selection/SelectionModel'; +import { IBufferLine } from '../../common/Types'; +import { MockBufferService, MockOptionsService, MockCoreService, MockMouseStateService, createCellData } from '../../common/TestUtils.test'; +import { BufferLine } from '../../common/buffer/BufferLine'; +import { BufferLineStringCache } from '../../common/buffer/BufferLineStringCache'; +import { IBufferService, IOptionsService } from '../../common/services/Services'; +import { MockCoreBrowserService, MockMouseService, MockRenderService } from '../TestUtils.test'; +import { CellData } from '../../common/buffer/CellData'; +import { IBuffer } from '../../common/buffer/Types'; +import { IRenderService } from './Services'; const TEST_STRING_CACHE = new BufferLineStringCache(); diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index 6516e5154..d6b015b61 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -3,20 +3,20 @@ * @license MIT */ -import { IBufferRange, ILinkifier2 } from 'browser/Types'; -import { getCoordsRelativeToElement } from 'browser/input/Mouse'; -import { moveToCellSequence } from 'browser/input/MoveToCell'; -import { SelectionModel } from 'browser/selection/SelectionModel'; -import { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types'; -import { ICoreBrowserService, IMouseCoordsService, IRenderService, ISelectionService } from 'browser/services/Services'; -import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; -import * as Browser from 'common/Platform'; -import { IBufferLine, ICellData, IDisposable } from 'common/Types'; -import { getRangeLength } from 'common/buffer/BufferRange'; -import { CellData } from 'common/buffer/CellData'; -import { IBuffer } from 'common/buffer/Types'; -import { IBufferService, ICoreService, IMouseStateService, IOptionsService } from 'common/services/Services'; -import { Emitter } from 'common/Event'; +import { IBufferRange, ILinkifier2 } from '../Types'; +import { getCoordsRelativeToElement } from '../input/Mouse'; +import { moveToCellSequence } from '../input/MoveToCell'; +import { SelectionModel } from '../selection/SelectionModel'; +import { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from '../selection/Types'; +import { ICoreBrowserService, IMouseCoordsService, IRenderService, ISelectionService } from './Services'; +import { Disposable, MutableDisposable, toDisposable } from '../../common/Lifecycle'; +import * as Browser from '../../common/Platform'; +import { IBufferLine, ICellData, IDisposable } from '../../common/Types'; +import { getRangeLength } from '../../common/buffer/BufferRange'; +import { CellData } from '../../common/buffer/CellData'; +import { IBuffer } from '../../common/buffer/Types'; +import { IBufferService, ICoreService, IMouseStateService, IOptionsService } from '../../common/services/Services'; +import { Emitter } from '../../common/Event'; const enum Constants { /** diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts index 72659cafe..2b8e06b7d 100644 --- a/src/browser/services/Services.ts +++ b/src/browser/services/Services.ts @@ -3,12 +3,12 @@ * @license MIT */ -import { IRenderDimensions, IRenderer } from 'browser/renderer/shared/Types'; -import { IColorSet, ILink, ReadonlyColorSet } from 'browser/Types'; -import { ISelectionRedrawRequestEvent as ISelectionRequestRedrawEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types'; -import { createDecorator } from 'common/services/ServiceRegistry'; -import { AllColorIndex, IDisposable, IKeyboardResult } from 'common/Types'; -import type { IEvent } from 'common/Event'; +import { IRenderDimensions, IRenderer } from '../renderer/shared/Types'; +import { IColorSet, ILink, ReadonlyColorSet } from '../Types'; +import { ISelectionRedrawRequestEvent as ISelectionRequestRedrawEvent, ISelectionRequestScrollLinesEvent } from '../selection/Types'; +import { createDecorator } from '../../common/services/ServiceRegistry'; +import { AllColorIndex, IDisposable, IKeyboardResult } from '../../common/Types'; +import type { IEvent } from '../../common/Event'; export const ICharSizeService = createDecorator('CharSizeService'); export interface ICharSizeService { diff --git a/src/browser/services/ThemeService.test.ts b/src/browser/services/ThemeService.test.ts index a36dea31c..08a319ef5 100644 --- a/src/browser/services/ThemeService.test.ts +++ b/src/browser/services/ThemeService.test.ts @@ -5,9 +5,9 @@ import jsdom = require('jsdom'); import { assert } from 'chai'; -import { ThemeService } from 'browser/services/ThemeService'; -import { OptionsService } from 'common/services/OptionsService'; -import { DEFAULT_ANSI_COLORS } from 'browser/Types'; +import { ThemeService } from './ThemeService'; +import { OptionsService } from '../../common/services/OptionsService'; +import { DEFAULT_ANSI_COLORS } from '../Types'; describe('ThemeService', () => { let themeService: ThemeService; diff --git a/src/browser/services/ThemeService.ts b/src/browser/services/ThemeService.ts index f2b23f3ce..9b39b99ad 100644 --- a/src/browser/services/ThemeService.ts +++ b/src/browser/services/ThemeService.ts @@ -3,14 +3,14 @@ * @license MIT */ -import { ColorContrastCache } from 'browser/ColorContrastCache'; -import { IThemeService } from 'browser/services/Services'; -import { DEFAULT_ANSI_COLORS, IColorContrastCache, IColorSet, ReadonlyColorSet } from 'browser/Types'; -import { color, css, NULL_COLOR } from 'common/Color'; -import { Disposable } from 'common/Lifecycle'; -import { IOptionsService, ITheme } from 'common/services/Services'; -import { AllColorIndex, IColor, SpecialColorIndex } from 'common/Types'; -import { Emitter } from 'common/Event'; +import { ColorContrastCache } from '../ColorContrastCache'; +import { IThemeService } from './Services'; +import { DEFAULT_ANSI_COLORS, IColorContrastCache, IColorSet, ReadonlyColorSet } from '../Types'; +import { color, css, NULL_COLOR } from '../../common/Color'; +import { Disposable } from '../../common/Lifecycle'; +import { IOptionsService, ITheme } from '../../common/services/Services'; +import { AllColorIndex, IColor, SpecialColorIndex } from '../../common/Types'; +import { Emitter } from '../../common/Event'; interface IRestoreColorSet { foreground: IColor; diff --git a/src/browser/tsconfig.json b/src/browser/tsconfig.json index ff5bf6ad7..52c2051ab 100644 --- a/src/browser/tsconfig.json +++ b/src/browser/tsconfig.json @@ -9,17 +9,15 @@ "types": [ "../../node_modules/@types/mocha" ], - "skipLibCheck": true, - "paths": { - "common/*": [ "./../common/*" ], - "*": [ "./../*" ] - } + "skipLibCheck": true }, "include": [ "./**/*", "../../typings/xterm.d.ts" ], "references": [ - { "path": "../common" } + { + "path": "../common" + } ] } diff --git a/src/common/Async.ts b/src/common/Async.ts index 276532fb8..3189d6293 100644 --- a/src/common/Async.ts +++ b/src/common/Async.ts @@ -5,7 +5,7 @@ * Minimal async helpers for xterm.js core. */ -import { DisposableStore, IDisposable, toDisposable } from 'common/Lifecycle'; +import { DisposableStore, IDisposable, toDisposable } from './Lifecycle'; export function timeout(millis: number): Promise { return new Promise(resolve => setTimeout(resolve, millis)); diff --git a/src/common/CircularList.test.ts b/src/common/CircularList.test.ts index 7a578a8cc..4c07b16c3 100644 --- a/src/common/CircularList.test.ts +++ b/src/common/CircularList.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { CircularList } from 'common/CircularList'; +import { CircularList } from './CircularList'; describe('CircularList', () => { describe('push', () => { diff --git a/src/common/CircularList.ts b/src/common/CircularList.ts index 3663bfc17..f0b12d6a2 100644 --- a/src/common/CircularList.ts +++ b/src/common/CircularList.ts @@ -3,9 +3,9 @@ * @license MIT */ -import { ICircularList } from 'common/Types'; -import { Disposable } from 'common/Lifecycle'; -import { Emitter } from 'common/Event'; +import { ICircularList } from './Types'; +import { Disposable } from './Lifecycle'; +import { Emitter } from './Event'; export interface IInsertEvent { index: number; diff --git a/src/common/Color.test.ts b/src/common/Color.test.ts index dbe759b4f..5f4140d9e 100644 --- a/src/common/Color.test.ts +++ b/src/common/Color.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { channels, color, css, rgb, rgba, toPaddedHex, contrastRatio } from 'common/Color'; +import { channels, color, css, rgb, rgba, toPaddedHex, contrastRatio } from './Color'; describe('Color', () => { diff --git a/src/common/Color.ts b/src/common/Color.ts index b3d13fc99..aa3ca00ab 100644 --- a/src/common/Color.ts +++ b/src/common/Color.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { IColor, IColorRGB } from 'common/Types'; +import { IColor, IColorRGB } from './Types'; let $r = 0; let $g = 0; diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index 80168aa65..2cc48b03a 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -21,24 +21,24 @@ * http://linux.die.net/man/7/urxvt */ -import { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService, IMouseStateService, IUnicodeService, LogLevelEnum, ITerminalOptions, IOscLinkService } from 'common/services/Services'; -import { InstantiationService } from 'common/services/InstantiationService'; -import { LogService } from 'common/services/LogService'; -import { BufferService, BufferServiceConstants } from 'common/services/BufferService'; -import { OptionsService } from 'common/services/OptionsService'; -import { IDisposable, IAttributeData, ICoreTerminal, IScrollEvent } from 'common/Types'; -import { CoreService } from 'common/services/CoreService'; -import { MouseStateService } from 'common/services/MouseStateService'; -import { UnicodeService } from 'common/services/UnicodeService'; -import { CharsetService } from 'common/services/CharsetService'; -import { updateWindowsModeWrappedState } from 'common/WindowsMode'; -import { IFunctionIdentifier, IParams } from 'common/parser/Types'; -import { IBufferSet } from 'common/buffer/Types'; -import { InputHandler } from 'common/InputHandler'; -import { WriteBuffer } from 'common/input/WriteBuffer'; -import { OscLinkService } from 'common/services/OscLinkService'; -import { Emitter, EventUtils, type IEvent } from 'common/Event'; -import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; +import { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService, IMouseStateService, IUnicodeService, LogLevelEnum, ITerminalOptions, IOscLinkService } from './services/Services'; +import { InstantiationService } from './services/InstantiationService'; +import { LogService } from './services/LogService'; +import { BufferService, BufferServiceConstants } from './services/BufferService'; +import { OptionsService } from './services/OptionsService'; +import { IDisposable, IAttributeData, ICoreTerminal, IScrollEvent } from './Types'; +import { CoreService } from './services/CoreService'; +import { MouseStateService } from './services/MouseStateService'; +import { UnicodeService } from './services/UnicodeService'; +import { CharsetService } from './services/CharsetService'; +import { updateWindowsModeWrappedState } from './WindowsMode'; +import { IFunctionIdentifier, IParams } from './parser/Types'; +import { IBufferSet } from './buffer/Types'; +import { InputHandler } from './InputHandler'; +import { WriteBuffer } from './input/WriteBuffer'; +import { OscLinkService } from './services/OscLinkService'; +import { Emitter, EventUtils, type IEvent } from './Event'; +import { Disposable, MutableDisposable, toDisposable } from './Lifecycle'; // Only trigger this warning a single time per session let hasWriteSyncWarnHappened = false; diff --git a/src/common/Event.test.ts b/src/common/Event.test.ts index 8d1cfadd5..a8d09c5ce 100644 --- a/src/common/Event.test.ts +++ b/src/common/Event.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { Emitter } from 'common/Event'; +import { Emitter } from './Event'; describe('Emitter', () => { it('should fire with 0 listeners without error', () => { diff --git a/src/common/Event.ts b/src/common/Event.ts index f92ab2ac1..88417fc83 100644 --- a/src/common/Event.ts +++ b/src/common/Event.ts @@ -6,7 +6,7 @@ * Simplified from VS Code's event.ts - no leak detection/profiling. */ -import { IDisposable, DisposableStore, toDisposable } from 'common/Lifecycle'; +import { IDisposable, DisposableStore, toDisposable } from './Lifecycle'; export interface IEvent { (listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[] | DisposableStore): IDisposable; diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index 5c59b062b..20d685e91 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -4,19 +4,19 @@ */ import { assert } from 'chai'; -import { InputHandler } from 'common/InputHandler'; -import { IBufferLine, IAttributeData, IColorEvent, ColorRequestType, SpecialColorIndex } from 'common/Types'; -import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; -import { CellData } from 'common/buffer/CellData'; -import { Attributes, BgFlags, UnderlineStyle } from 'common/buffer/Constants'; -import { AttributeData, ExtendedAttrs } from 'common/buffer/AttributeData'; -import { Params } from 'common/parser/Params'; -import { MockCoreService, MockBufferService, MockOptionsService, MockLogService, MockMouseStateService, MockCharsetService, MockUnicodeService, MockOscLinkService, extendedAttributes } from 'common/TestUtils.test'; -import { IBufferService, ICoreService, type IOscLinkService } from 'common/services/Services'; -import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; -import { BufferService } from 'common/services/BufferService'; -import { CoreService } from 'common/services/CoreService'; -import { OscLinkService } from 'common/services/OscLinkService'; +import { InputHandler } from './InputHandler'; +import { IBufferLine, IAttributeData, IColorEvent, ColorRequestType, SpecialColorIndex } from './Types'; +import { DEFAULT_ATTR_DATA } from './buffer/BufferLine'; +import { CellData } from './buffer/CellData'; +import { Attributes, BgFlags, UnderlineStyle } from './buffer/Constants'; +import { AttributeData, ExtendedAttrs } from './buffer/AttributeData'; +import { Params } from './parser/Params'; +import { MockCoreService, MockBufferService, MockOptionsService, MockLogService, MockMouseStateService, MockCharsetService, MockUnicodeService, MockOscLinkService, extendedAttributes } from './TestUtils.test'; +import { IBufferService, ICoreService, type IOscLinkService } from './services/Services'; +import { DEFAULT_OPTIONS } from './services/OptionsService'; +import { BufferService } from './services/BufferService'; +import { CoreService } from './services/CoreService'; +import { OscLinkService } from './services/OscLinkService'; function getCursor(bufferService: IBufferService): number[] { diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index fd1fdb8a1..f7ab9c435 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -4,26 +4,26 @@ * @license MIT */ -import { IInputHandler, IAttributeData, IDisposable, IWindowOptions, IColorEvent, IParseStack, ColorIndex, ColorRequestType, SpecialColorIndex } from 'common/Types'; -import { C0, C1 } from 'common/data/EscapeSequences'; -import { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets'; -import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser'; -import { Disposable } from 'common/Lifecycle'; -import { StringToUtf32, stringFromCodePoint, Utf8ToUtf32 } from 'common/input/TextDecoder'; -import { BufferLine, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; -import { IParsingState, IEscapeSequenceParser, IParams, IFunctionIdentifier } from 'common/parser/Types'; -import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags, Content, UnderlineStyle } from 'common/buffer/Constants'; -import { CellData } from 'common/buffer/CellData'; -import { AttributeData } from 'common/buffer/AttributeData'; -import { ICoreService, IBufferService, IOptionsService, ILogService, IMouseStateService, ICharsetService, IUnicodeService, LogLevelEnum, IOscLinkService } from 'common/services/Services'; -import { UnicodeService } from 'common/services/UnicodeService'; -import { OscHandler } from 'common/parser/OscParser'; -import { DcsHandler } from 'common/parser/DcsParser'; -import { ApcHandler } from 'common/parser/ApcParser'; -import { IBuffer } from 'common/buffer/Types'; -import { parseColor } from 'common/input/XParseColor'; -import { Emitter } from 'common/Event'; -import { XTERM_VERSION } from 'common/Version'; +import { IInputHandler, IAttributeData, IDisposable, IWindowOptions, IColorEvent, IParseStack, ColorIndex, ColorRequestType, SpecialColorIndex } from './Types'; +import { C0, C1 } from './data/EscapeSequences'; +import { CHARSETS, DEFAULT_CHARSET } from './data/Charsets'; +import { EscapeSequenceParser } from './parser/EscapeSequenceParser'; +import { Disposable } from './Lifecycle'; +import { StringToUtf32, stringFromCodePoint, Utf8ToUtf32 } from './input/TextDecoder'; +import { BufferLine, DEFAULT_ATTR_DATA } from './buffer/BufferLine'; +import { IParsingState, IEscapeSequenceParser, IParams, IFunctionIdentifier } from './parser/Types'; +import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags, Content, UnderlineStyle } from './buffer/Constants'; +import { CellData } from './buffer/CellData'; +import { AttributeData } from './buffer/AttributeData'; +import { ICoreService, IBufferService, IOptionsService, ILogService, IMouseStateService, ICharsetService, IUnicodeService, LogLevelEnum, IOscLinkService } from './services/Services'; +import { UnicodeService } from './services/UnicodeService'; +import { OscHandler } from './parser/OscParser'; +import { DcsHandler } from './parser/DcsParser'; +import { ApcHandler } from './parser/ApcParser'; +import { IBuffer } from './buffer/Types'; +import { parseColor } from './input/XParseColor'; +import { Emitter } from './Event'; +import { XTERM_VERSION } from './Version'; /** * Map collect to glevel. Used in `selectCharset`. diff --git a/src/common/MultiKeyMap.test.ts b/src/common/MultiKeyMap.test.ts index b908321c3..255cec1a1 100644 --- a/src/common/MultiKeyMap.test.ts +++ b/src/common/MultiKeyMap.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { FourKeyMap, TwoKeyMap } from 'common/MultiKeyMap'; +import { FourKeyMap, TwoKeyMap } from './MultiKeyMap'; const strictEqual = assert.strictEqual; diff --git a/src/common/SortedList.test.ts b/src/common/SortedList.test.ts index 4718cf971..a446b7874 100644 --- a/src/common/SortedList.test.ts +++ b/src/common/SortedList.test.ts @@ -4,8 +4,8 @@ */ import { assert } from 'chai'; -import { SortedList } from 'common/SortedList'; -import { MockLogService } from 'common/TestUtils.test'; +import { SortedList } from './SortedList'; +import { MockLogService } from './TestUtils.test'; const deepStrictEqual = assert.deepStrictEqual; diff --git a/src/common/SortedList.ts b/src/common/SortedList.ts index c6dc62085..8a10076e3 100644 --- a/src/common/SortedList.ts +++ b/src/common/SortedList.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { IdleTaskQueue } from 'common/TaskQueue'; -import type { ILogService } from 'common/services/Services'; +import { IdleTaskQueue } from './TaskQueue'; +import type { ILogService } from './services/Services'; // Work variables to avoid garbage collection. let i = 0; diff --git a/src/common/StringBuilder.test.ts b/src/common/StringBuilder.test.ts index 1794de0f7..da56c79c2 100644 --- a/src/common/StringBuilder.test.ts +++ b/src/common/StringBuilder.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { LimitedStringBuilder, StringBuilder } from 'common/StringBuilder'; +import { LimitedStringBuilder, StringBuilder } from './StringBuilder'; describe('StringBuilder', () => { it('should start empty', () => { diff --git a/src/common/TaskQueue.ts b/src/common/TaskQueue.ts index 77f9aea55..9e15c823a 100644 --- a/src/common/TaskQueue.ts +++ b/src/common/TaskQueue.ts @@ -3,7 +3,7 @@ * @license MIT */ -import type { ILogService } from 'common/services/Services'; +import type { ILogService } from './services/Services'; interface ITaskQueue { /** diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index 69c2d499d..3ae6db77d 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -3,17 +3,17 @@ * @license MIT */ -import { IBufferService, ICoreService, ILogService, IOptionsService, ITerminalOptions, IMouseStateService, ICharsetService, UnicodeCharProperties, UnicodeCharWidth, IUnicodeService, IUnicodeVersionProvider, LogLevelEnum, IDecorationService, IInternalDecoration, IOscLinkService, type IBufferResizeEvent } from 'common/services/Services'; -import { UnicodeService } from 'common/services/UnicodeService'; -import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; -import { IBufferSet, IBuffer } from 'common/buffer/Types'; -import { BufferSet } from 'common/buffer/BufferSet'; -import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEventType, ICharset, IModes, IAttributeData, IOscLinkData, IDisposable, IBufferLine, IExtendedAttrs } from 'common/Types'; -import { UnicodeV6 } from 'common/input/UnicodeV6'; +import { IBufferService, ICoreService, ILogService, IOptionsService, ITerminalOptions, IMouseStateService, ICharsetService, UnicodeCharProperties, UnicodeCharWidth, IUnicodeService, IUnicodeVersionProvider, LogLevelEnum, IDecorationService, IInternalDecoration, IOscLinkService, type IBufferResizeEvent } from './services/Services'; +import { UnicodeService } from './services/UnicodeService'; +import { DEFAULT_OPTIONS } from './services/OptionsService'; +import { IBufferSet, IBuffer } from './buffer/Types'; +import { BufferSet } from './buffer/BufferSet'; +import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEventType, ICharset, IModes, IAttributeData, IOscLinkData, IDisposable, IBufferLine, IExtendedAttrs } from './Types'; +import { UnicodeV6 } from './input/UnicodeV6'; import { IDecorationOptions, IDecoration } from '@xterm/xterm'; -import { Emitter, type IEvent } from 'common/Event'; -import { CellData } from 'common/buffer/CellData'; -import { DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH } from 'common/buffer/Constants'; +import { Emitter, type IEvent } from './Event'; +import { CellData } from './buffer/CellData'; +import { DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH } from './buffer/Constants'; export function createCellData(attr: number, char: string, width: number): CellData { return CellData.fromCharData([attr, char, width, char.length === 0 ? 0 : char.charCodeAt(0)]); diff --git a/src/common/Types.ts b/src/common/Types.ts index 07aebbe8f..11c590b12 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -3,13 +3,13 @@ * @license MIT */ -import { IDeleteEvent, IInsertEvent } from 'common/CircularList'; -import { UnderlineStyle } from 'common/buffer/Constants'; -import { IBufferSet } from 'common/buffer/Types'; -import { IParams } from 'common/parser/Types'; -import { IMouseStateService, ICoreService, IOptionsService, IUnicodeService } from 'common/services/Services'; +import { IDeleteEvent, IInsertEvent } from './CircularList'; +import { UnderlineStyle } from './buffer/Constants'; +import { IBufferSet } from './buffer/Types'; +import { IParams } from './parser/Types'; +import { IMouseStateService, ICoreService, IOptionsService, IUnicodeService } from './services/Services'; import { IFunctionIdentifier, ITerminalOptions as IPublicTerminalOptions } from '@xterm/xterm'; -import type { Emitter, IEvent } from 'common/Event'; +import type { Emitter, IEvent } from './Event'; export interface ICoreTerminal { mouseStateService: IMouseStateService; diff --git a/src/common/WindowsMode.ts b/src/common/WindowsMode.ts index 7cff094b2..d4862e00e 100644 --- a/src/common/WindowsMode.ts +++ b/src/common/WindowsMode.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { CHAR_DATA_CODE_INDEX, NULL_CELL_CODE, WHITESPACE_CELL_CODE } from 'common/buffer/Constants'; -import { IBufferService } from 'common/services/Services'; +import { CHAR_DATA_CODE_INDEX, NULL_CELL_CODE, WHITESPACE_CELL_CODE } from './buffer/Constants'; +import { IBufferService } from './services/Services'; export function updateWindowsModeWrappedState(bufferService: IBufferService): void { // Winpty does not support wraparound mode which means that lines will never diff --git a/src/common/buffer/AttributeData.ts b/src/common/buffer/AttributeData.ts index 6221fb81d..c9c940f93 100644 --- a/src/common/buffer/AttributeData.ts +++ b/src/common/buffer/AttributeData.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { IAttributeData, IColorRGB, IExtendedAttrs } from 'common/Types'; -import { Attributes, FgFlags, BgFlags, UnderlineStyle, ExtFlags } from 'common/buffer/Constants'; +import { IAttributeData, IColorRGB, IExtendedAttrs } from '../Types'; +import { Attributes, FgFlags, BgFlags, UnderlineStyle, ExtFlags } from './Constants'; export class AttributeData implements IAttributeData { public static toColorRGB(value: number): IColorRGB { diff --git a/src/common/buffer/Buffer.test.ts b/src/common/buffer/Buffer.test.ts index abfea7b06..8116c28c4 100644 --- a/src/common/buffer/Buffer.test.ts +++ b/src/common/buffer/Buffer.test.ts @@ -4,13 +4,13 @@ */ import { assert } from 'chai'; -import { Buffer } from 'common/buffer/Buffer'; -import { CircularList } from 'common/CircularList'; -import { MockOptionsService, MockBufferService, MockLogService, createCellData } from 'common/TestUtils.test'; -import { BufferLine, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; -import { BufferLineStringCache } from 'common/buffer/BufferLineStringCache'; -import { CellData } from 'common/buffer/CellData'; -import { ExtendedAttrs } from 'common/buffer/AttributeData'; +import { Buffer } from './Buffer'; +import { CircularList } from '../CircularList'; +import { MockOptionsService, MockBufferService, MockLogService, createCellData } from '../TestUtils.test'; +import { BufferLine, DEFAULT_ATTR_DATA } from './BufferLine'; +import { BufferLineStringCache } from './BufferLineStringCache'; +import { CellData } from './CellData'; +import { ExtendedAttrs } from './AttributeData'; const INIT_COLS = 80; const INIT_ROWS = 24; diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts index 34ef02f6a..83479f422 100644 --- a/src/common/buffer/Buffer.ts +++ b/src/common/buffer/Buffer.ts @@ -3,20 +3,20 @@ * @license MIT */ -import { CircularList, IInsertEvent } from 'common/CircularList'; -import { Disposable, toDisposable } from 'common/Lifecycle'; -import { IdleTaskQueue } from 'common/TaskQueue'; -import { IAttributeData, IBufferLine, ICellData, ICharset } from 'common/Types'; -import { ExtendedAttrs } from 'common/buffer/AttributeData'; -import { BufferLine, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; -import { BufferLineStringCache } from 'common/buffer/BufferLineStringCache'; -import { getWrappedLineTrimmedLength, reflowLargerApplyNewLayout, reflowLargerCreateNewLayout, reflowLargerGetLinesToRemove, reflowSmallerGetNewLineLengths } from 'common/buffer/BufferReflow'; -import { CellData } from 'common/buffer/CellData'; -import { NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_CODE, WHITESPACE_CELL_WIDTH } from 'common/buffer/Constants'; -import { Marker } from 'common/buffer/Marker'; -import { IBuffer } from 'common/buffer/Types'; -import { DEFAULT_CHARSET } from 'common/data/Charsets'; -import { IBufferService, ILogService, IOptionsService } from 'common/services/Services'; +import { CircularList, IInsertEvent } from '../CircularList'; +import { Disposable, toDisposable } from '../Lifecycle'; +import { IdleTaskQueue } from '../TaskQueue'; +import { IAttributeData, IBufferLine, ICellData, ICharset } from '../Types'; +import { ExtendedAttrs } from './AttributeData'; +import { BufferLine, DEFAULT_ATTR_DATA } from './BufferLine'; +import { BufferLineStringCache } from './BufferLineStringCache'; +import { getWrappedLineTrimmedLength, reflowLargerApplyNewLayout, reflowLargerCreateNewLayout, reflowLargerGetLinesToRemove, reflowSmallerGetNewLineLengths } from './BufferReflow'; +import { CellData } from './CellData'; +import { NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_CODE, WHITESPACE_CELL_WIDTH } from './Constants'; +import { Marker } from './Marker'; +import { IBuffer } from './Types'; +import { DEFAULT_CHARSET } from '../data/Charsets'; +import { IBufferService, ILogService, IOptionsService } from '../services/Services'; export const MAX_BUFFER_SIZE = 4294967295; // 2^32 - 1 diff --git a/src/common/buffer/BufferLine.test.ts b/src/common/buffer/BufferLine.test.ts index 3aec28872..3658611cb 100644 --- a/src/common/buffer/BufferLine.test.ts +++ b/src/common/buffer/BufferLine.test.ts @@ -2,14 +2,14 @@ * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ -import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR, Content, UnderlineStyle, BgFlags, Attributes, FgFlags } from 'common/buffer/Constants'; -import { BufferLine } from 'common/buffer//BufferLine'; -import { BufferLineStringCache } from 'common/buffer/BufferLineStringCache'; -import { CellData } from 'common/buffer/CellData'; +import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR, Content, UnderlineStyle, BgFlags, Attributes, FgFlags } from './Constants'; +import { BufferLine } from './BufferLine'; +import { BufferLineStringCache } from './BufferLineStringCache'; +import { CellData } from './CellData'; import { CharData, IBufferLine, ICellData } from '../Types'; import { assert } from 'chai'; -import { AttributeData } from 'common/buffer/AttributeData'; -import { createCellData, NULL_CELL_DATA, extendedAttributes } from 'common/TestUtils.test'; +import { AttributeData } from './AttributeData'; +import { createCellData, NULL_CELL_DATA, extendedAttributes } from '../TestUtils.test'; const TEST_STRING_CACHE = new BufferLineStringCache(); diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts index e24a000c9..f927f76fc 100644 --- a/src/common/buffer/BufferLine.ts +++ b/src/common/buffer/BufferLine.ts @@ -3,12 +3,12 @@ * @license MIT */ -import { CharData, IAttributeData, IBufferLine, ICellData, IExtendedAttrs } from 'common/Types'; -import { AttributeData } from 'common/buffer/AttributeData'; -import { CellData } from 'common/buffer/CellData'; -import { Attributes, BgFlags, CHAR_DATA_ATTR_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, Content, NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, WHITESPACE_CELL_CHAR } from 'common/buffer/Constants'; -import { stringFromCodePoint } from 'common/input/TextDecoder'; -import { StringBuilder } from 'common/StringBuilder'; +import { CharData, IAttributeData, IBufferLine, ICellData, IExtendedAttrs } from '../Types'; +import { AttributeData } from './AttributeData'; +import { CellData } from './CellData'; +import { Attributes, BgFlags, CHAR_DATA_ATTR_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, Content, NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, WHITESPACE_CELL_CHAR } from './Constants'; +import { stringFromCodePoint } from '../input/TextDecoder'; +import { StringBuilder } from '../StringBuilder'; // Buffer memory layout: // diff --git a/src/common/buffer/BufferLineStringCache.ts b/src/common/buffer/BufferLineStringCache.ts index b365eef70..54c97423a 100644 --- a/src/common/buffer/BufferLineStringCache.ts +++ b/src/common/buffer/BufferLineStringCache.ts @@ -3,9 +3,9 @@ * @license MIT */ -import type { IBufferLineStringCache, IBufferLineStringCacheEntry } from 'common/buffer/BufferLine'; -import { disposableTimeout } from 'common/Async'; -import { Disposable, MutableDisposable, toDisposable, type IDisposable } from 'common/Lifecycle'; +import type { IBufferLineStringCache, IBufferLineStringCacheEntry } from './BufferLine'; +import { disposableTimeout } from '../Async'; +import { Disposable, MutableDisposable, toDisposable, type IDisposable } from '../Lifecycle'; const enum Constants { CACHE_TTL_MS = 15000 diff --git a/src/common/buffer/BufferRange.test.ts b/src/common/buffer/BufferRange.test.ts index 15d1b8317..056e9c85a 100644 --- a/src/common/buffer/BufferRange.test.ts +++ b/src/common/buffer/BufferRange.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { getRangeLength } from 'common/buffer/BufferRange'; +import { getRangeLength } from './BufferRange'; import { IBufferRange } from '@xterm/xterm'; describe('BufferRange', () => { diff --git a/src/common/buffer/BufferReflow.test.ts b/src/common/buffer/BufferReflow.test.ts index d71186ddd..c799fee19 100644 --- a/src/common/buffer/BufferReflow.test.ts +++ b/src/common/buffer/BufferReflow.test.ts @@ -3,10 +3,10 @@ * @license MIT */ import { assert } from 'chai'; -import { BufferLine } from 'common/buffer/BufferLine'; -import { BufferLineStringCache } from 'common/buffer/BufferLineStringCache'; -import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE } from 'common/buffer/Constants'; -import { reflowSmallerGetNewLineLengths } from 'common/buffer/BufferReflow'; +import { BufferLine } from './BufferLine'; +import { BufferLineStringCache } from './BufferLineStringCache'; +import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE } from './Constants'; +import { reflowSmallerGetNewLineLengths } from './BufferReflow'; const TEST_STRING_CACHE = new BufferLineStringCache(); diff --git a/src/common/buffer/BufferReflow.ts b/src/common/buffer/BufferReflow.ts index 45a194711..d94739c30 100644 --- a/src/common/buffer/BufferReflow.ts +++ b/src/common/buffer/BufferReflow.ts @@ -3,9 +3,9 @@ * @license MIT */ -import { BufferLine } from 'common/buffer/BufferLine'; -import { CircularList } from 'common/CircularList'; -import { IBufferLine, ICellData } from 'common/Types'; +import { BufferLine } from './BufferLine'; +import { CircularList } from '../CircularList'; +import { IBufferLine, ICellData } from '../Types'; export interface INewLayoutResult { layout: number[]; diff --git a/src/common/buffer/BufferSet.test.ts b/src/common/buffer/BufferSet.test.ts index 8809a3cb6..71a1026dc 100644 --- a/src/common/buffer/BufferSet.test.ts +++ b/src/common/buffer/BufferSet.test.ts @@ -4,9 +4,9 @@ */ import { assert } from 'chai'; -import { BufferSet } from 'common/buffer/BufferSet'; -import { Buffer } from 'common/buffer/Buffer'; -import { MockOptionsService, MockBufferService, MockLogService, createCellData } from 'common/TestUtils.test'; +import { BufferSet } from './BufferSet'; +import { Buffer } from './Buffer'; +import { MockOptionsService, MockBufferService, MockLogService, createCellData } from '../TestUtils.test'; describe('BufferSet', () => { let bufferSet: BufferSet; diff --git a/src/common/buffer/BufferSet.ts b/src/common/buffer/BufferSet.ts index 32a4bf3b4..87b123386 100644 --- a/src/common/buffer/BufferSet.ts +++ b/src/common/buffer/BufferSet.ts @@ -3,12 +3,12 @@ * @license MIT */ -import { Disposable, MutableDisposable } from 'common/Lifecycle'; -import { IAttributeData } from 'common/Types'; -import { Buffer } from 'common/buffer/Buffer'; -import { IBuffer, IBufferSet } from 'common/buffer/Types'; -import { IBufferService, ILogService, IOptionsService } from 'common/services/Services'; -import { Emitter } from 'common/Event'; +import { Disposable, MutableDisposable } from '../Lifecycle'; +import { IAttributeData } from '../Types'; +import { Buffer } from './Buffer'; +import { IBuffer, IBufferSet } from './Types'; +import { IBufferService, ILogService, IOptionsService } from '../services/Services'; +import { Emitter } from '../Event'; /** * The BufferSet represents the set of two buffers used by xterm terminals (normal and alt) and diff --git a/src/common/buffer/CellData.test.ts b/src/common/buffer/CellData.test.ts index 77d1ef9ed..f611f418b 100644 --- a/src/common/buffer/CellData.test.ts +++ b/src/common/buffer/CellData.test.ts @@ -2,8 +2,8 @@ * Copyright (c) 2026 The xterm.js authors. All rights reserved. * @license MIT */ -import { Attributes, BgFlags, FgFlags, UnderlineStyle } from 'common/buffer/Constants'; -import { CellData } from 'common/buffer/CellData'; +import { Attributes, BgFlags, FgFlags, UnderlineStyle } from './Constants'; +import { CellData } from './CellData'; import { assert } from 'chai'; function createStyledCell(char: string, underlineStyle: UnderlineStyle, underlineColor: number): CellData { diff --git a/src/common/buffer/CellData.ts b/src/common/buffer/CellData.ts index 43c4c594b..18f04a30e 100644 --- a/src/common/buffer/CellData.ts +++ b/src/common/buffer/CellData.ts @@ -3,10 +3,10 @@ * @license MIT */ -import { CharData, ICellData, IExtendedAttrs } from 'common/Types'; -import { stringFromCodePoint } from 'common/input/TextDecoder'; -import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, Content } from 'common/buffer/Constants'; -import { AttributeData, ExtendedAttrs } from 'common/buffer/AttributeData'; +import { CharData, ICellData, IExtendedAttrs } from '../Types'; +import { stringFromCodePoint } from '../input/TextDecoder'; +import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, Content } from './Constants'; +import { AttributeData, ExtendedAttrs } from './AttributeData'; import type { IBufferCell as IBufferCellApi } from '@xterm/xterm'; /** diff --git a/src/common/buffer/Marker.ts b/src/common/buffer/Marker.ts index 59ad049b0..e69699c1b 100644 --- a/src/common/buffer/Marker.ts +++ b/src/common/buffer/Marker.ts @@ -3,9 +3,9 @@ * @license MIT */ -import { IDisposable, IMarker } from 'common/Types'; -import { Emitter } from 'common/Event'; -import { dispose } from 'common/Lifecycle'; +import { IDisposable, IMarker } from '../Types'; +import { Emitter } from '../Event'; +import { dispose } from '../Lifecycle'; export class Marker implements IMarker { private static _nextId = 1; diff --git a/src/common/buffer/Types.ts b/src/common/buffer/Types.ts index 85dd68eec..0cf448fcb 100644 --- a/src/common/buffer/Types.ts +++ b/src/common/buffer/Types.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { IAttributeData, ICircularList, IBufferLine, ICellData, IMarker, ICharset, IDisposable } from 'common/Types'; -import type { IEvent } from 'common/Event'; +import { IAttributeData, ICircularList, IBufferLine, ICellData, IMarker, ICharset, IDisposable } from '../Types'; +import type { IEvent } from '../Event'; // BufferIndex denotes a position in the buffer: [rowIndex, colIndex] export type BufferIndex = [number, number]; diff --git a/src/common/data/Charsets.ts b/src/common/data/Charsets.ts index 06c1f4b84..9680f4c05 100644 --- a/src/common/data/Charsets.ts +++ b/src/common/data/Charsets.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { ICharset } from 'common/Types'; +import { ICharset } from '../Types'; /** * The character sets supported by the terminal. These enable several languages diff --git a/src/common/input/Keyboard.test.ts b/src/common/input/Keyboard.test.ts index 47f516f0f..02c2c4380 100644 --- a/src/common/input/Keyboard.test.ts +++ b/src/common/input/Keyboard.test.ts @@ -1,7 +1,7 @@ import { assert } from 'chai'; -import { evaluateKeyboardEvent } from 'common/input/Keyboard'; -import { IKeyboardResult, IKeyboardEvent } from 'common/Types'; +import { evaluateKeyboardEvent } from './Keyboard'; +import { IKeyboardResult, IKeyboardEvent } from '../Types'; /** * A helper function for testing which allows passing in a partial event and defaults will be filled diff --git a/src/common/input/Keyboard.ts b/src/common/input/Keyboard.ts index 545b436c8..2c3ca28cd 100644 --- a/src/common/input/Keyboard.ts +++ b/src/common/input/Keyboard.ts @@ -4,8 +4,8 @@ * @license MIT */ -import { IKeyboardEvent, IKeyboardResult, KeyboardResultType } from 'common/Types'; -import { C0 } from 'common/data/EscapeSequences'; +import { IKeyboardEvent, IKeyboardResult, KeyboardResultType } from '../Types'; +import { C0 } from '../data/EscapeSequences'; // reg + shift key mappings for digits and special chars const KEYCODE_KEY_MAPPINGS: { [key: number]: [string, string]} = { diff --git a/src/common/input/KittyKeyboard.test.ts b/src/common/input/KittyKeyboard.test.ts index 2a832d75f..fe1bc30e2 100644 --- a/src/common/input/KittyKeyboard.test.ts +++ b/src/common/input/KittyKeyboard.test.ts @@ -1,7 +1,7 @@ import { assert } from 'chai'; -import { KittyKeyboard, KittyKeyboardEventType, KittyKeyboardFlags } from 'common/input/KittyKeyboard'; -import { IKeyboardEvent } from 'common/Types'; +import { KittyKeyboard, KittyKeyboardEventType, KittyKeyboardFlags } from './KittyKeyboard'; +import { IKeyboardEvent } from '../Types'; function createEvent(partialEvent: Partial = {}): IKeyboardEvent { return { diff --git a/src/common/input/KittyKeyboard.ts b/src/common/input/KittyKeyboard.ts index ff57b4f89..ec813e5b2 100644 --- a/src/common/input/KittyKeyboard.ts +++ b/src/common/input/KittyKeyboard.ts @@ -6,8 +6,8 @@ * @see https://sw.kovidgoyal.net/kitty/keyboard-protocol/ */ -import { IKeyboardEvent, IKeyboardResult, KeyboardResultType } from 'common/Types'; -import { C0 } from 'common/data/EscapeSequences'; +import { IKeyboardEvent, IKeyboardResult, KeyboardResultType } from '../Types'; +import { C0 } from '../data/EscapeSequences'; /** * Kitty keyboard protocol enhancement flags (bitfield). diff --git a/src/common/input/TextDecoder.test.ts b/src/common/input/TextDecoder.test.ts index 1959e1591..b32d8a721 100644 --- a/src/common/input/TextDecoder.test.ts +++ b/src/common/input/TextDecoder.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { StringToUtf32, stringFromCodePoint, Utf8ToUtf32, utf32ToString } from 'common/input/TextDecoder'; +import { StringToUtf32, stringFromCodePoint, Utf8ToUtf32, utf32ToString } from './TextDecoder'; // convert UTF32 codepoints to string diff --git a/src/common/input/UnicodeV6.test.ts b/src/common/input/UnicodeV6.test.ts index 98dc97dc2..a009ee5eb 100644 --- a/src/common/input/UnicodeV6.test.ts +++ b/src/common/input/UnicodeV6.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { UnicodeV6 } from 'common/input/UnicodeV6'; +import { UnicodeV6 } from './UnicodeV6'; it('wcwidth should match all values from the old implementation', function(): void { // new implementation diff --git a/src/common/input/UnicodeV6.ts b/src/common/input/UnicodeV6.ts index 83265f705..c872bc441 100644 --- a/src/common/input/UnicodeV6.ts +++ b/src/common/input/UnicodeV6.ts @@ -2,8 +2,8 @@ * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ -import { IUnicodeVersionProvider, UnicodeCharProperties, UnicodeCharWidth } from 'common/services/Services'; -import { UnicodeService } from 'common/services/UnicodeService'; +import { IUnicodeVersionProvider, UnicodeCharProperties, UnicodeCharWidth } from '../services/Services'; +import { UnicodeService } from '../services/UnicodeService'; const BMP_COMBINING = [ [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], diff --git a/src/common/input/Win32InputMode.test.ts b/src/common/input/Win32InputMode.test.ts index d0f333591..fdcc7459d 100644 --- a/src/common/input/Win32InputMode.test.ts +++ b/src/common/input/Win32InputMode.test.ts @@ -4,8 +4,8 @@ */ import { assert } from 'chai'; -import { Win32InputMode, Win32ControlKeyState } from 'common/input/Win32InputMode'; -import { IKeyboardEvent, KeyboardResultType } from 'common/Types'; +import { Win32InputMode, Win32ControlKeyState } from './Win32InputMode'; +import { IKeyboardEvent, KeyboardResultType } from '../Types'; type EventOpts = Partial; const ev = (opts: EventOpts): IKeyboardEvent => ({ diff --git a/src/common/input/Win32InputMode.ts b/src/common/input/Win32InputMode.ts index 72907836d..9f73440d5 100644 --- a/src/common/input/Win32InputMode.ts +++ b/src/common/input/Win32InputMode.ts @@ -14,8 +14,8 @@ * Rc: Repeat count (usually 1) */ -import { IKeyboardEvent, IKeyboardResult, KeyboardResultType } from 'common/Types'; -import { C0 } from 'common/data/EscapeSequences'; +import { IKeyboardEvent, IKeyboardResult, KeyboardResultType } from '../Types'; +import { C0 } from '../data/EscapeSequences'; /** * Win32 control key state flags (from Windows API). diff --git a/src/common/input/WriteBuffer.ts b/src/common/input/WriteBuffer.ts index 7ecab58ad..85774363b 100644 --- a/src/common/input/WriteBuffer.ts +++ b/src/common/input/WriteBuffer.ts @@ -4,9 +4,9 @@ * @license MIT */ -import { TimeoutTimer } from 'common/Async'; -import { Disposable, toDisposable } from 'common/Lifecycle'; -import { Emitter } from 'common/Event'; +import { TimeoutTimer } from '../Async'; +import { Disposable, toDisposable } from '../Lifecycle'; +import { Emitter } from '../Event'; const enum Constants { /** diff --git a/src/common/input/XParseColor.test.ts b/src/common/input/XParseColor.test.ts index 4a73cdbbf..9e7b03abe 100644 --- a/src/common/input/XParseColor.test.ts +++ b/src/common/input/XParseColor.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { parseColor, toRgbString } from 'common/input/XParseColor'; +import { parseColor, toRgbString } from './XParseColor'; describe('XParseColor', () => { describe('parseColor', () => { diff --git a/src/common/parser/ApcParser.test.ts b/src/common/parser/ApcParser.test.ts index c2200d918..b27b40039 100644 --- a/src/common/parser/ApcParser.test.ts +++ b/src/common/parser/ApcParser.test.ts @@ -3,9 +3,9 @@ * @license MIT */ import { assert } from 'chai'; -import { ApcParser, ApcHandler } from 'common/parser/ApcParser'; -import { StringToUtf32, utf32ToString } from 'common/input/TextDecoder'; -import { IApcHandler, IFunctionIdentifier } from 'common/parser/Types'; +import { ApcParser, ApcHandler } from './ApcParser'; +import { StringToUtf32, utf32ToString } from '../input/TextDecoder'; +import { IApcHandler, IFunctionIdentifier } from './Types'; function toUtf32(s: string): Uint32Array { const utf32 = new Uint32Array(s.length); diff --git a/src/common/parser/ApcParser.ts b/src/common/parser/ApcParser.ts index 4e647d037..f0d465efc 100644 --- a/src/common/parser/ApcParser.ts +++ b/src/common/parser/ApcParser.ts @@ -3,11 +3,11 @@ * @license MIT */ -import { IApcHandler, IHandlerCollection, ApcFallbackHandlerType, IApcParser, ISubParserStackState } from 'common/parser/Types'; -import { ParserConstants } from 'common/parser/Constants'; -import { utf32ToString } from 'common/input/TextDecoder'; -import { IDisposable } from 'common/Types'; -import { LimitedStringBuilder } from 'common/StringBuilder'; +import { IApcHandler, IHandlerCollection, ApcFallbackHandlerType, IApcParser, ISubParserStackState } from './Types'; +import { ParserConstants } from './Constants'; +import { utf32ToString } from '../input/TextDecoder'; +import { IDisposable } from '../Types'; +import { LimitedStringBuilder } from '../StringBuilder'; const EMPTY_HANDLERS: IApcHandler[] = []; diff --git a/src/common/parser/DcsParser.test.ts b/src/common/parser/DcsParser.test.ts index 0296fa88e..459bc8961 100644 --- a/src/common/parser/DcsParser.test.ts +++ b/src/common/parser/DcsParser.test.ts @@ -3,10 +3,10 @@ * @license MIT */ import { assert } from 'chai'; -import { DcsParser, DcsHandler } from 'common/parser/DcsParser'; -import { IDcsHandler, IParams, IFunctionIdentifier } from 'common/parser/Types'; -import { utf32ToString, StringToUtf32 } from 'common/input/TextDecoder'; -import { Params } from 'common/parser/Params'; +import { DcsParser, DcsHandler } from './DcsParser'; +import { IDcsHandler, IParams, IFunctionIdentifier } from './Types'; +import { utf32ToString, StringToUtf32 } from '../input/TextDecoder'; +import { Params } from './Params'; function toUtf32(s: string): Uint32Array { const utf32 = new Uint32Array(s.length); diff --git a/src/common/parser/DcsParser.ts b/src/common/parser/DcsParser.ts index 25c22b8dd..1b2d34835 100644 --- a/src/common/parser/DcsParser.ts +++ b/src/common/parser/DcsParser.ts @@ -3,12 +3,12 @@ * @license MIT */ -import { IDisposable } from 'common/Types'; -import { IDcsHandler, IParams, IHandlerCollection, IDcsParser, DcsFallbackHandlerType, ISubParserStackState } from 'common/parser/Types'; -import { utf32ToString } from 'common/input/TextDecoder'; -import { Params } from 'common/parser/Params'; -import { ParserConstants } from 'common/parser/Constants'; -import { LimitedStringBuilder } from 'common/StringBuilder'; +import { IDisposable } from '../Types'; +import { IDcsHandler, IParams, IHandlerCollection, IDcsParser, DcsFallbackHandlerType, ISubParserStackState } from './Types'; +import { utf32ToString } from '../input/TextDecoder'; +import { Params } from './Params'; +import { ParserConstants } from './Constants'; +import { LimitedStringBuilder } from '../StringBuilder'; const EMPTY_HANDLERS: IDcsHandler[] = []; diff --git a/src/common/parser/EscapeSequenceParser.test.ts b/src/common/parser/EscapeSequenceParser.test.ts index 2d5a04df7..f684da2c8 100644 --- a/src/common/parser/EscapeSequenceParser.test.ts +++ b/src/common/parser/EscapeSequenceParser.test.ts @@ -3,16 +3,16 @@ * @license MIT */ -import { IParsingState, IParams, ParamsArray, IOscParser, IOscHandler, OscFallbackHandlerType, IFunctionIdentifier, IParserStackState, ParserStackType } from 'common/parser/Types'; -import { EscapeSequenceParser, TransitionTable, VT500_TRANSITION_TABLE } from 'common/parser/EscapeSequenceParser'; +import { IParsingState, IParams, ParamsArray, IOscParser, IOscHandler, OscFallbackHandlerType, IFunctionIdentifier, IParserStackState, ParserStackType } from './Types'; +import { EscapeSequenceParser, TransitionTable, VT500_TRANSITION_TABLE } from './EscapeSequenceParser'; import { assert } from 'chai'; -import { StringToUtf32, stringFromCodePoint, utf32ToString } from 'common/input/TextDecoder'; -import { ParserState } from 'common/parser/Constants'; -import { Params } from 'common/parser/Params'; -import { OscHandler } from 'common/parser/OscParser'; -import { IDisposable } from 'common/Types'; -import { DcsHandler } from 'common/parser/DcsParser'; -import { ApcHandler } from 'common/parser/ApcParser'; +import { StringToUtf32, stringFromCodePoint, utf32ToString } from '../input/TextDecoder'; +import { ParserState } from './Constants'; +import { Params } from './Params'; +import { OscHandler } from './OscParser'; +import { IDisposable } from '../Types'; +import { DcsHandler } from './DcsParser'; +import { ApcHandler } from './ApcParser'; function r(a: number, b: number): string[] { diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index 2780d8916..a0c97b610 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -3,14 +3,14 @@ * @license MIT */ -import { IParsingState, IDcsHandler, IEscapeSequenceParser, IParams, IOscHandler, IHandlerCollection, CsiHandlerType, OscFallbackHandlerType, IOscParser, EscHandlerType, IDcsParser, DcsFallbackHandlerType, IFunctionIdentifier, ExecuteFallbackHandlerType, CsiFallbackHandlerType, EscFallbackHandlerType, PrintHandlerType, PrintFallbackHandlerType, ExecuteHandlerType, IParserStackState, ParserStackType, ResumableHandlersType, IApcHandler, IApcParser, ApcFallbackHandlerType } from 'common/parser/Types'; -import { ParserState, ParserAction } from 'common/parser/Constants'; -import { Disposable, toDisposable } from 'common/Lifecycle'; -import { IDisposable } from 'common/Types'; -import { Params } from 'common/parser/Params'; -import { OscParser } from 'common/parser/OscParser'; -import { DcsParser } from 'common/parser/DcsParser'; -import { ApcParser } from 'common/parser/ApcParser'; +import { IParsingState, IDcsHandler, IEscapeSequenceParser, IParams, IOscHandler, IHandlerCollection, CsiHandlerType, OscFallbackHandlerType, IOscParser, EscHandlerType, IDcsParser, DcsFallbackHandlerType, IFunctionIdentifier, ExecuteFallbackHandlerType, CsiFallbackHandlerType, EscFallbackHandlerType, PrintHandlerType, PrintFallbackHandlerType, ExecuteHandlerType, IParserStackState, ParserStackType, ResumableHandlersType, IApcHandler, IApcParser, ApcFallbackHandlerType } from './Types'; +import { ParserState, ParserAction } from './Constants'; +import { Disposable, toDisposable } from '../Lifecycle'; +import { IDisposable } from '../Types'; +import { Params } from './Params'; +import { OscParser } from './OscParser'; +import { DcsParser } from './DcsParser'; +import { ApcParser } from './ApcParser'; /** * VT commands done by the parser diff --git a/src/common/parser/OscParser.test.ts b/src/common/parser/OscParser.test.ts index b6b96cbad..b97a7617a 100644 --- a/src/common/parser/OscParser.test.ts +++ b/src/common/parser/OscParser.test.ts @@ -3,9 +3,9 @@ * @license MIT */ import { assert } from 'chai'; -import { OscParser, OscHandler } from 'common/parser/OscParser'; -import { StringToUtf32, utf32ToString } from 'common/input/TextDecoder'; -import { IOscHandler } from 'common/parser/Types'; +import { OscParser, OscHandler } from './OscParser'; +import { StringToUtf32, utf32ToString } from '../input/TextDecoder'; +import { IOscHandler } from './Types'; function toUtf32(s: string): Uint32Array { const utf32 = new Uint32Array(s.length); diff --git a/src/common/parser/OscParser.ts b/src/common/parser/OscParser.ts index 3e05f2298..0d2a06534 100644 --- a/src/common/parser/OscParser.ts +++ b/src/common/parser/OscParser.ts @@ -3,11 +3,11 @@ * @license MIT */ -import { IOscHandler, IHandlerCollection, OscFallbackHandlerType, IOscParser, ISubParserStackState } from 'common/parser/Types'; -import { OscState, ParserConstants } from 'common/parser/Constants'; -import { utf32ToString } from 'common/input/TextDecoder'; -import { IDisposable } from 'common/Types'; -import { LimitedStringBuilder } from 'common/StringBuilder'; +import { IOscHandler, IHandlerCollection, OscFallbackHandlerType, IOscParser, ISubParserStackState } from './Types'; +import { OscState, ParserConstants } from './Constants'; +import { utf32ToString } from '../input/TextDecoder'; +import { IDisposable } from '../Types'; +import { LimitedStringBuilder } from '../StringBuilder'; const EMPTY_HANDLERS: IOscHandler[] = []; diff --git a/src/common/parser/Params.test.ts b/src/common/parser/Params.test.ts index 2418fa452..3bd67d397 100644 --- a/src/common/parser/Params.test.ts +++ b/src/common/parser/Params.test.ts @@ -3,8 +3,8 @@ * @license MIT */ import { assert } from 'chai'; -import { Params } from 'common/parser/Params'; -import { ParamsArray } from 'common/parser/Types'; +import { Params } from './Params'; +import { ParamsArray } from './Types'; class TestParams extends Params { public get subParams(): Int32Array { diff --git a/src/common/parser/Params.ts b/src/common/parser/Params.ts index 8a77c78a4..6986a48ba 100644 --- a/src/common/parser/Params.ts +++ b/src/common/parser/Params.ts @@ -2,7 +2,7 @@ * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ -import { IParams, ParamsArray } from 'common/parser/Types'; +import { IParams, ParamsArray } from './Types'; const enum Constants { /** diff --git a/src/common/parser/Types.ts b/src/common/parser/Types.ts index c04788f7e..2e148c4d1 100644 --- a/src/common/parser/Types.ts +++ b/src/common/parser/Types.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { IDisposable } from 'common/Types'; -import { ParserState } from 'common/parser/Constants'; +import { IDisposable } from '../Types'; +import { ParserState } from './Constants'; /** sequence params serialized to js arrays */ diff --git a/src/common/public/BufferApiView.ts b/src/common/public/BufferApiView.ts index 834cedbee..a2f838ccb 100644 --- a/src/common/public/BufferApiView.ts +++ b/src/common/public/BufferApiView.ts @@ -4,9 +4,9 @@ */ import { IBuffer as IBufferApi, IBufferLine as IBufferLineApi, IBufferCell as IBufferCellApi } from '@xterm/xterm'; -import { IBuffer } from 'common/buffer/Types'; -import { BufferLineApiView } from 'common/public/BufferLineApiView'; -import { CellData } from 'common/buffer/CellData'; +import { IBuffer } from '../buffer/Types'; +import { BufferLineApiView } from './BufferLineApiView'; +import { CellData } from '../buffer/CellData'; export class BufferApiView implements IBufferApi { constructor( diff --git a/src/common/public/BufferLineApiView.ts b/src/common/public/BufferLineApiView.ts index 0a747fa41..ecffd1fbb 100644 --- a/src/common/public/BufferLineApiView.ts +++ b/src/common/public/BufferLineApiView.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { CellData } from 'common/buffer/CellData'; -import { IBufferLine, ICellData } from 'common/Types'; +import { CellData } from '../buffer/CellData'; +import { IBufferLine, ICellData } from '../Types'; import { IBufferCell as IBufferCellApi, IBufferLine as IBufferLineApi } from '@xterm/xterm'; export class BufferLineApiView implements IBufferLineApi { diff --git a/src/common/public/BufferNamespaceApi.ts b/src/common/public/BufferNamespaceApi.ts index b9744f943..9d56587f8 100644 --- a/src/common/public/BufferNamespaceApi.ts +++ b/src/common/public/BufferNamespaceApi.ts @@ -4,10 +4,10 @@ */ import { IBuffer as IBufferApi, IBufferNamespace as IBufferNamespaceApi } from '@xterm/xterm'; -import { BufferApiView } from 'common/public/BufferApiView'; -import { ICoreTerminal } from 'common/Types'; -import { Disposable } from 'common/Lifecycle'; -import { Emitter } from 'common/Event'; +import { BufferApiView } from './BufferApiView'; +import { ICoreTerminal } from '../Types'; +import { Disposable } from '../Lifecycle'; +import { Emitter } from '../Event'; export class BufferNamespaceApi extends Disposable implements IBufferNamespaceApi { private _normal: BufferApiView; diff --git a/src/common/public/ParserApi.ts b/src/common/public/ParserApi.ts index 4c482d7ca..02dcc44c9 100644 --- a/src/common/public/ParserApi.ts +++ b/src/common/public/ParserApi.ts @@ -3,9 +3,9 @@ * @license MIT */ -import { IParams } from 'common/parser/Types'; +import { IParams } from '../parser/Types'; import { IDisposable, IFunctionIdentifier, IParser } from '@xterm/xterm'; -import { ICoreTerminal } from 'common/Types'; +import { ICoreTerminal } from '../Types'; export class ParserApi implements IParser { constructor(private _core: ICoreTerminal) { } diff --git a/src/common/public/UnicodeApi.ts b/src/common/public/UnicodeApi.ts index ec9bd1f77..fff2665e1 100644 --- a/src/common/public/UnicodeApi.ts +++ b/src/common/public/UnicodeApi.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { ICoreTerminal } from 'common/Types'; +import { ICoreTerminal } from '../Types'; import { IUnicodeHandling, IUnicodeVersionProvider } from '@xterm/xterm'; export class UnicodeApi implements IUnicodeHandling { diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index 06ac6b10a..5135e7786 100644 --- a/src/common/services/BufferService.ts +++ b/src/common/services/BufferService.ts @@ -3,12 +3,12 @@ * @license MIT */ -import { Disposable } from 'common/Lifecycle'; -import { IAttributeData, IBufferLine } from 'common/Types'; -import { BufferSet } from 'common/buffer/BufferSet'; -import { IBuffer, IBufferSet } from 'common/buffer/Types'; -import { IBufferService, ILogService, IOptionsService, type IBufferResizeEvent } from 'common/services/Services'; -import { Emitter } from 'common/Event'; +import { Disposable } from '../Lifecycle'; +import { IAttributeData, IBufferLine } from '../Types'; +import { BufferSet } from '../buffer/BufferSet'; +import { IBuffer, IBufferSet } from '../buffer/Types'; +import { IBufferService, ILogService, IOptionsService, type IBufferResizeEvent } from './Services'; +import { Emitter } from '../Event'; export const enum BufferServiceConstants { MINIMUM_COLS = 2, // Less than 2 can mess with wide chars diff --git a/src/common/services/CharsetService.ts b/src/common/services/CharsetService.ts index bcd027a64..ffc68514e 100644 --- a/src/common/services/CharsetService.ts +++ b/src/common/services/CharsetService.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { ICharsetService } from 'common/services/Services'; -import { ICharset } from 'common/Types'; +import { ICharsetService } from './Services'; +import { ICharset } from '../Types'; export class CharsetService implements ICharsetService { public serviceBrand: any; diff --git a/src/common/services/CoreService.test.ts b/src/common/services/CoreService.test.ts index 7ae8bf185..c71d627d9 100644 --- a/src/common/services/CoreService.test.ts +++ b/src/common/services/CoreService.test.ts @@ -3,9 +3,9 @@ * @license MIT */ -import { ICoreService } from 'common/services/Services'; -import { CoreService } from 'common/services/CoreService'; -import { MockBufferService, MockLogService, MockOptionsService } from 'common/TestUtils.test'; +import { ICoreService } from './Services'; +import { CoreService } from './CoreService'; +import { MockBufferService, MockLogService, MockOptionsService } from '../TestUtils.test'; import { assert } from 'chai'; describe('CoreService', () => { diff --git a/src/common/services/CoreService.ts b/src/common/services/CoreService.ts index f5094f5a9..b3e04ebe6 100644 --- a/src/common/services/CoreService.ts +++ b/src/common/services/CoreService.ts @@ -3,10 +3,10 @@ * @license MIT */ -import { Disposable } from 'common/Lifecycle'; -import { IDecPrivateModes, IKittyKeyboardState, IModes } from 'common/Types'; -import { IBufferService, ICoreService, ILogService, IOptionsService } from 'common/services/Services'; -import { Emitter } from 'common/Event'; +import { Disposable } from '../Lifecycle'; +import { IDecPrivateModes, IKittyKeyboardState, IModes } from '../Types'; +import { IBufferService, ICoreService, ILogService, IOptionsService } from './Services'; +import { Emitter } from '../Event'; const DEFAULT_MODES: IModes = Object.freeze({ insertMode: false diff --git a/src/common/services/DecorationService.test.ts b/src/common/services/DecorationService.test.ts index e3a6c2f04..71299d330 100644 --- a/src/common/services/DecorationService.test.ts +++ b/src/common/services/DecorationService.test.ts @@ -5,12 +5,12 @@ import { assert } from 'chai'; import { DecorationLineCache, DecorationService } from './DecorationService'; -import { IMarker } from 'common/Types'; -import { Disposable } from 'common/Lifecycle'; -import { Emitter } from 'common/Event'; -import { MockLogService, MockBufferService, MockOptionsService } from 'common/TestUtils.test'; -import { Buffer } from 'common/buffer/Buffer'; -import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; +import { IMarker } from '../Types'; +import { Disposable } from '../Lifecycle'; +import { Emitter } from '../Event'; +import { MockLogService, MockBufferService, MockOptionsService } from '../TestUtils.test'; +import { Buffer } from '../buffer/Buffer'; +import { DEFAULT_ATTR_DATA } from '../buffer/BufferLine'; function createFakeMarker(line: number): IMarker { return Object.freeze(new class extends Disposable { diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts index 6db9bcf78..5c2a1d33a 100644 --- a/src/common/services/DecorationService.ts +++ b/src/common/services/DecorationService.ts @@ -3,15 +3,15 @@ * @license MIT */ -import type { IDeleteEvent, IInsertEvent } from 'common/CircularList'; -import { MicrotaskTimer } from 'common/Async'; -import { css } from 'common/Color'; -import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'common/Lifecycle'; -import { IBufferService, IDecorationService, IInternalDecoration, ILogService } from 'common/services/Services'; -import { SortedList } from 'common/SortedList'; -import { IColor, ICircularList } from 'common/Types'; +import type { IDeleteEvent, IInsertEvent } from '../CircularList'; +import { MicrotaskTimer } from '../Async'; +import { css } from '../Color'; +import { Disposable, DisposableStore, MutableDisposable, toDisposable } from '../Lifecycle'; +import { IBufferService, IDecorationService, IInternalDecoration, ILogService } from './Services'; +import { SortedList } from '../SortedList'; +import { IColor, ICircularList } from '../Types'; import { IDecoration, IDecorationOptions, IMarker } from '@xterm/xterm'; -import { Emitter } from 'common/Event'; +import { Emitter } from '../Event'; // Work variables to avoid garbage collection let $xmin = 0; diff --git a/src/common/services/InstantiationService.ts b/src/common/services/InstantiationService.ts index 7e7695485..f4dbf53c1 100644 --- a/src/common/services/InstantiationService.ts +++ b/src/common/services/InstantiationService.ts @@ -9,8 +9,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IInstantiationService, IServiceIdentifier } from 'common/services/Services'; -import { getServiceDependencies } from 'common/services/ServiceRegistry'; +import { IInstantiationService, IServiceIdentifier } from './Services'; +import { getServiceDependencies } from './ServiceRegistry'; export class ServiceCollection { diff --git a/src/common/services/LogService.ts b/src/common/services/LogService.ts index 8bc1e0aaa..75ff68e29 100644 --- a/src/common/services/LogService.ts +++ b/src/common/services/LogService.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { Disposable } from 'common/Lifecycle'; -import { ILogService, IOptionsService, LogLevelEnum } from 'common/services/Services'; +import { Disposable } from '../Lifecycle'; +import { ILogService, IOptionsService, LogLevelEnum } from './Services'; type LogType = (message?: any, ...optionalParams: any[]) => void; diff --git a/src/common/services/MouseStateService.test.ts b/src/common/services/MouseStateService.test.ts index 0f2cd489a..7daf7d1e8 100644 --- a/src/common/services/MouseStateService.test.ts +++ b/src/common/services/MouseStateService.test.ts @@ -2,9 +2,9 @@ * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ -import { MouseStateService } from 'common/services/MouseStateService'; +import { MouseStateService } from './MouseStateService'; import { assert } from 'chai'; -import { ICoreMouseEvent, CoreMouseEventType } from 'common/Types'; +import { ICoreMouseEvent, CoreMouseEventType } from '../Types'; function toBytes(s: string | undefined): number[] { if (!s) { diff --git a/src/common/services/MouseStateService.ts b/src/common/services/MouseStateService.ts index 21420460a..40a448f57 100644 --- a/src/common/services/MouseStateService.ts +++ b/src/common/services/MouseStateService.ts @@ -2,10 +2,10 @@ * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ -import { IMouseStateService } from 'common/services/Services'; -import { ICoreMouseProtocol, ICoreMouseEvent, CoreMouseEncoding, CoreMouseEventType, CoreMouseButton, CoreMouseAction } from 'common/Types'; -import { Disposable } from 'common/Lifecycle'; -import { Emitter } from 'common/Event'; +import { IMouseStateService } from './Services'; +import { ICoreMouseProtocol, ICoreMouseEvent, CoreMouseEncoding, CoreMouseEventType, CoreMouseButton, CoreMouseAction } from '../Types'; +import { Disposable } from '../Lifecycle'; +import { Emitter } from '../Event'; /** * Supported default protocols. diff --git a/src/common/services/OptionsService.test.ts b/src/common/services/OptionsService.test.ts index 004f73168..95e2058e9 100644 --- a/src/common/services/OptionsService.test.ts +++ b/src/common/services/OptionsService.test.ts @@ -4,8 +4,8 @@ */ import { assert } from 'chai'; -import { OptionsService, DEFAULT_OPTIONS } from 'common/services/OptionsService'; -import { IDisposable } from 'common/Types'; +import { OptionsService, DEFAULT_OPTIONS } from './OptionsService'; +import { IDisposable } from '../Types'; describe('OptionsService', () => { describe('constructor', () => { diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 8527d2ea0..d14982e3f 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -3,11 +3,11 @@ * @license MIT */ -import { Disposable, toDisposable } from 'common/Lifecycle'; -import { isMac } from 'common/Platform'; -import { CursorStyle, IDisposable } from 'common/Types'; -import { FontWeight, IOptionsService, ITerminalOptions } from 'common/services/Services'; -import { Emitter } from 'common/Event'; +import { Disposable, toDisposable } from '../Lifecycle'; +import { isMac } from '../Platform'; +import { CursorStyle, IDisposable } from '../Types'; +import { FontWeight, IOptionsService, ITerminalOptions } from './Services'; +import { Emitter } from '../Event'; export const DEFAULT_OPTIONS: Readonly> = { cols: 80, diff --git a/src/common/services/OscLinkService.test.ts b/src/common/services/OscLinkService.test.ts index cc6c2228a..c49928b51 100644 --- a/src/common/services/OscLinkService.test.ts +++ b/src/common/services/OscLinkService.test.ts @@ -4,12 +4,12 @@ */ import { assert } from 'chai'; -import { AttributeData } from 'common/buffer/AttributeData'; -import { BufferService } from 'common/services/BufferService'; -import { OptionsService } from 'common/services/OptionsService'; -import { OscLinkService } from 'common/services/OscLinkService'; -import { IBufferService, IOptionsService, IOscLinkService } from 'common/services/Services'; -import { MockLogService } from 'common/TestUtils.test'; +import { AttributeData } from '../buffer/AttributeData'; +import { BufferService } from './BufferService'; +import { OptionsService } from './OptionsService'; +import { OscLinkService } from './OscLinkService'; +import { IBufferService, IOptionsService, IOscLinkService } from './Services'; +import { MockLogService } from '../TestUtils.test'; describe('OscLinkService', () => { describe('constructor', () => { diff --git a/src/common/services/OscLinkService.ts b/src/common/services/OscLinkService.ts index 13bd8aa4b..e28f4cfc8 100644 --- a/src/common/services/OscLinkService.ts +++ b/src/common/services/OscLinkService.ts @@ -2,8 +2,8 @@ * Copyright (c) 2022 The xterm.js authors. All rights reserved. * @license MIT */ -import { IBufferService, IOscLinkService } from 'common/services/Services'; -import { IMarker, IOscLinkData } from 'common/Types'; +import { IBufferService, IOscLinkService } from './Services'; +import { IMarker, IOscLinkData } from '../Types'; export class OscLinkService implements IOscLinkService { public serviceBrand: any; diff --git a/src/common/services/ServiceRegistry.ts b/src/common/services/ServiceRegistry.ts index af51d1641..f4565b166 100644 --- a/src/common/services/ServiceRegistry.ts +++ b/src/common/services/ServiceRegistry.ts @@ -9,7 +9,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IServiceIdentifier } from 'common/services/Services'; +import { IServiceIdentifier } from './Services'; const enum Constants { DI_TARGET = 'di$target', diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index fa1c4412f..0df3786f6 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -4,10 +4,10 @@ */ import type { IDecoration, IDecorationOptions, ILinkHandler, ILogger, IWindowsPty, IOverviewRulerOptions } from '@xterm/xterm'; -import { CoreMouseEncoding, CoreMouseEventType, CursorInactiveStyle, CursorStyle, IAttributeData, ICharset, IColor, ICoreMouseEvent, ICoreMouseProtocol, IDecPrivateModes, IDisposable, IKittyKeyboardState, IModes, IOscLinkData, IWindowOptions } from 'common/Types'; -import { IBuffer, IBufferSet } from 'common/buffer/Types'; -import { createDecorator } from 'common/services/ServiceRegistry'; -import type { Emitter, IEvent } from 'common/Event'; +import { CoreMouseEncoding, CoreMouseEventType, CursorInactiveStyle, CursorStyle, IAttributeData, ICharset, IColor, ICoreMouseEvent, ICoreMouseProtocol, IDecPrivateModes, IDisposable, IKittyKeyboardState, IModes, IOscLinkData, IWindowOptions } from '../Types'; +import { IBuffer, IBufferSet } from '../buffer/Types'; +import { createDecorator } from './ServiceRegistry'; +import type { Emitter, IEvent } from '../Event'; export const IBufferService = createDecorator('BufferService'); export interface IBufferService { diff --git a/src/common/services/UnicodeService.test.ts b/src/common/services/UnicodeService.test.ts index 01e3c0862..d43605542 100644 --- a/src/common/services/UnicodeService.test.ts +++ b/src/common/services/UnicodeService.test.ts @@ -4,8 +4,8 @@ */ import { assert } from 'chai'; -import { UnicodeService } from 'common/services/UnicodeService'; -import { IUnicodeVersionProvider } from 'common/services/Services'; +import { UnicodeService } from './UnicodeService'; +import { IUnicodeVersionProvider } from './Services'; class DummyProvider implements IUnicodeVersionProvider { public version = '123'; diff --git a/src/common/services/UnicodeService.ts b/src/common/services/UnicodeService.ts index d0dd8584d..1d4d6cc8f 100644 --- a/src/common/services/UnicodeService.ts +++ b/src/common/services/UnicodeService.ts @@ -3,9 +3,9 @@ * @license MIT */ -import { UnicodeV6 } from 'common/input/UnicodeV6'; -import { IUnicodeService, IUnicodeVersionProvider, UnicodeCharProperties, UnicodeCharWidth } from 'common/services/Services'; -import { Emitter } from 'common/Event'; +import { UnicodeV6 } from '../input/UnicodeV6'; +import { IUnicodeService, IUnicodeVersionProvider, UnicodeCharProperties, UnicodeCharWidth } from './Services'; +import { Emitter } from '../Event'; export class UnicodeService implements IUnicodeService { public serviceBrand: any; diff --git a/src/common/tsconfig.json b/src/common/tsconfig.json index 31a893756..c7488a89e 100644 --- a/src/common/tsconfig.json +++ b/src/common/tsconfig.json @@ -10,11 +10,7 @@ "outDir": "../../out", "types": [ "../../node_modules/@types/mocha" - ], - "paths": { - "common/*": [ "./../common/*" ], - "*": [ "./../*" ] - } + ] }, "include": [ "./**/*", diff --git a/src/headless/Terminal.ts b/src/headless/Terminal.ts index a348ae824..4d4266d19 100644 --- a/src/headless/Terminal.ts +++ b/src/headless/Terminal.ts @@ -21,11 +21,11 @@ * http://linux.die.net/man/7/urxvt */ -import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; -import { IBuffer } from 'common/buffer/Types'; -import { CoreTerminal } from 'common/CoreTerminal'; -import { IMarker, ITerminalOptions } from 'common/Types'; -import { Emitter, EventUtils } from 'common/Event'; +import { DEFAULT_ATTR_DATA } from '../common/buffer/BufferLine'; +import { IBuffer } from '../common/buffer/Types'; +import { CoreTerminal } from '../common/CoreTerminal'; +import { IMarker, ITerminalOptions } from '../common/Types'; +import { Emitter, EventUtils } from '../common/Event'; export class Terminal extends CoreTerminal { private readonly _onBell = this._register(new Emitter()); diff --git a/src/headless/public/Terminal.test.ts b/src/headless/public/Terminal.test.ts index 73e2f4865..6fa9d32e1 100644 --- a/src/headless/public/Terminal.test.ts +++ b/src/headless/public/Terminal.test.ts @@ -4,7 +4,7 @@ */ import { deepStrictEqual, strictEqual, throws } from 'assert'; -import { Terminal } from 'headless/public/Terminal'; +import { Terminal } from './Terminal'; import { ITerminalOptions } from '@xterm/headless'; let term: Terminal; diff --git a/src/headless/public/Terminal.ts b/src/headless/public/Terminal.ts index d593dea15..22dabb02a 100644 --- a/src/headless/public/Terminal.ts +++ b/src/headless/public/Terminal.ts @@ -3,15 +3,15 @@ * @license MIT */ -import { BufferNamespaceApi } from 'common/public/BufferNamespaceApi'; -import { ParserApi } from 'common/public/ParserApi'; -import { UnicodeApi } from 'common/public/UnicodeApi'; +import { BufferNamespaceApi } from '../../common/public/BufferNamespaceApi'; +import { ParserApi } from '../../common/public/ParserApi'; +import { UnicodeApi } from '../../common/public/UnicodeApi'; import { IBufferNamespace as IBufferNamespaceApi, IMarker, IModes, IParser, ITerminalAddon, ITerminalInitOnlyOptions, IUnicodeHandling, Terminal as ITerminalApi } from '@xterm/headless'; -import { Terminal as TerminalCore } from 'headless/Terminal'; -import { AddonManager } from 'common/public/AddonManager'; -import { ITerminalOptions } from 'common/Types'; -import { Disposable } from 'common/Lifecycle'; -import type { IEvent } from 'common/Event'; +import { Terminal as TerminalCore } from '../Terminal'; +import { AddonManager } from '../../common/public/AddonManager'; +import { ITerminalOptions } from '../../common/Types'; +import { Disposable } from '../../common/Lifecycle'; +import type { IEvent } from '../../common/Event'; /** * The set of options that only have an effect when set in the Terminal constructor. */ diff --git a/src/headless/tsconfig.json b/src/headless/tsconfig.json index 0c445bcfc..96b0cc39b 100644 --- a/src/headless/tsconfig.json +++ b/src/headless/tsconfig.json @@ -10,11 +10,7 @@ "types": [ "../../node_modules/@types/mocha", "../../node_modules/@types/node" - ], - "paths": { - "common/*": [ "./../common/*" ], - "*": [ "./../*" ] - } + ] }, "include": [ "./**/*", diff --git a/test/benchmark/DecorationService.benchmark.ts b/test/benchmark/DecorationService.benchmark.ts index 1a2033de1..534a1fa30 100644 --- a/test/benchmark/DecorationService.benchmark.ts +++ b/test/benchmark/DecorationService.benchmark.ts @@ -4,8 +4,8 @@ */ import { perfContext, before, RuntimeCase } from 'xterm-benchmark'; -import { DecorationService } from 'common/services/DecorationService'; -import { MockLogService, MockBufferService, MockOptionsService } from 'common/TestUtils.test'; +import { DecorationService } from '../../src/common/services/DecorationService'; +import { MockLogService, MockBufferService, MockOptionsService } from '../../src/common/TestUtils.test'; const enum Constants { COLS = 80, ROWS = 30, diff --git a/test/benchmark/EscapeSequenceParser.benchmark.ts b/test/benchmark/EscapeSequenceParser.benchmark.ts index 5c9f372fb..c2cecfdbb 100644 --- a/test/benchmark/EscapeSequenceParser.benchmark.ts +++ b/test/benchmark/EscapeSequenceParser.benchmark.ts @@ -4,12 +4,12 @@ */ import { perfContext, before, beforeEach, ThroughputRuntimeCase } from 'xterm-benchmark'; -import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser'; -import { C0, C1 } from 'common/data/EscapeSequences'; -import { IDcsHandler, IOscHandler, IApcHandler, IParams } from 'common/parser/Types'; -import { OscHandler } from 'common/parser/OscParser'; -import { DcsHandler } from 'common/parser/DcsParser'; -import { ApcHandler } from 'common/parser/ApcParser'; +import { EscapeSequenceParser } from '../../src/common/parser/EscapeSequenceParser'; +import { C0, C1 } from '../../src/common/data/EscapeSequences'; +import { IDcsHandler, IOscHandler, IApcHandler, IParams } from '../../src/common/parser/Types'; +import { OscHandler } from '../../src/common/parser/OscParser'; +import { DcsHandler } from '../../src/common/parser/DcsParser'; +import { ApcHandler } from '../../src/common/parser/ApcParser'; const SIZE = 5000000; diff --git a/test/benchmark/Event.benchmark.ts b/test/benchmark/Event.benchmark.ts index 9cd8216dc..ba3f8de9c 100644 --- a/test/benchmark/Event.benchmark.ts +++ b/test/benchmark/Event.benchmark.ts @@ -4,7 +4,7 @@ */ import { perfContext, before, RuntimeCase } from 'xterm-benchmark'; -import { Emitter } from 'common/Event'; +import { Emitter } from '../../src/common/Event'; const ITERATIONS = 1_000_000; diff --git a/test/benchmark/Terminal.benchmark.ts b/test/benchmark/Terminal.benchmark.ts index fb06b429e..97579cb30 100644 --- a/test/benchmark/Terminal.benchmark.ts +++ b/test/benchmark/Terminal.benchmark.ts @@ -6,8 +6,8 @@ import { perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark'; import { spawn } from 'node-pty'; -import { Utf8ToUtf32, stringFromCodePoint } from 'common/input/TextDecoder'; -import { CoreBrowserTerminal } from 'browser/CoreBrowserTerminal'; +import { Utf8ToUtf32, stringFromCodePoint } from '../../src/common/input/TextDecoder'; +import { CoreBrowserTerminal } from '../../src/browser/CoreBrowserTerminal'; perfContext('Terminal: ls -lR /usr/lib', () => { let content = ''; diff --git a/test/benchmark/tsconfig.json b/test/benchmark/tsconfig.json index 2ddb57e32..fe2af282c 100644 --- a/test/benchmark/tsconfig.json +++ b/test/benchmark/tsconfig.json @@ -11,12 +11,7 @@ "moduleResolution": "nodenext", "strict": false, "target": "es2021", - "module": "nodenext", - "paths": { - "common/*": [ "../../src/common/*" ], - "browser/*": [ "../../src/browser/*" ], - "Terminal": ["../../src/Terminal"] - }, + "module": "nodenext" }, "include": [ "./**/*", diff --git a/test/playwright/Terminal.test.ts b/test/playwright/Terminal.test.ts index 2d1133cce..c6c93359e 100644 --- a/test/playwright/Terminal.test.ts +++ b/test/playwright/Terminal.test.ts @@ -5,7 +5,7 @@ import { test } from '@playwright/test'; import { deepStrictEqual, notStrictEqual, strictEqual } from 'assert'; import { ITestContext, createTestContext, openTerminal, pollFor, timeout } from './TestUtils'; -import { IRenderDimensions } from 'browser/renderer/shared/Types'; +import { IRenderDimensions } from '../../src/browser/renderer/shared/Types'; let ctx: ITestContext; test.beforeAll(async ({ browser }) => { diff --git a/test/playwright/TestUtils.ts b/test/playwright/TestUtils.ts index 8a76c8820..a27d2d20d 100644 --- a/test/playwright/TestUtils.ts +++ b/test/playwright/TestUtils.ts @@ -5,9 +5,9 @@ import { Browser, JSHandle, Page } from '@playwright/test'; import { deepStrictEqual, strictEqual } from 'assert'; -import type { IRenderDimensions as IRenderDimensionsInternal } from 'browser/renderer/shared/Types'; -import type { IRenderService } from 'browser/services/Services'; -import type { ICoreTerminal, IDisposable, IMarker } from 'common/Types'; +import type { IRenderDimensions as IRenderDimensionsInternal } from '../../src/browser/renderer/shared/Types'; +import type { IRenderService } from '../../src/browser/services/Services'; +import type { ICoreTerminal, IDisposable, IMarker } from '../../src/common/Types'; import * as playwright from '@playwright/test'; import { IBuffer, IBufferCell, IBufferLine, IBufferNamespace, IBufferRange, IDecoration, IDecorationOptions, IModes, IRenderDimensions, ITerminalInitOnlyOptions, ITerminalOptions, Terminal } from '@xterm/xterm'; diff --git a/test/playwright/tsconfig.json b/test/playwright/tsconfig.json index 7fea8d812..0339896bc 100644 --- a/test/playwright/tsconfig.json +++ b/test/playwright/tsconfig.json @@ -17,14 +17,6 @@ "noImplicitAny": false, "declaration": true, "moduleResolution": "nodenext", - "paths": { - "browser/*": [ - "../../src/browser/*" - ], - "common/*": [ - "../../src/common/*" - ] - }, "target": "ESNext", "module": "nodenext", "composite": true diff --git a/webpack.config.headless.js b/webpack.config.headless.js index ed2856bd0..6f9f26d22 100644 --- a/webpack.config.headless.js +++ b/webpack.config.headless.js @@ -8,8 +8,7 @@ const path = require('path'); /** * This webpack config does a production build for xterm.js headless. It works by taking the output * from tsc (via `npm run watch` or `npm run prebuild`) which are put into `out/` and webpacks them - * into a production mode umd library module in `lib-headless/`. The aliases are used fix up the - * absolute paths output by tsc (because of `baseUrl` and `paths` in `tsconfig.json`. + * into a production mode umd library module in `lib-headless/`. * * @type {import('webpack').Configuration} */ @@ -29,11 +28,6 @@ const config = { resolve: { modules: ['./node_modules'], extensions: [ '.js' ], - alias: { - common: path.resolve('./out/common'), - headless: path.resolve('./out/headless'), - vs: path.resolve('./out/vs') - } }, output: { filename: 'xterm-headless.js', diff --git a/webpack.config.js b/webpack.config.js index 74acb30f8..22a41e3ca 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -10,8 +10,7 @@ const path = require('path'); /** * This webpack config does a production build for xterm.js. It works by taking the output from tsc * (via `npm run watch` or `npm run prebuild`) which are put into `out/` and webpacks them into a - * production mode umd library module in `lib/`. The aliases are used fix up the absolute paths - * output by tsc (because of `baseUrl` and `paths` in `tsconfig.json`. + * production mode umd library module in `lib/`. * * @type {import('webpack').Configuration} */ @@ -31,11 +30,6 @@ const config = { resolve: { modules: ['./node_modules'], extensions: [ '.js' ], - alias: { - common: path.resolve('./out/common'), - browser: path.resolve('./out/browser'), - vs: path.resolve('./out/vs'), - } }, output: { filename: 'xterm.js', From 5d8da19c447bacf0119017354e0bffbc21f1c220 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 09:25:12 -0700 Subject: [PATCH 078/153] Keep path mappings for addons after core migration Follow-up to https://github.com/xtermjs/xterm.js/issues/5896 / PR #5966. Converting addons to ../../../src/common relative imports broke unit tests: esbuild emits addon code under addons/*/out-esbuild/ without mirroring the shared tree, so Node could not load TypeScript sources at runtime (Cannot find module '../../../src/common/Lifecycle'). Core src/, test/, and headless continue to use relative imports with no common/* path mappings. Addons keep the prior model until build layout can share out-esbuild the same way as the main package. Changes: - Restore common/* and browser/* paths in addon src/test/benchmark tsconfigs - Revert addon imports to common/ and browser/ path-mapped specifiers - Restore NODE_PATH=out-esbuild in bin/test_unit.js for Mocha resolution - Restore webpack resolve.alias on addons that bundle against out/ Fixes CI test-unit failures on remove-path-mappings-5896. Co-authored-by: Cursor --- addons/addon-attach/test/tsconfig.json | 6 ++++ addons/addon-clipboard/src/tsconfig.json | 3 ++ addons/addon-clipboard/test/tsconfig.json | 6 ++++ addons/addon-fit/src/FitAddon.ts | 2 +- addons/addon-fit/src/tsconfig.json | 3 ++ addons/addon-fit/test/tsconfig.json | 6 ++++ addons/addon-image/src/ImageAddon.ts | 2 +- addons/addon-image/src/ImageRenderer.ts | 2 +- addons/addon-image/src/Types.ts | 14 ++++---- addons/addon-image/src/tsconfig.json | 15 ++++----- addons/addon-image/test/tsconfig.json | 6 ++++ addons/addon-image/webpack.config.js | 5 +++ addons/addon-ligatures/test/tsconfig.json | 5 +++ addons/addon-progress/src/ProgressAddon.ts | 2 +- addons/addon-progress/src/tsconfig.json | 6 ++++ addons/addon-progress/test/tsconfig.json | 6 ++++ addons/addon-progress/webpack.config.js | 5 +++ addons/addon-search/src/DecorationManager.ts | 2 +- addons/addon-search/src/SearchAddon.ts | 6 ++-- addons/addon-search/src/SearchEngine.test.ts | 4 +-- .../addon-search/src/SearchLineCache.test.ts | 4 +-- addons/addon-search/src/SearchLineCache.ts | 4 +-- .../addon-search/src/SearchResultTracker.ts | 4 +-- addons/addon-search/src/tsconfig.json | 6 ++++ addons/addon-search/test/tsconfig.json | 6 ++++ addons/addon-search/webpack.config.js | 4 +++ .../benchmark/SerializeAddon.benchmark.ts | 4 +-- .../addon-serialize/benchmark/tsconfig.json | 28 +++++----------- .../src/SerializeAddon.test.ts | 8 ++--- addons/addon-serialize/src/SerializeAddon.ts | 6 ++-- addons/addon-serialize/src/tsconfig.json | 6 ++++ addons/addon-serialize/test/tsconfig.json | 8 +++++ addons/addon-serialize/webpack.config.js | 4 +++ .../UnicodeGraphemeAddon.benchmark.ts | 4 +-- .../benchmark/tsconfig.json | 33 +++++-------------- .../src/UnicodeGraphemeProvider.ts | 4 +-- .../addon-unicode-graphemes/src/tsconfig.json | 3 ++ .../test/tsconfig.json | 8 +++++ .../addon-unicode-graphemes/webpack.config.js | 4 +++ addons/addon-unicode11/src/UnicodeV11.ts | 4 +-- addons/addon-unicode11/src/tsconfig.json | 6 ++++ addons/addon-unicode11/test/tsconfig.json | 6 ++++ addons/addon-unicode11/webpack.config.js | 4 +++ addons/addon-web-fonts/test/tsconfig.json | 6 ++++ addons/addon-web-links/test/tsconfig.json | 6 ++++ addons/addon-webgl/src/CellColorResolver.ts | 16 ++++----- addons/addon-webgl/src/CharAtlasCache.ts | 4 +-- addons/addon-webgl/src/CharAtlasUtils.test.ts | 4 +-- addons/addon-webgl/src/CharAtlasUtils.ts | 6 ++-- addons/addon-webgl/src/Constants.ts | 2 +- .../src/CursorBlinkStateManager.ts | 4 +-- addons/addon-webgl/src/DevicePixelObserver.ts | 2 +- addons/addon-webgl/src/GlyphRenderer.ts | 10 +++--- addons/addon-webgl/src/RectangleRenderer.ts | 16 ++++----- addons/addon-webgl/src/RenderModel.ts | 4 +-- addons/addon-webgl/src/TextureAtlas.ts | 20 +++++------ addons/addon-webgl/src/Types.ts | 8 ++--- addons/addon-webgl/src/WebglAddon.ts | 12 +++---- addons/addon-webgl/src/WebglRenderer.ts | 26 +++++++-------- addons/addon-webgl/src/WebglUtils.ts | 4 +-- .../src/customGlyphs/CustomGlyphRasterizer.ts | 4 +-- .../src/renderLayer/BaseRenderLayer.ts | 14 ++++---- .../src/renderLayer/LinkRenderLayer.ts | 10 +++--- addons/addon-webgl/src/renderLayer/Types.ts | 2 +- addons/addon-webgl/src/tsconfig.json | 9 +++++ addons/addon-webgl/test/tsconfig.json | 6 ++++ addons/addon-webgl/webpack.config.js | 5 +++ bin/test_unit.js | 8 +++-- 68 files changed, 305 insertions(+), 177 deletions(-) diff --git a/addons/addon-attach/test/tsconfig.json b/addons/addon-attach/test/tsconfig.json index 73f17c2a5..900a727ed 100644 --- a/addons/addon-attach/test/tsconfig.json +++ b/addons/addon-attach/test/tsconfig.json @@ -10,6 +10,12 @@ "sourceMap": true, "removeComments": true, "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ], "*": [ "./*" ] diff --git a/addons/addon-clipboard/src/tsconfig.json b/addons/addon-clipboard/src/tsconfig.json index b1192735c..b61072808 100644 --- a/addons/addon-clipboard/src/tsconfig.json +++ b/addons/addon-clipboard/src/tsconfig.json @@ -15,6 +15,9 @@ "../../../node_modules/@types/mocha" ], "paths": { + "browser/*": [ + "../../../src/browser/*" + ], "@xterm/addon-clipboard": [ "../typings/addon-clipboard.d.ts" ] diff --git a/addons/addon-clipboard/test/tsconfig.json b/addons/addon-clipboard/test/tsconfig.json index 73f17c2a5..900a727ed 100644 --- a/addons/addon-clipboard/test/tsconfig.json +++ b/addons/addon-clipboard/test/tsconfig.json @@ -10,6 +10,12 @@ "sourceMap": true, "removeComments": true, "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ], "*": [ "./*" ] diff --git a/addons/addon-fit/src/FitAddon.ts b/addons/addon-fit/src/FitAddon.ts index 812d31d3f..3db988acd 100644 --- a/addons/addon-fit/src/FitAddon.ts +++ b/addons/addon-fit/src/FitAddon.ts @@ -5,7 +5,7 @@ import type { Terminal, ITerminalAddon, IRenderDimensions } from '@xterm/xterm'; import type { FitAddon as IFitApi } from '@xterm/addon-fit'; -import { ViewportConstants } from '../../../src/browser/shared/Constants'; +import { ViewportConstants } from 'browser/shared/Constants'; interface ITerminalDimensions { /** diff --git a/addons/addon-fit/src/tsconfig.json b/addons/addon-fit/src/tsconfig.json index 98bf0cf98..068b43370 100644 --- a/addons/addon-fit/src/tsconfig.json +++ b/addons/addon-fit/src/tsconfig.json @@ -15,6 +15,9 @@ "../../../node_modules/@types/mocha" ], "paths": { + "browser/*": [ + "../../../src/browser/*" + ], "@xterm/addon-fit": [ "../typings/addon-fit.d.ts" ] diff --git a/addons/addon-fit/test/tsconfig.json b/addons/addon-fit/test/tsconfig.json index 73f17c2a5..900a727ed 100644 --- a/addons/addon-fit/test/tsconfig.json +++ b/addons/addon-fit/test/tsconfig.json @@ -10,6 +10,12 @@ "sourceMap": true, "removeComments": true, "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ], "*": [ "./*" ] diff --git a/addons/addon-image/src/ImageAddon.ts b/addons/addon-image/src/ImageAddon.ts index 07aea05c5..8fd395431 100644 --- a/addons/addon-image/src/ImageAddon.ts +++ b/addons/addon-image/src/ImageAddon.ts @@ -5,7 +5,7 @@ import type { ITerminalAddon, IDisposable } from '@xterm/xterm'; import type { ImageAddon as IImageApi } from '@xterm/addon-image'; -import { Emitter, type IEvent } from '../../../src/common/Event'; +import { Emitter, type IEvent } from 'common/Event'; import { IIPHandler } from './IIPHandler'; import { ImageRenderer } from './ImageRenderer'; import { ImageStorage, CELL_SIZE_DEFAULT } from './ImageStorage'; diff --git a/addons/addon-image/src/ImageRenderer.ts b/addons/addon-image/src/ImageRenderer.ts index 285f13724..5854efaec 100644 --- a/addons/addon-image/src/ImageRenderer.ts +++ b/addons/addon-image/src/ImageRenderer.ts @@ -6,7 +6,7 @@ import { toRGBA8888 } from 'sixel/lib/Colors'; import { IDisposable } from '@xterm/xterm'; import { ICellSize, ImageLayer, ITerminalExt, IImageSpec, IRenderDimensions, IRenderService } from './Types'; -import { Disposable, MutableDisposable, toDisposable } from '../../../src/common/Lifecycle'; +import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; const enum Constants { PLACEHOLDER_LENGTH = 4096, diff --git a/addons/addon-image/src/Types.ts b/addons/addon-image/src/Types.ts index 9a70f4399..19a1acca1 100644 --- a/addons/addon-image/src/Types.ts +++ b/addons/addon-image/src/Types.ts @@ -6,13 +6,13 @@ import { IDisposable, IMarker, Terminal } from '@xterm/xterm'; // private imports from base repo we build against -import { Attributes, BgFlags, Content, ExtFlags, UnderlineStyle } from '../../../src/common/buffer/Constants'; -import type { AttributeData } from '../../../src/common/buffer/AttributeData'; -import type { IParams, IDcsHandler, IOscHandler, IApcHandler, IEscapeSequenceParser } from '../../../src/common/parser/Types'; -import type { IBufferLine, IExtendedAttrs, IInputHandler } from '../../../src/common/Types'; -import type { ITerminal, ReadonlyColorSet } from '../../../src/browser/Types'; -import type { IRenderDimensions } from '../../../src/browser/renderer/shared/Types'; -import type { ICoreBrowserService, IRenderService, IThemeService } from '../../../src/browser/services/Services'; +import { Attributes, BgFlags, Content, ExtFlags, UnderlineStyle } from 'common/buffer/Constants'; +import type { AttributeData } from 'common/buffer/AttributeData'; +import type { IParams, IDcsHandler, IOscHandler, IApcHandler, IEscapeSequenceParser } from 'common/parser/Types'; +import type { IBufferLine, IExtendedAttrs, IInputHandler } from 'common/Types'; +import type { ITerminal, ReadonlyColorSet } from 'browser/Types'; +import type { IRenderDimensions } from 'browser/renderer/shared/Types'; +import type { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; export const enum Cell { CONTENT = 0, // codepoint and wcwidth information (enum Content) diff --git a/addons/addon-image/src/tsconfig.json b/addons/addon-image/src/tsconfig.json index 18e276a35..9a5d24c1f 100644 --- a/addons/addon-image/src/tsconfig.json +++ b/addons/addon-image/src/tsconfig.json @@ -12,9 +12,10 @@ "../../../node_modules/@types/mocha" ], "paths": { - "@xterm/addon-image": [ - "../typings/addon-image.d.ts" - ] + "browser/*": [ "../../../src/browser/*" ], + "common/*": [ "../../../src/common/*" ], + "@xterm/addon-image": [ "../typings/addon-image.d.ts" ], + "*": [ "./*" ] } }, "include": [ @@ -22,11 +23,7 @@ "../../../typings/xterm.d.ts" ], "references": [ - { - "path": "../../../src/browser" - }, - { - "path": "../../../src/common" - } + { "path": "../../../src/browser" }, + { "path": "../../../src/common" } ] } diff --git a/addons/addon-image/test/tsconfig.json b/addons/addon-image/test/tsconfig.json index 73f17c2a5..900a727ed 100644 --- a/addons/addon-image/test/tsconfig.json +++ b/addons/addon-image/test/tsconfig.json @@ -10,6 +10,12 @@ "sourceMap": true, "removeComments": true, "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ], "*": [ "./*" ] diff --git a/addons/addon-image/webpack.config.js b/addons/addon-image/webpack.config.js index ba2fa35a4..01147d7e7 100644 --- a/addons/addon-image/webpack.config.js +++ b/addons/addon-image/webpack.config.js @@ -24,6 +24,11 @@ const addon = { resolve: { modules: ['./node_modules'], extensions: [ '.js' ], + alias: { + common: path.resolve('../../out/common'), + browser: path.resolve('../../out/browser'), + vs: path.resolve('../../out/vs') + } }, output: { filename: mainFile, diff --git a/addons/addon-ligatures/test/tsconfig.json b/addons/addon-ligatures/test/tsconfig.json index 25a92c8e4..6986e2b46 100644 --- a/addons/addon-ligatures/test/tsconfig.json +++ b/addons/addon-ligatures/test/tsconfig.json @@ -10,6 +10,11 @@ "sourceMap": true, "removeComments": true, "strict": true, + "paths": { + "*": [ + "./*" + ] + }, "types": [ "../../../node_modules/@types/mocha", "../../../node_modules/@types/node" diff --git a/addons/addon-progress/src/ProgressAddon.ts b/addons/addon-progress/src/ProgressAddon.ts index e474edca3..9d71179e1 100644 --- a/addons/addon-progress/src/ProgressAddon.ts +++ b/addons/addon-progress/src/ProgressAddon.ts @@ -5,7 +5,7 @@ import type { Terminal, ITerminalAddon, IDisposable } from '@xterm/xterm'; import type { ProgressAddon as IProgressApi, IProgressState } from '@xterm/addon-progress'; -import type { Emitter, IEvent } from '../../../src/common/Event'; +import type { Emitter, IEvent } from 'common/Event'; const enum ProgressType { diff --git a/addons/addon-progress/src/tsconfig.json b/addons/addon-progress/src/tsconfig.json index 3d64b7791..bd437f5b6 100644 --- a/addons/addon-progress/src/tsconfig.json +++ b/addons/addon-progress/src/tsconfig.json @@ -15,6 +15,12 @@ "../../../node_modules/@types/mocha" ], "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ], "@xterm/addon-progress": [ "../typings/addon-progress.d.ts" ] diff --git a/addons/addon-progress/test/tsconfig.json b/addons/addon-progress/test/tsconfig.json index 73f17c2a5..900a727ed 100644 --- a/addons/addon-progress/test/tsconfig.json +++ b/addons/addon-progress/test/tsconfig.json @@ -10,6 +10,12 @@ "sourceMap": true, "removeComments": true, "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ], "*": [ "./*" ] diff --git a/addons/addon-progress/webpack.config.js b/addons/addon-progress/webpack.config.js index 250b04a49..1bf29bfd2 100644 --- a/addons/addon-progress/webpack.config.js +++ b/addons/addon-progress/webpack.config.js @@ -24,6 +24,11 @@ module.exports = { resolve: { modules: ['./node_modules'], extensions: [ '.js' ], + alias: { + common: path.resolve('../../out/common'), + browser: path.resolve('../../out/browser'), + vs: path.resolve('../../out/vs') + } }, output: { filename: mainFile, diff --git a/addons/addon-search/src/DecorationManager.ts b/addons/addon-search/src/DecorationManager.ts index 1675ef10f..a8ec12017 100644 --- a/addons/addon-search/src/DecorationManager.ts +++ b/addons/addon-search/src/DecorationManager.ts @@ -5,7 +5,7 @@ import type { Terminal, IDisposable, IDecoration } from '@xterm/xterm'; import type { ISearchDecorationOptions } from '@xterm/addon-search'; -import { dispose, Disposable, toDisposable } from '../../../src/common/Lifecycle'; +import { dispose, Disposable, toDisposable } from 'common/Lifecycle'; import type { ISearchResult } from './SearchEngine'; /** diff --git a/addons/addon-search/src/SearchAddon.ts b/addons/addon-search/src/SearchAddon.ts index 720ed26f0..cb4094743 100644 --- a/addons/addon-search/src/SearchAddon.ts +++ b/addons/addon-search/src/SearchAddon.ts @@ -5,9 +5,9 @@ import type { Terminal, IDisposable, ITerminalAddon } from '@xterm/xterm'; import type { SearchAddon as ISearchApi, ISearchOptions, ISearchAddonOptions, ISearchResultChangeEvent, ISearchDecorationOptions } from '@xterm/addon-search'; -import { Emitter, type IEvent } from '../../../src/common/Event'; -import { Disposable, MutableDisposable, toDisposable } from '../../../src/common/Lifecycle'; -import { disposableTimeout } from '../../../src/common/Async'; +import { Emitter, type IEvent } from 'common/Event'; +import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; +import { disposableTimeout } from 'common/Async'; import { SearchLineCache } from './SearchLineCache'; import { SearchState } from './SearchState'; import { SearchEngine, type ISearchResult } from './SearchEngine'; diff --git a/addons/addon-search/src/SearchEngine.test.ts b/addons/addon-search/src/SearchEngine.test.ts index e2ab9939b..b3cea8f54 100644 --- a/addons/addon-search/src/SearchEngine.test.ts +++ b/addons/addon-search/src/SearchEngine.test.ts @@ -5,9 +5,9 @@ import { assert } from 'chai'; import { SearchEngine } from './SearchEngine'; import { SearchLineCache } from './SearchLineCache'; -import { Terminal } from '../../../src/browser/public/Terminal'; +import { Terminal } from 'browser/public/Terminal'; import type { ISearchOptions } from '@xterm/addon-search'; -import { DisposableStore } from '../../../src/common/Lifecycle'; +import { DisposableStore } from 'common/Lifecycle'; function writeP(terminal: Terminal, data: string): Promise { return new Promise(r => terminal.write(data, r)); diff --git a/addons/addon-search/src/SearchLineCache.test.ts b/addons/addon-search/src/SearchLineCache.test.ts index 1a465858c..77f64c81e 100644 --- a/addons/addon-search/src/SearchLineCache.test.ts +++ b/addons/addon-search/src/SearchLineCache.test.ts @@ -4,8 +4,8 @@ */ import { assert } from 'chai'; import { SearchLineCache, LineCacheEntry } from './SearchLineCache'; -import { Terminal } from '../../../src/browser/public/Terminal'; -import { timeout } from '../../../src/common/Async'; +import { Terminal } from 'browser/public/Terminal'; +import { timeout } from 'common/Async'; function writeP(terminal: Terminal, data: string): Promise { return new Promise(r => terminal.write(data, r)); diff --git a/addons/addon-search/src/SearchLineCache.ts b/addons/addon-search/src/SearchLineCache.ts index 1b590ec74..526f4bfcc 100644 --- a/addons/addon-search/src/SearchLineCache.ts +++ b/addons/addon-search/src/SearchLineCache.ts @@ -4,8 +4,8 @@ */ import type { Terminal } from '@xterm/xterm'; -import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from '../../../src/common/Lifecycle'; -import { disposableTimeout } from '../../../src/common/Async'; +import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; +import { disposableTimeout } from 'common/Async'; export type LineCacheEntry = [ /** diff --git a/addons/addon-search/src/SearchResultTracker.ts b/addons/addon-search/src/SearchResultTracker.ts index 9c7997741..285345b7a 100644 --- a/addons/addon-search/src/SearchResultTracker.ts +++ b/addons/addon-search/src/SearchResultTracker.ts @@ -5,8 +5,8 @@ import type { ISearchResultChangeEvent } from '@xterm/addon-search'; import type { IDisposable } from '@xterm/xterm'; -import { Emitter, type IEvent } from '../../../src/common/Event'; -import { Disposable } from '../../../src/common/Lifecycle'; +import { Emitter, type IEvent } from 'common/Event'; +import { Disposable } from 'common/Lifecycle'; import type { ISearchResult } from './SearchEngine'; /** diff --git a/addons/addon-search/src/tsconfig.json b/addons/addon-search/src/tsconfig.json index 7e05af738..9817710fa 100644 --- a/addons/addon-search/src/tsconfig.json +++ b/addons/addon-search/src/tsconfig.json @@ -15,6 +15,12 @@ "../../../node_modules/@types/mocha" ], "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ], "@xterm/addon-search" : [ "../typings/addon-search.d.ts" ] diff --git a/addons/addon-search/test/tsconfig.json b/addons/addon-search/test/tsconfig.json index 73f17c2a5..900a727ed 100644 --- a/addons/addon-search/test/tsconfig.json +++ b/addons/addon-search/test/tsconfig.json @@ -10,6 +10,12 @@ "sourceMap": true, "removeComments": true, "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ], "*": [ "./*" ] diff --git a/addons/addon-search/webpack.config.js b/addons/addon-search/webpack.config.js index 9410c84d0..adc3d4bdb 100644 --- a/addons/addon-search/webpack.config.js +++ b/addons/addon-search/webpack.config.js @@ -24,6 +24,10 @@ module.exports = { resolve: { modules: ['./node_modules'], extensions: [ '.js' ], + alias: { + common: path.resolve('../../out/common'), + vs: path.resolve('../../out/vs') + } }, output: { filename: mainFile, diff --git a/addons/addon-serialize/benchmark/SerializeAddon.benchmark.ts b/addons/addon-serialize/benchmark/SerializeAddon.benchmark.ts index 0b0130cca..b60dba1dd 100644 --- a/addons/addon-serialize/benchmark/SerializeAddon.benchmark.ts +++ b/addons/addon-serialize/benchmark/SerializeAddon.benchmark.ts @@ -6,8 +6,8 @@ import { perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark'; import { spawn } from 'node-pty'; -import { Utf8ToUtf32, stringFromCodePoint } from '../../../src/common/input/TextDecoder'; -import { Terminal } from '../../../src/browser/public/Terminal'; +import { Utf8ToUtf32, stringFromCodePoint } from 'common/input/TextDecoder'; +import { Terminal } from 'browser/public/Terminal'; import { SerializeAddon } from 'SerializeAddon'; class TestTerminal extends Terminal { diff --git a/addons/addon-serialize/benchmark/tsconfig.json b/addons/addon-serialize/benchmark/tsconfig.json index f187b3c8f..ad7ee6d04 100644 --- a/addons/addon-serialize/benchmark/tsconfig.json +++ b/addons/addon-serialize/benchmark/tsconfig.json @@ -6,36 +6,24 @@ ], "rootDir": "..", "outDir": "../out-benchmark", - "types": [ - "../../../node_modules/@types/node" - ], + "types": ["../../../node_modules/@types/node"], "moduleResolution": "nodenext", "strict": false, "target": "es2021", "module": "nodenext", "paths": { - "SerializeAddon": [ - "../src/SerializeAddon" - ], + "common/*": ["../../../src/common/*"], + "browser/*": ["../../../src/browser/*"], + "SerializeAddon": ["../src/SerializeAddon"], "@xterm/addon-serialize": [ "../typings/addon-serialize.d.ts" ] } }, - "include": [ - "../**/*", - "../../../typings/xterm.d.ts" - ], - "exclude": [ - "../../../**/*test.ts", - "../../**/*api.ts" - ], + "include": ["../**/*", "../../../typings/xterm.d.ts"], + "exclude": ["../../../**/*test.ts", "../../**/*api.ts"], "references": [ - { - "path": "../../../src/common" - }, - { - "path": "../../../src/browser" - } + { "path": "../../../src/common" }, + { "path": "../../../src/browser" } ] } diff --git a/addons/addon-serialize/src/SerializeAddon.test.ts b/addons/addon-serialize/src/SerializeAddon.test.ts index 0cfc83258..4f4fd5461 100644 --- a/addons/addon-serialize/src/SerializeAddon.test.ts +++ b/addons/addon-serialize/src/SerializeAddon.test.ts @@ -6,10 +6,10 @@ import jsdom = require('jsdom'); import { assert } from 'chai'; import { SerializeAddon } from './SerializeAddon'; -import { Terminal } from '../../../src/browser/public/Terminal'; -import { SelectionModel } from '../../../src/browser/selection/SelectionModel'; -import { IBufferService } from '../../../src/common/services/Services'; -import { ThemeService } from '../../../src/browser/services/ThemeService'; +import { Terminal } from 'browser/public/Terminal'; +import { SelectionModel } from 'browser/selection/SelectionModel'; +import { IBufferService } from 'common/services/Services'; +import { ThemeService } from 'browser/services/ThemeService'; function sgr(...seq: string[]): string { return `\x1b[${seq.join(';')}m`; diff --git a/addons/addon-serialize/src/SerializeAddon.ts b/addons/addon-serialize/src/SerializeAddon.ts index fbcc65cfa..4980c8eda 100644 --- a/addons/addon-serialize/src/SerializeAddon.ts +++ b/addons/addon-serialize/src/SerializeAddon.ts @@ -7,9 +7,9 @@ import type { IBuffer, IBufferCell, IBufferRange, ITerminalAddon, Terminal } from '@xterm/xterm'; import type { IHTMLSerializeOptions, SerializeAddon as ISerializeApi, ISerializeOptions, ISerializeRange } from '@xterm/addon-serialize'; -import { IAttributeData, IColor } from '../../../src/common/Types'; -import { DEFAULT_ANSI_COLORS } from '../../../src/browser/Types'; -import { UnderlineStyle } from '../../../src/common/buffer/Constants'; +import { IAttributeData, IColor } from 'common/Types'; +import { DEFAULT_ANSI_COLORS } from 'browser/Types'; +import { UnderlineStyle } from 'common/buffer/Constants'; function constrain(value: number, low: number, high: number): number { return Math.max(low, Math.min(value, high)); diff --git a/addons/addon-serialize/src/tsconfig.json b/addons/addon-serialize/src/tsconfig.json index 8176e660e..725647366 100644 --- a/addons/addon-serialize/src/tsconfig.json +++ b/addons/addon-serialize/src/tsconfig.json @@ -11,6 +11,12 @@ "sourceMap": true, "removeComments": true, "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ], "@xterm/addon-serialize": [ "../typings/addon-serialize.d.ts" ] diff --git a/addons/addon-serialize/test/tsconfig.json b/addons/addon-serialize/test/tsconfig.json index 9d3f4722c..769909b19 100644 --- a/addons/addon-serialize/test/tsconfig.json +++ b/addons/addon-serialize/test/tsconfig.json @@ -9,6 +9,14 @@ "outDir": "../out-test", "sourceMap": true, "removeComments": true, + "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ] + }, "strict": true, "types": [ "../../../node_modules/@types/node" diff --git a/addons/addon-serialize/webpack.config.js b/addons/addon-serialize/webpack.config.js index c38ab9f2b..837a73a38 100644 --- a/addons/addon-serialize/webpack.config.js +++ b/addons/addon-serialize/webpack.config.js @@ -24,6 +24,10 @@ module.exports = { resolve: { modules: ['./node_modules'], extensions: [ '.js' ], + alias: { + common: path.resolve('../../out/common'), + browser: path.resolve('../../out/browser') + } }, output: { filename: mainFile, diff --git a/addons/addon-unicode-graphemes/benchmark/UnicodeGraphemeAddon.benchmark.ts b/addons/addon-unicode-graphemes/benchmark/UnicodeGraphemeAddon.benchmark.ts index 74b434814..7538b9344 100644 --- a/addons/addon-unicode-graphemes/benchmark/UnicodeGraphemeAddon.benchmark.ts +++ b/addons/addon-unicode-graphemes/benchmark/UnicodeGraphemeAddon.benchmark.ts @@ -6,8 +6,8 @@ import { perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark'; import { spawn } from 'node-pty'; -import { Utf8ToUtf32, stringFromCodePoint } from '../../../src/common/input/TextDecoder'; -import { CoreBrowserTerminal } from '../../../src/browser/CoreBrowserTerminal'; +import { Utf8ToUtf32, stringFromCodePoint } from 'common/input/TextDecoder'; +import { CoreBrowserTerminal } from 'browser/CoreBrowserTerminal'; import { UnicodeGraphemeProvider } from 'UnicodeGraphemeProvider'; diff --git a/addons/addon-unicode-graphemes/benchmark/tsconfig.json b/addons/addon-unicode-graphemes/benchmark/tsconfig.json index 75fda6d08..1781ca318 100644 --- a/addons/addon-unicode-graphemes/benchmark/tsconfig.json +++ b/addons/addon-unicode-graphemes/benchmark/tsconfig.json @@ -1,41 +1,26 @@ { "compilerOptions": { - "lib": [ - "dom", - "es6" - ], + "lib": ["dom", "es6"], "rootDir": "..", "outDir": "../out-benchmark", - "types": [ - "../../../node_modules/@types/node" - ], + "types": ["../../../node_modules/@types/node"], "moduleResolution": "nodenext", "strict": false, "target": "es2015", "module": "nodenext", "paths": { - "UnicodeGraphemeProvider": [ - "../src/UnicodeGraphemeProvider" - ], + "common/*": ["../../../src/common/*"], + "browser/*": ["../../../src/browser/*"], + "UnicodeGraphemeProvider": ["../src/UnicodeGraphemeProvider"], "@xterm/addon-unicode-graphemes": [ "../typings/addon-unicode-graphemes.d.ts" ] } }, - "include": [ - "../**/*", - "../../../typings/xterm.d.ts" - ], - "exclude": [ - "../../../**/*test.ts", - "../../**/*api.ts" - ], + "include": ["../**/*", "../../../typings/xterm.d.ts"], + "exclude": ["../../../**/*test.ts", "../../**/*api.ts"], "references": [ - { - "path": "../../../src/common" - }, - { - "path": "../../../src/browser" - } + { "path": "../../../src/common" }, + { "path": "../../../src/browser" } ] } diff --git a/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts b/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts index ed258805d..3d5717a0f 100644 --- a/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts +++ b/addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts @@ -4,8 +4,8 @@ */ import { IUnicodeVersionProvider } from '@xterm/xterm'; -import { UnicodeCharProperties, UnicodeCharWidth } from '../../../src/common/services/Services'; -import { UnicodeService } from '../../../src/common/services/UnicodeService'; +import { UnicodeCharProperties, UnicodeCharWidth } from 'common/services/Services'; +import { UnicodeService } from 'common/services/UnicodeService'; import * as UC from './third-party/UnicodeProperties'; export class UnicodeGraphemeProvider implements IUnicodeVersionProvider { diff --git a/addons/addon-unicode-graphemes/src/tsconfig.json b/addons/addon-unicode-graphemes/src/tsconfig.json index de62f95cb..824909dbd 100644 --- a/addons/addon-unicode-graphemes/src/tsconfig.json +++ b/addons/addon-unicode-graphemes/src/tsconfig.json @@ -12,6 +12,9 @@ "removeComments": true, "strict": true, "paths": { + "common/*": [ + "../../../src/common/*" + ], "@xterm/addon-unicode-graphemes": [ "../typings/addon-unicode-graphemes.d.ts" ] diff --git a/addons/addon-unicode-graphemes/test/tsconfig.json b/addons/addon-unicode-graphemes/test/tsconfig.json index 9d3f4722c..769909b19 100644 --- a/addons/addon-unicode-graphemes/test/tsconfig.json +++ b/addons/addon-unicode-graphemes/test/tsconfig.json @@ -9,6 +9,14 @@ "outDir": "../out-test", "sourceMap": true, "removeComments": true, + "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ] + }, "strict": true, "types": [ "../../../node_modules/@types/node" diff --git a/addons/addon-unicode-graphemes/webpack.config.js b/addons/addon-unicode-graphemes/webpack.config.js index b3934006e..35e111d53 100644 --- a/addons/addon-unicode-graphemes/webpack.config.js +++ b/addons/addon-unicode-graphemes/webpack.config.js @@ -24,6 +24,10 @@ module.exports = { resolve: { modules: ['./node_modules'], extensions: [ '.js' ], + alias: { + common: path.resolve('../../out/common'), + vs: path.resolve('../../out/vs') + } }, output: { filename: mainFile, diff --git a/addons/addon-unicode11/src/UnicodeV11.ts b/addons/addon-unicode11/src/UnicodeV11.ts index d80b60b80..79568960a 100644 --- a/addons/addon-unicode11/src/UnicodeV11.ts +++ b/addons/addon-unicode11/src/UnicodeV11.ts @@ -4,8 +4,8 @@ */ import { IUnicodeVersionProvider } from '@xterm/xterm'; -import { UnicodeCharProperties, UnicodeCharWidth } from '../../../src/common/services/Services'; -import { UnicodeService } from '../../../src/common/services/UnicodeService'; +import { UnicodeCharProperties, UnicodeCharWidth } from 'common/services/Services'; +import { UnicodeService } from 'common/services/UnicodeService'; const BMP_COMBINING = [ [0x0300, 0x036F], [0x0483, 0x0489], [0x0591, 0x05BD], diff --git a/addons/addon-unicode11/src/tsconfig.json b/addons/addon-unicode11/src/tsconfig.json index a5b97d43e..31286fb0f 100644 --- a/addons/addon-unicode11/src/tsconfig.json +++ b/addons/addon-unicode11/src/tsconfig.json @@ -12,8 +12,14 @@ "removeComments": true, "strict": true, "paths": { + "common/*": [ + "../../../src/common/*" + ], "@xterm/addon-unicode11": [ "../typings/addon-unicode11.d.ts" + ], + "*": [ + "./*" ] }, "types": [ diff --git a/addons/addon-unicode11/test/tsconfig.json b/addons/addon-unicode11/test/tsconfig.json index 73f17c2a5..900a727ed 100644 --- a/addons/addon-unicode11/test/tsconfig.json +++ b/addons/addon-unicode11/test/tsconfig.json @@ -10,6 +10,12 @@ "sourceMap": true, "removeComments": true, "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ], "*": [ "./*" ] diff --git a/addons/addon-unicode11/webpack.config.js b/addons/addon-unicode11/webpack.config.js index f3ae10eef..9b66049fd 100644 --- a/addons/addon-unicode11/webpack.config.js +++ b/addons/addon-unicode11/webpack.config.js @@ -24,6 +24,10 @@ module.exports = { resolve: { modules: ['./node_modules'], extensions: [ '.js' ], + alias: { + common: path.resolve('../../out/common'), + vs: path.resolve('../../out/vs') + } }, output: { filename: mainFile, diff --git a/addons/addon-web-fonts/test/tsconfig.json b/addons/addon-web-fonts/test/tsconfig.json index 66097910d..59664e32b 100644 --- a/addons/addon-web-fonts/test/tsconfig.json +++ b/addons/addon-web-fonts/test/tsconfig.json @@ -10,6 +10,12 @@ "sourceMap": true, "removeComments": true, "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ], "*": [ "./*" ] diff --git a/addons/addon-web-links/test/tsconfig.json b/addons/addon-web-links/test/tsconfig.json index 66097910d..59664e32b 100644 --- a/addons/addon-web-links/test/tsconfig.json +++ b/addons/addon-web-links/test/tsconfig.json @@ -10,6 +10,12 @@ "sourceMap": true, "removeComments": true, "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ], "*": [ "./*" ] diff --git a/addons/addon-webgl/src/CellColorResolver.ts b/addons/addon-webgl/src/CellColorResolver.ts index 52e19a823..3a4661cc3 100644 --- a/addons/addon-webgl/src/CellColorResolver.ts +++ b/addons/addon-webgl/src/CellColorResolver.ts @@ -1,12 +1,12 @@ -import { ISelectionRenderModel } from '../../../src/browser/renderer/shared/Types'; -import { ICoreBrowserService, IThemeService } from '../../../src/browser/services/Services'; -import { ReadonlyColorSet } from '../../../src/browser/Types'; -import { Attributes, BgFlags, ExtFlags, FgFlags, NULL_CELL_CODE, UnderlineStyle } from '../../../src/common/buffer/Constants'; -import { IDecorationService, IOptionsService } from '../../../src/common/services/Services'; -import { ICellData } from '../../../src/common/Types'; +import { ISelectionRenderModel } from 'browser/renderer/shared/Types'; +import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; +import { ReadonlyColorSet } from 'browser/Types'; +import { Attributes, BgFlags, ExtFlags, FgFlags, NULL_CELL_CODE, UnderlineStyle } from 'common/buffer/Constants'; +import { IDecorationService, IOptionsService } from 'common/services/Services'; +import { ICellData } from 'common/Types'; import { Terminal } from '@xterm/xterm'; -import { rgba } from '../../../src/common/Color'; -import { treatGlyphAsBackgroundColor } from '../../../src/browser/renderer/shared/RendererUtils'; +import { rgba } from 'common/Color'; +import { treatGlyphAsBackgroundColor } from 'browser/renderer/shared/RendererUtils'; import { blockPatternCodepoints } from './customGlyphs/CustomGlyphDefinitions'; // Work variables to avoid garbage collection diff --git a/addons/addon-webgl/src/CharAtlasCache.ts b/addons/addon-webgl/src/CharAtlasCache.ts index 9aa165f12..823a74cc6 100644 --- a/addons/addon-webgl/src/CharAtlasCache.ts +++ b/addons/addon-webgl/src/CharAtlasCache.ts @@ -5,10 +5,10 @@ import { TextureAtlas } from './TextureAtlas'; import { ITerminalOptions, Terminal } from '@xterm/xterm'; -import { ITerminal, ReadonlyColorSet } from '../../../src/browser/Types'; +import { ITerminal, ReadonlyColorSet } from 'browser/Types'; import { ICharAtlasConfig, ITextureAtlas } from './Types'; import { generateConfig, configEquals } from './CharAtlasUtils'; -import type { ILogService } from '../../../src/common/services/Services'; +import type { ILogService } from 'common/services/Services'; interface ITextureAtlasCacheEntry { atlas: ITextureAtlas; diff --git a/addons/addon-webgl/src/CharAtlasUtils.test.ts b/addons/addon-webgl/src/CharAtlasUtils.test.ts index 572989410..0a135746e 100644 --- a/addons/addon-webgl/src/CharAtlasUtils.test.ts +++ b/addons/addon-webgl/src/CharAtlasUtils.test.ts @@ -6,8 +6,8 @@ import { assert } from 'chai'; import { configEquals } from './CharAtlasUtils'; import { ICharAtlasConfig } from './Types'; -import { NULL_COLOR } from '../../../src/common/Color'; -import { IColor } from '../../../src/common/Types'; +import { NULL_COLOR } from 'common/Color'; +import { IColor } from 'common/Types'; function createTestConfig(overrides: Partial = {}): ICharAtlasConfig { const color: IColor = { css: '#ffffff', rgba: 0xffffffff }; diff --git a/addons/addon-webgl/src/CharAtlasUtils.ts b/addons/addon-webgl/src/CharAtlasUtils.ts index 6a98c9a8d..d9ca69b39 100644 --- a/addons/addon-webgl/src/CharAtlasUtils.ts +++ b/addons/addon-webgl/src/CharAtlasUtils.ts @@ -4,10 +4,10 @@ */ import { ICharAtlasConfig } from './Types'; -import { Attributes } from '../../../src/common/buffer/Constants'; +import { Attributes } from 'common/buffer/Constants'; import { ITerminalOptions } from '@xterm/xterm'; -import { IColorSet, ReadonlyColorSet } from '../../../src/browser/Types'; -import { NULL_COLOR } from '../../../src/common/Color'; +import { IColorSet, ReadonlyColorSet } from 'browser/Types'; +import { NULL_COLOR } from 'common/Color'; export function generateConfig(deviceCellWidth: number, deviceCellHeight: number, deviceCharWidth: number, deviceCharHeight: number, options: Required, colors: ReadonlyColorSet, devicePixelRatio: number, deviceMaxTextureSize: number, customGlyphs: boolean = true): ICharAtlasConfig { // null out some fields that don't matter diff --git a/addons/addon-webgl/src/Constants.ts b/addons/addon-webgl/src/Constants.ts index e37aecdb4..a00434d61 100644 --- a/addons/addon-webgl/src/Constants.ts +++ b/addons/addon-webgl/src/Constants.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { isFirefox, isLegacyEdge } from '../../../src/common/Platform'; +import { isFirefox, isLegacyEdge } from 'common/Platform'; export const DIM_OPACITY = 0.5; // The text baseline is set conditionally by browser. Using 'ideographic' for Firefox or Legacy Edge diff --git a/addons/addon-webgl/src/CursorBlinkStateManager.ts b/addons/addon-webgl/src/CursorBlinkStateManager.ts index 1339dae03..688aee7b7 100644 --- a/addons/addon-webgl/src/CursorBlinkStateManager.ts +++ b/addons/addon-webgl/src/CursorBlinkStateManager.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { RendererConstants } from '../../../src/browser/renderer/shared/Constants'; -import { ICoreBrowserService } from '../../../src/browser/services/Services'; +import { RendererConstants } from 'browser/renderer/shared/Constants'; +import { ICoreBrowserService } from 'browser/services/Services'; const enum Constants { /** diff --git a/addons/addon-webgl/src/DevicePixelObserver.ts b/addons/addon-webgl/src/DevicePixelObserver.ts index 50ed09941..dcd8de0a6 100644 --- a/addons/addon-webgl/src/DevicePixelObserver.ts +++ b/addons/addon-webgl/src/DevicePixelObserver.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { toDisposable, IDisposable } from '../../../src/common/Lifecycle'; +import { toDisposable, IDisposable } from 'common/Lifecycle'; export function observeDevicePixelDimensions(element: HTMLElement, parentWindow: Window & typeof globalThis, callback: (deviceWidth: number, deviceHeight: number) => void): IDisposable { // Observe any resizes to the element and extract the actual pixel size of the element if the diff --git a/addons/addon-webgl/src/GlyphRenderer.ts b/addons/addon-webgl/src/GlyphRenderer.ts index 915979206..650fc451a 100644 --- a/addons/addon-webgl/src/GlyphRenderer.ts +++ b/addons/addon-webgl/src/GlyphRenderer.ts @@ -3,14 +3,14 @@ * @license MIT */ import { TextureAtlas } from './TextureAtlas'; -import { IRenderDimensions } from '../../../src/browser/renderer/shared/Types'; -import { NULL_CELL_CODE } from '../../../src/common/buffer/Constants'; -import { Disposable, toDisposable } from '../../../src/common/Lifecycle'; +import { IRenderDimensions } from 'browser/renderer/shared/Types'; +import { NULL_CELL_CODE } from 'common/buffer/Constants'; +import { Disposable, toDisposable } from 'common/Lifecycle'; import { Terminal } from '@xterm/xterm'; import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject, type IRasterizedGlyph, type ITextureAtlas } from './Types'; import { createProgram, GLTexture, PROJECTION_MATRIX } from './WebglUtils'; -import type { ILogService, IOptionsService } from '../../../src/common/services/Services'; -import { allowRescaling, throwIfFalsy } from '../../../src/browser/renderer/shared/RendererUtils'; +import type { ILogService, IOptionsService } from 'common/services/Services'; +import { allowRescaling, throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; interface IVertices { attributes: Float32Array; diff --git a/addons/addon-webgl/src/RectangleRenderer.ts b/addons/addon-webgl/src/RectangleRenderer.ts index 0eb45a11c..eebfda3b3 100644 --- a/addons/addon-webgl/src/RectangleRenderer.ts +++ b/addons/addon-webgl/src/RectangleRenderer.ts @@ -3,18 +3,18 @@ * @license MIT */ -import { IRenderDimensions } from '../../../src/browser/renderer/shared/Types'; -import { IThemeService } from '../../../src/browser/services/Services'; -import { ReadonlyColorSet } from '../../../src/browser/Types'; -import { Attributes, FgFlags } from '../../../src/common/buffer/Constants'; -import { Disposable, toDisposable } from '../../../src/common/Lifecycle'; -import { IColor } from '../../../src/common/Types'; +import { IRenderDimensions } from 'browser/renderer/shared/Types'; +import { IThemeService } from 'browser/services/Services'; +import { ReadonlyColorSet } from 'browser/Types'; +import { Attributes, FgFlags } from 'common/buffer/Constants'; +import { Disposable, toDisposable } from 'common/Lifecycle'; +import { IColor } from 'common/Types'; import { Terminal } from '@xterm/xterm'; import { RenderModelConstants } from './RenderModel'; import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject } from './Types'; import { createProgram, expandFloat32Array, PROJECTION_MATRIX } from './WebglUtils'; -import { throwIfFalsy } from '../../../src/browser/renderer/shared/RendererUtils'; -import type { ILogService } from '../../../src/common/services/Services'; +import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; +import type { ILogService } from 'common/services/Services'; const enum VertexAttribLocations { POSITION = 0, diff --git a/addons/addon-webgl/src/RenderModel.ts b/addons/addon-webgl/src/RenderModel.ts index e1fe54628..6113a9bdc 100644 --- a/addons/addon-webgl/src/RenderModel.ts +++ b/addons/addon-webgl/src/RenderModel.ts @@ -4,8 +4,8 @@ */ import { ICursorRenderModel, IRenderModel } from './Types'; -import { ISelectionRenderModel } from '../../../src/browser/renderer/shared/Types'; -import { createSelectionRenderModel } from '../../../src/browser/renderer/shared/SelectionRenderModel'; +import { ISelectionRenderModel } from 'browser/renderer/shared/Types'; +import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel'; export const enum RenderModelConstants { INDICIES_PER_CELL = 4, diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index a6dda7b71..dab0b1c85 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -3,19 +3,19 @@ * @license MIT */ -import { IColorContrastCache } from '../../../src/browser/Types'; +import { IColorContrastCache } from 'browser/Types'; import { DIM_OPACITY, TEXT_BASELINE } from './Constants'; import { tryDrawCustomGlyph } from './customGlyphs/CustomGlyphRasterizer'; -import { computeNextVariantOffset, treatGlyphAsBackgroundColor, isPowerlineGlyph, isRestrictedPowerlineGlyph, throwIfFalsy } from '../../../src/browser/renderer/shared/RendererUtils'; +import { computeNextVariantOffset, treatGlyphAsBackgroundColor, isPowerlineGlyph, isRestrictedPowerlineGlyph, throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; import { IBoundingBox, ICharAtlasConfig, IRasterizedGlyph, ITextureAtlas } from './Types'; -import { NULL_COLOR, channels, color, rgba } from '../../../src/common/Color'; -import { FourKeyMap } from '../../../src/common/MultiKeyMap'; -import { IdleTaskQueue } from '../../../src/common/TaskQueue'; -import { IColor } from '../../../src/common/Types'; -import { AttributeData } from '../../../src/common/buffer/AttributeData'; -import { Attributes, DEFAULT_COLOR, DEFAULT_EXT, UnderlineStyle } from '../../../src/common/buffer/Constants'; -import { ILogService, IUnicodeService } from '../../../src/common/services/Services'; -import { Emitter } from '../../../src/common/Event'; +import { NULL_COLOR, channels, color, rgba } from 'common/Color'; +import { FourKeyMap } from 'common/MultiKeyMap'; +import { IdleTaskQueue } from 'common/TaskQueue'; +import { IColor } from 'common/Types'; +import { AttributeData } from 'common/buffer/AttributeData'; +import { Attributes, DEFAULT_COLOR, DEFAULT_EXT, UnderlineStyle } from 'common/buffer/Constants'; +import { ILogService, IUnicodeService } from 'common/services/Services'; +import { Emitter } from 'common/Event'; /** * A shared object which is used to draw nothing for a particular cell. diff --git a/addons/addon-webgl/src/Types.ts b/addons/addon-webgl/src/Types.ts index 913d9c8ab..294582e37 100644 --- a/addons/addon-webgl/src/Types.ts +++ b/addons/addon-webgl/src/Types.ts @@ -4,10 +4,10 @@ */ import { FontWeight } from '@xterm/xterm'; -import { IColorSet } from '../../../src/browser/Types'; -import { ISelectionRenderModel } from '../../../src/browser/renderer/shared/Types'; -import { CursorInactiveStyle, CursorStyle, type IDisposable } from '../../../src/common/Types'; -import type { IEvent } from '../../../src/common/Event'; +import { IColorSet } from 'browser/Types'; +import { ISelectionRenderModel } from 'browser/renderer/shared/Types'; +import { CursorInactiveStyle, CursorStyle, type IDisposable } from 'common/Types'; +import type { IEvent } from 'common/Event'; export interface IRenderModel { cells: Uint32Array; diff --git a/addons/addon-webgl/src/WebglAddon.ts b/addons/addon-webgl/src/WebglAddon.ts index b370d2955..624c9ea38 100644 --- a/addons/addon-webgl/src/WebglAddon.ts +++ b/addons/addon-webgl/src/WebglAddon.ts @@ -5,14 +5,14 @@ import type { ITerminalAddon, Terminal } from '@xterm/xterm'; import type { IWebglAddonOptions, WebglAddon as IWebglApi } from '@xterm/addon-webgl'; -import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from '../../../src/browser/services/Services'; -import { ITerminal } from '../../../src/browser/Types'; -import { Disposable, toDisposable } from '../../../src/common/Lifecycle'; -import { getSafariVersion, isSafari } from '../../../src/common/Platform'; -import { ICoreService, IDecorationService, ILogService, IOptionsService } from '../../../src/common/services/Services'; +import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; +import { ITerminal } from 'browser/Types'; +import { Disposable, toDisposable } from 'common/Lifecycle'; +import { getSafariVersion, isSafari } from 'common/Platform'; +import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services'; import { IWebGL2RenderingContext } from './Types'; import { WebglRenderer } from './WebglRenderer'; -import { Emitter, EventUtils } from '../../../src/common/Event'; +import { Emitter, EventUtils } from 'common/Event'; export class WebglAddon extends Disposable implements ITerminalAddon, IWebglApi { private _terminal?: Terminal; diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 1302d6be2..9c2f459df 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -3,19 +3,19 @@ * @license MIT */ -import { ITerminal } from '../../../src/browser/Types'; +import { ITerminal } from 'browser/Types'; import { CellColorResolver } from './CellColorResolver'; import { acquireTextureAtlas, removeTerminalFromCache } from './CharAtlasCache'; import { CursorBlinkStateManager } from './CursorBlinkStateManager'; import { observeDevicePixelDimensions } from './DevicePixelObserver'; -import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from '../../../src/browser/renderer/shared/Types'; -import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IThemeService } from '../../../src/browser/services/Services'; -import { CharData, IBufferLine, ICellData } from '../../../src/common/Types'; -import { AttributeData } from '../../../src/common/buffer/AttributeData'; -import { CellData } from '../../../src/common/buffer/CellData'; -import { Attributes, Content, FgFlags, NULL_CELL_CHAR, NULL_CELL_CODE } from '../../../src/common/buffer/Constants'; -import { TextBlinkStateManager } from '../../../src/browser/renderer/shared/TextBlinkStateManager'; -import { ICoreService, IDecorationService, ILogService, IOptionsService } from '../../../src/common/services/Services'; +import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types'; +import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services'; +import { CharData, IBufferLine, ICellData } from 'common/Types'; +import { AttributeData } from 'common/buffer/AttributeData'; +import { CellData } from 'common/buffer/CellData'; +import { Attributes, Content, FgFlags, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants'; +import { TextBlinkStateManager } from 'browser/renderer/shared/TextBlinkStateManager'; +import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services'; import { Terminal } from '@xterm/xterm'; import { GlyphRenderer } from './GlyphRenderer'; import { RectangleRenderer } from './RectangleRenderer'; @@ -23,10 +23,10 @@ import { COMBINED_CHAR_BIT_MASK, RenderModel, RenderModelConstants } from './Ren import { IWebGL2RenderingContext, type ITextureAtlas } from './Types'; import { LinkRenderLayer } from './renderLayer/LinkRenderLayer'; import { IRenderLayer } from './renderLayer/Types'; -import { Emitter, EventUtils } from '../../../src/common/Event'; -import { addDisposableListener } from '../../../src/browser/Dom'; -import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from '../../../src/common/Lifecycle'; -import { createRenderDimensions } from '../../../src/browser/renderer/shared/RendererUtils'; +import { Emitter, EventUtils } from 'common/Event'; +import { addDisposableListener } from 'browser/Dom'; +import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; +import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils'; const enum Constants { MERGE_RETRY_LIMIT = 32 diff --git a/addons/addon-webgl/src/WebglUtils.ts b/addons/addon-webgl/src/WebglUtils.ts index b106f7d56..9944c6bd3 100644 --- a/addons/addon-webgl/src/WebglUtils.ts +++ b/addons/addon-webgl/src/WebglUtils.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { throwIfFalsy } from '../../../src/browser/renderer/shared/RendererUtils'; -import type { ILogService } from '../../../src/common/services/Services'; +import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; +import type { ILogService } from 'common/services/Services'; /** * A matrix that when multiplies will translate 0-1 coordinates (left to right, diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts index d1c9fd83c..357d512d2 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { throwIfFalsy } from '../../../../src/browser/renderer/shared/RendererUtils'; -import type { ILogService } from '../../../../src/common/services/Services'; +import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; +import type { ILogService } from 'common/services/Services'; import { customGlyphDefinitions } from './CustomGlyphDefinitions'; import { CustomGlyphDefinitionType, CustomGlyphScaleType, CustomGlyphVectorType, type CustomGlyphDefinitionPart, type CustomGlyphPathDrawFunctionDefinition, type CustomGlyphPatternDefinition, type ICustomGlyphSolidOctantBlockVector, type ICustomGlyphVectorShape } from './Types'; diff --git a/addons/addon-webgl/src/renderLayer/BaseRenderLayer.ts b/addons/addon-webgl/src/renderLayer/BaseRenderLayer.ts index 4c286284d..5b1161532 100644 --- a/addons/addon-webgl/src/renderLayer/BaseRenderLayer.ts +++ b/addons/addon-webgl/src/renderLayer/BaseRenderLayer.ts @@ -3,16 +3,16 @@ * @license MIT */ -import { ReadonlyColorSet } from '../../../../src/browser/Types'; +import { ReadonlyColorSet } from 'browser/Types'; import { acquireTextureAtlas } from '../CharAtlasCache'; -import { IRenderDimensions } from '../../../../src/browser/renderer/shared/Types'; -import { ICoreBrowserService, IThemeService } from '../../../../src/browser/services/Services'; -import { Disposable, toDisposable } from '../../../../src/common/Lifecycle'; -import { CellData } from '../../../../src/common/buffer/CellData'; -import { IOptionsService } from '../../../../src/common/services/Services'; +import { IRenderDimensions } from 'browser/renderer/shared/Types'; +import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; +import { Disposable, toDisposable } from 'common/Lifecycle'; +import { CellData } from 'common/buffer/CellData'; +import { IOptionsService } from 'common/services/Services'; import { Terminal } from '@xterm/xterm'; import { IRenderLayer } from './Types'; -import { throwIfFalsy } from '../../../../src/browser/renderer/shared/RendererUtils'; +import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; import { TEXT_BASELINE } from '../Constants'; import type { ITextureAtlas } from '../Types'; diff --git a/addons/addon-webgl/src/renderLayer/LinkRenderLayer.ts b/addons/addon-webgl/src/renderLayer/LinkRenderLayer.ts index e076b9bd7..faceac44f 100644 --- a/addons/addon-webgl/src/renderLayer/LinkRenderLayer.ts +++ b/addons/addon-webgl/src/renderLayer/LinkRenderLayer.ts @@ -4,11 +4,11 @@ */ import { is256Color } from '../CharAtlasUtils'; -import { INVERTED_DEFAULT_COLOR } from '../../../../src/browser/renderer/shared/Constants'; -import { IRenderDimensions } from '../../../../src/browser/renderer/shared/Types'; -import { ICoreBrowserService, IThemeService } from '../../../../src/browser/services/Services'; -import { ILinkifier2, ILinkifierEvent } from '../../../../src/browser/Types'; -import { IOptionsService } from '../../../../src/common/services/Services'; +import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants'; +import { IRenderDimensions } from 'browser/renderer/shared/Types'; +import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; +import { ILinkifier2, ILinkifierEvent } from 'browser/Types'; +import { IOptionsService } from 'common/services/Services'; import { Terminal } from '@xterm/xterm'; import { BaseRenderLayer } from './BaseRenderLayer'; diff --git a/addons/addon-webgl/src/renderLayer/Types.ts b/addons/addon-webgl/src/renderLayer/Types.ts index 74cd5e27b..feeceb145 100644 --- a/addons/addon-webgl/src/renderLayer/Types.ts +++ b/addons/addon-webgl/src/renderLayer/Types.ts @@ -4,7 +4,7 @@ */ import { IDisposable, Terminal } from '@xterm/xterm'; -import { IRenderDimensions } from '../../../../src/browser/renderer/shared/Types'; +import { IRenderDimensions } from 'browser/renderer/shared/Types'; export interface IRenderLayer extends IDisposable { /** diff --git a/addons/addon-webgl/src/tsconfig.json b/addons/addon-webgl/src/tsconfig.json index 99154a1ab..7035daa86 100644 --- a/addons/addon-webgl/src/tsconfig.json +++ b/addons/addon-webgl/src/tsconfig.json @@ -11,8 +11,17 @@ "sourceMap": true, "removeComments": true, "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ], "@xterm/addon-webgl": [ "../typings/addon-webgl.d.ts" + ], + "*": [ + "./*" ] }, "strict": true, diff --git a/addons/addon-webgl/test/tsconfig.json b/addons/addon-webgl/test/tsconfig.json index 3f3ca3dbc..73439ddc1 100644 --- a/addons/addon-webgl/test/tsconfig.json +++ b/addons/addon-webgl/test/tsconfig.json @@ -10,6 +10,12 @@ "sourceMap": true, "removeComments": true, "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ], "*": [ "./*" ] diff --git a/addons/addon-webgl/webpack.config.js b/addons/addon-webgl/webpack.config.js index 81e2dc3a8..2eab992e7 100644 --- a/addons/addon-webgl/webpack.config.js +++ b/addons/addon-webgl/webpack.config.js @@ -24,6 +24,11 @@ module.exports = { resolve: { modules: ['./node_modules'], extensions: [ '.js' ], + alias: { + common: path.resolve('../../out/common'), + browser: path.resolve('../../out/browser'), + vs: path.resolve('../../out/vs') + } }, output: { filename: mainFile, diff --git a/bin/test_unit.js b/bin/test_unit.js index e7270e6b1..58fdd9a32 100644 --- a/bin/test_unit.js +++ b/bin/test_unit.js @@ -8,6 +8,10 @@ const path = require('path'); const COVERAGE_LINES_THRESHOLD = 40; +// Add `out-esbuild` to NODE_PATH so addon path-mapped imports (common/*, browser/*) resolve. +const env = { ...process.env }; +env.NODE_PATH = path.resolve(__dirname, '../out-esbuild'); + let testFiles = ['**/out-esbuild/**/*.test.js']; let flagArgs = []; @@ -42,7 +46,7 @@ if (checkCoverage) { args, { cwd: path.resolve(__dirname, '..'), - env: process.env, + env, shell: true, stdio: 'inherit' } @@ -55,7 +59,7 @@ const run = cp.spawnSync( [...testFiles, ...flagArgs], { cwd: path.resolve(__dirname, '..'), - env: process.env, + env, shell: true, stdio: 'inherit' } From 7c82c1a81559553aa670482f90ff931931db65e2 Mon Sep 17 00:00:00 2001 From: Per Bothner Date: Sun, 31 May 2026 09:48:19 -0700 Subject: [PATCH 079/153] Update more DecorationService tests to use existing bufferService. --- src/common/services/DecorationService.test.ts | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/common/services/DecorationService.test.ts b/src/common/services/DecorationService.test.ts index ec86a85f3..51d020576 100644 --- a/src/common/services/DecorationService.test.ts +++ b/src/common/services/DecorationService.test.ts @@ -94,18 +94,16 @@ describe('DecorationService', () => { }); it('should find multi-line decoration when single-line decorations exist on other lines', () => { - const bufferService = new MockBufferService(80, 24, new MockOptionsService()); - const serviceWithBuffer = new DecorationService(new MockLogService(), bufferService); const buffer = bufferService.buffer; (buffer as Buffer).fillViewportRows(); for (let i = 0; i < buffer.lines.length; i++) { - serviceWithBuffer.registerDecoration({ + service.registerDecoration({ marker: buffer.addMarker(i), width: 5 }); } - const multiLine = serviceWithBuffer.registerDecoration({ + const multiLine = service.registerDecoration({ marker: buffer.addMarker(10), width: 10, height: 3 @@ -113,7 +111,7 @@ describe('DecorationService', () => { assert.ok(multiLine); const found: typeof multiLine[] = []; - serviceWithBuffer.forEachDecorationAtCell(0, 11, undefined, d => found.push(d)); + service.forEachDecorationAtCell(0, 11, undefined, d => found.push(d)); assert.include(found, multiLine); }); }); @@ -143,8 +141,6 @@ describe('DecorationService', () => { describe('line index maintenance', () => { it('should keep lookups correct after buffer trim', () => { - const bufferService = new MockBufferService(80, 5, new MockOptionsService({ scrollback: 0 })); - const service = new DecorationService(new MockLogService(), bufferService); const buffer = bufferService.buffer; (buffer as Buffer).fillViewportRows(); @@ -160,8 +156,6 @@ describe('DecorationService', () => { }); it('should remove decoration from line index when marker is trimmed off buffer', () => { - const bufferService = new MockBufferService(80, 5, new MockOptionsService({ scrollback: 0 })); - const service = new DecorationService(new MockLogService(), bufferService); const buffer = bufferService.buffer; (buffer as Buffer).fillViewportRows(); @@ -179,8 +173,6 @@ describe('DecorationService', () => { }); it('should keep multi-line decoration indexed after line insert', async () => { - const bufferService = new MockBufferService(80, 10, new MockOptionsService({ scrollback: 100 })); - const service = new DecorationService(new MockLogService(), bufferService); const buffer = bufferService.buffer; (buffer as Buffer).fillViewportRows(); From 59d1ec7bf0af78488647ce51147a7e6b7c861cec Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 09:48:32 -0700 Subject: [PATCH 080/153] Migrate linting from ESLint to Oxlint (Fixes #5690) Replace the primary lint pipeline with oxlint and oxlint-tsgolint for type-aware TypeScript rules, preserving existing rule behavior from eslint.config.mjs via @oxlint/migrate and targeted jsPlugins (@stylistic/eslint-plugin, eslint-plugin-jsdoc as jsdoc-js, oxlint-plugin-eslint for no-restricted-syntax and object-curly-spacing). Keep a hybrid ESLint pass (eslint.config.naming.mjs) only for @typescript-eslint/naming-convention until tsgolint implements it (oxc-project/tsgolint#186). Legacy eslint.config.mjs and eslint.config.typings.mjs remain for reference but are no longer used by npm scripts. Add .oxlintrc.json for src/addons/demo/test and .oxlintrc.typings.json for typings/ (syntax-only, no --type-aware). Update lint, lint-fix, lint-api, and lint-changes to run oxlint first, then naming ESLint. Platform binaries install via oxlint-tsgolint optionalDependencies on Linux, macOS, and Windows. Fixes https://github.com/xtermjs/xterm.js/issues/5690 Co-authored-by: Cursor --- .oxlintrc.json | 372 +++++++++++++ .oxlintrc.typings.json | 120 ++++ AGENTS.md | 1 + .../customGlyphs/CustomGlyphDefinitions.ts | 2 +- bin/lint_changes.js | 52 +- demo/client/components/window/gpuWindow.ts | 2 +- demo/client/components/window/webglWindow.ts | 2 +- eslint.config.naming.mjs | 71 +++ package-lock.json | 511 ++++++++++++++++++ package.json | 10 +- .../renderer/dom/DomRendererRowFactory.ts | 1 - 11 files changed, 1126 insertions(+), 18 deletions(-) create mode 100644 .oxlintrc.json create mode 100644 .oxlintrc.typings.json create mode 100644 eslint.config.naming.mjs diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 000000000..7d9cabfc5 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,372 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": [ + "typescript" + ], + "categories": { + "correctness": "off" + }, + "options": { + "typeAware": true, + "maxWarnings": 0 + }, + "env": { + "builtin": true + }, + "ignorePatterns": [ + "addons/*/src/third-party/*.ts", + "**/out/*", + "**/out-test/*", + "**/out-esbuild/*", + "**/out-esbuild-test/*", + "**/inwasm-sdks/*", + "**/typings/*.d.ts", + "**/node_modules", + "**/*.js", + "**/*.mjs" + ], + "rules": { + "constructor-super": "error", + "for-direction": "error", + "getter-return": "error", + "no-async-promise-executor": "error", + "no-case-declarations": "error", + "no-class-assign": "error", + "no-compare-neg-zero": "error", + "no-cond-assign": "error", + "no-const-assign": "error", + "no-constant-binary-expression": "error", + "no-constant-condition": "error", + "no-control-regex": "error", + "no-debugger": "error", + "no-delete-var": "error", + "no-dupe-class-members": "error", + "no-dupe-else-if": "error", + "no-dupe-keys": "error", + "no-duplicate-case": "error", + "no-empty": "error", + "no-empty-character-class": "error", + "no-empty-pattern": "error", + "no-empty-static-block": "error", + "no-ex-assign": "error", + "no-extra-boolean-cast": "error", + "no-fallthrough": "error", + "no-func-assign": "error", + "no-global-assign": "error", + "no-import-assign": "error", + "no-invalid-regexp": "error", + "no-irregular-whitespace": "error", + "no-loss-of-precision": "error", + "no-misleading-character-class": "error", + "no-new-native-nonconstructor": "error", + "no-nonoctal-decimal-escape": "error", + "no-obj-calls": "error", + "no-prototype-builtins": "error", + "no-redeclare": "error", + "no-regex-spaces": "error", + "no-self-assign": "error", + "no-setter-return": "error", + "no-shadow-restricted-names": "error", + "no-sparse-arrays": "error", + "no-this-before-super": "error", + "no-unassigned-vars": "error", + "no-unexpected-multiline": "error", + "no-unreachable": "error", + "no-unsafe-finally": "error", + "no-unsafe-negation": "error", + "no-unsafe-optional-chaining": "error", + "no-unused-labels": "error", + "no-unused-private-class-members": "error", + "no-unused-vars": "error", + "no-useless-backreference": "error", + "no-useless-catch": "error", + "no-useless-escape": "error", + "no-with": "error", + "preserve-caught-error": "error", + "require-yield": "error", + "use-isnan": "error", + "valid-typeof": "error", + "no-array-constructor": "error", + "no-unused-expressions": "error", + "typescript/ban-ts-comment": "error", + "typescript/no-duplicate-enum-values": "error", + "typescript/no-empty-object-type": "error", + "typescript/no-explicit-any": "error", + "typescript/no-extra-non-null-assertion": "error", + "typescript/no-misused-new": "error", + "typescript/no-namespace": "error", + "typescript/no-non-null-asserted-optional-chain": "error", + "typescript/no-require-imports": "error", + "typescript/no-this-alias": "error", + "typescript/no-unnecessary-type-constraint": "error", + "typescript/no-unsafe-declaration-merging": "error", + "typescript/no-unsafe-function-type": "error", + "typescript/no-wrapper-object-types": "error", + "typescript/prefer-as-const": "error", + "typescript/prefer-namespace-keyword": "error", + "typescript/triple-slash-reference": "error" + }, + "overrides": [ + { + "files": [ + "**/*.ts", + "**/*.tsx", + "**/*.mts", + "**/*.cts" + ], + "rules": { + "constructor-super": "off", + "getter-return": "off", + "no-class-assign": "off", + "no-const-assign": "off", + "no-dupe-class-members": "off", + "no-dupe-keys": "off", + "no-func-assign": "off", + "no-import-assign": "off", + "no-new-native-nonconstructor": "off", + "no-obj-calls": "off", + "no-redeclare": "off", + "no-setter-return": "off", + "no-this-before-super": "off", + "no-unreachable": "off", + "no-unsafe-negation": "off", + "no-var": "error", + "no-with": "off", + "prefer-const": "error", + "prefer-rest-params": "error", + "prefer-spread": "error" + } + }, + { + "files": [ + "**/*.ts" + ], + "jsPlugins": [ + "@stylistic/eslint-plugin", + { + "name": "jsdoc-js", + "specifier": "eslint-plugin-jsdoc" + }, + "oxlint-plugin-eslint" + ], + "rules": { + "@stylistic/comma-spacing": [ + "warn", + { + "before": false, + "after": true + } + ], + "@stylistic/indent": [ + "warn", + 2 + ], + "@stylistic/semi": [ + "warn", + "always" + ], + "@stylistic/quotes": [ + "warn", + "single", + { + "allowTemplateLiterals": "always" + } + ], + "@stylistic/member-delimiter-style": [ + "warn", + { + "multiline": { + "delimiter": "semi", + "requireLast": true + }, + "singleline": { + "delimiter": "comma", + "requireLast": false + } + } + ], + "@stylistic/type-annotation-spacing": "warn", + "@stylistic/space-unary-ops": "warn", + "@stylistic/keyword-spacing": "warn", + "@stylistic/max-len": [ + "warn", + { + "code": 1000, + "comments": 100, + "ignoreTrailingComments": true, + "ignoreUrls": true, + "ignorePattern": "^ *((?(//|\\*) @vt)|(?\\* \\| )|(?// ))" + } + ], + "@stylistic/new-parens": "warn", + "@stylistic/no-extra-semi": "error", + "@stylistic/no-trailing-spaces": "warn", + "eslint-js/object-curly-spacing": [ + "warn", + "always" + ], + "@stylistic/spaced-comment": [ + "warn", + "always", + { + "markers": [ + "/" + ], + "exceptions": [ + "-" + ] + } + ], + "jsdoc-js/check-alignment": "warn", + "jsdoc-js/check-param-names": "warn", + "jsdoc-js/multiline-blocks": "warn", + "jsdoc-js/no-multi-asterisks": "warn", + "eslint-js/one-var": [ + "warn", + "never" + ], + "eslint-js/no-restricted-syntax": [ + "warn", + { + "selector": "CallExpression[callee.name='requestAnimationFrame']", + "message": "The global requestAnimationFrame() should be avoided, call it on the parent window from ICoreBrowserService." + }, + { + "selector": "CallExpression[callee.name='cancelAnimationFrame']", + "message": "The global cancelAnimationFrame() should be avoided, call it on the parent window from ICoreBrowserService." + }, + { + "selector": "CallExpression > MemberExpression[object.name='window'][property.name='requestAnimationFrame']", + "message": "window.requestAnimationFrame() should be avoided, call it on the parent window from ICoreBrowserService." + }, + { + "selector": "CallExpression > MemberExpression[object.name='window'][property.name='cancelAnimationFrame']", + "message": "window.cancelAnimationFrame() should be avoided, call it on the parent window from ICoreBrowserService." + }, + { + "selector": "MemberExpression[object.name='window'][property.name='devicePixelRatio']", + "message": "window.devicePixelRatio should be avoided, get it from ICoreBrowserService." + } + ], + "curly": [ + "warn", + "multi-line" + ], + "eqeqeq": [ + "warn", + "always" + ], + "no-duplicate-imports": "warn", + "no-else-return": [ + "warn", + { + "allowElseIf": false + } + ], + "no-eval": "warn", + "no-irregular-whitespace": "warn", + "no-restricted-imports": [ + "warn", + { + "patterns": [ + ".*\\/out\\/.*" + ] + } + ], + "no-unsafe-finally": "warn", + "no-unused-vars": "off", + "no-var": "warn", + "no-empty": "off", + "no-empty-pattern": "off", + "no-cond-assign": "off", + "no-case-declarations": "off", + "for-direction": "off", + "no-prototype-builtins": "off", + "no-useless-escape": "off", + "no-self-assign": "off", + "no-async-promise-executor": "off", + "prefer-rest-params": "off", + "no-control-regex": "off", + "no-fallthrough": "off", + "prefer-spread": "off", + "prefer-const": "warn", + "no-unused-expressions": "off", + "typescript/array-type": [ + "warn", + { + "default": "array", + "readonly": "generic" + } + ], + "typescript/consistent-type-assertions": "warn", + "typescript/consistent-type-definitions": "warn", + "typescript/explicit-function-return-type": [ + "warn", + { + "allowExpressions": true + } + ], + "typescript/explicit-member-accessibility": [ + "warn", + { + "accessibility": "explicit", + "overrides": { + "constructors": "off" + } + } + ], + "typescript/no-confusing-void-expression": [ + "warn", + { + "ignoreArrowShorthand": true + } + ], + "typescript/no-useless-constructor": "warn", + "typescript/prefer-nullish-coalescing": [ + "warn", + { + "ignorePrimitives": true + } + ], + "typescript/prefer-namespace-keyword": "warn", + "typescript/no-unused-vars": [ + "warn", + { + "vars": "all", + "args": "none" + } + ], + "typescript/no-require-imports": "off", + "typescript/no-explicit-any": "off", + "typescript/no-wrapper-object-types": "off", + "typescript/no-empty-object-type": "off", + "typescript/no-unsafe-function-type": "off", + "typescript/no-this-alias": "off", + "typescript/no-namespace": "off", + "typescript/no-duplicate-enum-values": "off" + } + }, + { + "files": [ + "**/*.api.ts", + "**/*.test.ts" + ], + "rules": { + "eslint-js/object-curly-spacing": "off", + "@stylistic/max-len": "off", + "typescript/no-unused-vars": "off", + "typescript/explicit-function-return-type": "off", + "typescript/explicit-member-accessibility": "off" + } + }, + { + "files": [ + "demo/**/*.ts", + "**/*.benchmark.ts" + ], + "rules": { + "typescript/prefer-nullish-coalescing": "off" + } + } + ] +} diff --git a/.oxlintrc.typings.json b/.oxlintrc.typings.json new file mode 100644 index 000000000..d3dd28e01 --- /dev/null +++ b/.oxlintrc.typings.json @@ -0,0 +1,120 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "options": { + "maxWarnings": 0 + }, + "env": { + "builtin": true + }, + "overrides": [ + { + "files": [ + "typings/**/*.d.ts" + ], + "plugins": [ + "typescript" + ], + "jsPlugins": [ + "@stylistic/eslint-plugin", + { + "name": "jsdoc-js", + "specifier": "eslint-plugin-jsdoc" + }, + "oxlint-plugin-eslint" + ], + "rules": { + "@stylistic/indent": [ + "warn", + 2 + ], + "@stylistic/semi": [ + "warn", + "always" + ], + "@stylistic/quotes": [ + "warn", + "single", + { + "allowTemplateLiterals": "always" + } + ], + "@stylistic/member-delimiter-style": [ + "warn", + { + "multiline": { + "delimiter": "semi", + "requireLast": true + }, + "singleline": { + "delimiter": "comma", + "requireLast": false + } + } + ], + "@stylistic/type-annotation-spacing": "warn", + "@stylistic/keyword-spacing": "warn", + "@stylistic/max-len": [ + "warn", + { + "code": 1000, + "comments": 80, + "ignoreUrls": true, + "ignorePattern": "^ *(?\\* Ps=)" + } + ], + "@stylistic/no-extra-semi": "error", + "@stylistic/no-trailing-spaces": "warn", + "eslint-js/object-curly-spacing": [ + "warn", + "always" + ], + "@stylistic/spaced-comment": [ + "warn", + "always", + { + "markers": [ + "/" + ], + "exceptions": [ + "-" + ] + } + ], + "@stylistic/comma-dangle": [ + "warn", + { + "objects": "never", + "arrays": "never", + "functions": "never" + } + ], + "@stylistic/eol-last": "warn", + "curly": [ + "warn", + "multi-line" + ], + "eqeqeq": [ + "warn", + "always" + ], + "jsdoc-js/check-alignment": "warn", + "jsdoc-js/check-param-names": "warn", + "no-irregular-whitespace": "warn", + "typescript/array-type": [ + "warn", + { + "default": "array", + "readonly": "generic" + } + ], + "typescript/explicit-function-return-type": [ + "warn", + { + "allowExpressions": true + } + ], + "typescript/prefer-namespace-keyword": "warn" + } + } + ] +} diff --git a/AGENTS.md b/AGENTS.md index 5b07ce824..e32bae54c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,6 +26,7 @@ npm run build && npm run esbuild # Build all TypeScript and bundle - Integration tests: `npm run test-integration` (Playwright across Chrome/Firefox/WebKit) - Integration tests by file: `npm run test-integration -- test/playwright/InputHandler.test.ts`. Never use grep to filter tests, it doesn't work - Integration tests by addon: `npm run test-integration -- --suite=addon-search`. Suites always follow the format `addon-` +- Lint: `npm run lint` (oxlint with type-aware rules, then ESLint for `naming-convention` only), `npm run lint-api` for `typings/`, `npm run lint-fix` for oxlint auto-fix - Lint changes: `npm run lint-changes` to lint only changed files, `npm run lint-changes-fix` to fix them ## Addon Development Pattern diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index c35299809..cf8dd2493 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -5,7 +5,7 @@ import { CustomGlyphDefinitionType, CustomGlyphScaleType, CustomGlyphVectorType, type CustomGlyphCharacterDefinition, type CustomGlyphDefinitionPart, type CustomGlyphPathDrawFunctionDefinition } from './Types'; -/* eslint-disable max-len */ +/* oxlint-disable @stylistic/max-len -- generated glyph path data */ const enum Shapes { /** │ */ TOP_TO_BOTTOM = 'M.5,0 L.5,1', diff --git a/bin/lint_changes.js b/bin/lint_changes.js index 6ed7ae5fa..b7571c5e3 100644 --- a/bin/lint_changes.js +++ b/bin/lint_changes.js @@ -33,17 +33,47 @@ if (files.length === 0) { console.log(`Linting ${files.length} changed file(s)...`); -const eslintArgs = ['--max-warnings', '0', '--no-warn-ignored']; -if (fix) { - eslintArgs.push('--fix'); +const cwd = path.join(__dirname, '..'); +const isWin = process.platform === 'win32'; + +/** + * @param {string} command + * @param {string[]} args + * @returns {Promise} + */ +function run(command, args) { + return new Promise(/** @type {(resolve: () => void, reject: (reason: Error) => void) => void} */ ((resolve, reject) => { + const child = spawn(command, args, { + stdio: 'inherit', + cwd, + shell: isWin + }); + child.on('close', code => { + if (code === 0) { + resolve(); + } else { + reject(new Error(`${command} exited with code ${code}`)); + } + }); + })); } -eslintArgs.push(...files); -const eslint = process.platform === 'win32' ? 'eslint.cmd' : 'eslint'; -const child = spawn(eslint, eslintArgs, { - stdio: 'inherit', - cwd: path.join(__dirname, '..'), - shell: process.platform === 'win32' +async function main() { + const oxlintArgs = ['--type-aware', '--max-warnings', '0']; + if (fix) { + oxlintArgs.push('--fix'); + } + oxlintArgs.push(...files); + + const oxlint = isWin ? 'oxlint.cmd' : 'oxlint'; + await run(oxlint, oxlintArgs); + + const eslintArgs = ['--config', 'eslint.config.naming.mjs', '--max-warnings', '0', ...files]; + const eslint = isWin ? 'eslint.cmd' : 'eslint'; + await run(eslint, eslintArgs); +} + +main().catch(err => { + console.error(err.message); + process.exit(1); }); - -child.on('close', code => process.exit(code ?? 0)); diff --git a/demo/client/components/window/gpuWindow.ts b/demo/client/components/window/gpuWindow.ts index ac7b2c3db..382056aba 100644 --- a/demo/client/components/window/gpuWindow.ts +++ b/demo/client/components/window/gpuWindow.ts @@ -43,7 +43,7 @@ export class GpuWindow extends BaseWindow implements IControlWindow { } private _styleAtlasPage(canvas: HTMLCanvasElement): void { - // eslint-disable-next-line no-restricted-syntax + // oxlint-disable-next-line eslint-js/no-restricted-syntax const dpr = window.devicePixelRatio; canvas.style.width = `${canvas.width / dpr}px`; canvas.style.height = `${canvas.height / dpr}px`; diff --git a/demo/client/components/window/webglWindow.ts b/demo/client/components/window/webglWindow.ts index b61be231f..2886c3b57 100644 --- a/demo/client/components/window/webglWindow.ts +++ b/demo/client/components/window/webglWindow.ts @@ -43,7 +43,7 @@ export class WebglWindow extends BaseWindow implements IControlWindow { } private _styleAtlasPage(canvas: HTMLCanvasElement): void { - // eslint-disable-next-line no-restricted-syntax + // oxlint-disable-next-line eslint-js/no-restricted-syntax const dpr = window.devicePixelRatio; canvas.style.width = `${canvas.width / dpr}px`; canvas.style.height = `${canvas.height / dpr}px`; diff --git a/eslint.config.naming.mjs b/eslint.config.naming.mjs new file mode 100644 index 000000000..043f903c4 --- /dev/null +++ b/eslint.config.naming.mjs @@ -0,0 +1,71 @@ +// @ts-check +import tseslint from 'typescript-eslint'; + +const ignores = [ + 'addons/*/src/third-party/*.ts', + '**/out/*', + '**/out-test/*', + '**/out-esbuild/*', + '**/out-esbuild-test/*', + '**/inwasm-sdks/*', + '**/node_modules', + '**/*.js', + '**/*.mjs' +]; + +const namingConventionMain = [ + 'warn', + { selector: 'default', format: ['camelCase'], filter: { regex: '^[a-z]', match: true } }, + { selector: 'variable', format: ['camelCase', 'UPPER_CASE'] }, + { selector: 'variable', filter: '^I.+Service$', format: ['PascalCase'], prefix: ['I'] }, + { selector: 'memberLike', modifiers: ['private'], format: ['camelCase'], leadingUnderscore: 'require' }, + { selector: 'memberLike', modifiers: ['protected'], format: ['camelCase'], leadingUnderscore: 'require' }, + { selector: 'enumMember', format: ['UPPER_CASE'] }, + { selector: 'property', modifiers: ['public'], format: ['camelCase', 'UPPER_CASE'], filter: { regex: '^[a-z]', match: true } }, + { selector: 'method', modifiers: ['public'], format: ['camelCase', 'UPPER_CASE'], custom: { regex: '^on[A-Z].+', match: false } }, + { selector: 'method', modifiers: ['private'], format: ['camelCase'], leadingUnderscore: 'require', custom: { regex: '^on[A-Z].+', match: false } }, + { selector: 'method', modifiers: ['protected'], format: ['camelCase'], leadingUnderscore: 'require', custom: { regex: '^on[A-Z].+', match: false } }, + { selector: 'typeLike', format: ['PascalCase'] }, + { selector: 'interface', format: ['PascalCase'], prefix: ['I'] } +]; + +const namingConventionTypings = [ + 'warn', + { selector: 'typeLike', format: ['PascalCase'] }, + { selector: 'interface', format: ['PascalCase'], prefix: ['I'] } +]; + +export default tseslint.config( + { + ignores + }, + { + files: ['**/*.ts'], + ignores: ['typings/**/*.d.ts', 'addons/**/typings/**'], + plugins: { + '@typescript-eslint': tseslint.plugin + }, + languageOptions: { + parser: tseslint.parser, + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname + } + }, + rules: { + '@typescript-eslint/naming-convention': namingConventionMain + } + }, + { + files: ['typings/**/*.d.ts'], + plugins: { + '@typescript-eslint': tseslint.plugin + }, + languageOptions: { + parser: tseslint.parser + }, + rules: { + '@typescript-eslint/naming-convention': namingConventionTypings + } + } +); diff --git a/package-lock.json b/package-lock.json index 4c3c6f552..c3d4c0f77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "esbuild": "^0.28.0", "eslint": "^10.3.0", "eslint-plugin-jsdoc": "^62.9.0", + "eslint-plugin-oxlint": "^1.67.0", "express": "^5.2.1", "express-ws": "^5.0.2", "jsdom": "^29.1.1", @@ -39,6 +40,9 @@ "mustache": "^4.2.0", "node-pty": "^1.2.0-beta.9", "nyc": "^18.0.0", + "oxlint": "^1.67.0", + "oxlint-plugin-eslint": "^1.67.0", + "oxlint-tsgolint": "^0.23.0", "source-map-loader": "^5.0.0", "source-map-support": "^0.5.20", "ts-loader": "^9.5.7", @@ -1508,6 +1512,413 @@ "pako": "^2.0.4" } }, + "node_modules/@oxlint-tsgolint/darwin-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/darwin-arm64/-/darwin-arm64-0.23.0.tgz", + "integrity": "sha512-gOs9PVr2wEg4ox9z0aJo+RKhhImW86YL5N6yav8BK/rgPsIrwN/igSZ+pbRr723NFvUNKde9fgMhRA6JrXAOZw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxlint-tsgolint/darwin-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/darwin-x64/-/darwin-x64-0.23.0.tgz", + "integrity": "sha512-kjJ8B+7n4tB9VJdxS5A9GdJt6/bYpzbu4lXp2uO1S3sRmCB5gDEABlGoiePNApRWaW+xqL4b4xgiE727jSLhuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxlint-tsgolint/linux-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/linux-arm64/-/linux-arm64-0.23.0.tgz", + "integrity": "sha512-6dCZuKNu135seMXilkRk9SpCx6i1XgmiipYGalLij5WVRX6ZYS8c4xI7preN/zv9fCXhsQclTIMDu2Y/cytTjw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint-tsgolint/linux-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/linux-x64/-/linux-x64-0.23.0.tgz", + "integrity": "sha512-3bdilnyA7kmSTjK27rvjIjSxL5SIg3wt7vwNiRkouWB83ytssyKnuGvxSYJxgMEmFpSutzaBzcCUM2jDtPGcgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint-tsgolint/win32-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/win32-arm64/-/win32-arm64-0.23.0.tgz", + "integrity": "sha512-j+OEp44SVYiQ+ZD+uttsX7u6L9SvmbbQ77SO1pSFCcJlsVMeCk8qZsjhKfGKuT/jIA+ipOJMVs/+pqUfObBWNw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxlint-tsgolint/win32-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/win32-x64/-/win32-x64-0.23.0.tgz", + "integrity": "sha512-5MyjFuqf+g8OUPJBSGWHJtmoWnzFJYyOg4To9WMQshZYEWig/vtu7JtJ03VWnzHv9LJkAUeApY0gVCOywFR/iQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxlint/binding-android-arm-eabi": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.67.0.tgz", + "integrity": "sha512-VrSi571rDv1N8HaEDM+DEX8nmT0y9jJo8tzzW13vsOWTx59xQczCIJx68n2zWOXRT5YKZsOZXp4qkHN/10x4mw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-android-arm64": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.67.0.tgz", + "integrity": "sha512-l6+NdYxMoRohix5r5bbigW16LPicceCwGcQ6LKKuE1kUdjgFfQolJjrJsQYPFetIs78Gxj/G/f5TEGoTCwj9nQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-arm64": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.67.0.tgz", + "integrity": "sha512-jOzXxS1AxFxhImLIRbtGIMrEwaXcgMw3gR57WB1cRk8ai+vpr6726kxXqVvlNsrXtJ/FrmOm8RxlC0m8SW24Qg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-x64": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.67.0.tgz", + "integrity": "sha512-3DFAVY94OqjIZHXIPz37yGRSWwOFTAqChQ64/M69GYLawzP0KiwdhDNfqdKKYT0bTR/DNxmMnQsj3ns+8+X/Lg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-freebsd-x64": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.67.0.tgz", + "integrity": "sha512-e4dDKZuLu8TR9DEBssWSDahlPgZBwojTTHZUvnjBRJfJJbpxYCjfjKfi0Z1+CSLMiJBwI2yCDtRM1XJQaARjmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-gnueabihf": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.67.0.tgz", + "integrity": "sha512-BKytFdcQzbITV3xlnzDUDTEDtbUMCCiC4EaNTDZ4FyT8gdNvBC4gfiLucXp/sQl0XU3p7syTlorUWVVVBZab2g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-musleabihf": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.67.0.tgz", + "integrity": "sha512-XYAv0esBDX7BpTzRDjVX2Vdj+zndd8ll2dFQiaeQ6zTZr7A8GRDTN7fH3FP3jU+O0vCDx85oH/EtG7BzPgAXuw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-gnu": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.67.0.tgz", + "integrity": "sha512-zizRMjA0i6u/2B0evgda04iycu+MoNuf1pBy6Eh+1CjC5wMEG7qN5zdDKTCvFc0KSYSDM9QTG3gjZHirgtQuKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-musl": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.67.0.tgz", + "integrity": "sha512-zB/Tf6sUjmmvvbva9Gj3JTJ8rJ9t4I8/U0o6vSRtd0DRIsIuyegBwJAzhSUFQHdMijIRJkW0exs/yBhpw2S20w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-ppc64-gnu": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.67.0.tgz", + "integrity": "sha512-kgU40Gt74CK0TCsF51KZymkIwN9U0BajKsMijB52zPqOeZU9NAHkA/NSQkZDHEaCakx42DxhXkODiAqf2b4Gug==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-gnu": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.67.0.tgz", + "integrity": "sha512-tOYhkk/iaG9aD3FvGpBFd1Lrw0x0RaVoJBxjUkfNzS50rC5NS5BteNCwgr8A2zCdADrIIoze6D7u6U5Ic++/iQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-musl": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.67.0.tgz", + "integrity": "sha512-sEtywrPb+0b+tHYl1SDCrw903fiC4eyKoNqzP3v+f2JT3Xcv4NEYG+P8rj+eEnX7IWhqV/xj8/JmcmVj21CXaA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-s390x-gnu": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.67.0.tgz", + "integrity": "sha512-BvR8Moa0zCLxroOx4vZaZN9nUfwAUpSTwjZdxZyKy4bv3PrzrXrxKR/ZQ0L9wNSvlPhnMJeZfa3q5w6ZCTuN6Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-gnu": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.67.0.tgz", + "integrity": "sha512-mm2cxM6fksOpq6l0uFws8BUGKAR4dNa/cZCn37Npq7PFbhD5HDJqWfnoIvTaeRKMy5XdS2tO0MA0qbHDrnXAAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-musl": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.67.0.tgz", + "integrity": "sha512-WmbMuLapKyDlobMkXAaAL0Y+Uczh4LETfIfQsUpbId4Ip8Ai82/jqeYTOoUCkuuhBFapgqP253+d83tLKOksJg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-openharmony-arm64": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.67.0.tgz", + "integrity": "sha512-9g/PqxYJelzzTAOR5Y+RiRqdeydhEuXv2KxNeFcAKQ7UsvnWSY1OP4MsuPMbTO2Pf70tz7mFhl1j13H3fyh+8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-arm64-msvc": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.67.0.tgz", + "integrity": "sha512-2VhwE6Gatb0vJGnN0TBuQMbKCOiZlSQ/zJvVWYLK4a9d4iDiJOen/yVQkGpmsJ90MuH66fzi0kEKI0jRQMDxGA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-ia32-msvc": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.67.0.tgz", + "integrity": "sha512-EQ3VExXfeM1InbE5+JjufhZZTWy+kHUwgt3yZR7gQ47Je/mE0WspQPan0OJznh493L5anM210YNJtH1PXjTSFg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-x64-msvc": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.67.0.tgz", + "integrity": "sha512-bw24y+/1MHS4QDkons3YyHkPT9uCMoLHHgQhb+mb8NOjTYwub1CZ+K9Ngr8aO5DMrDrkqHwTzlTwFP2vS8Y/ZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -3652,6 +4063,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-plugin-oxlint": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-oxlint/-/eslint-plugin-oxlint-1.67.0.tgz", + "integrity": "sha512-tS/RGy8x2+rIUp7gV8ICgEeSAgFH6Qg7phzCbUblW/dPUsqXkcedrzO0nOaKhce4fvUVDR9uTmWlLkCNSDKcsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonc-parser": "^3.3.1" + }, + "peerDependencies": { + "oxlint": "~1.67.0" + } + }, "node_modules/eslint-scope": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", @@ -5045,6 +5469,13 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -5797,6 +6228,86 @@ "node": ">= 0.8.0" } }, + "node_modules/oxlint": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.67.0.tgz", + "integrity": "sha512-blwwaHPdoH8piQ5/z0KHeoHFR7FZgl12WluKJfu4qFLPkZl6mK04PkLE45Fw1NxfBRSlh40Gu7MkxHUw++ociQ==", + "dev": true, + "license": "MIT", + "bin": { + "oxlint": "bin/oxlint" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxlint/binding-android-arm-eabi": "1.67.0", + "@oxlint/binding-android-arm64": "1.67.0", + "@oxlint/binding-darwin-arm64": "1.67.0", + "@oxlint/binding-darwin-x64": "1.67.0", + "@oxlint/binding-freebsd-x64": "1.67.0", + "@oxlint/binding-linux-arm-gnueabihf": "1.67.0", + "@oxlint/binding-linux-arm-musleabihf": "1.67.0", + "@oxlint/binding-linux-arm64-gnu": "1.67.0", + "@oxlint/binding-linux-arm64-musl": "1.67.0", + "@oxlint/binding-linux-ppc64-gnu": "1.67.0", + "@oxlint/binding-linux-riscv64-gnu": "1.67.0", + "@oxlint/binding-linux-riscv64-musl": "1.67.0", + "@oxlint/binding-linux-s390x-gnu": "1.67.0", + "@oxlint/binding-linux-x64-gnu": "1.67.0", + "@oxlint/binding-linux-x64-musl": "1.67.0", + "@oxlint/binding-openharmony-arm64": "1.67.0", + "@oxlint/binding-win32-arm64-msvc": "1.67.0", + "@oxlint/binding-win32-ia32-msvc": "1.67.0", + "@oxlint/binding-win32-x64-msvc": "1.67.0" + }, + "peerDependencies": { + "oxlint-tsgolint": ">=0.22.1", + "vite-plus": "*" + }, + "peerDependenciesMeta": { + "oxlint-tsgolint": { + "optional": true + }, + "vite-plus": { + "optional": true + } + } + }, + "node_modules/oxlint-plugin-eslint": { + "version": "1.67.0", + "resolved": "https://registry.npmjs.org/oxlint-plugin-eslint/-/oxlint-plugin-eslint-1.67.0.tgz", + "integrity": "sha512-MfpO9t/l/1qWVKym4sTkz0bhErAkUn/cOOQsFegI5ngQkR1cZEp6G23FKIhGcALoYytPBRmu47yjiEa6XWFBRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/oxlint-tsgolint": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/oxlint-tsgolint/-/oxlint-tsgolint-0.23.0.tgz", + "integrity": "sha512-3mBv3CoPbh8dFbzfDGIWa2ytZjn2v+3EX4aKRXjIhsoGFzG8GCjfRirz3rwZf1wYbZzsNLTSgpw8VjQuWdp/jA==", + "dev": true, + "license": "MIT", + "bin": { + "tsgolint": "bin/tsgolint.js" + }, + "optionalDependencies": { + "@oxlint-tsgolint/darwin-arm64": "0.23.0", + "@oxlint-tsgolint/darwin-x64": "0.23.0", + "@oxlint-tsgolint/linux-arm64": "0.23.0", + "@oxlint-tsgolint/linux-x64": "0.23.0", + "@oxlint-tsgolint/win32-arm64": "0.23.0", + "@oxlint-tsgolint/win32-x64": "0.23.0" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", diff --git a/package.json b/package.json index ae4e9fb87..43e569d02 100644 --- a/package.json +++ b/package.json @@ -48,11 +48,11 @@ "esbuild-demo-server-watch": "node bin/esbuild.mjs --demo-server --watch", "test": "npm run test-unit", "posttest": "npm run lint", - "lint": "eslint --max-warnings 0 src/ addons/ demo/ test/", + "lint": "oxlint --type-aware --max-warnings 0 src addons demo test && eslint --config eslint.config.naming.mjs --max-warnings 0 src addons demo test", "lint-changes": "node ./bin/lint_changes.js", "lint-changes-fix": "node ./bin/lint_changes.js --fix", - "lint-fix": "eslint --fix src/ addons/ demo/ test/", - "lint-api": "eslint --config eslint.config.typings.mjs --max-warnings 0 typings/", + "lint-fix": "oxlint --type-aware --fix --max-warnings 0 src addons demo test", + "lint-api": "oxlint --max-warnings 0 -c .oxlintrc.typings.json typings && eslint --config eslint.config.naming.mjs --max-warnings 0 typings", "test-unit": "node ./bin/test_unit.js", "test-unit-slow-tests": "npm run test-unit | grep \"ms)\"", "test-unit-coverage": "node ./bin/test_unit.js --coverage", @@ -95,6 +95,7 @@ "esbuild": "^0.28.0", "eslint": "^10.3.0", "eslint-plugin-jsdoc": "^62.9.0", + "eslint-plugin-oxlint": "^1.67.0", "express": "^5.2.1", "express-ws": "^5.0.2", "jsdom": "^29.1.1", @@ -102,6 +103,9 @@ "mustache": "^4.2.0", "node-pty": "^1.2.0-beta.9", "nyc": "^18.0.0", + "oxlint": "^1.67.0", + "oxlint-plugin-eslint": "^1.67.0", + "oxlint-tsgolint": "^0.23.0", "source-map-loader": "^5.0.0", "source-map-support": "^0.5.20", "ts-loader": "^9.5.7", diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts index 7d2e8ed9b..baa5af801 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.ts @@ -231,7 +231,6 @@ export class DomRendererRowFactory { } charElement = this._document.createElement('span'); cellAmount = 0; - // eslint-disable-next-line no-useless-assignment text = ''; } } From 56a7195487131fa3200414799b7cb3109d6bbd4a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 10:08:04 -0700 Subject: [PATCH 081/153] Add oxlint import/no-cycle and break existing src/common cycles Enable oxlint's import/no-cycle rule (import plugin) so circular module dependencies are caught in CI, similar to eslint-plugin-import/no-cycle and biome no-import-cycles discussed in the issue. Relocate shared types and dependencies to remove cycles under src/common: - Types.ts: host IInsertEvent, IDeleteEvent, IParams, UnderlineStyle, IBuffer, and IBufferSet; drop imports from CircularList, buffer/Types, parser/Types, and Services - CoreTerminal.ts: define and export ICoreTerminal - ServiceRegistry.ts: define IServiceIdentifier (break Services registry loop) - UnicodeService: stop constructing UnicodeV6; CoreTerminal registers the default v6 provider after service creation - buffer/Types.ts and parser/Types.ts: re-export moved types for callers Fixes https://github.com/xtermjs/xterm.js/issues/5897 Co-authored-by: Cursor --- .oxlintrc.json | 4 +- src/browser/Types.ts | 3 +- src/common/CircularList.ts | 12 +-- src/common/CoreTerminal.ts | 22 +++- src/common/Types.ts | 106 ++++++++++++++++---- src/common/buffer/Constants.ts | 9 +- src/common/buffer/Types.ts | 49 +-------- src/common/input/UnicodeV6.ts | 1 + src/common/parser/Types.ts | 28 +----- src/common/public/BufferNamespaceApi.ts | 2 +- src/common/public/ParserApi.ts | 2 +- src/common/public/UnicodeApi.ts | 2 +- src/common/services/InstantiationService.ts | 4 +- src/common/services/ServiceRegistry.ts | 6 +- src/common/services/Services.ts | 8 +- src/common/services/UnicodeService.test.ts | 2 + src/common/services/UnicodeService.ts | 13 +-- test/playwright/TestUtils.ts | 3 +- 18 files changed, 139 insertions(+), 137 deletions(-) diff --git a/.oxlintrc.json b/.oxlintrc.json index 7d9cabfc5..237e00028 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -1,6 +1,7 @@ { "$schema": "./node_modules/oxlint/configuration_schema.json", "plugins": [ + "import", "typescript" ], "categories": { @@ -104,7 +105,8 @@ "typescript/no-wrapper-object-types": "error", "typescript/prefer-as-const": "error", "typescript/prefer-namespace-keyword": "error", - "typescript/triple-slash-reference": "error" + "typescript/triple-slash-reference": "error", + "import/no-cycle": "error" }, "overrides": [ { diff --git a/src/browser/Types.ts b/src/browser/Types.ts index 126857e17..0c454c118 100644 --- a/src/browser/Types.ts +++ b/src/browser/Types.ts @@ -3,7 +3,8 @@ * @license MIT */ -import { CharData, IColor, ICoreTerminal, ITerminalOptions } from '../common/Types'; +import { CharData, IColor, ITerminalOptions } from '../common/Types'; +import { ICoreTerminal } from '../common/CoreTerminal'; import { IBuffer } from '../common/buffer/Types'; import { IDisposable, IRenderDimensions as IRenderDimensionsApi, Terminal as ITerminalApi } from '@xterm/xterm'; import { channels, css } from '../common/Color'; diff --git a/src/common/CircularList.ts b/src/common/CircularList.ts index f0b12d6a2..21dd081da 100644 --- a/src/common/CircularList.ts +++ b/src/common/CircularList.ts @@ -3,19 +3,11 @@ * @license MIT */ -import { ICircularList } from './Types'; +import { ICircularList, IDeleteEvent, IInsertEvent } from './Types'; import { Disposable } from './Lifecycle'; import { Emitter } from './Event'; -export interface IInsertEvent { - index: number; - amount: number; -} - -export interface IDeleteEvent { - index: number; - amount: number; -} +export type { IDeleteEvent, IInsertEvent } from './Types'; /** * Represents a circular list; a list with a maximum size that wraps around when push is called, diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index 2cc48b03a..920de796f 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -21,18 +21,19 @@ * http://linux.die.net/man/7/urxvt */ -import { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService, IMouseStateService, IUnicodeService, LogLevelEnum, ITerminalOptions, IOscLinkService } from './services/Services'; +import { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService, IMouseStateService, IUnicodeService, LogLevelEnum, IOscLinkService } from './services/Services'; import { InstantiationService } from './services/InstantiationService'; import { LogService } from './services/LogService'; import { BufferService, BufferServiceConstants } from './services/BufferService'; import { OptionsService } from './services/OptionsService'; -import { IDisposable, IAttributeData, ICoreTerminal, IScrollEvent } from './Types'; +import { IDisposable, IAttributeData, IScrollEvent, ITerminalOptions, IParams } from './Types'; import { CoreService } from './services/CoreService'; import { MouseStateService } from './services/MouseStateService'; +import { UnicodeV6 } from './input/UnicodeV6'; import { UnicodeService } from './services/UnicodeService'; import { CharsetService } from './services/CharsetService'; import { updateWindowsModeWrappedState } from './WindowsMode'; -import { IFunctionIdentifier, IParams } from './parser/Types'; +import { IFunctionIdentifier } from './parser/Types'; import { IBufferSet } from './buffer/Types'; import { InputHandler } from './InputHandler'; import { WriteBuffer } from './input/WriteBuffer'; @@ -43,6 +44,20 @@ import { Disposable, MutableDisposable, toDisposable } from './Lifecycle'; // Only trigger this warning a single time per session let hasWriteSyncWarnHappened = false; +export interface ICoreTerminal { + mouseStateService: IMouseStateService; + coreService: ICoreService; + optionsService: IOptionsService; + unicodeService: IUnicodeService; + buffers: IBufferSet; + options: Required; + registerCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean | Promise): IDisposable; + registerDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean | Promise): IDisposable; + registerEscHandler(id: IFunctionIdentifier, callback: () => boolean | Promise): IDisposable; + registerOscHandler(ident: number, callback: (data: string) => boolean | Promise): IDisposable; + registerApcHandler(id: IFunctionIdentifier, callback: (data: string) => boolean | Promise): IDisposable; +} + export abstract class CoreTerminal extends Disposable implements ICoreTerminal { protected readonly _instantiationService: IInstantiationService; protected readonly _bufferService: IBufferService; @@ -116,6 +131,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal { this.mouseStateService = this._register(this._instantiationService.createInstance(MouseStateService)); this._instantiationService.setService(IMouseStateService, this.mouseStateService); this.unicodeService = this._register(this._instantiationService.createInstance(UnicodeService)); + this.unicodeService.register(new UnicodeV6()); this._instantiationService.setService(IUnicodeService, this.unicodeService); this._charsetService = this._instantiationService.createInstance(CharsetService); this._instantiationService.setService(ICharsetService, this._charsetService); diff --git a/src/common/Types.ts b/src/common/Types.ts index 11c590b12..5b7b42bb2 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -3,26 +3,51 @@ * @license MIT */ -import { IDeleteEvent, IInsertEvent } from './CircularList'; -import { UnderlineStyle } from './buffer/Constants'; -import { IBufferSet } from './buffer/Types'; -import { IParams } from './parser/Types'; -import { IMouseStateService, ICoreService, IOptionsService, IUnicodeService } from './services/Services'; import { IFunctionIdentifier, ITerminalOptions as IPublicTerminalOptions } from '@xterm/xterm'; import type { Emitter, IEvent } from './Event'; -export interface ICoreTerminal { - mouseStateService: IMouseStateService; - coreService: ICoreService; - optionsService: IOptionsService; - unicodeService: IUnicodeService; - buffers: IBufferSet; - options: Required; - registerCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean | Promise): IDisposable; - registerDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean | Promise): IDisposable; - registerEscHandler(id: IFunctionIdentifier, callback: () => boolean | Promise): IDisposable; - registerOscHandler(ident: number, callback: (data: string) => boolean | Promise): IDisposable; - registerApcHandler(id: IFunctionIdentifier, callback: (data: string) => boolean | Promise): IDisposable; +export interface IInsertEvent { + index: number; + amount: number; +} + +export interface IDeleteEvent { + index: number; + amount: number; +} + +/** sequence params serialized to js arrays */ +export type ParamsArray = (number | number[])[]; + +/** Interface of Params storage class. */ +export interface IParams { + /** from ctor */ + maxLength: number; + maxSubParamsLength: number; + + /** param values and its length */ + params: Int32Array; + length: number; + + /** methods */ + clone(): IParams; + toArray(): ParamsArray; + reset(): void; + resetZdm(): void; + addParam(value: number): void; + addSubParam(value: number): void; + hasSubParams(idx: number): boolean; + getSubParams(idx: number): Int32Array | null; + getSubParamsAll(): {[idx: number]: Int32Array}; +} + +export const enum UnderlineStyle { + NONE = 0, + SINGLE = 1, + DOUBLE = 2, + CURLY = 3, + DOTTED = 4, + DASHED = 5 } export interface IDisposable { @@ -260,6 +285,53 @@ export interface IMarker extends IDisposable { readonly line: number; onDispose: IEvent; } + +export interface IBuffer { + readonly lines: ICircularList; + ydisp: number; + ybase: number; + y: number; + x: number; + tabs: any; + scrollBottom: number; + scrollTop: number; + hasScrollback: boolean; + savedY: number; + savedX: number; + savedCharset: ICharset | undefined; + savedCharsets: (ICharset | undefined)[]; + savedGlevel: number; + savedOriginMode: boolean; + savedWraparoundMode: boolean; + savedCurAttrData: IAttributeData; + isCursorInViewport: boolean; + markers: IMarker[]; + translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string; + getWrappedRangeForLine(y: number): { first: number, last: number }; + nextStop(x?: number): number; + prevStop(x?: number): number; + getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine; + getNullCell(attr?: IAttributeData): ICellData; + getWhitespaceCell(attr?: IAttributeData): ICellData; + addMarker(y: number): IMarker; + clearMarkers(y: number): void; + clearAllMarkers(): void; +} + +export interface IBufferSet extends IDisposable { + alt: IBuffer; + normal: IBuffer; + active: IBuffer; + + onBufferActivate: IEvent<{ activeBuffer: IBuffer, inactiveBuffer: IBuffer }>; + + activateNormalBuffer(): void; + activateAltBuffer(fillAttr?: IAttributeData): void; + reset(): void; + resize(newCols: number, newRows: number): void; + setupTabStops(i?: number): void; +} + export interface IModes { insertMode: boolean; } diff --git a/src/common/buffer/Constants.ts b/src/common/buffer/Constants.ts index 5ce075cf7..12b2d7ad4 100644 --- a/src/common/buffer/Constants.ts +++ b/src/common/buffer/Constants.ts @@ -147,11 +147,4 @@ export const enum ExtFlags { VARIANT_OFFSET = 0xE0000000 } -export const enum UnderlineStyle { - NONE = 0, - SINGLE = 1, - DOUBLE = 2, - CURLY = 3, - DOTTED = 4, - DASHED = 5 -} +export { UnderlineStyle } from '../Types'; diff --git a/src/common/buffer/Types.ts b/src/common/buffer/Types.ts index 0cf448fcb..d5bee5efd 100644 --- a/src/common/buffer/Types.ts +++ b/src/common/buffer/Types.ts @@ -3,54 +3,7 @@ * @license MIT */ -import { IAttributeData, ICircularList, IBufferLine, ICellData, IMarker, ICharset, IDisposable } from '../Types'; -import type { IEvent } from '../Event'; +export type { IBuffer, IBufferSet } from '../Types'; // BufferIndex denotes a position in the buffer: [rowIndex, colIndex] export type BufferIndex = [number, number]; - -export interface IBuffer { - readonly lines: ICircularList; - ydisp: number; - ybase: number; - y: number; - x: number; - tabs: any; - scrollBottom: number; - scrollTop: number; - hasScrollback: boolean; - savedY: number; - savedX: number; - savedCharset: ICharset | undefined; - savedCharsets: (ICharset | undefined)[]; - savedGlevel: number; - savedOriginMode: boolean; - savedWraparoundMode: boolean; - savedCurAttrData: IAttributeData; - isCursorInViewport: boolean; - markers: IMarker[]; - translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string; - getWrappedRangeForLine(y: number): { first: number, last: number }; - nextStop(x?: number): number; - prevStop(x?: number): number; - getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine; - getNullCell(attr?: IAttributeData): ICellData; - getWhitespaceCell(attr?: IAttributeData): ICellData; - addMarker(y: number): IMarker; - clearMarkers(y: number): void; - clearAllMarkers(): void; -} - -export interface IBufferSet extends IDisposable { - alt: IBuffer; - normal: IBuffer; - active: IBuffer; - - onBufferActivate: IEvent<{ activeBuffer: IBuffer, inactiveBuffer: IBuffer }>; - - activateNormalBuffer(): void; - activateAltBuffer(fillAttr?: IAttributeData): void; - reset(): void; - resize(newCols: number, newRows: number): void; - setupTabStops(i?: number): void; -} diff --git a/src/common/input/UnicodeV6.ts b/src/common/input/UnicodeV6.ts index c872bc441..617b49006 100644 --- a/src/common/input/UnicodeV6.ts +++ b/src/common/input/UnicodeV6.ts @@ -132,6 +132,7 @@ export class UnicodeV6 implements IUnicodeVersionProvider { public charProperties(codepoint: number, preceding: UnicodeCharProperties): UnicodeCharProperties { let width = this.wcwidth(codepoint); let shouldJoin = width === 0 && preceding !== 0; + // HACK: Ideally this file would not depend on the service which uses it if (shouldJoin) { const oldWidth = UnicodeService.extractWidth(preceding); if (oldWidth === 0) { diff --git a/src/common/parser/Types.ts b/src/common/parser/Types.ts index 2e148c4d1..7306cdf76 100644 --- a/src/common/parser/Types.ts +++ b/src/common/parser/Types.ts @@ -3,12 +3,10 @@ * @license MIT */ -import { IDisposable } from '../Types'; +import { IDisposable, IParams, ParamsArray } from '../Types'; import { ParserState } from './Constants'; - -/** sequence params serialized to js arrays */ -export type ParamsArray = (number | number[])[]; +export type { IParams, ParamsArray }; /** Params constructor type. */ export interface IParamsConstructor { @@ -18,28 +16,6 @@ export interface IParamsConstructor { fromArray(values: ParamsArray): IParams; } -/** Interface of Params storage class. */ -export interface IParams { - /** from ctor */ - maxLength: number; - maxSubParamsLength: number; - - /** param values and its length */ - params: Int32Array; - length: number; - - /** methods */ - clone(): IParams; - toArray(): ParamsArray; - reset(): void; - resetZdm(): void; - addParam(value: number): void; - addSubParam(value: number): void; - hasSubParams(idx: number): boolean; - getSubParams(idx: number): Int32Array | null; - getSubParamsAll(): {[idx: number]: Int32Array}; -} - /** * Internal state of EscapeSequenceParser. * Used as argument of the error handler to allow diff --git a/src/common/public/BufferNamespaceApi.ts b/src/common/public/BufferNamespaceApi.ts index 9d56587f8..8a914cfcc 100644 --- a/src/common/public/BufferNamespaceApi.ts +++ b/src/common/public/BufferNamespaceApi.ts @@ -5,7 +5,7 @@ import { IBuffer as IBufferApi, IBufferNamespace as IBufferNamespaceApi } from '@xterm/xterm'; import { BufferApiView } from './BufferApiView'; -import { ICoreTerminal } from '../Types'; +import { ICoreTerminal } from '../CoreTerminal'; import { Disposable } from '../Lifecycle'; import { Emitter } from '../Event'; diff --git a/src/common/public/ParserApi.ts b/src/common/public/ParserApi.ts index 02dcc44c9..0b886c058 100644 --- a/src/common/public/ParserApi.ts +++ b/src/common/public/ParserApi.ts @@ -5,7 +5,7 @@ import { IParams } from '../parser/Types'; import { IDisposable, IFunctionIdentifier, IParser } from '@xterm/xterm'; -import { ICoreTerminal } from '../Types'; +import { ICoreTerminal } from '../CoreTerminal'; export class ParserApi implements IParser { constructor(private _core: ICoreTerminal) { } diff --git a/src/common/public/UnicodeApi.ts b/src/common/public/UnicodeApi.ts index fff2665e1..9527dbd6d 100644 --- a/src/common/public/UnicodeApi.ts +++ b/src/common/public/UnicodeApi.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { ICoreTerminal } from '../Types'; +import { ICoreTerminal } from '../CoreTerminal'; import { IUnicodeHandling, IUnicodeVersionProvider } from '@xterm/xterm'; export class UnicodeApi implements IUnicodeHandling { diff --git a/src/common/services/InstantiationService.ts b/src/common/services/InstantiationService.ts index f4dbf53c1..5d2489a86 100644 --- a/src/common/services/InstantiationService.ts +++ b/src/common/services/InstantiationService.ts @@ -9,8 +9,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IInstantiationService, IServiceIdentifier } from './Services'; -import { getServiceDependencies } from './ServiceRegistry'; +import { IInstantiationService } from './Services'; +import { IServiceIdentifier, getServiceDependencies } from './ServiceRegistry'; export class ServiceCollection { diff --git a/src/common/services/ServiceRegistry.ts b/src/common/services/ServiceRegistry.ts index f4565b166..282a7bbe1 100644 --- a/src/common/services/ServiceRegistry.ts +++ b/src/common/services/ServiceRegistry.ts @@ -9,7 +9,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IServiceIdentifier } from './Services'; +export interface IServiceIdentifier { + (...args: any[]): void; + type: T; + _id: string; +} const enum Constants { DI_TARGET = 'di$target', diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 0df3786f6..5308f843d 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -6,7 +6,7 @@ import type { IDecoration, IDecorationOptions, ILinkHandler, ILogger, IWindowsPty, IOverviewRulerOptions } from '@xterm/xterm'; import { CoreMouseEncoding, CoreMouseEventType, CursorInactiveStyle, CursorStyle, IAttributeData, ICharset, IColor, ICoreMouseEvent, ICoreMouseProtocol, IDecPrivateModes, IDisposable, IKittyKeyboardState, IModes, IOscLinkData, IWindowOptions } from '../Types'; import { IBuffer, IBufferSet } from '../buffer/Types'; -import { createDecorator } from './ServiceRegistry'; +import { createDecorator, IServiceIdentifier } from './ServiceRegistry'; import type { Emitter, IEvent } from '../Event'; export const IBufferService = createDecorator('BufferService'); @@ -119,12 +119,6 @@ export interface ICharsetService { setgCharset(g: number, charset: ICharset | undefined): void; } -export interface IServiceIdentifier { - (...args: any[]): void; - type: T; - _id: string; -} - export interface IBrandedService { serviceBrand: undefined; } diff --git a/src/common/services/UnicodeService.test.ts b/src/common/services/UnicodeService.test.ts index d43605542..6d5903fb0 100644 --- a/src/common/services/UnicodeService.test.ts +++ b/src/common/services/UnicodeService.test.ts @@ -4,6 +4,7 @@ */ import { assert } from 'chai'; +import { UnicodeV6 } from '../input/UnicodeV6'; import { UnicodeService } from './UnicodeService'; import { IUnicodeVersionProvider } from './Services'; @@ -21,6 +22,7 @@ describe('unicode provider', () => { let us: UnicodeService; beforeEach(() => { us = new UnicodeService(); + us.register(new UnicodeV6()); }); it('default to V6', () => { assert.equal(us.activeVersion, '6'); diff --git a/src/common/services/UnicodeService.ts b/src/common/services/UnicodeService.ts index 1d4d6cc8f..61283c466 100644 --- a/src/common/services/UnicodeService.ts +++ b/src/common/services/UnicodeService.ts @@ -3,7 +3,6 @@ * @license MIT */ -import { UnicodeV6 } from '../input/UnicodeV6'; import { IUnicodeService, IUnicodeVersionProvider, UnicodeCharProperties, UnicodeCharWidth } from './Services'; import { Emitter } from '../Event'; @@ -12,7 +11,7 @@ export class UnicodeService implements IUnicodeService { private _providers: {[key: string]: IUnicodeVersionProvider} = Object.create(null); private _active: string = ''; - private _activeProvider: IUnicodeVersionProvider; + private _activeProvider!: IUnicodeVersionProvider; private readonly _onChange = new Emitter(); public readonly onChange = this._onChange.event; @@ -30,13 +29,6 @@ export class UnicodeService implements IUnicodeService { return ((state & 0xffffff) << 3) | ((width & 3) << 1) | (shouldJoin?1:0); } - constructor() { - const defaultProvider = new UnicodeV6(); - this.register(defaultProvider); - this._active = defaultProvider.version; - this._activeProvider = defaultProvider; - } - public dispose(): void { this._onChange.dispose(); } @@ -60,6 +52,9 @@ export class UnicodeService implements IUnicodeService { public register(provider: IUnicodeVersionProvider): void { this._providers[provider.version] = provider; + if (!this._active) { + this.activeVersion = provider.version; + } } /** diff --git a/test/playwright/TestUtils.ts b/test/playwright/TestUtils.ts index a27d2d20d..05f3a8e31 100644 --- a/test/playwright/TestUtils.ts +++ b/test/playwright/TestUtils.ts @@ -7,7 +7,8 @@ import { Browser, JSHandle, Page } from '@playwright/test'; import { deepStrictEqual, strictEqual } from 'assert'; import type { IRenderDimensions as IRenderDimensionsInternal } from '../../src/browser/renderer/shared/Types'; import type { IRenderService } from '../../src/browser/services/Services'; -import type { ICoreTerminal, IDisposable, IMarker } from '../../src/common/Types'; +import type { IDisposable, IMarker } from '../../src/common/Types'; +import type { ICoreTerminal } from '../../src/common/CoreTerminal'; import * as playwright from '@playwright/test'; import { IBuffer, IBufferCell, IBufferLine, IBufferNamespace, IBufferRange, IDecoration, IDecorationOptions, IModes, IRenderDimensions, ITerminalInitOnlyOptions, ITerminalOptions, Terminal } from '@xterm/xterm'; From 587856e2ca2e45efe0bf6b29b84ba601fd7ab57c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 31 May 2026 17:08:48 +0000 Subject: [PATCH 082/153] Add mise.toml to .gitignore Co-authored-by: Daniel Imms --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index aa46b451f..4ba4c5fee 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ docs/ npm-debug.log /.idea/ .env +mise.toml build/ .DS_Store yarn.lock From cd3dcc52b26a8b3d18f5756e2fc683ba6cf28a99 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 14:35:08 -0700 Subject: [PATCH 083/153] Colocate buffer types in buffer/Types.ts The import/no-cycle refactor had moved IBuffer, IBufferSet, and related cell/line types into src/common/Types.ts, with buffer/Types.ts only re-exporting them. That inverted the natural module boundary and made buffer code depend on the top-level types hub. Define buffer-owned types in buffer/Types.ts instead: - CharData, IExtendedAttrs, IAttributeData, ICellData, IBufferLine - IMarker, IBuffer, IBufferSet, BufferIndex Restore UnderlineStyle to buffer/Constants.ts (used by IExtendedAttrs) and remove those definitions from Types.ts. buffer/Types.ts imports only what it needs from outside the buffer layer: ICircularList and ICharset from Types, IDisposable from Lifecycle, IEvent from Event, and UnderlineStyle from Constants. Types.ts does not import buffer/Types.ts, so the previous Types <-> buffer cycle stays broken. Update imports across common, browser, headless, addons, and tests to use common/buffer/Types for buffer types rather than common/Types. Co-authored-by: Cursor --- addons/addon-image/src/Types.ts | 3 +- addons/addon-serialize/src/SerializeAddon.ts | 3 +- addons/addon-webgl/src/CellColorResolver.ts | 2 +- addons/addon-webgl/src/WebglRenderer.ts | 2 +- src/browser/OscLinkProvider.test.ts | 3 +- src/browser/OscLinkProvider.ts | 2 +- src/browser/Terminal.test.ts | 2 +- src/browser/TestUtils.test.ts | 4 +- src/browser/Types.ts | 4 +- .../dom/DomRendererRowFactory.test.ts | 2 +- .../renderer/dom/DomRendererRowFactory.ts | 3 +- .../services/CharacterJoinerService.test.ts | 2 +- .../services/CharacterJoinerService.ts | 2 +- src/browser/services/SelectionService.test.ts | 3 +- src/browser/services/SelectionService.ts | 4 +- src/common/CoreTerminal.ts | 4 +- src/common/InputHandler.test.ts | 3 +- src/common/InputHandler.ts | 4 +- src/common/TestUtils.test.ts | 4 +- src/common/Types.ts | 198 ------------------ src/common/buffer/AttributeData.ts | 3 +- src/common/buffer/Buffer.ts | 4 +- src/common/buffer/BufferLine.test.ts | 2 +- src/common/buffer/BufferLine.ts | 2 +- src/common/buffer/BufferReflow.ts | 2 +- src/common/buffer/BufferSet.ts | 3 +- src/common/buffer/CellData.ts | 2 +- src/common/buffer/Constants.ts | 9 +- src/common/buffer/Marker.ts | 4 +- src/common/buffer/Types.ts | 194 ++++++++++++++++- src/common/public/BufferLineApiView.ts | 2 +- src/common/services/BufferService.ts | 3 +- src/common/services/DecorationService.test.ts | 2 +- src/common/services/OscLinkService.ts | 3 +- src/common/services/Services.ts | 4 +- src/headless/Terminal.ts | 4 +- test/playwright/TestUtils.ts | 3 +- 37 files changed, 253 insertions(+), 247 deletions(-) diff --git a/addons/addon-image/src/Types.ts b/addons/addon-image/src/Types.ts index 19a1acca1..53d357a97 100644 --- a/addons/addon-image/src/Types.ts +++ b/addons/addon-image/src/Types.ts @@ -9,7 +9,8 @@ import { IDisposable, IMarker, Terminal } from '@xterm/xterm'; import { Attributes, BgFlags, Content, ExtFlags, UnderlineStyle } from 'common/buffer/Constants'; import type { AttributeData } from 'common/buffer/AttributeData'; import type { IParams, IDcsHandler, IOscHandler, IApcHandler, IEscapeSequenceParser } from 'common/parser/Types'; -import type { IBufferLine, IExtendedAttrs, IInputHandler } from 'common/Types'; +import type { IInputHandler } from 'common/Types'; +import type { IBufferLine, IExtendedAttrs } from 'common/buffer/Types'; import type { ITerminal, ReadonlyColorSet } from 'browser/Types'; import type { IRenderDimensions } from 'browser/renderer/shared/Types'; import type { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; diff --git a/addons/addon-serialize/src/SerializeAddon.ts b/addons/addon-serialize/src/SerializeAddon.ts index 4980c8eda..0aad8e1d6 100644 --- a/addons/addon-serialize/src/SerializeAddon.ts +++ b/addons/addon-serialize/src/SerializeAddon.ts @@ -7,7 +7,8 @@ import type { IBuffer, IBufferCell, IBufferRange, ITerminalAddon, Terminal } from '@xterm/xterm'; import type { IHTMLSerializeOptions, SerializeAddon as ISerializeApi, ISerializeOptions, ISerializeRange } from '@xterm/addon-serialize'; -import { IAttributeData, IColor } from 'common/Types'; +import { IColor } from 'common/Types'; +import { IAttributeData } from 'common/buffer/Types'; import { DEFAULT_ANSI_COLORS } from 'browser/Types'; import { UnderlineStyle } from 'common/buffer/Constants'; diff --git a/addons/addon-webgl/src/CellColorResolver.ts b/addons/addon-webgl/src/CellColorResolver.ts index 3a4661cc3..49fdf5b1b 100644 --- a/addons/addon-webgl/src/CellColorResolver.ts +++ b/addons/addon-webgl/src/CellColorResolver.ts @@ -3,7 +3,7 @@ import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { ReadonlyColorSet } from 'browser/Types'; import { Attributes, BgFlags, ExtFlags, FgFlags, NULL_CELL_CODE, UnderlineStyle } from 'common/buffer/Constants'; import { IDecorationService, IOptionsService } from 'common/services/Services'; -import { ICellData } from 'common/Types'; +import { ICellData } from 'common/buffer/Types'; import { Terminal } from '@xterm/xterm'; import { rgba } from 'common/Color'; import { treatGlyphAsBackgroundColor } from 'browser/renderer/shared/RendererUtils'; diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 9c2f459df..264bfda6e 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -10,7 +10,7 @@ import { CursorBlinkStateManager } from './CursorBlinkStateManager'; import { observeDevicePixelDimensions } from './DevicePixelObserver'; import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types'; import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services'; -import { CharData, IBufferLine, ICellData } from 'common/Types'; +import { CharData, IBufferLine, ICellData } from 'common/buffer/Types'; import { AttributeData } from 'common/buffer/AttributeData'; import { CellData } from 'common/buffer/CellData'; import { Attributes, Content, FgFlags, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants'; diff --git a/src/browser/OscLinkProvider.test.ts b/src/browser/OscLinkProvider.test.ts index 2693f9ddd..0356bcfdc 100644 --- a/src/browser/OscLinkProvider.test.ts +++ b/src/browser/OscLinkProvider.test.ts @@ -8,7 +8,8 @@ import { OscLinkProvider } from './OscLinkProvider'; import { ILink } from './Types'; import { createCellData, MockBufferService, MockOptionsService } from '../common/TestUtils.test'; import { IBufferService, IOscLinkService } from '../common/services/Services'; -import { IBufferLine, IOscLinkData } from '../common/Types'; +import { IOscLinkData } from '../common/Types'; +import { IBufferLine } from '../common/buffer/Types'; class TestOscLinkService implements IOscLinkService { public serviceBrand: any; diff --git a/src/browser/OscLinkProvider.ts b/src/browser/OscLinkProvider.ts index d52d68c08..9fdef1561 100644 --- a/src/browser/OscLinkProvider.ts +++ b/src/browser/OscLinkProvider.ts @@ -6,7 +6,7 @@ import { IBufferRange, ILink } from './Types'; import { ILinkProvider } from './services/Services'; import { CellData } from '../common/buffer/CellData'; -import { IBufferLine } from '../common/Types'; +import { IBufferLine } from '../common/buffer/Types'; import { IBufferService, IOptionsService, IOscLinkService } from '../common/services/Services'; export class OscLinkProvider implements ILinkProvider { diff --git a/src/browser/Terminal.test.ts b/src/browser/Terminal.test.ts index 38165b84f..a98ddf865 100644 --- a/src/browser/Terminal.test.ts +++ b/src/browser/Terminal.test.ts @@ -9,7 +9,7 @@ import { assert } from 'chai'; import { DEFAULT_ATTR_DATA } from '../common/buffer/BufferLine'; import { CellData } from '../common/buffer/CellData'; import { MockUnicodeService, createCellData } from '../common/TestUtils.test'; -import { IMarker } from '../common/Types'; +import { IMarker } from '../common/buffer/Types'; const INIT_COLS = 80; const INIT_ROWS = 24; diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts index ee5423eea..d99c7244b 100644 --- a/src/browser/TestUtils.test.ts +++ b/src/browser/TestUtils.test.ts @@ -7,8 +7,8 @@ import { IDisposable, IMarker, ILinkProvider, IDecorationOptions, IDecoration, I import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IMouseService, IRenderService, ISelectionService, IThemeService } from './services/Services'; import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from './renderer/shared/Types'; import { IColorSet, ITerminal, ILinkifier2, IBrowser, IViewport, ICompositionHelper, CharacterJoinerHandler, IBufferRange, ReadonlyColorSet, IBufferElementProvider } from './Types'; -import { IBuffer, IBufferSet } from '../common/buffer/Types'; -import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset, ITerminalOptions, ColorIndex } from '../common/Types'; +import { IAttributeData, IBuffer, IBufferLine, IBufferSet, ICellData } from '../common/buffer/Types'; +import { ICircularList, XtermListener, ICharset, ITerminalOptions, ColorIndex } from '../common/Types'; import { Buffer } from '../common/buffer/Buffer'; import * as Browser from '../common/Platform'; import { CoreBrowserTerminal } from './CoreBrowserTerminal'; diff --git a/src/browser/Types.ts b/src/browser/Types.ts index 0c454c118..497afcf53 100644 --- a/src/browser/Types.ts +++ b/src/browser/Types.ts @@ -3,9 +3,9 @@ * @license MIT */ -import { CharData, IColor, ITerminalOptions } from '../common/Types'; +import { IColor, ITerminalOptions } from '../common/Types'; +import { CharData, IBuffer } from '../common/buffer/Types'; import { ICoreTerminal } from '../common/CoreTerminal'; -import { IBuffer } from '../common/buffer/Types'; import { IDisposable, IRenderDimensions as IRenderDimensionsApi, Terminal as ITerminalApi } from '@xterm/xterm'; import { channels, css } from '../common/Color'; import type { IEvent } from '../common/Event'; diff --git a/src/browser/renderer/dom/DomRendererRowFactory.test.ts b/src/browser/renderer/dom/DomRendererRowFactory.test.ts index 732766ccc..ade53f461 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.test.ts @@ -9,7 +9,7 @@ import { DomRendererRowFactory } from './DomRendererRowFactory'; import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, DEFAULT_ATTR, FgFlags, BgFlags, Attributes, UnderlineStyle } from '../../../common/buffer/Constants'; import { BufferLine, DEFAULT_ATTR_DATA } from '../../../common/buffer/BufferLine'; import { BufferLineStringCache } from '../../../common/buffer/BufferLineStringCache'; -import { IBufferLine } from '../../../common/Types'; +import { IBufferLine } from '../../../common/buffer/Types'; import { CellData } from '../../../common/buffer/CellData'; import { MockCoreService, MockDecorationService, MockOptionsService, createCellData, NULL_CELL_DATA } from '../../../common/TestUtils.test'; import { MockCharacterJoinerService, MockCoreBrowserService, MockThemeService } from '../../TestUtils.test'; diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts index baa5af801..6ae089a43 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.ts @@ -3,7 +3,8 @@ * @license MIT */ -import { IBufferLine, ICellData, IColor } from '../../../common/Types'; +import { IColor } from '../../../common/Types'; +import { IBufferLine, ICellData } from '../../../common/buffer/Types'; import { INVERTED_DEFAULT_COLOR } from '../shared/Constants'; import { WHITESPACE_CELL_CHAR, Attributes } from '../../../common/buffer/Constants'; import { CellData } from '../../../common/buffer/CellData'; diff --git a/src/browser/services/CharacterJoinerService.test.ts b/src/browser/services/CharacterJoinerService.test.ts index 1dc3db334..2e73f035b 100644 --- a/src/browser/services/CharacterJoinerService.test.ts +++ b/src/browser/services/CharacterJoinerService.test.ts @@ -8,7 +8,7 @@ import { ICharacterJoinerService } from './Services'; import { CharacterJoinerService } from './CharacterJoinerService'; import { BufferLine } from '../../common/buffer/BufferLine'; import { BufferLineStringCache } from '../../common/buffer/BufferLineStringCache'; -import { IBufferLine } from '../../common/Types'; +import { IBufferLine } from '../../common/buffer/Types'; import { CellData } from '../../common/buffer/CellData'; import { MockBufferService, createCellData } from '../../common/TestUtils.test'; diff --git a/src/browser/services/CharacterJoinerService.ts b/src/browser/services/CharacterJoinerService.ts index 67ee70748..e1da0bd28 100644 --- a/src/browser/services/CharacterJoinerService.ts +++ b/src/browser/services/CharacterJoinerService.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { IBufferLine, ICellData, CharData } from '../../common/Types'; +import { CharData, IBufferLine, ICellData } from '../../common/buffer/Types'; import { ICharacterJoiner } from '../Types'; import { AttributeData } from '../../common/buffer/AttributeData'; import { WHITESPACE_CELL_CHAR, Content } from '../../common/buffer/Constants'; diff --git a/src/browser/services/SelectionService.test.ts b/src/browser/services/SelectionService.test.ts index b48a4ee38..1f65e909e 100644 --- a/src/browser/services/SelectionService.test.ts +++ b/src/browser/services/SelectionService.test.ts @@ -6,14 +6,13 @@ import { assert } from 'chai'; import { SelectionService, SelectionMode } from './SelectionService'; import { SelectionModel } from '../selection/SelectionModel'; -import { IBufferLine } from '../../common/Types'; +import { IBuffer, IBufferLine } from '../../common/buffer/Types'; import { MockBufferService, MockOptionsService, MockCoreService, MockMouseStateService, createCellData } from '../../common/TestUtils.test'; import { BufferLine } from '../../common/buffer/BufferLine'; import { BufferLineStringCache } from '../../common/buffer/BufferLineStringCache'; import { IBufferService, IOptionsService } from '../../common/services/Services'; import { MockCoreBrowserService, MockMouseService, MockRenderService } from '../TestUtils.test'; import { CellData } from '../../common/buffer/CellData'; -import { IBuffer } from '../../common/buffer/Types'; import { IRenderService } from './Services'; const TEST_STRING_CACHE = new BufferLineStringCache(); diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index d6b015b61..576bdc525 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -11,10 +11,10 @@ import { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from import { ICoreBrowserService, IMouseCoordsService, IRenderService, ISelectionService } from './Services'; import { Disposable, MutableDisposable, toDisposable } from '../../common/Lifecycle'; import * as Browser from '../../common/Platform'; -import { IBufferLine, ICellData, IDisposable } from '../../common/Types'; +import { IDisposable } from '../../common/Types'; +import { IBuffer, IBufferLine, ICellData } from '../../common/buffer/Types'; import { getRangeLength } from '../../common/buffer/BufferRange'; import { CellData } from '../../common/buffer/CellData'; -import { IBuffer } from '../../common/buffer/Types'; import { IBufferService, ICoreService, IMouseStateService, IOptionsService } from '../../common/services/Services'; import { Emitter } from '../../common/Event'; diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index 920de796f..cf16c1e14 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -26,7 +26,8 @@ import { InstantiationService } from './services/InstantiationService'; import { LogService } from './services/LogService'; import { BufferService, BufferServiceConstants } from './services/BufferService'; import { OptionsService } from './services/OptionsService'; -import { IDisposable, IAttributeData, IScrollEvent, ITerminalOptions, IParams } from './Types'; +import { IDisposable, IScrollEvent, ITerminalOptions, IParams } from './Types'; +import { IAttributeData, IBufferSet } from './buffer/Types'; import { CoreService } from './services/CoreService'; import { MouseStateService } from './services/MouseStateService'; import { UnicodeV6 } from './input/UnicodeV6'; @@ -34,7 +35,6 @@ import { UnicodeService } from './services/UnicodeService'; import { CharsetService } from './services/CharsetService'; import { updateWindowsModeWrappedState } from './WindowsMode'; import { IFunctionIdentifier } from './parser/Types'; -import { IBufferSet } from './buffer/Types'; import { InputHandler } from './InputHandler'; import { WriteBuffer } from './input/WriteBuffer'; import { OscLinkService } from './services/OscLinkService'; diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index 20d685e91..3cfc9e019 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -5,7 +5,8 @@ import { assert } from 'chai'; import { InputHandler } from './InputHandler'; -import { IBufferLine, IAttributeData, IColorEvent, ColorRequestType, SpecialColorIndex } from './Types'; +import { IColorEvent, ColorRequestType, SpecialColorIndex } from './Types'; +import { IAttributeData, IBufferLine } from './buffer/Types'; import { DEFAULT_ATTR_DATA } from './buffer/BufferLine'; import { CellData } from './buffer/CellData'; import { Attributes, BgFlags, UnderlineStyle } from './buffer/Constants'; diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index f7ab9c435..9d725cd1b 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -4,7 +4,8 @@ * @license MIT */ -import { IInputHandler, IAttributeData, IDisposable, IWindowOptions, IColorEvent, IParseStack, ColorIndex, ColorRequestType, SpecialColorIndex } from './Types'; +import { IInputHandler, IDisposable, IWindowOptions, IColorEvent, IParseStack, ColorIndex, ColorRequestType, SpecialColorIndex } from './Types'; +import { IAttributeData, IBuffer } from './buffer/Types'; import { C0, C1 } from './data/EscapeSequences'; import { CHARSETS, DEFAULT_CHARSET } from './data/Charsets'; import { EscapeSequenceParser } from './parser/EscapeSequenceParser'; @@ -20,7 +21,6 @@ import { UnicodeService } from './services/UnicodeService'; import { OscHandler } from './parser/OscParser'; import { DcsHandler } from './parser/DcsParser'; import { ApcHandler } from './parser/ApcParser'; -import { IBuffer } from './buffer/Types'; import { parseColor } from './input/XParseColor'; import { Emitter } from './Event'; import { XTERM_VERSION } from './Version'; diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index 3ae6db77d..bedfa6ba1 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -6,9 +6,9 @@ import { IBufferService, ICoreService, ILogService, IOptionsService, ITerminalOptions, IMouseStateService, ICharsetService, UnicodeCharProperties, UnicodeCharWidth, IUnicodeService, IUnicodeVersionProvider, LogLevelEnum, IDecorationService, IInternalDecoration, IOscLinkService, type IBufferResizeEvent } from './services/Services'; import { UnicodeService } from './services/UnicodeService'; import { DEFAULT_OPTIONS } from './services/OptionsService'; -import { IBufferSet, IBuffer } from './buffer/Types'; +import { IAttributeData, IBuffer, IBufferLine, IBufferSet, IExtendedAttrs } from './buffer/Types'; import { BufferSet } from './buffer/BufferSet'; -import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEventType, ICharset, IModes, IAttributeData, IOscLinkData, IDisposable, IBufferLine, IExtendedAttrs } from './Types'; +import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEventType, ICharset, IModes, IOscLinkData, IDisposable } from './Types'; import { UnicodeV6 } from './input/UnicodeV6'; import { IDecorationOptions, IDecoration } from '@xterm/xterm'; import { Emitter, type IEvent } from './Event'; diff --git a/src/common/Types.ts b/src/common/Types.ts index 5b7b42bb2..c2416c6c9 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -41,15 +41,6 @@ export interface IParams { getSubParamsAll(): {[idx: number]: Int32Array}; } -export const enum UnderlineStyle { - NONE = 0, - SINGLE = 1, - DOUBLE = 2, - CURLY = 3, - DOTTED = 4, - DASHED = 5 -} - export interface IDisposable { dispose(): void; } @@ -126,24 +117,12 @@ export interface ICharset { [key: string]: string | undefined; } -export type CharData = [attr: number, char: string, width: number, code: number]; - export interface IColor { readonly css: string; readonly rgba: number; // 32-bit int with rgba in each byte } export type IColorRGB = [red: number, green: number, blue: number]; -export interface IExtendedAttrs { - ext: number; - underlineStyle: UnderlineStyle; - underlineColor: number; - underlineVariantOffset: number; - urlId: number; - clone(): IExtendedAttrs; - isEmpty(): boolean; -} - /** * Tracks the current hyperlink. Since these are treated as extended attirbutes, these get passed on * to the linkifier when anything is printed. Doing it this way ensures that even when the cursor @@ -155,183 +134,6 @@ export interface IOscLinkData { uri: string; } -/** - * An object that represents all attributes of a cell. - */ -export interface IAttributeData { - /** - * "fg" is a 32-bit unsigned integer that stores the foreground color of the cell in the 24 least - * significant bits and additional flags in the remaining 8 bits. - */ - fg: number; - /** - * "bg" is a 32-bit unsigned integer that stores the background color of the cell in the 24 least - * significant bits and additional flags in the remaining 8 bits. - */ - bg: number; - /** - * "extended", aka "ext", stores extended attributes beyond those available in fg and bg. This - * data is optional on a cell and encodes less common data. - */ - extended: IExtendedAttrs; - - clone(): IAttributeData; - - // flags - isInverse(): number; - isBold(): number; - isUnderline(): number; - isBlink(): number; - isInvisible(): number; - isItalic(): number; - isDim(): number; - isStrikethrough(): number; - isProtected(): number; - isOverline(): number; - - /** - * The color mode of the foreground color which determines how to decode {@link getFgColor}, - * possible values include {@link Attributes.CM_DEFAULT}, {@link Attributes.CM_P16}, - * {@link Attributes.CM_P256} and {@link Attributes.CM_RGB}. - */ - getFgColorMode(): number; - /** - * The color mode of the background color which determines how to decode {@link getBgColor}, - * possible values include {@link Attributes.CM_DEFAULT}, {@link Attributes.CM_P16}, - * {@link Attributes.CM_P256} and {@link Attributes.CM_RGB}. - */ - getBgColorMode(): number; - isFgRGB(): boolean; - isBgRGB(): boolean; - isFgPalette(): boolean; - isBgPalette(): boolean; - isFgDefault(): boolean; - isBgDefault(): boolean; - isAttributeDefault(): boolean; - - /** - * Gets an integer representation of the foreground color, how to decode the color depends on the - * color mode {@link getFgColorMode}. - */ - getFgColor(): number; - /** - * Gets an integer representation of the background color, how to decode the color depends on the - * color mode {@link getBgColorMode}. - */ - getBgColor(): number; - - // extended attrs - hasExtendedAttrs(): number; - updateExtended(): void; - getUnderlineColor(): number; - getUnderlineColorMode(): number; - isUnderlineColorRGB(): boolean; - isUnderlineColorPalette(): boolean; - isUnderlineColorDefault(): boolean; - getUnderlineStyle(): number; - getUnderlineVariantOffset(): number; -} - -/** Cell data */ -export interface ICellData extends IAttributeData { - content: number; - combinedData: string; - isCombined(): number; - getWidth(): number; - getChars(): string; - getCode(): number; - setFromCharData(value: CharData): void; - getAsCharData(): CharData; -} - -/** - * Interface for a line in the terminal buffer. - */ -export interface IBufferLine { - length: number; - isWrapped: boolean; - get(index: number): CharData; - set(index: number, value: CharData): void; - loadCell(index: number, cell: ICellData): ICellData; - setCell(index: number, cell: ICellData): void; - setCellFromCodepoint(index: number, codePoint: number, width: number, attrs: IAttributeData): void; - addCodepointToCell(index: number, codePoint: number, width: number): void; - insertCells(pos: number, n: number, ch: ICellData): void; - deleteCells(pos: number, n: number, fill: ICellData): void; - replaceCells(start: number, end: number, fill: ICellData, respectProtect?: boolean): void; - resize(cols: number, fill: ICellData): boolean; - cleanupMemory(): number; - fill(fillCellData: ICellData, respectProtect?: boolean): void; - copyFrom(line: IBufferLine): void; - clone(): IBufferLine; - getTrimmedLength(): number; - getNoBgTrimmedLength(): number; - translateToString(trimRight?: boolean, startCol?: number, endCol?: number, outColumns?: number[]): string; - - /* direct access to cell attrs */ - getWidth(index: number): number; - hasWidth(index: number): number; - getFg(index: number): number; - getBg(index: number): number; - hasContent(index: number): number; - getCodePoint(index: number): number; - isCombined(index: number): number; - getString(index: number): string; -} - -export interface IMarker extends IDisposable { - readonly id: number; - readonly isDisposed: boolean; - readonly line: number; - onDispose: IEvent; -} - -export interface IBuffer { - readonly lines: ICircularList; - ydisp: number; - ybase: number; - y: number; - x: number; - tabs: any; - scrollBottom: number; - scrollTop: number; - hasScrollback: boolean; - savedY: number; - savedX: number; - savedCharset: ICharset | undefined; - savedCharsets: (ICharset | undefined)[]; - savedGlevel: number; - savedOriginMode: boolean; - savedWraparoundMode: boolean; - savedCurAttrData: IAttributeData; - isCursorInViewport: boolean; - markers: IMarker[]; - translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string; - getWrappedRangeForLine(y: number): { first: number, last: number }; - nextStop(x?: number): number; - prevStop(x?: number): number; - getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine; - getNullCell(attr?: IAttributeData): ICellData; - getWhitespaceCell(attr?: IAttributeData): ICellData; - addMarker(y: number): IMarker; - clearMarkers(y: number): void; - clearAllMarkers(): void; -} - -export interface IBufferSet extends IDisposable { - alt: IBuffer; - normal: IBuffer; - active: IBuffer; - - onBufferActivate: IEvent<{ activeBuffer: IBuffer, inactiveBuffer: IBuffer }>; - - activateNormalBuffer(): void; - activateAltBuffer(fillAttr?: IAttributeData): void; - reset(): void; - resize(newCols: number, newRows: number): void; - setupTabStops(i?: number): void; -} - export interface IModes { insertMode: boolean; } diff --git a/src/common/buffer/AttributeData.ts b/src/common/buffer/AttributeData.ts index c9c940f93..3bcb22a00 100644 --- a/src/common/buffer/AttributeData.ts +++ b/src/common/buffer/AttributeData.ts @@ -3,7 +3,8 @@ * @license MIT */ -import { IAttributeData, IColorRGB, IExtendedAttrs } from '../Types'; +import { IColorRGB } from '../Types'; +import { IAttributeData, IExtendedAttrs } from './Types'; import { Attributes, FgFlags, BgFlags, UnderlineStyle, ExtFlags } from './Constants'; export class AttributeData implements IAttributeData { diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts index 83479f422..be3df09b1 100644 --- a/src/common/buffer/Buffer.ts +++ b/src/common/buffer/Buffer.ts @@ -6,7 +6,8 @@ import { CircularList, IInsertEvent } from '../CircularList'; import { Disposable, toDisposable } from '../Lifecycle'; import { IdleTaskQueue } from '../TaskQueue'; -import { IAttributeData, IBufferLine, ICellData, ICharset } from '../Types'; +import { ICharset } from '../Types'; +import { IAttributeData, IBuffer, IBufferLine, ICellData } from './Types'; import { ExtendedAttrs } from './AttributeData'; import { BufferLine, DEFAULT_ATTR_DATA } from './BufferLine'; import { BufferLineStringCache } from './BufferLineStringCache'; @@ -14,7 +15,6 @@ import { getWrappedLineTrimmedLength, reflowLargerApplyNewLayout, reflowLargerCr import { CellData } from './CellData'; import { NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_CODE, WHITESPACE_CELL_WIDTH } from './Constants'; import { Marker } from './Marker'; -import { IBuffer } from './Types'; import { DEFAULT_CHARSET } from '../data/Charsets'; import { IBufferService, ILogService, IOptionsService } from '../services/Services'; diff --git a/src/common/buffer/BufferLine.test.ts b/src/common/buffer/BufferLine.test.ts index 3658611cb..7fc12ad76 100644 --- a/src/common/buffer/BufferLine.test.ts +++ b/src/common/buffer/BufferLine.test.ts @@ -6,7 +6,7 @@ import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR, Content, import { BufferLine } from './BufferLine'; import { BufferLineStringCache } from './BufferLineStringCache'; import { CellData } from './CellData'; -import { CharData, IBufferLine, ICellData } from '../Types'; +import { CharData, IBufferLine, ICellData } from './Types'; import { assert } from 'chai'; import { AttributeData } from './AttributeData'; import { createCellData, NULL_CELL_DATA, extendedAttributes } from '../TestUtils.test'; diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts index f927f76fc..4b6f6e8bb 100644 --- a/src/common/buffer/BufferLine.ts +++ b/src/common/buffer/BufferLine.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { CharData, IAttributeData, IBufferLine, ICellData, IExtendedAttrs } from '../Types'; +import { CharData, IAttributeData, IBufferLine, ICellData, IExtendedAttrs } from './Types'; import { AttributeData } from './AttributeData'; import { CellData } from './CellData'; import { Attributes, BgFlags, CHAR_DATA_ATTR_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, Content, NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, WHITESPACE_CELL_CHAR } from './Constants'; diff --git a/src/common/buffer/BufferReflow.ts b/src/common/buffer/BufferReflow.ts index d94739c30..2c0fb74ba 100644 --- a/src/common/buffer/BufferReflow.ts +++ b/src/common/buffer/BufferReflow.ts @@ -5,7 +5,7 @@ import { BufferLine } from './BufferLine'; import { CircularList } from '../CircularList'; -import { IBufferLine, ICellData } from '../Types'; +import { IBufferLine, ICellData } from './Types'; export interface INewLayoutResult { layout: number[]; diff --git a/src/common/buffer/BufferSet.ts b/src/common/buffer/BufferSet.ts index 87b123386..4ea2cee8d 100644 --- a/src/common/buffer/BufferSet.ts +++ b/src/common/buffer/BufferSet.ts @@ -4,9 +4,8 @@ */ import { Disposable, MutableDisposable } from '../Lifecycle'; -import { IAttributeData } from '../Types'; import { Buffer } from './Buffer'; -import { IBuffer, IBufferSet } from './Types'; +import { IAttributeData, IBuffer, IBufferSet } from './Types'; import { IBufferService, ILogService, IOptionsService } from '../services/Services'; import { Emitter } from '../Event'; diff --git a/src/common/buffer/CellData.ts b/src/common/buffer/CellData.ts index 18f04a30e..3c738f77f 100644 --- a/src/common/buffer/CellData.ts +++ b/src/common/buffer/CellData.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { CharData, ICellData, IExtendedAttrs } from '../Types'; +import { CharData, ICellData, IExtendedAttrs } from './Types'; import { stringFromCodePoint } from '../input/TextDecoder'; import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, Content } from './Constants'; import { AttributeData, ExtendedAttrs } from './AttributeData'; diff --git a/src/common/buffer/Constants.ts b/src/common/buffer/Constants.ts index 12b2d7ad4..5ce075cf7 100644 --- a/src/common/buffer/Constants.ts +++ b/src/common/buffer/Constants.ts @@ -147,4 +147,11 @@ export const enum ExtFlags { VARIANT_OFFSET = 0xE0000000 } -export { UnderlineStyle } from '../Types'; +export const enum UnderlineStyle { + NONE = 0, + SINGLE = 1, + DOUBLE = 2, + CURLY = 3, + DOTTED = 4, + DASHED = 5 +} diff --git a/src/common/buffer/Marker.ts b/src/common/buffer/Marker.ts index e69699c1b..ed5124add 100644 --- a/src/common/buffer/Marker.ts +++ b/src/common/buffer/Marker.ts @@ -3,9 +3,9 @@ * @license MIT */ -import { IDisposable, IMarker } from '../Types'; +import { dispose, IDisposable } from '../Lifecycle'; +import { IMarker } from './Types'; import { Emitter } from '../Event'; -import { dispose } from '../Lifecycle'; export class Marker implements IMarker { private static _nextId = 1; diff --git a/src/common/buffer/Types.ts b/src/common/buffer/Types.ts index d5bee5efd..174defb9b 100644 --- a/src/common/buffer/Types.ts +++ b/src/common/buffer/Types.ts @@ -3,7 +3,199 @@ * @license MIT */ -export type { IBuffer, IBufferSet } from '../Types'; +import type { IEvent } from '../Event'; +import type { ICircularList, ICharset } from '../Types'; +import type { IDisposable } from '../Lifecycle'; +import type { UnderlineStyle } from './Constants'; // BufferIndex denotes a position in the buffer: [rowIndex, colIndex] export type BufferIndex = [number, number]; + +export type CharData = [attr: number, char: string, width: number, code: number]; + +export interface IExtendedAttrs { + ext: number; + underlineStyle: UnderlineStyle; + underlineColor: number; + underlineVariantOffset: number; + urlId: number; + clone(): IExtendedAttrs; + isEmpty(): boolean; +} + +/** + * An object that represents all attributes of a cell. + */ +export interface IAttributeData { + /** + * "fg" is a 32-bit unsigned integer that stores the foreground color of the cell in the 24 least + * significant bits and additional flags in the remaining 8 bits. + */ + fg: number; + /** + * "bg" is a 32-bit unsigned integer that stores the background color of the cell in the 24 least + * significant bits and additional flags in the remaining 8 bits. + */ + bg: number; + /** + * "extended", aka "ext", stores extended attributes beyond those available in fg and bg. This + * data is optional on a cell and encodes less common data. + */ + extended: IExtendedAttrs; + + clone(): IAttributeData; + + // flags + isInverse(): number; + isBold(): number; + isUnderline(): number; + isBlink(): number; + isInvisible(): number; + isItalic(): number; + isDim(): number; + isStrikethrough(): number; + isProtected(): number; + isOverline(): number; + + /** + * The color mode of the foreground color which determines how to decode {@link getFgColor}, + * possible values include {@link Attributes.CM_DEFAULT}, {@link Attributes.CM_P16}, + * {@link Attributes.CM_P256} and {@link Attributes.CM_RGB}. + */ + getFgColorMode(): number; + /** + * The color mode of the background color which determines how to decode {@link getBgColor}, + * possible values include {@link Attributes.CM_DEFAULT}, {@link Attributes.CM_P16}, + * {@link Attributes.CM_P256} and {@link Attributes.CM_RGB}. + */ + getBgColorMode(): number; + isFgRGB(): boolean; + isBgRGB(): boolean; + isFgPalette(): boolean; + isBgPalette(): boolean; + isFgDefault(): boolean; + isBgDefault(): boolean; + isAttributeDefault(): boolean; + + /** + * Gets an integer representation of the foreground color, how to decode the color depends on the + * color mode {@link getFgColorMode}. + */ + getFgColor(): number; + /** + * Gets an integer representation of the background color, how to decode the color depends on the + * color mode {@link getBgColorMode}. + */ + getBgColor(): number; + + // extended attrs + hasExtendedAttrs(): number; + updateExtended(): void; + getUnderlineColor(): number; + getUnderlineColorMode(): number; + isUnderlineColorRGB(): boolean; + isUnderlineColorPalette(): boolean; + isUnderlineColorDefault(): boolean; + getUnderlineStyle(): number; + getUnderlineVariantOffset(): number; +} + +/** Cell data */ +export interface ICellData extends IAttributeData { + content: number; + combinedData: string; + isCombined(): number; + getWidth(): number; + getChars(): string; + getCode(): number; + setFromCharData(value: CharData): void; + getAsCharData(): CharData; +} + +/** + * Interface for a line in the terminal buffer. + */ +export interface IBufferLine { + length: number; + isWrapped: boolean; + get(index: number): CharData; + set(index: number, value: CharData): void; + loadCell(index: number, cell: ICellData): ICellData; + setCell(index: number, cell: ICellData): void; + setCellFromCodepoint(index: number, codePoint: number, width: number, attrs: IAttributeData): void; + addCodepointToCell(index: number, codePoint: number, width: number): void; + insertCells(pos: number, n: number, ch: ICellData): void; + deleteCells(pos: number, n: number, fill: ICellData): void; + replaceCells(start: number, end: number, fill: ICellData, respectProtect?: boolean): void; + resize(cols: number, fill: ICellData): boolean; + cleanupMemory(): number; + fill(fillCellData: ICellData, respectProtect?: boolean): void; + copyFrom(line: IBufferLine): void; + clone(): IBufferLine; + getTrimmedLength(): number; + getNoBgTrimmedLength(): number; + translateToString(trimRight?: boolean, startCol?: number, endCol?: number, outColumns?: number[]): string; + + /* direct access to cell attrs */ + getWidth(index: number): number; + hasWidth(index: number): number; + getFg(index: number): number; + getBg(index: number): number; + hasContent(index: number): number; + getCodePoint(index: number): number; + isCombined(index: number): number; + getString(index: number): string; +} + +export interface IMarker extends IDisposable { + readonly id: number; + readonly isDisposed: boolean; + readonly line: number; + onDispose: IEvent; +} + +export interface IBuffer { + readonly lines: ICircularList; + ydisp: number; + ybase: number; + y: number; + x: number; + tabs: any; + scrollBottom: number; + scrollTop: number; + hasScrollback: boolean; + savedY: number; + savedX: number; + savedCharset: ICharset | undefined; + savedCharsets: (ICharset | undefined)[]; + savedGlevel: number; + savedOriginMode: boolean; + savedWraparoundMode: boolean; + savedCurAttrData: IAttributeData; + isCursorInViewport: boolean; + markers: IMarker[]; + translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string; + getWrappedRangeForLine(y: number): { first: number, last: number }; + nextStop(x?: number): number; + prevStop(x?: number): number; + getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine; + getNullCell(attr?: IAttributeData): ICellData; + getWhitespaceCell(attr?: IAttributeData): ICellData; + addMarker(y: number): IMarker; + clearMarkers(y: number): void; + clearAllMarkers(): void; +} + +export interface IBufferSet extends IDisposable { + alt: IBuffer; + normal: IBuffer; + active: IBuffer; + + onBufferActivate: IEvent<{ activeBuffer: IBuffer, inactiveBuffer: IBuffer }>; + + activateNormalBuffer(): void; + activateAltBuffer(fillAttr?: IAttributeData): void; + reset(): void; + resize(newCols: number, newRows: number): void; + setupTabStops(i?: number): void; +} diff --git a/src/common/public/BufferLineApiView.ts b/src/common/public/BufferLineApiView.ts index ecffd1fbb..bd822bfb5 100644 --- a/src/common/public/BufferLineApiView.ts +++ b/src/common/public/BufferLineApiView.ts @@ -4,7 +4,7 @@ */ import { CellData } from '../buffer/CellData'; -import { IBufferLine, ICellData } from '../Types'; +import { IBufferLine, ICellData } from '../buffer/Types'; import { IBufferCell as IBufferCellApi, IBufferLine as IBufferLineApi } from '@xterm/xterm'; export class BufferLineApiView implements IBufferLineApi { diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index 5135e7786..21b0a163a 100644 --- a/src/common/services/BufferService.ts +++ b/src/common/services/BufferService.ts @@ -4,9 +4,8 @@ */ import { Disposable } from '../Lifecycle'; -import { IAttributeData, IBufferLine } from '../Types'; +import { IAttributeData, IBuffer, IBufferLine, IBufferSet } from '../buffer/Types'; import { BufferSet } from '../buffer/BufferSet'; -import { IBuffer, IBufferSet } from '../buffer/Types'; import { IBufferService, ILogService, IOptionsService, type IBufferResizeEvent } from './Services'; import { Emitter } from '../Event'; diff --git a/src/common/services/DecorationService.test.ts b/src/common/services/DecorationService.test.ts index 71299d330..61a7668fc 100644 --- a/src/common/services/DecorationService.test.ts +++ b/src/common/services/DecorationService.test.ts @@ -5,7 +5,7 @@ import { assert } from 'chai'; import { DecorationLineCache, DecorationService } from './DecorationService'; -import { IMarker } from '../Types'; +import { IMarker } from '../buffer/Types'; import { Disposable } from '../Lifecycle'; import { Emitter } from '../Event'; import { MockLogService, MockBufferService, MockOptionsService } from '../TestUtils.test'; diff --git a/src/common/services/OscLinkService.ts b/src/common/services/OscLinkService.ts index e28f4cfc8..605e85002 100644 --- a/src/common/services/OscLinkService.ts +++ b/src/common/services/OscLinkService.ts @@ -3,7 +3,8 @@ * @license MIT */ import { IBufferService, IOscLinkService } from './Services'; -import { IMarker, IOscLinkData } from '../Types'; +import { IOscLinkData } from '../Types'; +import { IMarker } from '../buffer/Types'; export class OscLinkService implements IOscLinkService { public serviceBrand: any; diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 5308f843d..cd1bc9cd4 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -4,8 +4,8 @@ */ import type { IDecoration, IDecorationOptions, ILinkHandler, ILogger, IWindowsPty, IOverviewRulerOptions } from '@xterm/xterm'; -import { CoreMouseEncoding, CoreMouseEventType, CursorInactiveStyle, CursorStyle, IAttributeData, ICharset, IColor, ICoreMouseEvent, ICoreMouseProtocol, IDecPrivateModes, IDisposable, IKittyKeyboardState, IModes, IOscLinkData, IWindowOptions } from '../Types'; -import { IBuffer, IBufferSet } from '../buffer/Types'; +import { CoreMouseEncoding, CoreMouseEventType, CursorInactiveStyle, CursorStyle, ICharset, IColor, ICoreMouseEvent, ICoreMouseProtocol, IDecPrivateModes, IDisposable, IKittyKeyboardState, IModes, IOscLinkData, IWindowOptions } from '../Types'; +import { IAttributeData, IBuffer, IBufferSet } from '../buffer/Types'; import { createDecorator, IServiceIdentifier } from './ServiceRegistry'; import type { Emitter, IEvent } from '../Event'; diff --git a/src/headless/Terminal.ts b/src/headless/Terminal.ts index 4d4266d19..4d90cc03e 100644 --- a/src/headless/Terminal.ts +++ b/src/headless/Terminal.ts @@ -22,9 +22,9 @@ */ import { DEFAULT_ATTR_DATA } from '../common/buffer/BufferLine'; -import { IBuffer } from '../common/buffer/Types'; +import { IBuffer, IMarker } from '../common/buffer/Types'; import { CoreTerminal } from '../common/CoreTerminal'; -import { IMarker, ITerminalOptions } from '../common/Types'; +import { ITerminalOptions } from '../common/Types'; import { Emitter, EventUtils } from '../common/Event'; export class Terminal extends CoreTerminal { diff --git a/test/playwright/TestUtils.ts b/test/playwright/TestUtils.ts index 05f3a8e31..2a44c3592 100644 --- a/test/playwright/TestUtils.ts +++ b/test/playwright/TestUtils.ts @@ -7,7 +7,8 @@ import { Browser, JSHandle, Page } from '@playwright/test'; import { deepStrictEqual, strictEqual } from 'assert'; import type { IRenderDimensions as IRenderDimensionsInternal } from '../../src/browser/renderer/shared/Types'; import type { IRenderService } from '../../src/browser/services/Services'; -import type { IDisposable, IMarker } from '../../src/common/Types'; +import type { IDisposable } from '../../src/common/Types'; +import type { IMarker } from '../../src/common/buffer/Types'; import type { ICoreTerminal } from '../../src/common/CoreTerminal'; import * as playwright from '@playwright/test'; import { IBuffer, IBufferCell, IBufferLine, IBufferNamespace, IBufferRange, IDecoration, IDecorationOptions, IModes, IRenderDimensions, ITerminalInitOnlyOptions, ITerminalOptions, Terminal } from '@xterm/xterm'; From 5684f95c8efa5508a925ecd99733adfe3ee53f92 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 14:37:01 -0700 Subject: [PATCH 084/153] Colocate circular list types in CircularList.ts The import/no-cycle refactor had moved IInsertEvent, IDeleteEvent, and ICircularList into Types.ts, with CircularList.ts re-exporting the event types from there. That split the list abstraction from its implementation and pulled Types into the circular-list dependency graph. Define IInsertEvent, IDeleteEvent, and ICircularList in CircularList.ts alongside the CircularList class. CircularList.ts now depends only on Lifecycle and Event, not Types.ts. Remove those definitions from Types.ts (and the unused Emitter import). Update callers to import list types from CircularList: - buffer/Types.ts (IBuffer.lines) - DecorationService.ts (buffer line insert/delete handlers) - browser TestUtils.test.ts (mock buffer lines) Types.ts no longer imports or re-exports circular-list types, preserving the acyclic Types -> buffer/CircularList boundary. Co-authored-by: Cursor --- src/browser/TestUtils.test.ts | 3 +- src/common/CircularList.ts | 35 ++++++++++++++++++++++-- src/common/Types.ts | 34 +---------------------- src/common/buffer/Types.ts | 3 +- src/common/services/DecorationService.ts | 4 +-- 5 files changed, 39 insertions(+), 40 deletions(-) diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts index d99c7244b..7b3464595 100644 --- a/src/browser/TestUtils.test.ts +++ b/src/browser/TestUtils.test.ts @@ -8,7 +8,8 @@ import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IMouseS import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from './renderer/shared/Types'; import { IColorSet, ITerminal, ILinkifier2, IBrowser, IViewport, ICompositionHelper, CharacterJoinerHandler, IBufferRange, ReadonlyColorSet, IBufferElementProvider } from './Types'; import { IAttributeData, IBuffer, IBufferLine, IBufferSet, ICellData } from '../common/buffer/Types'; -import { ICircularList, XtermListener, ICharset, ITerminalOptions, ColorIndex } from '../common/Types'; +import { ICircularList } from '../common/CircularList'; +import { XtermListener, ICharset, ITerminalOptions, ColorIndex } from '../common/Types'; import { Buffer } from '../common/buffer/Buffer'; import * as Browser from '../common/Platform'; import { CoreBrowserTerminal } from './CoreBrowserTerminal'; diff --git a/src/common/CircularList.ts b/src/common/CircularList.ts index 21dd081da..b773391f9 100644 --- a/src/common/CircularList.ts +++ b/src/common/CircularList.ts @@ -3,11 +3,40 @@ * @license MIT */ -import { ICircularList, IDeleteEvent, IInsertEvent } from './Types'; import { Disposable } from './Lifecycle'; -import { Emitter } from './Event'; +import { Emitter, type IEvent } from './Event'; -export type { IDeleteEvent, IInsertEvent } from './Types'; +export interface IInsertEvent { + index: number; + amount: number; +} + +export interface IDeleteEvent { + index: number; + amount: number; +} + +export interface ICircularList { + length: number; + maxLength: number; + isFull: boolean; + + onDeleteEmitter: Emitter; + onDelete: IEvent; + onInsertEmitter: Emitter; + onInsert: IEvent; + onTrimEmitter: Emitter; + onTrim: IEvent; + + get(index: number): T | undefined; + set(index: number, value: T): void; + push(value: T): void; + recycle(): T; + pop(): T | undefined; + splice(start: number, deleteCount: number, ...items: T[]): void; + trimStart(count: number): void; + shiftElements(start: number, count: number, offset: number): void; +} /** * Represents a circular list; a list with a maximum size that wraps around when push is called, diff --git a/src/common/Types.ts b/src/common/Types.ts index c2416c6c9..9e1481fd3 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -4,17 +4,7 @@ */ import { IFunctionIdentifier, ITerminalOptions as IPublicTerminalOptions } from '@xterm/xterm'; -import type { Emitter, IEvent } from './Event'; - -export interface IInsertEvent { - index: number; - amount: number; -} - -export interface IDeleteEvent { - index: number; - amount: number; -} +import type { IEvent } from './Event'; /** sequence params serialized to js arrays */ export type ParamsArray = (number | number[])[]; @@ -78,28 +68,6 @@ export interface IScrollEvent { position: number; } -export interface ICircularList { - length: number; - maxLength: number; - isFull: boolean; - - onDeleteEmitter: Emitter; - onDelete: IEvent; - onInsertEmitter: Emitter; - onInsert: IEvent; - onTrimEmitter: Emitter; - onTrim: IEvent; - - get(index: number): T | undefined; - set(index: number, value: T): void; - push(value: T): void; - recycle(): T; - pop(): T | undefined; - splice(start: number, deleteCount: number, ...items: T[]): void; - trimStart(count: number): void; - shiftElements(start: number, count: number, offset: number): void; -} - export const enum KeyboardResultType { SEND_KEY, SELECT_ALL, diff --git a/src/common/buffer/Types.ts b/src/common/buffer/Types.ts index 174defb9b..994ca0f43 100644 --- a/src/common/buffer/Types.ts +++ b/src/common/buffer/Types.ts @@ -4,7 +4,8 @@ */ import type { IEvent } from '../Event'; -import type { ICircularList, ICharset } from '../Types'; +import type { ICircularList } from '../CircularList'; +import type { ICharset } from '../Types'; import type { IDisposable } from '../Lifecycle'; import type { UnderlineStyle } from './Constants'; diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts index 5c2a1d33a..ea9b7fb14 100644 --- a/src/common/services/DecorationService.ts +++ b/src/common/services/DecorationService.ts @@ -3,13 +3,13 @@ * @license MIT */ -import type { IDeleteEvent, IInsertEvent } from '../CircularList'; +import type { ICircularList, IDeleteEvent, IInsertEvent } from '../CircularList'; import { MicrotaskTimer } from '../Async'; import { css } from '../Color'; import { Disposable, DisposableStore, MutableDisposable, toDisposable } from '../Lifecycle'; import { IBufferService, IDecorationService, IInternalDecoration, ILogService } from './Services'; import { SortedList } from '../SortedList'; -import { IColor, ICircularList } from '../Types'; +import { IColor } from '../Types'; import { IDecoration, IDecorationOptions, IMarker } from '@xterm/xterm'; import { Emitter } from '../Event'; From d4acf2fa45ab2a5a9f1147b6fb2b36cd8656d871 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:39:43 -0700 Subject: [PATCH 085/153] fix(browser): use radix 10 for padding parseInt in Mouse coords parseInt without a radix can mis-parse leading-zero padding values as octal. Match AccessibilityManager and document rowCount JSDoc typo. Co-authored-by: Cursor --- src/browser/input/Mouse.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/browser/input/Mouse.ts b/src/browser/input/Mouse.ts index c40a7cc78..5f73743fb 100644 --- a/src/browser/input/Mouse.ts +++ b/src/browser/input/Mouse.ts @@ -6,8 +6,8 @@ export function getCoordsRelativeToElement(window: Pick, event: {clientX: number, clientY: number}, element: HTMLElement): [number, number] { const rect = element.getBoundingClientRect(); const elementStyle = window.getComputedStyle(element); - const leftPadding = parseInt(elementStyle.getPropertyValue('padding-left')); - const topPadding = parseInt(elementStyle.getPropertyValue('padding-top')); + const leftPadding = parseInt(elementStyle.getPropertyValue('padding-left'), 10); + const topPadding = parseInt(elementStyle.getPropertyValue('padding-top'), 10); return [ event.clientX - rect.left - leftPadding, event.clientY - rect.top - topPadding @@ -22,7 +22,7 @@ export function getCoordsRelativeToElement(window: Pick Date: Sun, 31 May 2026 21:55:20 -0700 Subject: [PATCH 086/153] fix(common): pass radix 10 to parseInt in getSafariVersion Safari major version parsing used parseInt without an explicit radix. Other call sites in the repo use base 10; add the radix for consistency and to avoid octal interpretation of leading-zero strings. Co-authored-by: Cursor --- src/common/Platform.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/Platform.ts b/src/common/Platform.ts index 09ba097e6..9ccf8bda8 100644 --- a/src/common/Platform.ts +++ b/src/common/Platform.ts @@ -41,7 +41,7 @@ export function getSafariVersion(): number { if (majorVersion === null || majorVersion.length < 2) { return 0; } - return parseInt(majorVersion[1]); + return parseInt(majorVersion[1], 10); } // Find the users platform. We use this to interpret the meta key From f04679a42ef4dadd3ba919377550d98a11cce29e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:12:26 -0700 Subject: [PATCH 087/153] fix(common): pass radix 10 when parsing rgb() components in Color rgba/rgb CSS parsing used parseInt without a radix on captured digit groups. Add base 10 explicitly, matching getSafariVersion and other call sites in the codebase. Co-authored-by: Cursor --- src/common/Color.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/Color.ts b/src/common/Color.ts index aa3ca00ab..d416d670e 100644 --- a/src/common/Color.ts +++ b/src/common/Color.ts @@ -177,9 +177,9 @@ export namespace css { // Formats: rgb() or rgba() const rgbaMatch = css.match(/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(,\s*(0|1|\d?\.(\d+))\s*)?\)/); if (rgbaMatch) { - $r = parseInt(rgbaMatch[1]); - $g = parseInt(rgbaMatch[2]); - $b = parseInt(rgbaMatch[3]); + $r = parseInt(rgbaMatch[1], 10); + $g = parseInt(rgbaMatch[2], 10); + $b = parseInt(rgbaMatch[3], 10); $a = Math.round((rgbaMatch[5] === undefined ? 1 : parseFloat(rgbaMatch[5])) * 0xFF); return channels.toColor($r, $g, $b, $a); } From 878e3fc52d3cf2313b3d5775663e8286f431b38f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:32:13 -0700 Subject: [PATCH 088/153] fix(browser): pass radix 10 when parsing Chrome major version Wheel event DPR factoring parses Chrome/(\d+) with parseInt without a radix. Add base 10 explicitly, matching getSafariVersion and other version parsing in the codebase. Co-authored-by: Cursor --- src/browser/scrollable/mouseEvent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/scrollable/mouseEvent.ts b/src/browser/scrollable/mouseEvent.ts index 6be8963e2..f6fca88f1 100644 --- a/src/browser/scrollable/mouseEvent.ts +++ b/src/browser/scrollable/mouseEvent.ts @@ -217,7 +217,7 @@ export class StandardWheelEvent { let shouldFactorDPR: boolean = false; if (platform.isChrome) { const chromeVersionMatch = navigator.userAgent.match(/Chrome\/(\d+)/); - const chromeMajorVersion = chromeVersionMatch ? parseInt(chromeVersionMatch[1]) : 123; + const chromeMajorVersion = chromeVersionMatch ? parseInt(chromeVersionMatch[1], 10) : 123; shouldFactorDPR = chromeMajorVersion <= 122; } From b0f6aed4a1a7ff27609b15d77f683a8d4923b0b7 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 23:03:46 -0700 Subject: [PATCH 089/153] fix(common): pass radix 10 when parsing OSC color indices InputHandler parsed palette indices from OSC color sequences with parseInt without a radix after validating digits-only slots. Use base 10 for set/report and restore color index parsing. Co-authored-by: Cursor --- src/common/InputHandler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 9d725cd1b..0b12dd8e1 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -3065,7 +3065,7 @@ export class InputHandler extends Disposable implements IInputHandler { const idx = slots.shift() as string; const spec = slots.shift() as string; if (/^\d+$/.exec(idx)) { - const index = parseInt(idx); + const index = parseInt(idx, 10); if (isValidColorIndex(index)) { if (spec === '?') { event.push({ type: ColorRequestType.REPORT, index }); @@ -3228,7 +3228,7 @@ export class InputHandler extends Disposable implements IInputHandler { const slots = data.split(';'); for (let i = 0; i < slots.length; ++i) { if (/^\d+$/.exec(slots[i])) { - const index = parseInt(slots[i]); + const index = parseInt(slots[i], 10); if (isValidColorIndex(index)) { event.push({ type: ColorRequestType.RESTORE, index }); } From 99550840d24b8e43e0a6d2dfca73d7fe93b09b4f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:43:31 -0700 Subject: [PATCH 090/153] fix(fit): add radix and zero fallback for parent width parsing Align parent width parsing with the height fix: parseInt with radix 10 and || 0 inside Math.max so non-numeric CSS width does not yield NaN cols in proposeDimensions. Co-authored-by: Cursor --- addons/addon-fit/src/FitAddon.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/addon-fit/src/FitAddon.ts b/addons/addon-fit/src/FitAddon.ts index 3db988acd..fab52dfde 100644 --- a/addons/addon-fit/src/FitAddon.ts +++ b/addons/addon-fit/src/FitAddon.ts @@ -74,8 +74,8 @@ export class FitAddon implements ITerminalAddon, IFitApi { : (this._terminal.options.scrollbar?.width ?? ViewportConstants.DEFAULT_SCROLL_BAR_WIDTH)); const parentElementStyle = _getComputedStyle(this._terminal.element.parentElement); - const parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')); - const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width'))); + const parentElementHeight = Math.max(0, parseInt(parentElementStyle.getPropertyValue('height'), 10) || 0); + const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width'), 10) || 0); const elementStyle = _getComputedStyle(this._terminal.element); const elementPadding = { top: parseInt(elementStyle.getPropertyValue('padding-top')), From a2cd1ab29c6a72c25bb953c687f773280c2157ff Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:46:40 -0700 Subject: [PATCH 091/153] fix(fit): parse terminal padding with radix 10 and zero fallback proposeDimensions summed padding-top/bottom/left/right via bare parseInt, so non-numeric padding values produced NaN and broke available width/height. Use radix 10 and || 0 on each side, consistent with parent element size parsing. Co-authored-by: Cursor --- addons/addon-fit/src/FitAddon.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/addon-fit/src/FitAddon.ts b/addons/addon-fit/src/FitAddon.ts index fab52dfde..75bbe9caa 100644 --- a/addons/addon-fit/src/FitAddon.ts +++ b/addons/addon-fit/src/FitAddon.ts @@ -78,10 +78,10 @@ export class FitAddon implements ITerminalAddon, IFitApi { const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width'), 10) || 0); const elementStyle = _getComputedStyle(this._terminal.element); const elementPadding = { - top: parseInt(elementStyle.getPropertyValue('padding-top')), - bottom: parseInt(elementStyle.getPropertyValue('padding-bottom')), - right: parseInt(elementStyle.getPropertyValue('padding-right')), - left: parseInt(elementStyle.getPropertyValue('padding-left')) + top: parseInt(elementStyle.getPropertyValue('padding-top'), 10) || 0, + bottom: parseInt(elementStyle.getPropertyValue('padding-bottom'), 10) || 0, + right: parseInt(elementStyle.getPropertyValue('padding-right'), 10) || 0, + left: parseInt(elementStyle.getPropertyValue('padding-left'), 10) || 0 }; const elementPaddingVer = elementPadding.top + elementPadding.bottom; const elementPaddingHor = elementPadding.right + elementPadding.left; From f34d0a255c8944543e345ab8b9b9a9b0eed5855c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:50:32 -0700 Subject: [PATCH 092/153] fix(image): pass radix 10 in IIPHandler dimension parsing _dim() parsed percent, pixel, and cell dimension strings with parseInt without an explicit radix. Use base 10 for all three paths, consistent with other numeric parsing fixes in the repo. Co-authored-by: Cursor --- addons/addon-image/src/IIPHandler.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/addon-image/src/IIPHandler.ts b/addons/addon-image/src/IIPHandler.ts index 5e15c5277..56f5cdf35 100644 --- a/addons/addon-image/src/IIPHandler.ts +++ b/addons/addon-image/src/IIPHandler.ts @@ -222,8 +222,8 @@ export class IIPHandler implements IOscHandler, IResetHandler { private _dim(s: string, total: number, cdim: number): number { if (s === 'auto') return 0; - if (s.endsWith('%')) return parseInt(s.slice(0, -1)) * total / 100; - if (s.endsWith('px')) return parseInt(s.slice(0, -2)); - return parseInt(s) * cdim; + if (s.endsWith('%')) return parseInt(s.slice(0, -1), 10) * total / 100; + if (s.endsWith('px')) return parseInt(s.slice(0, -2), 10); + return parseInt(s, 10) * cdim; } } From a1a8d1abae74aa11988ec48385d88a3aa3eccdc1 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:57:02 -0700 Subject: [PATCH 093/153] fix(image): pass radix 10 when parsing kitty graphics numeric keys KittyGraphicsTypes parsed numeric control values with parseInt without a radix. Use base 10 explicitly for format, id, dimensions, and offset fields. Co-authored-by: Cursor --- addons/addon-image/src/kitty/KittyGraphicsTypes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-image/src/kitty/KittyGraphicsTypes.ts b/addons/addon-image/src/kitty/KittyGraphicsTypes.ts index 448ebcc05..4288dd0a4 100644 --- a/addons/addon-image/src/kitty/KittyGraphicsTypes.ts +++ b/addons/addon-image/src/kitty/KittyGraphicsTypes.ts @@ -168,7 +168,7 @@ export function parseKittyCommand(data: string): IKittyCommand { cmd.deleteSelector = value; continue; } - const numValue = parseInt(value); + const numValue = parseInt(value, 10); switch (key) { case KittyKey.FORMAT: cmd.format = numValue; break; case KittyKey.ID: cmd.id = numValue; break; From d407bf561f5edde2955955a6067523b62043bea3 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 23:00:26 -0700 Subject: [PATCH 094/153] fix(webgl): pass radix 10 for SVG arc flag parsing CustomGlyphRasterizer parsed large-arc and sweep flags with parseInt without a radix when drawing SVG arc paths. Use base 10 for 0/1 flags. Co-authored-by: Cursor --- addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts index 357d512d2..137cca7e3 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts @@ -221,8 +221,8 @@ function drawPathDefinitionCharacter( const rx = parseFloat(args[0]) * deviceCellWidth; const ry = parseFloat(args[1]) * deviceCellHeight; const xAxisRotation = parseFloat(args[2]) * Math.PI / 180; - const largeArcFlag = parseInt(args[3]); - const sweepFlag = parseInt(args[4]); + const largeArcFlag = parseInt(args[3], 10); + const sweepFlag = parseInt(args[4], 10); const x = xOffset + parseFloat(args[5]) * deviceCellWidth; const y = yOffset + parseFloat(args[6]) * deviceCellHeight; drawSvgArc(ctx, currentX, currentY, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y); From c961408bf7ef733afff29c8de87d48bc72f31a64 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:31:29 -0700 Subject: [PATCH 095/153] fix(browser): use startY in horizontalDirection row helper call horizontalDirection called moveToRequestedRow with targetX as the first argument, but that parameter is the starting row (startY). resetStartingRow and moveToRequestedCol already pass startY correctly; only this copy-paste mistake passed a column index where a row was expected. That could miscompute wrapped-row offsets and emit the wrong horizontal arrow key sequence when building selection/cursor movement strings. Co-authored-by: Cursor --- src/browser/input/MoveToCell.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/input/MoveToCell.ts b/src/browser/input/MoveToCell.ts index b5ddc7ca2..9cc27088d 100644 --- a/src/browser/input/MoveToCell.ts +++ b/src/browser/input/MoveToCell.ts @@ -156,7 +156,7 @@ function wrappedRowsForRow(currentRow: number, bufferService: IBufferService): n */ function horizontalDirection(startX: number, startY: number, targetX: number, targetY: number, bufferService: IBufferService, applicationCursor: boolean): Direction { let startRow; - if (moveToRequestedRow(targetX, targetY, bufferService, applicationCursor).length > 0) { + if (moveToRequestedRow(startY, targetY, bufferService, applicationCursor).length > 0) { startRow = targetY - wrappedRowsForRow(targetY, bufferService); } else { startRow = startY; From 3f3f6abfb90ebd0367e0e894e16aa48fd6c62a39 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:33:19 -0700 Subject: [PATCH 096/153] fix(browser): parenthesize document fallback for CoreBrowserService Nullish coalescing binds tighter than the conditional operator, so the previous expression was parsed as (this._document ?? windowDefined) ? window.document : null. When this._document was set (e.g. parent ownerDocument or documentOverride), the ternary still returned window.document instead of the configured document. CoreBrowserService.mainDocument is used by AccessibilityManager, viewport styles, and decoration renderers; CharSizeService already received this._document directly. Add parentheses so the fallback applies only when _document is nullish. Co-authored-by: Cursor --- src/browser/CoreBrowserTerminal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index f7e20b914..5b65a93db 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -505,7 +505,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this.textarea, parent.ownerDocument.defaultView ?? window, // Force unsafe null in node.js environment for tests - this._document ?? (typeof window !== 'undefined') ? window.document : null as any + this._document ?? ((typeof window !== 'undefined') ? window.document : null as any) )); this._instantiationService.setService(ICoreBrowserService, this._coreBrowserService); From 536eeb4db09c4787e3197c76e08aef09cc12ce09 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:34:13 -0700 Subject: [PATCH 097/153] refactor(browser): remove unreachable coords guard in getCoords getCoordsRelativeToElement always returns a two-element tuple, so the array is always truthy and if (!coords) could never run. hasValidCharSize already handles the only early-exit path before coordinates are computed. Co-authored-by: Cursor --- src/browser/input/Mouse.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/browser/input/Mouse.ts b/src/browser/input/Mouse.ts index 5f73743fb..ba8bddaa2 100644 --- a/src/browser/input/Mouse.ts +++ b/src/browser/input/Mouse.ts @@ -37,10 +37,6 @@ export function getCoords(window: Pick, event: Pick< } const coords = getCoordsRelativeToElement(window, event, element); - if (!coords) { - return undefined; - } - coords[0] = Math.ceil((coords[0] + (isSelection ? cssCellWidth / 2 : 0)) / cssCellWidth); coords[1] = Math.ceil(coords[1] / cssCellHeight); From 1dd113b38a49dbb030ad52f8171254ba88ac5ff2 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:38:07 -0700 Subject: [PATCH 098/153] fix(browser): reset TimeBasedDebouncer state on dispose dispose() cleared the pending timeout but left _refreshTimeoutID and _additionalRefreshRequested set. After dispose, a stale timeout id or trailing-refresh flag could confuse a new instance or tests. Match RenderDebouncer by clearing the timeout id and reset the trailing flag. Co-authored-by: Cursor --- src/browser/TimeBasedDebouncer.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index 0860e7927..959c77a4e 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -31,7 +31,9 @@ export class TimeBasedDebouncer implements IRenderDebouncer { public dispose(): void { if (this._refreshTimeoutID) { clearTimeout(this._refreshTimeoutID); + this._refreshTimeoutID = undefined; } + this._additionalRefreshRequested = false; } public refresh(rowStart: number | undefined, rowEnd: number | undefined, rowCount: number): void { From e35a9c1e902b3f6aff3d5564161e635abd843a62 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:35:49 -0700 Subject: [PATCH 099/153] fix(addon-search): reject startCol equal to terminal column count Valid column indices are 0 through cols - 1. The previous check used startCol > cols, so on an 80-column terminal column 80 was accepted while column 100 correctly threw. Use >= so the out-of-range guard matches the documented error for invalid search positions. Co-authored-by: Cursor --- addons/addon-search/src/SearchEngine.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-search/src/SearchEngine.ts b/addons/addon-search/src/SearchEngine.ts index 7e89c03a1..1760bc2bd 100644 --- a/addons/addon-search/src/SearchEngine.ts +++ b/addons/addon-search/src/SearchEngine.ts @@ -60,7 +60,7 @@ export class SearchEngine { this._terminal.clearSelection(); return undefined; } - if (startCol > this._terminal.cols) { + if (startCol >= this._terminal.cols) { throw new Error(`Invalid col: ${startCol} to search in terminal of ${this._terminal.cols} cols`); } From 1a01e9634f694c9cf4d9898a75d67c418148af12 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:50:16 -0700 Subject: [PATCH 100/153] fix(parser): use correct grammar in Params validation errors Replace "values lesser than -1" with "values less than -1" in addParam and addSubParam throw messages. "Lesser" is not standard English for numeric comparisons; tests assert the exact message. Co-authored-by: Cursor --- src/common/parser/Params.test.ts | 4 ++-- src/common/parser/Params.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/parser/Params.test.ts b/src/common/parser/Params.test.ts index 3bd67d397..a30b1376c 100644 --- a/src/common/parser/Params.test.ts +++ b/src/common/parser/Params.test.ts @@ -199,13 +199,13 @@ describe('Params', () => { it('reject params lesser -1', () => { const params = new Params(); params.addParam(-1); - assert.throws(() => params.addParam(-2), 'values lesser than -1 are not allowed'); + assert.throws(() => params.addParam(-2), 'values less than -1 are not allowed'); }); it('reject subparams lesser -1', () => { const params = new Params(); params.addParam(-1); params.addSubParam(-1); - assert.throws(() => params.addSubParam(-2), 'values lesser than -1 are not allowed'); + assert.throws(() => params.addSubParam(-2), 'values less than -1 are not allowed'); assert.deepEqual(params.toArray(), [-1, [-1]]); }); it('clamp parsed params', () => { diff --git a/src/common/parser/Params.ts b/src/common/parser/Params.ts index 6986a48ba..9b6c3f855 100644 --- a/src/common/parser/Params.ts +++ b/src/common/parser/Params.ts @@ -162,7 +162,7 @@ export class Params implements IParams { return; } if (value < -1) { - throw new Error('values lesser than -1 are not allowed'); + throw new Error('values less than -1 are not allowed'); } this._subParamsIdx[this.length] = this._subParamsLength << 8 | this._subParamsLength; this.params[this.length++] = value > Constants.MAX_VALUE ? Constants.MAX_VALUE : value; @@ -185,7 +185,7 @@ export class Params implements IParams { return; } if (value < -1) { - throw new Error('values lesser than -1 are not allowed'); + throw new Error('values less than -1 are not allowed'); } this._subParams[this._subParamsLength++] = value > Constants.MAX_VALUE ? Constants.MAX_VALUE : value; this._subParamsIdx[this.length - 1]++; From b4bd92d62c4abb244f1773c588588dc26a522a23 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:03:26 -0700 Subject: [PATCH 101/153] fix(webgl): use stable numeric sort for merging texture pages The merging-pages index sort used (a, b) => a > b ? 1 : -1, which returns -1 when a === b and violates the comparator contract. Use (a, b) => a - b for ascending numeric order. Co-authored-by: Cursor --- addons/addon-webgl/src/TextureAtlas.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index dab0b1c85..0180fd781 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -191,7 +191,7 @@ export class TextureAtlas implements ITextureAtlas { return newPage; } - const sortedMergingPagesIndexes = mergingPages.map(e => e.glyphs[0].texturePage).sort((a, b) => a > b ? 1 : -1); + const sortedMergingPagesIndexes = mergingPages.map(e => e.glyphs[0].texturePage).sort((a, b) => a - b); const mergedPageIndex = this.pages.length - mergingPages.length; // Merge into the new page From 4500485e7993d0956423356770fb777b9bf3ee2b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 21:52:33 -0700 Subject: [PATCH 102/153] fix(parser): validate ESC prefix byte range with correct precedence _identifier() checked custom prefix bytes with `res && 0x3c > res || res > 0x3f`, which parsed as `(res && 0x3c > res) || (res > 0x3f)` because && binds tighter than ||. A prefix of NUL (0x00) skipped both clauses and was accepted even though only 0x3c..0x3f are valid. Use an explicit range test: res < 0x3c || res > 0x3f. Mirror the same fix in ApcParser and DcsParser test identifier helpers. Co-authored-by: Cursor --- src/common/parser/ApcParser.test.ts | 2 +- src/common/parser/DcsParser.test.ts | 2 +- src/common/parser/EscapeSequenceParser.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/parser/ApcParser.test.ts b/src/common/parser/ApcParser.test.ts index b27b40039..563ddbeab 100644 --- a/src/common/parser/ApcParser.test.ts +++ b/src/common/parser/ApcParser.test.ts @@ -21,7 +21,7 @@ function identifier(id: IFunctionIdentifier): number { throw new Error('only one byte as prefix supported'); } res = id.prefix.charCodeAt(0); - if (res && 0x3c > res || res > 0x3f) { + if (res < 0x3c || res > 0x3f) { throw new Error('prefix must be in range 0x3c .. 0x3f'); } } diff --git a/src/common/parser/DcsParser.test.ts b/src/common/parser/DcsParser.test.ts index 459bc8961..5fa7b9416 100644 --- a/src/common/parser/DcsParser.test.ts +++ b/src/common/parser/DcsParser.test.ts @@ -22,7 +22,7 @@ function identifier(id: IFunctionIdentifier): number { throw new Error('only one byte as prefix supported'); } res = id.prefix.charCodeAt(0); - if (res && 0x3c > res || res > 0x3f) { + if (res < 0x3c || res > 0x3f) { throw new Error('prefix must be in range 0x3c .. 0x3f'); } } diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index a0c97b610..c12ca3446 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -342,7 +342,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP throw new Error('only one byte as prefix supported'); } res = id.prefix.charCodeAt(0); - if (res && 0x3c > res || res > 0x3f) { + if (res < 0x3c || res > 0x3f) { throw new Error('prefix must be in range 0x3c .. 0x3f'); } } From 6d1f12cb7b70e7da8f3739025947ae5d5efe03e3 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:08:25 -0700 Subject: [PATCH 103/153] fix(web-links): avoid duplicate g flag when cloning url regex LinkComputer rebuilt the search RegExp with (regex.flags || '') + 'g', which throws if the caller already passed a global regex. Only append g when it is not already present in regex.flags. Co-authored-by: Cursor --- addons/addon-web-links/src/WebLinkProvider.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/addon-web-links/src/WebLinkProvider.ts b/addons/addon-web-links/src/WebLinkProvider.ts index 0a826eb3a..5df5d268a 100644 --- a/addons/addon-web-links/src/WebLinkProvider.ts +++ b/addons/addon-web-links/src/WebLinkProvider.ts @@ -57,7 +57,8 @@ function isUrl(urlString: string): boolean { export class LinkComputer { public static computeLink(y: number, regex: RegExp, terminal: Terminal, activate: (event: MouseEvent, uri: string) => void): ILink[] { - const rex = new RegExp(regex.source, (regex.flags || '') + 'g'); + const flags = regex.flags.includes('g') ? regex.flags : `${regex.flags}g`; + const rex = new RegExp(regex.source, flags); const [lines, startLineIndex] = LinkComputer._getWindowedLineStrings(y - 1, terminal); const line = lines.join(''); From 98d7c25bd5476ae39773fee378ac48053fa98ceb Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 21:44:30 -0700 Subject: [PATCH 104/153] fix(browser): cancel trailing debounce timeout on immediate refresh TimeBasedDebouncer can schedule a trailing setTimeout when refresh requests are throttled. If the debounce window later elapses and an immediate refresh runs, that pending timeout was left armed and could fire afterward, causing a duplicate _innerRefresh (e.g. extra live region updates for screen readers). Clear any pending timeout and reset _additionalRefreshRequested before running the immediate refresh path. Co-authored-by: Cursor --- src/browser/TimeBasedDebouncer.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index 959c77a4e..f0dd682e2 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -49,7 +49,12 @@ export class TimeBasedDebouncer implements IRenderDebouncer { // enough time to pass before refreshing again. const refreshRequestTime: number = performance.now(); if (refreshRequestTime - this._lastRefreshMs >= this._debounceThresholdMS) { - // Enough time has lapsed since the last refresh; refresh immediately + // Enough time has elapsed since the last refresh; refresh immediately + if (this._refreshTimeoutID !== undefined) { + clearTimeout(this._refreshTimeoutID); + this._refreshTimeoutID = undefined; + this._additionalRefreshRequested = false; + } this._lastRefreshMs = refreshRequestTime; this._innerRefresh(); } else if (!this._additionalRefreshRequested) { From 69b11a1a48f721c30ba9a79a324a6355212cf681 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:00:22 -0700 Subject: [PATCH 105/153] fix(browser): treat rAF id 0 as scheduled in RenderDebouncer dispose(), addRefreshCallback(), and refresh() used truthiness on _animationFrame, so a requestAnimationFrame handle of 0 would not be cancelled or deduplicated. Compare against undefined instead, matching other timer checks in the browser layer. Co-authored-by: Cursor --- src/browser/RenderDebouncer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/browser/RenderDebouncer.ts b/src/browser/RenderDebouncer.ts index f6ea59bf6..fcfdaa5ec 100644 --- a/src/browser/RenderDebouncer.ts +++ b/src/browser/RenderDebouncer.ts @@ -23,7 +23,7 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback { } public dispose(): void { - if (this._animationFrame) { + if (this._animationFrame !== undefined) { this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame); this._animationFrame = undefined; } @@ -31,7 +31,7 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback { public addRefreshCallback(callback: FrameRequestCallback): number { this._refreshCallbacks.push(callback); - if (!this._animationFrame) { + if (this._animationFrame === undefined) { this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => this._innerRefresh()); } return this._animationFrame; @@ -46,7 +46,7 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback { this._rowStart = this._rowStart !== undefined ? Math.min(this._rowStart, rowStart) : rowStart; this._rowEnd = this._rowEnd !== undefined ? Math.max(this._rowEnd, rowEnd) : rowEnd; - if (this._animationFrame) { + if (this._animationFrame !== undefined) { return; } From 80468eeb4cbf1269c8c7fe0d858a9c9c4bfcfbbc Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:04:23 -0700 Subject: [PATCH 106/153] fix(browser): clear idle blink timer when handle is 0 _clearIdleTimer() used truthiness on _idleTimeout, so a setTimeout handle of 0 would not be cleared. Compare against undefined, consistent with RenderDebouncer rAF handling. Co-authored-by: Cursor --- src/browser/renderer/dom/DomRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index de08ac0ac..383cf3025 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -694,7 +694,7 @@ class CursorBlinkStateManager { } private _clearIdleTimer(): void { - if (this._idleTimeout) { + if (this._idleTimeout !== undefined) { this._coreBrowserService.window.clearTimeout(this._idleTimeout); this._idleTimeout = undefined; } From bc26cc16c1ad1fe7f66db302b93008825262c550 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:14:29 -0700 Subject: [PATCH 107/153] fix(browser): place imports before module const in TimeBasedDebouncer RENDER_DEBOUNCE_THRESHOLD_MS was declared above the import, which breaks ES module conventions and can confuse tooling. Move the import to the top of the module after the license header. Co-authored-by: Cursor --- src/browser/TimeBasedDebouncer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index f0dd682e2..27b692bae 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -3,10 +3,10 @@ * @license MIT */ -const RENDER_DEBOUNCE_THRESHOLD_MS = 1000; // 1 Second - import { IRenderDebouncer } from './Types'; +const RENDER_DEBOUNCE_THRESHOLD_MS = 1000; // 1 Second + /** * Debounces calls to update screen readers to update at most once configurable interval of time. */ From bc42be20740bcbded0771ebe0acfbcfe6c686652 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:37:23 -0700 Subject: [PATCH 108/153] docs(common): correct ST sequence metadata and comment typos - EscapeSequenceParser: ESC ST is ESC backslash (0x1B 0x5C), not ESC plus a double-quote character; align @vt metadata with C1 ST on the following line. - Color.contrastRatio: document l2 as the second luminance argument. - Marker.dispose: fix "change" -> "chance" in dispose listener comment. - Buffer.clear: fix possessive "it's" -> "its" in JSDoc. Co-authored-by: Cursor --- src/common/Color.ts | 2 +- src/common/buffer/Buffer.ts | 2 +- src/common/buffer/Marker.ts | 2 +- src/common/parser/EscapeSequenceParser.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/Color.ts b/src/common/Color.ts index d416d670e..b8a9d8d60 100644 --- a/src/common/Color.ts +++ b/src/common/Color.ts @@ -373,7 +373,7 @@ export function toPaddedHex(c: number): string { /** * Gets the contrast ratio between two relative luminance values. * @param l1 The first relative luminance. - * @param l2 The first relative luminance. + * @param l2 The second relative luminance. * @see https://www.w3.org/TR/WCAG20/#contrast-ratiodef */ export function contrastRatio(l1: number, l2: number): number { diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts index be3df09b1..f41b9da67 100644 --- a/src/common/buffer/Buffer.ts +++ b/src/common/buffer/Buffer.ts @@ -142,7 +142,7 @@ export class Buffer extends Disposable implements IBuffer { } /** - * Clears the buffer to it's initial state, discarding all previous data. + * Clears the buffer to its initial state, discarding all previous data. */ public clear(): void { this._stringCache.clear(); diff --git a/src/common/buffer/Marker.ts b/src/common/buffer/Marker.ts index ed5124add..c21575919 100644 --- a/src/common/buffer/Marker.ts +++ b/src/common/buffer/Marker.ts @@ -30,7 +30,7 @@ export class Marker implements IMarker { } this.isDisposed = true; this.line = -1; - // Emit before super.dispose such that dispose listeners get a change to react + // Emit before super.dispose such that dispose listeners get a chance to react this._onDispose.fire(); dispose(this._disposables); this._disposables.length = 0; diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index a0c97b610..868ab574f 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -18,7 +18,7 @@ import { ApcParser } from './ApcParser'; // @vt: #Y ESC CSI "Control Sequence Introducer" "ESC [" "Start of a CSI sequence." // @vt: #Y ESC OSC "Operating System Command" "ESC ]" "Start of an OSC sequence." // @vt: #Y ESC DCS "Device Control String" "ESC P" "Start of a DCS sequence." -// @vt: #Y ESC ST "String Terminator" "ESC \" "Terminator used for string type sequences." +// @vt: #Y ESC ST "String Terminator" "ESC \\" "Terminator used for string type sequences." // @vt: #Y ESC PM "Privacy Message" "ESC ^" "Start of a privacy message." // @vt: #Y ESC APC "Application Program Command" "ESC _" "Start of an APC sequence." // @vt: #Y C1 CSI "Control Sequence Introducer" "\x9B" "Start of a CSI sequence." From b45167e00b8656917789e27404cefc98fd5a5f7f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:38:08 -0700 Subject: [PATCH 109/153] docs: fix comment typos in ThemeService and Constants Co-authored-by: Cursor --- src/browser/services/ThemeService.ts | 2 +- src/common/buffer/Constants.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/services/ThemeService.ts b/src/browser/services/ThemeService.ts index 9b39b99ad..e12e5e2b3 100644 --- a/src/browser/services/ThemeService.ts +++ b/src/browser/services/ThemeService.ts @@ -132,7 +132,7 @@ export class ThemeService extends Disposable implements IThemeService { colors.ansi[i + 16] = parseColor(theme.extendedAnsi[i], DEFAULT_ANSI_COLORS[i + 16]); } } - // Clear our the cache + // Clear the cache this._contrastCache.clear(); this._halfContrastCache.clear(); this._updateRestoreColors(); diff --git a/src/common/buffer/Constants.ts b/src/common/buffer/Constants.ts index 5ce075cf7..592b5258b 100644 --- a/src/common/buffer/Constants.ts +++ b/src/common/buffer/Constants.ts @@ -24,7 +24,7 @@ export const NULL_CELL_CODE = 0; /** * Whitespace cell. * This is meant as a replacement for empty cells when needed - * during rendering lines to preserve correct aligment. + * during rendering lines to preserve correct alignment. */ export const WHITESPACE_CELL_CHAR = ' '; export const WHITESPACE_CELL_WIDTH = 1; From 7b05c8a1b2d14645347f4dbda2878a8e9533b309 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:51:02 -0700 Subject: [PATCH 110/153] docs: fix comment and JSDoc spelling typos Correct "occurred" in IMouseEvent and keydown JSDoc, remove duplicate "This" in EscapeSequenceParser ZDM comment, use "elapsed" instead of "lapsed" in TimeBasedDebouncer, and fix "event" in Keyboard comment. Co-authored-by: Cursor --- src/browser/CoreBrowserTerminal.ts | 2 +- src/browser/TimeBasedDebouncer.ts | 2 +- src/common/Types.ts | 2 +- src/common/input/Keyboard.ts | 2 +- src/common/parser/EscapeSequenceParser.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index f7e20b914..d089c62c0 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -102,7 +102,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { private _keyDownHandled: boolean = false; /** - * Records whether a keydown event has occured since the last keyup event, i.e. whether a key + * Records whether a keydown event has occurred since the last keyup event, i.e. whether a key * is currently "pressed". */ private _keyDownSeen: boolean = false; diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index 0860e7927..6c92aad24 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -47,7 +47,7 @@ export class TimeBasedDebouncer implements IRenderDebouncer { // enough time to pass before refreshing again. const refreshRequestTime: number = performance.now(); if (refreshRequestTime - this._lastRefreshMs >= this._debounceThresholdMS) { - // Enough time has lapsed since the last refresh; refresh immediately + // Enough time has elapsed since the last refresh; refresh immediately this._lastRefreshMs = refreshRequestTime; this._innerRefresh(); } else if (!this._additionalRefreshRequested) { diff --git a/src/common/Types.ts b/src/common/Types.ts index 9e1481fd3..b73fc8413 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -181,7 +181,7 @@ export interface ICoreMouseEvent { x: number; y: number; /** - * Button the action occured. Due to restrictions of the tracking protocols + * Button the action occurred. Due to restrictions of the tracking protocols * it is not possible to report multiple buttons at once. * Wheel is treated as a button. * There are invalid combinations of buttons and actions possible diff --git a/src/common/input/Keyboard.ts b/src/common/input/Keyboard.ts index 2c3ca28cd..2f8de1963 100644 --- a/src/common/input/Keyboard.ts +++ b/src/common/input/Keyboard.ts @@ -46,7 +46,7 @@ export function evaluateKeyboardEvent( // Whether to cancel event propagation (NOTE: this may not be needed since the event is // canceled at the end of keyDown cancel: false, - // The new key even to emit + // The new key event to emit key: undefined }; const modifiers = (ev.shiftKey ? 1 : 0) | (ev.altKey ? 2 : 0) | (ev.ctrlKey ? 4 : 0) | (ev.metaKey ? 8 : 0); diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index 868ab574f..9fffbcb4f 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -243,7 +243,7 @@ export const VT500_TRANSITION_TABLE = (function (): TransitionTable { * * This parser is currently hardcoded to operate in ZDM (Zero Default Mode) * as suggested by the original parser, thus empty parameters are set to 0. - * This this is not in line with the latest ECMA-48 specification + * This is not in line with the latest ECMA-48 specification * (ZDM was part of the early specs and got completely removed later on). * * Other than the original parser from vt100.net this parser supports From 3a4f9e235d739f7c821eeb3737defd65e5b14ea4 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 21:38:39 -0700 Subject: [PATCH 111/153] docs: fix "transferred" spelling in CONTRIBUTING.md Correct typo in the note about moving support questions to GitHub discussions. Co-authored-by: Cursor --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fe61ad265..b20229f1c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,7 +39,7 @@ The preferred way to report bugs or request features is to use It's pretty common for maintainers of large open source projects to suffer from burnout, especially when needing to triage a large number of incoming issues instead of actually building things. Here are some of the steps we take to try mitigate this: -- Support questions live in [GH discussions](https://github.com/xtermjs/xterm.js/discussions), issues may be transfered there without further comment and core maintainers may or may not participate in discussions. +- Support questions live in [GH discussions](https://github.com/xtermjs/xterm.js/discussions), issues may be transferred there without further comment and core maintainers may or may not participate in discussions. - Issues are strictly for well defined features or bugs that are _actionable_. - Sometimes features are out of scope. A common example of this is a niche feature that the pricipal implementation ([VS Code](https://code.visualstudio.com/)) won't leverage and therefore would be difficult to maintain and likely suffer from bitrot. The reporter may not agree with this, but you could always create an addon if that works or maintain your own fork if it comes to that. - If a feature does not have a clear way forward or needs more discussion it may be closed ro moved to a discussion. From e12f007597c8c5d288bb39817b8967208a18009f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 21:53:21 -0700 Subject: [PATCH 112/153] docs(buffer): fix Content enum names in Constants JSDoc The Content bitmask JSDoc examples used camelCase names that do not exist on the enum (codepointMask, isCombined, hasContent, widthMask, widthShift). Update examples to match the real members: CODEPOINT_MASK, IS_COMBINED_MASK, HAS_CONTENT_MASK, WIDTH_MASK, and WIDTH_SHIFT as used throughout BufferLine and CellData. Co-authored-by: Cursor --- src/common/buffer/Constants.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/common/buffer/Constants.ts b/src/common/buffer/Constants.ts index 592b5258b..c02dba50d 100644 --- a/src/common/buffer/Constants.ts +++ b/src/common/buffer/Constants.ts @@ -36,18 +36,18 @@ export const WHITESPACE_CELL_CODE = 32; export const enum Content { /** * bit 1..21 codepoint, max allowed in UTF32 is 0x10FFFF (21 bits taken) - * read: `codepoint = content & Content.codepointMask;` - * write: `content |= codepoint & Content.codepointMask;` + * read: `codepoint = content & Content.CODEPOINT_MASK;` + * write: `content |= codepoint & Content.CODEPOINT_MASK;` * shortcut if precondition `codepoint <= 0x10FFFF` is met: * `content |= codepoint;` */ CODEPOINT_MASK = 0x1FFFFF, /** - * bit 22 flag indication whether a cell contains combined content - * read: `isCombined = content & Content.isCombined;` - * set: `content |= Content.isCombined;` - * clear: `content &= ~Content.isCombined;` + * bit 22 flag indicating whether a cell contains combined content + * read: `isCombined = content & Content.IS_COMBINED_MASK;` + * set: `content |= Content.IS_COMBINED_MASK;` + * clear: `content &= ~Content.IS_COMBINED_MASK;` */ IS_COMBINED_MASK = 0x200000, // 1 << 21 @@ -55,19 +55,19 @@ export const enum Content { * bit 1..22 mask to check whether a cell contains any string data * we need to check for codepoint and isCombined bits to see * whether a cell contains anything - * read: `isEmpty = !(content & Content.hasContent)` + * read: `isEmpty = !(content & Content.HAS_CONTENT_MASK)` */ HAS_CONTENT_MASK = 0x3FFFFF, /** * bit 23..24 wcwidth value of cell, takes 2 bits (ranges from 0..2) - * read: `width = (content & Content.widthMask) >> Content.widthShift;` - * `hasWidth = content & Content.widthMask;` + * read: `width = (content & Content.WIDTH_MASK) >> Content.WIDTH_SHIFT;` + * `hasWidth = content & Content.WIDTH_MASK;` * as long as wcwidth is highest value in `content`: - * `width = content >> Content.widthShift;` - * write: `content |= (width << Content.widthShift) & Content.widthMask;` + * `width = content >> Content.WIDTH_SHIFT;` + * write: `content |= (width << Content.WIDTH_SHIFT) & Content.WIDTH_MASK;` * shortcut if precondition `0 <= width <= 3` is met: - * `content |= width << Content.widthShift;` + * `content |= width << Content.WIDTH_SHIFT;` */ WIDTH_MASK = 0xC00000, // 3 << 22 WIDTH_SHIFT = 22 From cf1484d7fd43ed32cbeacc69713a23046d523ca1 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 21:54:19 -0700 Subject: [PATCH 113/153] docs(browser): fix incomplete and typo comments in Mouse.ts getCoords had a truncated comment ("if there are no valid") and a typo ("addition" vs "additional") in the selection precision note. Complete the char-size guard comment and fix the selection wording. Co-authored-by: Cursor --- src/browser/input/Mouse.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/input/Mouse.ts b/src/browser/input/Mouse.ts index 5f73743fb..9ea1d6019 100644 --- a/src/browser/input/Mouse.ts +++ b/src/browser/input/Mouse.ts @@ -31,7 +31,7 @@ export function getCoordsRelativeToElement(window: Pick, event: Pick, element: HTMLElement, colCount: number, rowCount: number, hasValidCharSize: boolean, cssCellWidth: number, cssCellHeight: number, isSelection?: boolean): [number, number] | undefined { - // Coordinates cannot be measured if there are no valid + // Coordinates cannot be measured if there is no valid character size. if (!hasValidCharSize) { return undefined; } @@ -45,7 +45,7 @@ export function getCoords(window: Pick, event: Pick< coords[1] = Math.ceil(coords[1] / cssCellHeight); // Ensure coordinates are within the terminal viewport. Note that selections - // need an addition point of precision to cover the end point (as characters + // need an additional point of precision to cover the end point (as characters // cover half of one char and half of the next). coords[0] = Math.min(Math.max(coords[0], 1), colCount + (isSelection ? 1 : 0)); coords[1] = Math.min(Math.max(coords[1], 1), rowCount); From e2d1420ff763c5aa347dc1323631991c6d78468c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 21:57:19 -0700 Subject: [PATCH 114/153] docs(common): fix throughput typo in WriteBuffer comment Correct "throughtput" to "throughput" in the FPS vs throughput tradeoff note inside WriteBuffer. Co-authored-by: Cursor --- src/common/input/WriteBuffer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/input/WriteBuffer.ts b/src/common/input/WriteBuffer.ts index 85774363b..0b4619690 100644 --- a/src/common/input/WriteBuffer.ts +++ b/src/common/input/WriteBuffer.ts @@ -267,7 +267,7 @@ export class WriteBuffer extends Disposable { * this condition here, also the renderer has no way to spot nonsense updates either. * FIXME: A proper fix for this would track the FPS at the renderer entry level separately. * - * If favoring of FPS shows bad throughtput impact, use the following instead. It favors + * If favoring of FPS shows bad throughput impact, use the following instead. It favors * throughput by eval'ing `startTime` upfront pulling at least one more chunk into the * current microtask queue (executed before setTimeout). */ From 5ef80014139c726ad661f57ec59ae1ab970f3860 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 21:58:21 -0700 Subject: [PATCH 115/153] docs(common): fix activeVersion onChange JSDoc wording The onChange event comment said "activate version changed" while the property is activeVersion. Clarify that the event fires when the active Unicode version changes. Co-authored-by: Cursor --- src/common/services/Services.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index cd1bc9cd4..32e67f181 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -350,7 +350,7 @@ export interface IUnicodeService { readonly versions: string[]; /** Currently active version. */ activeVersion: string; - /** Event triggered, when activate version changed. */ + /** Event triggered when the active version changes. */ readonly onChange: IEvent; /** From fd13893687acd77f4acd0d6179ad8de39ebb5a8b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 21:59:22 -0700 Subject: [PATCH 116/153] docs(buffer): fix possessive typo in Windows conpty comment Change "it's view" to "its view" in the comment explaining why new rows are appended on Windows when using conpty. Co-authored-by: Cursor --- src/common/buffer/Buffer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts index f41b9da67..c7d094a43 100644 --- a/src/common/buffer/Buffer.ts +++ b/src/common/buffer/Buffer.ts @@ -197,7 +197,7 @@ export class Buffer extends Disposable implements IBuffer { for (let y = this._rows; y < newRows; y++) { if (this.lines.length < newRows + this.ybase) { if (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 + // Just add the new missing rows on Windows as conpty reprints the screen with its // view of the world. Once a line enters scrollback for conpty it remains there this.lines.push(new BufferLine(this._stringCache, newCols, nullCell, false)); } else { From 26e771d0756dcd15f9498031e7aad862b691e751 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:09:34 -0700 Subject: [PATCH 117/153] docs(common): fix grammar in Unicode and platform comments Use "a Unicode" instead of "an Unicode" on IUnicodeService.register, and "user's platform" instead of "users platform" above the platform detection exports. Co-authored-by: Cursor --- src/common/Platform.ts | 2 +- src/common/services/Services.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/Platform.ts b/src/common/Platform.ts index 9ccf8bda8..a034ca69c 100644 --- a/src/common/Platform.ts +++ b/src/common/Platform.ts @@ -44,7 +44,7 @@ export function getSafariVersion(): number { return parseInt(majorVersion[1], 10); } -// Find the users platform. We use this to interpret the meta key +// Find the user's platform. We use this to interpret the meta key // and ISO third level shifts. // http://stackoverflow.com/q/19877924/577598 export const isMac = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'].includes(platform); diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 32e67f181..8f0aeeed1 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -344,7 +344,7 @@ export type UnicodeCharWidth = 0 | 1 | 2; export const IUnicodeService = createDecorator('UnicodeService'); export interface IUnicodeService { serviceBrand: undefined; - /** Register an Unicode version provider. */ + /** Register a Unicode version provider. */ register(provider: IUnicodeVersionProvider): void; /** Registered Unicode versions. */ readonly versions: string[]; From 8854430df4c08b58a312129bf824f685c787b856 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:11:27 -0700 Subject: [PATCH 118/153] docs(parser): fix sub-parameter limit name in Params JSDoc addSubParam documents a cap on stored sub parameters; the limit is enforced via maxSubParamsLength, not subParamsLength (the current count). Co-authored-by: Cursor --- src/common/parser/Params.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/parser/Params.ts b/src/common/parser/Params.ts index 6986a48ba..76dbe2b3d 100644 --- a/src/common/parser/Params.ts +++ b/src/common/parser/Params.ts @@ -172,7 +172,7 @@ export class Params implements IParams { * Add a sub parameter value. * The sub parameter is automatically associated with the last parameter value. * Thus it is not possible to add a subparameter without any parameter added yet. - * `Params` only stores up to `subParamsLength` sub parameters, any later + * `Params` only stores up to `maxSubParamsLength` sub parameters, any later * sub parameter will be ignored. */ public addSubParam(value: number): void { From 2db34c49554bb4292bea9ea2aaec3e321316bbb4 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:13:26 -0700 Subject: [PATCH 119/153] docs(common): fix main thread spelling in TaskQueue comment Change "mainthread" to "main thread" in the workload scheduling note. Co-authored-by: Cursor --- src/common/TaskQueue.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/TaskQueue.ts b/src/common/TaskQueue.ts index 9e15c823a..b1bcf023a 100644 --- a/src/common/TaskQueue.ts +++ b/src/common/TaskQueue.ts @@ -8,7 +8,7 @@ import type { ILogService } from './services/Services'; interface ITaskQueue { /** * Adds a task to the queue which will run in a future idle callback. - * To avoid perceivable stalls on the mainthread, tasks with heavy workload + * To avoid perceivable stalls on the main thread, tasks with heavy workload * should split their work into smaller pieces and return `true` to get * called again until the work is done (on falsy return value). */ From be3dd80171748116cc0fc9cfecb4df733dc8ffb8 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 21:35:46 -0700 Subject: [PATCH 120/153] docs(parser): fix JSDoc typos in IFunctionIdentifier Correct "representation" and "registration" spelling in the comment describing how handler identifiers are encoded for EscapeSequenceParser. Co-authored-by: Cursor --- src/common/parser/Types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/parser/Types.ts b/src/common/parser/Types.ts index 7306cdf76..f7c8ae2d4 100644 --- a/src/common/parser/Types.ts +++ b/src/common/parser/Types.ts @@ -234,8 +234,8 @@ export interface IApcParser extends ISubParser Date: Sun, 31 May 2026 20:38:51 -0700 Subject: [PATCH 121/153] docs(addons): fix comment typos in unicode-graphemes and web-links Correct grapheme/handling spelling in UnicodeGraphemesAddon header and everything/addresses typos in WebLinksAddon URL matcher comments. Co-authored-by: Cursor --- addons/addon-unicode-graphemes/src/UnicodeGraphemesAddon.ts | 2 +- addons/addon-web-links/src/WebLinksAddon.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/addon-unicode-graphemes/src/UnicodeGraphemesAddon.ts b/addons/addon-unicode-graphemes/src/UnicodeGraphemesAddon.ts index 574a36cc8..09a073571 100644 --- a/addons/addon-unicode-graphemes/src/UnicodeGraphemesAddon.ts +++ b/addons/addon-unicode-graphemes/src/UnicodeGraphemesAddon.ts @@ -2,7 +2,7 @@ * Copyright (c) 2023 The xterm.js authors. All rights reserved. * @license MIT * - * UnicodeVersionProvider for V15 with grapeme cluster handleing. + * UnicodeVersionProvider for V15 with grapheme cluster handling. */ import type { Terminal, ITerminalAddon, IUnicodeHandling } from '@xterm/xterm'; diff --git a/addons/addon-web-links/src/WebLinksAddon.ts b/addons/addon-web-links/src/WebLinksAddon.ts index 3328a3c3f..5e9f6a0ef 100644 --- a/addons/addon-web-links/src/WebLinksAddon.ts +++ b/addons/addon-web-links/src/WebLinksAddon.ts @@ -7,13 +7,13 @@ import type { Terminal, ITerminalAddon, IDisposable } from '@xterm/xterm'; import type { WebLinksAddon as IWebLinksApi } from '@xterm/addon-web-links'; import { ILinkProviderOptions, WebLinkProvider } from './WebLinkProvider'; -// consider everthing starting with http:// or https:// +// consider everything starting with http:// or https:// // up to first whitespace, `"` or `'` as url // NOTE: The repeated end clause is needed to not match a dangling `:` // resembling the old (...)*([^:"\'\\s]) final path clause // additionally exclude early + final: // - unsafe from rfc3986: !*'() -// - unsafe chars from rfc1738: {}|\^~[]` (minus [] as we need them for ipv6 adresses, also allow ~) +// - unsafe chars from rfc1738: {}|\^~[]` (minus [] as we need them for ipv6 addresses, also allow ~) // also exclude as finals: // - final interpunction like ,.!? // - any sort of brackets <>()[]{} (not spec conform, but often used to enclose urls) From a47e4905e6d2f7f8098f23da01a763e1c8500a7a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 20:53:52 -0700 Subject: [PATCH 122/153] docs(addon-webgl): fix comment typos in TextureAtlas and GlyphRenderer Correct "transferred", remove duplicate "is", and use "one-off" instead of "once of" in developer-facing comments. Co-authored-by: Cursor --- addons/addon-webgl/src/GlyphRenderer.ts | 2 +- addons/addon-webgl/src/TextureAtlas.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/addon-webgl/src/GlyphRenderer.ts b/addons/addon-webgl/src/GlyphRenderer.ts index 650fc451a..4117525c4 100644 --- a/addons/addon-webgl/src/GlyphRenderer.ts +++ b/addons/addon-webgl/src/GlyphRenderer.ts @@ -220,7 +220,7 @@ export class GlyphRenderer extends Disposable { public updateCell(x: number, y: number, code: number, bg: number, fg: number, ext: number, chars: string, width: number, lastBg: number): void { // Since this function is called for every cell (`rows*cols`), it must be very optimized. It // should not instantiate any variables unless a new glyph is drawn to the cache where the - // slight slowdown is acceptable for the developer ergonomics provided as it's a once of for + // slight slowdown is acceptable for the developer ergonomics provided as it's a one-off for // each glyph. this._updateCell(this._vertices.attributes, x, y, code, bg, fg, ext, chars, width, lastBg); } diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index dab0b1c85..31db49a7f 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -69,7 +69,7 @@ export class TextureAtlas implements ITextureAtlas { private _overflowSizePage: AtlasPage | undefined; private _tmpCanvas: HTMLCanvasElement; - // A temporary context that glyphs are drawn to before being transfered to the atlas. + // A temporary context that glyphs are drawn to before being transferred to the atlas. private _tmpCtx: CanvasRenderingContext2D; private _workBoundingBox: IBoundingBox = { top: 0, left: 0, bottom: 0, right: 0 }; @@ -152,7 +152,7 @@ export class TextureAtlas implements ITextureAtlas { } private _createNewPage(): AtlasPage { - // Try merge the set of the 4 most used pages of the largest size. This is is deferred to a + // Try merge the set of the 4 most used pages of the largest size. This is deferred to a // microtask to ensure it does not interrupt textures that will be rendered in the current // animation frame which would result in blank rendered areas. This is actually not that // expensive relative to drawing the glyphs, so there is no need to wait for an idle callback. From 58fc09ff0e33017b87525b4d599767a0ef8e2594 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:05:21 -0700 Subject: [PATCH 123/153] docs(webgl): remove duplicate word in context lost README The webglcontextlost section repeated "fired" ("fires the ... event fired on the canvas"). Drop the extra word. Co-authored-by: Cursor --- addons/addon-webgl/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-webgl/README.md b/addons/addon-webgl/README.md index 9d89863f3..47a03b72e 100644 --- a/addons/addon-webgl/README.md +++ b/addons/addon-webgl/README.md @@ -23,7 +23,7 @@ See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/addon- ### Handling Context Loss -The browser may drop WebGL contexts for various reasons like OOM or after the system has been suspended. There is an API exposed that fires the `webglcontextlost` event fired on the canvas so embedders can handle it however they wish. An easy, but suboptimal way, to handle this is by disposing of WebglAddon when the event fires: +The browser may drop WebGL contexts for various reasons like OOM or after the system has been suspended. There is an API exposed that fires the `webglcontextlost` event on the canvas so embedders can handle it however they wish. An easy, but suboptimal way, to handle this is by disposing of WebglAddon when the event fires: ```ts const terminal = new Terminal(); From 3494f6b5b0c31bcaf2bf52927749efe714d84952 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 1 Jun 2026 15:29:02 +0000 Subject: [PATCH 124/153] docs: fix additional comment typos across renderer and selection (addons only) Co-authored-by: Daniel Imms --- addons/addon-webgl/src/WebglRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 264bfda6e..d26c6d25e 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -667,7 +667,7 @@ export class WebglRenderer extends Disposable implements IRenderer { // The the size of the canvas on the page. It's important that this rounds to nearest integer // and not ceils as browsers often have floating point precision issues where // `window.devicePixelRatio` ends up being something like `1.100000023841858` for example, when - // it's actually 1.1. Ceiling may causes blurriness as the backing canvas image is 1 pixel too + // it's actually 1.1. Ceiling may cause blurriness as the backing canvas image is 1 pixel too // large for the canvas element size. this.dimensions.css.canvas.height = Math.round(this.dimensions.device.canvas.height / this._devicePixelRatio); this.dimensions.css.canvas.width = Math.round(this.dimensions.device.canvas.width / this._devicePixelRatio); From fa5eb33cdb141762042410cb80bacd8087cbea45 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 1 Jun 2026 15:29:02 +0000 Subject: [PATCH 125/153] fix: small comment, error string, and guard cleanups (addons only) Co-authored-by: Daniel Imms --- addons/addon-serialize/src/SerializeAddon.ts | 2 +- addons/addon-webgl/src/TextureAtlas.ts | 2 +- addons/addon-webgl/src/WebglAddon.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/addon-serialize/src/SerializeAddon.ts b/addons/addon-serialize/src/SerializeAddon.ts index 0aad8e1d6..26ede1c41 100644 --- a/addons/addon-serialize/src/SerializeAddon.ts +++ b/addons/addon-serialize/src/SerializeAddon.ts @@ -173,7 +173,7 @@ class StringSerializeHandler extends BaseSerializeHandler { private _thisRowLastSecondChar: IBufferCell = this._buffer.getNullCell(); private _nextRowFirstChar: IBufferCell = this._buffer.getNullCell(); protected _rowEnd(row: number, isLastRow: boolean): void { - // if there is colorful empty cell at line end, whe must pad it back, or the the color block + // if there is colorful empty cell at line end, we must pad it back, or the color block // will missing if (this._nullCellCount > 0 && !equalBg(this._cursorStyle, this._backgroundCell)) { // use clear right to set background. diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index 31db49a7f..53d2d056f 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -1125,7 +1125,7 @@ function clearColor(imageData: ImageData, bg: IColor, fg: IColor, enableThreshol // were covered (fg=#8ae234, bg=#c4a000). const threshold = Math.floor((Math.abs(r - fgR) + Math.abs(g - fgG) + Math.abs(b - fgB)) / 12); - // Set alpha channel of relevent pixels to 0 + // Set alpha channel of relevant pixels to 0 let isEmpty = true; for (let offset = 0; offset < imageData.data.length; offset += 4) { // Check exact match diff --git a/addons/addon-webgl/src/WebglAddon.ts b/addons/addon-webgl/src/WebglAddon.ts index 624c9ea38..f61206b13 100644 --- a/addons/addon-webgl/src/WebglAddon.ts +++ b/addons/addon-webgl/src/WebglAddon.ts @@ -40,7 +40,7 @@ export class WebglAddon extends Disposable implements ITerminalAddon, IWebglApi }; const gl = document.createElement('canvas').getContext('webgl2', contextAttributes) as IWebGL2RenderingContext; if (!gl) { - throw new Error('Webgl2 is only supported on Safari 16 and above'); + throw new Error('WebGL2 is only supported on Safari 16 and above'); } } super(); From 1faa41eac1585260786e87cb0db2ca2aea88d620 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 1 Jun 2026 15:29:03 +0000 Subject: [PATCH 126/153] docs: fix typos in typings, tests, and core comments (addons only) Co-authored-by: Daniel Imms --- addons/addon-clipboard/typings/addon-clipboard.d.ts | 2 +- addons/addon-webgl/typings/addon-webgl.d.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/addon-clipboard/typings/addon-clipboard.d.ts b/addons/addon-clipboard/typings/addon-clipboard.d.ts index f8e024ac3..f5a892e21 100644 --- a/addons/addon-clipboard/typings/addon-clipboard.d.ts +++ b/addons/addon-clipboard/typings/addon-clipboard.d.ts @@ -82,7 +82,7 @@ declare module '@xterm/addon-clipboard' { /** * The clipboard provider interface that enables xterm.js to access the browser clipboard. * - * Note this this clipboard provider does not implement own cut buffers as xterm does, + * Note that this clipboard provider does not implement own cut buffers as xterm does, * but redirect the selection parameter always to navigator.clipboard. */ export class BrowserClipboardProvider implements IClipboardProvider{ diff --git a/addons/addon-webgl/typings/addon-webgl.d.ts b/addons/addon-webgl/typings/addon-webgl.d.ts index 37b6b8a95..37acbb8a9 100644 --- a/addons/addon-webgl/typings/addon-webgl.d.ts +++ b/addons/addon-webgl/typings/addon-webgl.d.ts @@ -23,12 +23,12 @@ declare module '@xterm/addon-webgl' { public readonly onChangeTextureAtlas: IEvent; /** - * An event that is fired when the a new page is added to the texture atlas. + * An event that is fired when a new page is added to the texture atlas. */ public readonly onAddTextureAtlasCanvas: IEvent; /** - * An event that is fired when the a page is removed from the texture atlas. + * An event that is fired when a page is removed from the texture atlas. */ public readonly onRemoveTextureAtlasCanvas: IEvent; From 0b590dacd64d489669eb2b73edd44d40ab72c6ce Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 1 Jun 2026 15:29:04 +0000 Subject: [PATCH 127/153] docs: fix comment typos in BufferReflow, Types, and addons (addons only) Co-authored-by: Daniel Imms --- addons/addon-serialize/test/SerializeAddon.test.ts | 2 +- addons/addon-webgl/src/WebglRenderer.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/addon-serialize/test/SerializeAddon.test.ts b/addons/addon-serialize/test/SerializeAddon.test.ts index fb42d807d..c8dba3039 100644 --- a/addons/addon-serialize/test/SerializeAddon.test.ts +++ b/addons/addon-serialize/test/SerializeAddon.test.ts @@ -192,7 +192,7 @@ test.describe('SerializeAddon', () => { const cols = 10; const line = '+'.repeat(cols); const lines: string[] = [ - sgr(FG_P16_GREEN) + line, // Workaround: If we clear all flags a the end, serialize will use \x1b[0m to clear instead of the sepcific disable sequence + sgr(FG_P16_GREEN) + line, // Workaround: If we clear all flags at the end, serialize will use \x1b[0m to clear instead of the specific disable sequence sgr(INVERSE) + line, sgr(BOLD) + line, sgr(UNDERLINED) + line, diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index d26c6d25e..2f38e03cb 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -664,7 +664,7 @@ export class WebglRenderer extends Disposable implements IRenderer { this.dimensions.device.canvas.height = this._terminal.rows * this.dimensions.device.cell.height; this.dimensions.device.canvas.width = this._terminal.cols * this.dimensions.device.cell.width; - // The the size of the canvas on the page. It's important that this rounds to nearest integer + // The size of the canvas on the page. It's important that this rounds to nearest integer // and not ceils as browsers often have floating point precision issues where // `window.devicePixelRatio` ends up being something like `1.100000023841858` for example, when // it's actually 1.1. Ceiling may cause blurriness as the backing canvas image is 1 pixel too From be38252e37ecfa1f86f82e8a02543d8ea2d9bed4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 1 Jun 2026 15:29:04 +0000 Subject: [PATCH 128/153] docs: fix comment typos in Constants, DomRenderer, and addons (addons only) Co-authored-by: Daniel Imms --- addons/addon-ligatures/src/index.ts | 2 +- addons/addon-serialize/src/SerializeAddon.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/addon-ligatures/src/index.ts b/addons/addon-ligatures/src/index.ts index fb8e85e73..2fc664f5b 100644 --- a/addons/addon-ligatures/src/index.ts +++ b/addons/addon-ligatures/src/index.ts @@ -21,7 +21,7 @@ const CACHE_SIZE = 100000; /** * Enable ligature support for the provided Terminal instance. To function - * properly, this must be called after `open()` is called on the therminal. If + * properly, this must be called after `open()` is called on the terminal. If * the font currently in use supports ligatures, the terminal will automatically * start to render them. * @param term Terminal instance from xterm.js diff --git a/addons/addon-serialize/src/SerializeAddon.ts b/addons/addon-serialize/src/SerializeAddon.ts index 26ede1c41..e1728feb2 100644 --- a/addons/addon-serialize/src/SerializeAddon.ts +++ b/addons/addon-serialize/src/SerializeAddon.ts @@ -221,7 +221,7 @@ class StringSerializeHandler extends BaseSerializeHandler { // you can't use control sequence to move cursor to (x === row) (thisRowLastChar.getChars() || thisRowLastChar.getWidth() === 0) && // change background of the first wrapped cell also affects BCE - // so we mark it as invalid to simply the process to determine line separator + // so we mark it as invalid to simplify the process to determine line separator equalBg(thisRowLastChar, nextRowFirstChar) ) { isValid = true; @@ -233,7 +233,7 @@ class StringSerializeHandler extends BaseSerializeHandler { isNextRowFirstCharDoubleWidth && (thisRowLastSecondChar.getChars() || thisRowLastSecondChar.getWidth() === 0) && // change background of the first wrapped cell also affects BCE - // so we mark it as invalid to simply the process to determine line separator + // so we mark it as invalid to simplify the process to determine line separator equalBg(thisRowLastChar, nextRowFirstChar) && equalBg(thisRowLastSecondChar, nextRowFirstChar) ) { From 92a1bfcf2e7f06523153a68bd2e73f7156619f7d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:06:29 -0700 Subject: [PATCH 129/153] test(search): send real CSI CUP in #3834 null-char case The integration test intended to move the cursor forward with ESC [ C but wrote the literal string \x1b[C due to double escaping. Use a single-escaped '\x1b[CHi Hi' so the terminal receives the control sequence and creates the null cell the scenario describes. Co-authored-by: Cursor --- addons/addon-search/test/SearchAddon.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-search/test/SearchAddon.test.ts b/addons/addon-search/test/SearchAddon.test.ts index a4ccb6c0e..89f0ff4fa 100644 --- a/addons/addon-search/test/SearchAddon.test.ts +++ b/addons/addon-search/test/SearchAddon.test.ts @@ -501,7 +501,7 @@ test.describe('Search Tests', () => { window.search.onDidChangeResults(e => window.calls.push(e)); `); // Move cursor forward 1 time to create a null character, as opposed to regular whitespace - await ctx.proxy.write('\\x1b[CHi Hi'); + await ctx.proxy.write('\x1b[CHi Hi'); strictEqual(await ctx.page.evaluate(`window.search.findPrevious('h', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); deepStrictEqual(await ctx.page.evaluate('window.calls'), [ { resultCount: 2, resultIndex: 1 } From a9a2a8e3c166a05bb13bbab0f812ca01de8f0297 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:28:51 -0700 Subject: [PATCH 130/153] test(search): reject readFile errors in #2444 fixture loader beforeAll always resolved the readFile promise with data even when err was set, which led to calling toString on undefined. Reject the promise on err so fixture load failures surface clearly. Use res/rej callback names to avoid shadowing path.resolve. Co-authored-by: Cursor --- addons/addon-search/test/SearchAddon.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-search/test/SearchAddon.test.ts b/addons/addon-search/test/SearchAddon.test.ts index 89f0ff4fa..3438772a8 100644 --- a/addons/addon-search/test/SearchAddon.test.ts +++ b/addons/addon-search/test/SearchAddon.test.ts @@ -417,7 +417,7 @@ test.describe('Search Tests', () => { test.describe('#2444 wrapped line content not being found', () => { let fixture: string; test.beforeAll(async () => { - fixture = (await new Promise(r => readFile(resolve(__dirname, '../fixtures/issue-2444'), (err, data) => r(data)))).toString(); + fixture = (await new Promise((res, rej) => readFile(resolve(__dirname, '../fixtures/issue-2444'), (err, data) => err ? rej(err) : res(data)))).toString(); if (process.platform !== 'win32') { fixture = fixture.replace(/\n/g, '\n\r'); } From 8e7e4f16e5cf5487afdc366762ac115e1cb90108 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 May 2026 22:10:27 -0700 Subject: [PATCH 131/153] test(parser): fix typo in EscapeSequenceParser test title Rename the mocha case "inital states" to "initial states". Co-authored-by: Cursor --- src/common/parser/EscapeSequenceParser.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/parser/EscapeSequenceParser.test.ts b/src/common/parser/EscapeSequenceParser.test.ts index f684da2c8..73238d039 100644 --- a/src/common/parser/EscapeSequenceParser.test.ts +++ b/src/common/parser/EscapeSequenceParser.test.ts @@ -243,7 +243,7 @@ describe('EscapeSequenceParser', () => { p = new TestEscapeSequenceParser(tansitions); assert.deepEqual(p.transitions, tansitions); }); - it('inital states', () => { + it('initial states', () => { assert.equal(parser.initialState, ParserState.GROUND); assert.equal(parser.currentState, ParserState.GROUND); assert.equal(parser.osc, ''); From 9a70816dad388d7d09ab2716d28b0a190221080b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 1 Jun 2026 07:42:15 -0700 Subject: [PATCH 132/153] test(addon-attach): correct Playwright suite name in AttachAddon tests The integration test file used test.describe('Search Tests'), copied from addon-search. That mislabels attach-addon results in Playwright reports and makes the suite harder to filter or debug. Rename the block to 'AttachAddon' so the describe name matches the addon under test. No test logic changed. Validated with: npm run build && npm run esbuild, npm run test-unit, npm run esbuild-demo-client && npm run esbuild-demo-server && npm run test-integration, npm run package, npm run package-headless. Co-authored-by: Cursor --- addons/addon-attach/test/AttachAddon.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-attach/test/AttachAddon.test.ts b/addons/addon-attach/test/AttachAddon.test.ts index b49d9f337..ee9b22c2e 100644 --- a/addons/addon-attach/test/AttachAddon.test.ts +++ b/addons/addon-attach/test/AttachAddon.test.ts @@ -15,7 +15,7 @@ test.beforeAll(async ({ browser }) => { }); test.afterAll(async () => await ctx.page.close()); -test.describe('Search Tests', () => { +test.describe('AttachAddon', () => { test.beforeEach(async () => { await ctx.proxy.reset(); From 967d3f59e64e819637340972107e912a437a6113 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 1 Jun 2026 15:29:08 +0000 Subject: [PATCH 133/153] docs(addon-image): fix README spelling and wording README spelling and wording fixes only; no code changes. Co-authored-by: Daniel Imms --- addons/addon-image/README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/addons/addon-image/README.md b/addons/addon-image/README.md index 80c88de8e..3b636d922 100644 --- a/addons/addon-image/README.md +++ b/addons/addon-image/README.md @@ -90,7 +90,7 @@ terminal.loadAddon(imageAddon); therefore palette animations cannot be used. - **SIXEL Raster Attributes Handling** - If raster attributes were found in the SIXEL data (level 2), the image will always be truncated to the given height/width extend. We deviate here from the specification on purpose, as it allows several processing optimizations. For level 1 SIXEL data without any raster attributes the image can freely grow in width and height up to the last data byte, which has a much higher processing penalty. In general encoding libraries should not create level 1 data anymore and should not produce pixel information beyond the announced height/width extend. Both is discouraged by the >30 years old specification. + If raster attributes were found in the SIXEL data (level 2), the image will always be truncated to the given height/width extent. We deviate here from the specification on purpose, as it allows several processing optimizations. For level 1 SIXEL data without any raster attributes the image can freely grow in width and height up to the last data byte, which has a much higher processing penalty. In general encoding libraries should not create level 1 data anymore and should not produce pixel information beyond the announced height/width extent. Both is discouraged by the >30 years old specification. Currently the SIXEL implementation of the addon does not take custom pixel sizes into account, a SIXEL pixel will map 1:1 to a screen pixel. @@ -149,9 +149,9 @@ The addon provides the following API endpoints to retrieve raw image data as can ### Memory Usage -The addon does most image processing in Javascript and therefore can occupy a rather big amount of memory. To get an idea where the memory gets eaten, lets look at the data flow and processing steps: -- Incomming image data chunk at `term.write` (terminal) - `term.write` might stock up incoming chunks. To circumvent this, you will need proper flow control (see xterm.js docs). Note that with image output it is more likely to run into this issue, as it can create lots of MBs in very short time. +The addon does most image processing in JavaScript and therefore can occupy a rather big amount of memory. To get an idea where the memory gets eaten, let's look at the data flow and processing steps: +- Incoming image data chunk at `term.write` (terminal) + `term.write` might stack up incoming chunks. To circumvent this, you will need proper flow control (see xterm.js docs). Note that with image output it is more likely to run into this issue, as it can create lots of MBs in very short time. - Sequence Parser (terminal) The parser operates on a buffer containing up to 2^17 codepoints (~0.5 MB). - Sequence Handler - Chunk Decoding (addon) @@ -177,18 +177,18 @@ const totalActive = storageBytes + decodingBytes; ``` Note that browsers have offloading tricks for rarely touched memory segments, esp. `storageBytes` might not directly translate into real memory usage. Usage peaks will happen during active decoding of multiple big images due to the need of 2 full pixel buffers at the same time, which cannot be offloaded. Thus you may want to keep an eye on `pixelLimit` under limited memory conditions. -Further note that the formulas above do not respect the Javascript object's overhead. Compared to the raw buffer needs the book keeping by these objects is rather small (<<5%). +Further note that the formulas above do not respect the JavaScript object's overhead. Compared to the raw buffer needs the bookkeeping by these objects is rather small (<<5%). _Why should I care about memory usage at all?_ -Well you don't have to, and it probably will just work fine with the addon defaults. But for bigger integrations, where much more data is held in the Javascript context (like multiple terminals on one page), it is likely to hit the engine's memory limit sooner or later under decoding and/or storage pressure. +Well you don't have to, and it probably will just work fine with the addon defaults. But for bigger integrations, where much more data is held in the JavaScript context (like multiple terminals on one page), it is likely to hit the engine's memory limit sooner or later under decoding and/or storage pressure. _How can I adjust the memory usage?_ - `pixelLimit` - A constructor setting, thus you would have to anticipate, whether (multiple) terminals in your page gonna do lots of concurrent decoding. Since this is normally not the case and the memory usage is only temporarily peaking, a rather high value should work even with multiple terminals in one page. + A constructor setting, thus you would have to anticipate, whether (multiple) terminals in your page gonna do lots of concurrent decoding. Since this is normally not the case and the memory usage only peaks temporarily, a rather high value should work even with multiple terminals in one page. - `storageLimit` - A constructor and a runtime setting. In conjunction with `storageUsage` you can do runtime checks and adjust the limit to your needs. If you have to lower the limit below the current usage, images will be removed in FIFO order and may turn into a placeholder in the terminal's scrollback (if `showPlaceholder` is set). When adjusting keep in mind to leave enough room for memory peaking for decoding. + A constructor and a runtime setting. In conjunction with `storageUsage` you can do runtime checks and adjust the limit to your needs. If you have to lower the limit below the current usage, images will be removed in FIFO order and may turn into a placeholder in the terminal's scrollback (if `showPlaceholder` is set). When adjusting keep in mind to leave enough room for peak memory use during decoding. - `sixelSizeLimit | iipSizeLimit | kittySizeLimit` - Constructor settings. This has only a small impact on peaking memory during decoding. It is meant to avoid processing of overly big or broken image sequences at an earlier phase, thus may stop the invoked decoders from entering memory intensive tasks for potentially invalid data. + Constructor settings. This has only a small impact on peak memory during decoding. It is meant to avoid processing of overly big or broken image sequences at an earlier phase, thus may stop the invoked decoders from entering memory intensive tasks for potentially invalid data. ### Terminal Interaction From 096707d7363cb7085e568eecf96e7f2449a75381 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 1 Jun 2026 15:29:18 +0000 Subject: [PATCH 134/153] test: fix typos in test titles and comments Apply test-only hunks from docs typo fixes: SharedRendererTests duplicate word, SerializeAddon workaround comment, and mouse range spelling in MouseService and InputHandler tests. Co-authored-by: Daniel Imms --- addons/addon-serialize/test/SerializeAddon.test.ts | 2 +- src/browser/services/MouseService.test.ts | 2 +- src/common/InputHandler.test.ts | 2 +- test/playwright/SharedRendererTests.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/addon-serialize/test/SerializeAddon.test.ts b/addons/addon-serialize/test/SerializeAddon.test.ts index fb42d807d..c8dba3039 100644 --- a/addons/addon-serialize/test/SerializeAddon.test.ts +++ b/addons/addon-serialize/test/SerializeAddon.test.ts @@ -192,7 +192,7 @@ test.describe('SerializeAddon', () => { const cols = 10; const line = '+'.repeat(cols); const lines: string[] = [ - sgr(FG_P16_GREEN) + line, // Workaround: If we clear all flags a the end, serialize will use \x1b[0m to clear instead of the sepcific disable sequence + sgr(FG_P16_GREEN) + line, // Workaround: If we clear all flags at the end, serialize will use \x1b[0m to clear instead of the specific disable sequence sgr(INVERSE) + line, sgr(BOLD) + line, sgr(UNDERLINED) + line, diff --git a/src/browser/services/MouseService.test.ts b/src/browser/services/MouseService.test.ts index 6d760ff0a..b4655a1a1 100644 --- a/src/browser/services/MouseService.test.ts +++ b/src/browser/services/MouseService.test.ts @@ -176,7 +176,7 @@ describe('MouseService _triggerMouseEvent', () => { for (let i = 0; i < bufferService.cols; ++i) { assert.equal(trigger({ col: i, row: 0, x: 0, y: 0, button: CoreMouseButton.LEFT, action: CoreMouseAction.DOWN }), true); if (i > 222) { - // supress mouse reports if we are out of addressible range (max. 222) + // suppress mouse reports if we are out of addressable range (max. 222) assert.deepEqual(toBytes(reports.pop()), []); } else { assert.deepEqual(toBytes(reports.pop()), [0x1b, 0x5b, 0x4d, 0x20, i + 33, 0x21]); diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index 3cfc9e019..8f0109727 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -1241,7 +1241,7 @@ describe('InputHandler', () => { await inputHandler.parseP('\x1b[e'); assert.deepEqual(getCursor(bufferService), [8, 5]); }); - describe('should clamp cursor into addressible range', () => { + describe('should clamp cursor into addressable range', () => { it('CUF', async () => { bufferService.buffer.x = 10000; bufferService.buffer.y = 10000; diff --git a/test/playwright/SharedRendererTests.ts b/test/playwright/SharedRendererTests.ts index db5c4c073..1983fd410 100644 --- a/test/playwright/SharedRendererTests.ts +++ b/test/playwright/SharedRendererTests.ts @@ -980,7 +980,7 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void }); test.describe('selectionInactiveBackground', async () => { - test('should render the the inactive selection when not focused', async () => { + test('should render the inactive selection when not focused', async () => { const theme: ITheme = { selectionBackground: '#FF000080', selectionInactiveBackground: '#0000FF80' From 6426573f455eba613655a641e5e90da4b3dc48e6 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 1 Jun 2026 15:30:04 +0000 Subject: [PATCH 135/153] docs(addon-web-links): shorten comment to satisfy max-len after typo fix Co-authored-by: Daniel Imms --- addons/addon-web-links/src/WebLinksAddon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-web-links/src/WebLinksAddon.ts b/addons/addon-web-links/src/WebLinksAddon.ts index 5e9f6a0ef..6ea1bc8d9 100644 --- a/addons/addon-web-links/src/WebLinksAddon.ts +++ b/addons/addon-web-links/src/WebLinksAddon.ts @@ -13,7 +13,7 @@ import { ILinkProviderOptions, WebLinkProvider } from './WebLinkProvider'; // resembling the old (...)*([^:"\'\\s]) final path clause // additionally exclude early + final: // - unsafe from rfc3986: !*'() -// - unsafe chars from rfc1738: {}|\^~[]` (minus [] as we need them for ipv6 addresses, also allow ~) +// - unsafe chars from rfc1738: {}|\^~[]` (minus [] for ipv6 addresses, also allow ~) // also exclude as finals: // - final interpunction like ,.!? // - any sort of brackets <>()[]{} (not spec conform, but often used to enclose urls) From 94958c2489ec7ec9c16c1c51ecab9f4bb416dce8 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 1 Jun 2026 08:33:02 -0700 Subject: [PATCH 136/153] Fix whitespace --- src/common/parser/EscapeSequenceParser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index 9fffbcb4f..87844113a 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -18,7 +18,7 @@ import { ApcParser } from './ApcParser'; // @vt: #Y ESC CSI "Control Sequence Introducer" "ESC [" "Start of a CSI sequence." // @vt: #Y ESC OSC "Operating System Command" "ESC ]" "Start of an OSC sequence." // @vt: #Y ESC DCS "Device Control String" "ESC P" "Start of a DCS sequence." -// @vt: #Y ESC ST "String Terminator" "ESC \\" "Terminator used for string type sequences." +// @vt: #Y ESC ST "String Terminator" "ESC \\" "Terminator used for string type sequences." // @vt: #Y ESC PM "Privacy Message" "ESC ^" "Start of a privacy message." // @vt: #Y ESC APC "Application Program Command" "ESC _" "Start of an APC sequence." // @vt: #Y C1 CSI "Control Sequence Introducer" "\x9B" "Start of a CSI sequence." From 454e0749c267e88ace5c3604f513cff6d9ea1589 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 1 Jun 2026 16:15:01 +0000 Subject: [PATCH 137/153] fix(browser): use ??= in RenderDebouncer for lint Co-authored-by: Daniel Imms --- src/browser/RenderDebouncer.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/browser/RenderDebouncer.ts b/src/browser/RenderDebouncer.ts index fcfdaa5ec..eeadbe018 100644 --- a/src/browser/RenderDebouncer.ts +++ b/src/browser/RenderDebouncer.ts @@ -31,9 +31,7 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback { public addRefreshCallback(callback: FrameRequestCallback): number { this._refreshCallbacks.push(callback); - if (this._animationFrame === undefined) { - this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => this._innerRefresh()); - } + this._animationFrame ??= this._coreBrowserService.window.requestAnimationFrame(() => this._innerRefresh()); return this._animationFrame; } From 21c99d05748513674de7bdea5eea465450822c11 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 1 Jun 2026 19:34:13 -0700 Subject: [PATCH 138/153] fix(bin): script path, exit codes, and integration suite handling (#5973) ## Summary Improves reliability and correctness of `bin/` maintenance scripts used for local development and CI. ## Behavioral fixes - **Exit codes**: `test_integration.js` exits with code 1 when `--suite` names an unknown suite; `test_unit.js` uses a non-zero fallback (`-1`) when the coverage or mocha child process has no status; `esbuild_all.mjs` exits non-zero if any parallel esbuild child fails. - **Repo root**: `setup-full.mjs` resolves the repository root one directory above `bin/` (was incorrectly using `bin/` as root). - **esbuild_all**: Propagates child build failures instead of always exiting 0. - **Integration suites**: When `--suite` is omitted, all configured Playwright suites (core + addons) run; when provided, only the matching suite runs. - **test_unit.js**: Declares the `files` filter variable so file arguments from `npm run test-unit` work without a ReferenceError. - **test_mousemodes.js**: Uses the correct cursor-home CSI (`ESC [ H`) when clearing the menu screen. - **Cleanup**: Removes debug logging from the test-unit coverage path; adds a missing semicolon after the suite regex match. ## Affected scripts - `bin/test_integration.js` - `bin/test_unit.js` - `bin/esbuild_all.mjs` - `bin/setup-full.mjs` - `bin/test_mousemodes.js` ## Testing - Syntax-checked affected scripts with `node --check` - Verified `node bin/test_integration.js --suite=unknown` exits 1 with an error message - `npm run lint-changes` (no TypeScript files changed)
    Open in Web Open in Cursor 
    --- bin/esbuild_all.mjs | 7 +++++-- bin/setup-full.mjs | 4 ++-- bin/test_integration.js | 15 ++++++++++++--- bin/test_mousemodes.js | 2 +- bin/test_unit.js | 6 ++---- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/bin/esbuild_all.mjs b/bin/esbuild_all.mjs index 1a3aadea5..789cb5df0 100644 --- a/bin/esbuild_all.mjs +++ b/bin/esbuild_all.mjs @@ -19,14 +19,17 @@ for (const addon of addons) { // Demo job - This requires the others to be built so it's not included when building all // jobs.push(createJob('demo-client', [`--demo-client`])); -await Promise.all(jobs.map((job, i) => { +const codes = await Promise.all(jobs.map(job => { return new Promise(r => { job.cp.on('exit', code => { log(`Finished \x1b[32m${job.name}\x1b[0m${code ? ' \x1b[31mwith errors\x1b[0m' : ''}`); - r(code); + r(code ?? 1); }); }); })); +if (codes.some(code => code !== 0)) { + process.exit(1); +} /** * @param {string} message diff --git a/bin/setup-full.mjs b/bin/setup-full.mjs index 2db85d87a..35a62cbd7 100644 --- a/bin/setup-full.mjs +++ b/bin/setup-full.mjs @@ -5,7 +5,7 @@ import { spawnSync } from 'node:child_process'; import { basename, dirname, resolve, sep } from 'node:path'; import { fileURLToPath } from 'node:url'; -const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../..'); +const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..'); const nodeModulesPath = resolve(repoRoot, 'node_modules'); const npmExecutable = process.platform === 'win32' ? 'npm.cmd' : 'npm'; @@ -13,7 +13,7 @@ const npmExecutable = process.platform === 'win32' ? 'npm.cmd' : 'npm'; /** @param {string} message */ function log(message) { - console.info(`[setup-fast] ${message}`); + console.info(`[setup-full] ${message}`); } /** @param {string[]} args */ diff --git a/bin/test_integration.js b/bin/test_integration.js index 37e6aca7c..0a2531e23 100644 --- a/bin/test_integration.js +++ b/bin/test_integration.js @@ -12,7 +12,7 @@ let argv = process.argv.slice(2); let suiteFilter = undefined; while (argv.some(e => e.startsWith('--suite='))) { const i = argv.findIndex(e => e.startsWith('--suite=')); - const match = argv[i].match(/--suite=(?.+)/) + const match = argv[i].match(/--suite=(?.+)/); suiteFilter = match?.groups?.suitename ?? undefined; argv.splice(i, 1); } @@ -42,12 +42,18 @@ if (suiteFilter) { configs = configs.filter(e => e.name === suiteFilter); } +if (suiteFilter && configs.length === 0) { + console.error(`Unknown suite: ${suiteFilter}`); + process.exit(1); +} + function npmBinScript(script) { return path.resolve(__dirname, `../node_modules/.bin/` + (process.platform === 'win32' ? `${script}.cmd` : script)); } async function run() { + let exitCode = 0; for (const config of configs) { const command = npmBinScript('playwright'); const args = ['test', '-c', config.path, ...argv]; @@ -62,10 +68,13 @@ async function run() { if (run.error) { console.error(run.error); - process.exit(run.status ?? -1); + process.exit(run.status ?? 1); } - process.exit(run.status); + if (run.status) { + exitCode = run.status; + } } + process.exit(exitCode); } run(); diff --git a/bin/test_mousemodes.js b/bin/test_mousemodes.js index 0f12d208b..8954c1d77 100644 --- a/bin/test_mousemodes.js +++ b/bin/test_mousemodes.js @@ -177,7 +177,7 @@ const ENC = { } function printMenu() { - console.log('\x1b[2J\x1b\[HTest mouse reports [Ctrl-C to exit]'); + console.log('\x1b[2J\x1b[HTest mouse reports [Ctrl-C to exit]'); console.log(); console.log(' Selected protocol [Ctrl-A to switch]'); const protocols = Object.keys(PROTOCOLS); diff --git a/bin/test_unit.js b/bin/test_unit.js index 58fdd9a32..2ff7f6250 100644 --- a/bin/test_unit.js +++ b/bin/test_unit.js @@ -21,7 +21,7 @@ if (process.argv.length > 2) { flagArgs = args.filter(e => e.startsWith('--')); // ability to inject particular test files via // npm run test [testFileA testFileB ...] - files = args.filter(e => !e.startsWith('--')); + const files = args.filter(e => !e.startsWith('--')); if (files.length) { testFiles = files; } @@ -39,8 +39,6 @@ if (checkCoverage) { ...testFiles, ...flagArgs ]; - console.info('executable', executable); - console.info('args', args); const run = cp.spawnSync( executable, args, @@ -51,7 +49,7 @@ if (checkCoverage) { stdio: 'inherit' } ); - process.exit(run.status); + process.exit(run.status ?? -1); } const run = cp.spawnSync( From 1f054097f3968e38db34ba0ba03906c79108c9fd Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 1 Jun 2026 19:43:47 -0700 Subject: [PATCH 139/153] Fix benchmarks with NODE_PATH and common/browser imports (#5985) PR #5966 switched benchmark imports to ../../src/ paths that do not resolve at runtime. Restore common/* and browser/* imports (with benchmark-only tsconfig paths for typechecking) and NODE_PATH=./out on benchmark npm scripts so emitted requires resolve against out/. Fixes #5970 Co-authored-by: Cursor Agent Co-authored-by: Daniel Imms --- package.json | 6 +++--- test/benchmark/DecorationService.benchmark.ts | 4 ++-- test/benchmark/EscapeSequenceParser.benchmark.ts | 12 ++++++------ test/benchmark/Event.benchmark.ts | 2 +- test/benchmark/Terminal.benchmark.ts | 4 ++-- test/benchmark/tsconfig.json | 6 +++++- 6 files changed, 19 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 43e569d02..d3da864b6 100644 --- a/package.json +++ b/package.json @@ -61,9 +61,9 @@ "test-integration-firefox": "node ./bin/test_integration.js --workers=75% \"--project=FirefoxStable\"", "test-integration-webkit": "node ./bin/test_integration.js --workers=75% \"--project=WebKit\"", "test-integration-debug": "node ./bin/test_integration.js --workers=1 --headed --timeout=30000", - "benchmark": "xterm-benchmark -r 5 -c test/benchmark/benchmark.json", - "benchmark-baseline": "xterm-benchmark -r 5 -c test/benchmark/benchmark.json --baseline out-test/benchmark/*benchmark.js", - "benchmark-eval": "xterm-benchmark -r 5 -c test/benchmark/benchmark.json --eval out-test/benchmark/*benchmark.js", + "benchmark": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json", + "benchmark-baseline": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --baseline out-test/benchmark/*benchmark.js", + "benchmark-eval": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --eval out-test/benchmark/*benchmark.js", "clean": "rm -rf lib out addons/*/lib addons/*/out", "vtfeatures": "node bin/extract_vtfeatures.js src/*/*.ts src/*/*/*.ts src/*.ts addons/**/src/*.ts", "prepackage": "npm run build", diff --git a/test/benchmark/DecorationService.benchmark.ts b/test/benchmark/DecorationService.benchmark.ts index 534a1fa30..1a2033de1 100644 --- a/test/benchmark/DecorationService.benchmark.ts +++ b/test/benchmark/DecorationService.benchmark.ts @@ -4,8 +4,8 @@ */ import { perfContext, before, RuntimeCase } from 'xterm-benchmark'; -import { DecorationService } from '../../src/common/services/DecorationService'; -import { MockLogService, MockBufferService, MockOptionsService } from '../../src/common/TestUtils.test'; +import { DecorationService } from 'common/services/DecorationService'; +import { MockLogService, MockBufferService, MockOptionsService } from 'common/TestUtils.test'; const enum Constants { COLS = 80, ROWS = 30, diff --git a/test/benchmark/EscapeSequenceParser.benchmark.ts b/test/benchmark/EscapeSequenceParser.benchmark.ts index c2cecfdbb..5c9f372fb 100644 --- a/test/benchmark/EscapeSequenceParser.benchmark.ts +++ b/test/benchmark/EscapeSequenceParser.benchmark.ts @@ -4,12 +4,12 @@ */ import { perfContext, before, beforeEach, ThroughputRuntimeCase } from 'xterm-benchmark'; -import { EscapeSequenceParser } from '../../src/common/parser/EscapeSequenceParser'; -import { C0, C1 } from '../../src/common/data/EscapeSequences'; -import { IDcsHandler, IOscHandler, IApcHandler, IParams } from '../../src/common/parser/Types'; -import { OscHandler } from '../../src/common/parser/OscParser'; -import { DcsHandler } from '../../src/common/parser/DcsParser'; -import { ApcHandler } from '../../src/common/parser/ApcParser'; +import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser'; +import { C0, C1 } from 'common/data/EscapeSequences'; +import { IDcsHandler, IOscHandler, IApcHandler, IParams } from 'common/parser/Types'; +import { OscHandler } from 'common/parser/OscParser'; +import { DcsHandler } from 'common/parser/DcsParser'; +import { ApcHandler } from 'common/parser/ApcParser'; const SIZE = 5000000; diff --git a/test/benchmark/Event.benchmark.ts b/test/benchmark/Event.benchmark.ts index ba3f8de9c..9cd8216dc 100644 --- a/test/benchmark/Event.benchmark.ts +++ b/test/benchmark/Event.benchmark.ts @@ -4,7 +4,7 @@ */ import { perfContext, before, RuntimeCase } from 'xterm-benchmark'; -import { Emitter } from '../../src/common/Event'; +import { Emitter } from 'common/Event'; const ITERATIONS = 1_000_000; diff --git a/test/benchmark/Terminal.benchmark.ts b/test/benchmark/Terminal.benchmark.ts index 97579cb30..fb06b429e 100644 --- a/test/benchmark/Terminal.benchmark.ts +++ b/test/benchmark/Terminal.benchmark.ts @@ -6,8 +6,8 @@ import { perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark'; import { spawn } from 'node-pty'; -import { Utf8ToUtf32, stringFromCodePoint } from '../../src/common/input/TextDecoder'; -import { CoreBrowserTerminal } from '../../src/browser/CoreBrowserTerminal'; +import { Utf8ToUtf32, stringFromCodePoint } from 'common/input/TextDecoder'; +import { CoreBrowserTerminal } from 'browser/CoreBrowserTerminal'; perfContext('Terminal: ls -lR /usr/lib', () => { let content = ''; diff --git a/test/benchmark/tsconfig.json b/test/benchmark/tsconfig.json index fe2af282c..c8e17ee61 100644 --- a/test/benchmark/tsconfig.json +++ b/test/benchmark/tsconfig.json @@ -11,7 +11,11 @@ "moduleResolution": "nodenext", "strict": false, "target": "es2021", - "module": "nodenext" + "module": "nodenext", + "paths": { + "common/*": ["../../src/common/*"], + "browser/*": ["../../src/browser/*"] + } }, "include": [ "./**/*", From b3de9fa6db6a9278e4e16808c137e423f49ac896 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 1 Jun 2026 20:04:25 -0700 Subject: [PATCH 140/153] fix(buffer): clear stale attrs and combined data in BufferLine (#5972) * fix(buffer): bound combined-cell copy in copyCellsFrom When copying a column range, combined-string metadata was copied for every source column >= srcCol with no upper limit. Partial copies could pull _combined entries from outside [srcCol, srcCol + length) into the destination and corrupt cells beyond the copied span. Only copy combined keys within the same range as the cell data loop. Co-authored-by: Cursor * fix(buffer): clear stale _combined entry in BufferLine.setCell setCell updated combined data when IS_COMBINED_MASK was set but left orphaned _combined[index] entries when writing a non-combined cell. Delete the map entry in that case so later combined copies and lookups stay aligned with cell content flags. Co-authored-by: Cursor * fix(buffer): clear stale extended attrs in BufferLine.setCell setCell stored extended attributes when HAS_EXTENDED was set but did not remove orphaned _extendedAttrs[index] when overwriting with a cell without extended background data. Delete the entry in that case, matching the combined-cell cleanup added for setCell earlier. Co-authored-by: Cursor * fix(buffer): clear dest extended attrs in copyCellsFrom When copying a column range, extended attributes were copied from source cells with HAS_EXTENDED but stale _extendedAttrs on the destination were left for cells without extended data. Delete dest entries in that case, matching setCell behavior and the bounded _combined copy loop. Co-authored-by: Cursor * fix(buffer): clear extended attrs in setCellFromCodepoint The input-handler fast path set extended data when HAS_EXTENDED was present but left orphaned _extendedAttrs entries when overwriting with non-extended cells. Delete the map entry in that case, matching setCell and copyCellsFrom. Co-authored-by: Cursor * fix(buffer): clear stale combined data in setCellFromCodepoint When the input handler overwrites a cell that previously held a grapheme cluster (IS_COMBINED_MASK), setCellFromCodepoint updated _data and _extendedAttrs but left this._combined[index] intact. setCell already deletes _combined for non-combined cells; this path did not, so getString and renderers could show the old cluster string after a normal codepoint write. Add delete this._combined[index] at the start of setCellFromCodepoint, matching setCell's non-combined branch and the extended-attrs cleanup already performed in the same method. Co-authored-by: Cursor * perf(buffer): only delete stale combined/extended map entries when present Co-authored-by: Daniel Imms * fix(buffer): treat combined/extended maps as flag-gated caches _flags in _data are authoritative; sparse maps are only read when IS_COMBINED_MASK or HAS_EXTENDED is set. setCellFromCodepoint no longer touches _combined. loadCell clears CellData side fields when flags are off. copyCellsFrom copies map entries per-cell from src flags only. Co-authored-by: Daniel Imms --------- Co-authored-by: Cursor Co-authored-by: Daniel Imms --- src/common/buffer/BufferLine.ts | 61 +++++++++++++++++---------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts index 4b6f6e8bb..30b4ed3bb 100644 --- a/src/common/buffer/BufferLine.ts +++ b/src/common/buffer/BufferLine.ts @@ -73,7 +73,9 @@ export interface IBufferLineStringCache { */ export class BufferLine implements IBufferLine { protected _data: Uint32Array; + /** Sparse cache; only read when `IS_COMBINED_MASK` is set in `_data`. */ protected _combined: {[index: number]: string} = {}; + /** Sparse cache; only read when `HAS_EXTENDED` is set in `_data`. */ protected _extendedAttrs: {[index: number]: IExtendedAttrs | undefined} = {}; protected _stringCacheEntryRef: WeakRef | undefined; public length: number; @@ -205,9 +207,15 @@ export class BufferLine implements IBufferLine { cell.bg = this._data[$startIndex + Cell.BG]; if (cell.content & Content.IS_COMBINED_MASK) { cell.combinedData = this._combined[index]; + } else { + cell.combinedData = ''; } if (cell.bg & BgFlags.HAS_EXTENDED) { cell.extended = this._extendedAttrs[index]!; + } else { + // Do not mutate cell.extended in place: it may still reference this line's map entry from a + // prior loadCell into a reused CellData (e.g. $workCell during insert/delete). + cell.extended = DEFAULT_ATTR_DATA.extended.clone(); } return cell; } @@ -459,14 +467,7 @@ export class BufferLine implements IBufferLine { this._data.set(line._data); } this.length = line.length; - this._combined = {}; - for (const el in line._combined) { - this._combined[el] = line._combined[el]; - } - this._extendedAttrs = {}; - for (const el in line._extendedAttrs) { - this._extendedAttrs[el] = line._extendedAttrs[el]; - } + this._copySparseMapsFrom(line); this.isWrapped = line.isWrapped; } @@ -475,12 +476,7 @@ export class BufferLine implements IBufferLine { const newLine = new BufferLine(this._stringCache, 0, undefined, false); newLine._data = new Uint32Array(this._data); newLine.length = this.length; - for (const el in this._combined) { - newLine._combined[el] = this._combined[el]; - } - for (const el in this._extendedAttrs) { - newLine._extendedAttrs[el] = this._extendedAttrs[el]; - } + newLine._copySparseMapsFrom(this); newLine.isWrapped = this.isWrapped; return newLine; } @@ -511,27 +507,14 @@ export class BufferLine implements IBufferLine { for (let i = 0; i < Constants.CELL_INDICIES; i++) { this._data[(destCol + cell) * Constants.CELL_INDICIES + i] = srcData[(srcCol + cell) * Constants.CELL_INDICIES + i]; } - if (srcData[(srcCol + cell) * Constants.CELL_INDICIES + Cell.BG] & BgFlags.HAS_EXTENDED) { - this._extendedAttrs[destCol + cell] = src._extendedAttrs[srcCol + cell]; - } + this._copyCellMapsFrom(src, srcCol + cell, destCol + cell); } } else { for (let cell = 0; cell < length; cell++) { for (let i = 0; i < Constants.CELL_INDICIES; i++) { this._data[(destCol + cell) * Constants.CELL_INDICIES + i] = srcData[(srcCol + cell) * Constants.CELL_INDICIES + i]; } - if (srcData[(srcCol + cell) * Constants.CELL_INDICIES + Cell.BG] & BgFlags.HAS_EXTENDED) { - this._extendedAttrs[destCol + cell] = src._extendedAttrs[srcCol + cell]; - } - } - } - - // Move any combined data over as needed, FIXME: repeat for extended attrs - const srcCombinedKeys = Object.keys(src._combined); - for (let i = 0; i < srcCombinedKeys.length; i++) { - const key = parseInt(srcCombinedKeys[i], 10); - if (key >= srcCol) { - this._combined[key - srcCol + destCol] = src._combined[key]; + this._copyCellMapsFrom(src, srcCol + cell, destCol + cell); } } } @@ -620,4 +603,24 @@ export class BufferLine implements IBufferLine { cacheEntry.isTrimmed = false; } } + + /** Copy sparse map entries for a single cell when `_data` flags require them. */ + private _copyCellMapsFrom(src: BufferLine, srcCol: number, destCol: number): void { + const srcStart = srcCol * Constants.CELL_INDICIES; + if (src._data[srcStart + Cell.CONTENT] & Content.IS_COMBINED_MASK) { + this._combined[destCol] = src._combined[srcCol]; + } + if (src._data[srcStart + Cell.BG] & BgFlags.HAS_EXTENDED) { + this._extendedAttrs[destCol] = src._extendedAttrs[srcCol]; + } + } + + /** Rebuild sparse maps from another line, keyed only by `_data` flags. */ + private _copySparseMapsFrom(line: BufferLine): void { + this._combined = {}; + this._extendedAttrs = {}; + for (let i = 0; i < line.length; i++) { + this._copyCellMapsFrom(line, i, i); + } + } } From 564113e0531693458497f3bbcdce8f53ad12ba44 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 2 Jun 2026 05:41:54 -0700 Subject: [PATCH 141/153] Add unit tests for parser reset, buffer scroll, ConPTY, and search (#5988) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add InputHandler unit tests for ISO-2022 charset mapping InputHandler unit tests always used MockCharsetService, so regressions in print()'s ASCII remapping, G0/G1 designation (ESC ( ) * +), and SO/SI level switching were never caught at unit level. Add an ISO-2022 character sets suite wired to real CharsetService and BufferService that verifies DEC line drawing on G0 (ESC ( 0), G1 after ESC ) 0 + SO, and charset/glevel restoration via ESC 7 / ESC 8 before printing. Co-authored-by: Cursor * Add BufferService.scroll unit tests for isUserScrolling BufferService.scroll() adjusts ybase and ydisp differently depending on whether the circular buffer is full and whether isUserScrolling is set, but no unit test asserted those branches. InputHandler and other callers use MockBufferService.scroll, which throws, so real scroll viewport logic was only exercised indirectly. Add BufferService.test.ts covering full-buffer scroll with the user scrolled up (ydisp decrements), partial-buffer scroll while scrolled up (ydisp unchanged), and full-buffer scroll at the viewport bottom (ydisp tracks ybase). Co-authored-by: Cursor * Add InputHandler unit tests for DECOM cursor positioning CUP with DEC origin mode (DECOM) offsets rows by the scroll region top and clamps within DECSTBM margins, but unit tests only toggled the mode for save/restore. Playwright covered the combination; headless unit tests did not. Add a cursor positioning test matching the Playwright DECOM case: with margins and origin on, CUP places the cursor relative to scrollTop, clamps to the margin, and uses absolute rows when origin is off. Co-authored-by: Cursor * Add WindowsMode unit tests for wrap heuristic edges ConPTY does not set isWrapped on soft-wrapped lines; updateWindowsModeWrappedState infers wrapping from the last cell of the previous row. Browser Terminal tests exercise this only through full writes, with no direct coverage of the helper or negative cases (trailing space or null). Add WindowsMode.test.ts asserting isWrapped is set for a full non-space line, cleared for trailing whitespace, and cleared when the previous line ends empty. Co-authored-by: Cursor * Add OscParser.reset unit test for mid-payload abort OscParser.reset() must call end(false) on active handlers when aborted during PAYLOAD, but no test invoked reset() directly. EscapeSequenceParser tests mock OSC, so production reset/cleanup was unverified at unit level. Add a reset describe block asserting handlers receive END false after a partial payload, then a subsequent OSC sequence completes normally. Co-authored-by: Cursor * Add DcsParser.reset unit test for mid-payload abort DcsParser.reset() must call unhook(false) on hooked handlers when aborted mid-DCS, matching OscParser's end(false) cleanup. No DcsParser test called reset() directly, leaving the abort path unverified at unit level. Add a reset describe block asserting UNHOOK false after partial put, then a subsequent DCS sequence completes with UNHOOK true. Co-authored-by: Cursor * Add InputHandler print tests for grapheme joining print() uses UnicodeService.shouldJoin to merge combining marks, but InputHandler tests never exercised that path. The U+200B infinite-loop regression test used mocks and had no assertions, so it would pass even if print() misbehaved. Add tests for e + combining acute in one parse and across two parses. Strengthen the U+200B regression test to use BufferService and assert the buffer does not grow spurious rows. Co-authored-by: Cursor * Add CharsetService unit tests for glevel and designation CharsetService only updates the active charset when setgCharset targets the current glevel; most InputHandler tests use MockCharsetService instead. A regression in setgLevel or inactive-slot designation would not be caught without direct service tests. Add CharsetService.test.ts covering inactive designation, setgLevel activation, active-slot updates, and reset clearing state. Co-authored-by: Cursor * Add reflowCursorLine unit tests for ConPTY resize behavior reflowCursorLine lets resize reflow wrapped lines that contain the cursor (needed when ConPTY does not). No test referenced the option or reflowLargerGetLinesToRemove's cursor skip branch. Add BufferReflow tests for skip vs reflow when the cursor is inside a wrapped block, and Buffer.resize tests showing merge on widen only when reflowCursorLine is enabled. Co-authored-by: Cursor * Add EscapeSequenceParser reset test for async pause recovery reset() sets ParserStackType.RESET so the next parse continues in GROUND at chunkPos + 1 without resuming a paused async handler. Existing async tests only verified reset lifts FAIL after improper continuation, not mid-chunk recovery with trailing printable data. Add a test that pauses on async CSI, calls reset(), then continues parsing and asserts XY is printed once and the stack returns to NONE. Co-authored-by: Cursor * Add Buffer.resize unit test for ConPTY row growth When rows increase on Windows ConPTY, Buffer.resize appends lines without decrementing ybase or ydisp so the viewport stays stable while ConPTY repaints. Buffer tests covered the non-Windows scroll-up path but never passed windowsPty into options. Add a ConPTY row-growth test asserting ybase, ydisp, and line count when windowsPty is configured. Co-authored-by: Cursor * Add Buffer reflow test for ConPTY buildNumber threshold Buffer disables reflow on ConPTY when buildNumber is below 21376, but no unit test asserted that gate. Column shrink without reflow leaves wrapped content on a single line instead of splitting across rows. Add a test comparing shrink behavior at build 21375 versus 21376, plus retain unwrap-on-widen coverage for supported ConPTY builds. Co-authored-by: Cursor * Add ApcParser.reset unit test for mid-payload abort ApcParser.reset() calls end(false) on active handlers when aborted mid-APC, matching OscParser and DcsParser, but no ApcParser test invoked reset() directly. Add a reset describe block asserting END false after partial put and a clean subsequent APC sequence. Co-authored-by: Cursor * Add InputHandler unit test for REP with grapheme clusters repeatPrecedingCharacter uses parser.precedingJoinState and reprints the full cluster from the buffer, but only Playwright covered e + combining acute with CSI b. A regression could break REP for joined sequences without failing unit tests. Add a print-suite test asserting CSI 2 b triples e + acute in one parse. Co-authored-by: Cursor * Add InputHandler unit tests for REP no-op cases repeatPrecedingCharacter returns early when parser.precedingJoinState is zero, including after another escape sequence clears join state. Only the positive grapheme REP case was unit tested. Add tests for bare CSI b and for REP following SGR so regressions cannot spuriously duplicate buffer content. Co-authored-by: Cursor * Add DecorationManager unit test for wrapped search matches DecorationManager splits matches across wrapped rows with per-segment x and width, but only SearchEngine had unit coverage; decoration geometry was left to Playwright. Add DecorationManager.test.ts using a real Terminal, a wrapped-buffer match from SearchEngine, and assertions on registerDecoration options for both row segments. Co-authored-by: Cursor * Add BufferService.scroll unit test for DECSTBM margins When scrollTop is non-zero, scroll() shifts lines inside the margin with shiftElements instead of pushing scrollback. Existing BufferService tests only covered scrollTop === 0. Add a test with margins on rows 1–3 that verifies rows outside the region stay fixed and lines inside the region scroll correctly. Co-authored-by: Cursor * Add BufferService.scrollLines unit tests scrollLines adjusts ydisp and toggles isUserScrolling for viewport scrolling, but only scroll() had unit coverage. Regressions in the user-scroll flag or ydisp clamping would affect the whole viewport. Add tests for scrolling up, clamping at ydisp 0, and clearing isUserScrolling when reaching the buffer bottom. Co-authored-by: Cursor * Add DecorationManager overview ruler dedup unit test Multiple highlight matches on the same buffer line should only register one overview ruler marker; later matches on that line omit ruler options. Co-authored-by: Cursor * Assert overview ruler on each line of wrapped search match Wrapped highlight decorations span two buffer lines; each segment should register overview ruler options unlike same-line dedup. Co-authored-by: Cursor --------- Co-authored-by: Cursor --- .../src/DecorationManager.test.ts | 81 +++++++++++ src/common/InputHandler.test.ts | 97 +++++++++++-- src/common/WindowsMode.test.ts | 54 +++++++ src/common/buffer/Buffer.test.ts | 86 +++++++++++ src/common/buffer/BufferReflow.test.ts | 32 +++- src/common/parser/ApcParser.test.ts | 25 ++++ src/common/parser/DcsParser.test.ts | 26 ++++ .../parser/EscapeSequenceParser.test.ts | 32 ++++ src/common/parser/OscParser.test.ts | 24 +++ src/common/services/BufferService.test.ts | 137 ++++++++++++++++++ src/common/services/CharsetService.test.ts | 43 ++++++ 11 files changed, 626 insertions(+), 11 deletions(-) create mode 100644 addons/addon-search/src/DecorationManager.test.ts create mode 100644 src/common/WindowsMode.test.ts create mode 100644 src/common/services/BufferService.test.ts create mode 100644 src/common/services/CharsetService.test.ts diff --git a/addons/addon-search/src/DecorationManager.test.ts b/addons/addon-search/src/DecorationManager.test.ts new file mode 100644 index 000000000..26e40dbc2 --- /dev/null +++ b/addons/addon-search/src/DecorationManager.test.ts @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2026 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { assert } from 'chai'; +import { DecorationManager } from './DecorationManager'; +import { SearchEngine } from './SearchEngine'; +import { SearchLineCache } from './SearchLineCache'; +import { Terminal } from 'browser/public/Terminal'; +import type { ISearchDecorationOptions } from '@xterm/addon-search'; +import type { IDecorationOptions } from '@xterm/xterm'; +import { DisposableStore } from 'common/Lifecycle'; + +function writeP(terminal: Terminal, data: string): Promise { + return new Promise(r => terminal.write(data, r)); +} + +describe('DecorationManager', () => { + let store: DisposableStore; + let terminal: Terminal; + let decorationManager: DecorationManager; + + beforeEach(() => { + store = new DisposableStore(); + terminal = store.add(new Terminal({ cols: 10, rows: 5 })); + decorationManager = store.add(new DecorationManager(terminal)); + }); + + it('should split highlight decorations for a wrapped match', async () => { + await writeP(terminal, '0123456789abcde'); + const searchEngine = new SearchEngine(terminal, store.add(new SearchLineCache(terminal))); + const match = searchEngine.find('9abc', 0, 0); + assert.ok(match); + + const decorationOptions: IDecorationOptions[] = []; + const registerDecoration = terminal.registerDecoration.bind(terminal); + terminal.registerDecoration = (options: IDecorationOptions) => { + decorationOptions.push(options); + return registerDecoration(options); + }; + + const options: ISearchDecorationOptions = { + matchOverviewRuler: '#ff0000', + activeMatchColorOverviewRuler: '#00ff00' + }; + decorationManager.createHighlightDecorations([match], options); + + assert.strictEqual(decorationOptions.length, 2); + assert.strictEqual(decorationOptions[0].x, 9); + assert.strictEqual(decorationOptions[0].width, 1); + assert.strictEqual(decorationOptions[1].x, 0); + assert.strictEqual(decorationOptions[1].width, 3); + + const withOverviewRuler = decorationOptions.filter(o => o.overviewRulerOptions !== undefined); + assert.strictEqual(withOverviewRuler.length, 2); + }); + + it('should only add one overview ruler marker per buffer line', async () => { + await writeP(terminal, 'abcdefghij'); + const decorationOptions: IDecorationOptions[] = []; + const registerDecoration = terminal.registerDecoration.bind(terminal); + terminal.registerDecoration = (options: IDecorationOptions) => { + decorationOptions.push(options); + return registerDecoration(options); + }; + + const options: ISearchDecorationOptions = { + matchOverviewRuler: '#ff0000', + activeMatchColorOverviewRuler: '#00ff00' + }; + decorationManager.createHighlightDecorations([ + { term: 'a', col: 0, row: 0, size: 1 }, + { term: 'f', col: 5, row: 0, size: 1 } + ], options); + + const withOverviewRuler = decorationOptions.filter(o => o.overviewRulerOptions !== undefined); + assert.strictEqual(withOverviewRuler.length, 1); + assert.strictEqual(withOverviewRuler[0].x, 0); + }); +}); diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index 8f0109727..9eab8cfa2 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -16,8 +16,10 @@ import { MockCoreService, MockBufferService, MockOptionsService, MockLogService, import { IBufferService, ICoreService, type IOscLinkService } from './services/Services'; import { DEFAULT_OPTIONS } from './services/OptionsService'; import { BufferService } from './services/BufferService'; +import { CharsetService } from './services/CharsetService'; import { CoreService } from './services/CoreService'; import { OscLinkService } from './services/OscLinkService'; +import { CHARSETS } from './data/Charsets'; function getCursor(bufferService: IBufferService): number[] { @@ -639,19 +641,38 @@ describe('InputHandler', () => { }); describe('print', () => { it('should not cause an infinite loop (regression test)', () => { - const inputHandler = new TestInputHandler( - new MockBufferService(80, 30), - new MockCharsetService(), - new MockCoreService(), - new MockLogService(), - new MockOptionsService(), - new MockOscLinkService(), - new MockMouseStateService(), - new MockUnicodeService() - ); const container = new Uint32Array(10); container[0] = 0x200B; + const lineCountBefore = bufferService.buffer.lines.length; inputHandler.print(container, 0, 1); + assert.strictEqual(bufferService.buffer.y, 0); + assert.strictEqual(bufferService.buffer.lines.length, lineCountBefore); + }); + it('should join combining characters in a single print', async () => { + await inputHandler.parseP('e\u0301'); + assert.strictEqual(bufferService.buffer.translateBufferLineToString(0, true), 'e\u0301'); + assert.strictEqual(bufferService.buffer.x, 1); + }); + it('should join combining characters split across parse calls', async () => { + await inputHandler.parseP('e'); + await inputHandler.parseP('\u0301'); + assert.strictEqual(bufferService.buffer.translateBufferLineToString(0, true), 'e\u0301'); + assert.strictEqual(bufferService.buffer.x, 1); + }); + it('should repeat preceding grapheme cluster via REP', async () => { + await inputHandler.parseP('e\u0301\x1b[2b'); + assert.strictEqual(bufferService.buffer.translateBufferLineToString(0, true), 'e\u0301e\u0301e\u0301'); + assert.strictEqual(bufferService.buffer.x, 3); + }); + it('should not repeat when REP has no preceding join state', async () => { + await inputHandler.parseP('\x1b[2b'); + assert.strictEqual(bufferService.buffer.translateBufferLineToString(0, true), ''); + assert.strictEqual(bufferService.buffer.x, 0); + }); + it('should not repeat after an intervening escape sequence', async () => { + await inputHandler.parseP('a\x1b[0m\x1b[2b'); + assert.strictEqual(bufferService.buffer.translateBufferLineToString(0, true), 'a'); + assert.strictEqual(bufferService.buffer.x, 1); }); it('should clear cells to the right on early wrap-around', async () => { bufferService.resize(5, 5); @@ -668,6 +689,49 @@ describe('InputHandler', () => { }); }); + describe('ISO-2022 character sets', () => { + let charsetService: CharsetService; + + beforeEach(() => { + charsetService = new CharsetService(); + inputHandler = new TestInputHandler( + bufferService, + charsetService, + coreService, + new MockLogService(), + optionsService, + oscLinkService, + new MockMouseStateService(), + new MockUnicodeService() + ); + }); + + it('should map G0 line drawing via ESC ( 0', async () => { + await inputHandler.parseP('\x1b(0q\x1b(Bq'); + assert.strictEqual(bufferService.buffer.translateBufferLineToString(0, true), '\u2500q'); + }); + + it('should map G1 line drawing after ESC ) 0 and SO', async () => { + await inputHandler.parseP('\x1b)0\x0eq\x0f\x1b(Bq'); + assert.strictEqual(bufferService.buffer.translateBufferLineToString(0, true), '\u2500q'); + }); + + it('should restore charset and glevel on ESC 7 / ESC 8', async () => { + await inputHandler.parseP('\x1b)0\x0e'); + assert.strictEqual(charsetService.glevel, 1); + assert.strictEqual(charsetService.charset, CHARSETS['0']); + await inputHandler.parseP('\x1b7'); + await inputHandler.parseP('\x0f\x1b(B'); + assert.strictEqual(charsetService.glevel, 0); + assert.ok(charsetService.charset === undefined); + await inputHandler.parseP('\x1b8'); + assert.strictEqual(charsetService.glevel, 1); + assert.strictEqual(charsetService.charset, CHARSETS['0']); + await inputHandler.parseP('q'); + assert.strictEqual(bufferService.buffer.translateBufferLineToString(0, true), '\u2500'); + }); + }); + describe('alt screen', () => { let bufferService: IBufferService; let handler: TestInputHandler; @@ -1182,6 +1246,19 @@ describe('InputHandler', () => { await inputHandler.parseP('\x1b[100;100H'); assert.deepEqual(getCursor(bufferService), [9, 9]); }); + it('cursor position (CUP) with DECOM and scroll margins', async () => { + await inputHandler.parseP('\x1b[?6h\x1b[2;3r\x1b[1;1H'); + assert.deepEqual(getCursor(bufferService), [0, 1]); + await inputHandler.parseP('X'); + assert.strictEqual(getLines(bufferService, 3)[1], 'X'); + await inputHandler.parseP('\x1b[2;1H'); + assert.deepEqual(getCursor(bufferService), [0, 2]); + await inputHandler.parseP('\x1b[10;10H'); + assert.deepEqual(getCursor(bufferService), [9, 2]); + await inputHandler.parseP('\x1b[?6l'); + await inputHandler.parseP('\x1b[2;1H'); + assert.deepEqual(getCursor(bufferService), [0, 1]); + }); it('horizontal position absolute (HPA)', async () => { await inputHandler.parseP('\x1b[`'); assert.deepEqual(getCursor(bufferService), [0, 0]); diff --git a/src/common/WindowsMode.test.ts b/src/common/WindowsMode.test.ts new file mode 100644 index 000000000..38daa52b7 --- /dev/null +++ b/src/common/WindowsMode.test.ts @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2026 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { assert } from 'chai'; +import { DEFAULT_ATTR_DATA } from './buffer/BufferLine'; +import { updateWindowsModeWrappedState } from './WindowsMode'; +import { BufferService } from './services/BufferService'; +import { OptionsService } from './services/OptionsService'; +import { MockLogService } from './TestUtils.test'; + +describe('WindowsMode', () => { + describe('updateWindowsModeWrappedState', () => { + it('should mark the next line wrapped when the previous line ends in a non-whitespace character', () => { + const bufferService = new BufferService(new OptionsService({ rows: 5, cols: 10 }), new MockLogService()); + const buffer = bufferService.buffer; + const previousLine = buffer.lines.get(buffer.ybase)!; + for (let i = 0; i < bufferService.cols; i++) { + previousLine!.setCellFromCodepoint(i, 'a'.charCodeAt(0), 1, DEFAULT_ATTR_DATA); + } + buffer.y = 1; + + updateWindowsModeWrappedState(bufferService); + + assert.strictEqual(buffer.lines.get(buffer.ybase + 1)!.isWrapped, true); + }); + + it('should not mark the next line wrapped when the previous line ends in whitespace', () => { + const bufferService = new BufferService(new OptionsService({ rows: 5, cols: 10 }), new MockLogService()); + const buffer = bufferService.buffer; + const previousLine = buffer.lines.get(buffer.ybase)!; + for (let i = 0; i < bufferService.cols - 1; i++) { + previousLine!.setCellFromCodepoint(i, 'a'.charCodeAt(0), 1, DEFAULT_ATTR_DATA); + } + previousLine!.setCellFromCodepoint(bufferService.cols - 1, ' '.charCodeAt(0), 1, DEFAULT_ATTR_DATA); + buffer.y = 1; + + updateWindowsModeWrappedState(bufferService); + + assert.strictEqual(buffer.lines.get(buffer.ybase + 1)!.isWrapped, false); + }); + + it('should not mark the next line wrapped when the previous line ends in a null cell', () => { + const bufferService = new BufferService(new OptionsService({ rows: 5, cols: 10 }), new MockLogService()); + const buffer = bufferService.buffer; + buffer.y = 1; + + updateWindowsModeWrappedState(bufferService); + + assert.strictEqual(buffer.lines.get(buffer.ybase + 1)!.isWrapped, false); + }); + }); +}); diff --git a/src/common/buffer/Buffer.test.ts b/src/common/buffer/Buffer.test.ts index 8116c28c4..cbb8cf9ed 100644 --- a/src/common/buffer/Buffer.test.ts +++ b/src/common/buffer/Buffer.test.ts @@ -233,6 +233,27 @@ describe('Buffer', () => { assert.equal(buffer.lines.length, INIT_ROWS + 10); }); }); + + describe('Windows ConPTY', () => { + beforeEach(() => { + optionsService.options.windowsPty = { backend: 'conpty', buildNumber: 19000 }; + }); + + it('should not adjust ybase or ydisp when growing rows', () => { + buffer.fillViewportRows(); + for (let i = 0; i < 10; i++) { + buffer.lines.push(buffer.getBlankLine(DEFAULT_ATTR_DATA)); + } + buffer.y = INIT_ROWS - 1; + buffer.ybase = 10; + buffer.ydisp = 10; + const linesBefore = buffer.lines.length; + buffer.resize(INIT_COLS, INIT_ROWS + 5); + assert.equal(buffer.ybase, 10); + assert.equal(buffer.ydisp, 10); + assert.equal(buffer.lines.length, linesBefore + 5); + }); + }); }); describe('row and column increased', () => { @@ -305,6 +326,71 @@ describe('Buffer', () => { assert.equal(buffer.lines.get(8)!.translateToString(), ' '); assert.equal(buffer.lines.get(9)!.translateToString(), ' '); }); + it('should gate reflow on ConPTY buildNumber 21376', () => { + const prepareWrappedShrink = () => { + buffer.fillViewportRows(); + buffer.resize(5, 10); + const firstLine = buffer.lines.get(0)!; + for (let i = 0; i < 5; i++) { + const code = 'a'.charCodeAt(0) + i; + firstLine.set(i, [0, String.fromCharCode(code), 1, code]); + } + buffer.y = 1; + buffer.resize(1, 10); + }; + + optionsService.options.windowsPty = { backend: 'conpty', buildNumber: 21375 }; + prepareWrappedShrink(); + assert.equal(buffer.lines.get(1)!.translateToString().trim(), ''); + + buffer = new TestBuffer(true, optionsService, bufferService, new MockLogService()); + optionsService.options.windowsPty = { backend: 'conpty', buildNumber: 21376 }; + prepareWrappedShrink(); + assert.equal(buffer.lines.get(1)!.translateToString().trim(), 'b'); + assert.ok(buffer.lines.get(1)!.isWrapped); + }); + it('should unwrap lines on ConPTY builds with reflow support', () => { + optionsService.options.windowsPty = { backend: 'conpty', buildNumber: 21376 }; + buffer.fillViewportRows(); + buffer.resize(5, 10); + const firstLine = buffer.lines.get(0)!; + for (let i = 0; i < 5; i++) { + const code = 'a'.charCodeAt(0) + i; + firstLine.set(i, [0, String.fromCharCode(code), 1, code]); + } + buffer.y = 1; + buffer.resize(1, 10); + buffer.resize(5, 10); + assert.equal(buffer.lines.get(0)!.translateToString(), 'abcde'); + assert.equal(buffer.lines.get(1)!.translateToString(), ' '); + }); + it('should reflow wrapped lines containing the cursor when reflowCursorLine is enabled', () => { + optionsService.options.reflowCursorLine = true; + buffer.fillViewportRows(); + buffer.resize(5, 10); + const firstLine = buffer.lines.get(0)!; + for (let i = 0; i < 5; i++) { + const code = 'a'.charCodeAt(0) + i; + firstLine.set(i, [0, String.fromCharCode(code), 1, code]); + } + buffer.resize(1, 10); + buffer.y = 2; + buffer.resize(5, 10); + assert.equal(buffer.lines.get(0)!.translateToString(), 'abcde'); + }); + it('should not reflow wrapped lines containing the cursor by default', () => { + buffer.fillViewportRows(); + buffer.resize(5, 10); + const firstLine = buffer.lines.get(0)!; + for (let i = 0; i < 5; i++) { + const code = 'a'.charCodeAt(0) + i; + firstLine.set(i, [0, String.fromCharCode(code), 1, code]); + } + buffer.resize(1, 10); + buffer.y = 2; + buffer.resize(5, 10); + assert.notEqual(buffer.lines.get(0)!.translateToString(), 'abcde'); + }); it('should discard parts of wrapped lines that go out of the scrollback', () => { buffer.fillViewportRows(); optionsService.options.scrollback = 1; diff --git a/src/common/buffer/BufferReflow.test.ts b/src/common/buffer/BufferReflow.test.ts index c799fee19..8bcf5bf9c 100644 --- a/src/common/buffer/BufferReflow.test.ts +++ b/src/common/buffer/BufferReflow.test.ts @@ -3,10 +3,13 @@ * @license MIT */ import { assert } from 'chai'; +import { CircularList } from '../CircularList'; import { BufferLine } from './BufferLine'; import { BufferLineStringCache } from './BufferLineStringCache'; +import { CellData } from './CellData'; import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE } from './Constants'; -import { reflowSmallerGetNewLineLengths } from './BufferReflow'; +import { reflowLargerGetLinesToRemove, reflowSmallerGetNewLineLengths } from './BufferReflow'; +import { IBufferLine } from './Types'; const TEST_STRING_CACHE = new BufferLineStringCache(); @@ -93,4 +96,31 @@ describe('BufferReflow', () => { assert.deepEqual(reflowSmallerGetNewLineLengths([line], 4, 2), [2, 2], 'line: 汉, 语'); }); }); + describe('reflowLargerGetLinesToRemove', () => { + const nullCell = CellData.fromCharData([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]); + + function createWrappedLines(chars: string): CircularList { + const lines = new CircularList(chars.length); + for (let i = 0; i < chars.length; i++) { + const line = new BufferLine(TEST_STRING_CACHE, 1); + line.set(0, [0, chars[i], 1, chars.charCodeAt(i)]); + line.isWrapped = i > 0; + lines.push(line); + } + return lines; + } + + it('should skip reflow when the cursor is in a wrapped block and reflowCursorLine is false', () => { + const lines = createWrappedLines('abcde'); + const skipped = reflowLargerGetLinesToRemove(lines, 1, 5, 2, nullCell, false); + const reflowed = reflowLargerGetLinesToRemove(lines, 1, 5, 2, nullCell, true); + assert.deepEqual(skipped, []); + assert.notDeepEqual(reflowed, []); + }); + + it('should reflow wrapped blocks when the cursor is outside the block', () => { + const lines = createWrappedLines('abcde'); + assert.notDeepEqual(reflowLargerGetLinesToRemove(lines, 1, 5, 10, nullCell, false), []); + }); + }); }); diff --git a/src/common/parser/ApcParser.test.ts b/src/common/parser/ApcParser.test.ts index 563ddbeab..49fd26132 100644 --- a/src/common/parser/ApcParser.test.ts +++ b/src/common/parser/ApcParser.test.ts @@ -459,4 +459,29 @@ describe('ApcParser - async tests', () => { }); }); }); + describe('reset', () => { + it('should abort active handlers with end(false) when reset during payload', () => { + const ident = identifier({ intermediates: '+', final: 'p' }); + parser.registerHandler(ident, new TestHandler(reports, 'th')); + parser.start(ident); + let data = toUtf32('partial'); + parser.put(data, 0, data.length); + parser.reset(); + assert.deepEqual(reports, [ + ['th', 'START'], + ['th', 'PUT', 'partial'], + ['th', 'END', false] + ]); + reports.length = 0; + parser.start(ident); + data = toUtf32('complete'); + parser.put(data, 0, data.length); + parser.end(true); + assert.deepEqual(reports, [ + ['th', 'START'], + ['th', 'PUT', 'complete'], + ['th', 'END', true] + ]); + }); + }); }); diff --git a/src/common/parser/DcsParser.test.ts b/src/common/parser/DcsParser.test.ts index 5fa7b9416..6732a18f3 100644 --- a/src/common/parser/DcsParser.test.ts +++ b/src/common/parser/DcsParser.test.ts @@ -470,4 +470,30 @@ describe('DcsParser - async tests', () => { }); }); }); + describe('reset', () => { + it('should abort active handlers with unhook(false) when reset during payload', () => { + const ident = identifier({ intermediates: '+', final: 'p' }); + const params = Params.fromArray([1, 2, 3]); + parser.registerHandler(ident, new TestHandler(reports, 'th')); + parser.hook(ident, params); + let data = toUtf32('partial'); + parser.put(data, 0, data.length); + parser.reset(); + assert.deepEqual(reports, [ + ['th', 'HOOK', [1, 2, 3]], + ['th', 'PUT', 'partial'], + ['th', 'UNHOOK', false] + ]); + reports.length = 0; + parser.hook(ident, params); + data = toUtf32('complete'); + parser.put(data, 0, data.length); + parser.unhook(true); + assert.deepEqual(reports, [ + ['th', 'HOOK', [1, 2, 3]], + ['th', 'PUT', 'complete'], + ['th', 'UNHOOK', true] + ]); + }); + }); }); diff --git a/src/common/parser/EscapeSequenceParser.test.ts b/src/common/parser/EscapeSequenceParser.test.ts index 73238d039..5f12137df 100644 --- a/src/common/parser/EscapeSequenceParser.test.ts +++ b/src/common/parser/EscapeSequenceParser.test.ts @@ -2177,6 +2177,38 @@ describe('EscapeSequenceParser - async', () => { parser.reset(); await parseP(parser, INPUT); // does not throw anymore }); + it('reset during async pause continues at next codepoint in chunk', async () => { + const localParser = new TestEscapeSequenceParser(); + const localStack: any[] = []; + localParser.setPrintHandler((data, start, end) => { + let result = ''; + for (let i = start; i < end; ++i) { + result += stringFromCodePoint(data[i]); + } + localStack.push(['PRINT', result]); + }); + localParser.registerCsiHandler({ final: 'm' }, async params => { + localStack.push(['SGR', params.toArray()]); + return new Promise(resolve => setTimeout(() => resolve(true), 0)); + }); + const data = '\x1b[1mXY'; + const container = new Uint32Array(data.length); + const decoder = new StringToUtf32(); + const len = decoder.decode(data, container); + + assert.ok(localParser.parse(container, len) instanceof Promise); + localParser.reset(); + assert.equal(localParser.parseStack.state, ParserStackType.RESET); + + let prev: boolean | undefined = true; + let result: void | Promise | undefined; + while (result = localParser.parse(container, len, prev)) { + prev = await result; + } + + assert.deepEqual(localStack, [['SGR', [1]], ['PRINT', 'XY']]); + assert.equal(localParser.parseStack.state, ParserStackType.NONE); + }); it('correct result on awaited parse call', async () => { await parseP(parser, INPUT); assert.deepEqual(callstack, RESULT); diff --git a/src/common/parser/OscParser.test.ts b/src/common/parser/OscParser.test.ts index b97a7617a..9a58f617c 100644 --- a/src/common/parser/OscParser.test.ts +++ b/src/common/parser/OscParser.test.ts @@ -464,4 +464,28 @@ describe('OscParser - async tests', () => { }); }); }); + describe('reset', () => { + it('should abort active handlers with end(false) when reset during payload', () => { + parser.registerHandler(1234, new TestHandler(1234, reports, 'th')); + parser.start(); + let data = toUtf32('1234;partial'); + parser.put(data, 0, data.length); + parser.reset(); + assert.deepEqual(reports, [ + ['th', 1234, 'START'], + ['th', 1234, 'PUT', 'partial'], + ['th', 1234, 'END', false] + ]); + reports.length = 0; + parser.start(); + data = toUtf32('1234;complete'); + parser.put(data, 0, data.length); + parser.end(true); + assert.deepEqual(reports, [ + ['th', 1234, 'START'], + ['th', 1234, 'PUT', 'complete'], + ['th', 1234, 'END', true] + ]); + }); + }); }); diff --git a/src/common/services/BufferService.test.ts b/src/common/services/BufferService.test.ts new file mode 100644 index 000000000..90716cdb4 --- /dev/null +++ b/src/common/services/BufferService.test.ts @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2026 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { assert } from 'chai'; +import { AttributeData } from '../buffer/AttributeData'; +import { BufferService } from './BufferService'; +import { OptionsService } from './OptionsService'; +import { MockLogService } from '../TestUtils.test'; + +describe('BufferService', () => { + describe('scroll', () => { + const eraseAttr = new AttributeData(); + + it('should decrement ydisp when the buffer is full and the user has scrolled up', () => { + const optionsService = new OptionsService({ rows: 3, cols: 10, scrollback: 2 }); + const bufferService = new BufferService(optionsService, new MockLogService()); + const buffer = bufferService.buffer; + + while (!buffer.lines.isFull) { + bufferService.scroll(eraseAttr); + } + assert.strictEqual(buffer.lines.length, 5); + + bufferService.isUserScrolling = true; + buffer.ydisp = 2; + const ybaseBefore = buffer.ybase; + + bufferService.scroll(eraseAttr); + + assert.strictEqual(buffer.ybase, ybaseBefore); + assert.strictEqual(buffer.ydisp, 1); + }); + + it('should not advance ydisp with ybase while the user has scrolled up and the buffer is not full', () => { + const optionsService = new OptionsService({ rows: 3, cols: 10, scrollback: 2 }); + const bufferService = new BufferService(optionsService, new MockLogService()); + const buffer = bufferService.buffer; + + bufferService.isUserScrolling = true; + buffer.ydisp = 0; + const ybaseBefore = buffer.ybase; + + bufferService.scroll(eraseAttr); + + assert.strictEqual(buffer.ybase, ybaseBefore + 1); + assert.strictEqual(buffer.ydisp, 0); + }); + + it('should follow ybase with ydisp when the user is not scrolling', () => { + const optionsService = new OptionsService({ rows: 3, cols: 10, scrollback: 2 }); + const bufferService = new BufferService(optionsService, new MockLogService()); + const buffer = bufferService.buffer; + + while (!buffer.lines.isFull) { + bufferService.scroll(eraseAttr); + } + + bufferService.isUserScrolling = false; + bufferService.scroll(eraseAttr); + + assert.strictEqual(buffer.ydisp, buffer.ybase); + }); + + it('should scroll within DECSTBM margins without affecting lines outside the region', () => { + const optionsService = new OptionsService({ rows: 5, cols: 10, scrollback: 10 }); + const bufferService = new BufferService(optionsService, new MockLogService()); + const buffer = bufferService.buffer; + + const markRow = (row: number, ch: string) => { + buffer.lines.get(buffer.ybase + row)!.setCellFromCodepoint(0, ch.charCodeAt(0), 1, eraseAttr); + }; + markRow(0, 'A'); + markRow(1, 'B'); + markRow(2, 'C'); + markRow(3, 'D'); + markRow(4, 'E'); + buffer.scrollTop = 1; + buffer.scrollBottom = 3; + + bufferService.scroll(eraseAttr); + + assert.strictEqual(buffer.lines.get(buffer.ybase + 0)!.translateToString().trim(), 'A'); + assert.strictEqual(buffer.lines.get(buffer.ybase + 1)!.translateToString().trim(), 'C'); + assert.strictEqual(buffer.lines.get(buffer.ybase + 2)!.translateToString().trim(), 'D'); + assert.strictEqual(buffer.lines.get(buffer.ybase + 3)!.translateToString(true).trim(), ''); + assert.strictEqual(buffer.lines.get(buffer.ybase + 4)!.translateToString().trim(), 'E'); + }); + }); + + describe('scrollLines', () => { + it('should move ydisp and set isUserScrolling when scrolling up', () => { + const optionsService = new OptionsService({ rows: 10, cols: 80, scrollback: 10 }); + const bufferService = new BufferService(optionsService, new MockLogService()); + const buffer = bufferService.buffer; + buffer.ybase = 5; + buffer.ydisp = 5; + + let scrollEvent: number | undefined; + bufferService.onScroll(e => { scrollEvent = e; }); + + bufferService.scrollLines(-2); + + assert.strictEqual(buffer.ydisp, 3); + assert.strictEqual(bufferService.isUserScrolling, true); + assert.strictEqual(scrollEvent, 3); + }); + + it('should not scroll above the top of the buffer', () => { + const optionsService = new OptionsService({ rows: 10, cols: 80, scrollback: 10 }); + const bufferService = new BufferService(optionsService, new MockLogService()); + const buffer = bufferService.buffer; + buffer.ybase = 5; + buffer.ydisp = 0; + + bufferService.scrollLines(-1); + + assert.strictEqual(buffer.ydisp, 0); + assert.strictEqual(bufferService.isUserScrolling, false); + }); + + it('should clear isUserScrolling when scrolling to the bottom', () => { + const optionsService = new OptionsService({ rows: 10, cols: 80, scrollback: 10 }); + const bufferService = new BufferService(optionsService, new MockLogService()); + const buffer = bufferService.buffer; + buffer.ybase = 5; + buffer.ydisp = 2; + bufferService.isUserScrolling = true; + + bufferService.scrollLines(10); + + assert.strictEqual(buffer.ydisp, 5); + assert.strictEqual(bufferService.isUserScrolling, false); + }); + }); +}); diff --git a/src/common/services/CharsetService.test.ts b/src/common/services/CharsetService.test.ts new file mode 100644 index 000000000..a4c3646b7 --- /dev/null +++ b/src/common/services/CharsetService.test.ts @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2026 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { assert } from 'chai'; +import { CHARSETS } from '../data/Charsets'; +import { CharsetService } from './CharsetService'; + +describe('CharsetService', () => { + let service: CharsetService; + + beforeEach(() => { + service = new CharsetService(); + }); + + it('should not update active charset when designating an inactive glevel', () => { + service.setgCharset(1, CHARSETS['0']); + assert.strictEqual(service.glevel, 0); + assert.ok(service.charset === undefined); + }); + + it('should expose the designated charset after setgLevel', () => { + service.setgCharset(1, CHARSETS['0']); + service.setgLevel(1); + assert.strictEqual(service.charset, CHARSETS['0']); + }); + + it('should update active charset when designating the current glevel', () => { + service.setgLevel(1); + service.setgCharset(1, CHARSETS['0']); + assert.strictEqual(service.charset, CHARSETS['0']); + }); + + it('should reset glevel, charsets, and active charset', () => { + service.setgCharset(1, CHARSETS['0']); + service.setgLevel(1); + service.reset(); + assert.strictEqual(service.glevel, 0); + assert.deepEqual(service.charsets, []); + assert.ok(service.charset === undefined); + }); +}); From ea75b99e2b6b2f76fa38816a7d31bb93457cd1d0 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 2 Jun 2026 06:34:19 -0700 Subject: [PATCH 142/153] fix(deps): override serialize-javascript to ^7.0.3 (#5989) Mocha 11.7.x pulls in serialize-javascript 6.0.2, which is affected by code injection via RegExp.flags and Date.toISOString (CVE incomplete fix). Force the patched 7.x release via npm overrides. Co-authored-by: Cursor Agent Co-authored-by: Daniel Imms --- package-lock.json | 41 +++++------------------------------------ package.json | 3 +++ 2 files changed, 8 insertions(+), 36 deletions(-) diff --git a/package-lock.json b/package-lock.json index c3d4c0f77..5f5877240 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6689,16 +6689,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -6932,27 +6922,6 @@ "tslib": "^2.1.0" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -7078,13 +7047,13 @@ } }, "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", + "integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", "dev": true, "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" + "engines": { + "node": ">=20.0.0" } }, "node_modules/serve-static": { diff --git a/package.json b/package.json index d3da864b6..cb7d146f0 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,9 @@ "workspaces": [ "addons/*" ], + "overrides": { + "serialize-javascript": "^7.0.3" + }, "keywords": [ "cli", "command-line", From 64718444e2c27a4e005afbaab7993c52975c14e7 Mon Sep 17 00:00:00 2001 From: Will Hampson Date: Wed, 3 Jun 2026 06:00:44 -0700 Subject: [PATCH 143/153] fix(webgl): avoid glyph atlas mipmaps (#5987) Co-authored-by: Will Hampson Co-authored-by: jerch --- addons/addon-webgl/src/GlyphRenderer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/addon-webgl/src/GlyphRenderer.ts b/addons/addon-webgl/src/GlyphRenderer.ts index 4117525c4..be1990c2a 100644 --- a/addons/addon-webgl/src/GlyphRenderer.ts +++ b/addons/addon-webgl/src/GlyphRenderer.ts @@ -389,8 +389,9 @@ export class GlyphRenderer extends Disposable { gl.bindTexture(gl.TEXTURE_2D, this._atlasTextures[i].texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, atlas.pages[i].canvas); - gl.generateMipmap(gl.TEXTURE_2D); this._atlasTextures[i].version = atlas.pages[i].version; } From f3f4dcaf9fa295a4b3807391bf9336d741c8557e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 3 Jun 2026 07:05:19 -0700 Subject: [PATCH 144/153] fix(CharacterJoiner): bound join ranges to trimmed line length (#5990) Character joiner scanning already iterated cells with getTrimmedLength(), but trailing attribute runs and _stringRangesToCellRanges used buffer cols. On short lines that could run joiners on a single trailing cell and extend open ranges through empty tail columns, affecting ligature/join rendering. Use trimmed length consistently for trailing-run detection, the cell-range walk, and the final range end column. Co-authored-by: Cursor --- src/browser/services/CharacterJoinerService.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/browser/services/CharacterJoinerService.ts b/src/browser/services/CharacterJoinerService.ts index e1da0bd28..2cf3bd506 100644 --- a/src/browser/services/CharacterJoinerService.ts +++ b/src/browser/services/CharacterJoinerService.ts @@ -100,6 +100,7 @@ export class CharacterJoinerService implements ICharacterJoinerService { const ranges: [number, number][] = []; const lineStr = line.translateToString(true); + const trimmedLength = line.getTrimmedLength(); // Because some cells can be represented by multiple javascript characters, // we track the cell and the string indexes separately. This allows us to @@ -111,7 +112,7 @@ export class CharacterJoinerService implements ICharacterJoinerService { let rangeAttrFG = line.getFg(0); let rangeAttrBG = line.getBg(0); - for (let x = 0; x < line.getTrimmedLength(); x++) { + for (let x = 0; x < trimmedLength; x++) { line.loadCell(x, this._workCell); if (this._workCell.getWidth() === 0) { @@ -147,7 +148,7 @@ export class CharacterJoinerService implements ICharacterJoinerService { } // Process any trailing ranges. - if (this._bufferService.cols - rangeStartColumn > 1) { + if (trimmedLength - rangeStartColumn > 1) { const joinedRanges = this._getJoinedRanges( lineStr, rangeStartStringIndex, @@ -216,7 +217,8 @@ export class CharacterJoinerService implements ICharacterJoinerService { return; } - for (let x = startCol; x < this._bufferService.cols; x++) { + const trimmedLength = line.getTrimmedLength(); + for (let x = startCol; x < trimmedLength; x++) { const width = line.getWidth(x); const length = line.getString(x).length || WHITESPACE_CELL_CHAR.length; @@ -264,7 +266,7 @@ export class CharacterJoinerService implements ICharacterJoinerService { // If there is still a range left at the end, it must extend all the way to // the end of the line. if (currentRange) { - currentRange[1] = this._bufferService.cols; + currentRange[1] = trimmedLength; } } From 0c8271db4998a9ac5a164714e8b5054b444b3f31 Mon Sep 17 00:00:00 2001 From: jerch Date: Sat, 6 Jun 2026 18:01:49 +0200 Subject: [PATCH 145/153] fix atlas callstack exception (#6001) * fix atlas callstack exception * remove spread operator, integrate stress test into demo --- addons/addon-webgl/src/TextureAtlas.ts | 28 +++++++--- demo/client/components/window/webglWindow.ts | 54 ++++++++++++++++++++ 2 files changed, 76 insertions(+), 6 deletions(-) diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index 9d42ca9e1..1c3e08a1c 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -1080,17 +1080,33 @@ class AtlasPage { size: number, sourcePages?: AtlasPage[] ) { - if (sourcePages) { - for (const p of sourcePages) { - this._glyphs.push(...p.glyphs); - this._usedPixels += p._usedPixels; - } - } this.canvas = createCanvas(document, size, size); // The canvas needs alpha because we use clearColor to convert the background color to alpha. // It might also contain some characters with transparent backgrounds if allowTransparency is // set. this.ctx = throwIfFalsy(this.canvas.getContext('2d', { alpha: true })); + if (sourcePages) { + if (sourcePages.length === 4) { + // optimized for quadmerge + this._glyphs = this._glyphs.concat( + sourcePages[0].glyphs, + sourcePages[1].glyphs, + sourcePages[2].glyphs, + sourcePages[3].glyphs + ); + this._usedPixels = sourcePages[0]._usedPixels + + sourcePages[1]._usedPixels + + sourcePages[2]._usedPixels + + sourcePages[3]._usedPixels; + } else { + // fallback for non quadmerges (should never be used) + for (let i = 0; i < sourcePages.length; ++i) { + this._glyphs = this._glyphs.concat(sourcePages[i].glyphs); + this._usedPixels += sourcePages[i]._usedPixels; + } + + } + } } public clear(): void { diff --git a/demo/client/components/window/webglWindow.ts b/demo/client/components/window/webglWindow.ts index 2886c3b57..d7c175c45 100644 --- a/demo/client/components/window/webglWindow.ts +++ b/demo/client/components/window/webglWindow.ts @@ -11,8 +11,33 @@ export class WebglWindow extends BaseWindow implements IControlWindow { public readonly label = 'webgl'; private _textureAtlasContainer!: HTMLElement; + private _stressRunning = false; + private _stressScrolling: HTMLInputElement | undefined; public build(container: HTMLElement): void { + const stressDiv = document.createElement('div'); + const stressStart = document.createElement('button'); + stressStart.id = 'stress-start'; + stressStart.textContent = 'atlas stress start'; + stressStart.addEventListener('click', () => this._stress()); + stressDiv.appendChild(stressStart); + + const stressStop = document.createElement('button'); + stressStop.id = 'stress-stop'; + stressStop.textContent = 'atlas stress stop'; + stressStop.addEventListener('click', () => this._stressRunning = false); + stressDiv.appendChild(stressStop); + + this._stressScrolling = document.createElement('input'); + this._stressScrolling.type = 'checkbox'; + this._stressScrolling.id = 'stress-scrolling'; + stressDiv.appendChild(this._stressScrolling); + const stressScrollingLabel = document.createElement('label'); + stressScrollingLabel.htmlFor = 'stress-scrolling'; + stressScrollingLabel.textContent = 'stress scrolling'; + stressDiv.appendChild(stressScrollingLabel); + container.appendChild(stressDiv); + const zoomCheckbox = document.createElement('input'); zoomCheckbox.type = 'checkbox'; zoomCheckbox.id = 'texture-atlas-zoom'; @@ -48,4 +73,33 @@ export class WebglWindow extends BaseWindow implements IControlWindow { canvas.style.width = `${canvas.width / dpr}px`; canvas.style.height = `${canvas.height / dpr}px`; } + + private async _stress(): Promise { + const TEXT = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'; + if (this._stressRunning) { + return; + } + this._stressRunning = true; + const scrolling = !!(this._stressScrolling?.checked); + for (let r = 0; r < 256; r += 2) { + for (let g = 0; g < 256; g += 2) { + let s: string[] = []; + for (let b = 0; b < 256; b += 2) { + if (!this._stressRunning) { + return; + } + const rbg = `RGB: ${[r, g, b]}`; + s.push(`\r\x1b[38;2;${r};${g};${b}m${rbg.padEnd(18, ' ')}${TEXT}`); + if (s.length >= 16) { + if (scrolling) { + await new Promise(r => this._terminal.write(s.join('\r\n') + '\r\n', r)); + } else { + await new Promise(r => this._terminal.write('\x1b[H' + s.join('\r\n'), r)); + } + s = []; + } + } + } + } + } } From 596634ac4189852a15d0178e49201fc7d52c968d Mon Sep 17 00:00:00 2001 From: jerch Date: Sat, 6 Jun 2026 18:02:43 +0200 Subject: [PATCH 146/153] fix #6003 (#6004) --- src/common/input/TextDecoder.test.ts | 64 +++++++++++++++++++--------- src/common/input/TextDecoder.ts | 4 +- 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/src/common/input/TextDecoder.test.ts b/src/common/input/TextDecoder.test.ts index b32d8a721..0276fc8c8 100644 --- a/src/common/input/TextDecoder.test.ts +++ b/src/common/input/TextDecoder.test.ts @@ -79,7 +79,7 @@ function assertDecodedRange( } const target = new Uint32Array(count); const length = decode(input, target); - assert.equal(length, count); + assert.strictEqual(length, count); let mismatchIndex = -1; let index = 0; for (let i = min; i < max; ++i) { @@ -92,8 +92,8 @@ function assertDecodedRange( } index++; } - assert.equal(mismatchIndex, -1); - assert.equal(outputToString(target, length), input); + assert.strictEqual(mismatchIndex, -1); + assert.strictEqual(outputToString(target, length), input); } const BATCH_SIZE = 8192; @@ -116,9 +116,9 @@ describe('text encodings', () => { const data = new Uint32Array(s.length); for (let i = 0; i < s.length; ++i) { data[i] = s.charCodeAt(i); - assert.equal(stringFromCodePoint(data[i]), s[i]); + assert.strictEqual(stringFromCodePoint(data[i]), s[i]); } - assert.equal(utf32ToString(data), s); + assert.strictEqual(utf32ToString(data), s); }); describe('StringToUtf32 decoder', () => { @@ -159,7 +159,7 @@ describe('text encodings', () => { const decoder = new StringToUtf32(); const target = new Uint32Array(5); const length = decoder.decode(String.fromCharCode(0xFEFF), target); - assert.equal(length, 0); + assert.strictEqual(length, 0); decoder.clear(); }); }); @@ -169,7 +169,7 @@ describe('text encodings', () => { const target = new Uint32Array(500); for (let i = 0; i < TEST_STRINGS.length; ++i) { const length = decoder.decode(TEST_STRINGS[i], target); - assert.equal(toString(target, length), TEST_STRINGS[i]); + assert.strictEqual(toString(target, length), TEST_STRINGS[i]); decoder.clear(); } }); @@ -184,7 +184,7 @@ describe('text encodings', () => { const written = decoder.decode(input[i], target); decoded += toString(target, written); } - assert(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€'); + assert.strictEqual(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€'); }); }); }); @@ -226,7 +226,7 @@ describe('text encodings', () => { const target = new Uint32Array(5); const utf8Data = stringToUtf8Bytes(String.fromCharCode(0xFEFF)); const length = decoder.decode(utf8Data, target); - assert.equal(length, 0); + assert.strictEqual(length, 0); decoder.clear(); }); }); @@ -237,7 +237,7 @@ describe('text encodings', () => { for (let i = 0; i < TEST_STRINGS.length; ++i) { const utf8Data = stringToUtf8Bytes(TEST_STRINGS[i]); const length = decoder.decode(utf8Data, target); - assert.equal(toString(target, length), TEST_STRINGS[i]); + assert.strictEqual(toString(target, length), TEST_STRINGS[i]); decoder.clear(); } }); @@ -252,7 +252,7 @@ describe('text encodings', () => { const written = decoder.decode(utf8Data.slice(i, i + 1), target); decoded += toString(target, written); } - assert(decoded, 'ÄÖÜßöäü'); + assert.strictEqual(decoded, 'ÄÖÜßöäü'); }); it('2/3 byte sequences - advance by 1', () => { @@ -264,7 +264,7 @@ describe('text encodings', () => { const written = decoder.decode(utf8Data.slice(i, i + 1), target); decoded += toString(target, written); } - assert(decoded, 'Āր܀߀ö€ä€ü'); + assert.strictEqual(decoded, 'Āր܀߀ö€ä€ü'); }); it('2/3/4 byte sequences - advance by 1', () => { @@ -276,7 +276,7 @@ describe('text encodings', () => { const written = decoder.decode(utf8Data.slice(i, i + 1), target); decoded += toString(target, written); } - assert(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€'); + assert.strictEqual(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€'); }); it('2/3/4 byte sequences - advance by 2', () => { @@ -288,7 +288,7 @@ describe('text encodings', () => { const written = decoder.decode(utf8Data.slice(i, i + 2), target); decoded += toString(target, written); } - assert(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€'); + assert.strictEqual(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€'); }); it('2/3/4 byte sequences - advance by 3', () => { @@ -300,7 +300,7 @@ describe('text encodings', () => { const written = decoder.decode(utf8Data.slice(i, i + 3), target); decoded += toString(target, written); } - assert(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€'); + assert.strictEqual(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€'); }); it('BOMs (3 byte sequences) - advance by 2', () => { @@ -312,7 +312,7 @@ describe('text encodings', () => { const written = decoder.decode(utf8Data.slice(i, i + 2), target); decoded += toString(target, written); } - assert.equal(decoded, ''); + assert.strictEqual(decoded, ''); }); it('test break after 3 bytes - issue #2495', () => { @@ -320,11 +320,37 @@ describe('text encodings', () => { const target = new Uint32Array(5); const utf8Data = fromByteString('\xf0\xa0\x9c\x8e'); let written = decoder.decode(utf8Data.slice(0, 3), target); - assert.equal(written, 0); + assert.strictEqual(written, 0); written = decoder.decode(utf8Data.slice(3), target); - assert.equal(written, 1); - assert(toString(target, written), '𠜎'); + assert.strictEqual(written, 1); + assert.strictEqual(toString(target, written), '𠜎'); }); + + describe('0x80 not swallowed in continuation', () => { + it('A—B', () => { + const decoder = new Utf8ToUtf32(); + const target = new Uint32Array(5); + const utf8Data = new TextEncoder().encode('A—BA—BA—BA—BA—B'); + let decoded = ''; + for (let i = 0; i < utf8Data.length; i += 2) { + const written = decoder.decode(utf8Data.slice(i, i + 2), target); + decoded += toString(target, written); + } + assert.strictEqual(decoded, 'A—BA—BA—BA—BA—B'); + }); + it('A𐀀B', () => { + const decoder = new Utf8ToUtf32(); + const target = new Uint32Array(5); + const utf8Data = new TextEncoder().encode('A𐀀BA𐀀BA𐀀BA𐀀BA𐀀B'); + let decoded = ''; + for (let i = 0; i < utf8Data.length; i += 2) { + const written = decoder.decode(utf8Data.slice(i, i + 2), target); + decoded += toString(target, written); + } + assert.strictEqual(decoded, 'A𐀀BA𐀀BA𐀀BA𐀀BA𐀀B'); + }); + }); + }); }); }); diff --git a/src/common/input/TextDecoder.ts b/src/common/input/TextDecoder.ts index ac7f8d155..7e0495630 100644 --- a/src/common/input/TextDecoder.ts +++ b/src/common/input/TextDecoder.ts @@ -158,9 +158,9 @@ export class Utf8ToUtf32 { cp &= ((((cp & 0xE0) === 0xC0)) ? 0x1F : (((cp & 0xF0) === 0xE0)) ? 0x0F : 0x07); let pos = 0; let tmp: number; - while ((tmp = this.interim[++pos] & 0x3F) && pos < 4) { + while ((tmp = this.interim[++pos]) && pos < 4) { cp <<= 6; - cp |= tmp; + cp |= tmp & 0x3F; } // missing bytes - read ahead from input const type = (((this.interim[0] & 0xE0) === 0xC0)) ? 2 : (((this.interim[0] & 0xF0) === 0xE0)) ? 3 : 4; From 7c24c580072daea61f607c102d515f37cf2dbb17 Mon Sep 17 00:00:00 2001 From: jerch Date: Sat, 6 Jun 2026 18:07:03 +0200 Subject: [PATCH 147/153] fix non-ascii clipboard handling (#6002) * fix non-ascii clipboard handling * rename type helpers * make linter happy --- addons/addon-clipboard/src/ClipboardAddon.ts | 34 +++++++++++++++++-- .../test/ClipboardAddon.test.ts | 20 ++++++++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/addons/addon-clipboard/src/ClipboardAddon.ts b/addons/addon-clipboard/src/ClipboardAddon.ts index 5e194880c..535c450be 100644 --- a/addons/addon-clipboard/src/ClipboardAddon.ts +++ b/addons/addon-clipboard/src/ClipboardAddon.ts @@ -78,13 +78,43 @@ export class BrowserClipboardProvider implements IClipboardProvider { } } +/** + * TODO: Once the base64 handling on Uint8Arrays is more common, + * remove the btoa/atob fallbacks below. + */ +interface IUint8ArrayB64 extends Uint8Array { + toBase64(): string; +} +interface IUint8ArrayB64Ctor extends Uint8ArrayConstructor { + fromBase64(s: string): IUint8ArrayB64; +} + export class Base64 implements IBase64 { public encodeText(data: string): string { - return btoa(data); + const bytes = new TextEncoder().encode(data) as IUint8ArrayB64; + if (bytes.toBase64 !== undefined) { + return bytes.toBase64(); + } + let bin = ''; + for (let i = 0; i < bytes.length; i++) { + bin += String.fromCharCode(bytes[i]); + } + return btoa(bin); } public decodeText(data: string): string { + if ((Uint8Array as IUint8ArrayB64Ctor).fromBase64 !== undefined) { + try { + return new TextDecoder().decode((Uint8Array as IUint8ArrayB64Ctor).fromBase64(data)); + } catch {} + return ''; + } try { - return atob(data); + const bin = atob(data); + const bytes = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; ++i) { + bytes[i] = bin.charCodeAt(i); + } + return new TextDecoder().decode(bytes); } catch {} return ''; } diff --git a/addons/addon-clipboard/test/ClipboardAddon.test.ts b/addons/addon-clipboard/test/ClipboardAddon.test.ts index 2c04e619b..6995850d4 100644 --- a/addons/addon-clipboard/test/ClipboardAddon.test.ts +++ b/addons/addon-clipboard/test/ClipboardAddon.test.ts @@ -29,7 +29,7 @@ test.describe('ClipboardAddon', () => { await ctx.page.context().grantPermissions(['clipboard-read', 'clipboard-write']); } await ctx.page.evaluate(` - window.term.reset() + window.term.reset(); window.clipboard?.dispose(); window.clipboard = new ClipboardAddon(); window.term.loadAddon(window.clipboard); @@ -92,4 +92,22 @@ test.describe('ClipboardAddon', () => { deepEqual(await ctx.page.evaluate(() => window.navigator.clipboard.readText()), ''); }); }); + + test.describe('non-ASCII', () => { + const testDataEncoded = '4oKsbWzDpMO8dMOf'; + const testDataDecoded = '€mläütß'; + test('write simple string', async () => { + await ctx.proxy.write(`\x1b]52;c;${testDataEncoded}\x07`); + deepEqual(await ctx.page.evaluate(() => window.navigator.clipboard.readText()), testDataDecoded); + }); + test('read simple string', async () => { + await ctx.page.evaluate(` + window.data2 = []; + window.term.onData(e => data2.push(e)); + `); + await ctx.page.evaluate((d) => window.navigator.clipboard.writeText(d), testDataDecoded); + await ctx.proxy.write(`\x1b]52;c;?\x07`); + deepEqual(await ctx.page.evaluate('window.data2'), [`\x1b]52;c;${testDataEncoded}\x07`]); + }); + }); }); From f33f502267455f40e64a89a6a554b8d6beae6284 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 6 Jun 2026 09:09:32 -0700 Subject: [PATCH 148/153] fix(DomRenderer): resilience when buffer lines are missing (#5998) * fix(DomRenderer): skip missing buffer lines during row render renderRows and link underline refresh stopped updating when any viewport row lacked buffer data, leaving stale DOM on rows below. Match ImageStorage.render behavior: clear the row element when present and continue with remaining rows. Co-authored-by: Cursor * fix(DomRenderer): clear blink state when skipping missing lines When a viewport row has no buffer line, renderRows clears the row DOM and continues but did not reset _rowHasBlinkingCells for that row. That could leave the text blink manager thinking blinking cells remain after the row was emptied. Clear blink state for the row when skipping a missing buffer line, including during link underline refresh. Co-authored-by: Cursor --------- Co-authored-by: Cursor --- src/browser/renderer/dom/DomRenderer.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index 383cf3025..657217f71 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -536,9 +536,14 @@ export class DomRenderer extends Disposable implements IRenderer { for (let y = start; y <= end; y++) { const row = y + buffer.ydisp; const rowElement = this._rowElements[y]; + if (!rowElement) { + continue; + } const lineData = buffer.lines.get(row); - if (!rowElement || !lineData) { - break; + if (!lineData) { + rowElement.replaceChildren(); + this._setRowBlinkState(y, false); + continue; } rowElement.replaceChildren( ...this._rowFactory.createRow( @@ -610,9 +615,14 @@ export class DomRenderer extends Disposable implements IRenderer { for (let i = y; i <= y2; ++i) { const row = i + buffer.ydisp; const rowElement = this._rowElements[i]; + if (!rowElement) { + continue; + } const bufferline = buffer.lines.get(row); - if (!rowElement || !bufferline) { - break; + if (!bufferline) { + rowElement.replaceChildren(); + this._setRowBlinkState(i, false); + continue; } rowElement.replaceChildren( ...this._rowFactory.createRow( From 2fe3fd13a164f956d0c6a2365fa89feaf4366074 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 6 Jun 2026 09:15:09 -0700 Subject: [PATCH 149/153] fix(addon-webgl): skip missing buffer lines in _updateModel (#5991) * fix(addon-webgl): skip missing buffer lines in _updateModel _updateModel force-unwrapped buffer.lines.get(row), which can be undefined during trim or resize. That threw and left stale glyphs on rows below the gap. Clear the WebGL model row and glyph cells, reset blink state, and continue with remaining viewport rows, matching DomRenderer and ImageStorage handling. Co-authored-by: Cursor * refactor(addon-webgl): extract _nullModelCell helper Co-authored-by: Daniel Imms --------- Co-authored-by: Cursor Co-authored-by: Daniel Imms --- addons/addon-webgl/src/WebglRenderer.ts | 27 +++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 2f38e03cb..a951efba5 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -456,7 +456,18 @@ export class WebglRenderer extends Disposable implements IRenderer { for (y = start; y <= end; y++) { row = y + terminal.buffer.ydisp; - line = terminal.buffer.lines.get(row)!; + const bufferLine = terminal.buffer.lines.get(row); + if (!bufferLine) { + this._model.lineLengths[y] = 0; + for (x = 0; x < terminal.cols; x++) { + j = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL; + modelUpdated = true; + this._nullModelCell(x, y, j, 0, 0, 0); + } + this._setRowBlinkState(y, false); + continue; + } + line = bufferLine; let rowHasBlinkingCells = false; this._model.lineLengths[y] = 0; isCursorRow = cursorY === row; @@ -587,13 +598,9 @@ export class WebglRenderer extends Disposable implements IRenderer { // Null out non-first cells for (x++; x <= lastCharX; x++) { j = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL; - this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0, 0); - this._model.cells[j] = NULL_CELL_CODE; // Don't re-resolve the cell color since multi-colored ligature backgrounds are not // supported - this._model.cells[j + RenderModelConstants.BG_OFFSET] = this._cellColorResolver.result.bg; - this._model.cells[j + RenderModelConstants.FG_OFFSET] = this._cellColorResolver.result.fg; - this._model.cells[j + RenderModelConstants.EXT_OFFSET] = this._cellColorResolver.result.ext; + this._nullModelCell(x, y, j, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext); } x--; // Go back to the previous update cell for next iteration } @@ -607,6 +614,14 @@ export class WebglRenderer extends Disposable implements IRenderer { this._updateTextBlinkState(); } + private _nullModelCell(x: number, y: number, cellIndex: number, bg: number, fg: number, ext: number): void { + this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, bg, fg, ext, NULL_CELL_CHAR, 0, 0); + this._model.cells[cellIndex] = NULL_CELL_CODE; + this._model.cells[cellIndex + RenderModelConstants.BG_OFFSET] = bg; + this._model.cells[cellIndex + RenderModelConstants.FG_OFFSET] = fg; + this._model.cells[cellIndex + RenderModelConstants.EXT_OFFSET] = ext; + } + private _resetBlinkingRowState(): void { this._rowHasBlinkingCells = new Array(this._terminal.rows).fill(false); this._rowHasBlinkingCellsCount = 0; From 3d929a1db0e01f94b3bd1c4f02d538ac5ea454ae Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 6 Jun 2026 09:18:47 -0700 Subject: [PATCH 150/153] fix(selection): drag-scroll end row and trim start clamp (#5993) * fix(selection): drag-scroll end row matches last visible line Use ydisp + rows - 1 for selectionEnd when scrolling down during drag, consistent with finalSelectionEnd for select-all. Co-authored-by: Daniel Imms * fix(selection): reset start to origin when start row is trimmed When handleTrim removes the selection start row, clamp to [0,0] and refresh; do not keep a stale column on row 0. Co-authored-by: Daniel Imms --------- Co-authored-by: Cursor Agent Co-authored-by: Daniel Imms --- src/browser/selection/SelectionModel.test.ts | 7 +++++++ src/browser/selection/SelectionModel.ts | 5 +++-- src/browser/services/SelectionService.ts | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/browser/selection/SelectionModel.test.ts b/src/browser/selection/SelectionModel.test.ts index 5f813d7ee..10ca79a34 100644 --- a/src/browser/selection/SelectionModel.test.ts +++ b/src/browser/selection/SelectionModel.test.ts @@ -64,6 +64,13 @@ describe('SelectionModel', () => { assert.deepEqual(model.finalSelectionStart, undefined); assert.deepEqual(model.finalSelectionEnd, undefined); }); + it('should reset selection start to origin when start row is trimmed', () => { + model.selectionStart = [50, 0]; + model.selectionEnd = [10, 2]; + assert.equal(model.handleTrim(1), true); + assert.deepEqual(model.finalSelectionStart, [0, 0]); + assert.deepEqual(model.finalSelectionEnd, [10, 1]); + }); }); describe('finalSelectionStart', () => { diff --git a/src/browser/selection/SelectionModel.ts b/src/browser/selection/SelectionModel.ts index 7b62b230b..8f87f17e2 100644 --- a/src/browser/selection/SelectionModel.ts +++ b/src/browser/selection/SelectionModel.ts @@ -135,9 +135,10 @@ export class SelectionModel { return true; } - // If the selection start is trimmed, ensure the start column is 0. + // If the selection start row is trimmed away, reset to the buffer origin. if (this.selectionStart && this.selectionStart[1] < 0) { - this.selectionStart[1] = 0; + this.selectionStart = [0, 0]; + return true; } return false; } diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index 576bdc525..39d8dd76b 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -704,7 +704,7 @@ export class SelectionService extends Disposable implements ISelectionService { if (this._activeSelectionMode !== SelectionMode.COLUMN) { this._model.selectionEnd[0] = this._bufferService.cols; } - this._model.selectionEnd[1] = Math.min(buffer.ydisp + this._bufferService.rows, buffer.lines.length - 1); + this._model.selectionEnd[1] = Math.min(buffer.ydisp + this._bufferService.rows - 1, buffer.lines.length - 1); } else { if (this._activeSelectionMode !== SelectionMode.COLUMN) { this._model.selectionEnd[0] = 0; From c8f11d63b5f8b01f4bccbbf4d9bd25ef19e0dfad Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 6 Jun 2026 09:43:22 -0700 Subject: [PATCH 151/153] fix(terminal): always run clear() at cursor home (#5992) Remove early return when ybase===0 && y===0 so clear() always clears markers, resets buffer lines, and refills the viewport. Co-authored-by: Cursor Agent Co-authored-by: Daniel Imms --- src/browser/CoreBrowserTerminal.ts | 4 ---- src/headless/Terminal.ts | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index 2b024e72e..4557e1652 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -1072,10 +1072,6 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { * Clear the entire buffer, making the prompt line the new first line. */ public clear(): void { - if (this.buffer.ybase === 0 && this.buffer.y === 0) { - // Don't clear if it's already clear - return; - } this.buffer.clearAllMarkers(); this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)!); this.buffer.lines.length = 1; diff --git a/src/headless/Terminal.ts b/src/headless/Terminal.ts index 4d90cc03e..16dc932be 100644 --- a/src/headless/Terminal.ts +++ b/src/headless/Terminal.ts @@ -99,10 +99,6 @@ export class Terminal extends CoreTerminal { * Clear the entire buffer, making the prompt line the new first line. */ public clear(): void { - if (this.buffer.ybase === 0 && this.buffer.y === 0) { - // Don't clear if it's already clear - return; - } this.buffer.clearAllMarkers(); this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)!); this.buffer.lines.length = 1; From f57b8e1534ac7ab013002a09df140c470fddf9bf Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 6 Jun 2026 09:58:42 -0700 Subject: [PATCH 152/153] Update GitHub Actions to Node 24 compatible versions (#6007) Upgrade actions/checkout, setup-node, upload-artifact, download-artifact, and codeql-action to their latest major versions that run on Node 24. Replace NathanFirmo/wait-for-other-job with native needs: build for test-unit and test-integration jobs. Fixes #6006 Co-authored-by: Cursor Agent Co-authored-by: Daniel Imms --- .github/workflows/ci.yml | 46 ++++++++++------------- .github/workflows/codeql-analysis.yml | 8 ++-- .github/workflows/copilot-setup-steps.yml | 4 +- .github/workflows/release.yml | 4 +- 4 files changed, 27 insertions(+), 35 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 204faf12a..0f8385894 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,9 +11,9 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 - name: Use Node.js 22.x - uses: actions/setup-node@v3 + uses: actions/setup-node@v5 with: node-version: 22.x cache: 'npm' @@ -69,7 +69,7 @@ jobs: ./addons/addon-webgl/out/* \ ./addons/addon-webgl/out-*st/* - name: Upload artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: build-artifacts path: compressed-build.zip @@ -79,9 +79,9 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 - name: Use Node.js 22.x - uses: actions/setup-node@v3 + uses: actions/setup-node@v5 with: node-version: 22.x cache: 'npm' @@ -100,16 +100,16 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 - name: Use Node.js 22.x - uses: actions/setup-node@v3 + uses: actions/setup-node@v5 with: node-version: 22.x cache: 'npm' - name: Install dependencies run: | npm ci - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v7 with: name: build-artifacts - name: Unzip artifacts @@ -129,6 +129,7 @@ jobs: exit $EXIT_CODE test-unit: + needs: build timeout-minutes: 20 strategy: matrix: @@ -136,21 +137,16 @@ jobs: runs-on: [ubuntu, macos, windows] runs-on: ${{ matrix.runs-on }}-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 - name: Use Node.js ${{ matrix.node-version }}.x - uses: actions/setup-node@v3 + uses: actions/setup-node@v5 with: node-version: ${{ matrix.node-version }}.x cache: 'npm' - name: Install dependencies run: | npm ci - - name: Wait for build job - uses: NathanFirmo/wait-for-other-job@v1.1.1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - job: build - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v7 with: name: build-artifacts - name: Unzip artifacts @@ -166,6 +162,7 @@ jobs: run: npm run test-unit --forbid-only test-integration: + needs: build timeout-minutes: 20 strategy: matrix: @@ -174,9 +171,9 @@ jobs: browser: [chromium, firefox, webkit] runs-on: ${{ matrix.runs-on }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 - name: Use Node.js ${{ matrix.node-version }}.x - uses: actions/setup-node@v3 + uses: actions/setup-node@v5 with: node-version: ${{ matrix.node-version }}.x cache: 'npm' @@ -185,12 +182,7 @@ jobs: npm ci - name: Install playwright run: npx playwright install --with-deps ${{ matrix.browser }} - - name: Wait for build job - uses: NathanFirmo/wait-for-other-job@v1.1.1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - job: build - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v7 with: name: build-artifacts - name: Unzip artifacts @@ -240,9 +232,9 @@ jobs: matrix: node-version: [22] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 - name: Use Node.js ${{ matrix.node-version }}.x - uses: actions/setup-node@v3 + uses: actions/setup-node@v5 with: node-version: ${{ matrix.node-version }}.x cache: 'npm' @@ -251,7 +243,7 @@ jobs: npm ci - name: Install playwright run: npx playwright install - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v7 with: name: build-artifacts - name: Unzip artifacts diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 3ba91f7e9..f0d27975c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -24,12 +24,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v5 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v4 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v4 diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index f4854bd28..79e0fc306 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -18,9 +18,9 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Use Node.js 22.x - uses: actions/setup-node@v3 + uses: actions/setup-node@v5 with: node-version: 22.x cache: 'npm' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e1e659ecf..3dacee1d6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,9 +10,9 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 - name: Use Node.js 22.x - uses: actions/setup-node@v3 + uses: actions/setup-node@v5 with: node-version: 22.x cache: 'npm' From 3cdbc77386af12e6812171cc3c525b569d076835 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 6 Jun 2026 11:12:04 -0700 Subject: [PATCH 153/153] fix(addon-search): advance highlight scan by buffer match size (#5994) * fix(addon-search): advance highlight scan by buffer match size _highlightAllMatches() stepped to the next find() using term.length and col + 1 when staying on the same row. Search results expose size in buffer columns (wide glyphs, wrapped matches), which select() and decorations already use. Advance with col + size and wrap row/col when crossing the terminal width so highlight-all does not skip, duplicate, or stop early on non-ASCII matches. Co-authored-by: Cursor * test(addon-search): regression for highlight-all wide char advance by size Co-authored-by: Daniel Imms * test(addon-search): move highlight-all regression to integration tests Co-authored-by: Daniel Imms --------- Co-authored-by: Cursor Co-authored-by: Daniel Imms --- addons/addon-search/src/SearchAddon.ts | 14 ++++++++------ addons/addon-search/test/SearchAddon.test.ts | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/addons/addon-search/src/SearchAddon.ts b/addons/addon-search/src/SearchAddon.ts index cb4094743..e5fd31cfb 100644 --- a/addons/addon-search/src/SearchAddon.ts +++ b/addons/addon-search/src/SearchAddon.ts @@ -142,12 +142,14 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp } prevResult = result; results.push(prevResult); - result = this._engine.find( - term, - 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 - ); + const cols = this._terminal.cols; + let nextCol = prevResult.col + prevResult.size; + let nextRow = prevResult.row; + if (nextCol >= cols) { + nextRow += Math.floor(nextCol / cols); + nextCol = nextCol % cols; + } + result = this._engine.find(term, nextRow, nextCol, searchOptions); } this._resultTracker.updateResults(results, this._highlightLimit); diff --git a/addons/addon-search/test/SearchAddon.test.ts b/addons/addon-search/test/SearchAddon.test.ts index 3438772a8..4a62aca7f 100644 --- a/addons/addon-search/test/SearchAddon.test.ts +++ b/addons/addon-search/test/SearchAddon.test.ts @@ -414,6 +414,20 @@ test.describe('Search Tests', () => { }); test.describe('Regression tests', () => { + test('should advance highlight-all scan by buffer match size for wide characters', async () => { + await ctx.page.evaluate(` + window.calls = []; + window.search.onDidChangeResults(e => window.calls.push(e)); + `); + await ctx.proxy.resize(3, 5); + await ctx.proxy.write('𝄞𝄞𝄞'); + strictEqual(await ctx.page.evaluate(`window.search.findNext('𝄞', { decorations: { activeMatchColorOverviewRuler: '#ff0000', matchOverviewRuler: '#ffff00' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 3, resultIndex: 0 } + ]); + await ctx.proxy.resize(80, 24); + }); + test.describe('#2444 wrapped line content not being found', () => { let fixture: string; test.beforeAll(async () => {