mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
More move handlers into addons and test window
This commit is contained in:
+1
-153
@@ -220,7 +220,7 @@ if (document.location.pathname === '/test') {
|
||||
|
||||
// TODO: Most of below should be encapsulated within windows
|
||||
paddingElement = styleWindow.paddingElement;
|
||||
|
||||
|
||||
controlBar.setTabVisible('gpu', true);
|
||||
gpuWindow.setTextureAtlas(addons.webgl.instance.textureAtlas);
|
||||
addons.webgl.instance.onChangeTextureAtlas(e => gpuWindow.setTextureAtlas(e));
|
||||
@@ -253,12 +253,6 @@ if (document.location.pathname === '/test') {
|
||||
addDomListener(actionElements.findPrevious, 'blur', (e) => {
|
||||
addons.search.instance.clearActiveDecoration();
|
||||
});
|
||||
|
||||
document.getElementById('serialize').addEventListener('click', serializeButtonHandler);
|
||||
document.getElementById('htmlserialize').addEventListener('click', htmlSerializeButtonHandler);
|
||||
initImageAddonExposed();
|
||||
testEvents();
|
||||
progressButtons();
|
||||
}
|
||||
|
||||
function createTerminal(): Terminal {
|
||||
@@ -574,32 +568,6 @@ function updateTerminalSize(): void {
|
||||
addons.fit.instance.fit();
|
||||
}
|
||||
|
||||
function serializeButtonHandler(): 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(): 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';
|
||||
}
|
||||
|
||||
(console as any).image = (source: ImageData | HTMLCanvasElement, scale: number = 1) => {
|
||||
function getBox(width: number, height: number): any {
|
||||
return {
|
||||
@@ -630,123 +598,3 @@ function htmlSerializeButtonHandler(): void {
|
||||
);
|
||||
console.groupEnd();
|
||||
};
|
||||
|
||||
function initImageAddonExposed(): 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(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(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._core._mouseService!.getCoords(ev, term._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 testEvents(): void {
|
||||
document.getElementById('event-focus').addEventListener('click', ()=> term.focus());
|
||||
document.getElementById('event-blur').addEventListener('click', ()=> term.blur());
|
||||
}
|
||||
|
||||
|
||||
function progressButtons(): 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\\'));
|
||||
}
|
||||
|
||||
@@ -3,8 +3,13 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/// <reference path="../../../typings/xterm.d.ts"/>
|
||||
|
||||
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';
|
||||
@@ -55,6 +60,7 @@ export class AddonsWindow extends BaseWindow implements IControlWindow {
|
||||
|
||||
// ImageAddon section
|
||||
this._buildImageSection(container);
|
||||
initImageAddonExposed(this._terminal, this._addons);
|
||||
}
|
||||
|
||||
private _buildSearchSection(container: HTMLElement): void {
|
||||
@@ -141,6 +147,7 @@ export class AddonsWindow extends BaseWindow implements IControlWindow {
|
||||
const serializeBtn = document.createElement('button');
|
||||
serializeBtn.id = 'serialize';
|
||||
serializeBtn.textContent = 'Serialize the content of terminal';
|
||||
serializeBtn.addEventListener('click', () => serializeButtonHandler(this._terminal, this._addons));
|
||||
wrapper.appendChild(serializeBtn);
|
||||
|
||||
// Write to terminal checkbox
|
||||
@@ -163,6 +170,7 @@ export class AddonsWindow extends BaseWindow implements IControlWindow {
|
||||
const htmlSerializeBtn = document.createElement('button');
|
||||
htmlSerializeBtn.id = 'htmlserialize';
|
||||
htmlSerializeBtn.textContent = 'Serialize the content of terminal in HTML';
|
||||
htmlSerializeBtn.addEventListener('click', () => htmlSerializeButtonHandler(this._terminal, this._addons));
|
||||
wrapper.appendChild(htmlSerializeBtn);
|
||||
|
||||
// HTML serialize result
|
||||
@@ -244,3 +252,109 @@ export class AddonsWindow extends BaseWindow implements IControlWindow {
|
||||
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';
|
||||
}
|
||||
|
||||
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) });
|
||||
}
|
||||
@@ -10,6 +10,7 @@ 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';
|
||||
|
||||
export class TestWindow extends BaseWindow implements IControlWindow {
|
||||
public readonly id = 'test';
|
||||
@@ -84,8 +85,8 @@ export class TestWindow extends BaseWindow implements IControlWindow {
|
||||
|
||||
// Events Test section
|
||||
this._addDt(dl, 'Events Test');
|
||||
this._addDdWithButton(dl, 'event-focus', 'focus', '');
|
||||
this._addDdWithButton(dl, 'event-blur', 'blur', '');
|
||||
this._addDdWithButton(dl, 'event-focus', 'focus', '', () => this._terminal.focus());
|
||||
this._addDdWithButton(dl, 'event-blur', 'blur', '', () => this._terminal.blur());
|
||||
|
||||
// Progress Addon section
|
||||
this._addDt(dl, 'Progress Addon');
|
||||
@@ -116,6 +117,8 @@ export class TestWindow extends BaseWindow implements IControlWindow {
|
||||
stateDiv.textContent = 'State:';
|
||||
stateDd.appendChild(stateDiv);
|
||||
dl.appendChild(stateDd);
|
||||
|
||||
initProgress(this._terminal, this._addons);
|
||||
|
||||
wrapper.appendChild(dl);
|
||||
container.appendChild(wrapper);
|
||||
@@ -842,4 +845,44 @@ function decorationStressTest(term: Terminal): void {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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\\'));
|
||||
}
|
||||
Reference in New Issue
Block a user