cleanup API

This commit is contained in:
Jörg Breitbart
2024-10-04 02:06:02 +02:00
parent b104bcd144
commit 23bef99132
4 changed files with 127 additions and 78 deletions
+2
View File
@@ -223,6 +223,8 @@ jobs:
run: yarn test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-unicode-graphemes
- name: Integration tests (addon-unicode11)
run: yarn test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-unicode11
- name: Integration tests (addon-web-fonts)
run: yarn test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-web-fonts
- name: Integration tests (addon-web-links)
run: yarn test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-web-links
- name: Integration tests (addon-webgl)
+6 -6
View File
@@ -72,7 +72,7 @@ const webFontsAddon = new WebFontsAddon();
terminal.loadAddon(webFontsAddon);
// wait for webfonts to be fully loaded
WebFontsAddon.loadFonts(['Web Mono 1', 'Super Powerline']).then(() => {
webFontsAddon.loadFonts(['Web Mono 1', 'Super Powerline']).then(() => {
terminal.open(your_terminal_div_element);
// more boostrapping goes here ...
});
@@ -83,13 +83,13 @@ on the initial document load (more precise - by the time this code runs).
Please note, that this code cannot run synchronous anymore, so you will have to split your
bootstrapping code into several stages. If thats too much of a hassle, you can also move the whole
bootstrapping under that waiting condition (`loadFonts` is actually a static method):
bootstrapping under that waiting condition (import `loadFonts` for a static variant):
```typescript
import { Terminal } from '@xterm/xterm';
import { XYAddon } from '@xterm/addon-xy';
import { WebFontsAddon } from '@xterm/addon-web-fonts';
import { WebFontsAddon, loadFonts } from '@xterm/addon-web-fonts';
WebFontsAddon.loadFonts(['Web Mono 1', 'Super Powerline']).then(() => {
loadFonts(['Web Mono 1', 'Super Powerline']).then(() => {
// create a `Terminal` instance, now with webfonts
const terminal = new Terminal({fontFamily: '"Web Mono 1", "Super Powerline", monospace'});
const xyAddon = new XYAddon();
@@ -112,7 +112,7 @@ That can be achieved like this:
const ff1 = new FontFace('New Web Mono', url1, ...);
const ff2 = new FontFace('New Web Mono', url2, ...);
// and await their loading
WebFontsAddon.loadFonts([ff1, ff2]).then(() => {
loadFonts([ff1, ff2]).then(() => {
// apply new webfont to terminal
terminal.options.fontFamily = 'New Web Mono';
// since the new font might have slighly different metrics,
@@ -124,7 +124,7 @@ WebFontsAddon.loadFonts([ff1, ff2]).then(() => {
document.styleSheets[0].insertRule(
"@font-face { font-family: 'New Web Mono'; src: url(newfont.woff); }", 0);
// and await the new font family name
WebFontsAddon.loadFonts(['New Web Mono']).then(() => {
loadFonts(['New Web Mono']).then(() => {
// apply new webfont to terminal
terminal.options.fontFamily = 'New Web Mono';
// since the new font might have slighly different metrics,
+67 -69
View File
@@ -8,7 +8,7 @@ import type { WebFontsAddon as IWebFontsApi } from '@xterm/addon-web-fonts';
/**
* Unquote family name.
* Unquote a font family name.
*/
function unquote(s: string): string {
if (s[0] === '"' && s[s.length - 1] === '"') return s.slice(1, -1);
@@ -18,7 +18,7 @@ function unquote(s: string): string {
/**
* Quote family name.
* Quote a font family name conditionally.
* @see https://mathiasbynens.be/notes/unquoted-font-family
*/
function quote(s: string): string {
@@ -40,16 +40,46 @@ function createFamily(families: string[]): string {
}
/**
* Hash a font face from it properties.
* Used in `loadFonts` to avoid bloating
* `document.fonts` from multiple calls.
*/
function hashFontFace(ff: FontFace): string {
return JSON.stringify([
unquote(ff.family),
ff.stretch,
ff.style,
ff.unicodeRange,
ff.weight
]);
}
/**
* Wait for webfont resources to be loaded.
*
* Without any argument, all fonts currently listed in
* `document.fonts` will be loaded.
* For a more fine-grained loading strategy you can populate
* the `fonts` argument with:
* - font families : loads all fontfaces in `document.fonts`
* matching the family names
* - fontface objects : loads given fontfaces and adds them to
* `document.fonts`
*
* The returned promise will resolve, when all loading is done.
*/
function _loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]> {
const ffs = Array.from(document.fonts);
if (!fonts || !fonts.length) {
return Promise.all(ffs.map(ff => ff.load()));
}
let toLoad: FontFace[] = [];
const ffsHashed = ffs.map(ff => WebFontsAddon.hashFontFace(ff));
const ffsHashed = ffs.map(ff => hashFontFace(ff));
for (const font of fonts) {
if (font instanceof FontFace) {
const fontHashed = WebFontsAddon.hashFontFace(font);
const fontHashed = hashFontFace(font);
const idx = ffsHashed.indexOf(fontHashed);
if (idx === -1) {
document.fonts.add(font);
@@ -72,85 +102,53 @@ function _loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]> {
}
export async function loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]> {
await document.fonts.ready;
return _loadFonts(fonts);
}
export class WebFontsAddon implements ITerminalAddon, IWebFontsApi {
constructor(public forceInitialRelayout: boolean = true) { }
public dispose(): void { }
private _term: Terminal | undefined;
public activate(terminal: Terminal): void {
constructor(public forceInitialRelayout: boolean = true) { }
public dispose(): void {
this._term = undefined;
}
public activate(term: Terminal): void {
this._term = term;
if (this.forceInitialRelayout) {
document.fonts.ready.then(() => this.relayout(terminal));
document.fonts.ready.then(() => this.relayout());
}
}
/**
* Force a terminal re-layout by altering `options.FontFamily`.
*
* Found webfonts in `fontFamily` are temporarily removed until the webfont
* resources are fully loaded.
*
* This method is meant as a fallback fix for sloppy integrations,
* that wrongly placed a webfont at the terminal contructor options.
* It is likely to lead to terminal flickering in all browsers (FOUT).
*
* To avoid triggering this fallback in your integration, make sure to have
* the needed webfonts loaded at the time `terminal.open` is called.
*/
public relayout(terminal: Terminal): void {
const family = terminal.options.fontFamily;
public async loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]> {
return loadFonts(fonts);
}
public async relayout(): Promise<void> {
if (!this._term) {
return;
}
await document.fonts.ready;
const family = this._term.options.fontFamily;
const families = splitFamily(family);
const webFamilies = WebFontsAddon.getFontFamilies();
const webFamilies = Array.from(new Set(Array.from(document.fonts).map(e => unquote(e.family))));
const dirty: string[] = [];
const clean: string[] = [];
for (const fam of families) {
(webFamilies.indexOf(fam) !== -1 ? dirty : clean).push(fam);
}
if (dirty.length) {
_loadFonts(dirty).then(() => {
terminal.options.fontFamily = clean.length ? createFamily(clean) : 'monospace';
terminal.options.fontFamily = family;
});
if (!dirty.length) {
return;
}
await _loadFonts(dirty);
if (this._term) {
this._term.options.fontFamily = clean.length ? createFamily(clean) : 'monospace';
this._term.options.fontFamily = family;
}
}
/**
* Hash a font face from it properties.
* Used in `loadFonts` to avoid bloating
* `document.fonts` from multiple calls.
*/
public static hashFontFace(ff: FontFace): string {
return JSON.stringify([
unquote(ff.family),
ff.stretch,
ff.style,
ff.unicodeRange,
ff.weight
]);
}
/**
* Return font families known in `document.fonts`.
*/
public static getFontFamilies(): string[] {
return Array.from(new Set(Array.from(document.fonts).map(e => unquote(e.family))));
}
/**
* Wait for webfont resources to be loaded.
*
* Without any argument, all fonts currently listed in
* `document.fonts` will be loaded.
* For a more fine-grained loading strategy you can populate
* the `fonts` argument with:
* - font families : loads all fontfaces in `document.fonts`
* matching the family names
* - fontface objects : loads given fontfaces and adds them to
* `document.fonts`
*
* The returned promise will resolve, when all loading is done.
*/
public static loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]> {
return document.fonts.ready.then(() => _loadFonts(fonts));
}
}
+52 -3
View File
@@ -4,15 +4,64 @@
*/
import { Terminal, ITerminalAddon, IViewportRange } from '@xterm/xterm';
import { Terminal, ITerminalAddon } from '@xterm/xterm';
declare module '@xterm/addon-web-fonts' {
/**
* An xterm.js addon that enables web links.
* Addon to use webfonts in xterm.js
*/
export class WebFontsAddon implements ITerminalAddon {
constructor();
/**
* @param forceInitialRelayout Force an initial relayout, if a webfont was found.
*/
constructor(forceInitialRelayout?: boolean);
public activate(terminal: Terminal): void;
public dispose(): void;
/**
* Wait for webfont resources to be loaded.
*
* Without any argument, all fonts currently listed in
* `document.fonts` will be loaded.
* For a more fine-grained loading strategy you can populate
* the `fonts` argument with:
* - font families : loads all fontfaces in `document.fonts`
* matching the family names
* - fontface objects : loads given fontfaces and adds them to
* `document.fonts`
*
* The returned promise will resolve, when all loading is done.
*/
public loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]>;
/**
* Force a terminal relayout by altering `options.FontFamily`.
*
* Found webfonts in `fontFamily` are temporarily removed until the webfont
* resources are fully loaded.
*
* Call this method, if a terminal with webfonts is stuck with broken
* glyph metrics.
*
* Returns a promise on completion.
*/
public relayout(): Promise<void>;
}
/**
* Wait for webfont resources to be loaded.
*
* Without any argument, all fonts currently listed in
* `document.fonts` will be loaded.
* For a more fine-grained loading strategy you can populate
* the `fonts` argument with:
* - font families : loads all fontfaces in `document.fonts`
* matching the family names
* - fontface objects : loads given fontfaces and adds them to
* `document.fonts`
*
* The returned promise will resolve, when all loading is done.
*/
function loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]>;
}