From 82f24ad5a5364fc6e8afca92f5e68483132d8fd9 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 27 Dec 2025 06:47:17 -0800 Subject: [PATCH] Demo: Add addon tabs, shuffle order --- demo/client/client.ts | 61 +++-- demo/client/components/controlBar.ts | 6 +- .../components/window/addonImageWindow.ts | 76 ++++++ .../components/window/addonSearchWindow.ts | 105 ++++++++ .../components/window/addonSerializeWindow.ts | 97 +++++++ demo/client/components/window/addonsWindow.ts | 242 ------------------ demo/client/components/window/gpuWindow.ts | 4 +- demo/client/components/window/webglWindow.ts | 53 ++++ 8 files changed, 376 insertions(+), 268 deletions(-) create mode 100644 demo/client/components/window/addonImageWindow.ts create mode 100644 demo/client/components/window/addonSearchWindow.ts create mode 100644 demo/client/components/window/addonSerializeWindow.ts create mode 100644 demo/client/components/window/webglWindow.ts diff --git a/demo/client/client.ts b/demo/client/client.ts index 2571e73b..63da8fca 100644 --- a/demo/client/client.ts +++ b/demo/client/client.ts @@ -15,9 +15,12 @@ if ('WebAssembly' in window) { import { Terminal, ITerminalOptions, type ITheme } from '@xterm/xterm'; import { AttachAddon } from '@xterm/addon-attach'; +import { AddonImageWindow } from './components/window/addonImageWindow'; +import { AddonSearchWindow } from './components/window/addonSearchWindow'; +import { AddonSerializeWindow } from './components/window/addonSerializeWindow'; import { AddonsWindow } from './components/window/addonsWindow'; import { ControlBar } from './components/controlBar'; -import { GpuWindow } from './components/window/gpuWindow'; +import { WebglWindow } from './components/window/webglWindow'; import { OptionsWindow } from './components/window/optionsWindow'; import { StyleWindow } from './components/window/styleWindow'; import { TestWindow } from './components/window/testWindow'; @@ -59,7 +62,8 @@ let socket; let pid; let controlBar: ControlBar; let addonsWindow: AddonsWindow; -let gpuWindow: GpuWindow; +let addonSearchWindow: AddonSearchWindow; +let addonWebglWindow: WebglWindow; let optionsWindow: OptionsWindow; const addons: AddonCollection = { @@ -200,27 +204,33 @@ if (document.location.pathname === '/test') { const typedTerm = createTerminal(); controlBar = new ControlBar(document.getElementById('sidebar'), document.querySelector('.banner-tabs'), []); - addonsWindow = controlBar.registerWindow(new AddonsWindow(typedTerm, addons)); - actionElements = { - findNext: addonsWindow.findNextInput, - findPrevious: addonsWindow.findPreviousInput, - findResults: addonsWindow.findResultsSpan - }; - gpuWindow = controlBar.registerWindow(new GpuWindow(typedTerm, addons), { afterId: 'addons', hidden: true, smallTab: true }); - const styleWindow = controlBar.registerWindow(new StyleWindow(typedTerm, addons)); optionsWindow = controlBar.registerWindow(new OptionsWindow(typedTerm, addons, { updateTerminalSize, updateTerminalContainerBackground })); - controlBar.registerWindow(new TestWindow(typedTerm, addons, { disposeRecreateButtonHandler, createNewWindowButtonHandler })); + const styleWindow = controlBar.registerWindow(new StyleWindow(typedTerm, addons)); controlBar.registerWindow(new VtWindow(typedTerm, addons)); + addonsWindow = controlBar.registerWindow(new AddonsWindow(typedTerm, addons)); + addonSearchWindow = controlBar.registerWindow(new AddonSearchWindow(typedTerm, addons), { afterId: 'addons', hidden: true, italics: true }); + controlBar.registerWindow(new AddonSerializeWindow(typedTerm, addons), { afterId: 'addon-search', hidden: true, italics: true }); + controlBar.registerWindow(new AddonImageWindow(typedTerm, addons), { afterId: 'addon-serialize', hidden: true, italics: true }); + addonWebglWindow = controlBar.registerWindow(new WebglWindow(typedTerm, addons), { afterId: 'addon-image', hidden: true, italics: true }); + controlBar.registerWindow(new TestWindow(typedTerm, addons, { disposeRecreateButtonHandler, createNewWindowButtonHandler }), { afterId: 'options' }); + actionElements = { + findNext: addonSearchWindow.findNextInput, + findPrevious: addonSearchWindow.findPreviousInput, + findResults: addonSearchWindow.findResultsSpan + }; controlBar.activateDefaultTab(); // 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)); - addons.webgl.instance.onAddTextureAtlasCanvas(e => gpuWindow.appendTextureAtlas(e)); - addons.webgl.instance.onRemoveTextureAtlasCanvas(e => gpuWindow.removeTextureAtlas(e)); + controlBar.setTabVisible('addon-webgl', true); + controlBar.setTabVisible('addon-search', true); + controlBar.setTabVisible('addon-serialize', true); + controlBar.setTabVisible('addon-image', true); + addonWebglWindow.setTextureAtlas(addons.webgl.instance.textureAtlas); + addons.webgl.instance.onChangeTextureAtlas(e => addonWebglWindow.setTextureAtlas(e)); + addons.webgl.instance.onAddTextureAtlasCanvas(e => addonWebglWindow.appendTextureAtlas(e)); + addons.webgl.instance.onRemoveTextureAtlasCanvas(e => addonWebglWindow.removeTextureAtlas(e)); paddingElement.value = '0'; addDomListener(paddingElement, 'change', setPadding); @@ -409,15 +419,15 @@ function initAddons(term: Terminal): void { const fragment = document.createDocumentFragment(); function postInitWebgl(): void { - controlBar.setTabVisible('gpu', true); + controlBar.setTabVisible('addon-webgl', true); setTimeout(() => { - gpuWindow.setTextureAtlas(addons.webgl.instance.textureAtlas); - addons.webgl.instance.onChangeTextureAtlas(e => gpuWindow.setTextureAtlas(e)); - addons.webgl.instance.onAddTextureAtlasCanvas(e => gpuWindow.appendTextureAtlas(e)); + addonWebglWindow.setTextureAtlas(addons.webgl.instance.textureAtlas); + addons.webgl.instance.onChangeTextureAtlas(e => addonWebglWindow.setTextureAtlas(e)); + addons.webgl.instance.onAddTextureAtlasCanvas(e => addonWebglWindow.appendTextureAtlas(e)); }, 500); } function preDisposeWebgl(): void { - controlBar.setTabVisible('gpu', false); + controlBar.setTabVisible('addon-webgl', false); if (addons.webgl.instance.textureAtlas) { addons.webgl.instance.textureAtlas.remove(); } @@ -448,7 +458,9 @@ function initAddons(term: Terminal): void { ? new addons[name].ctor(JSON.parse(ctorOptionsJson)) : new addons[name].ctor(); term.loadAddon(addon.instance); + controlBar.setTabVisible('addon-image', true); } else { + controlBar.setTabVisible('addon-image', false); addon.instance!.dispose(); addon.instance = undefined; } @@ -466,7 +478,10 @@ function initAddons(term: Terminal): void { } else if (name === 'unicodeGraphemes') { term.unicode.activeVersion = '15-graphemes'; } else if (name === 'search') { + controlBar.setTabVisible('addon-search', true); addons[name].instance.onDidChangeResults(e => updateFindResults(e)); + } else if (name === 'serialize') { + controlBar.setTabVisible('addon-serialize', true); } } catch { @@ -479,6 +494,10 @@ function initAddons(term: Terminal): void { preDisposeWebgl(); } else if (name === 'unicode11' || name === 'unicodeGraphemes') { term.unicode.activeVersion = '6'; + } else if (name === 'search') { + controlBar.setTabVisible('addon-search', false); + } else if (name === 'serialize') { + controlBar.setTabVisible('addon-serialize', false); } addon.instance!.dispose(); addon.instance = undefined; diff --git a/demo/client/components/controlBar.ts b/demo/client/components/controlBar.ts index a0424878..8fc70f7f 100644 --- a/demo/client/components/controlBar.ts +++ b/demo/client/components/controlBar.ts @@ -149,7 +149,7 @@ export class ControlBar { } } - public registerWindow(window: T, options?: { afterId?: string, hidden?: boolean, smallTab?: boolean }): T { + public registerWindow(window: T, options?: { afterId?: string, hidden?: boolean, italics?: boolean }): T { // Create button const button = document.createElement('button'); button.id = `${window.id}button`; @@ -158,8 +158,8 @@ export class ControlBar { button.addEventListener('click', (e) => this._openSection(e, window.id)); // Apply small tab styling - if (options?.smallTab) { - button.style.fontSize = '0.85em'; + if (options?.italics) { + button.style.fontStyle = 'italic'; } // Insert after specified tab or append at end diff --git a/demo/client/components/window/addonImageWindow.ts b/demo/client/components/window/addonImageWindow.ts new file mode 100644 index 00000000..4112132f --- /dev/null +++ b/demo/client/components/window/addonImageWindow.ts @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { BaseWindow } from './baseWindow'; +import type { IControlWindow } from '../controlBar'; + +export class AddonImageWindow extends BaseWindow implements IControlWindow { + public readonly id = 'addon-image'; + public readonly label = 'image'; + + private _imageStorageLimitInput: HTMLInputElement; + private _imageShowPlaceholderCheckbox: HTMLInputElement; + private _imageOptionsTextarea: HTMLTextAreaElement; + + public build(container: HTMLElement): void { + const h4 = document.createElement('h4'); + h4.textContent = 'Image Addon'; + container.appendChild(h4); + + const details = document.createElement('details'); + const summary = document.createElement('summary'); + summary.textContent = 'image addon settings'; + details.appendChild(summary); + + const wrapper = document.createElement('div'); + + // Storage limit + const storageLimitLabel = document.createElement('label'); + storageLimitLabel.textContent = 'Storage Limit (in MB) '; + this._imageStorageLimitInput = document.createElement('input'); + this._imageStorageLimitInput.type = 'number'; + this._imageStorageLimitInput.id = 'image-storagelimit'; + storageLimitLabel.appendChild(this._imageStorageLimitInput); + wrapper.appendChild(storageLimitLabel); + wrapper.appendChild(document.createElement('br')); + + // Show placeholder + const placeholderLabel = document.createElement('label'); + placeholderLabel.textContent = 'Show Placeholder '; + this._imageShowPlaceholderCheckbox = document.createElement('input'); + this._imageShowPlaceholderCheckbox.type = 'checkbox'; + this._imageShowPlaceholderCheckbox.id = 'image-showplaceholder'; + placeholderLabel.appendChild(this._imageShowPlaceholderCheckbox); + wrapper.appendChild(placeholderLabel); + wrapper.appendChild(document.createElement('br')); + wrapper.appendChild(document.createElement('br')); + + // Ctor options + const optionsLabel = document.createElement('label'); + optionsLabel.appendChild(document.createTextNode('Ctor options (applied on addon relaunch)')); + optionsLabel.appendChild(document.createElement('br')); + this._imageOptionsTextarea = document.createElement('textarea'); + this._imageOptionsTextarea.id = 'image-options'; + this._imageOptionsTextarea.cols = 40; + this._imageOptionsTextarea.rows = 12; + optionsLabel.appendChild(this._imageOptionsTextarea); + wrapper.appendChild(optionsLabel); + + details.appendChild(wrapper); + container.appendChild(details); + } + + public get imageStorageLimitInput(): HTMLInputElement { + return this._imageStorageLimitInput; + } + + public get imageShowPlaceholderCheckbox(): HTMLInputElement { + return this._imageShowPlaceholderCheckbox; + } + + public get imageOptionsTextarea(): HTMLTextAreaElement { + return this._imageOptionsTextarea; + } +} diff --git a/demo/client/components/window/addonSearchWindow.ts b/demo/client/components/window/addonSearchWindow.ts new file mode 100644 index 00000000..47217f3b --- /dev/null +++ b/demo/client/components/window/addonSearchWindow.ts @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { BaseWindow } from './baseWindow'; +import type { IControlWindow } from '../controlBar'; + +export class AddonSearchWindow extends BaseWindow implements IControlWindow { + public readonly id = 'addon-search'; + public readonly label = 'search'; + + private _findNextInput: HTMLInputElement; + private _findPreviousInput: HTMLInputElement; + private _findResultsSpan: HTMLElement; + private _regexCheckbox: HTMLInputElement; + private _caseSensitiveCheckbox: HTMLInputElement; + private _wholeWordCheckbox: HTMLInputElement; + private _highlightAllMatchesCheckbox: HTMLInputElement; + + public build(container: HTMLElement): void { + const h4 = document.createElement('h4'); + h4.textContent = 'SearchAddon'; + container.appendChild(h4); + + const wrapper = document.createElement('div'); + wrapper.style.display = 'flex'; + wrapper.style.flexDirection = 'column'; + + // Find next + const findNextLabel = document.createElement('label'); + findNextLabel.textContent = 'Find next '; + this._findNextInput = document.createElement('input'); + this._findNextInput.id = 'find-next'; + findNextLabel.appendChild(this._findNextInput); + wrapper.appendChild(findNextLabel); + + // Find previous + const findPrevLabel = document.createElement('label'); + findPrevLabel.textContent = 'Find previous '; + this._findPreviousInput = document.createElement('input'); + this._findPreviousInput.id = 'find-previous'; + findPrevLabel.appendChild(this._findPreviousInput); + wrapper.appendChild(findPrevLabel); + + // Results + const resultsDiv = document.createElement('div'); + resultsDiv.textContent = 'Results: '; + this._findResultsSpan = document.createElement('span'); + this._findResultsSpan.id = 'find-results'; + resultsDiv.appendChild(this._findResultsSpan); + wrapper.appendChild(resultsDiv); + + // Regex checkbox + const regexLabel = document.createElement('label'); + this._regexCheckbox = document.createElement('input'); + this._regexCheckbox.type = 'checkbox'; + this._regexCheckbox.id = 'regex'; + regexLabel.appendChild(this._regexCheckbox); + regexLabel.appendChild(document.createTextNode('Use regex')); + wrapper.appendChild(regexLabel); + + // Case sensitive checkbox + const caseLabel = document.createElement('label'); + this._caseSensitiveCheckbox = document.createElement('input'); + this._caseSensitiveCheckbox.type = 'checkbox'; + this._caseSensitiveCheckbox.id = 'case-sensitive'; + caseLabel.appendChild(this._caseSensitiveCheckbox); + caseLabel.appendChild(document.createTextNode('Case sensitive')); + wrapper.appendChild(caseLabel); + + // Whole word checkbox + const wholeWordLabel = document.createElement('label'); + this._wholeWordCheckbox = document.createElement('input'); + this._wholeWordCheckbox.type = 'checkbox'; + this._wholeWordCheckbox.id = 'whole-word'; + wholeWordLabel.appendChild(this._wholeWordCheckbox); + wholeWordLabel.appendChild(document.createTextNode('Whole word')); + wrapper.appendChild(wholeWordLabel); + + // Highlight all matches checkbox + const highlightLabel = document.createElement('label'); + this._highlightAllMatchesCheckbox = document.createElement('input'); + this._highlightAllMatchesCheckbox.type = 'checkbox'; + this._highlightAllMatchesCheckbox.id = 'highlight-all-matches'; + this._highlightAllMatchesCheckbox.checked = true; + highlightLabel.appendChild(this._highlightAllMatchesCheckbox); + highlightLabel.appendChild(document.createTextNode('Highlight All Matches')); + wrapper.appendChild(highlightLabel); + + container.appendChild(wrapper); + } + + public get findNextInput(): HTMLInputElement { + return this._findNextInput; + } + + public get findPreviousInput(): HTMLInputElement { + return this._findPreviousInput; + } + + public get findResultsSpan(): HTMLElement { + return this._findResultsSpan; + } +} diff --git a/demo/client/components/window/addonSerializeWindow.ts b/demo/client/components/window/addonSerializeWindow.ts new file mode 100644 index 00000000..031f02dd --- /dev/null +++ b/demo/client/components/window/addonSerializeWindow.ts @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import type { Terminal } from '@xterm/xterm'; +import { BaseWindow } from './baseWindow'; +import type { IControlWindow } from '../controlBar'; +import type { AddonCollection } from '../../types'; + +export class AddonSerializeWindow extends BaseWindow implements IControlWindow { + public readonly id = 'addon-serialize'; + public readonly label = 'serialize'; + + private _serializeOutputPre: HTMLPreElement; + private _htmlSerializeOutputPre: HTMLPreElement; + private _htmlSerializeOutputResult: HTMLElement; + private _writeToTerminalCheckbox: HTMLInputElement; + + public build(container: HTMLElement): void { + const h4 = document.createElement('h4'); + h4.textContent = 'SerializeAddon'; + container.appendChild(h4); + + const wrapper = document.createElement('div'); + + // Serialize button + const serializeBtn = document.createElement('button'); + serializeBtn.id = 'serialize'; + serializeBtn.textContent = 'Serialize the content of terminal'; + serializeBtn.addEventListener('click', () => this._serializeButtonHandler()); + wrapper.appendChild(serializeBtn); + + // Write to terminal checkbox + const writeLabel = document.createElement('label'); + this._writeToTerminalCheckbox = document.createElement('input'); + this._writeToTerminalCheckbox.type = 'checkbox'; + this._writeToTerminalCheckbox.id = 'write-to-terminal'; + writeLabel.appendChild(this._writeToTerminalCheckbox); + writeLabel.appendChild(document.createTextNode('Write back to terminal')); + wrapper.appendChild(writeLabel); + + // Serialize output + const outputDiv = document.createElement('div'); + this._serializeOutputPre = document.createElement('pre'); + this._serializeOutputPre.id = 'serialize-output'; + outputDiv.appendChild(this._serializeOutputPre); + wrapper.appendChild(outputDiv); + + // HTML serialize button + const htmlSerializeBtn = document.createElement('button'); + htmlSerializeBtn.id = 'htmlserialize'; + htmlSerializeBtn.textContent = 'Serialize the content of terminal in HTML'; + htmlSerializeBtn.addEventListener('click', () => this._htmlSerializeButtonHandler()); + wrapper.appendChild(htmlSerializeBtn); + + // HTML serialize result + this._htmlSerializeOutputResult = document.createElement('span'); + this._htmlSerializeOutputResult.id = 'htmlserialize-output-result'; + wrapper.appendChild(this._htmlSerializeOutputResult); + + // HTML serialize output + const htmlOutputDiv = document.createElement('div'); + this._htmlSerializeOutputPre = document.createElement('pre'); + this._htmlSerializeOutputPre.id = 'htmlserialize-output'; + htmlOutputDiv.appendChild(this._htmlSerializeOutputPre); + wrapper.appendChild(htmlOutputDiv); + + container.appendChild(wrapper); + } + + private _serializeButtonHandler(): void { + const output = this._addons.serialize.instance.serialize(); + const outputString = JSON.stringify(output); + + this._serializeOutputPre.innerText = outputString; + if (this._writeToTerminalCheckbox.checked) { + this._terminal.reset(); + this._terminal.write(output); + } + } + + private _htmlSerializeButtonHandler(): void { + const output = this._addons.serialize.instance.serializeAsHTML(); + this._htmlSerializeOutputPre.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); + this._htmlSerializeOutputResult.innerText = 'Copied to clipboard'; + } +} diff --git a/demo/client/components/window/addonsWindow.ts b/demo/client/components/window/addonsWindow.ts index b05fd0a4..a0590482 100644 --- a/demo/client/components/window/addonsWindow.ts +++ b/demo/client/components/window/addonsWindow.ts @@ -3,30 +3,14 @@ * @license MIT */ -import type { Terminal } from '@xterm/xterm'; import { BaseWindow } from './baseWindow'; import type { IControlWindow } from '../controlBar'; -import type { AddonCollection } from '../../types'; export class AddonsWindow extends BaseWindow implements IControlWindow { public readonly id = 'addons'; public readonly label = 'Addons'; private _addonsContainer: HTMLElement; - private _findNextInput: HTMLInputElement; - private _findPreviousInput: HTMLInputElement; - private _findResultsSpan: HTMLElement; - private _regexCheckbox: HTMLInputElement; - private _caseSensitiveCheckbox: HTMLInputElement; - private _wholeWordCheckbox: HTMLInputElement; - private _highlightAllMatchesCheckbox: HTMLInputElement; - private _serializeOutputPre: HTMLPreElement; - private _htmlSerializeOutputPre: HTMLPreElement; - private _htmlSerializeOutputResult: HTMLElement; - private _writeToTerminalCheckbox: HTMLInputElement; - private _imageStorageLimitInput: HTMLInputElement; - private _imageShowPlaceholderCheckbox: HTMLInputElement; - private _imageOptionsTextarea: HTMLTextAreaElement; public build(container: HTMLElement): void { // Heading @@ -43,235 +27,9 @@ export class AddonsWindow extends BaseWindow implements IControlWindow { this._addonsContainer = document.createElement('div'); this._addonsContainer.id = 'addons-container'; container.appendChild(this._addonsContainer); - - // Addons Control section - const controlHeading = document.createElement('h3'); - controlHeading.textContent = 'Addons Control'; - container.appendChild(controlHeading); - - // SearchAddon section - this._buildSearchSection(container); - - // SerializeAddon section - this._buildSerializeSection(container); - - // ImageAddon section - this._buildImageSection(container); - } - - private _buildSearchSection(container: HTMLElement): void { - const h4 = document.createElement('h4'); - h4.textContent = 'SearchAddon'; - container.appendChild(h4); - - const wrapper = document.createElement('div'); - wrapper.style.display = 'flex'; - wrapper.style.flexDirection = 'column'; - - // Find next - const findNextLabel = document.createElement('label'); - findNextLabel.textContent = 'Find next '; - this._findNextInput = document.createElement('input'); - this._findNextInput.id = 'find-next'; - findNextLabel.appendChild(this._findNextInput); - wrapper.appendChild(findNextLabel); - - // Find previous - const findPrevLabel = document.createElement('label'); - findPrevLabel.textContent = 'Find previous '; - this._findPreviousInput = document.createElement('input'); - this._findPreviousInput.id = 'find-previous'; - findPrevLabel.appendChild(this._findPreviousInput); - wrapper.appendChild(findPrevLabel); - - // Results - const resultsDiv = document.createElement('div'); - resultsDiv.textContent = 'Results: '; - this._findResultsSpan = document.createElement('span'); - this._findResultsSpan.id = 'find-results'; - resultsDiv.appendChild(this._findResultsSpan); - wrapper.appendChild(resultsDiv); - - // Regex checkbox - const regexLabel = document.createElement('label'); - this._regexCheckbox = document.createElement('input'); - this._regexCheckbox.type = 'checkbox'; - this._regexCheckbox.id = 'regex'; - regexLabel.appendChild(this._regexCheckbox); - regexLabel.appendChild(document.createTextNode('Use regex')); - wrapper.appendChild(regexLabel); - - // Case sensitive checkbox - const caseLabel = document.createElement('label'); - this._caseSensitiveCheckbox = document.createElement('input'); - this._caseSensitiveCheckbox.type = 'checkbox'; - this._caseSensitiveCheckbox.id = 'case-sensitive'; - caseLabel.appendChild(this._caseSensitiveCheckbox); - caseLabel.appendChild(document.createTextNode('Case sensitive')); - wrapper.appendChild(caseLabel); - - // Whole word checkbox - const wholeWordLabel = document.createElement('label'); - this._wholeWordCheckbox = document.createElement('input'); - this._wholeWordCheckbox.type = 'checkbox'; - this._wholeWordCheckbox.id = 'whole-word'; - wholeWordLabel.appendChild(this._wholeWordCheckbox); - wholeWordLabel.appendChild(document.createTextNode('Whole word')); - wrapper.appendChild(wholeWordLabel); - - // Highlight all matches checkbox - const highlightLabel = document.createElement('label'); - this._highlightAllMatchesCheckbox = document.createElement('input'); - this._highlightAllMatchesCheckbox.type = 'checkbox'; - this._highlightAllMatchesCheckbox.id = 'highlight-all-matches'; - this._highlightAllMatchesCheckbox.checked = true; - highlightLabel.appendChild(this._highlightAllMatchesCheckbox); - highlightLabel.appendChild(document.createTextNode('Highlight All Matches')); - wrapper.appendChild(highlightLabel); - - container.appendChild(wrapper); - } - - private _buildSerializeSection(container: HTMLElement): void { - const h4 = document.createElement('h4'); - h4.textContent = 'SerializeAddon'; - container.appendChild(h4); - - const wrapper = document.createElement('div'); - - // Serialize button - 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 - const writeLabel = document.createElement('label'); - this._writeToTerminalCheckbox = document.createElement('input'); - this._writeToTerminalCheckbox.type = 'checkbox'; - this._writeToTerminalCheckbox.id = 'write-to-terminal'; - writeLabel.appendChild(this._writeToTerminalCheckbox); - writeLabel.appendChild(document.createTextNode('Write back to terminal')); - wrapper.appendChild(writeLabel); - - // Serialize output - const outputDiv = document.createElement('div'); - this._serializeOutputPre = document.createElement('pre'); - this._serializeOutputPre.id = 'serialize-output'; - outputDiv.appendChild(this._serializeOutputPre); - wrapper.appendChild(outputDiv); - - // HTML serialize button - 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 - this._htmlSerializeOutputResult = document.createElement('span'); - this._htmlSerializeOutputResult.id = 'htmlserialize-output-result'; - wrapper.appendChild(this._htmlSerializeOutputResult); - - // HTML serialize output - const htmlOutputDiv = document.createElement('div'); - this._htmlSerializeOutputPre = document.createElement('pre'); - this._htmlSerializeOutputPre.id = 'htmlserialize-output'; - htmlOutputDiv.appendChild(this._htmlSerializeOutputPre); - wrapper.appendChild(htmlOutputDiv); - - container.appendChild(wrapper); - } - - private _buildImageSection(container: HTMLElement): void { - const h4 = document.createElement('h4'); - h4.textContent = 'Image Addon'; - container.appendChild(h4); - - const details = document.createElement('details'); - const summary = document.createElement('summary'); - summary.textContent = 'image addon settings'; - details.appendChild(summary); - - const wrapper = document.createElement('div'); - - // Storage limit - const storageLimitLabel = document.createElement('label'); - storageLimitLabel.textContent = 'Storage Limit (in MB) '; - this._imageStorageLimitInput = document.createElement('input'); - this._imageStorageLimitInput.type = 'number'; - this._imageStorageLimitInput.id = 'image-storagelimit'; - storageLimitLabel.appendChild(this._imageStorageLimitInput); - wrapper.appendChild(storageLimitLabel); - wrapper.appendChild(document.createElement('br')); - - // Show placeholder - const placeholderLabel = document.createElement('label'); - placeholderLabel.textContent = 'Show Placeholder '; - this._imageShowPlaceholderCheckbox = document.createElement('input'); - this._imageShowPlaceholderCheckbox.type = 'checkbox'; - this._imageShowPlaceholderCheckbox.id = 'image-showplaceholder'; - placeholderLabel.appendChild(this._imageShowPlaceholderCheckbox); - wrapper.appendChild(placeholderLabel); - wrapper.appendChild(document.createElement('br')); - wrapper.appendChild(document.createElement('br')); - - // Ctor options - const optionsLabel = document.createElement('label'); - optionsLabel.appendChild(document.createTextNode('Ctor options (applied on addon relaunch)')); - optionsLabel.appendChild(document.createElement('br')); - this._imageOptionsTextarea = document.createElement('textarea'); - this._imageOptionsTextarea.id = 'image-options'; - this._imageOptionsTextarea.cols = 40; - this._imageOptionsTextarea.rows = 12; - optionsLabel.appendChild(this._imageOptionsTextarea); - wrapper.appendChild(optionsLabel); - - details.appendChild(wrapper); - container.appendChild(details); } public get addonsContainer(): HTMLElement { return this._addonsContainer; } - - public get findNextInput(): HTMLInputElement { - return this._findNextInput; - } - - public get findPreviousInput(): HTMLInputElement { - return this._findPreviousInput; - } - - public get findResultsSpan(): HTMLElement { - 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'; } diff --git a/demo/client/components/window/gpuWindow.ts b/demo/client/components/window/gpuWindow.ts index 95d178fa..05827287 100644 --- a/demo/client/components/window/gpuWindow.ts +++ b/demo/client/components/window/gpuWindow.ts @@ -8,13 +8,13 @@ import type { IControlWindow } from '../controlBar'; export class GpuWindow extends BaseWindow implements IControlWindow { public readonly id = 'gpu'; - public readonly label = 'GPU'; + public readonly label = 'WebGL'; private _textureAtlasContainer: HTMLElement; public build(container: HTMLElement): void { const heading = document.createElement('h3'); - heading.textContent = 'GPU'; + heading.textContent = 'WebGL'; container.appendChild(heading); const zoomCheckbox = document.createElement('input'); diff --git a/demo/client/components/window/webglWindow.ts b/demo/client/components/window/webglWindow.ts new file mode 100644 index 00000000..30544241 --- /dev/null +++ b/demo/client/components/window/webglWindow.ts @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { BaseWindow } from './baseWindow'; +import type { IControlWindow } from '../controlBar'; + +export class WebglWindow extends BaseWindow implements IControlWindow { + public readonly id = 'addon-webgl'; + public readonly label = 'webgl'; + + private _textureAtlasContainer: HTMLElement; + + public build(container: HTMLElement): void { + const heading = document.createElement('h3'); + heading.textContent = 'WebGL'; + container.appendChild(heading); + + const zoomCheckbox = document.createElement('input'); + zoomCheckbox.type = 'checkbox'; + zoomCheckbox.id = 'texture-atlas-zoom'; + container.appendChild(zoomCheckbox); + + const zoomLabel = document.createElement('label'); + zoomLabel.htmlFor = 'texture-atlas-zoom'; + zoomLabel.textContent = 'Zoom texture atlas'; + container.appendChild(zoomLabel); + + this._textureAtlasContainer = document.createElement('div'); + this._textureAtlasContainer.id = 'texture-atlas'; + container.appendChild(this._textureAtlasContainer); + } + + public setTextureAtlas(canvas: HTMLCanvasElement): void { + this._styleAtlasPage(canvas); + this._textureAtlasContainer.replaceChildren(canvas); + } + + public appendTextureAtlas(canvas: HTMLCanvasElement): void { + this._styleAtlasPage(canvas); + this._textureAtlasContainer.appendChild(canvas); + } + + public removeTextureAtlas(canvas: HTMLCanvasElement): void { + canvas.remove(); + } + + private _styleAtlasPage(canvas: HTMLCanvasElement): void { + canvas.style.width = `${canvas.width / window.devicePixelRatio}px`; + canvas.style.height = `${canvas.height / window.devicePixelRatio}px`; + } +}