load unicode graphemes addon in terminals

This commit is contained in:
Jake Alti
2026-09-09 11:55:49 -04:00
parent 05f8e34943
commit 13b5b82797
8 changed files with 57 additions and 3 deletions
+12
View File
@@ -392,6 +392,9 @@ importers:
'@xterm/addon-image':
specifier: ^0.9.0
version: 0.9.0
'@xterm/addon-unicode-graphemes':
specifier: ^0.4.0
version: 0.4.0
'@xterm/addon-web-links':
specifier: ^0.12.0
version: 0.12.0
@@ -478,6 +481,9 @@ importers:
'@xterm/addon-fit':
specifier: ^0.11.0
version: 0.11.0
'@xterm/addon-unicode-graphemes':
specifier: ^0.4.0
version: 0.4.0
'@xterm/xterm':
specifier: ^6.0.0
version: 6.0.0
@@ -4194,6 +4200,7 @@ packages:
'@xmldom/[email protected]':
resolution: {integrity: sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==}
engines: {node: '>=10.0.0'}
deprecated: this version has critical issues, please update to the latest version
'@xterm/[email protected]':
resolution: {integrity: sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g==}
@@ -4204,6 +4211,9 @@ packages:
'@xterm/[email protected]':
resolution: {integrity: sha512-9OeuBFu0/uZJPu+9AHKY6g/w0Czyb/Ut0A5t79I4ULoU4IfU5BEpPFVGQxP4zTTMdfZEYkVIRYbHBX1xWwjeSA==}
'@xterm/[email protected]':
resolution: {integrity: sha512-9+/CqwbKcnlkJU4d3wIgO+wjsL8f6vyz+UwUWLu6nADQz8Gr8ONqGCJfdDjIdI+yYZLABQqQy47FzEM6AWELjw==}
'@xterm/[email protected]':
resolution: {integrity: sha512-4Smom3RPyVp7ZMYOYDoC/9eGJJJqYhnPLGGqJ6wOBfB8VxPViJNSKdgRYb8NpaM6YSelEKbA2SStD7lGyqaobw==}
@@ -11743,6 +11753,8 @@ snapshots:
'@xterm/[email protected]': {}
'@xterm/[email protected]': {}
'@xterm/[email protected]': {}
'@xterm/[email protected]': {}
+1
View File
@@ -37,6 +37,7 @@
"@opentelemetry/semantic-conventions": "^1.43.0",
"@xterm/addon-fit": "^0.11.0",
"@xterm/addon-image": "^0.9.0",
"@xterm/addon-unicode-graphemes": "^0.4.0",
"@xterm/addon-web-links": "^0.12.0",
"@xterm/addon-webgl": "^0.19.0",
"@xterm/xterm": "^6.0.0",
@@ -17,6 +17,7 @@
*/
import { ImageAddon } from '@xterm/addon-image';
import { UnicodeGraphemesAddon } from '@xterm/addon-unicode-graphemes';
import { WebLinksAddon } from '@xterm/addon-web-links';
import { WebglAddon } from '@xterm/addon-webgl';
import { ITerminalAddon, ITheme, Terminal } from '@xterm/xterm';
@@ -64,11 +65,14 @@ export class TtyPlayer extends Player<TtyEvent> {
cols: this.size.cols,
rows: this.size.rows,
theme: this.xtermTheme,
allowProposedApi: true, // required for the Unicode API
});
const linksAddon = new WebLinksAddon();
this.addons.push(this.aspectFitAddon, linksAddon);
const unicodeGraphemesAddon = new UnicodeGraphemesAddon();
this.addons.push(this.aspectFitAddon, linksAddon, unicodeGraphemesAddon);
this.aspectFitAddon.activate(this.terminal);
@@ -87,6 +87,17 @@ describe('copy blocking', () => {
});
});
// The resulting widths are asserted in teleterm's ctrl.test.ts, which can await
// a write to the terminal. This test cannot: WebglAddon throws "WebGL2 not
// supported" from a jsdom timer, failing whichever test is still running when it
// fires.
it('measures emoji with Unicode 15 width tables', () => {
const { terminal } = createTerminal();
expect(terminal.term.unicode.activeVersion).toBe('15-graphemes');
terminal.destroy();
});
function createTerminal(options: { disableCopy?: boolean } = {}) {
const el = document.createElement('div');
document.body.appendChild(el);
@@ -18,6 +18,7 @@
import { FitAddon } from '@xterm/addon-fit';
import { ImageAddon } from '@xterm/addon-image';
import { UnicodeGraphemesAddon } from '@xterm/addon-unicode-graphemes';
import { WebLinksAddon } from '@xterm/addon-web-links';
import { WebglAddon } from '@xterm/addon-webgl';
import { ITheme, Terminal } from '@xterm/xterm';
@@ -117,12 +118,13 @@ export default class TtyTerminal implements TerminalSearcher {
minimumContrastRatio: 4.5, // minimum for WCAG AA compliance
screenReaderMode: true,
theme: this.options.theme,
allowProposedApi: true, // required for customizing SearchAddon properties
allowProposedApi: true, // required for customizing SearchAddon properties and for the Unicode API
});
this.term.loadAddon(this._fitAddon);
this.term.loadAddon(this._webLinksAddon);
this.term.loadAddon(this._searchAddon);
this.term.loadAddon(new UnicodeGraphemesAddon());
// @xterm/addon-image relies on WebAssembly internally. The vite plugin guard-wasm
// rewrites bare WebAssembly references so the module can be statically imported
+1
View File
@@ -44,6 +44,7 @@
"@types/tar-fs": "^2.0.4",
"@types/which": "^3.0.4",
"@xterm/addon-fit": "^0.11.0",
"@xterm/addon-unicode-graphemes": "^0.4.0",
"@xterm/xterm": "^6.0.0",
"electron": "43.3.0",
"electron-builder": "^26.15.7",
@@ -53,6 +53,27 @@ describe('Alt+Arrow sends escape sequences for word navigation', () => {
});
});
// Without the Unicode graphemes addon, xterm.js measures these with Unicode 6
// width tables and advances the cursor by one cell, while the remote application
// advances by two, which shifts the rest of the frame.
it.each([
['⚡', 'U+26A1'],
['🤖', 'U+1F916'],
['❤️', 'U+2764 U+FE0F'],
['👨‍👩‍👧', 'ZWJ sequence'],
])('%s (%s) is two cells wide', async text => {
const { tty } = createTerminal();
await write(tty, text);
expect(tty.term.buffer.active.cursorX).toBe(2);
tty.destroy();
});
function write(tty: TtyTerminal, data: string) {
return new Promise<void>(resolve => tty.term.write(data, resolve));
}
function createTerminal() {
const el = document.createElement('div');
document.body.appendChild(el);
@@ -17,6 +17,7 @@
*/
import { FitAddon } from '@xterm/addon-fit';
import { UnicodeGraphemesAddon } from '@xterm/addon-unicode-graphemes';
import { IDisposable, ITheme, Terminal } from '@xterm/xterm';
import '@xterm/xterm/css/xterm.css';
@@ -105,7 +106,7 @@ export default class TtyTerminal implements TerminalSearcher {
windowOptions: {
setWinSizeChars: true,
},
allowProposedApi: true, // required for customizing SearchAddon properties
allowProposedApi: true, // required for customizing SearchAddon properties and for the Unicode API
});
this.term.onSelectionChange(() => {
@@ -116,6 +117,7 @@ export default class TtyTerminal implements TerminalSearcher {
this.term.loadAddon(this.fitAddon);
this.term.loadAddon(this.searchAddon);
this.term.loadAddon(new UnicodeGraphemesAddon());
this.registerResizeHandler();