From 1daf594e4bc7ab2b7cbffd6f856920ff638943d7 Mon Sep 17 00:00:00 2001 From: Labhansh Agrawal Date: Thu, 15 Jun 2023 22:40:34 +0530 Subject: [PATCH] upgrade to xterm v5 --- bin/snapshot-libs.js | 2 +- lib/components/term.tsx | 101 +++++++++++++++++++--------------------- lib/hyper.d.ts | 4 +- package.json | 15 +++--- webpack.config.ts | 2 +- yarn.lock | 65 ++++++++++++++------------ 6 files changed, 96 insertions(+), 93 deletions(-) diff --git a/bin/snapshot-libs.js b/bin/snapshot-libs.js index 40dd79b6..d1aacbfa 100644 --- a/bin/snapshot-libs.js +++ b/bin/snapshot-libs.js @@ -23,9 +23,9 @@ if (false) { require('mousetrap'); require('open'); require('xterm-addon-fit'); - require('xterm-addon-ligatures'); require('xterm-addon-search'); require('xterm-addon-web-links'); require('xterm-addon-webgl'); + require('xterm-addon-canvas'); require('xterm'); } diff --git a/lib/components/term.tsx b/lib/components/term.tsx index 57ee2917..a8a0057e 100644 --- a/lib/components/term.tsx +++ b/lib/components/term.tsx @@ -4,6 +4,7 @@ import {FitAddon} from 'xterm-addon-fit'; import {WebLinksAddon} from 'xterm-addon-web-links'; import {SearchAddon, ISearchDecorationOptions} from 'xterm-addon-search'; import {WebglAddon} from 'xterm-addon-webgl'; +import {CanvasAddon} from 'xterm-addon-canvas'; import {LigaturesAddon} from 'xterm-addon-ligatures'; import {Unicode11Addon} from 'xterm-addon-unicode11'; import {clipboard, shell} from 'electron'; @@ -12,7 +13,7 @@ import terms from '../terms'; import processClipboard from '../utils/paste'; import _SearchBox from './searchBox'; import {TermProps} from '../hyper'; -import {ObjectTypedKeys} from '../utils/object'; +import {pickBy, isEqual} from 'lodash'; import {decorate} from '../utils/plugins'; import 'xterm/css/xterm.css'; @@ -57,14 +58,13 @@ const getTermOptions = (props: TermProps): ITerminalOptions => { letterSpacing: props.letterSpacing, allowTransparency: needTransparency, macOptionClickForcesSelection: props.macOptionSelectionMode === 'force', - bellStyle: props.bell === 'SOUND' ? 'sound' : 'none', windowsMode: isWindows, theme: { foreground: props.foregroundColor, background: backgroundColor, cursor: props.cursorColor, cursorAccent: props.cursorAccentColor, - selection: props.selectionColor, + selectionBackground: props.selectionColor, black: props.colors.black, red: props.colors.red, green: props.colors.green, @@ -83,7 +83,8 @@ const getTermOptions = (props: TermProps): ITerminalOptions => { brightWhite: props.colors.lightWhite }, screenReaderMode: props.screenReaderMode, - overviewRulerWidth: 20 + overviewRulerWidth: 20, + allowProposedApi: true }; }; @@ -107,7 +108,8 @@ export default class Term extends React.PureComponent< termWrapperRef: HTMLElement | null; termOptions: ITerminalOptions; disposableListeners: IDisposable[]; - termDefaultBellSound: string | null; + defaultBellSound: HTMLAudioElement | null; + bellSound: HTMLAudioElement | null; fitAddon: FitAddon; searchAddon: SearchAddon; static rendererTypes: Record; @@ -131,7 +133,8 @@ export default class Term extends React.PureComponent< this.termWrapperRef = null; this.termOptions = {}; this.disposableListeners = []; - this.termDefaultBellSound = null; + this.defaultBellSound = null; + this.bellSound = null; this.fitAddon = new FitAddon(); this.searchAddon = new SearchAddon(); this.searchDecorations = { @@ -158,7 +161,14 @@ export default class Term extends React.PureComponent< this.termOptions = getTermOptions(props); this.term = props.term || new Terminal(this.termOptions); - this.termDefaultBellSound = this.term.getOption('bellSound'); + this.defaultBellSound = new Audio( + // Source: https://freesound.org/people/altemark/sounds/45759/ + // This sound is released under the Creative Commons Attribution 3.0 Unported + // (CC BY 3.0) license. It was created by 'altemark'. No modifications have been + // made, apart from the conversion to base64. + 'data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjMyLjEwNAAAAAAAAAAAAAAA//tQxAADB8AhSmxhIIEVCSiJrDCQBTcu3UrAIwUdkRgQbFAZC1CQEwTJ9mjRvBA4UOLD8nKVOWfh+UlK3z/177OXrfOdKl7pyn3Xf//WreyTRUoAWgBgkOAGbZHBgG1OF6zM82DWbZaUmMBptgQhGjsyYqc9ae9XFz280948NMBWInljyzsNRFLPWdnZGWrddDsjK1unuSrVN9jJsK8KuQtQCtMBjCEtImISdNKJOopIpBFpNSMbIHCSRpRR5iakjTiyzLhchUUBwCgyKiweBv/7UsQbg8isVNoMPMjAAAA0gAAABEVFGmgqK////9bP/6XCykxBTUUzLjEwMKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq' + ); + this.setBellSound(props.bell, props.bellSound); // The parent element for the terminal is attached and removed manually so // that we can preserve it across mounts and unmounts of the component @@ -186,9 +196,9 @@ export default class Term extends React.PureComponent< } Term.reportRenderer(props.uid, useWebGL ? 'WebGL' : 'Canvas'); - const shallActivateWebLink = (event: Record | undefined): boolean => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return event && (!props.webLinksActivationKey || event[`${props.webLinksActivationKey}Key`]); + const shallActivateWebLink = (event: MouseEvent): boolean => { + if (!event) return false; + return props.webLinksActivationKey ? event[`${props.webLinksActivationKey}Key`] : true; }; // eslint-disable-next-line @typescript-eslint/unbound-method @@ -196,25 +206,17 @@ export default class Term extends React.PureComponent< this.term.loadAddon(this.fitAddon); this.term.loadAddon(this.searchAddon); this.term.loadAddon( - new WebLinksAddon( - (event: MouseEvent | undefined, uri: string) => { - if (shallActivateWebLink(event)) void shell.openExternal(uri); - }, - { - // prevent default electron link handling to allow selection, e.g. via double-click - willLinkActivate: (event: MouseEvent | undefined) => { - event?.preventDefault(); - return shallActivateWebLink(event); - }, - priority: Date.now() - } - ) + new WebLinksAddon((event, uri) => { + if (shallActivateWebLink(event)) void shell.openExternal(uri); + }) ); this.term.open(this.termRef); if (useWebGL) { this.term.loadAddon(new WebglAddon()); + } else { + this.term.loadAddon(new CanvasAddon()); } - if (props.disableLigatures !== true && !useWebGL) { + if (props.disableLigatures !== true) { this.term.loadAddon(new LigaturesAddon()); } this.term.loadAddon(new Unicode11Addon()); @@ -252,6 +254,10 @@ export default class Term extends React.PureComponent< this.disposableListeners.push(this.term.onData(props.onData)); } + this.term.onBell(() => { + this.ringBell(); + }); + if (props.onResize) { this.disposableListeners.push( this.term.onResize(({cols, rows}) => { @@ -393,6 +399,18 @@ export default class Term extends React.PureComponent< return !e.catched; } + setBellSound(bell: string | null, sound: string | null) { + if (bell?.toUpperCase() === 'SOUND') { + this.bellSound = sound ? new Audio(sound) : this.defaultBellSound; + } else { + this.bellSound = null; + } + } + + ringBell() { + void this.bellSound?.play(); + } + componentDidUpdate(prevProps: TermProps) { if (!prevProps.cleared && this.props.cleared) { this.clear(); @@ -400,40 +418,19 @@ export default class Term extends React.PureComponent< const nextTermOptions = getTermOptions(this.props); - // Use bellSound in nextProps if it exists - // otherwise use the default sound found in xterm. - nextTermOptions.bellSound = this.props.bellSound || this.termDefaultBellSound!; + if (prevProps.bell !== this.props.bell || prevProps.bellSound !== this.props.bellSound) { + this.setBellSound(this.props.bell, this.props.bellSound); + } if (prevProps.search && !this.props.search) { this.closeSearchBox(); } // Update only options that have changed. - ObjectTypedKeys(nextTermOptions) - .filter((option) => option !== 'theme' && nextTermOptions[option] !== this.termOptions[option]) - .forEach((option) => { - try { - this.term.setOption(option, nextTermOptions[option]); - } catch (_e) { - const e = _e as {message: string}; - if (/The webgl renderer only works with the webgl char atlas/i.test(e.message)) { - // Ignore this because the char atlas will also be changed - } else { - throw e; - } - } - }); - - // Do we need to update theme? - const shouldUpdateTheme = - !this.termOptions.theme || - nextTermOptions.rendererType !== this.termOptions.rendererType || - ObjectTypedKeys(nextTermOptions.theme!).some( - (option) => nextTermOptions.theme![option] !== this.termOptions.theme![option] - ); - if (shouldUpdateTheme) { - this.term.setOption('theme', nextTermOptions.theme); - } + this.term.options = pickBy( + nextTermOptions, + (value, key) => !isEqual(this.termOptions[key as keyof ITerminalOptions], value) + ); this.termOptions = nextTermOptions; diff --git a/lib/hyper.d.ts b/lib/hyper.d.ts index ee69014f..dc49b52a 100644 --- a/lib/hyper.d.ts +++ b/lib/hyper.d.ts @@ -102,7 +102,7 @@ export type uiState = Immutable<{ updateReleaseUrl: string | null; updateVersion: string | null; webGLRenderer: boolean; - webLinksActivationKey: string; + webLinksActivationKey: 'ctrl' | 'alt' | 'meta' | 'shift' | ''; }>; export type session = { @@ -381,7 +381,7 @@ export type TermProps = { uiFontFamily: string; url: string | null; webGLRenderer: boolean; - webLinksActivationKey: string; + webLinksActivationKey: 'ctrl' | 'alt' | 'meta' | 'shift' | ''; ref_: (uid: string, term: Term | null) => void; } & extensionProps; diff --git a/package.json b/package.json index 69cdf8ff..f92d8d32 100644 --- a/package.json +++ b/package.json @@ -59,13 +59,14 @@ "typescript-json-schema": "0.57.0", "uuid": "9.0.0", "webpack-cli": "5.1.4", - "xterm": "4.19.0", - "xterm-addon-fit": "^0.5.0", - "xterm-addon-ligatures": "0.6.0-beta.19", - "xterm-addon-search": "^0.9.0", - "xterm-addon-unicode11": "^0.3.0", - "xterm-addon-web-links": "^0.6.0", - "xterm-addon-webgl": "0.12.0" + "xterm": "5.2.1", + "xterm-addon-canvas": "0.4.0", + "xterm-addon-fit": "0.7.0", + "xterm-addon-ligatures": "0.6.0", + "xterm-addon-search": "0.12.0", + "xterm-addon-unicode11": "0.5.0", + "xterm-addon-web-links": "0.8.0", + "xterm-addon-webgl": "0.15.0" }, "devDependencies": { "@ava/babel": "2.0.0", diff --git a/webpack.config.ts b/webpack.config.ts index 6184d889..17ff0266 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -118,10 +118,10 @@ const config: webpack.Configuration[] = [ mousetrap: 'require("./node_modules/mousetrap/mousetrap.js")', open: 'require("./node_modules/open/index.js")', 'xterm-addon-fit': 'require("./node_modules/xterm-addon-fit/lib/xterm-addon-fit.js")', - 'xterm-addon-ligatures': 'require("./node_modules/xterm-addon-ligatures/lib/xterm-addon-ligatures.js")', 'xterm-addon-search': 'require("./node_modules/xterm-addon-search/lib/xterm-addon-search.js")', 'xterm-addon-web-links': 'require("./node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js")', 'xterm-addon-webgl': 'require("./node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js")', + 'xterm-addon-canvas': 'require("./node_modules/xterm-addon-canvas/lib/xterm-addon-canvas.js")', xterm: 'require("./node_modules/xterm/lib/xterm.js")' }, plugins: [ diff --git a/yarn.lock b/yarn.lock index 7315ccaf..699f56f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7532,43 +7532,48 @@ xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.0: resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -xterm-addon-fit@^0.5.0: - version "0.5.0" - resolved "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.5.0.tgz#2d51b983b786a97dcd6cde805e700c7f913bc596" - integrity sha512-DsS9fqhXHacEmsPxBJZvfj2la30Iz9xk+UKjhQgnYNkrUIN5CYLbw7WEfz117c7+S86S/tpHPfvNxJsF5/G8wQ== +xterm-addon-canvas@0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/xterm-addon-canvas/-/xterm-addon-canvas-0.4.0.tgz#a6ee6a56deb0c495fcef29afe6d94b7119a0f334" + integrity sha512-iTC8CdjX9+hGX7jiEuiDMXzHsY/FKJdVnbjep5xjRXNu7RKOk15xuecIkJ7HZORqMVPpr4DGS3jyd9XUoBuxqw== -xterm-addon-ligatures@0.6.0-beta.19: - version "0.6.0-beta.19" - resolved "https://registry.npmjs.org/xterm-addon-ligatures/-/xterm-addon-ligatures-0.6.0-beta.19.tgz#5e43eeaf84968e014769a5f2c6a3a3601dc4771c" - integrity sha512-A0BIjFF6g5aPI0HiI2JMhhMV3gaHbpZ+ua+UNagkID0GxZ/ezn0wVOAtNQl/KlqnFueoyZIxlbyXmWNAfJVPRg== +xterm-addon-fit@0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.7.0.tgz#b8ade6d96e63b47443862088f6670b49fb752c6a" + integrity sha512-tQgHGoHqRTgeROPnvmtEJywLKoC/V9eNs4bLLz7iyJr1aW/QFzRwfd3MGiJ6odJd9xEfxcW36/xRU47JkD5NKQ== + +xterm-addon-ligatures@0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/xterm-addon-ligatures/-/xterm-addon-ligatures-0.6.0.tgz#c51801b0150c62ac1165654757b55c796457d195" + integrity sha512-DxiYCXXYEpnwr8li4/QhG64exjrLX1nHBfNNfrQgx5e8Z9tK2SjWKpxI6PZEy++8+YdL1F7VjWI4aKOaDt2VVw== dependencies: font-finder "^1.1.0" font-ligatures "^1.4.1" -xterm-addon-search@^0.9.0: - version "0.9.0" - resolved "https://registry.npmjs.org/xterm-addon-search/-/xterm-addon-search-0.9.0.tgz#95278ebb818cfcf882209ae75be96e0bea5d52a5" - integrity sha512-aoolI8YuHvdGw+Qjg8g2M4kst0v86GtB7WeBm4F0jNXA005/6QbWWy9eCsvnIDLJOFI5JSSrZnD6CaOkvBQYPA== - -xterm-addon-unicode11@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/xterm-addon-unicode11/-/xterm-addon-unicode11-0.3.0.tgz#e4435c3c91a5294a7eb8b79c380acbb28a659463" - integrity sha512-x5fHDZT2j9tlTlHnzPHt++9uKZ2kJ/lYQOj3L6xJA22xoJsS8UQRw/5YIFg2FUHqEAbV77Z1fZij/9NycMSH/A== - -xterm-addon-web-links@^0.6.0: - version "0.6.0" - resolved "https://registry.npmjs.org/xterm-addon-web-links/-/xterm-addon-web-links-0.6.0.tgz#0296cb6c99588847894670d998c9ea6a6aeb26ee" - integrity sha512-H6XzjWWZu8FBo+fnYpxdPk9w5M6drbsvwPEJZGRS38MihiQaVFpKlCMKdfRgDbKGE530tw1yH54rhpZfHgt2/A== - -xterm-addon-webgl@0.12.0: +xterm-addon-search@0.12.0: version "0.12.0" - resolved "https://registry.npmjs.org/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0.tgz#2fba8d31890a122adafa1c2fb945482e2ae12973" - integrity sha512-3P5ihdjPnxH6Wrvqjki9UD+duoVrp1fvnO/pSpXP2F1L2GwY6TDNExgj8Yg141vMCNgQbcVqmsTLYEYZxjY92A== + resolved "https://registry.npmjs.org/xterm-addon-search/-/xterm-addon-search-0.12.0.tgz#2ef8f56aecf699a3989223a1260f1e079d7c74e2" + integrity sha512-hXAuO7Ts2+Jf9K8mZrUx8IFd7c/Flgks/jyqA1L4reymyfmXtcsd+WDLel8R9Tgy2CLyKABVBP09/Ua/FmXcvg== -xterm@4.19.0: - version "4.19.0" - resolved "https://registry.npmjs.org/xterm/-/xterm-4.19.0.tgz#c0f9d09cd61de1d658f43ca75f992197add9ef6d" - integrity sha512-c3Cp4eOVsYY5Q839dR5IejghRPpxciGmLWWaP9g+ppfMeBChMeLa1DCA+pmX/jyDZ+zxFOmlJL/82qVdayVoGQ== +xterm-addon-unicode11@0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7" + integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg== + +xterm-addon-web-links@0.8.0: + version "0.8.0" + resolved "https://registry.npmjs.org/xterm-addon-web-links/-/xterm-addon-web-links-0.8.0.tgz#2cb1d57129271022569208578b0bf4774e7e6ea9" + integrity sha512-J4tKngmIu20ytX9SEJjAP3UGksah7iALqBtfTwT9ZnmFHVplCumYQsUJfKuS+JwMhjsjH61YXfndenLNvjRrEw== + +xterm-addon-webgl@0.15.0: + version "0.15.0" + resolved "https://registry.npmjs.org/xterm-addon-webgl/-/xterm-addon-webgl-0.15.0.tgz#c10f93ca619524f5a470eaac44258bab0ae8e3c7" + integrity sha512-ZLcqogMFHr4g/YRhcCh3xE8tTklnyut/M+O/XhVsFBRB/YCvYhPdLQ5/AQk54V0wjWAQpa8CF3W8DVR9OqyMCg== + +xterm@5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/xterm/-/xterm-5.2.1.tgz#b3fea7bdb55b9be1d4b31f4cd1091f26ac42afb8" + integrity sha512-cs5Y1fFevgcdoh2hJROMVIWwoBHD80P1fIP79gopLHJIE4kTzzblanoivxTiQ4+92YM9IxS36H1q0MxIJXQBcA== y18n@^5.0.5: version "5.0.5"