mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
initial
This commit is contained in:
@@ -33,6 +33,8 @@
|
||||
"addons/addon-unicode-graphemes/src/tsconfig.json",
|
||||
"addons/addon-unicode-graphemes/test/tsconfig.json",
|
||||
"addons/addon-unicode-graphemes/benchmark/tsconfig.json",
|
||||
"addons/addon-web-fonts/src/tsconfig.json",
|
||||
"addons/addon-web-fonts/test/tsconfig.json",
|
||||
"addons/addon-web-links/src/tsconfig.json",
|
||||
"addons/addon-web-links/test/tsconfig.json",
|
||||
"addons/addon-webgl/src/tsconfig.json",
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2024, The xterm.js authors (https://github.com/xtermjs/xterm.js)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,137 @@
|
||||
## @xterm/addon-web-fonts
|
||||
|
||||
Addon to use webfonts with [xterm.js](https://github.com/xtermjs/xterm.js). This addon requires xterm.js v5+.
|
||||
|
||||
### Install
|
||||
|
||||
```bash
|
||||
npm install --save @xterm/addon-web-fonts
|
||||
```
|
||||
|
||||
### Issue with Webfonts
|
||||
|
||||
Webfonts are announced by CSS `font-face` rules (or its Javascript `FontFace` counterparts). Since font files tend to be quite big assets, browser engines often postpone their loading to an actual styling request of a codepoint matching a font file's `unicode-range`. In short - font files will not be loaded until really needed.
|
||||
|
||||
xterm.js on the other hand heavily relies on exact measurements of character glyphs to layout its output. This is done by determining the glyph width (DOM renderer) or by creating a glyph texture (WebGl renderer) for every output character.
|
||||
For performance reasons both is done in synchronous code and cached. This logic only works properly,
|
||||
if a font glyph is available on its first usage, or a wrong glyph from a fallback font chosen by the browser will be used instead.
|
||||
|
||||
For webfonts and xterm.js this means, that we cannot rely on the default loading strategy of the browser, but have to preload the font files before using that font in xterm.js.
|
||||
|
||||
|
||||
### Static Preloading for the Rescue?
|
||||
|
||||
If you dont mind higher initial loading times of the embedding document, you can tell the browser to preload the needed font files by placing the following link elements in the document's head above any other CSS/Javascript:
|
||||
```html
|
||||
<link rel="preload" as="font" href="/path/to/your/fontfile1.woff" type="font/woff2" crossorigin="anonymous">
|
||||
<link rel="preload" as="font" href="/path/to/your/fontfile2.woff" type="font/woff2" crossorigin="anonymous">
|
||||
...
|
||||
<!-- CSS with font-face rules matching the URLs above -->
|
||||
```
|
||||
Downside of this approach is the much higher initial loading time showing as a white page. Browsers also will resort to system fonts, if the preloading takes too long, so with a slow connection or a very big font this solves literally nothing.
|
||||
|
||||
|
||||
### Preloading with WebFontsAddon
|
||||
|
||||
The webfonts addon offers several ways to deal with the loading of font assets without leaving the terminal in an unusable state.
|
||||
|
||||
|
||||
Recap - normally boostrapping of a new terminal involves these basic steps:
|
||||
|
||||
```typescript
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
import { XYAddon } from '@xterm/addon-xy';
|
||||
|
||||
// create a `Terminal` instance with some options, e.g. a custom font family
|
||||
const terminal = new Terminal({fontFamily: 'monospace'});
|
||||
|
||||
// create and load all addons you want to use, e.g. fit addon
|
||||
const xyAddon = new XYAddon();
|
||||
terminal.loadAddon(xyAddon);
|
||||
|
||||
// finally: call `open` of the terminal instance
|
||||
terminal.open(your_terminal_div_element); // <-- critical path for webfonts
|
||||
// more boostrapping goes here ...
|
||||
```
|
||||
|
||||
This synchronous code is guaranteed to work in all browsers, as the font `monospace` will always be available.
|
||||
It will also work that way with any installed system font, but breaks horribly for webfonts. The actual culprit here is the call to `terminal.open`, which attaches the terminal to the DOM and starts the renderer with all the glyph caching mentioned above, while the webfont is not fully available yet.
|
||||
|
||||
To fix that, the webfonts addon provides a waiting condition:
|
||||
```typescript
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
import { XYAddon } from '@xterm/addon-xy';
|
||||
import { WebFontsAddon } from '@xterm/addon-web-fonts';
|
||||
|
||||
// create a `Terminal` instance, now with webfonts
|
||||
const terminal = new Terminal({fontFamily: '"Web Mono 1", "Super Powerline", monospace'});
|
||||
const xyAddon = new XYAddon();
|
||||
terminal.loadAddon(xyAddon);
|
||||
|
||||
const webFontsAddon = new WebFontsAddon();
|
||||
terminal.loadAddon(webFontsAddon);
|
||||
|
||||
// wait for webfonts to be fully loaded
|
||||
await WebFontsAddon.loadFonts(['Web Mono 1', 'Super Powerline']).then(() => {
|
||||
terminal.open(your_terminal_div_element);
|
||||
// more boostrapping goes here ...
|
||||
});
|
||||
```
|
||||
Here `loadFonts` will look up the font face objects in `document.fonts` and load them before continuing.
|
||||
For this to work, you have to make sure, that the CSS `font-face` rules for these webfonts are loaded
|
||||
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):
|
||||
```typescript
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
import { XYAddon } from '@xterm/addon-xy';
|
||||
import { WebFontsAddon } from '@xterm/addon-web-fonts';
|
||||
|
||||
WebFontsAddon.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();
|
||||
terminal.loadAddon(xyAddon);
|
||||
|
||||
const webFontsAddon = new WebFontsAddon();
|
||||
terminal.loadAddon(webFontsAddon);
|
||||
|
||||
terminal.open(your_terminal_div_element);
|
||||
// more boostrapping goes here ...
|
||||
});
|
||||
```
|
||||
|
||||
### Webfont Loading at Runtime
|
||||
|
||||
Given you have a terminal already running and want to change the font family to a different not yet loaded webfont.
|
||||
That can be achieved like this:
|
||||
```typescript
|
||||
// either create font face objects in javascript
|
||||
const ff1 = new FontFace('New Web Mono', url1, ...);
|
||||
const ff2 = new FontFace('New Web Mono', url2, ...);
|
||||
// and await their loading
|
||||
await WebFontsAddon.loadFonts([ff1, ff2]).then(() => {
|
||||
// apply new webfont to terminal
|
||||
terminal.options.fontFamily = 'New Web Mono';
|
||||
// since the new font might have slighly different metrics,
|
||||
// also run the fit addon here (or any other custom resize logic)
|
||||
fitAddon.fit();
|
||||
});
|
||||
|
||||
// or alternatively use CSS to add new font-face rules, e.g.
|
||||
document.styleSheets[0].insertRule(
|
||||
"@font-face { font-family: 'New Web Mono'; src: url(newfont.woff); }", 0);
|
||||
// and await the new font family name
|
||||
await WebFontsAddon.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,
|
||||
// also run the fit addon here (or any other custom resize logic)
|
||||
fitAddon.fit();
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/addon-web-fonts/typings/addon-web-fonts.d.ts) for more advanced usage.
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "@xterm/addon-web-fonts",
|
||||
"version": "0.1.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
},
|
||||
"main": "lib/addon-web-fonts.js",
|
||||
"module": "lib/addon-web-fonts.mjs",
|
||||
"types": "typings/addon-web-fonts.d.ts",
|
||||
"repository": "https://github.com/xtermjs/xterm.js/tree/master/addons/addon-web-fonts",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"terminal",
|
||||
"xterm",
|
||||
"xterm.js"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "../../node_modules/.bin/tsc -p .",
|
||||
"prepackage": "npm run build",
|
||||
"package": "../../node_modules/.bin/webpack",
|
||||
"prepublishOnly": "npm run package",
|
||||
"start": "node ../../demo/start"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^5.0.0"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
/**
|
||||
* Copyright (c) 2024 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import type { Terminal, ITerminalAddon } from '@xterm/xterm';
|
||||
import type { WebFontsAddon as IWebFontsApi } from '@xterm/addon-web-fonts';
|
||||
|
||||
|
||||
/**
|
||||
* Unquote family name.
|
||||
*/
|
||||
function unquote(s: string): string {
|
||||
if (s[0] === '"' && s[s.length - 1] === '"') return s.slice(1, -1);
|
||||
if (s[0] === "'" && s[s.length - 1] === "'") return s.slice(1, -1);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Quote family name.
|
||||
* @see https://mathiasbynens.be/notes/unquoted-font-family
|
||||
*/
|
||||
function quote(s: string): string {
|
||||
const pos = s.match(/([-_a-zA-Z0-9\xA0-\u{10FFFF}]+)/u);
|
||||
const neg = s.match(/^(-?\d|--)/m);
|
||||
if (!neg && pos && pos[1] === s) return s;
|
||||
return `"${s.replace('"', '\\"')}"`;
|
||||
}
|
||||
|
||||
|
||||
function splitFamily(family: string | undefined): string[] {
|
||||
if (!family) return [];
|
||||
return family.split(',').map(e => unquote(e.trim()));
|
||||
}
|
||||
|
||||
|
||||
function createFamily(families: string[]): string {
|
||||
return families.map(quote).join(', ');
|
||||
}
|
||||
|
||||
|
||||
function _loadFonts(fonts?: (string | FontFace)[]): Promise<FontFace[]> {
|
||||
let ffs = Array.from(document.fonts);
|
||||
if (!fonts || !fonts.length) {
|
||||
return Promise.all(ffs.map(ff => ff.load()));
|
||||
}
|
||||
let toLoad: FontFace[] = [];
|
||||
let ffsHashed = ffs.map(ff => WebFontsAddon.hashFontFace(ff));
|
||||
for (const font of fonts) {
|
||||
if (font instanceof FontFace) {
|
||||
const fontHashed = WebFontsAddon.hashFontFace(font);
|
||||
const idx = ffsHashed.indexOf(fontHashed);
|
||||
if (idx === -1) {
|
||||
document.fonts.add(font);
|
||||
ffs.push(font);
|
||||
ffsHashed.push(fontHashed);
|
||||
toLoad.push(font);
|
||||
} else {
|
||||
toLoad.push(ffs[idx]);
|
||||
}
|
||||
} else {
|
||||
// string as font
|
||||
const familyFiltered = ffs.filter(ff => font === unquote(ff.family));
|
||||
toLoad = toLoad.concat(familyFiltered);
|
||||
if (!familyFiltered.length) {
|
||||
console.warn(`font family "${font}" not registered in document.fonts`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.all(toLoad.map(ff => ff.load()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class WebFontsAddon implements ITerminalAddon, IWebFontsApi {
|
||||
constructor(public forceInitialRelayout: boolean = true) { }
|
||||
public dispose(): void { }
|
||||
|
||||
public activate(terminal: Terminal): void {
|
||||
if (this.forceInitialRelayout) {
|
||||
document.fonts.ready.then(() => this.relayout(terminal));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
const families = splitFamily(family);
|
||||
const webFamilies = WebFontsAddon.getFontFamilies();
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO: place into test cases
|
||||
/*
|
||||
(window as any).__roboto = [
|
||||
// cyrillic-ext
|
||||
new FontFace(
|
||||
'Roboto Mono',
|
||||
"url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3CWWoKC.woff2) format('woff2')",
|
||||
{
|
||||
style: 'italic',
|
||||
weight: '100 700',
|
||||
display: 'swap',
|
||||
unicodeRange: 'U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F'
|
||||
}
|
||||
),
|
||||
// cyrillic
|
||||
new FontFace(
|
||||
'Roboto Mono',
|
||||
"url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3mWWoKC.woff2) format('woff2')",
|
||||
{
|
||||
style: 'italic',
|
||||
weight: '100 700',
|
||||
display: 'swap',
|
||||
unicodeRange: 'U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116'
|
||||
}
|
||||
),
|
||||
// greek
|
||||
new FontFace(
|
||||
'Roboto Mono',
|
||||
"url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm36WWoKC.woff2) format('woff2')",
|
||||
{
|
||||
style: 'italic',
|
||||
weight: '100 700',
|
||||
display: 'swap',
|
||||
unicodeRange: 'U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF'
|
||||
}
|
||||
),
|
||||
// vietnamese
|
||||
new FontFace(
|
||||
'Roboto Mono',
|
||||
"url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3KWWoKC.woff2) format('woff2')",
|
||||
{
|
||||
style: 'italic',
|
||||
weight: '100 700',
|
||||
display: 'swap',
|
||||
unicodeRange: 'U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB'
|
||||
}
|
||||
),
|
||||
// latin-ext
|
||||
new FontFace(
|
||||
'Roboto Mono',
|
||||
"url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3OWWoKC.woff2) format('woff2')",
|
||||
{
|
||||
style: 'italic',
|
||||
weight: '100 700',
|
||||
display: 'swap',
|
||||
unicodeRange: 'U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF'
|
||||
}
|
||||
),
|
||||
// latin
|
||||
new FontFace(
|
||||
'Roboto Mono',
|
||||
"url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm32WWg.woff2) format('woff2')",
|
||||
{
|
||||
style: 'italic',
|
||||
weight: '100 700',
|
||||
display: 'swap',
|
||||
unicodeRange: 'U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD'
|
||||
}
|
||||
),
|
||||
// cyrillic-ext
|
||||
new FontFace(
|
||||
'Roboto Mono',
|
||||
"url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhGq3-OXg.woff2) format('woff2')",
|
||||
{
|
||||
style: 'normal',
|
||||
weight: '100 700',
|
||||
display: 'swap',
|
||||
unicodeRange: 'U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F'
|
||||
}
|
||||
),
|
||||
// cyrillic
|
||||
new FontFace(
|
||||
'Roboto Mono',
|
||||
"url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhPq3-OXg.woff2) format('woff2')",
|
||||
{
|
||||
style: 'normal',
|
||||
weight: '100 700',
|
||||
display: 'swap',
|
||||
unicodeRange: 'U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116'
|
||||
}
|
||||
),
|
||||
// greek
|
||||
new FontFace(
|
||||
'Roboto Mono',
|
||||
"url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhIq3-OXg.woff2) format('woff2')",
|
||||
{
|
||||
style: 'normal',
|
||||
weight: '100 700',
|
||||
display: 'swap',
|
||||
unicodeRange: 'U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF'
|
||||
}
|
||||
),
|
||||
// vietnamese
|
||||
new FontFace(
|
||||
'Roboto Mono',
|
||||
"url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhEq3-OXg.woff2) format('woff2')",
|
||||
{
|
||||
style: 'normal',
|
||||
weight: '100 700',
|
||||
display: 'swap',
|
||||
unicodeRange: 'U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB'
|
||||
}
|
||||
),
|
||||
// latin-ext
|
||||
new FontFace(
|
||||
'Roboto Mono',
|
||||
"url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhFq3-OXg.woff2) format('woff2')",
|
||||
{
|
||||
style: 'normal',
|
||||
weight: '100 700',
|
||||
display: 'swap',
|
||||
unicodeRange: 'U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF'
|
||||
}
|
||||
),
|
||||
// latin
|
||||
new FontFace(
|
||||
'Roboto Mono',
|
||||
"url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhLq38.woff2) format('woff2')",
|
||||
{
|
||||
style: 'normal',
|
||||
weight: '100 700',
|
||||
display: 'swap',
|
||||
unicodeRange: 'U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD'
|
||||
}
|
||||
),
|
||||
];
|
||||
*/
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2021",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015",
|
||||
"dom.iterable"
|
||||
],
|
||||
"rootDir": ".",
|
||||
"outDir": "../out",
|
||||
"sourceMap": true,
|
||||
"removeComments": true,
|
||||
"strict": true,
|
||||
"types": [
|
||||
"../../../node_modules/@types/mocha"
|
||||
],
|
||||
"paths": {
|
||||
"@xterm/addon-web-fonts": [
|
||||
"../typings/addon-web-fonts.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"./**/*",
|
||||
"../../../typings/xterm.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
import test from '@playwright/test';
|
||||
import { deepStrictEqual, strictEqual } from 'assert';
|
||||
import { readFile } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import { ITestContext, createTestContext, openTerminal, pollFor, timeout } from '../../../test/playwright/TestUtils';
|
||||
|
||||
interface ILinkStateData {
|
||||
uri?: string;
|
||||
range?: {
|
||||
start: {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
end: {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
let ctx: ITestContext;
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
ctx = await createTestContext(browser);
|
||||
await openTerminal(ctx, { cols: 40 });
|
||||
});
|
||||
test.afterAll(async () => await ctx.page.close());
|
||||
|
||||
test.describe('WebLinksAddon', () => {
|
||||
|
||||
test.beforeEach(async () => {
|
||||
await ctx.page.evaluate(`
|
||||
window.term.reset()
|
||||
window._linkaddon?.dispose();
|
||||
window._linkaddon = new WebLinksAddon();
|
||||
window.term.loadAddon(window._linkaddon);
|
||||
`);
|
||||
});
|
||||
|
||||
const countryTlds = [
|
||||
'.ac', '.ad', '.ae', '.af', '.ag', '.ai', '.al', '.am', '.ao', '.aq', '.ar', '.as', '.at',
|
||||
'.au', '.aw', '.ax', '.az', '.ba', '.bb', '.bd', '.be', '.bf', '.bg', '.bh', '.bi', '.bj',
|
||||
'.bm', '.bn', '.bo', '.bq', '.br', '.bs', '.bt', '.bw', '.by', '.bz', '.ca', '.cc', '.cd',
|
||||
'.cf', '.cg', '.ch', '.ci', '.ck', '.cl', '.cm', '.cn', '.co', '.cr', '.cu', '.cv', '.cw',
|
||||
'.cx', '.cy', '.cz', '.de', '.dj', '.dk', '.dm', '.do', '.dz', '.ec', '.ee', '.eg', '.eh',
|
||||
'.er', '.es', '.et', '.eu', '.fi', '.fj', '.fk', '.fm', '.fo', '.fr', '.ga', '.gd', '.ge',
|
||||
'.gf', '.gg', '.gh', '.gi', '.gl', '.gm', '.gn', '.gp', '.gq', '.gr', '.gs', '.gt', '.gu',
|
||||
'.gw', '.gy', '.hk', '.hm', '.hn', '.hr', '.ht', '.hu', '.id', '.ie', '.il', '.im', '.in',
|
||||
'.io', '.iq', '.ir', '.is', '.it', '.je', '.jm', '.jo', '.jp', '.ke', '.kg', '.kh', '.ki',
|
||||
'.km', '.kn', '.kp', '.kr', '.kw', '.ky', '.kz', '.la', '.lb', '.lc', '.li', '.lk', '.lr',
|
||||
'.ls', '.lt', '.lu', '.lv', '.ly', '.ma', '.mc', '.md', '.me', '.mg', '.mh', '.mk', '.ml',
|
||||
'.mm', '.mn', '.mo', '.mp', '.mq', '.mr', '.ms', '.mt', '.mu', '.mv', '.mw', '.mx', '.my',
|
||||
'.mz', '.na', '.nc', '.ne', '.nf', '.ng', '.ni', '.nl', '.no', '.np', '.nr', '.nu', '.nz',
|
||||
'.om', '.pa', '.pe', '.pf', '.pg', '.ph', '.pk', '.pl', '.pm', '.pn', '.pr', '.ps', '.pt',
|
||||
'.pw', '.py', '.qa', '.re', '.ro', '.rs', '.ru', '.rw', '.sa', '.sb', '.sc', '.sd', '.se',
|
||||
'.sg', '.sh', '.si', '.sk', '.sl', '.sm', '.sn', '.so', '.sr', '.ss', '.st', '.su', '.sv',
|
||||
'.sx', '.sy', '.sz', '.tc', '.td', '.tf', '.tg', '.th', '.tj', '.tk', '.tl', '.tm', '.tn',
|
||||
'.to', '.tr', '.tt', '.tv', '.tw', '.tz', '.ua', '.ug', '.uk', '.us', '.uy', '.uz', '.va',
|
||||
'.vc', '.ve', '.vg', '.vi', '.vn', '.vu', '.wf', '.ws', '.ye', '.yt', '.za', '.zm', '.zw'
|
||||
];
|
||||
for (const tld of countryTlds) {
|
||||
test(tld, async () => await testHostName(`foo${tld}`));
|
||||
}
|
||||
test(`.com`, async () => await testHostName(`foo.com`));
|
||||
for (const tld of countryTlds) {
|
||||
test(`.com${tld}`, async () => await testHostName(`foo.com${tld}`));
|
||||
}
|
||||
|
||||
test.describe('correct buffer offsets & uri', () => {
|
||||
test.beforeEach(async () => {
|
||||
await ctx.page.evaluate(`
|
||||
window._linkStateData = {uri:''};
|
||||
window._linkaddon._options.hover = (event, uri, range) => { window._linkStateData = { uri, range }; };
|
||||
`);
|
||||
});
|
||||
test('all half width', async () => {
|
||||
await ctx.proxy.write('aaa http://example.com aaa http://example.com aaa');
|
||||
await resetAndHover(5, 0);
|
||||
await evalLinkStateData('http://example.com', { start: { x: 5, y: 1 }, end: { x: 22, y: 1 } });
|
||||
await resetAndHover(1, 1);
|
||||
await evalLinkStateData('http://example.com', { start: { x: 28, y: 1 }, end: { x: 5, y: 2 } });
|
||||
});
|
||||
test('url after full width', async () => {
|
||||
await ctx.proxy.write('¥¥¥ http://example.com ¥¥¥ http://example.com aaa');
|
||||
await resetAndHover(8, 0);
|
||||
await evalLinkStateData('http://example.com', { start: { x: 8, y: 1 }, end: { x: 25, y: 1 } });
|
||||
await resetAndHover(1, 1);
|
||||
await evalLinkStateData('http://example.com', { start: { x: 34, y: 1 }, end: { x: 11, y: 2 } });
|
||||
});
|
||||
test('full width within url and before', async () => {
|
||||
await ctx.proxy.write('¥¥¥ https://ko.wikipedia.org/wiki/위키백과:대문 aaa https://ko.wikipedia.org/wiki/위키백과:대문 ¥¥¥');
|
||||
await resetAndHover(8, 0);
|
||||
await evalLinkStateData('https://ko.wikipedia.org/wiki/위키백과:대문', { start: { x: 8, y: 1 }, end: { x: 11, y: 2 } });
|
||||
await resetAndHover(1, 1);
|
||||
await evalLinkStateData('https://ko.wikipedia.org/wiki/위키백과:대문', { start: { x: 8, y: 1 }, end: { x: 11, y: 2 } });
|
||||
await resetAndHover(17, 1);
|
||||
await evalLinkStateData('https://ko.wikipedia.org/wiki/위키백과:대문', { start: { x: 17, y: 2 }, end: { x: 19, y: 3 } });
|
||||
});
|
||||
test('name + password url after full width and combining', async () => {
|
||||
await ctx.proxy.write('¥¥¥cafe\u0301 http://test:password@example.com/some_path');
|
||||
await resetAndHover(12, 0);
|
||||
await evalLinkStateData('http://test:password@example.com/some_path', { start: { x: 12, y: 1 }, end: { x: 13, y: 2 } });
|
||||
await resetAndHover(5, 1);
|
||||
await evalLinkStateData('http://test:password@example.com/some_path', { start: { x: 12, y: 1 }, end: { x: 13, y: 2 } });
|
||||
});
|
||||
test('url encoded params work properly', async () => {
|
||||
await ctx.proxy.write('¥¥¥cafe\u0301 http://test:password@example.com/some_path?param=1%202%3');
|
||||
await resetAndHover(12, 0);
|
||||
await evalLinkStateData('http://test:password@example.com/some_path?param=1%202%3', { start: { x: 12, y: 1 }, end: { x: 27, y: 2 } });
|
||||
await resetAndHover(5, 1);
|
||||
await evalLinkStateData('http://test:password@example.com/some_path?param=1%202%3', { start: { x: 12, y: 1 }, end: { x: 27, y: 2 } });
|
||||
});
|
||||
});
|
||||
|
||||
// issue #4964
|
||||
test('uppercase in protocol and host, default ports', async () => {
|
||||
await ctx.proxy.write(
|
||||
` HTTP://EXAMPLE.COM \r\n` +
|
||||
` HTTPS://Example.com \r\n` +
|
||||
` HTTP://Example.com:80 \r\n` +
|
||||
` HTTP://Example.com:80/staysUpper \r\n` +
|
||||
` HTTP://Ab:xY@abc.com:80/staysUpper \r\n`
|
||||
);
|
||||
await pollForLinkAtCell(3, 0, `HTTP://EXAMPLE.COM`);
|
||||
await pollForLinkAtCell(3, 1, `HTTPS://Example.com`);
|
||||
await pollForLinkAtCell(3, 2, `HTTP://Example.com:80`);
|
||||
await pollForLinkAtCell(3, 3, `HTTP://Example.com:80/staysUpper`);
|
||||
await pollForLinkAtCell(3, 4, `HTTP://Ab:xY@abc.com:80/staysUpper`);
|
||||
});
|
||||
});
|
||||
|
||||
async function testHostName(hostname: string): Promise<void> {
|
||||
await ctx.proxy.write(
|
||||
` http://${hostname} \r\n` +
|
||||
` http://${hostname}/a~b#c~d?e~f \r\n` +
|
||||
` http://${hostname}/colon:test \r\n` +
|
||||
` http://${hostname}/colon:test: \r\n` +
|
||||
`"http://${hostname}/"\r\n` +
|
||||
`\'http://${hostname}/\'\r\n` +
|
||||
`http://${hostname}/subpath/+/id`
|
||||
);
|
||||
await pollForLinkAtCell(3, 0, `http://${hostname}`);
|
||||
await pollForLinkAtCell(3, 1, `http://${hostname}/a~b#c~d?e~f`);
|
||||
await pollForLinkAtCell(3, 2, `http://${hostname}/colon:test`);
|
||||
await pollForLinkAtCell(3, 3, `http://${hostname}/colon:test`);
|
||||
await pollForLinkAtCell(2, 4, `http://${hostname}/`);
|
||||
await pollForLinkAtCell(2, 5, `http://${hostname}/`);
|
||||
await pollForLinkAtCell(1, 6, `http://${hostname}/subpath/+/id`);
|
||||
}
|
||||
|
||||
async function pollForLinkAtCell(col: number, row: number, value: string): Promise<void> {
|
||||
await ctx.page.mouse.move(...(await cellPos(col, row)));
|
||||
await pollFor(ctx.page, `!!Array.from(document.querySelectorAll('.xterm-rows > :nth-child(${row+1}) > span[style]')).filter(el => el.style.textDecoration == 'underline').length`, true);
|
||||
const text = await ctx.page.evaluate(`Array.from(document.querySelectorAll('.xterm-rows > :nth-child(${row+1}) > span[style]')).filter(el => el.style.textDecoration == 'underline').map(el => el.textContent).join('');`);
|
||||
deepStrictEqual(text, value);
|
||||
}
|
||||
|
||||
async function resetAndHover(col: number, row: number): Promise<void> {
|
||||
await ctx.page.mouse.move(0, 0);
|
||||
await ctx.page.evaluate(`window._linkStateData = {uri:''};`);
|
||||
await new Promise(r => setTimeout(r, 200));
|
||||
await ctx.page.mouse.move(...(await cellPos(col, row)));
|
||||
await pollFor(ctx.page, `!!window._linkStateData.uri.length`, true);
|
||||
}
|
||||
|
||||
async function evalLinkStateData(uri: string, range: any): Promise<void> {
|
||||
const data: ILinkStateData = await ctx.page.evaluate(`window._linkStateData`);
|
||||
strictEqual(data.uri, uri);
|
||||
deepStrictEqual(data.range, range);
|
||||
}
|
||||
|
||||
async function cellPos(col: number, row: number): Promise<[number, number]> {
|
||||
const coords: any = await ctx.page.evaluate(`
|
||||
(function() {
|
||||
const rect = window.term.element.getBoundingClientRect();
|
||||
const dim = term._core._renderService.dimensions;
|
||||
return {left: rect.left, top: rect.top, bottom: rect.bottom, right: rect.right, width: dim.css.cell.width, height: dim.css.cell.height};
|
||||
})();
|
||||
`);
|
||||
return [col * coords.width + coords.left + 2, row * coords.height + coords.top + 2];
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { PlaywrightTestConfig } from '@playwright/test';
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
testDir: '.',
|
||||
timeout: 10000,
|
||||
projects: [
|
||||
{
|
||||
name: 'ChromeStable',
|
||||
use: {
|
||||
browserName: 'chromium',
|
||||
channel: 'chrome'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'FirefoxStable',
|
||||
use: {
|
||||
browserName: 'firefox'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'WebKit',
|
||||
use: {
|
||||
browserName: 'webkit'
|
||||
}
|
||||
}
|
||||
],
|
||||
reporter: 'list',
|
||||
webServer: {
|
||||
command: 'npm run start',
|
||||
port: 3000,
|
||||
timeout: 120000,
|
||||
reuseExistingServer: !process.env.CI
|
||||
}
|
||||
};
|
||||
export default config;
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2021",
|
||||
"lib": [
|
||||
"es2021",
|
||||
],
|
||||
// "downlevelIteration": true,
|
||||
"rootDir": ".",
|
||||
"outDir": "../out-test",
|
||||
"sourceMap": true,
|
||||
"removeComments": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"common/*": [
|
||||
"../../../src/common/*"
|
||||
],
|
||||
"browser/*": [
|
||||
"../../../src/browser/*"
|
||||
]
|
||||
},
|
||||
"strict": true,
|
||||
"types": [
|
||||
"../../../node_modules/@types/node"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"./**/*",
|
||||
"../../../typings/xterm.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../../src/common"
|
||||
},
|
||||
{
|
||||
"path": "../../../src/browser"
|
||||
},
|
||||
{
|
||||
"path": "../../../test/playwright"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{ "path": "./src" },
|
||||
{ "path": "./test" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
|
||||
import { Terminal, ITerminalAddon, IViewportRange } from '@xterm/xterm';
|
||||
|
||||
declare module '@xterm/addon-web-fonts' {
|
||||
/**
|
||||
* An xterm.js addon that enables web links.
|
||||
*/
|
||||
export class WebFontsAddon implements ITerminalAddon {
|
||||
constructor();
|
||||
public activate(terminal: Terminal): void;
|
||||
public dispose(): void;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
|
||||
const addonName = 'WebFontsAddon';
|
||||
const mainFile = 'addon-web-fonts.js';
|
||||
|
||||
module.exports = {
|
||||
entry: `./out/${addonName}.js`,
|
||||
devtool: 'source-map',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
use: ["source-map-loader"],
|
||||
enforce: "pre",
|
||||
exclude: /node_modules/
|
||||
}
|
||||
]
|
||||
},
|
||||
output: {
|
||||
filename: mainFile,
|
||||
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'
|
||||
};
|
||||
+21
-12
@@ -23,6 +23,7 @@ import { FitAddon } from '@xterm/addon-fit';
|
||||
import { LigaturesAddon } from '@xterm/addon-ligatures';
|
||||
import { SearchAddon, ISearchOptions } from '@xterm/addon-search';
|
||||
import { SerializeAddon } from '@xterm/addon-serialize';
|
||||
import { WebFontsAddon } from '@xterm/addon-web-fonts';
|
||||
import { WebLinksAddon } from '@xterm/addon-web-links';
|
||||
import { WebglAddon } from '@xterm/addon-webgl';
|
||||
import { Unicode11Addon } from '@xterm/addon-unicode11';
|
||||
@@ -37,6 +38,7 @@ export interface IWindowWithTerminal extends Window {
|
||||
ImageAddon?: typeof ImageAddon; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
SearchAddon?: typeof SearchAddon; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
SerializeAddon?: typeof SerializeAddon; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
WebFontsAddon?: typeof WebFontsAddon; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
WebLinksAddon?: typeof WebLinksAddon; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
WebglAddon?: typeof WebglAddon; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
Unicode11Addon?: typeof Unicode11Addon; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
@@ -52,7 +54,7 @@ let socket;
|
||||
let pid;
|
||||
let autoResize: boolean = true;
|
||||
|
||||
type AddonType = 'attach' | 'clipboard' | 'fit' | 'image' | 'search' | 'serialize' | 'unicode11' | 'unicodeGraphemes' | 'webLinks' | 'webgl' | 'ligatures';
|
||||
type AddonType = 'attach' | 'clipboard' | 'fit' | 'image' | 'search' | 'serialize' | 'unicode11' | 'unicodeGraphemes' | 'webFonts' | 'webLinks' | 'webgl' | 'ligatures';
|
||||
|
||||
interface IDemoAddon<T extends AddonType> {
|
||||
name: T;
|
||||
@@ -65,11 +67,12 @@ interface IDemoAddon<T extends AddonType> {
|
||||
T extends 'ligatures' ? typeof LigaturesAddon :
|
||||
T extends 'search' ? typeof SearchAddon :
|
||||
T extends 'serialize' ? typeof SerializeAddon :
|
||||
T extends 'webLinks' ? typeof WebLinksAddon :
|
||||
T extends 'unicode11' ? typeof Unicode11Addon :
|
||||
T extends 'unicodeGraphemes' ? typeof UnicodeGraphemesAddon :
|
||||
T extends 'webgl' ? typeof WebglAddon :
|
||||
never
|
||||
T extends 'webFonts' ? typeof WebFontsAddon :
|
||||
T extends 'webLinks' ? typeof WebLinksAddon :
|
||||
T extends 'unicode11' ? typeof Unicode11Addon :
|
||||
T extends 'unicodeGraphemes' ? typeof UnicodeGraphemesAddon :
|
||||
T extends 'webgl' ? typeof WebglAddon :
|
||||
never
|
||||
);
|
||||
instance?: (
|
||||
T extends 'attach' ? AttachAddon :
|
||||
@@ -79,11 +82,12 @@ interface IDemoAddon<T extends AddonType> {
|
||||
T extends 'ligatures' ? LigaturesAddon :
|
||||
T extends 'search' ? SearchAddon :
|
||||
T extends 'serialize' ? SerializeAddon :
|
||||
T extends 'webLinks' ? WebLinksAddon :
|
||||
T extends 'unicode11' ? Unicode11Addon :
|
||||
T extends 'unicodeGraphemes' ? UnicodeGraphemesAddon :
|
||||
T extends 'webgl' ? WebglAddon :
|
||||
never
|
||||
T extends 'webFonts' ? WebFontsAddon :
|
||||
T extends 'webLinks' ? WebLinksAddon :
|
||||
T extends 'unicode11' ? Unicode11Addon :
|
||||
T extends 'unicodeGraphemes' ? UnicodeGraphemesAddon :
|
||||
T extends 'webgl' ? WebglAddon :
|
||||
never
|
||||
);
|
||||
}
|
||||
|
||||
@@ -94,6 +98,7 @@ const addons: { [T in AddonType]: IDemoAddon<T> } = {
|
||||
image: { name: 'image', ctor: ImageAddon, canChange: true },
|
||||
search: { name: 'search', ctor: SearchAddon, canChange: true },
|
||||
serialize: { name: 'serialize', ctor: SerializeAddon, canChange: true },
|
||||
webFonts: { name: 'webFonts', ctor: WebFontsAddon, canChange: true },
|
||||
webLinks: { name: 'webLinks', ctor: WebLinksAddon, canChange: true },
|
||||
webgl: { name: 'webgl', ctor: WebglAddon, canChange: true },
|
||||
unicode11: { name: 'unicode11', ctor: Unicode11Addon, canChange: true },
|
||||
@@ -169,6 +174,7 @@ const disposeRecreateButtonHandler: () => void = () => {
|
||||
addons.unicode11.instance = undefined;
|
||||
addons.unicodeGraphemes.instance = undefined;
|
||||
addons.ligatures.instance = undefined;
|
||||
addons.webFonts.instance = undefined;
|
||||
addons.webLinks.instance = undefined;
|
||||
addons.webgl.instance = undefined;
|
||||
document.getElementById('dispose').innerHTML = 'Recreate Terminal';
|
||||
@@ -218,6 +224,7 @@ if (document.location.pathname === '/test') {
|
||||
window.Unicode11Addon = Unicode11Addon;
|
||||
window.UnicodeGraphemesAddon = UnicodeGraphemesAddon;
|
||||
window.LigaturesAddon = LigaturesAddon;
|
||||
window.WebFontsAddon = WebFontsAddon;
|
||||
window.WebLinksAddon = WebLinksAddon;
|
||||
window.WebglAddon = WebglAddon;
|
||||
} else {
|
||||
@@ -261,7 +268,7 @@ function createTerminal(): void {
|
||||
backend: 'conpty',
|
||||
buildNumber: 22621
|
||||
} : undefined,
|
||||
fontFamily: '"Fira Code", courier-new, courier, monospace, "Powerline Extra Symbols"',
|
||||
fontFamily: '"Roboto Mono", "Fira Code", courier-new, courier, monospace, "Powerline Extra Symbols"',
|
||||
theme: xtermjsTheme
|
||||
} as ITerminalOptions);
|
||||
|
||||
@@ -278,12 +285,14 @@ function createTerminal(): void {
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
addons.webFonts.instance = new WebFontsAddon();
|
||||
addons.webLinks.instance = new WebLinksAddon();
|
||||
typedTerm.loadAddon(addons.fit.instance);
|
||||
typedTerm.loadAddon(addons.image.instance);
|
||||
typedTerm.loadAddon(addons.search.instance);
|
||||
typedTerm.loadAddon(addons.serialize.instance);
|
||||
typedTerm.loadAddon(addons.unicodeGraphemes.instance);
|
||||
typedTerm.loadAddon(addons.webFonts.instance);
|
||||
typedTerm.loadAddon(addons.webLinks.instance);
|
||||
typedTerm.loadAddon(addons.clipboard.instance);
|
||||
|
||||
|
||||
+112
@@ -1,3 +1,115 @@
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: italic;
|
||||
font-weight: 100 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3CWWoKC.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: italic;
|
||||
font-weight: 100 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3mWWoKC.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: italic;
|
||||
font-weight: 100 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm36WWoKC.woff2) format('woff2');
|
||||
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: italic;
|
||||
font-weight: 100 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3KWWoKC.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: italic;
|
||||
font-weight: 100 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3OWWoKC.woff2) format('woff2');
|
||||
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: italic;
|
||||
font-weight: 100 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm32WWg.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: normal;
|
||||
font-weight: 100 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhGq3-OXg.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: normal;
|
||||
font-weight: 100 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhPq3-OXg.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: normal;
|
||||
font-weight: 100 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhIq3-OXg.woff2) format('woff2');
|
||||
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: normal;
|
||||
font-weight: 100 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhEq3-OXg.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: normal;
|
||||
font-weight: 100 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhFq3-OXg.woff2) format('woff2');
|
||||
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: normal;
|
||||
font-weight: 100 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhLq38.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
body {
|
||||
font-family: helvetica, sans-serif, arial;
|
||||
font-size: 1em;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"@xterm/addon-image": ["../addons/addon-image"],
|
||||
"@xterm/addon-search": ["../addons/addon-search"],
|
||||
"@xterm/addon-serialize": ["../addons/addon-serialize"],
|
||||
"@xterm/addon-web-fonts": ["../addons/addon-web-fonts"],
|
||||
"@xterm/addon-web-links": ["../addons/addon-web-links"],
|
||||
"@xterm/addon-webgl": ["../addons/addon-webgl"],
|
||||
"@xterm/addon-unicode11": ["../addons/addon-unicode11"],
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
{ "path": "./addons/addon-serialize" },
|
||||
{ "path": "./addons/addon-unicode11" },
|
||||
{ "path": "./addons/addon-unicode-graphemes" },
|
||||
{ "path": "./addons/addon-web-fonts" },
|
||||
{ "path": "./addons/addon-web-links" },
|
||||
{ "path": "./addons/addon-webgl" }
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user