@xterm/addon-web-fonts 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +19 -0
- package/README.md +220 -0
- package/lib/addon-web-fonts.js +2 -0
- package/lib/addon-web-fonts.js.map +1 -0
- package/lib/addon-web-fonts.mjs +18 -0
- package/lib/addon-web-fonts.mjs.map +7 -0
- package/package.json +30 -0
- package/src/WebFontsAddon.ts +152 -0
- package/typings/addon-web-fonts.d.ts +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2024, The xterm.js authors (https://github.com/xtermjs/xterm.js)
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
|
11
|
+
all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
## @xterm/addon-web-fonts
|
|
2
|
+
|
|
3
|
+
Addon to use webfonts with [xterm.js](https://github.com/xtermjs/xterm.js).
|
|
4
|
+
This addon requires xterm.js v5+.
|
|
5
|
+
|
|
6
|
+
### Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install --save @xterm/addon-web-fonts
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
### Issue with Webfonts
|
|
13
|
+
|
|
14
|
+
Webfonts are announced by CSS `font-face` rules (or its Javascript `FontFace` counterparts).
|
|
15
|
+
Since font files tend to be quite big assets, browser engines often postpone their loading
|
|
16
|
+
to an actual styling request of a codepoint matching a font file's `unicode-range`.
|
|
17
|
+
In short - font files will not be loaded until really needed.
|
|
18
|
+
|
|
19
|
+
xterm.js on the other hand heavily relies on exact measurement of character glyphs
|
|
20
|
+
to layout its output. This is done by determining the glyph width (DOM renderer) or
|
|
21
|
+
by creating a glyph texture (WebGl renderer) for every output character.
|
|
22
|
+
For performance reasons both is done in synchronous code and cached.
|
|
23
|
+
This logic only works properly, if a font glyph is available on its first usage,
|
|
24
|
+
otherwise the browser will pick a glyph from a fallback font messing up the metrics.
|
|
25
|
+
|
|
26
|
+
For webfonts and xterm.js this means that we cannot rely on the default loading strategy
|
|
27
|
+
of the browser, but have to preload the font files before using that font in xterm.js.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Static Preloading for the Rescue?
|
|
31
|
+
|
|
32
|
+
If you dont mind higher initial loading times with a white page shown,
|
|
33
|
+
you can tell the browser to preload the needed font files by placing the following
|
|
34
|
+
link elements in the document's head above any other CSS/Javascript:
|
|
35
|
+
```html
|
|
36
|
+
<link rel="preload" as="font" href="/path/to/your/fontfile1.woff" type="font/woff2" crossorigin="anonymous">
|
|
37
|
+
<link rel="preload" as="font" href="/path/to/your/fontfile2.woff" type="font/woff2" crossorigin="anonymous">
|
|
38
|
+
...
|
|
39
|
+
<!-- CSS with font-face rules matching the URLs above -->
|
|
40
|
+
```
|
|
41
|
+
Browsers also will resort to system fonts, if the preloading takes too long.
|
|
42
|
+
So this solution has only a very limited scope.
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
### Loading with WebFontsAddon
|
|
46
|
+
|
|
47
|
+
The webfonts addon offers several ways to deal with font assets loading
|
|
48
|
+
without leaving the terminal in an unusable state.
|
|
49
|
+
|
|
50
|
+
Recap - normally boostrapping of a new terminal involves these basic steps (Typescript):
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { Terminal } from '@xterm/xterm';
|
|
54
|
+
import { XYAddon } from '@xterm/addon-xy';
|
|
55
|
+
|
|
56
|
+
// create a `Terminal` instance with some options, e.g. a custom font family
|
|
57
|
+
const terminal = new Terminal({fontFamily: 'monospace'});
|
|
58
|
+
|
|
59
|
+
// create and load all addons you want to use, e.g. fit addon
|
|
60
|
+
const xyInstance = new XYAddon();
|
|
61
|
+
terminal.loadAddon(xyInstance);
|
|
62
|
+
|
|
63
|
+
// finally: call `open` of the terminal instance
|
|
64
|
+
terminal.open(your_terminal_div_element); // <-- critical path for webfonts
|
|
65
|
+
// more boostrapping goes here ...
|
|
66
|
+
```
|
|
67
|
+
This code is guaranteed to work in all browsers synchronously, as the identifier `monospace`
|
|
68
|
+
will always be available. It will also work synchronously with any installed system font,
|
|
69
|
+
but breaks horribly with webfonts. The actual culprit here is the call to `terminal.open`,
|
|
70
|
+
which attaches the terminal to the DOM and starts the renderer with all the glyph caching
|
|
71
|
+
mentioned above, while the webfont is not yet fully available.
|
|
72
|
+
|
|
73
|
+
To fix that, the webfonts addon provides a waiting condition (Typescript):
|
|
74
|
+
```typescript
|
|
75
|
+
import { Terminal } from '@xterm/xterm';
|
|
76
|
+
import { XYAddon } from '@xterm/addon-xy';
|
|
77
|
+
import { WebFontsAddon } from '@xterm/addon-web-fonts';
|
|
78
|
+
|
|
79
|
+
// create a `Terminal` instance, now with webfonts
|
|
80
|
+
const terminal = new Terminal({fontFamily: '"Web Mono 1", "Super Powerline", monospace'});
|
|
81
|
+
const xyInstance = new XYAddon();
|
|
82
|
+
terminal.loadAddon(xyInstance);
|
|
83
|
+
|
|
84
|
+
const webFontsInstance = new WebFontsAddon();
|
|
85
|
+
terminal.loadAddon(webFontsInstance);
|
|
86
|
+
|
|
87
|
+
// wait for webfonts to be fully loaded
|
|
88
|
+
webFontsInstance.loadFonts(['Web Mono 1', 'Super Powerline']).then(() => {
|
|
89
|
+
terminal.open(your_terminal_div_element);
|
|
90
|
+
// more boostrapping goes here ...
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
Here `loadFonts` will look up the font face objects in `document.fonts`
|
|
94
|
+
and load them before continuing. For this to work, you have to make sure,
|
|
95
|
+
that the CSS `font-face` rules for these webfonts are loaded beforehand,
|
|
96
|
+
otherwise `loadFonts` will not find the font family names (promise will be
|
|
97
|
+
rejected for missing font family names).
|
|
98
|
+
|
|
99
|
+
Please note, that this cannot run synchronous anymore, so you will have to split your
|
|
100
|
+
bootstrapping code into several stages. If that is too much of a hassle,
|
|
101
|
+
you can also move the whole bootstrapping under the waiting condition by using
|
|
102
|
+
the static loader instead (Typescript):
|
|
103
|
+
```typescript
|
|
104
|
+
import { Terminal } from '@xterm/xterm';
|
|
105
|
+
import { XYAddon } from '@xterm/addon-xy';
|
|
106
|
+
// import static loader
|
|
107
|
+
import { loadFonts } from '@xterm/addon-web-fonts';
|
|
108
|
+
|
|
109
|
+
loadFonts(['Web Mono 1', 'Super Powerline']).then(() => {
|
|
110
|
+
// create a `Terminal` instance, now with webfonts
|
|
111
|
+
const terminal = new Terminal({fontFamily: '"Web Mono 1", "Super Powerline", monospace'});
|
|
112
|
+
const xyInstance = new XYAddon();
|
|
113
|
+
terminal.loadAddon(xyInstance);
|
|
114
|
+
|
|
115
|
+
// optional when using static loader
|
|
116
|
+
const webfontsInstance = new WebFontsAddon();
|
|
117
|
+
terminal.loadAddon(webfontsInstance);
|
|
118
|
+
|
|
119
|
+
terminal.open(your_terminal_div_element);
|
|
120
|
+
// more boostrapping goes here ...
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
With the static loader creating and loading of the actual addon can be omitted,
|
|
124
|
+
as fonts are already loaded before any terminal setup happens.
|
|
125
|
+
|
|
126
|
+
### Webfont Loading at Runtime
|
|
127
|
+
|
|
128
|
+
Given you have a terminal already running and want to change the font family
|
|
129
|
+
to a different not yet loaded webfont:
|
|
130
|
+
```typescript
|
|
131
|
+
// either create font face objects in javascript
|
|
132
|
+
const ff1 = new FontFace('New Web Mono', url1, ...);
|
|
133
|
+
const ff2 = new FontFace('New Web Mono', url2, ...);
|
|
134
|
+
// and await their loading
|
|
135
|
+
loadFonts([ff1, ff2]).then(() => {
|
|
136
|
+
// apply new webfont to terminal
|
|
137
|
+
terminal.options.fontFamily = 'New Web Mono';
|
|
138
|
+
// since the new font might have slighly different metrics,
|
|
139
|
+
// also run the fit addon here (or any other custom resize logic)
|
|
140
|
+
fitAddon.fit();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// or alternatively use CSS to add new font-face rules, e.g.
|
|
144
|
+
document.styleSheets[0].insertRule(
|
|
145
|
+
"@font-face { font-family: 'New Web Mono'; src: url(newfont.woff); }", 0);
|
|
146
|
+
// and await the new font family name
|
|
147
|
+
loadFonts(['New Web Mono']).then(() => {
|
|
148
|
+
// apply new webfont to terminal
|
|
149
|
+
terminal.options.fontFamily = 'New Web Mono';
|
|
150
|
+
// since the new font might have slighly different metrics,
|
|
151
|
+
// also run the fit addon here (or any other custom resize logic)
|
|
152
|
+
fitAddon.fit();
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Forced Layout Update
|
|
157
|
+
|
|
158
|
+
If you have the addon loaded into your terminal, you can force the terminal to update
|
|
159
|
+
the layout with the method `WebFontsAddon.relayout`. This might come handy,
|
|
160
|
+
if the terminal shows webfont related output issue for unknown reasons:
|
|
161
|
+
```typescript
|
|
162
|
+
...
|
|
163
|
+
// given - terminal shows weird font issues, run:
|
|
164
|
+
webFontsInstance.relayout().then(() => {
|
|
165
|
+
// also run resize logic here, e.g. fit addon
|
|
166
|
+
fitAddon.fit();
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
Note that this method is only meant as a quickfix on a running terminal to keep it
|
|
170
|
+
in a working condition. A production-ready integration should never rely on it,
|
|
171
|
+
better fix the real root cause (most likely not properly awaiting the font loader
|
|
172
|
+
higher up in the code).
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
### Webfonts from Fontsource
|
|
176
|
+
|
|
177
|
+
The addon has been tested to work with webfonts from fontsource.
|
|
178
|
+
Javascript example for `vite` with ESM import:
|
|
179
|
+
```javascript
|
|
180
|
+
import { Terminal } from '@xterm/xterm';
|
|
181
|
+
import { FitAddon } from '@xterm/addon-fit';
|
|
182
|
+
import { loadFonts } from '@xterm/addon-web-fonts';
|
|
183
|
+
import '@xterm/xterm/css/xterm.css';
|
|
184
|
+
import '@fontsource/roboto-mono';
|
|
185
|
+
import '@fontsource/roboto-mono/400.css';
|
|
186
|
+
import '@fontsource/roboto-mono/400-italic.css';
|
|
187
|
+
import '@fontsource/roboto-mono/700.css';
|
|
188
|
+
import '@fontsource/roboto-mono/700-italic.css';
|
|
189
|
+
|
|
190
|
+
async function main() {
|
|
191
|
+
let fontFamily = '"Roboto Mono", monospace';
|
|
192
|
+
try {
|
|
193
|
+
await loadFonts(['Roboto Mono']);
|
|
194
|
+
} catch (e) {
|
|
195
|
+
fontFamily = 'monospace';
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const terminal = new Terminal({ fontFamily });
|
|
199
|
+
const fitAddon = new FitAddon();
|
|
200
|
+
terminal.loadAddon(fitAddon);
|
|
201
|
+
terminal.open(document.getElementById('your-xterm-container-div'));
|
|
202
|
+
fitAddon.fit();
|
|
203
|
+
|
|
204
|
+
// sync writing shows up in Roboto Mono w'o FOUT
|
|
205
|
+
// and a fallback to monospace
|
|
206
|
+
terminal.write('put any unicode char here');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
main();
|
|
210
|
+
```
|
|
211
|
+
The fontsource packages download the font files to your project folder to be delivered
|
|
212
|
+
from there later on. For security sensitive projects this should be the preferred way,
|
|
213
|
+
as it brings the font files under your control.
|
|
214
|
+
|
|
215
|
+
The example furthermore contains proper exception handling with a fallback
|
|
216
|
+
(skipped in all other examples for better readability).
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
Also see the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/addon-web-fonts/typings/addon-web-fonts.d.ts).
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.WebFontsAddon=e():t.WebFontsAddon=e()}(globalThis,()=>(()=>{"use strict";var t={};return(()=>{var e=t;function n(t){return'"'===t[0]&&'"'===t[t.length-1]||"'"===t[0]&&"'"===t[t.length-1]?t.slice(1,-1):t}function o(t){const e=t.match(/([-_a-zA-Z0-9\xA0-\u{10FFFF}]+)/u);return!t.match(/^(-?\d|--)/m)&&e&&e[1]===t?t:`"${t.replace(/"/g,'\\"')}"`}function r(t){return JSON.stringify([n(t.family),t.stretch,t.style,t.unicodeRange,t.weight])}function i(t){const e=Array.from(document.fonts);if(!t||!t.length)return Promise.all(e.map(t=>t.load()));let o=[];const i=e.map(t=>r(t));for(const s of t)if(s instanceof FontFace){const t=r(s),n=i.indexOf(t);-1===n?(document.fonts.add(s),e.push(s),i.push(t),o.push(s)):o.push(e[n])}else{const t=e.filter(t=>s===n(t.family));if(o=o.concat(t),!t.length)return Promise.reject(`font family "${s}" not registered in document.fonts`)}return Promise.all(o.map(t=>t.load()))}function s(t){return document.fonts.ready.then(()=>i(t))}Object.defineProperty(e,"__esModule",{value:!0}),e.WebFontsAddon=void 0,e.loadFonts=s,e.WebFontsAddon=class{constructor(t=!0){this.initialRelayout=t}dispose(){this._term=void 0}activate(t){this._term=t,this.initialRelayout&&document.fonts.ready.then(()=>this.relayout())}loadFonts(t){return s(t)}async relayout(){if(!this._term)return;await document.fonts.ready;const t=this._term.options.fontFamily,e=function(t){return t?t.split(",").map(t=>n(t.trim())):[]}(t),r=Array.from(new Set(Array.from(document.fonts).map(t=>n(t.family)))),s=[],a=[];for(const t of e)(-1!==r.indexOf(t)?s:a).push(t);s.length&&(await i(s),this._term&&(this._term.options.fontFamily=a.length?function(t){return t.map(o).join(", ")}(a):"monospace",this._term.options.fontFamily=t))}}})(),t})());
|
|
2
|
+
//# sourceMappingURL=addon-web-fonts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"addon-web-fonts.js","mappings":"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","sources":["webpack://WebFontsAddon/webpack/universalModuleDefinition","webpack://WebFontsAddon/./src/WebFontsAddon.ts"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"WebFontsAddon\"] = factory();\n\telse\n\t\troot[\"WebFontsAddon\"] = factory();\n})(globalThis, () => {\nreturn ","/**\n * Copyright (c) 2024 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport type { Terminal, ITerminalAddon } from '@xterm/xterm';\nimport type { WebFontsAddon as IWebFontsApi } from '@xterm/addon-web-fonts';\n\n\n/**\n * Unquote a font family name.\n */\nfunction unquote(s: string): string {\n if (s[0] === '\"' && s[s.length - 1] === '\"') return s.slice(1, -1);\n if (s[0] === '\\'' && s[s.length - 1] === '\\'') return s.slice(1, -1);\n return s;\n}\n\n\n/**\n * Quote a font family name conditionally.\n * @see https://mathiasbynens.be/notes/unquoted-font-family\n */\nfunction quote(s: string): string {\n const pos = s.match(/([-_a-zA-Z0-9\\xA0-\\u{10FFFF}]+)/u);\n const neg = s.match(/^(-?\\d|--)/m);\n if (!neg && pos && pos[1] === s) return s;\n return `\"${s.replace(/\"/g, '\\\\\"')}\"`;\n}\n\n\nfunction splitFamily(family: string | undefined): string[] {\n if (!family) return [];\n return family.split(',').map(e => unquote(e.trim()));\n}\n\n\nfunction createFamily(families: string[]): string {\n return families.map(quote).join(', ');\n}\n\n\n/**\n * Hash a font face from it properties.\n * Used in `loadFonts` to avoid bloating\n * `document.fonts` from multiple calls.\n */\nfunction hashFontFace(ff: FontFace): string {\n return JSON.stringify([\n unquote(ff.family),\n ff.stretch,\n ff.style,\n ff.unicodeRange,\n ff.weight\n ]);\n}\n\n\n/**\n * Wait for webfont resources to be loaded.\n *\n * Without any argument, all fonts currently listed in\n * `document.fonts` will be loaded.\n * For a more fine-grained loading strategy you can populate\n * the `fonts` argument with:\n * - font families : loads all fontfaces in `document.fonts`\n * matching the family names\n * - fontface objects : loads given fontfaces and adds them to\n * `document.fonts`\n *\n * The returned promise will resolve, when all loading is done.\n */\nfunction _loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]> {\n const ffs = Array.from(document.fonts);\n if (!fonts || !fonts.length) {\n return Promise.all(ffs.map(ff => ff.load()));\n }\n let toLoad: FontFace[] = [];\n const ffsHashed = ffs.map(ff => hashFontFace(ff));\n for (const font of fonts) {\n if (font instanceof FontFace) {\n const fontHashed = hashFontFace(font);\n const idx = ffsHashed.indexOf(fontHashed);\n if (idx === -1) {\n document.fonts.add(font);\n ffs.push(font);\n ffsHashed.push(fontHashed);\n toLoad.push(font);\n } else {\n toLoad.push(ffs[idx]);\n }\n } else {\n // string as font\n const familyFiltered = ffs.filter(ff => font === unquote(ff.family));\n toLoad = toLoad.concat(familyFiltered);\n if (!familyFiltered.length) {\n return Promise.reject(`font family \"${font}\" not registered in document.fonts`);\n }\n }\n }\n return Promise.all(toLoad.map(ff => ff.load()));\n}\n\n\nexport function loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]> {\n return document.fonts.ready.then(() => _loadFonts(fonts));\n}\n\n\nexport class WebFontsAddon implements ITerminalAddon, IWebFontsApi {\n private _term: Terminal | undefined;\n\n constructor(public initialRelayout: boolean = true) { }\n\n public dispose(): void {\n this._term = undefined;\n }\n\n public activate(term: Terminal): void {\n this._term = term;\n if (this.initialRelayout) {\n document.fonts.ready.then(() => this.relayout());\n }\n }\n\n public loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]> {\n return loadFonts(fonts);\n }\n\n public async relayout(): Promise<void> {\n if (!this._term) {\n return;\n }\n await document.fonts.ready;\n const family = this._term.options.fontFamily;\n const families = splitFamily(family);\n const webFamilies = Array.from(new Set(Array.from(document.fonts).map(e => unquote(e.family))));\n const dirty: string[] = [];\n const clean: string[] = [];\n for (const fam of families) {\n (webFamilies.indexOf(fam) !== -1 ? dirty : clean).push(fam);\n }\n if (!dirty.length) {\n return;\n }\n await _loadFonts(dirty);\n if (this._term) {\n this._term.options.fontFamily = clean.length ? createFamily(clean) : 'monospace';\n this._term.options.fontFamily = family;\n }\n }\n}\n"],"names":["root","factory","exports","module","define","amd","globalThis","unquote","s","length","slice","quote","pos","match","replace","hashFontFace","ff","JSON","stringify","family","stretch","style","unicodeRange","weight","_loadFonts","fonts","ffs","Array","from","document","Promise","all","map","load","toLoad","ffsHashed","font","FontFace","fontHashed","idx","indexOf","add","push","familyFiltered","filter","concat","reject","loadFonts","ready","then","constructor","initialRelayout","dispose","this","_term","undefined","activate","term","relayout","options","fontFamily","families","split","e","trim","splitFamily","webFamilies","Set","dirty","clean","fam","join","createFamily"],"ignoreList":[],"sourceRoot":""}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2014-2024 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
|
6
|
+
* @license MIT
|
|
7
|
+
*
|
|
8
|
+
* Originally forked from (with the author's permission):
|
|
9
|
+
* Fabrice Bellard's javascript vt100 for jslinux:
|
|
10
|
+
* http://bellard.org/jslinux/
|
|
11
|
+
* Copyright (c) 2011 Fabrice Bellard
|
|
12
|
+
*/
|
|
13
|
+
/*---------------------------------------------------------------------------------------------
|
|
14
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
15
|
+
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
16
|
+
*--------------------------------------------------------------------------------------------*/
|
|
17
|
+
function s(t){return t[0]==='"'&&t[t.length-1]==='"'||t[0]==="'"&&t[t.length-1]==="'"?t.slice(1,-1):t}function u(t){let n=t.match(/([-_a-zA-Z0-9\xA0-\u{10FFFF}]+)/u);return!t.match(/^(-?\d|--)/m)&&n&&n[1]===t?t:`"${t.replace(/"/g,'\\"')}"`}function f(t){return t?t.split(",").map(n=>s(n.trim())):[]}function d(t){return t.map(u).join(", ")}function m(t){return JSON.stringify([s(t.family),t.stretch,t.style,t.unicodeRange,t.weight])}function l(t){let n=Array.from(document.fonts);if(!t||!t.length)return Promise.all(n.map(e=>e.load()));let r=[],a=n.map(e=>m(e));for(let e of t)if(e instanceof FontFace){let i=m(e),o=a.indexOf(i);o===-1?(document.fonts.add(e),n.push(e),a.push(i),r.push(e)):r.push(n[o])}else{let i=n.filter(o=>e===s(o.family));if(r=r.concat(i),!i.length)return Promise.reject(`font family "${e}" not registered in document.fonts`)}return Promise.all(r.map(e=>e.load()))}function F(t){return document.fonts.ready.then(()=>l(t))}var c=class{constructor(n=!0){this.initialRelayout=n}dispose(){this._term=void 0}activate(n){this._term=n,this.initialRelayout&&document.fonts.ready.then(()=>this.relayout())}loadFonts(n){return F(n)}async relayout(){if(!this._term)return;await document.fonts.ready;let n=this._term.options.fontFamily,r=f(n),a=Array.from(new Set(Array.from(document.fonts).map(o=>s(o.family)))),e=[],i=[];for(let o of r)(a.indexOf(o)!==-1?e:i).push(o);e.length&&(await l(e),this._term&&(this._term.options.fontFamily=i.length?d(i):"monospace",this._term.options.fontFamily=n))}};export{c as WebFontsAddon,F as loadFonts};
|
|
18
|
+
//# sourceMappingURL=addon-web-fonts.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/WebFontsAddon.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2024 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport type { Terminal, ITerminalAddon } from '@xterm/xterm';\nimport type { WebFontsAddon as IWebFontsApi } from '@xterm/addon-web-fonts';\n\n\n/**\n * Unquote a font family name.\n */\nfunction unquote(s: string): string {\n if (s[0] === '\"' && s[s.length - 1] === '\"') return s.slice(1, -1);\n if (s[0] === '\\'' && s[s.length - 1] === '\\'') return s.slice(1, -1);\n return s;\n}\n\n\n/**\n * Quote a font family name conditionally.\n * @see https://mathiasbynens.be/notes/unquoted-font-family\n */\nfunction quote(s: string): string {\n const pos = s.match(/([-_a-zA-Z0-9\\xA0-\\u{10FFFF}]+)/u);\n const neg = s.match(/^(-?\\d|--)/m);\n if (!neg && pos && pos[1] === s) return s;\n return `\"${s.replace(/\"/g, '\\\\\"')}\"`;\n}\n\n\nfunction splitFamily(family: string | undefined): string[] {\n if (!family) return [];\n return family.split(',').map(e => unquote(e.trim()));\n}\n\n\nfunction createFamily(families: string[]): string {\n return families.map(quote).join(', ');\n}\n\n\n/**\n * Hash a font face from it properties.\n * Used in `loadFonts` to avoid bloating\n * `document.fonts` from multiple calls.\n */\nfunction hashFontFace(ff: FontFace): string {\n return JSON.stringify([\n unquote(ff.family),\n ff.stretch,\n ff.style,\n ff.unicodeRange,\n ff.weight\n ]);\n}\n\n\n/**\n * Wait for webfont resources to be loaded.\n *\n * Without any argument, all fonts currently listed in\n * `document.fonts` will be loaded.\n * For a more fine-grained loading strategy you can populate\n * the `fonts` argument with:\n * - font families : loads all fontfaces in `document.fonts`\n * matching the family names\n * - fontface objects : loads given fontfaces and adds them to\n * `document.fonts`\n *\n * The returned promise will resolve, when all loading is done.\n */\nfunction _loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]> {\n const ffs = Array.from(document.fonts);\n if (!fonts || !fonts.length) {\n return Promise.all(ffs.map(ff => ff.load()));\n }\n let toLoad: FontFace[] = [];\n const ffsHashed = ffs.map(ff => hashFontFace(ff));\n for (const font of fonts) {\n if (font instanceof FontFace) {\n const fontHashed = hashFontFace(font);\n const idx = ffsHashed.indexOf(fontHashed);\n if (idx === -1) {\n document.fonts.add(font);\n ffs.push(font);\n ffsHashed.push(fontHashed);\n toLoad.push(font);\n } else {\n toLoad.push(ffs[idx]);\n }\n } else {\n // string as font\n const familyFiltered = ffs.filter(ff => font === unquote(ff.family));\n toLoad = toLoad.concat(familyFiltered);\n if (!familyFiltered.length) {\n return Promise.reject(`font family \"${font}\" not registered in document.fonts`);\n }\n }\n }\n return Promise.all(toLoad.map(ff => ff.load()));\n}\n\n\nexport function loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]> {\n return document.fonts.ready.then(() => _loadFonts(fonts));\n}\n\n\nexport class WebFontsAddon implements ITerminalAddon, IWebFontsApi {\n private _term: Terminal | undefined;\n\n constructor(public initialRelayout: boolean = true) { }\n\n public dispose(): void {\n this._term = undefined;\n }\n\n public activate(term: Terminal): void {\n this._term = term;\n if (this.initialRelayout) {\n document.fonts.ready.then(() => this.relayout());\n }\n }\n\n public loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]> {\n return loadFonts(fonts);\n }\n\n public async relayout(): Promise<void> {\n if (!this._term) {\n return;\n }\n await document.fonts.ready;\n const family = this._term.options.fontFamily;\n const families = splitFamily(family);\n const webFamilies = Array.from(new Set(Array.from(document.fonts).map(e => unquote(e.family))));\n const dirty: string[] = [];\n const clean: string[] = [];\n for (const fam of families) {\n (webFamilies.indexOf(fam) !== -1 ? dirty : clean).push(fam);\n }\n if (!dirty.length) {\n return;\n }\n await _loadFonts(dirty);\n if (this._term) {\n this._term.options.fontFamily = clean.length ? createFamily(clean) : 'monospace';\n this._term.options.fontFamily = family;\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["unquote", "s", "quote", "pos", "splitFamily", "family", "e", "createFamily", "families", "hashFontFace", "ff", "_loadFonts", "fonts", "ffs", "toLoad", "ffsHashed", "font", "fontHashed", "idx", "familyFiltered", "loadFonts", "WebFontsAddon", "initialRelayout", "term", "webFamilies", "dirty", "clean", "fam"]
|
|
7
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xterm/addon-web-fonts",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"author": {
|
|
5
|
+
"name": "The xterm.js authors",
|
|
6
|
+
"url": "https://xtermjs.org/"
|
|
7
|
+
},
|
|
8
|
+
"main": "lib/addon-web-fonts.js",
|
|
9
|
+
"module": "lib/addon-web-fonts.mjs",
|
|
10
|
+
"types": "typings/addon-web-fonts.d.ts",
|
|
11
|
+
"repository": "https://github.com/xtermjs/xterm.js/tree/master/addons/addon-web-fonts",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"terminal",
|
|
15
|
+
"xterm",
|
|
16
|
+
"xterm.js"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "../../node_modules/.bin/tsc -p .",
|
|
20
|
+
"prepackage": "npm run build",
|
|
21
|
+
"package": "../../node_modules/.bin/webpack",
|
|
22
|
+
"prepublishOnly": "npm run package",
|
|
23
|
+
"start": "node ../../demo/start"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {},
|
|
26
|
+
"commit": "e1bfc7dd1f379dee9e2dc4cafc6b53efbff4154f",
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@xterm/xterm": "^6.1.0-beta.86"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2024 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Terminal, ITerminalAddon } from '@xterm/xterm';
|
|
7
|
+
import type { WebFontsAddon as IWebFontsApi } from '@xterm/addon-web-fonts';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Unquote a font family name.
|
|
12
|
+
*/
|
|
13
|
+
function unquote(s: string): string {
|
|
14
|
+
if (s[0] === '"' && s[s.length - 1] === '"') return s.slice(1, -1);
|
|
15
|
+
if (s[0] === '\'' && s[s.length - 1] === '\'') return s.slice(1, -1);
|
|
16
|
+
return s;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Quote a font family name conditionally.
|
|
22
|
+
* @see https://mathiasbynens.be/notes/unquoted-font-family
|
|
23
|
+
*/
|
|
24
|
+
function quote(s: string): string {
|
|
25
|
+
const pos = s.match(/([-_a-zA-Z0-9\xA0-\u{10FFFF}]+)/u);
|
|
26
|
+
const neg = s.match(/^(-?\d|--)/m);
|
|
27
|
+
if (!neg && pos && pos[1] === s) return s;
|
|
28
|
+
return `"${s.replace(/"/g, '\\"')}"`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
function splitFamily(family: string | undefined): string[] {
|
|
33
|
+
if (!family) return [];
|
|
34
|
+
return family.split(',').map(e => unquote(e.trim()));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
function createFamily(families: string[]): string {
|
|
39
|
+
return families.map(quote).join(', ');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Hash a font face from it properties.
|
|
45
|
+
* Used in `loadFonts` to avoid bloating
|
|
46
|
+
* `document.fonts` from multiple calls.
|
|
47
|
+
*/
|
|
48
|
+
function hashFontFace(ff: FontFace): string {
|
|
49
|
+
return JSON.stringify([
|
|
50
|
+
unquote(ff.family),
|
|
51
|
+
ff.stretch,
|
|
52
|
+
ff.style,
|
|
53
|
+
ff.unicodeRange,
|
|
54
|
+
ff.weight
|
|
55
|
+
]);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Wait for webfont resources to be loaded.
|
|
61
|
+
*
|
|
62
|
+
* Without any argument, all fonts currently listed in
|
|
63
|
+
* `document.fonts` will be loaded.
|
|
64
|
+
* For a more fine-grained loading strategy you can populate
|
|
65
|
+
* the `fonts` argument with:
|
|
66
|
+
* - font families : loads all fontfaces in `document.fonts`
|
|
67
|
+
* matching the family names
|
|
68
|
+
* - fontface objects : loads given fontfaces and adds them to
|
|
69
|
+
* `document.fonts`
|
|
70
|
+
*
|
|
71
|
+
* The returned promise will resolve, when all loading is done.
|
|
72
|
+
*/
|
|
73
|
+
function _loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]> {
|
|
74
|
+
const ffs = Array.from(document.fonts);
|
|
75
|
+
if (!fonts || !fonts.length) {
|
|
76
|
+
return Promise.all(ffs.map(ff => ff.load()));
|
|
77
|
+
}
|
|
78
|
+
let toLoad: FontFace[] = [];
|
|
79
|
+
const ffsHashed = ffs.map(ff => hashFontFace(ff));
|
|
80
|
+
for (const font of fonts) {
|
|
81
|
+
if (font instanceof FontFace) {
|
|
82
|
+
const fontHashed = hashFontFace(font);
|
|
83
|
+
const idx = ffsHashed.indexOf(fontHashed);
|
|
84
|
+
if (idx === -1) {
|
|
85
|
+
document.fonts.add(font);
|
|
86
|
+
ffs.push(font);
|
|
87
|
+
ffsHashed.push(fontHashed);
|
|
88
|
+
toLoad.push(font);
|
|
89
|
+
} else {
|
|
90
|
+
toLoad.push(ffs[idx]);
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
// string as font
|
|
94
|
+
const familyFiltered = ffs.filter(ff => font === unquote(ff.family));
|
|
95
|
+
toLoad = toLoad.concat(familyFiltered);
|
|
96
|
+
if (!familyFiltered.length) {
|
|
97
|
+
return Promise.reject(`font family "${font}" not registered in document.fonts`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return Promise.all(toLoad.map(ff => ff.load()));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
export function loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]> {
|
|
106
|
+
return document.fonts.ready.then(() => _loadFonts(fonts));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
export class WebFontsAddon implements ITerminalAddon, IWebFontsApi {
|
|
111
|
+
private _term: Terminal | undefined;
|
|
112
|
+
|
|
113
|
+
constructor(public initialRelayout: boolean = true) { }
|
|
114
|
+
|
|
115
|
+
public dispose(): void {
|
|
116
|
+
this._term = undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public activate(term: Terminal): void {
|
|
120
|
+
this._term = term;
|
|
121
|
+
if (this.initialRelayout) {
|
|
122
|
+
document.fonts.ready.then(() => this.relayout());
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]> {
|
|
127
|
+
return loadFonts(fonts);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public async relayout(): Promise<void> {
|
|
131
|
+
if (!this._term) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
await document.fonts.ready;
|
|
135
|
+
const family = this._term.options.fontFamily;
|
|
136
|
+
const families = splitFamily(family);
|
|
137
|
+
const webFamilies = Array.from(new Set(Array.from(document.fonts).map(e => unquote(e.family))));
|
|
138
|
+
const dirty: string[] = [];
|
|
139
|
+
const clean: string[] = [];
|
|
140
|
+
for (const fam of families) {
|
|
141
|
+
(webFamilies.indexOf(fam) !== -1 ? dirty : clean).push(fam);
|
|
142
|
+
}
|
|
143
|
+
if (!dirty.length) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
await _loadFonts(dirty);
|
|
147
|
+
if (this._term) {
|
|
148
|
+
this._term.options.fontFamily = clean.length ? createFamily(clean) : 'monospace';
|
|
149
|
+
this._term.options.fontFamily = family;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2024 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
import { Terminal, ITerminalAddon } from '@xterm/xterm';
|
|
8
|
+
|
|
9
|
+
declare module '@xterm/addon-web-fonts' {
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Addon to use webfonts in xterm.js
|
|
13
|
+
*/
|
|
14
|
+
export class WebFontsAddon implements ITerminalAddon {
|
|
15
|
+
/**
|
|
16
|
+
* @param initialRelayout Force initial relayout, if a webfont was found (default true).
|
|
17
|
+
*/
|
|
18
|
+
constructor(initialRelayout?: boolean);
|
|
19
|
+
public activate(terminal: Terminal): void;
|
|
20
|
+
public dispose(): void;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Wait for webfont resources to be loaded.
|
|
24
|
+
*
|
|
25
|
+
* Without any argument, all fonts currently listed in
|
|
26
|
+
* `document.fonts` will be loaded.
|
|
27
|
+
* For a more fine-grained loading strategy you can populate
|
|
28
|
+
* the `fonts` argument with:
|
|
29
|
+
* - font families : loads all fontfaces in `document.fonts`
|
|
30
|
+
* matching the family names
|
|
31
|
+
* - fontface objects : loads given fontfaces and adds them to
|
|
32
|
+
* `document.fonts`
|
|
33
|
+
*
|
|
34
|
+
* The returned promise will resolve, when all loading is done.
|
|
35
|
+
*/
|
|
36
|
+
public loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Force a terminal relayout by altering `options.FontFamily`.
|
|
40
|
+
*
|
|
41
|
+
* Found webfonts in `fontFamily` are temporarily removed until the webfont
|
|
42
|
+
* resources are fully loaded.
|
|
43
|
+
*
|
|
44
|
+
* Call this method, if a terminal with webfonts is stuck with broken
|
|
45
|
+
* glyph metrics.
|
|
46
|
+
*
|
|
47
|
+
* The returned promise will resolve, when font loading and layouting are done.
|
|
48
|
+
*/
|
|
49
|
+
public relayout(): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Wait for webfont resources to be loaded.
|
|
54
|
+
*
|
|
55
|
+
* Without any argument, all fonts currently listed in
|
|
56
|
+
* `document.fonts` will be loaded.
|
|
57
|
+
* For a more fine-grained loading strategy you can populate
|
|
58
|
+
* the `fonts` argument with:
|
|
59
|
+
* - font families : loads all fontfaces in `document.fonts`
|
|
60
|
+
* matching the family names
|
|
61
|
+
* - fontface objects : loads given fontfaces and adds them to
|
|
62
|
+
* `document.fonts`
|
|
63
|
+
*
|
|
64
|
+
* The returned promise will resolve, when all loading is done.
|
|
65
|
+
*/
|
|
66
|
+
function loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]>;
|
|
67
|
+
}
|