+50
-919
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,11 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/// <reference path="../../typings/xterm.d.ts"/>
|
||||||
|
|
||||||
|
import type { Terminal } from '@xterm/xterm';
|
||||||
|
import type { AddonCollection } from 'types';
|
||||||
|
|
||||||
export interface ITabConfig {
|
export interface ITabConfig {
|
||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -12,6 +17,7 @@ export interface IControlWindow {
|
|||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly label: string;
|
readonly label: string;
|
||||||
build(container: HTMLElement): void;
|
build(container: HTMLElement): void;
|
||||||
|
setTerminal(terminal: Terminal): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ControlBar {
|
export class ControlBar {
|
||||||
@@ -146,7 +152,7 @@ export class ControlBar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public registerWindow(window: IControlWindow, options?: { afterId?: string; hidden?: boolean; smallTab?: boolean }): void {
|
public registerWindow<T extends IControlWindow>(window: T, options?: { afterId?: string; hidden?: boolean; smallTab?: boolean }): T {
|
||||||
// Create button
|
// Create button
|
||||||
const button = document.createElement('button');
|
const button = document.createElement('button');
|
||||||
button.id = `${window.id}button`;
|
button.id = `${window.id}button`;
|
||||||
@@ -187,6 +193,8 @@ export class ControlBar {
|
|||||||
window.build(content);
|
window.build(content);
|
||||||
|
|
||||||
this._tabs.set(window.id, { button, content });
|
this._tabs.set(window.id, { button, content });
|
||||||
|
|
||||||
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setTabVisible(tabId: string, visible: boolean): void {
|
public setTabVisible(tabId: string, visible: boolean): void {
|
||||||
|
|||||||
@@ -3,10 +3,15 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Terminal } from '@xterm/xterm';
|
/// <reference path="../../../typings/xterm.d.ts"/>
|
||||||
import type { IControlWindow } from '../controlBar';
|
|
||||||
|
|
||||||
export class AddonsWindow implements IControlWindow {
|
import type { Terminal } from '@xterm/xterm';
|
||||||
|
import { BaseWindow } from './baseWindow';
|
||||||
|
import type { IControlWindow } from '../controlBar';
|
||||||
|
import type { AddonCollection } from 'types';
|
||||||
|
import type { IImageAddonOptions } from '@xterm/addon-image';
|
||||||
|
|
||||||
|
export class AddonsWindow extends BaseWindow implements IControlWindow {
|
||||||
public readonly id = 'addons';
|
public readonly id = 'addons';
|
||||||
public readonly label = 'Addons';
|
public readonly label = 'Addons';
|
||||||
|
|
||||||
@@ -141,6 +146,7 @@ export class AddonsWindow implements IControlWindow {
|
|||||||
const serializeBtn = document.createElement('button');
|
const serializeBtn = document.createElement('button');
|
||||||
serializeBtn.id = 'serialize';
|
serializeBtn.id = 'serialize';
|
||||||
serializeBtn.textContent = 'Serialize the content of terminal';
|
serializeBtn.textContent = 'Serialize the content of terminal';
|
||||||
|
serializeBtn.addEventListener('click', () => serializeButtonHandler(this._terminal, this._addons));
|
||||||
wrapper.appendChild(serializeBtn);
|
wrapper.appendChild(serializeBtn);
|
||||||
|
|
||||||
// Write to terminal checkbox
|
// Write to terminal checkbox
|
||||||
@@ -163,6 +169,7 @@ export class AddonsWindow implements IControlWindow {
|
|||||||
const htmlSerializeBtn = document.createElement('button');
|
const htmlSerializeBtn = document.createElement('button');
|
||||||
htmlSerializeBtn.id = 'htmlserialize';
|
htmlSerializeBtn.id = 'htmlserialize';
|
||||||
htmlSerializeBtn.textContent = 'Serialize the content of terminal in HTML';
|
htmlSerializeBtn.textContent = 'Serialize the content of terminal in HTML';
|
||||||
|
htmlSerializeBtn.addEventListener('click', () => htmlSerializeButtonHandler(this._terminal, this._addons));
|
||||||
wrapper.appendChild(htmlSerializeBtn);
|
wrapper.appendChild(htmlSerializeBtn);
|
||||||
|
|
||||||
// HTML serialize result
|
// HTML serialize result
|
||||||
@@ -244,3 +251,30 @@ export class AddonsWindow implements IControlWindow {
|
|||||||
return this._findResultsSpan;
|
return this._findResultsSpan;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function serializeButtonHandler(term: Terminal, addons: AddonCollection): void {
|
||||||
|
const output = addons.serialize.instance.serialize();
|
||||||
|
const outputString = JSON.stringify(output);
|
||||||
|
|
||||||
|
document.getElementById('serialize-output').innerText = outputString;
|
||||||
|
if ((document.getElementById('write-to-terminal') as HTMLInputElement).checked) {
|
||||||
|
term.reset();
|
||||||
|
term.write(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function htmlSerializeButtonHandler(term: Terminal, addons: AddonCollection): void {
|
||||||
|
const output = addons.serialize.instance.serializeAsHTML();
|
||||||
|
document.getElementById('htmlserialize-output').innerText = output;
|
||||||
|
|
||||||
|
// Deprecated, but the most supported for now.
|
||||||
|
function listener(e: any): void {
|
||||||
|
e.clipboardData.setData('text/html', output);
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
document.addEventListener('copy', listener);
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.removeEventListener('copy', listener);
|
||||||
|
document.getElementById('htmlserialize-output-result').innerText = 'Copied to clipboard';
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 The xterm.js authors. All rights reserved.
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <reference path="../../../typings/xterm.d.ts"/>
|
||||||
|
|
||||||
|
import type { AddonCollection } from '../../types';
|
||||||
|
import type { IControlWindow } from '../controlBar';
|
||||||
|
import type { Terminal } from '@xterm/xterm';
|
||||||
|
|
||||||
|
export abstract class BaseWindow implements IControlWindow {
|
||||||
|
protected get _terminal(): Terminal { return this.__terminal; }
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private __terminal: Terminal,
|
||||||
|
protected readonly _addons: AddonCollection,
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
setTerminal(terminal: Terminal): void {
|
||||||
|
this.__terminal = terminal;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract id: string;
|
||||||
|
abstract label: string;
|
||||||
|
abstract build(container: HTMLElement): void;
|
||||||
|
}
|
||||||
@@ -3,9 +3,10 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { BaseWindow } from './baseWindow';
|
||||||
import type { IControlWindow } from '../controlBar';
|
import type { IControlWindow } from '../controlBar';
|
||||||
|
|
||||||
export class GpuWindow implements IControlWindow {
|
export class GpuWindow extends BaseWindow implements IControlWindow {
|
||||||
public readonly id = 'gpu';
|
public readonly id = 'gpu';
|
||||||
public readonly label = 'GPU';
|
public readonly label = 'GPU';
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,10 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { BaseWindow } from './baseWindow';
|
||||||
import type { Terminal, ITheme } from '@xterm/xterm';
|
import type { Terminal, ITheme } from '@xterm/xterm';
|
||||||
import type { IControlWindow } from '../controlBar';
|
import type { IControlWindow } from '../controlBar';
|
||||||
|
import type { AddonCollection } from 'types';
|
||||||
|
|
||||||
const xtermjsTheme: ITheme = {
|
const xtermjsTheme: ITheme = {
|
||||||
foreground: '#F8F8F8',
|
foreground: '#F8F8F8',
|
||||||
@@ -29,23 +31,23 @@ const xtermjsTheme: ITheme = {
|
|||||||
brightWhite: '#FFFFFF'
|
brightWhite: '#FFFFFF'
|
||||||
};
|
};
|
||||||
|
|
||||||
export class OptionsWindow implements IControlWindow {
|
export class OptionsWindow extends BaseWindow implements IControlWindow {
|
||||||
public readonly id = 'options';
|
public readonly id = 'options';
|
||||||
public readonly label = 'Options';
|
public readonly label = 'Options';
|
||||||
|
|
||||||
private _container: HTMLElement;
|
private _container: HTMLElement;
|
||||||
private _optionsContainer: HTMLElement;
|
private _optionsContainer: HTMLElement;
|
||||||
private _term: Terminal;
|
|
||||||
private _autoResize: boolean = true;
|
private _autoResize: boolean = true;
|
||||||
private _updateTerminalSize: () => void;
|
|
||||||
private _updateTerminalContainerBackground: () => void;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
updateTerminalSize: () => void,
|
terminal: Terminal,
|
||||||
updateTerminalContainerBackground: () => void
|
addons: AddonCollection,
|
||||||
|
private readonly _handlers: {
|
||||||
|
updateTerminalSize: () => void,
|
||||||
|
updateTerminalContainerBackground: () => void
|
||||||
|
},
|
||||||
) {
|
) {
|
||||||
this._updateTerminalSize = updateTerminalSize;
|
super(terminal, addons)
|
||||||
this._updateTerminalContainerBackground = updateTerminalContainerBackground;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public build(container: HTMLElement): void {
|
public build(container: HTMLElement): void {
|
||||||
@@ -64,9 +66,7 @@ export class OptionsWindow implements IControlWindow {
|
|||||||
container.appendChild(this._optionsContainer);
|
container.appendChild(this._optionsContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public initOptions(term: Terminal, addDomListener: (el: HTMLElement, type: string, handler: (...args: any[]) => any) => void): void {
|
public initOptions(addDomListener: (el: HTMLElement, type: string, handler: (...args: any[]) => any) => void): void {
|
||||||
this._term = term;
|
|
||||||
|
|
||||||
const blacklistedOptions = [
|
const blacklistedOptions = [
|
||||||
'cancelEvents',
|
'cancelEvents',
|
||||||
'convertEol',
|
'convertEol',
|
||||||
@@ -91,11 +91,11 @@ export class OptionsWindow implements IControlWindow {
|
|||||||
wordSeparator: null,
|
wordSeparator: null,
|
||||||
colsRows: null
|
colsRows: null
|
||||||
};
|
};
|
||||||
const options = Object.getOwnPropertyNames(term.options);
|
const options = Object.getOwnPropertyNames(this._terminal.options);
|
||||||
const booleanOptions: string[] = [];
|
const booleanOptions: string[] = [];
|
||||||
const numberOptions: string[] = [];
|
const numberOptions: string[] = [];
|
||||||
options.filter(o => blacklistedOptions.indexOf(o) === -1).forEach(o => {
|
options.filter(o => blacklistedOptions.indexOf(o) === -1).forEach(o => {
|
||||||
switch (typeof term.options[o]) {
|
switch (typeof this._terminal.options[o]) {
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
booleanOptions.push(o);
|
booleanOptions.push(o);
|
||||||
break;
|
break;
|
||||||
@@ -112,21 +112,21 @@ export class OptionsWindow implements IControlWindow {
|
|||||||
let html = '';
|
let html = '';
|
||||||
html += '<div class="option-group">';
|
html += '<div class="option-group">';
|
||||||
booleanOptions.forEach(o => {
|
booleanOptions.forEach(o => {
|
||||||
html += `<div class="option"><label><input id="opt-${o}" type="checkbox" ${term.options[o] ? 'checked' : ''}/> ${o}</label></div>`;
|
html += `<div class="option"><label><input id="opt-${o}" type="checkbox" ${this._terminal.options[o] ? 'checked' : ''}/> ${o}</label></div>`;
|
||||||
});
|
});
|
||||||
html += '</div><div class="option-group">';
|
html += '</div><div class="option-group">';
|
||||||
numberOptions.forEach(o => {
|
numberOptions.forEach(o => {
|
||||||
html += `<div class="option"><label>${o} <input id="opt-${o}" type="number" value="${term.options[o] ?? ''}" step="${o === 'lineHeight' || o === 'scrollSensitivity' ? '0.1' : '1'}"/></label></div>`;
|
html += `<div class="option"><label>${o} <input id="opt-${o}" type="number" value="${this._terminal.options[o] ?? ''}" step="${o === 'lineHeight' || o === 'scrollSensitivity' ? '0.1' : '1'}"/></label></div>`;
|
||||||
});
|
});
|
||||||
html += '</div><div class="option-group">';
|
html += '</div><div class="option-group">';
|
||||||
Object.keys(stringOptions).forEach(o => {
|
Object.keys(stringOptions).forEach(o => {
|
||||||
if (o === 'colsRows') {
|
if (o === 'colsRows') {
|
||||||
html += `<div class="option"><label>size (<var>cols</var><code>x</code><var>rows</var> or <code>auto</code>) <input id="opt-${o}" type="text" value="auto"/></label></div>`;
|
html += `<div class="option"><label>size (<var>cols</var><code>x</code><var>rows</var> or <code>auto</code>) <input id="opt-${o}" type="text" value="auto"/></label></div>`;
|
||||||
} else if (stringOptions[o]) {
|
} else if (stringOptions[o]) {
|
||||||
const selectedOption = o === 'theme' ? 'xtermjs' : term.options[o];
|
const selectedOption = o === 'theme' ? 'xtermjs' : this._terminal.options[o];
|
||||||
html += `<div class="option"><label>${o} <select id="opt-${o}">${stringOptions[o]!.map(v => `<option ${v === selectedOption ? 'selected' : ''}>${v}</option>`).join('')}</select></label></div>`;
|
html += `<div class="option"><label>${o} <select id="opt-${o}">${stringOptions[o]!.map(v => `<option ${v === selectedOption ? 'selected' : ''}>${v}</option>`).join('')}</select></label></div>`;
|
||||||
} else {
|
} else {
|
||||||
html += `<div class="option"><label>${o} <input id="opt-${o}" type="text" value="${term.options[o]}"/></label></div>`;
|
html += `<div class="option"><label>${o} <input id="opt-${o}" type="text" value="${this._terminal.options[o]}"/></label></div>`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
@@ -138,7 +138,7 @@ export class OptionsWindow implements IControlWindow {
|
|||||||
const input = document.getElementById(`opt-${o}`) as HTMLInputElement;
|
const input = document.getElementById(`opt-${o}`) as HTMLInputElement;
|
||||||
addDomListener(input, 'change', () => {
|
addDomListener(input, 'change', () => {
|
||||||
console.log('change', o, input.checked);
|
console.log('change', o, input.checked);
|
||||||
term.options[o] = input.checked;
|
this._terminal.options[o] = input.checked;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
numberOptions.forEach(o => {
|
numberOptions.forEach(o => {
|
||||||
@@ -146,16 +146,16 @@ export class OptionsWindow implements IControlWindow {
|
|||||||
addDomListener(input, 'change', () => {
|
addDomListener(input, 'change', () => {
|
||||||
console.log('change', o, input.value);
|
console.log('change', o, input.value);
|
||||||
if (o === 'lineHeight') {
|
if (o === 'lineHeight') {
|
||||||
term.options.lineHeight = parseFloat(input.value);
|
this._terminal.options.lineHeight = parseFloat(input.value);
|
||||||
} else if (o === 'scrollSensitivity') {
|
} else if (o === 'scrollSensitivity') {
|
||||||
term.options.scrollSensitivity = parseFloat(input.value);
|
this._terminal.options.scrollSensitivity = parseFloat(input.value);
|
||||||
} else if (o === 'scrollback') {
|
} else if (o === 'scrollback') {
|
||||||
term.options.scrollback = parseInt(input.value);
|
this._terminal.options.scrollback = parseInt(input.value);
|
||||||
setTimeout(() => this._updateTerminalSize(), 5);
|
setTimeout(() => this._handlers.updateTerminalSize(), 5);
|
||||||
} else {
|
} else {
|
||||||
term.options[o] = parseInt(input.value);
|
this._terminal.options[o] = parseInt(input.value);
|
||||||
}
|
}
|
||||||
this._updateTerminalSize();
|
this._handlers.updateTerminalSize();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
Object.keys(stringOptions).forEach(o => {
|
Object.keys(stringOptions).forEach(o => {
|
||||||
@@ -167,11 +167,11 @@ export class OptionsWindow implements IControlWindow {
|
|||||||
const m = input.value.match(/^([0-9]+)x([0-9]+)$/);
|
const m = input.value.match(/^([0-9]+)x([0-9]+)$/);
|
||||||
if (m) {
|
if (m) {
|
||||||
this._autoResize = false;
|
this._autoResize = false;
|
||||||
term.resize(parseInt(m[1]), parseInt(m[2]));
|
this._terminal.resize(parseInt(m[1]), parseInt(m[2]));
|
||||||
} else {
|
} else {
|
||||||
this._autoResize = true;
|
this._autoResize = true;
|
||||||
input.value = 'auto';
|
input.value = 'auto';
|
||||||
this._updateTerminalSize();
|
this._handlers.updateTerminalSize();
|
||||||
}
|
}
|
||||||
} else if (o === 'theme') {
|
} else if (o === 'theme') {
|
||||||
switch (input.value) {
|
switch (input.value) {
|
||||||
@@ -232,9 +232,9 @@ export class OptionsWindow implements IControlWindow {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
term.options[o] = value;
|
this._terminal.options[o] = value;
|
||||||
if (o === 'theme') {
|
if (o === 'theme') {
|
||||||
this._updateTerminalContainerBackground();
|
this._handlers.updateTerminalContainerBackground();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,9 +3,10 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { BaseWindow } from './baseWindow';
|
||||||
import type { IControlWindow } from '../controlBar';
|
import type { IControlWindow } from '../controlBar';
|
||||||
|
|
||||||
export class StyleWindow implements IControlWindow {
|
export class StyleWindow extends BaseWindow implements IControlWindow {
|
||||||
public readonly id = 'style';
|
public readonly id = 'style';
|
||||||
public readonly label = 'Style';
|
public readonly label = 'Style';
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,31 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { IControlWindow } from '../controlBar';
|
/// <reference path="../../../typings/xterm.d.ts"/>
|
||||||
|
|
||||||
export class TestWindow implements IControlWindow {
|
import { writeUnicodeTable } from 'unicodeTable';
|
||||||
|
import type { IControlWindow } from '../controlBar';
|
||||||
|
import { BaseWindow } from './baseWindow';
|
||||||
|
import type { IDisposable, Terminal } from '@xterm/xterm';
|
||||||
|
import type { AddonCollection } from 'types';
|
||||||
|
import type { IProgressState } from '@xterm/addon-progress';
|
||||||
|
import type { IImageAddonOptions } from '@xterm/addon-image';
|
||||||
|
|
||||||
|
export class TestWindow extends BaseWindow implements IControlWindow {
|
||||||
public readonly id = 'test';
|
public readonly id = 'test';
|
||||||
public readonly label = 'Test';
|
public readonly label = 'Test';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
terminal: Terminal,
|
||||||
|
addons: AddonCollection,
|
||||||
|
private readonly _handlers: {
|
||||||
|
disposeRecreateButtonHandler: () => void,
|
||||||
|
createNewWindowButtonHandler: () => void,
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
super(terminal, addons);
|
||||||
|
}
|
||||||
|
|
||||||
public build(container: HTMLElement): void {
|
public build(container: HTMLElement): void {
|
||||||
const heading = document.createElement('h3');
|
const heading = document.createElement('h3');
|
||||||
heading.textContent = 'Test';
|
heading.textContent = 'Test';
|
||||||
@@ -23,41 +42,41 @@ export class TestWindow implements IControlWindow {
|
|||||||
// Lifecycle section
|
// Lifecycle section
|
||||||
this._addDt(dl, 'Lifecycle');
|
this._addDt(dl, 'Lifecycle');
|
||||||
this._addDdWithCheckbox(dl, 'use-real-terminal', 'Use real terminal', 'This is used to real vs fake terminals', true);
|
this._addDdWithCheckbox(dl, 'use-real-terminal', 'Use real terminal', 'This is used to real vs fake terminals', true);
|
||||||
this._addDdWithButton(dl, 'dispose', 'Dispose terminal', 'This is used to testing memory leaks');
|
this._addDdWithButton(dl, 'dispose', 'Dispose terminal', 'This is used to testing memory leaks', () => this._handlers.disposeRecreateButtonHandler());
|
||||||
this._addDdWithButton(dl, 'create-new-window', 'Create terminal in new window', 'This is used to test rendering in other windows');
|
this._addDdWithButton(dl, 'create-new-window', 'Create terminal in new window', 'This is used to test rendering in other windows', () => this._handlers.createNewWindowButtonHandler());
|
||||||
|
|
||||||
// Performance section
|
// Performance section
|
||||||
this._addDt(dl, 'Performance');
|
this._addDt(dl, 'Performance');
|
||||||
this._addDdWithButton(dl, 'load-test', 'Load test', 'Write several MB of data to simulate a lot of data coming from the process');
|
this._addDdWithButton(dl, 'load-test', 'Load test', 'Write several MB of data to simulate a lot of data coming from the process', () => loadTest(this._terminal, this._addons));
|
||||||
this._addDdWithButton(dl, 'load-test-long-lines', 'Load test (long lines)', 'Write several MB of data with long lines to simulate a lot of data coming from the process');
|
this._addDdWithButton(dl, 'load-test-long-lines', 'Load test (long lines)', 'Write several MB of data with long lines to simulate a lot of data coming from the process', () => loadTestLongLines(this._terminal, this._addons));
|
||||||
this._addDdWithButton(dl, 'print-cjk', 'CJK Unified Ideographs', 'Prints the 20977 characters from the CJK Unified Ideographs unicode block');
|
this._addDdWithButton(dl, 'print-cjk', 'CJK Unified Ideographs', 'Prints the 20977 characters from the CJK Unified Ideographs unicode block', () => addCjk(this._terminal));
|
||||||
this._addDdWithButton(dl, 'print-cjk-sgr', 'CJK Unified Ideographs (random SGR)', 'Prints the 20977 characters from the CJK Unified Ideographs unicode block with randomized SGR attributes');
|
this._addDdWithButton(dl, 'print-cjk-sgr', 'CJK Unified Ideographs (random SGR)', 'Prints the 20977 characters from the CJK Unified Ideographs unicode block with randomized SGR attributes', () => addCjkRandomSgr(this._terminal));
|
||||||
|
|
||||||
// Styles section
|
// Styles section
|
||||||
this._addDt(dl, 'Styles');
|
this._addDt(dl, 'Styles');
|
||||||
this._addDdWithButton(dl, 'custom-glyph-alignment', 'Custom glyph alignment test', 'Write custom glyph alignment tests to the terminal');
|
this._addDdWithButton(dl, 'custom-glyph-alignment', 'Custom glyph alignment test', 'Write custom glyph alignment tests to the terminal', () => customGlyphAlignmentHandler(this._terminal));
|
||||||
this._addDdWithButton(dl, 'custom-glyph-ranges', 'Custom glyph ranges', 'Write custom glyph unicode range to the terminal');
|
this._addDdWithButton(dl, 'custom-glyph-ranges', 'Custom glyph ranges', 'Write custom glyph unicode range to the terminal', () => customGlyphRangesHandler(this._terminal));
|
||||||
this._addDdWithButton(dl, 'powerline-symbol-test', 'Powerline symbol test', 'Write powerline symbol characters to the terminal (\\ue0a0+)');
|
this._addDdWithButton(dl, 'powerline-symbol-test', 'Powerline symbol test', 'Write powerline symbol characters to the terminal (\\ue0a0+)', () => powerlineSymbolTest(this._terminal));
|
||||||
this._addDdWithButton(dl, 'underline-test', 'Underline test', 'Write text with Kitty\'s extended underline sequences');
|
this._addDdWithButton(dl, 'underline-test', 'Underline test', 'Write text with Kitty\'s extended underline sequences', () => underlineTest(this._terminal));
|
||||||
this._addDdWithButton(dl, 'sgr-test', 'SGR test', 'Write text with SGR attribute');
|
this._addDdWithButton(dl, 'sgr-test', 'SGR test', 'Write text with SGR attribute', () => sgrTest(this._terminal));
|
||||||
this._addDdWithButton(dl, 'ansi-colors', 'Ansi colors test', 'Write a wide range of ansi colors');
|
this._addDdWithButton(dl, 'ansi-colors', 'Ansi colors test', 'Write a wide range of ansi colors', () => ansiColorsTest(this._terminal));
|
||||||
this._addDdWithButton(dl, 'osc-hyperlinks', 'Ansi hyperlinks test', 'Write some OSC 8 hyperlinks');
|
this._addDdWithButton(dl, 'osc-hyperlinks', 'Ansi hyperlinks test', 'Write some OSC 8 hyperlinks', () => addAnsiHyperlink(this._terminal));
|
||||||
this._addDdWithButton(dl, 'bce', 'Colored Erase (BCE)', 'Test colored erase');
|
this._addDdWithButton(dl, 'bce', 'Colored Erase (BCE)', 'Test colored erase', () => coloredErase(this._terminal));
|
||||||
this._addDdWithButton(dl, 'add-grapheme-clusters', 'Grapheme clusters', 'Write grapheme cluster test strings');
|
this._addDdWithButton(dl, 'add-grapheme-clusters', 'Grapheme clusters', 'Write grapheme cluster test strings', () => addGraphemeClusters(this._terminal));
|
||||||
|
|
||||||
// Decorations section
|
// Decorations section
|
||||||
this._addDt(dl, 'Decorations');
|
this._addDt(dl, 'Decorations');
|
||||||
this._addDdWithButton(dl, 'add-decoration', 'Decoration', 'Add a decoration to the terminal');
|
this._addDdWithButton(dl, 'add-decoration', 'Decoration', 'Add a decoration to the terminal', () => addDecoration(this._terminal));
|
||||||
this._addDdWithButton(dl, 'add-overview-ruler', 'Add Overview Ruler', 'Add an overview ruler to the terminal');
|
this._addDdWithButton(dl, 'add-overview-ruler', 'Add Overview Ruler', 'Add an overview ruler to the terminal', () => addOverviewRuler(this._terminal));
|
||||||
this._addDdWithButton(dl, 'decoration-stress-test', 'Stress Test', 'Toggle between adding and removing a decoration to each line');
|
this._addDdWithButton(dl, 'decoration-stress-test', 'Stress Test', 'Toggle between adding and removing a decoration to each line', () => decorationStressTest(this._terminal));
|
||||||
|
|
||||||
// Ligatures Addon section
|
// Ligatures Addon section
|
||||||
this._addDt(dl, 'Ligatures Addon');
|
this._addDt(dl, 'Ligatures Addon');
|
||||||
this._addDdWithButton(dl, 'ligatures-test', 'Common ligatures', 'Write common ligatures sequences');
|
this._addDdWithButton(dl, 'ligatures-test', 'Common ligatures', 'Write common ligatures sequences', () => ligaturesTest(this._terminal));
|
||||||
|
|
||||||
// Weblinks Addon section
|
// Weblinks Addon section
|
||||||
this._addDt(dl, 'Weblinks Addon');
|
this._addDt(dl, 'Weblinks Addon');
|
||||||
this._addDdWithButton(dl, 'weblinks-test', 'Test URLs', 'Various url conditions from demo data, hover&click to test');
|
this._addDdWithButton(dl, 'weblinks-test', 'Test URLs', 'Various url conditions from demo data, hover&click to test', () => testWeblinks(this._terminal));
|
||||||
|
|
||||||
// Image Test section
|
// Image Test section
|
||||||
this._addDt(dl, 'Image Test');
|
this._addDt(dl, 'Image Test');
|
||||||
@@ -67,8 +86,8 @@ export class TestWindow implements IControlWindow {
|
|||||||
|
|
||||||
// Events Test section
|
// Events Test section
|
||||||
this._addDt(dl, 'Events Test');
|
this._addDt(dl, 'Events Test');
|
||||||
this._addDdWithButton(dl, 'event-focus', 'focus', '');
|
this._addDdWithButton(dl, 'event-focus', 'focus', '', () => this._terminal.focus());
|
||||||
this._addDdWithButton(dl, 'event-blur', 'blur', '');
|
this._addDdWithButton(dl, 'event-blur', 'blur', '', () => this._terminal.blur());
|
||||||
|
|
||||||
// Progress Addon section
|
// Progress Addon section
|
||||||
this._addDt(dl, 'Progress Addon');
|
this._addDt(dl, 'Progress Addon');
|
||||||
@@ -103,8 +122,9 @@ export class TestWindow implements IControlWindow {
|
|||||||
wrapper.appendChild(dl);
|
wrapper.appendChild(dl);
|
||||||
container.appendChild(wrapper);
|
container.appendChild(wrapper);
|
||||||
|
|
||||||
// Add progress bar styles
|
initProgress(this._terminal, this._addons);
|
||||||
this._addProgressStyles(container);
|
this._addProgressStyles(container);
|
||||||
|
initImageAddonExposed(this._terminal, this._addons);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addDt(dl: HTMLElement, text: string): void {
|
private _addDt(dl: HTMLElement, text: string): void {
|
||||||
@@ -113,7 +133,7 @@ export class TestWindow implements IControlWindow {
|
|||||||
dl.appendChild(dt);
|
dl.appendChild(dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addDdWithButton(dl: HTMLElement, id: string, label: string, title: string): void {
|
private _addDdWithButton(dl: HTMLElement, id: string, label: string, title: string, handler?: () => void): void {
|
||||||
const dd = document.createElement('dd');
|
const dd = document.createElement('dd');
|
||||||
const button = document.createElement('button');
|
const button = document.createElement('button');
|
||||||
button.id = id;
|
button.id = id;
|
||||||
@@ -123,6 +143,7 @@ export class TestWindow implements IControlWindow {
|
|||||||
}
|
}
|
||||||
dd.appendChild(button);
|
dd.appendChild(button);
|
||||||
dl.appendChild(dd);
|
dl.appendChild(dd);
|
||||||
|
button.addEventListener('click', handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addDdWithCheckbox(dl: HTMLElement, id: string, label: string, title: string, checked: boolean): void {
|
private _addDdWithCheckbox(dl: HTMLElement, id: string, label: string, title: string, checked: boolean): void {
|
||||||
@@ -175,3 +196,772 @@ export class TestWindow implements IControlWindow {
|
|||||||
container.appendChild(style);
|
container.appendChild(style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints the 20977 characters from the CJK Unified Ideographs unicode block.
|
||||||
|
*/
|
||||||
|
export function addCjk(term: Terminal): void {
|
||||||
|
term.write('\n\n\r');
|
||||||
|
for (let i = 0x4E00; i < 0x9FCC; i++) {
|
||||||
|
term.write(String.fromCharCode(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints the 20977 characters from the CJK Unified Ideographs unicode block with randomized styles.
|
||||||
|
*/
|
||||||
|
function addCjkRandomSgr(term: Terminal): void {
|
||||||
|
term.write('\n\n\r');
|
||||||
|
for (let i = 0x4E00; i < 0x9FCC; i++) {
|
||||||
|
term.write(`\x1b[${getRandomSgr()}m${String.fromCharCode(i)}\x1b[0m`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const randomSgrAttributes = [
|
||||||
|
'1', '2', '3', '4', '5', '6', '7', '9',
|
||||||
|
'21', '22', '23', '24', '25', '26', '27', '28', '29',
|
||||||
|
'30', '31', '32', '33', '34', '35', '36', '37', '38', '39',
|
||||||
|
'40', '41', '42', '43', '44', '45', '46', '47', '48', '49'
|
||||||
|
];
|
||||||
|
function getRandomSgr(): string {
|
||||||
|
return randomSgrAttributes[Math.floor(Math.random() * randomSgrAttributes.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
function powerlineSymbolTest(term: Terminal): void {
|
||||||
|
function s(char: string): string {
|
||||||
|
return `${char} \x1b[7m${char}\x1b[0m `;
|
||||||
|
}
|
||||||
|
term.write('\n\n\r');
|
||||||
|
term.writeln('Standard powerline symbols:');
|
||||||
|
term.writeln(' 0 1 2 3 4 5 6 7 8 9 A B C D E F');
|
||||||
|
term.writeln(`0xA_ ${s('\ue0a0')}${s('\ue0a1')}${s('\ue0a2')}`);
|
||||||
|
term.writeln(`0xB_ ${s('\ue0b0')}${s('\ue0b1')}${s('\ue0b2')}${s('\ue0b3')}`);
|
||||||
|
term.writeln('');
|
||||||
|
term.writeln(
|
||||||
|
`\x1b[7m` +
|
||||||
|
` inverse \ue0b1 \x1b[0;40m\ue0b0` +
|
||||||
|
` 0 \ue0b1 \x1b[30;41m\ue0b0\x1b[39m` +
|
||||||
|
` 1 \ue0b1 \x1b[31;42m\ue0b0\x1b[39m` +
|
||||||
|
` 2 \ue0b1 \x1b[32;43m\ue0b0\x1b[39m` +
|
||||||
|
` 3 \ue0b1 \x1b[33;44m\ue0b0\x1b[39m` +
|
||||||
|
` 4 \ue0b1 \x1b[34;45m\ue0b0\x1b[39m` +
|
||||||
|
` 5 \ue0b1 \x1b[35;46m\ue0b0\x1b[39m` +
|
||||||
|
` 6 \ue0b1 \x1b[36;47m\ue0b0\x1b[30m` +
|
||||||
|
` 7 \ue0b1 \x1b[37;49m\ue0b0\x1b[0m`
|
||||||
|
);
|
||||||
|
term.writeln('');
|
||||||
|
term.writeln(
|
||||||
|
`\x1b[7m` +
|
||||||
|
` inverse \ue0b3 \x1b[0;7;40m\ue0b2\x1b[27m` +
|
||||||
|
` 0 \ue0b3 \x1b[7;30;41m\ue0b2\x1b[27;39m` +
|
||||||
|
` 1 \ue0b3 \x1b[7;31;42m\ue0b2\x1b[27;39m` +
|
||||||
|
` 2 \ue0b3 \x1b[7;32;43m\ue0b2\x1b[27;39m` +
|
||||||
|
` 3 \ue0b3 \x1b[7;33;44m\ue0b2\x1b[27;39m` +
|
||||||
|
` 4 \ue0b3 \x1b[7;34;45m\ue0b2\x1b[27;39m` +
|
||||||
|
` 5 \ue0b3 \x1b[7;35;46m\ue0b2\x1b[27;39m` +
|
||||||
|
` 6 \ue0b3 \x1b[7;36;47m\ue0b2\x1b[27;30m` +
|
||||||
|
` 7 \ue0b3 \x1b[7;37;49m\ue0b2\x1b[0m`
|
||||||
|
);
|
||||||
|
term.writeln('');
|
||||||
|
term.writeln(
|
||||||
|
`\x1b[7m` +
|
||||||
|
` inverse \ue0b5 \x1b[0;40m\ue0b4` +
|
||||||
|
` 0 \ue0b5 \x1b[30;41m\ue0b4\x1b[39m` +
|
||||||
|
` 1 \ue0b5 \x1b[31;42m\ue0b4\x1b[39m` +
|
||||||
|
` 2 \ue0b5 \x1b[32;43m\ue0b4\x1b[39m` +
|
||||||
|
` 3 \ue0b5 \x1b[33;44m\ue0b4\x1b[39m` +
|
||||||
|
` 4 \ue0b5 \x1b[34;45m\ue0b4\x1b[39m` +
|
||||||
|
` 5 \ue0b5 \x1b[35;46m\ue0b4\x1b[39m` +
|
||||||
|
` 6 \ue0b5 \x1b[36;47m\ue0b4\x1b[30m` +
|
||||||
|
` 7 \ue0b5 \x1b[37;49m\ue0b4\x1b[0m`
|
||||||
|
);
|
||||||
|
term.writeln('');
|
||||||
|
term.writeln(
|
||||||
|
`\x1b[7m` +
|
||||||
|
` inverse \ue0b7 \x1b[0;7;40m\ue0b6\x1b[27m` +
|
||||||
|
` 0 \ue0b7 \x1b[7;30;41m\ue0b6\x1b[27;39m` +
|
||||||
|
` 1 \ue0b7 \x1b[7;31;42m\ue0b6\x1b[27;39m` +
|
||||||
|
` 2 \ue0b7 \x1b[7;32;43m\ue0b6\x1b[27;39m` +
|
||||||
|
` 3 \ue0b7 \x1b[7;33;44m\ue0b6\x1b[27;39m` +
|
||||||
|
` 4 \ue0b7 \x1b[7;34;45m\ue0b6\x1b[27;39m` +
|
||||||
|
` 5 \ue0b7 \x1b[7;35;46m\ue0b6\x1b[27;39m` +
|
||||||
|
` 6 \ue0b7 \x1b[7;36;47m\ue0b6\x1b[27;30m` +
|
||||||
|
` 7 \ue0b7 \x1b[7;37;49m\ue0b6\x1b[0m`
|
||||||
|
);
|
||||||
|
term.writeln('');
|
||||||
|
term.writeln('Powerline extra symbols:');
|
||||||
|
term.writeln(' 0 1 2 3 4 5 6 7 8 9 A B C D E F');
|
||||||
|
term.writeln(`0xA_ ${s('\ue0a3')}`);
|
||||||
|
term.writeln(`0xB_ ${s('\ue0b4')}${s('\ue0b5')}${s('\ue0b6')}${s('\ue0b7')}${s('\ue0b8')}${s('\ue0b9')}${s('\ue0ba')}${s('\ue0bb')}${s('\ue0bc')}${s('\ue0bd')}${s('\ue0be')}${s('\ue0bf')}`);
|
||||||
|
term.writeln(`0xC_ ${s('\ue0c0')}${s('\ue0c1')}${s('\ue0c2')}${s('\ue0c3')}${s('\ue0c4')}${s('\ue0c5')}${s('\ue0c6')}${s('\ue0c7')}${s('\ue0c8')}${s('\ue0c9')}${s('\ue0ca')}${s('\ue0cb')}${s('\ue0cc')}${s('\ue0cd')}${s('\ue0be')}${s('\ue0bf')}`);
|
||||||
|
term.writeln(`0xD_ ${s('\ue0d0')}${s('\ue0d1')}${s('\ue0d2')} ${s('\ue0d4')}`);
|
||||||
|
term.writeln('');
|
||||||
|
term.writeln('Sample of nerd fonts icons:');
|
||||||
|
term.writeln(' nf-linux-apple (\\uF302) \uf302');
|
||||||
|
term.writeln('nf-mdi-github_face (\\uFbd9) \ufbd9');
|
||||||
|
}
|
||||||
|
|
||||||
|
function underlineTest(term: Terminal): void {
|
||||||
|
function u(style: number): string {
|
||||||
|
return `\x1b[4:${style}m`;
|
||||||
|
}
|
||||||
|
function c(color: string): string {
|
||||||
|
return `\x1b[58:${color}m`;
|
||||||
|
}
|
||||||
|
term.write('\n\n\r');
|
||||||
|
term.writeln('Underline styles:');
|
||||||
|
term.writeln('');
|
||||||
|
function showSequence(id: number, name: string): string {
|
||||||
|
let alphabet = '';
|
||||||
|
for (let i = 97; i < 123; i++) {
|
||||||
|
alphabet += String.fromCharCode(i);
|
||||||
|
}
|
||||||
|
let numbers = '';
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
numbers += i.toString();
|
||||||
|
}
|
||||||
|
return `${u(id)}4:${id}m - ${name}\x1b[4:0m`.padEnd(33, ' ') + `${u(id)}${alphabet} ${numbers} 汉语 한국어 👽\x1b[4:0m`;
|
||||||
|
}
|
||||||
|
term.writeln(showSequence(0, 'No underline'));
|
||||||
|
term.writeln(showSequence(1, 'Straight'));
|
||||||
|
term.writeln(showSequence(2, 'Double'));
|
||||||
|
term.writeln(showSequence(3, 'Curly'));
|
||||||
|
term.writeln(showSequence(4, 'Dotted'));
|
||||||
|
term.writeln(showSequence(5, 'Dashed'));
|
||||||
|
term.writeln('');
|
||||||
|
term.writeln(`Underline colors (256 color mode):`);
|
||||||
|
term.writeln('');
|
||||||
|
for (let i = 0; i < 256; i++) {
|
||||||
|
term.write((i !== 0 ? '\x1b[0m, ' : '') + u(1 + i % 5) + c('5:' + i) + i);
|
||||||
|
}
|
||||||
|
term.writeln(`\x1b[0m\n\n\rUnderline colors (true color mode):`);
|
||||||
|
term.writeln('');
|
||||||
|
for (let i = 0; i < 80; i++) {
|
||||||
|
const v = Math.round(i / 79 * 255);
|
||||||
|
term.write(u(1) + c(`2:0:${v}:${v}:${v}`) + (i < 4 ? 'grey'[i] : ' '));
|
||||||
|
}
|
||||||
|
term.write('\n\r');
|
||||||
|
for (let i = 0; i < 80; i++) {
|
||||||
|
const v = Math.round(i / 79 * 255);
|
||||||
|
term.write(u(1) + c(`2:0:${v}:${0}:${0}`) + (i < 3 ? 'red'[i] : ' '));
|
||||||
|
}
|
||||||
|
term.write('\n\r');
|
||||||
|
for (let i = 0; i < 80; i++) {
|
||||||
|
const v = Math.round(i / 79 * 255);
|
||||||
|
term.write(u(1) + c(`2:0:${0}:${v}:${0}`) + (i < 5 ? 'green'[i] : ' '));
|
||||||
|
}
|
||||||
|
term.write('\n\r');
|
||||||
|
for (let i = 0; i < 80; i++) {
|
||||||
|
const v = Math.round(i / 79 * 255);
|
||||||
|
term.write(u(1) + c(`2:0:${0}:${0}:${v}`) + (i < 4 ? 'blue'[i] : ' '));
|
||||||
|
}
|
||||||
|
term.write('\x1b[0m\n\r');
|
||||||
|
}
|
||||||
|
|
||||||
|
function customGlyphAlignmentHandler(term: Terminal): void {
|
||||||
|
term.write('\n\r');
|
||||||
|
term.write('\n\r');
|
||||||
|
term.write('Box styles: ┎┰┒┍┯┑╓╥╖╒╤╕ ┏┳┓┌┲┓┌┬┐┏┱┐\n\r');
|
||||||
|
term.write('┌─┬─┐ ┏━┳━┓ ╔═╦═╗ ┠╂┨┝┿┥╟╫╢╞╪╡ ┡╇┩├╊┫┢╈┪┣╉┤\n\r');
|
||||||
|
term.write('│ │ │ ┃ ┃ ┃ ║ ║ ║ ┖┸┚┕┷┙╙╨╜╘╧╛ └┴┘└┺┛┗┻┛┗┹┘\n\r');
|
||||||
|
term.write('├─┼─┤ ┣━╋━┫ ╠═╬═╣ ┏┱┐┌┲┓┌┬┐┌┬┐ ┏┳┓┌┮┓┌┬┐┏┭┐\n\r');
|
||||||
|
term.write('│ │ │ ┃ ┃ ┃ ║ ║ ║ ┡╃┤├╄┩├╆┪┢╅┤ ┞╀┦├┾┫┟╁┧┣┽┤\n\r');
|
||||||
|
term.write('└─┴─┘ ┗━┻━┛ ╚═╩═╝ └┴┘└┴┘└┺┛┗┹┘ └┴┘└┶┛┗┻┛┗┵┘\n\r');
|
||||||
|
term.write('\n\r');
|
||||||
|
|
||||||
|
term.write('Other:\n\r');
|
||||||
|
term.write('╭─╮ ╲ ╱ ╷╻╎╏┆┇┊┋ ╺╾╴ ╌╌╌ ┄┄┄ ┈┈┈\n\r');
|
||||||
|
term.write('│ │ ╳ ╽╿╎╏┆┇┊┋ ╶╼╸ ╍╍╍ ┅┅┅ ┉┉┉\n\r');
|
||||||
|
term.write('╰─╯ ╱ ╲ ╹╵╎╏┆┇┊┋\n\r');
|
||||||
|
term.write('\n\r');
|
||||||
|
|
||||||
|
term.write('Box drawing alignment tests:\x1b[31m █\n\r');
|
||||||
|
term.write(' ▉\n\r');
|
||||||
|
term.write(' ╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳\n\r');
|
||||||
|
term.write(' ║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳\n\r');
|
||||||
|
term.write(' ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳\n\r');
|
||||||
|
term.write(' ╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳\n\r');
|
||||||
|
term.write(' ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎\n\r');
|
||||||
|
term.write(' ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏\n\r');
|
||||||
|
term.write(' ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█\n\r');
|
||||||
|
term.write('\x1b[0mBox drawing alignment tests:\x1b[32m █\n\r');
|
||||||
|
term.write(' ▉\n\r');
|
||||||
|
term.write(' ╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳\n\r');
|
||||||
|
term.write(' ║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳\n\r');
|
||||||
|
term.write(' ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳\n\r');
|
||||||
|
term.write(' ╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳\n\r');
|
||||||
|
term.write(' ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎\n\r');
|
||||||
|
term.write(' ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏\n\r');
|
||||||
|
term.write(' ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█\n\r');
|
||||||
|
|
||||||
|
term.write('\x1b[0mSmooth mosaic terminal graphic characters alignment tests:\x1b[33m\n\r');
|
||||||
|
term.write(' 🭇🬼 🭈🬽 🭉🬾 🭊🬿 🭋🭀 🭁🭌 🭂🭍 🭃🭎 🭄🭏 🭅🭐 🭆🭑 🭨🭪 🭩 🭯 🭮🭬\n\r');
|
||||||
|
term.write(' 🭢🭗 🭣🭘 🭤🭙 🭥🭚 🭦🭛 🭒🭝 🭓🭞 🭔🭟 🭕🭠 🭖🭡 🭧🭜 🭫 🭭\n\r');
|
||||||
|
term.write(' 🭇🬼 🭉🬾 🭋🭀\n\r');
|
||||||
|
term.write(' 🭊🭁🭌🬿 🭈🭆🭂🭍🭑🬽 🭇🭄🭏🬼 🭃🭎 🭅🭐 🭨🭪\n\r');
|
||||||
|
term.write(' 🭥🭒🭝🭚 🭣🭧🭓🭞🭜🭘 🭢🭕🭠🭗 🭔🭟 🭖🭡 🭪🭨\n\r');
|
||||||
|
term.write(' 🭢🭗 🭤🭙 🭦🭛\n\r');
|
||||||
|
|
||||||
|
term.write('\x1b[0mCharacter cell diagonals (1FBA0-1FBAE) alignment tests:\x1b[34m\n\r');
|
||||||
|
term.write(' \u{1FBA3}\u{1FBA7}\u{1FBA2} \u{1FBA3}\u{1FBA8}\u{1FBA0} \u{1FBAD}\u{1FBA2} \u{1FBA3}\u{1FBAC} \u{1FBAE}\n\r');
|
||||||
|
term.write(' \u{1FBA3}\u{1FBA0} \u{1FBA1}\u{1FBA2} \u{1FBA1}\u{1FBA9}\u{1FBA2} \u{1FBA1}\u{1FBAA} \u{1FBAB}\u{1FBA0}\n\r');
|
||||||
|
term.write(' \u{1FBA4} \u{1FBA5}\n\r');
|
||||||
|
term.write(' \u{1FBA1}\u{1FBA2} \u{1FBA3}\u{1FBA0}\n\r');
|
||||||
|
term.write(' \u{1FBA1}\u{1FBA6}\u{1FBA0}\n\r');
|
||||||
|
|
||||||
|
term.write('\x1b[0mCharacter cell diagonals (1FBD0-1FBDF) alignment tests:\x1b[34m\n\r');
|
||||||
|
term.write(' \u{1FBD6}\u{1FBD4} \u{1FBD0}\u{1FBD1}\u{1FBD2}\u{1FBD3} \u{1FBDA} \u{1FBD9}\u{1FBDB} \u{1FBDE} \u{1FBDD}\u{1FBDF}\n\r');
|
||||||
|
term.write(' \u{1FBD7}\u{1FBD5} \u{1FBD2}\u{1FBD3}\u{1FBD0}\u{1FBD1} \u{1FBD8} \u{1FBDC}\n\r');
|
||||||
|
term.write(' \u{1FBD4}\u{1FBD6}\n\r');
|
||||||
|
term.write(' \u{1FBD5}\u{1FBD7}\n\r');
|
||||||
|
term.write('');
|
||||||
|
|
||||||
|
term.write('\x1b[0mComposite terminal graphics characters:\x1b[35m\n\r');
|
||||||
|
term.write('\u{1FBB2}\u{1FBB3} \u{1FBB9}\u{1FBBA} \u{1FBC1}\u{1FBC2}\u{1FBC3}\n\r');
|
||||||
|
|
||||||
|
term.write('\x1b[0mFill tests:\x1b[36m\n\r');
|
||||||
|
const fillChars = ['\u{2591}', '\u{2592}', '\u{2593}', '\u{1FB8C}', '\u{1FB8D}', '\u{1FB8E}', '\u{1FB8F}', '\u{1FB90}', '\u{1FB91}', '\u{1FB92}', '\u{1FB94}', '\u{1FB95}', '\u{1FB96}', '\u{1FB97}', '\u{1FB98}', '\u{1FB99}'];
|
||||||
|
while (fillChars.length > 0) {
|
||||||
|
const batch = fillChars.splice(0, 10);
|
||||||
|
for (const fillChar of batch) {
|
||||||
|
term.write(`${fillChar.codePointAt(0).toString(16).toUpperCase().padEnd(5, ' ')} `);
|
||||||
|
}
|
||||||
|
term.write('\n\r');
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
for (const fillChar of batch) {
|
||||||
|
term.write(fillChar.repeat(5));
|
||||||
|
term.write(' ');
|
||||||
|
}
|
||||||
|
term.write('\n\r');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
term.write('\x1b[0mPowerline alignment tests:\n\r');
|
||||||
|
const powerlineLeftChars = ['\u{E0B2}', '\u{E0B3}', '\u{E0B6}', '\u{E0B7}', '\u{E0BA}', '\u{E0BB}', '\u{E0BE}', '\u{E0BF}', '\u{E0C2}', '\u{E0C3}', '\u{E0C5}', '\u{E0C7}', '\u{E0CA}', '\u{E0D4}'];
|
||||||
|
const powerlineRightChars = ['\u{E0B0}', '\u{E0B1}', '\u{E0B4}', '\u{E0B5}', '\u{E0B8}', '\u{E0B9}', '\u{E0BC}', '\u{E0BD}', '\u{E0C0}', '\u{E0C1}', '\u{E0C4}', '\u{E0C6}', '\u{E0C8}', '\u{E0D2}', '\u{E0CC}', '\u{E0CD}', '\u{E0CE}', '\u{E0CF}', '\u{E0D0}', '\u{E0D1}'];
|
||||||
|
for (const char of powerlineLeftChars) {
|
||||||
|
term.write(`\x1b[31m${char}\x1b[0;41m \x1b[0m `);
|
||||||
|
}
|
||||||
|
term.write('\n\r');
|
||||||
|
for (const char of powerlineRightChars) {
|
||||||
|
term.write(`\x1b[41m \x1b[0;31m${char}\x1b[0m `);
|
||||||
|
}
|
||||||
|
term.write('\n\r');
|
||||||
|
|
||||||
|
term.write('\x1b[0mGit Branch Symbols alignment tests:\x1b[32m\n\r');
|
||||||
|
term.write(' \u{F5F7}\u{F5F7} \u{F5EE}\u{F5EF} \u{F5F6}\u{F5F7} \u{F5D6}\u{F5D0}\u{F5D7} \u{F5FC}\u{F5FC}\u{F5FE} \u{F5FD}\u{F609}\u{F5FF}\n\r');
|
||||||
|
term.write(' \u{F5DA}\u{F5DD} \u{F5F0}\u{F5F4}\u{F5F2} \u{F5FA}\u{F5FB} \u{F5D1} \u{F5D1} \u{F604}\u{F60C}\u{F606} \u{F605}\u{F60D}\u{F607}\n\r');
|
||||||
|
term.write(' \u{F5DB}\u{F5DE} \u{F5F1}\u{F5F5}\u{F5F3} \u{F5F8}\u{F5F9} \u{F5D8}\u{F5D0}\u{F5D9} \u{F600}\u{F60A}\u{F602} \u{F601}\u{F60B}\u{F603}\n\r');
|
||||||
|
term.write(' \u{F5DC}\u{F5DF} \u{F5F7}\u{F5F7}\u{F5F7}\u{F5F7}\u{F5F7}\u{F5F7}\u{F5F7}\n\r');
|
||||||
|
term.write('\u{F5F1}\u{F5E6}\u{F5E7}\u{F5F3} \u{F5D3}\u{F5D0}\u{F5E0}\u{F5E1}\u{F5E2}\u{F5E3}\u{F5E4}\u{F5E5}\u{F5E8}\u{F5E9}\u{F5EC}\u{F5ED}\u{F5D2}\n\r');
|
||||||
|
term.write('\u{F5F1}\u{F5EA}\u{F5EB}\u{F5F3} \u{F5F9}\u{F5F9}\u{F5F9} \u{F5F9}\u{F5F9}\u{F5F9}\u{F5F9}\n\r');
|
||||||
|
term.write(' \u{F5F9}\u{F5F9} \n\r');
|
||||||
|
term.write('\x1b[0m');
|
||||||
|
term.write('\n\r');
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function customGlyphRangesHandler(term: Terminal): void {
|
||||||
|
// Box Drawing
|
||||||
|
// 2500-257F
|
||||||
|
// https://www.unicode.org/charts/PDF/U2500.pdf
|
||||||
|
writeUnicodeTable(term, 'Box Drawing', 0x2500, 0x257F, [
|
||||||
|
['Light and heavy solid lines', 0x2500, 0x2503],
|
||||||
|
['Light and heavy dashed lines', 0x2504, 0x250B],
|
||||||
|
['Light and heavy line box components', 0x250C, 0x254B],
|
||||||
|
['Light and heavy dashed lines', 0x254C, 0x254F],
|
||||||
|
['Double lines', 0x2550, 0x2551],
|
||||||
|
['Light and double line box components', 0x2552, 0x256C],
|
||||||
|
['Character cell arcs', 0x256D, 0x2570],
|
||||||
|
['Character cell diagonals', 0x2571, 0x2573],
|
||||||
|
['Light and heavy half lines', 0x2574, 0x257B],
|
||||||
|
['Mixed light and heavy lines', 0x257C, 0x257F],
|
||||||
|
]);
|
||||||
|
// Box Elements
|
||||||
|
// 2580-259F
|
||||||
|
// https://www.unicode.org/charts/PDF/U2580.pdf
|
||||||
|
writeUnicodeTable(term, 'Box Elements', 0x2580, 0x259F, [
|
||||||
|
['Block elements', 0x2580, 0x2590],
|
||||||
|
['Shade characters', 0x2591, 0x2593],
|
||||||
|
['Block elements', 0x2594, 0x2595],
|
||||||
|
['Terminal graphic characters', 0x2596, 0x259F],
|
||||||
|
]);
|
||||||
|
// Braille Patterns
|
||||||
|
// 2800-28FF
|
||||||
|
// https://www.unicode.org/charts/PDF/U2800.pdf
|
||||||
|
writeUnicodeTable(term, 'Braille patterns', 0x2800, 0x28FF, [
|
||||||
|
['Braille patterns', 0x2800, 0x28FF],
|
||||||
|
]);
|
||||||
|
// Powerline Symbols
|
||||||
|
// Range: E0A0–E0D4
|
||||||
|
// https://github.com/ryanoasis/nerd-fonts
|
||||||
|
writeUnicodeTable(term, 'Powerline Symbols', 0xE0A0, 0xE0D4, [
|
||||||
|
['Powerline symbols', 0xE0A0, 0xE0B3, [0xE0A4, 0xE0A5, 0xE0A6, 0xE0A7, 0xE0A8, 0xE0A9, 0xE0AA, 0xE0AB, 0xE0AC, 0xE0AD, 0xE0AE, 0xE0AF]],
|
||||||
|
['Powerline extra symbols', 0xE0B4, 0xE0D4, [0xE0C9, 0xE0CB, 0xE0D3]],
|
||||||
|
]);
|
||||||
|
// Git Branch Symbols
|
||||||
|
// F5D0-F60D
|
||||||
|
// https://github.com/xtermjs/xterm.js/issues/5477
|
||||||
|
writeUnicodeTable(term, 'Git Branch Symbols', 0xF5D0, 0xF5FB, [
|
||||||
|
['Straight lines', 0xF5D0, 0xF5D5],
|
||||||
|
['Curved lines', 0xF5D6, 0xF5D9],
|
||||||
|
['Branching lines', 0xF5DA, 0xF5ED],
|
||||||
|
['Nodes', 0xF5EE, 0xF5FB],
|
||||||
|
['Extended nodes', 0xF5FC, 0xF60D],
|
||||||
|
]);
|
||||||
|
// Symbols for Legacy Computing
|
||||||
|
// Range: 1FB00–1FBFF
|
||||||
|
// https://www.unicode.org/charts/PDF/U1FB00.pdf
|
||||||
|
writeUnicodeTable(term, 'Symbols for Legacy Computing', 0x1FB00, 0x1FBFF, [
|
||||||
|
['Block mosaic terminal graphic characters (Sextants)', 0x1FB00, 0x1FB3B],
|
||||||
|
['Smooth mosaic terminal graphic characters', 0x1FB3C, 0x1FB6F],
|
||||||
|
['Block elements', 0x1FB70, 0x1FB80],
|
||||||
|
['Window title bar', 0x1FB81, 0x1FB81],
|
||||||
|
['Block elements', 0x1FB82, 0x1FB8B],
|
||||||
|
['Rectangular shade characters', 0x1FB8C, 0x1FB94, [0x1FB93]],
|
||||||
|
['Fill characters', 0x1FB95, 0x1FB97],
|
||||||
|
['Diagonal fill characters', 0x1FB98, 0x1FB99],
|
||||||
|
['Smooth mosaic terminal graphic characters', 0x1FB9A, 0x1FB9B],
|
||||||
|
['Triangular shade characters', 0x1FB9C, 0x1FB9F],
|
||||||
|
['Character cell diagonals', 0x1FBA0, 0x1FBAE],
|
||||||
|
['Light solid line with stroke', 0x1FBAF, 0x1FBAF],
|
||||||
|
['Terminal graphic characters', 0x1FBB0, 0x1FBB3],
|
||||||
|
['Arrows', 0x1FBB4, 0x1FBB8],
|
||||||
|
['Terminal graphic characters', 0x1FBB9, 0x1FBBC],
|
||||||
|
['Negative terminal graphic characters', 0x1FBBD, 0x1FBBF],
|
||||||
|
['Terminal graphic characters', 0x1FBC0, 0x1FBCA],
|
||||||
|
['Terminal graphic characters', 0x1FBCB, 0x1FBCD],
|
||||||
|
['Block elements', 0x1FBCE, 0x1FBCF],
|
||||||
|
['Character cell diagonals', 0x1FBD0, 0x1FBDF],
|
||||||
|
['Geometrics shapes', 0x1FBE0, 0x1FBEF],
|
||||||
|
['Segmented digits', 0x1FBF0, 0x1FBF9],
|
||||||
|
['Terminal graphic character', 0x1FBFA, 0x1FBFA],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ansiColorsTest(term: Terminal): void {
|
||||||
|
term.writeln(`\x1b[0m\n\n\rStandard colors: Bright colors:`);
|
||||||
|
for (let i = 0; i < 16; i++) {
|
||||||
|
term.write(`\x1b[48;5;${i}m ${i.toString().padEnd(2, ' ').padStart(3, ' ')} \x1b[0m`);
|
||||||
|
}
|
||||||
|
|
||||||
|
term.writeln(`\x1b[0m\n\n\rColors 17-231 from 256 palette:`);
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
const startId = 16 + i * 36;
|
||||||
|
const endId = 16 + (i + 1) * 36 - 1;
|
||||||
|
term.write(`${startId.toString().padStart(3, ' ')}-${endId.toString().padStart(3, ' ')} `);
|
||||||
|
for (let j = 0; j < 36; j++) {
|
||||||
|
const id = 16 + i * 36 + j;
|
||||||
|
term.write(`\x1b[48;5;${id}m${(id % 10).toString().padStart(2, ' ')}\x1b[0m`);
|
||||||
|
}
|
||||||
|
term.write(`\r\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
term.writeln(`\x1b[0m\n\rGreyscale from 256 palette:`);
|
||||||
|
term.write('232-255 ');
|
||||||
|
for (let i = 232; i < 256; i++) {
|
||||||
|
term.write(`\x1b[48;5;${i}m ${(i % 10)} \x1b[0m`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeTestString(): string {
|
||||||
|
let alphabet = '';
|
||||||
|
for (let i = 97; i < 123; i++) {
|
||||||
|
alphabet += String.fromCharCode(i);
|
||||||
|
}
|
||||||
|
let numbers = '';
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
numbers += i.toString();
|
||||||
|
}
|
||||||
|
return `${alphabet} ${numbers} 汉语 한국어 👽`;
|
||||||
|
}
|
||||||
|
const testString = writeTestString();
|
||||||
|
|
||||||
|
function sgrTest(term: Terminal): void {
|
||||||
|
term.write('\n\n\r');
|
||||||
|
term.writeln(`Character Attributes (SGR, Select Graphic Rendition)`);
|
||||||
|
const entries: { ps: number, name: string }[] = [
|
||||||
|
{ ps: 0, name: 'Normal' },
|
||||||
|
{ ps: 1, name: 'Bold' },
|
||||||
|
{ ps: 2, name: 'Faint/dim' },
|
||||||
|
{ ps: 3, name: 'Italicized' },
|
||||||
|
{ ps: 4, name: 'Underlined' },
|
||||||
|
{ ps: 5, name: 'Blink' },
|
||||||
|
{ ps: 7, name: 'Inverse' },
|
||||||
|
{ ps: 8, name: 'Invisible' },
|
||||||
|
{ ps: 9, name: 'Crossed-out characters' },
|
||||||
|
{ ps: 21, name: 'Doubly-underlined' },
|
||||||
|
{ ps: 22, name: 'Normal' },
|
||||||
|
{ ps: 23, name: 'Not italicized' },
|
||||||
|
{ ps: 24, name: 'Not underlined' },
|
||||||
|
{ ps: 25, name: 'Steady (not blink)' },
|
||||||
|
{ ps: 27, name: 'Positive (not inverse)' },
|
||||||
|
{ ps: 28, name: 'Visible (not hidden)' },
|
||||||
|
{ ps: 29, name: 'Not crossed-out' },
|
||||||
|
{ ps: 30, name: 'Foreground Black' },
|
||||||
|
{ ps: 31, name: 'Foreground Red' },
|
||||||
|
{ ps: 32, name: 'Foreground Green' },
|
||||||
|
{ ps: 33, name: 'Foreground Yellow' },
|
||||||
|
{ ps: 34, name: 'Foreground Blue' },
|
||||||
|
{ ps: 35, name: 'Foreground Magenta' },
|
||||||
|
{ ps: 36, name: 'Foreground Cyan' },
|
||||||
|
{ ps: 37, name: 'Foreground White' },
|
||||||
|
{ ps: 39, name: 'Foreground default' },
|
||||||
|
{ ps: 40, name: 'Background Black' },
|
||||||
|
{ ps: 41, name: 'Background Red' },
|
||||||
|
{ ps: 42, name: 'Background Green' },
|
||||||
|
{ ps: 43, name: 'Background Yellow' },
|
||||||
|
{ ps: 44, name: 'Background Blue' },
|
||||||
|
{ ps: 45, name: 'Background Magenta' },
|
||||||
|
{ ps: 46, name: 'Background Cyan' },
|
||||||
|
{ ps: 47, name: 'Background White' },
|
||||||
|
{ ps: 49, name: 'Background default' },
|
||||||
|
{ ps: 53, name: 'Overlined' },
|
||||||
|
{ ps: 55, name: 'Not overlined' }
|
||||||
|
];
|
||||||
|
const maxNameLength = entries.reduce<number>((p, c) => Math.max(c.name.length, p), 0);
|
||||||
|
for (const e of entries) {
|
||||||
|
term.writeln(`\x1b[0m\x1b[${e.ps}m ${e.ps.toString().padEnd(2, ' ')} ${e.name.padEnd(maxNameLength, ' ')} - ${testString}\x1b[0m`);
|
||||||
|
}
|
||||||
|
const entriesByPs: Map<number, string> = new Map();
|
||||||
|
for (const e of entries) {
|
||||||
|
entriesByPs.set(e.ps, e.name);
|
||||||
|
}
|
||||||
|
const comboEntries: { ps: number[] }[] = [
|
||||||
|
{ ps: [1, 2, 3, 4, 5, 6, 7, 9] },
|
||||||
|
{ ps: [2, 41] },
|
||||||
|
{ ps: [4, 53] }
|
||||||
|
];
|
||||||
|
term.write('\n\n\r');
|
||||||
|
term.writeln(`Combinations`);
|
||||||
|
for (const e of comboEntries) {
|
||||||
|
const name = e.ps.map(e => entriesByPs.get(e)).join(', ');
|
||||||
|
term.writeln(`\x1b[0m\x1b[${e.ps.join(';')}m ${name}\n\r${testString}\x1b[0m`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addAnsiHyperlink(term: Terminal): void {
|
||||||
|
term.write('\n\n\r');
|
||||||
|
term.writeln(`Regular link with no id:`);
|
||||||
|
term.writeln('\x1b]8;;https://github.com\x07GitHub\x1b]8;;\x07');
|
||||||
|
term.writeln('\x1b]8;;https://xtermjs.org\x07https://xtermjs.org\x1b]8;;\x07\x1b[C<- null cell');
|
||||||
|
term.writeln(`\nAdjacent links:`);
|
||||||
|
term.writeln('\x1b]8;;https://github.com\x07GitHub\x1b]8;;https://xtermjs.org\x07\x1b[32mxterm.js\x1b[0m\x1b]8;;\x07');
|
||||||
|
term.writeln(`\nShared ID link (underline should be shared):`);
|
||||||
|
term.writeln('╔════╗');
|
||||||
|
term.writeln('║\x1b]8;id=testid;https://github.com\x07GitH\x1b]8;;\x07║');
|
||||||
|
term.writeln('║\x1b]8;id=testid;https://github.com\x07ub\x1b]8;;\x07 ║');
|
||||||
|
term.writeln('╚════╝');
|
||||||
|
term.writeln(`\nWrapped link with no ID (not necessarily meant to share underline):`);
|
||||||
|
term.writeln('╔════╗');
|
||||||
|
term.writeln('║ ║');
|
||||||
|
term.writeln('║ ║');
|
||||||
|
term.writeln('╚════╝');
|
||||||
|
term.write('\x1b[3A\x1b[1C\x1b]8;;https://xtermjs.org\x07xter\x1b[B\x1b[4Dm.js\x1b]8;;\x07\x1b[2B\x1b[5D');
|
||||||
|
}
|
||||||
|
|
||||||
|
function addGraphemeClusters(term: Terminal): void {
|
||||||
|
term.write('\n\n\r');
|
||||||
|
term.writeln('🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣 [Simple emoji v6: 10 cells, v15: 20 cells]');
|
||||||
|
term.writeln('\u{1F476}\u{1F3FF}\u{1F476} [baby with emoji modifier fitzpatrick type-6; baby]');
|
||||||
|
term.writeln('\u{1F469}\u200d\u{1f469}\u200d\u{1f466} [woman+zwj+woman+zwj+boy]');
|
||||||
|
term.writeln('\u{1F64B}\u{1F64B}\u{200D}\u{2642}\u{FE0F} [person/man raising hand]');
|
||||||
|
term.writeln('\u{1F3CB}\u{FE0F}=\u{1F3CB}\u{1F3FE}\u{200D}\u{2640}\u{FE0F} [person lifting weights emoji; woman lighting weights, medium dark]');
|
||||||
|
term.writeln('\u{1F469}\u{1F469}\u{200D}\u{1F393}\u{1F468}\u{1F3FF}\u{200D}\u{1F393} [woman; woman student; man student dark]');
|
||||||
|
term.writeln('\u{1f1f3}\u{1f1f4}_ [REGIONAL INDICATOR SYMBOL LETTER N and RI O]');
|
||||||
|
term.writeln('\u{1f1f3}_\u{1f1f4} {RI N; underscore; RI O]');
|
||||||
|
term.writeln('\u0061\u0301 [letter a with acute accent]');
|
||||||
|
term.writeln('\u1100\u1161\u11A8=\u1100\u1161= [Korean Jamo]');
|
||||||
|
term.writeln('\uAC00=\uD685= [Hangul syllables (pre-composed)]');
|
||||||
|
term.writeln('(\u26b0\ufe0e) [coffin with text_presentation]');
|
||||||
|
term.writeln('(\u26b0\ufe0f) [coffin with Emoji_presentation]');
|
||||||
|
term.writeln('<E\u0301\ufe0fg\ufe0fa\ufe0fl\ufe0fi\ufe0f\ufe0ft\ufe0fe\u0301\ufe0f> [Égalité (using separate acute) emoij_presentation]');
|
||||||
|
}
|
||||||
|
|
||||||
|
function coloredErase(term: Terminal): void {
|
||||||
|
const sp5 = ' ';
|
||||||
|
const data = `
|
||||||
|
Test BG-colored Erase (BCE):
|
||||||
|
The color block in the following lines should look identical.
|
||||||
|
For newly created rows at the bottom the last color should be applied
|
||||||
|
for all cells to the right.
|
||||||
|
|
||||||
|
def 41 42 43 44 45 46 47\x1b[47m
|
||||||
|
\x1b[m${sp5}\x1b[41m${sp5}\x1b[42m${sp5}\x1b[43m${sp5}\x1b[44m${sp5}\x1b[45m${sp5}\x1b[46m${sp5}\x1b[47m${sp5}
|
||||||
|
\x1b[m\x1b[5X\x1b[41m\x1b[5C\x1b[5X\x1b[42m\x1b[5C\x1b[5X\x1b[43m\x1b[5C\x1b[5X\x1b[44m\x1b[5C\x1b[5X\x1b[45m\x1b[5C\x1b[5X\x1b[46m\x1b[5C\x1b[5X\x1b[47m\x1b[5C\x1b[5X\x1b[m
|
||||||
|
`;
|
||||||
|
term.write(data.split('\n').join('\r\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function ligaturesTest(term: Terminal): void {
|
||||||
|
term.write([
|
||||||
|
'',
|
||||||
|
'-<< -< -<- <-- <--- <<- <- -> ->> --> ---> ->- >- >>-',
|
||||||
|
'=<< =< =<= <== <=== <<= <= => =>> ==> ===> =>= >= >>=',
|
||||||
|
'<-> <--> <---> <----> <=> <==> <===> <====> :: ::: __',
|
||||||
|
'<~~ </ </> /> ~~> == != /= ~= <> === !== !=== =/= =!=',
|
||||||
|
'<: := *= *+ <* <*> *> <| <|> |> <. <.> .> +* =* =: :>',
|
||||||
|
'(* *) /* */ [| |] {| |} ++ +++ \/ /\ |- -| <!-- <!---',
|
||||||
|
'==== ===== ====== ======= ======== =========',
|
||||||
|
'---- ----- ------ ------- -------- ---------'
|
||||||
|
].join('\r\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function testWeblinks(term: Terminal): void {
|
||||||
|
const linkExamples = `
|
||||||
|
aaa http://example.com aaa http://example.com aaa
|
||||||
|
¥¥¥ http://example.com aaa http://example.com aaa
|
||||||
|
aaa http://example.com ¥¥¥ http://example.com aaa
|
||||||
|
¥¥¥ http://example.com ¥¥¥ http://example.com aaa
|
||||||
|
aaa https://ko.wikipedia.org/wiki/위키백과:대문 aaa https://ko.wikipedia.org/wiki/위키백과:대문 aaa
|
||||||
|
¥¥¥ https://ko.wikipedia.org/wiki/위키백과:대문 aaa https://ko.wikipedia.org/wiki/위키백과:대문 ¥¥¥
|
||||||
|
aaa http://test:password@example.com/some_path aaa
|
||||||
|
brackets enclosed:
|
||||||
|
aaa [http://example.de] aaa
|
||||||
|
aaa (http://example.de) aaa
|
||||||
|
aaa <http://example.de> aaa
|
||||||
|
aaa {http://example.de} aaa
|
||||||
|
ipv6 https://[::1]/with/some?vars=and&a#hash aaa
|
||||||
|
stop at final '.': This is a sentence with an url to http://example.com.
|
||||||
|
stop at final '?': Is this the right url http://example.com/?
|
||||||
|
stop at final '?': Maybe this one http://example.com/with?arguments=false?
|
||||||
|
`;
|
||||||
|
term.write(linkExamples.split('\n').join('\r\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadTest(term: Terminal, addons: AddonCollection): void {
|
||||||
|
const rendererName = addons.webgl.instance ? 'webgl' : 'dom';
|
||||||
|
const testData = [];
|
||||||
|
let byteCount = 0;
|
||||||
|
for (let i = 0; i < 50; i++) {
|
||||||
|
const count = 1 + Math.floor(Math.random() * 79);
|
||||||
|
byteCount += count + 2;
|
||||||
|
const data = new Uint8Array(count + 2);
|
||||||
|
data[0] = 0x0A; // \n
|
||||||
|
for (let i = 1; i < count + 1; i++) {
|
||||||
|
data[i] = 0x61 + Math.floor(Math.random() * (0x7A - 0x61));
|
||||||
|
}
|
||||||
|
// End each line with \r so the cursor remains constant, this is what ls/tree do and improves
|
||||||
|
// performance significantly due to the cursor DOM element not needing to change
|
||||||
|
data[data.length - 1] = 0x0D; // \r
|
||||||
|
testData.push(data);
|
||||||
|
}
|
||||||
|
const start = performance.now();
|
||||||
|
for (let i = 0; i < 1024; i++) {
|
||||||
|
for (const d of testData) {
|
||||||
|
term.write(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Wait for all data to be parsed before evaluating time
|
||||||
|
term.write('', () => {
|
||||||
|
const time = Math.round(performance.now() - start);
|
||||||
|
const mbs = ((byteCount / 1024) * (1 / (time / 1000))).toFixed(2);
|
||||||
|
term.write(`\n\r\nWrote ${byteCount}kB in ${time}ms (${mbs}MB/s) using the (${rendererName} renderer)`);
|
||||||
|
// Send ^C to get a new prompt
|
||||||
|
(term as any)._core._onData.fire('\x03');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadTestLongLines(term: Terminal, addons: AddonCollection): void {
|
||||||
|
const rendererName = addons.webgl.instance ? 'webgl' : 'dom';
|
||||||
|
const testData = [];
|
||||||
|
let byteCount = 0;
|
||||||
|
for (let i = 0; i < 50; i++) {
|
||||||
|
const count = 1 + Math.floor(Math.random() * 500);
|
||||||
|
byteCount += count + 2;
|
||||||
|
const data = new Uint8Array(count + 2);
|
||||||
|
data[0] = 0x0A; // \n
|
||||||
|
for (let i = 1; i < count + 1; i++) {
|
||||||
|
data[i] = 0x61 + Math.floor(Math.random() * (0x7A - 0x61));
|
||||||
|
}
|
||||||
|
// End each line with \r so the cursor remains constant, this is what ls/tree do and improves
|
||||||
|
// performance significantly due to the cursor DOM element not needing to change
|
||||||
|
data[data.length - 1] = 0x0D; // \r
|
||||||
|
testData.push(data);
|
||||||
|
}
|
||||||
|
const start = performance.now();
|
||||||
|
for (let i = 0; i < 1024 * 50; i++) {
|
||||||
|
for (const d of testData) {
|
||||||
|
term.write(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Wait for all data to be parsed before evaluating time
|
||||||
|
term.write('', () => {
|
||||||
|
const time = Math.round(performance.now() - start);
|
||||||
|
const mbs = ((byteCount / 1024) * (1 / (time / 1000))).toFixed(2);
|
||||||
|
term.write(`\n\r\nWrote ${byteCount}kB in ${time}ms (${mbs}MB/s) using the (${rendererName} renderer)`);
|
||||||
|
// Send ^C to get a new prompt
|
||||||
|
(term as any)._core._onData.fire('\x03');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addDecoration(term: Terminal): void {
|
||||||
|
term.options['overviewRuler'] = { width: 14 };
|
||||||
|
const marker = term.registerMarker(1);
|
||||||
|
const decoration = term.registerDecoration({
|
||||||
|
marker,
|
||||||
|
backgroundColor: '#00FF00',
|
||||||
|
foregroundColor: '#00FE00',
|
||||||
|
overviewRulerOptions: { color: '#ef292980', position: 'left' }
|
||||||
|
});
|
||||||
|
decoration.onRender((e: HTMLElement) => {
|
||||||
|
e.style.right = '100%';
|
||||||
|
e.style.backgroundColor = '#ef292980';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addOverviewRuler(term: Terminal): void {
|
||||||
|
term.options['overviewRuler'] = { width: 14 };
|
||||||
|
term.registerDecoration({ marker: term.registerMarker(1), overviewRulerOptions: { color: '#ef2929' } });
|
||||||
|
term.registerDecoration({ marker: term.registerMarker(3), overviewRulerOptions: { color: '#8ae234' } });
|
||||||
|
term.registerDecoration({ marker: term.registerMarker(5), overviewRulerOptions: { color: '#729fcf' } });
|
||||||
|
term.registerDecoration({ marker: term.registerMarker(7), overviewRulerOptions: { color: '#ef2929', position: 'left' } });
|
||||||
|
term.registerDecoration({ marker: term.registerMarker(7), overviewRulerOptions: { color: '#8ae234', position: 'center' } });
|
||||||
|
term.registerDecoration({ marker: term.registerMarker(7), overviewRulerOptions: { color: '#729fcf', position: 'right' } });
|
||||||
|
term.registerDecoration({ marker: term.registerMarker(10), overviewRulerOptions: { color: '#8ae234', position: 'center' } });
|
||||||
|
term.registerDecoration({ marker: term.registerMarker(10), overviewRulerOptions: { color: '#ffffff80', position: 'full' } });
|
||||||
|
}
|
||||||
|
|
||||||
|
let decorationStressTestDecorations: IDisposable[] | undefined;
|
||||||
|
function decorationStressTest(term: Terminal): void {
|
||||||
|
if (decorationStressTestDecorations) {
|
||||||
|
for (const d of decorationStressTestDecorations) {
|
||||||
|
d.dispose();
|
||||||
|
}
|
||||||
|
decorationStressTestDecorations = undefined;
|
||||||
|
} else {
|
||||||
|
const buffer = term.buffer.active;
|
||||||
|
const cursorY = buffer.baseY + buffer.cursorY;
|
||||||
|
decorationStressTestDecorations = [];
|
||||||
|
for (const x of [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]) {
|
||||||
|
for (let y = 0; y < term.buffer.active.length; y++) {
|
||||||
|
const cursorOffsetY = y - cursorY;
|
||||||
|
decorationStressTestDecorations.push(term.registerDecoration({
|
||||||
|
marker: term.registerMarker(cursorOffsetY),
|
||||||
|
x,
|
||||||
|
width: 4,
|
||||||
|
backgroundColor: '#FF0000',
|
||||||
|
overviewRulerOptions: { color: '#FF0000' }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initProgress(term: Terminal, addons: AddonCollection): void {
|
||||||
|
const STATES = { 0: 'remove', 1: 'set', 2: 'error', 3: 'indeterminate', 4: 'pause' };
|
||||||
|
const COLORS = { 0: '', 1: 'green', 2: 'red', 3: '', 4: 'yellow' };
|
||||||
|
|
||||||
|
function progressHandler({ state, value }: IProgressState): void {
|
||||||
|
// Simulate windows taskbar hack by windows terminal:
|
||||||
|
// Since the taskbar has no means to indicate error/pause state other than by coloring
|
||||||
|
// the current progress, we move 0 to 10% and distribute higher values in the remaining 90 %
|
||||||
|
// NOTE: This is most likely not what you want to do for other progress indicators,
|
||||||
|
// that have a proper visual state for error/paused.
|
||||||
|
value = Math.min(10 + value * 0.9, 100);
|
||||||
|
document.getElementById('progress-percent').style.width = `${value}%`;
|
||||||
|
document.getElementById('progress-percent').style.backgroundColor = COLORS[state];
|
||||||
|
document.getElementById('progress-state').innerText = `State: ${STATES[state]}`;
|
||||||
|
|
||||||
|
document.getElementById('progress-percent').style.display = state === 3 ? 'none' : 'block';
|
||||||
|
document.getElementById('progress-indeterminate').style.display = state === 3 ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
const progressAddon = addons.progress.instance;
|
||||||
|
progressAddon.onChange(progressHandler);
|
||||||
|
|
||||||
|
// apply initial state once to make it visible on page load
|
||||||
|
const initialProgress = progressAddon.progress;
|
||||||
|
progressHandler(initialProgress);
|
||||||
|
|
||||||
|
document.getElementById('progress-run').addEventListener('click', async () => {
|
||||||
|
term.write('\x1b]9;4;0\x1b\\');
|
||||||
|
for (let i = 0; i <= 100; i += 5) {
|
||||||
|
term.write(`\x1b]9;4;1;${i}\x1b\\`);
|
||||||
|
await new Promise(res => setTimeout(res, 200));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.getElementById('progress-0').addEventListener('click', () => term.write('\x1b]9;4;0\x1b\\'));
|
||||||
|
document.getElementById('progress-1').addEventListener('click', () => term.write('\x1b]9;4;1;20\x1b\\'));
|
||||||
|
document.getElementById('progress-2').addEventListener('click', () => term.write('\x1b]9;4;2\x1b\\'));
|
||||||
|
document.getElementById('progress-3').addEventListener('click', () => term.write('\x1b]9;4;3\x1b\\'));
|
||||||
|
document.getElementById('progress-4').addEventListener('click', () => term.write('\x1b]9;4;4\x1b\\'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function initImageAddonExposed(term: Terminal, addons: AddonCollection): void {
|
||||||
|
const DEFAULT_OPTIONS: IImageAddonOptions = (addons.image.instance as any)._defaultOpts;
|
||||||
|
const limitStorageElement = document.querySelector<HTMLInputElement>('#image-storagelimit');
|
||||||
|
limitStorageElement.valueAsNumber = addons.image.instance.storageLimit;
|
||||||
|
addDomListener(term, limitStorageElement, 'change', () => {
|
||||||
|
try {
|
||||||
|
addons.image.instance.storageLimit = limitStorageElement.valueAsNumber;
|
||||||
|
limitStorageElement.valueAsNumber = addons.image.instance.storageLimit;
|
||||||
|
console.log('changed storageLimit to', addons.image.instance.storageLimit);
|
||||||
|
} catch (e) {
|
||||||
|
limitStorageElement.valueAsNumber = addons.image.instance.storageLimit;
|
||||||
|
console.log('storageLimit at', addons.image.instance.storageLimit);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const showPlaceholderElement = document.querySelector<HTMLInputElement>('#image-showplaceholder');
|
||||||
|
showPlaceholderElement.checked = addons.image.instance.showPlaceholder;
|
||||||
|
addDomListener(term, showPlaceholderElement, 'change', () => {
|
||||||
|
addons.image.instance.showPlaceholder = showPlaceholderElement.checked;
|
||||||
|
});
|
||||||
|
const ctorOptionsElement = document.querySelector<HTMLTextAreaElement>('#image-options');
|
||||||
|
ctorOptionsElement.value = JSON.stringify(DEFAULT_OPTIONS, null, 2);
|
||||||
|
|
||||||
|
const sixelDemo = (url: string) => () => fetch(url)
|
||||||
|
.then(resp => resp.arrayBuffer())
|
||||||
|
.then(buffer => {
|
||||||
|
term.write('\r\n');
|
||||||
|
term.write(new Uint8Array(buffer));
|
||||||
|
});
|
||||||
|
|
||||||
|
const iipDemo = (url: string) => () => fetch(url)
|
||||||
|
.then(resp => resp.arrayBuffer())
|
||||||
|
.then(buffer => {
|
||||||
|
const data = new Uint8Array(buffer);
|
||||||
|
let sdata = '';
|
||||||
|
for (let i = 0; i < data.length; ++i) sdata += String.fromCharCode(data[i]);
|
||||||
|
term.write('\r\n');
|
||||||
|
term.write(`\x1b]1337;File=inline=1;size=${data.length}:${btoa(sdata)}\x1b\\`);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('image-demo1').addEventListener('click',
|
||||||
|
sixelDemo('https://raw.githubusercontent.com/saitoha/libsixel/master/images/snake.six'));
|
||||||
|
document.getElementById('image-demo2').addEventListener('click',
|
||||||
|
sixelDemo('https://raw.githubusercontent.com/jerch/node-sixel/master/testfiles/test2.sixel'));
|
||||||
|
document.getElementById('image-demo3').addEventListener('click',
|
||||||
|
iipDemo('https://raw.githubusercontent.com/jerch/node-sixel/master/palette.png'));
|
||||||
|
|
||||||
|
// demo for image retrieval API
|
||||||
|
term.element.addEventListener('click', (ev: MouseEvent) => {
|
||||||
|
if (!ev.ctrlKey || !addons.image.instance) return;
|
||||||
|
|
||||||
|
// TODO...
|
||||||
|
// if (ev.altKey) {
|
||||||
|
// const sel = term.getSelectionPosition();
|
||||||
|
// if (sel) {
|
||||||
|
// addons.image.instance
|
||||||
|
// .extractCanvasAtBufferRange(term.getSelectionPosition())
|
||||||
|
// ?.toBlob(data => window.open(URL.createObjectURL(data), '_blank'));
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
const pos = (term as any)._core._mouseService!.getCoords(ev, (term as any)._core.screenElement!, term.cols, term.rows);
|
||||||
|
const x = pos[0] - 1;
|
||||||
|
const y = pos[1] - 1;
|
||||||
|
const canvas = ev.shiftKey
|
||||||
|
// ctrl+shift+click: get single tile
|
||||||
|
? addons.image.instance.extractTileAtBufferCell(x, term.buffer.active.viewportY + y)
|
||||||
|
// ctrl+click: get original image
|
||||||
|
: addons.image.instance.getImageAtBufferCell(x, term.buffer.active.viewportY + y);
|
||||||
|
canvas?.toBlob(data => window.open(URL.createObjectURL(data), '_blank'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addDomListener(term: Terminal, element: HTMLElement, type: string, handler: (...args: any[]) => any): void {
|
||||||
|
element.addEventListener(type, handler);
|
||||||
|
(term as any)._core._register({ dispose: () => element.removeEventListener(type, handler) });
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,10 +3,11 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { BaseWindow } from './baseWindow';
|
||||||
import type { IControlWindow } from '../controlBar';
|
import type { IControlWindow } from '../controlBar';
|
||||||
import type { Terminal } from '@xterm/xterm';
|
import type { Terminal } from '@xterm/xterm';
|
||||||
|
|
||||||
export class VtWindow implements IControlWindow {
|
export class VtWindow extends BaseWindow implements IControlWindow {
|
||||||
public readonly id = 'vt';
|
public readonly id = 'vt';
|
||||||
public readonly label = 'VT';
|
public readonly label = 'VT';
|
||||||
|
|
||||||
@@ -23,16 +24,6 @@ export class VtWindow implements IControlWindow {
|
|||||||
const vtContainer = document.createElement('div');
|
const vtContainer = document.createElement('div');
|
||||||
vtContainer.id = 'vt-container';
|
vtContainer.id = 'vt-container';
|
||||||
container.appendChild(vtContainer);
|
container.appendChild(vtContainer);
|
||||||
}
|
|
||||||
|
|
||||||
public initTerminal(term: Terminal): void {
|
|
||||||
this._term = term;
|
|
||||||
this._addVtButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _addVtButtons(): void {
|
|
||||||
const vtContainer = this._container.querySelector('#vt-container');
|
|
||||||
if (!vtContainer) return;
|
|
||||||
|
|
||||||
const vtFragment = document.createDocumentFragment();
|
const vtFragment = document.createDocumentFragment();
|
||||||
const buttonSpecs: { [key: string]: { label: string; description: string; paramCount?: number } } = {
|
const buttonSpecs: { [key: string]: { label: string; description: string; paramCount?: number } } = {
|
||||||
|
|||||||
+6
-2
@@ -9,9 +9,13 @@ body {
|
|||||||
grid-template-rows: 24px 1fr;
|
grid-template-rows: 24px 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Top banner */
|
body:not(#test) {
|
||||||
|
height: initial;
|
||||||
|
display: initial;
|
||||||
|
}
|
||||||
|
|
||||||
#banner {
|
#banner {
|
||||||
background: #2D2E2C;
|
background: #161716;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|||||||
+14
-2
@@ -3,9 +3,21 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>xterm.js integration test fixture</title>
|
<title>xterm.js integration test fixture</title>
|
||||||
<link rel="stylesheet" href="/xterm.css" />
|
<link rel="stylesheet" href="/xterm.css" />
|
||||||
<link rel="stylesheet" href="/style.css" />
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: helvetica, sans-serif, arial;
|
||||||
|
font-size: 1em;
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
#terminal-container {
|
||||||
|
height: 60%;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body id="test">
|
||||||
<div id="terminal-container"></div>
|
<div id="terminal-container"></div>
|
||||||
<script src="/dist/client-bundle.js" defer></script>
|
<script src="/dist/client-bundle.js" defer></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
||||||
|
* @license MIT
|
||||||
|
*
|
||||||
|
* This file is the entry point for browserify.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { ImageAddon } from '@xterm/addon-image';
|
||||||
|
import type { AttachAddon } from '@xterm/addon-attach';
|
||||||
|
import type { ClipboardAddon } from '@xterm/addon-clipboard';
|
||||||
|
import type { FitAddon } from '@xterm/addon-fit';
|
||||||
|
import type { LigaturesAddon } from '@xterm/addon-ligatures';
|
||||||
|
import type { ProgressAddon } from '@xterm/addon-progress';
|
||||||
|
import type { SearchAddon } from '@xterm/addon-search';
|
||||||
|
import type { SerializeAddon } from '@xterm/addon-serialize';
|
||||||
|
import type { UnicodeGraphemesAddon } from '@xterm/addon-unicode-graphemes';
|
||||||
|
import type { Unicode11Addon } from '@xterm/addon-unicode11';
|
||||||
|
import type { WebLinksAddon } from '@xterm/addon-web-links';
|
||||||
|
import type { WebglAddon } from '@xterm/addon-webgl';
|
||||||
|
|
||||||
|
export type AddonType = 'attach' | 'clipboard' | 'fit' | 'image' | 'progress' | 'search' | 'serialize' | 'unicode11' | 'unicodeGraphemes' | 'webLinks' | 'webgl' | 'ligatures';
|
||||||
|
|
||||||
|
export interface IDemoAddon<T extends AddonType> {
|
||||||
|
name: T;
|
||||||
|
canChange: boolean;
|
||||||
|
ctor: (
|
||||||
|
T extends 'attach' ? typeof AttachAddon :
|
||||||
|
T extends 'clipboard' ? typeof ClipboardAddon :
|
||||||
|
T extends 'fit' ? typeof FitAddon :
|
||||||
|
T extends 'image' ? typeof ImageAddon :
|
||||||
|
T extends 'ligatures' ? typeof LigaturesAddon :
|
||||||
|
T extends 'progress' ? typeof ProgressAddon :
|
||||||
|
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
|
||||||
|
);
|
||||||
|
instance?: (
|
||||||
|
T extends 'attach' ? AttachAddon :
|
||||||
|
T extends 'clipboard' ? ClipboardAddon :
|
||||||
|
T extends 'fit' ? FitAddon :
|
||||||
|
T extends 'image' ? ImageAddon :
|
||||||
|
T extends 'ligatures' ? LigaturesAddon :
|
||||||
|
T extends 'progress' ? ProgressAddon :
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AddonCollection = { [T in AddonType]: IDemoAddon<T> };
|
||||||
Reference in New Issue
Block a user