From 0aeeebdd646b7ddd9b990565669778cf46b42dfa Mon Sep 17 00:00:00 2001 From: Simon Lamon <32477463+silamon@users.noreply.github.com> Date: Sat, 4 Nov 2023 13:05:42 +0000 Subject: [PATCH 01/36] Devcontainer update --- .devcontainer/devcontainer.json | 8 +++++--- .nvmrc | 2 +- .vscode/launch.json | 2 +- package.json | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c09a29bfd8..9c83cc4cb9 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,13 +1,15 @@ { "name": "xterm.js", - "image": "mcr.microsoft.com/devcontainers/typescript-node:0-18-buster", + "image": "mcr.microsoft.com/devcontainers/typescript-node:18-bookworm", "features": { - "ghcr.io/devcontainers/features/node:1": {} // yarn + "ghcr.io/devcontainers/features/node:1": { + "version": 18 + } // yarn }, "forwardPorts": [ 3000 ], - "postCreateCommand": "yarn install", + "postCreateCommand": "yarn install && yarn setup", "customizations": { "vscode": { "extensions": [ diff --git a/.nvmrc b/.nvmrc index b6a7d89c68..3c032078a4 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -16 +18 diff --git a/.vscode/launch.json b/.vscode/launch.json index 5dbd01ce24..eaa5e12ec7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -61,7 +61,7 @@ "runtimeExecutable": "npm", "runtimeArgs": ["start"], "stopOnEntry": true, - "runtimeVersion": "16", + "runtimeVersion": "18", "serverReadyAction": { "action": "openExternally", "pattern": "App listening to (http://.*?:[0-9]+)" diff --git a/package.json b/package.json index d0510e5d1a..28e9952b58 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "cross-env": "^7.0.3", "deep-equal": "^2.0.5", "eslint": "^8.45.0", - "eslint-plugin-jsdoc": "^39.3.6", + "eslint-plugin-jsdoc": "^46.8.2", "express": "^4.17.1", "express-ws": "^5.0.2", "glob": "^7.2.0", From 5c1d9b28cd411ee1c1787ddc971b7bdc9c418b80 Mon Sep 17 00:00:00 2001 From: Simon Lamon <32477463+silamon@users.noreply.github.com> Date: Sat, 4 Nov 2023 13:10:36 +0000 Subject: [PATCH 02/36] Update yarn lock --- yarn.lock | 69 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/yarn.lock b/yarn.lock index a0cb39da57..5f25390b02 100644 --- a/yarn.lock +++ b/yarn.lock @@ -265,14 +265,14 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@es-joy/jsdoccomment@~0.36.1": - version "0.36.1" - resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.36.1.tgz#c37db40da36e4b848da5fd427a74bae3b004a30f" - integrity sha512-922xqFsTpHs6D0BUiG4toiyPOMc8/jafnWKxz1KWgS4XzKPy2qXf1Pe6UFuNSCQqt6tOuhAWXBNuuyUhJmw9Vg== +"@es-joy/jsdoccomment@~0.40.1": + version "0.40.1" + resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.40.1.tgz#13acd77fb372ed1c83b7355edd865a3b370c9ec4" + integrity sha512-YORCdZSusAlBrFpZ77pJjc5r1bQs5caPWtAu+WWmiSo+8XaUzseapVrfAtiRFbQWnrBxxLLEwF6f6ZG/UgCQCg== dependencies: - comment-parser "1.3.1" - esquery "^1.4.0" - jsdoc-type-pratt-parser "~3.1.0" + comment-parser "1.4.0" + esquery "^1.5.0" + jsdoc-type-pratt-parser "~4.0.0" "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" @@ -1029,6 +1029,11 @@ archy@^1.0.0: resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" integrity sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw== +are-docs-informative@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/are-docs-informative/-/are-docs-informative-0.0.2.tgz#387f0e93f5d45280373d387a59d34c96db321963" + integrity sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -1149,6 +1154,11 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +builtin-modules@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" + integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== + bytes@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" @@ -1355,10 +1365,10 @@ commander@^7.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== -comment-parser@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.3.1.tgz#3d7ea3adaf9345594aedee6563f422348f165c1b" - integrity sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA== +comment-parser@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.4.0.tgz#0f8c560f59698193854f12884c20c0e39a26d32c" + integrity sha512-QLyTNiZ2KDOibvFPlZ6ZngVsZ/0gYnE6uTXi5aoDg8ed3AkJAz4sEje3Y8a29hQ1s6A99MZXe47fLAXQ1rTqaw== commondir@^1.0.1: version "1.0.1" @@ -1666,17 +1676,19 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-plugin-jsdoc@^39.3.6: - version "39.9.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.9.1.tgz#e9ce1723411fd7ea0933b3ef0dd02156ae3068e2" - integrity sha512-Rq2QY6BZP2meNIs48aZ3GlIlJgBqFCmR55+UBvaDkA3ZNQ0SvQXOs2QKkubakEijV8UbIVbVZKsOVN8G3MuqZw== +eslint-plugin-jsdoc@^46.8.2: + version "46.8.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.8.2.tgz#3e6b1c93e91e38fe01874d45da121b56393c54a5" + integrity sha512-5TSnD018f3tUJNne4s4gDWQflbsgOycIKEUBoCLn6XtBMgNHxQFmV8vVxUtiPxAQq8lrX85OaSG/2gnctxw9uQ== dependencies: - "@es-joy/jsdoccomment" "~0.36.1" - comment-parser "1.3.1" + "@es-joy/jsdoccomment" "~0.40.1" + are-docs-informative "^0.0.2" + comment-parser "1.4.0" debug "^4.3.4" escape-string-regexp "^4.0.0" - esquery "^1.4.0" - semver "^7.3.8" + esquery "^1.5.0" + is-builtin-module "^3.2.1" + semver "^7.5.4" spdx-expression-parse "^3.0.1" eslint-scope@5.1.1: @@ -1757,7 +1769,7 @@ esprima@^4.0.0, esprima@^4.0.1: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.4.0, esquery@^1.4.2: +esquery@^1.4.2, esquery@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== @@ -2341,6 +2353,13 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-builtin-module@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" + integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== + dependencies: + builtin-modules "^3.3.0" + is-callable@^1.1.3: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" @@ -2599,10 +2618,10 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -jsdoc-type-pratt-parser@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-3.1.0.tgz#a4a56bdc6e82e5865ffd9febc5b1a227ff28e67e" - integrity sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw== +jsdoc-type-pratt-parser@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz#136f0571a99c184d84ec84662c45c29ceff71114" + integrity sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ== jsdom@^18.0.1: version "18.1.1" @@ -3348,7 +3367,7 @@ semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.4, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4: +semver@^7.3.4, semver@^7.5.3, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== From b1d9b4751deffd1669b3cbf7b9384619ccd13dca Mon Sep 17 00:00:00 2001 From: tisilent Date: Mon, 1 Jan 2024 22:25:54 +0800 Subject: [PATCH 03/36] Check after updating the SelectionRenderModel --- src/browser/renderer/dom/DomRenderer.ts | 8 +++----- test/playwright/SharedRendererTests.ts | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index 1549b1307c..439f8457e8 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -324,6 +324,9 @@ export class DomRenderer extends Disposable implements IRenderer { } this._selectionRenderModel.update(this._terminal, start, end, columnSelectMode); + if (!this._selectionRenderModel.hasSelection) { + return; + } // Translate from buffer position to viewport position const viewportStartRow = this._selectionRenderModel.viewportStartRow; @@ -331,11 +334,6 @@ export class DomRenderer extends Disposable implements IRenderer { const viewportCappedStartRow = this._selectionRenderModel.viewportCappedStartRow; const viewportCappedEndRow = this._selectionRenderModel.viewportCappedEndRow; - // No need to draw the selection - if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) { - return; - } - // Create the selections const documentFragment = this._document.createDocumentFragment(); diff --git a/test/playwright/SharedRendererTests.ts b/test/playwright/SharedRendererTests.ts index 657fba4441..5871e8e4e8 100644 --- a/test/playwright/SharedRendererTests.ts +++ b/test/playwright/SharedRendererTests.ts @@ -1128,6 +1128,20 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 0, 255]); // inverse foreground of '■' should be default await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [0, 0, 255, 255]); // inverse background of ' ' should be decoration bg override }); + test('#4911 The selection should not be displayed if it is not within the scope of the viewport.', async () => { + const theme: ITheme = { + selectionBackground: '#FF0000' + }; + await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); + for (let index = 0; index < 160; index++) { + await ctx.value.proxy.writeln(``); + } + await ctx.value.proxy.scrollToBottom(); + const rows = await ctx.value.proxy.buffer.active.length; + await ctx.value.proxy.selectLines(rows - 1, rows - 1); + await ctx.value.proxy.scrollLines(-2); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 0, 255]); + }); }); test.describe('regression tests', () => { From 750f3179dec28a3a8620b6856ce2d898b70ece5e Mon Sep 17 00:00:00 2001 From: tisilent Date: Mon, 1 Jan 2024 22:48:39 +0800 Subject: [PATCH 04/36] move test --- test/playwright/SharedRendererTests.ts | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/playwright/SharedRendererTests.ts b/test/playwright/SharedRendererTests.ts index 5871e8e4e8..322e03f499 100644 --- a/test/playwright/SharedRendererTests.ts +++ b/test/playwright/SharedRendererTests.ts @@ -1128,20 +1128,6 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 0, 255]); // inverse foreground of '■' should be default await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [0, 0, 255, 255]); // inverse background of ' ' should be decoration bg override }); - test('#4911 The selection should not be displayed if it is not within the scope of the viewport.', async () => { - const theme: ITheme = { - selectionBackground: '#FF0000' - }; - await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - for (let index = 0; index < 160; index++) { - await ctx.value.proxy.writeln(``); - } - await ctx.value.proxy.scrollToBottom(); - const rows = await ctx.value.proxy.buffer.active.length; - await ctx.value.proxy.selectLines(rows - 1, rows - 1); - await ctx.value.proxy.scrollLines(-2); - await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 0, 255]); - }); }); test.describe('regression tests', () => { @@ -1246,6 +1232,20 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, rows), [0, 0, 0, 255]); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, rows, CellColorPosition.FIRST), [0, 0, 255, 255]); }); + test('#4917 The selection should not be displayed if it is not within the scope of the viewport.', async () => { + const theme: ITheme = { + selectionBackground: '#FF0000' + }; + await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); + for (let index = 0; index < 160; index++) { + await ctx.value.proxy.writeln(``); + } + await ctx.value.proxy.scrollToBottom(); + const rows = await ctx.value.proxy.buffer.active.length; + await ctx.value.proxy.selectLines(rows - 1, rows - 1); + await ctx.value.proxy.scrollLines(-2); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 0, 255]); + }); }); } From b0667aacca4cc1d0c14d57bb6f51333bd03fecea Mon Sep 17 00:00:00 2001 From: Simon Siefke Date: Thu, 4 Jan 2024 22:05:23 +0100 Subject: [PATCH 05/36] fix: memory leak in CoreBrowserService --- src/browser/services/CoreBrowserService.ts | 28 ++++++++++++---------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/browser/services/CoreBrowserService.ts b/src/browser/services/CoreBrowserService.ts index 575b62b63c..9999ceec31 100644 --- a/src/browser/services/CoreBrowserService.ts +++ b/src/browser/services/CoreBrowserService.ts @@ -3,17 +3,17 @@ * @license MIT */ -import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; -import { ICoreBrowserService } from './Services'; -import { EventEmitter, forwardEvent } from 'common/EventEmitter'; -import { addDisposableDomListener } from 'browser/Lifecycle'; +import { Disposable, MutableDisposable, toDisposable } from "common/Lifecycle"; +import { ICoreBrowserService } from "./Services"; +import { EventEmitter, forwardEvent } from "common/EventEmitter"; +import { addDisposableDomListener } from "browser/Lifecycle"; export class CoreBrowserService extends Disposable implements ICoreBrowserService { public serviceBrand: undefined; private _isFocused = false; private _cachedIsFocused: boolean | undefined = undefined; - private _screenDprMonitor = new ScreenDprMonitor(this._window); + private _screenDprMonitor = this.register(new ScreenDprMonitor(this._window)); private readonly _onDprChange = this.register(new EventEmitter()); public readonly onDprChange = this._onDprChange.event; @@ -28,11 +28,11 @@ export class CoreBrowserService extends Disposable implements ICoreBrowserServic super(); // Monitor device pixel ratio - this.register(this.onWindowChange(w => this._screenDprMonitor.setWindow(w))); + this.register(this.onWindowChange((w) => this._screenDprMonitor.setWindow(w))); this.register(forwardEvent(this._screenDprMonitor.onDprChange, this._onDprChange)); - this._textarea.addEventListener('focus', () => this._isFocused = true); - this._textarea.addEventListener('blur', () => this._isFocused = false); + this._textarea.addEventListener("focus", () => (this._isFocused = true)); + this._textarea.addEventListener("blur", () => (this._isFocused = false)); } public get window(): Window & typeof globalThis { @@ -53,13 +53,12 @@ export class CoreBrowserService extends Disposable implements ICoreBrowserServic public get isFocused(): boolean { if (this._cachedIsFocused === undefined) { this._cachedIsFocused = this._isFocused && this._textarea.ownerDocument.hasFocus(); - queueMicrotask(() => this._cachedIsFocused = undefined); + queueMicrotask(() => (this._cachedIsFocused = undefined)); } return this._cachedIsFocused; } } - /** * The screen device pixel ratio monitor allows listening for when the * window.devicePixelRatio value changes. This is done not with polling but with @@ -94,7 +93,6 @@ class ScreenDprMonitor extends Disposable { this.register(toDisposable(() => this.clearListener())); } - public setWindow(parentWindow: Window): void { this._parentWindow = parentWindow; this._setWindowResizeListener(); @@ -102,7 +100,9 @@ class ScreenDprMonitor extends Disposable { } private _setWindowResizeListener(): void { - this._windowResizeListener.value = addDisposableDomListener(this._parentWindow, 'resize', () => this._setDprAndFireIfDiffers()); + this._windowResizeListener.value = addDisposableDomListener(this._parentWindow, "resize", () => + this._setDprAndFireIfDiffers() + ); } private _setDprAndFireIfDiffers(): void { @@ -122,7 +122,9 @@ class ScreenDprMonitor extends Disposable { // Add listeners for new DPR this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio; - this._resolutionMediaMatchList = this._parentWindow.matchMedia(`screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)`); + this._resolutionMediaMatchList = this._parentWindow.matchMedia( + `screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)` + ); this._resolutionMediaMatchList.addListener(this._outerListener); } From 9e98b631da085b099af25634bca7100d35162806 Mon Sep 17 00:00:00 2001 From: Simon Siefke Date: Thu, 4 Jan 2024 22:29:12 +0100 Subject: [PATCH 06/36] use disposable for text area listeners --- src/browser/services/CoreBrowserService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/services/CoreBrowserService.ts b/src/browser/services/CoreBrowserService.ts index 9999ceec31..8a400ffd05 100644 --- a/src/browser/services/CoreBrowserService.ts +++ b/src/browser/services/CoreBrowserService.ts @@ -31,8 +31,8 @@ export class CoreBrowserService extends Disposable implements ICoreBrowserServic this.register(this.onWindowChange((w) => this._screenDprMonitor.setWindow(w))); this.register(forwardEvent(this._screenDprMonitor.onDprChange, this._onDprChange)); - this._textarea.addEventListener("focus", () => (this._isFocused = true)); - this._textarea.addEventListener("blur", () => (this._isFocused = false)); + this.register(addDisposableDomListener(this._textarea, 'focus', () => (this._isFocused = true))) + this.register(addDisposableDomListener(this._textarea, 'blur', () => (this._isFocused = false))) } public get window(): Window & typeof globalThis { From f4869896977168bd608e7f878509bcde48559012 Mon Sep 17 00:00:00 2001 From: Simon Siefke Date: Thu, 4 Jan 2024 22:32:59 +0100 Subject: [PATCH 07/36] fix formatting --- src/browser/services/CoreBrowserService.ts | 28 ++++++++++++---------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/browser/services/CoreBrowserService.ts b/src/browser/services/CoreBrowserService.ts index 8a400ffd05..6b2c13c3c4 100644 --- a/src/browser/services/CoreBrowserService.ts +++ b/src/browser/services/CoreBrowserService.ts @@ -3,10 +3,10 @@ * @license MIT */ -import { Disposable, MutableDisposable, toDisposable } from "common/Lifecycle"; -import { ICoreBrowserService } from "./Services"; -import { EventEmitter, forwardEvent } from "common/EventEmitter"; -import { addDisposableDomListener } from "browser/Lifecycle"; +import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; +import { ICoreBrowserService } from './Services'; +import { EventEmitter, forwardEvent } from 'common/EventEmitter'; +import { addDisposableDomListener } from 'browser/Lifecycle'; export class CoreBrowserService extends Disposable implements ICoreBrowserService { public serviceBrand: undefined; @@ -28,11 +28,15 @@ export class CoreBrowserService extends Disposable implements ICoreBrowserServic super(); // Monitor device pixel ratio - this.register(this.onWindowChange((w) => this._screenDprMonitor.setWindow(w))); + this.register(this.onWindowChange(w => this._screenDprMonitor.setWindow(w))); this.register(forwardEvent(this._screenDprMonitor.onDprChange, this._onDprChange)); - this.register(addDisposableDomListener(this._textarea, 'focus', () => (this._isFocused = true))) - this.register(addDisposableDomListener(this._textarea, 'blur', () => (this._isFocused = false))) + this.register( + addDisposableDomListener(this._textarea, 'focus', () => (this._isFocused = true)) + ); + this.register( + addDisposableDomListener(this._textarea, 'blur', () => (this._isFocused = false)) + ); } public get window(): Window & typeof globalThis { @@ -53,12 +57,13 @@ export class CoreBrowserService extends Disposable implements ICoreBrowserServic public get isFocused(): boolean { if (this._cachedIsFocused === undefined) { this._cachedIsFocused = this._isFocused && this._textarea.ownerDocument.hasFocus(); - queueMicrotask(() => (this._cachedIsFocused = undefined)); + queueMicrotask(() => this._cachedIsFocused = undefined); } return this._cachedIsFocused; } } + /** * The screen device pixel ratio monitor allows listening for when the * window.devicePixelRatio value changes. This is done not with polling but with @@ -93,6 +98,7 @@ class ScreenDprMonitor extends Disposable { this.register(toDisposable(() => this.clearListener())); } + public setWindow(parentWindow: Window): void { this._parentWindow = parentWindow; this._setWindowResizeListener(); @@ -100,7 +106,7 @@ class ScreenDprMonitor extends Disposable { } private _setWindowResizeListener(): void { - this._windowResizeListener.value = addDisposableDomListener(this._parentWindow, "resize", () => + this._windowResizeListener.value = addDisposableDomListener(this._parentWindow, 'resize', () => this._setDprAndFireIfDiffers() ); } @@ -122,9 +128,7 @@ class ScreenDprMonitor extends Disposable { // Add listeners for new DPR this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio; - this._resolutionMediaMatchList = this._parentWindow.matchMedia( - `screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)` - ); + this._resolutionMediaMatchList = this._parentWindow.matchMedia(`screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)`); this._resolutionMediaMatchList.addListener(this._outerListener); } From 819254e5403cc4a1adb8b5be39eb0c0af2d22460 Mon Sep 17 00:00:00 2001 From: Simon Siefke Date: Thu, 4 Jan 2024 22:33:39 +0100 Subject: [PATCH 08/36] fix formatting --- src/browser/services/CoreBrowserService.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/browser/services/CoreBrowserService.ts b/src/browser/services/CoreBrowserService.ts index 6b2c13c3c4..a6c066b208 100644 --- a/src/browser/services/CoreBrowserService.ts +++ b/src/browser/services/CoreBrowserService.ts @@ -106,9 +106,7 @@ class ScreenDprMonitor extends Disposable { } private _setWindowResizeListener(): void { - this._windowResizeListener.value = addDisposableDomListener(this._parentWindow, 'resize', () => - this._setDprAndFireIfDiffers() - ); + this._windowResizeListener.value = addDisposableDomListener(this._parentWindow, 'resize', () => this._setDprAndFireIfDiffers()); } private _setDprAndFireIfDiffers(): void { From 815758529d6d4ad34164b942e03de349a13d8a16 Mon Sep 17 00:00:00 2001 From: octoclonius <25781800+octoclonius@users.noreply.github.com> Date: Sat, 13 Jan 2024 13:21:30 -0600 Subject: [PATCH 09/36] Update global object fix --- addons/addon-attach/webpack.config.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/addon-attach/webpack.config.js b/addons/addon-attach/webpack.config.js index 599bb14204..a882928b06 100644 --- a/addons/addon-attach/webpack.config.js +++ b/addons/addon-attach/webpack.config.js @@ -26,6 +26,8 @@ module.exports = { path: path.resolve('./lib'), library: addonName, libraryTarget: 'umd' + // Force usage of globalThis instead of global / self. (This is cross-env compatible) + globalObject: 'globalThis', }, mode: 'production' }; From 7f7de14e6f3e17b0ab7ecf77a1c1a0242e0b5d42 Mon Sep 17 00:00:00 2001 From: octoclonius <25781800+octoclonius@users.noreply.github.com> Date: Sat, 13 Jan 2024 13:23:33 -0600 Subject: [PATCH 10/36] Add comma --- addons/addon-attach/webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-attach/webpack.config.js b/addons/addon-attach/webpack.config.js index a882928b06..3599a977fa 100644 --- a/addons/addon-attach/webpack.config.js +++ b/addons/addon-attach/webpack.config.js @@ -25,7 +25,7 @@ module.exports = { filename: mainFile, path: path.resolve('./lib'), library: addonName, - libraryTarget: 'umd' + libraryTarget: 'umd', // Force usage of globalThis instead of global / self. (This is cross-env compatible) globalObject: 'globalThis', }, From 1a67241f67f68831e487c75cfe76ae65326362d9 Mon Sep 17 00:00:00 2001 From: octoclonius <25781800+octoclonius@users.noreply.github.com> Date: Sat, 13 Jan 2024 13:23:57 -0600 Subject: [PATCH 11/36] Update global object fix --- addons/addon-canvas/webpack.config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/addon-canvas/webpack.config.js b/addons/addon-canvas/webpack.config.js index 9daa08f9b2..e0c7fde2f3 100644 --- a/addons/addon-canvas/webpack.config.js +++ b/addons/addon-canvas/webpack.config.js @@ -33,7 +33,9 @@ module.exports = { filename: mainFile, path: path.resolve('./lib'), library: addonName, - libraryTarget: 'umd' + libraryTarget: 'umd', + // Force usage of globalThis instead of global / self. (This is cross-env compatible) + globalObject: 'globalThis', }, mode: 'production' }; From f84fff5f873b44438115183d8058b66bf2bdc867 Mon Sep 17 00:00:00 2001 From: octoclonius <25781800+octoclonius@users.noreply.github.com> Date: Sat, 13 Jan 2024 13:24:15 -0600 Subject: [PATCH 12/36] Update global object fix --- addons/addon-fit/webpack.config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/addon-fit/webpack.config.js b/addons/addon-fit/webpack.config.js index e220668c74..aebb523a6b 100644 --- a/addons/addon-fit/webpack.config.js +++ b/addons/addon-fit/webpack.config.js @@ -25,7 +25,9 @@ module.exports = { filename: mainFile, path: path.resolve('./lib'), library: addonName, - libraryTarget: 'umd' + libraryTarget: 'umd', + // Force usage of globalThis instead of global / self. (This is cross-env compatible) + globalObject: 'globalThis', }, mode: 'production' }; From 8d6fff7000158cc3f118b921f4814b0eda1b17f9 Mon Sep 17 00:00:00 2001 From: octoclonius <25781800+octoclonius@users.noreply.github.com> Date: Sat, 13 Jan 2024 13:27:44 -0600 Subject: [PATCH 13/36] Update webpack.config.js global object fix --- addons/addon-image/webpack.config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/addon-image/webpack.config.js b/addons/addon-image/webpack.config.js index b4283b66e3..239ebd245c 100644 --- a/addons/addon-image/webpack.config.js +++ b/addons/addon-image/webpack.config.js @@ -33,7 +33,9 @@ const addon = { filename: mainFile, path: path.resolve('./lib'), library: addonName, - libraryTarget: 'umd' + libraryTarget: 'umd', + // Force usage of globalThis instead of global / self. (This is cross-env compatible) + globalObject: 'globalThis', }, mode: 'production' }; From 46baff84593bbc3c2854f4743a45e5057e9ccfa8 Mon Sep 17 00:00:00 2001 From: octoclonius <25781800+octoclonius@users.noreply.github.com> Date: Sat, 13 Jan 2024 13:28:05 -0600 Subject: [PATCH 14/36] Update webpack.config.js global object fix --- addons/addon-ligatures/webpack.config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/addon-ligatures/webpack.config.js b/addons/addon-ligatures/webpack.config.js index 6ec7f42d02..f9e9f34784 100644 --- a/addons/addon-ligatures/webpack.config.js +++ b/addons/addon-ligatures/webpack.config.js @@ -25,7 +25,9 @@ module.exports = { filename: mainFile, path: path.resolve('./lib'), library: addonName, - libraryTarget: 'umd' + libraryTarget: 'umd', + // Force usage of globalThis instead of global / self. (This is cross-env compatible) + globalObject: 'globalThis', }, mode: 'production', externals: { From 072cd029e424891f34c109a17b7c8c127763f8ef Mon Sep 17 00:00:00 2001 From: octoclonius <25781800+octoclonius@users.noreply.github.com> Date: Sat, 13 Jan 2024 13:28:21 -0600 Subject: [PATCH 15/36] Update webpack.config.js global object fix --- addons/addon-search/webpack.config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/addon-search/webpack.config.js b/addons/addon-search/webpack.config.js index a770f93f50..7858054871 100644 --- a/addons/addon-search/webpack.config.js +++ b/addons/addon-search/webpack.config.js @@ -32,7 +32,9 @@ module.exports = { filename: mainFile, path: path.resolve('./lib'), library: addonName, - libraryTarget: 'umd' + libraryTarget: 'umd', + // Force usage of globalThis instead of global / self. (This is cross-env compatible) + globalObject: 'globalThis', }, mode: 'production' }; From d8f96ab9c744032f6f6deb91dc5fe4c8ce9e5a32 Mon Sep 17 00:00:00 2001 From: octoclonius <25781800+octoclonius@users.noreply.github.com> Date: Sat, 13 Jan 2024 13:29:20 -0600 Subject: [PATCH 16/36] Update webpack.config.js global object fix `this` might be okay, I'm not actually sure, but xterm.js uses `globalThis` and so I think consistency is better maybe? --- addons/addon-serialize/webpack.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/addon-serialize/webpack.config.js b/addons/addon-serialize/webpack.config.js index bd08ca3745..837a73a388 100644 --- a/addons/addon-serialize/webpack.config.js +++ b/addons/addon-serialize/webpack.config.js @@ -34,7 +34,8 @@ module.exports = { path: path.resolve('./lib'), library: addonName, libraryTarget: 'umd', - globalObject: 'this' + // Force usage of globalThis instead of global / self. (This is cross-env compatible) + globalObject: 'globalThis', }, mode: 'production' }; From 330d7b3100c79bc5f890ca0c28bfef67d55db33c Mon Sep 17 00:00:00 2001 From: octoclonius <25781800+octoclonius@users.noreply.github.com> Date: Sat, 13 Jan 2024 13:29:33 -0600 Subject: [PATCH 17/36] Update webpack.config.js global object fix --- addons/addon-unicode-graphemes/webpack.config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/addon-unicode-graphemes/webpack.config.js b/addons/addon-unicode-graphemes/webpack.config.js index 6a80bdea60..1ebaecaab1 100644 --- a/addons/addon-unicode-graphemes/webpack.config.js +++ b/addons/addon-unicode-graphemes/webpack.config.js @@ -32,7 +32,9 @@ module.exports = { filename: mainFile, path: path.resolve('./lib'), library: addonName, - libraryTarget: 'umd' + libraryTarget: 'umd', + // Force usage of globalThis instead of global / self. (This is cross-env compatible) + globalObject: 'globalThis', }, mode: 'production' }; From 693ec1b2bcaae2f05496f4a696ec39997230cdeb Mon Sep 17 00:00:00 2001 From: octoclonius <25781800+octoclonius@users.noreply.github.com> Date: Sat, 13 Jan 2024 13:29:48 -0600 Subject: [PATCH 18/36] Update webpack.config.js global object fix --- addons/addon-unicode11/webpack.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/addon-unicode11/webpack.config.js b/addons/addon-unicode11/webpack.config.js index 1913481d2c..746d25819b 100644 --- a/addons/addon-unicode11/webpack.config.js +++ b/addons/addon-unicode11/webpack.config.js @@ -33,7 +33,8 @@ module.exports = { path: path.resolve('./lib'), library: addonName, libraryTarget: 'umd', - globalObject: 'this' + // Force usage of globalThis instead of global / self. (This is cross-env compatible) + globalObject: 'globalThis', }, mode: 'production' }; From 8696cf8789575cfabb1b9b364b6b3c1a085e848d Mon Sep 17 00:00:00 2001 From: octoclonius <25781800+octoclonius@users.noreply.github.com> Date: Sat, 13 Jan 2024 13:29:59 -0600 Subject: [PATCH 19/36] Update webpack.config.js global object fix --- addons/addon-web-links/webpack.config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/addon-web-links/webpack.config.js b/addons/addon-web-links/webpack.config.js index 4484dbf604..e8dceceff0 100644 --- a/addons/addon-web-links/webpack.config.js +++ b/addons/addon-web-links/webpack.config.js @@ -25,7 +25,9 @@ module.exports = { filename: mainFile, path: path.resolve('./lib'), library: addonName, - libraryTarget: 'umd' + libraryTarget: 'umd', + // Force usage of globalThis instead of global / self. (This is cross-env compatible) + globalObject: 'globalThis', }, mode: 'production' }; From c0044dc4a5f07d04d1da1b0346bdef09869fabd5 Mon Sep 17 00:00:00 2001 From: octoclonius <25781800+octoclonius@users.noreply.github.com> Date: Sat, 13 Jan 2024 13:30:10 -0600 Subject: [PATCH 20/36] Update webpack.config.js global object fix --- addons/addon-webgl/webpack.config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/addon-webgl/webpack.config.js b/addons/addon-webgl/webpack.config.js index f31ffd513e..7365acff01 100644 --- a/addons/addon-webgl/webpack.config.js +++ b/addons/addon-webgl/webpack.config.js @@ -33,7 +33,9 @@ module.exports = { filename: mainFile, path: path.resolve('./lib'), library: addonName, - libraryTarget: 'umd' + libraryTarget: 'umd', + // Force usage of globalThis instead of global / self. (This is cross-env compatible) + globalObject: 'globalThis', }, mode: 'production' }; From d5827ec77d044af9f8cc3e8719710fb93bd1abb4 Mon Sep 17 00:00:00 2001 From: octoclonius <25781800+octoclonius@users.noreply.github.com> Date: Sat, 13 Jan 2024 19:54:27 -0600 Subject: [PATCH 21/36] Update webpack.config.headless.js --- webpack.config.headless.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/webpack.config.headless.js b/webpack.config.headless.js index 9e9099cdd8..12e7484d7e 100644 --- a/webpack.config.headless.js +++ b/webpack.config.headless.js @@ -39,8 +39,10 @@ const config = { path: path.resolve('./headless/lib-headless'), library: { type: 'commonjs' - } + }, + // Force usage of globalThis instead of global / self. (This is cross-env compatible) + globalObject: 'globalThis', }, - mode: 'production' + mode: 'production', }; module.exports = config; From 67e8e60a6990621ba32b3ee12f89395a070dd495 Mon Sep 17 00:00:00 2001 From: octoclonius <25781800+octoclonius@users.noreply.github.com> Date: Sat, 13 Jan 2024 19:55:18 -0600 Subject: [PATCH 22/36] Update webpack.config.headless.js --- webpack.config.headless.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webpack.config.headless.js b/webpack.config.headless.js index 12e7484d7e..5c8deb160d 100644 --- a/webpack.config.headless.js +++ b/webpack.config.headless.js @@ -43,6 +43,6 @@ const config = { // Force usage of globalThis instead of global / self. (This is cross-env compatible) globalObject: 'globalThis', }, - mode: 'production', + mode: 'production' }; module.exports = config; From d83f442cffc30ecfe4874c0ce27f817451df8262 Mon Sep 17 00:00:00 2001 From: octoclonius <25781800+octoclonius@users.noreply.github.com> Date: Sat, 13 Jan 2024 19:57:38 -0600 Subject: [PATCH 23/36] Update webpack.config.headless.js --- webpack.config.headless.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webpack.config.headless.js b/webpack.config.headless.js index 5c8deb160d..12e7484d7e 100644 --- a/webpack.config.headless.js +++ b/webpack.config.headless.js @@ -43,6 +43,6 @@ const config = { // Force usage of globalThis instead of global / self. (This is cross-env compatible) globalObject: 'globalThis', }, - mode: 'production' + mode: 'production', }; module.exports = config; From 9107ee905a66c9a3e22b6ae6f54e8ea87c8616af Mon Sep 17 00:00:00 2001 From: Homa Wong Date: Fri, 16 Feb 2024 18:08:58 -0800 Subject: [PATCH 24/36] Update xterm.d.ts Fix doc typo --- typings/xterm.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index d957d6551d..9ba11bb3bc 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -48,7 +48,7 @@ declare module '@xterm/xterm' { /** * When enabled the cursor will be set to the beginning of the next line * with every new line. This is equivalent to sending '\r\n' for each '\n'. - * Normally the termios settings of the underlying PTY deals with the + * Normally the terminal settings of the underlying PTY deals with the * translation of '\n' to '\r\n' and this setting should not be used. If you * deal with data from a non-PTY related source, this settings might be * useful. From 88bfc4ad9ccba4e4457f84fdb42f832bc04f15c9 Mon Sep 17 00:00:00 2001 From: Homa Wong Date: Mon, 19 Feb 2024 17:18:52 -0800 Subject: [PATCH 25/36] add termios link --- typings/xterm.d.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 9ba11bb3bc..d2fa9f99c7 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -47,11 +47,13 @@ declare module '@xterm/xterm' { /** * When enabled the cursor will be set to the beginning of the next line - * with every new line. This is equivalent to sending '\r\n' for each '\n'. - * Normally the terminal settings of the underlying PTY deals with the - * translation of '\n' to '\r\n' and this setting should not be used. If you + * with every new line. This is equivalent to sending `\r\n` for each `\n`. + * Normally the settings of the underlying PTY (`termios`) deal with the + * translation of `\n` to `\r\n` and this setting should not be used. If you * deal with data from a non-PTY related source, this settings might be * useful. + * + * @see https://pubs.opengroup.org/onlinepubs/007904975/basedefs/termios.h.html */ convertEol?: boolean; From fe22671a6edfefb38727e1c2d9346baea1e7ad9a Mon Sep 17 00:00:00 2001 From: tisilent Date: Wed, 6 Mar 2024 17:03:25 +0800 Subject: [PATCH 26/36] Clear timer when dispose --- src/browser/Viewport.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index a8e1a49877..cea2ca8fb4 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -49,6 +49,8 @@ export class Viewport extends Disposable implements IViewport { target: -1 }; + private _ensureTimeout: number; + private readonly _onRequestScrollLines = this.register(new EventEmitter<{ amount: number, suppressScrollEvent: boolean }>()); public readonly onRequestScrollLines = this._onRequestScrollLines.event; @@ -81,7 +83,7 @@ export class Viewport extends Disposable implements IViewport { this.register(this._optionsService.onSpecificOptionChange('scrollback', () => this.syncScrollArea())); // Perform this async to ensure the ICharSizeService is ready. - setTimeout(() => this.syncScrollArea()); + this._ensureTimeout = window.setTimeout(() => this.syncScrollArea()); } private _handleThemeChange(colors: ReadonlyColorSet): void { @@ -398,4 +400,8 @@ export class Viewport extends Disposable implements IViewport { this._viewportElement.scrollTop += deltaY; return this._bubbleScroll(ev, deltaY); } + + public dispose(): void { + clearTimeout(this._ensureTimeout); + } } From 553b9f261a2f231d4e16f27d6e745592432c0b94 Mon Sep 17 00:00:00 2001 From: Knox Lively Date: Wed, 6 Mar 2024 23:56:04 -0700 Subject: [PATCH 27/36] adding Wave Terminal to the README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b055ea7b7f..5f4a786ad4 100644 --- a/README.md +++ b/README.md @@ -222,6 +222,7 @@ Xterm.js is used in several world-class applications to provide great terminal e - [**Cloudtutor.io**](https://cloudtutor.io): innovative online learning platform that offers users access to an interactive lab. - [**Helix Editor Playground**](https://github.com/tomgroenwoldt/helix-editor-playground): Online playground for the terminal based helix editor. - [**Coder**](https://github.com/coder/coder): Self-Hosted Remote Development Environments +- [**Wave Terminal**](https://waveterm.dev): An open-source, ai-native, terminal built for seamless workflows. - [And much more...](https://github.com/xtermjs/xterm.js/network/dependents?package_id=UGFja2FnZS0xNjYzMjc4OQ%3D%3D) Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it on our list. Note: Please add any new contributions to the end of the list only. From b40e58bc7709c0d36122c2d78eece11c603b81ef Mon Sep 17 00:00:00 2001 From: Josiah Hudson <108340950+josiahhudson@users.noreply.github.com> Date: Fri, 15 Mar 2024 17:40:21 -0400 Subject: [PATCH 28/36] Fix https://github.com/xtermjs/xterm.js/issues/4944 by only splitting on the first ";" in InputHandler.setHyperlink(). --- src/common/InputHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index a4b8c64b02..3482abc7d1 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -2972,7 +2972,7 @@ export class InputHandler extends Disposable implements IInputHandler { * feedback. Use `OSC 8 ; ; BEL` to finish the current hyperlink. */ public setHyperlink(data: string): boolean { - const args = data.split(';'); + const args = data.match(/^([^;]*);(.*)$/)?.slice(1) ?? []; if (args.length < 2) { return false; } From cdb45c743f63eaa56b1e2e2594bcf5fd86151d9b Mon Sep 17 00:00:00 2001 From: sawka Date: Fri, 29 Mar 2024 00:30:29 -0700 Subject: [PATCH 29/36] escape special html characters in addon-serialize --- addons/addon-serialize/src/SerializeAddon.test.ts | 10 ++++++++++ addons/addon-serialize/src/SerializeAddon.ts | 10 +++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/addons/addon-serialize/src/SerializeAddon.test.ts b/addons/addon-serialize/src/SerializeAddon.test.ts index ac485e549f..5899ad8091 100644 --- a/addons/addon-serialize/src/SerializeAddon.test.ts +++ b/addons/addon-serialize/src/SerializeAddon.test.ts @@ -138,6 +138,16 @@ describe('SerializeAddon', () => { assert.equal((output.match(/
terminal<\/span><\/div>/g) || []).length, 1, output); }); + it('basic terminal with html unsafe chars', async () => { + await writeP(terminal, ' '); + terminal.select(1, 0, 37); + + const output = serializeAddon.serializeAsHTML({ + onlySelection: true + }); + assert.equal((output.match(/
<script>alert("&pi; = 3.14")<\/script><\/span><\/div>/g) || []).length, 1, output); + }); + it('cells with bold styling', async () => { await writeP(terminal, ' ' + sgr('1') + 'terminal' + sgr('22') + ' '); diff --git a/addons/addon-serialize/src/SerializeAddon.ts b/addons/addon-serialize/src/SerializeAddon.ts index e654eddbee..0f87885fe3 100644 --- a/addons/addon-serialize/src/SerializeAddon.ts +++ b/addons/addon-serialize/src/SerializeAddon.ts @@ -14,6 +14,14 @@ function constrain(value: number, low: number, high: number): number { return Math.max(low, Math.min(value, high)); } +function escapeHtmlChar(c: string): string { + switch (c) { + case '&': return '&'; + case '<': return '<'; + } + return c; +} + // TODO: Refine this template class later abstract class BaseSerializeHandler { constructor( @@ -669,7 +677,7 @@ export class HTMLSerializeHandler extends BaseSerializeHandler { if (isEmptyCell) { this._currentRow += ' '; } else { - this._currentRow += cell.getChars(); + this._currentRow += escapeHtmlChar(cell.getChars()); } } From 55e34cb174106ffefa6afb636bbc291eee72c897 Mon Sep 17 00:00:00 2001 From: sawka Date: Fri, 29 Mar 2024 00:44:02 -0700 Subject: [PATCH 30/36] fix unit test (test terminal only has 10 cols) --- addons/addon-serialize/src/SerializeAddon.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/addon-serialize/src/SerializeAddon.test.ts b/addons/addon-serialize/src/SerializeAddon.test.ts index 5899ad8091..7cb071bffb 100644 --- a/addons/addon-serialize/src/SerializeAddon.test.ts +++ b/addons/addon-serialize/src/SerializeAddon.test.ts @@ -139,13 +139,13 @@ describe('SerializeAddon', () => { }); it('basic terminal with html unsafe chars', async () => { - await writeP(terminal, ' '); - terminal.select(1, 0, 37); + await writeP(terminal, ' π '); + terminal.select(1, 0, 7); const output = serializeAddon.serializeAsHTML({ onlySelection: true }); - assert.equal((output.match(/
<script>alert("&pi; = 3.14")<\/script><\/span><\/div>/g) || []).length, 1, output); + assert.equal((output.match(/
<a>&pi;<\/span><\/div>/g) || []).length, 1, output); }); it('cells with bold styling', async () => { From 431045619e8f9cf314738e5aeece56d709cfbe30 Mon Sep 17 00:00:00 2001 From: sawka Date: Fri, 29 Mar 2024 10:26:44 -0700 Subject: [PATCH 31/36] match capitalization of HTML with the rest of the file --- addons/addon-serialize/src/SerializeAddon.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/addon-serialize/src/SerializeAddon.ts b/addons/addon-serialize/src/SerializeAddon.ts index 0f87885fe3..cd15cfc373 100644 --- a/addons/addon-serialize/src/SerializeAddon.ts +++ b/addons/addon-serialize/src/SerializeAddon.ts @@ -14,7 +14,7 @@ function constrain(value: number, low: number, high: number): number { return Math.max(low, Math.min(value, high)); } -function escapeHtmlChar(c: string): string { +function escapeHTMLChar(c: string): string { switch (c) { case '&': return '&'; case '<': return '<'; @@ -677,7 +677,7 @@ export class HTMLSerializeHandler extends BaseSerializeHandler { if (isEmptyCell) { this._currentRow += ' '; } else { - this._currentRow += escapeHtmlChar(cell.getChars()); + this._currentRow += escapeHTMLChar(cell.getChars()); } } From 75491f41526512c4786b1cb3feea0bcf4ca124e1 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 21 Apr 2024 07:42:17 -0700 Subject: [PATCH 32/36] Remove trailing whitespace --- typings/xterm.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 8c92fec82c..b64702757c 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -52,7 +52,7 @@ declare module '@xterm/xterm' { * translation of `\n` to `\r\n` and this setting should not be used. If you * deal with data from a non-PTY related source, this settings might be * useful. - * + * * @see https://pubs.opengroup.org/onlinepubs/007904975/basedefs/termios.h.html */ convertEol?: boolean; From f1e0737c5ef6222a6f41f65efa7fa68c2251b2b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Apr 2024 14:47:44 +0000 Subject: [PATCH 33/36] Bump express from 4.18.2 to 4.19.2 Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2) --- updated-dependencies: - dependency-name: express dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- yarn.lock | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/yarn.lock b/yarn.lock index b278879316..e566fff21f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1099,13 +1099,13 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -body-parser@1.20.1: - version "1.20.1" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" - integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== +body-parser@1.20.2: + version "1.20.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" + integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== dependencies: bytes "3.1.2" - content-type "~1.0.4" + content-type "~1.0.5" debug "2.6.9" depd "2.0.0" destroy "1.2.0" @@ -1113,7 +1113,7 @@ body-parser@1.20.1: iconv-lite "0.4.24" on-finished "2.4.1" qs "6.11.0" - raw-body "2.5.1" + raw-body "2.5.2" type-is "~1.6.18" unpipe "1.0.0" @@ -1402,7 +1402,7 @@ content-disposition@0.5.4: dependencies: safe-buffer "5.2.1" -content-type@~1.0.4: +content-type@~1.0.4, content-type@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== @@ -1417,10 +1417,10 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" - integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== cross-env@^7.0.3: version "7.0.3" @@ -1832,16 +1832,16 @@ express-ws@^5.0.2: ws "^7.4.6" express@^4.17.1: - version "4.18.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" - integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== + version "4.19.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" + integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.1" + body-parser "1.20.2" content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.5.0" + cookie "0.6.0" cookie-signature "1.0.6" debug "2.6.9" depd "2.0.0" @@ -3242,10 +3242,10 @@ range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" - integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== dependencies: bytes "3.1.2" http-errors "2.0.0" From b0c55d1bbf56b1836a2d79509decfaaed92acfb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Apr 2024 14:47:44 +0000 Subject: [PATCH 34/36] Bump follow-redirects from 1.15.3 to 1.15.6 in /addons/addon-ligatures Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.3 to 1.15.6. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.6) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] --- addons/addon-ligatures/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/addon-ligatures/yarn.lock b/addons/addon-ligatures/yarn.lock index 966fc1135b..ac58cbe278 100644 --- a/addons/addon-ligatures/yarn.lock +++ b/addons/addon-ligatures/yarn.lock @@ -86,9 +86,9 @@ fd-slicer@~1.1.0: pend "~1.2.0" follow-redirects@^1.15.0: - version "1.15.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" - integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== font-finder@^1.0.3: version "1.0.4" From 0e77f839ff904d030319e8514af827247faadb05 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 21 Apr 2024 08:30:45 -0700 Subject: [PATCH 35/36] Use indexOf(';') approach --- src/common/InputHandler.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index c50c39606a..6db8751ea6 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -2972,14 +2972,18 @@ export class InputHandler extends Disposable implements IInputHandler { * feedback. Use `OSC 8 ; ; BEL` to finish the current hyperlink. */ public setHyperlink(data: string): boolean { - const args = data.match(/^([^;]*);(.*)$/)?.slice(1) ?? []; - if (args.length < 2) { - return false; + // Arg parsing is special cases to support unencoded semi-colons in the URIs (#4944) + const idx = data.indexOf(';'); + if (idx === -1) { + // malformed sequence, just return as handled + return true; } - if (args[1]) { - return this._createHyperlink(args[0], args[1]); + const id = data.slice(0, idx).trim(); + const uri = data.slice(idx + 1); + if (uri) { + return this._createHyperlink(id, uri); } - if (args[0].trim()) { + if (id.trim()) { return false; } return this._finishHyperlink(); From d77b41ed256f1a8410d307476a791bd1433457e7 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 21 Apr 2024 08:38:12 -0700 Subject: [PATCH 36/36] Add tests to cover OSC 8 hyperlinks --- src/common/InputHandler.test.ts | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index 8f9a988f14..0f17a3e69a 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -12,11 +12,12 @@ 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, MockCoreMouseService, MockCharsetService, MockUnicodeService, MockOscLinkService } from 'common/TestUtils.test'; -import { IBufferService, ICoreService } from 'common/services/Services'; +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'; function getCursor(bufferService: IBufferService): number[] { @@ -59,6 +60,7 @@ describe('InputHandler', () => { let bufferService: IBufferService; let coreService: ICoreService; let optionsService: MockOptionsService; + let oscLinkService: IOscLinkService; let inputHandler: TestInputHandler; beforeEach(() => { @@ -66,8 +68,9 @@ describe('InputHandler', () => { bufferService = new BufferService(optionsService); bufferService.resize(80, 30); coreService = new CoreService(bufferService, new MockLogService(), optionsService); + oscLinkService = new OscLinkService(bufferService); - inputHandler = new TestInputHandler(bufferService, new MockCharsetService(), coreService, new MockLogService(), optionsService, new MockOscLinkService(), new MockCoreMouseService(), new MockUnicodeService()); + inputHandler = new TestInputHandler(bufferService, new MockCharsetService(), coreService, new MockLogService(), optionsService, oscLinkService, new MockCoreMouseService(), new MockUnicodeService()); }); describe('SL/SR/DECIC/DECDC', () => { @@ -1982,6 +1985,32 @@ describe('InputHandler', () => { assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: 0, color: [170, 187, 204] }, { type: ColorRequestType.SET, index: 123, color: [0, 17, 34] }]]); stack.length = 0; }); + it('8: hyperlink with id', async () => { + await inputHandler.parseP('\x1b]8;id=100;http://localhost:3000\x07'); + assert.notStrictEqual(inputHandler.curAttrData.extended.urlId, 0); + assert.deepStrictEqual( + oscLinkService.getLinkData(inputHandler.curAttrData.extended.urlId), + { + id: '100', + uri: 'http://localhost:3000' + } + ); + await inputHandler.parseP('\x1b]8;;\x07'); + assert.strictEqual(inputHandler.curAttrData.extended.urlId, 0); + }); + it('8: hyperlink with semi-colon', async () => { + await inputHandler.parseP('\x1b]8;;http://localhost:3000;abc=def\x07'); + assert.notStrictEqual(inputHandler.curAttrData.extended.urlId, 0); + assert.deepStrictEqual( + oscLinkService.getLinkData(inputHandler.curAttrData.extended.urlId), + { + id: undefined, + uri: 'http://localhost:3000;abc=def' + } + ); + await inputHandler.parseP('\x1b]8;;\x07'); + assert.strictEqual(inputHandler.curAttrData.extended.urlId, 0); + }); it('104: restore events', async () => { const stack: IColorEvent[] = []; inputHandler.onColor(ev => stack.push(ev));