From c375696ee65777fb6b0cbe97636c119c8d0783f5 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 31 Jan 2026 09:22:45 -0800 Subject: [PATCH] Make demo use strict TS compiler mode --- demo/client/client.ts | 140 +++++++++--------- .../components/window/addonImageWindow.ts | 6 +- .../components/window/addonSearchWindow.ts | 14 +- .../components/window/addonSerializeWindow.ts | 12 +- demo/client/components/window/addonsWindow.ts | 2 +- .../components/window/cellInspectorWindow.ts | 16 +- demo/client/components/window/gpuWindow.ts | 2 +- .../client/components/window/optionsWindow.ts | 32 ++-- demo/client/components/window/styleWindow.ts | 2 +- demo/client/components/window/testWindow.ts | 80 +++++----- demo/client/components/window/vtWindow.ts | 5 +- demo/client/components/window/webglWindow.ts | 2 +- demo/client/tsconfig.json | 1 + 13 files changed, 160 insertions(+), 154 deletions(-) diff --git a/demo/client/client.ts b/demo/client/client.ts index 68202ad7..45bdd1d4 100644 --- a/demo/client/client.ts +++ b/demo/client/client.ts @@ -58,11 +58,11 @@ export interface IWindowWithTerminal extends Window { } declare let window: IWindowWithTerminal; -let term; -let protocol; -let socketURL; -let socket; -let pid; +let term: Terminal | null; +let protocol: string; +let socketURL: string; +let socket: WebSocket | null; +let pid: string; let controlBar: ControlBar; let addonsWindow: AddonsWindow; let addonSearchWindow: AddonSearchWindow; @@ -73,7 +73,7 @@ const addons: AddonCollection = { attach: { name: 'attach', ctor: AttachAddon, canChange: false }, clipboard: { name: 'clipboard', ctor: ClipboardAddon, canChange: true }, fit: { name: 'fit', ctor: FitAddon, canChange: false }, - image: { name: 'image', ctor: ImageAddon, canChange: true }, + image: { name: 'image', ctor: ImageAddon!, canChange: true }, progress: { name: 'progress', ctor: ProgressAddon, canChange: true }, search: { name: 'search', ctor: SearchAddon, canChange: true }, serialize: { name: 'serialize', ctor: SerializeAddon, canChange: true }, @@ -116,8 +116,8 @@ const xtermjsTheme = { brightWhite: '#FFFFFF' } satisfies ITheme; function setPadding(): void { - term.element.style.padding = parseInt(paddingElement.value, 10).toString() + 'px'; - addons.fit.instance.fit(); + term!.element!.style.padding = parseInt(paddingElement.value, 10).toString() + 'px'; + addons.fit.instance!.fit(); } function getSearchOptions(): ISearchOptions { @@ -141,7 +141,7 @@ const disposeRecreateButtonHandler: () => void = () => { if (term) { term.dispose(); term = null; - window.term = null; + (window as any).term = null; socket = null; addons.attach.instance = undefined; addons.clipboard.instance = undefined; @@ -154,10 +154,10 @@ const disposeRecreateButtonHandler: () => void = () => { addons.ligatures.instance = undefined; addons.webLinks.instance = undefined; addons.webgl.instance = undefined; - document.getElementById('dispose').innerHTML = 'Recreate Terminal'; + document.getElementById('dispose')!.innerHTML = 'Recreate Terminal'; } else { createTerminal(); - document.getElementById('dispose').innerHTML = 'Dispose terminal'; + document.getElementById('dispose')!.innerHTML = 'Dispose terminal'; } }; @@ -165,7 +165,7 @@ const createNewWindowButtonHandler: () => void = () => { if (term) { disposeRecreateButtonHandler(); } - const win = window.open(); + const win = window.open()!; terminalContainer = win.document.createElement('div'); terminalContainer.id = 'terminal-container'; win.document.body.appendChild(terminalContainer); @@ -207,7 +207,7 @@ if (document.location.pathname === '/test') { } else { const typedTerm = createTerminal(); - controlBar = new ControlBar(document.getElementById('sidebar'), document.querySelector('.banner-tabs'), []); + controlBar = new ControlBar(document.getElementById('sidebar')!, document.querySelector('.banner-tabs')!, []); optionsWindow = controlBar.registerWindow(new OptionsWindow(typedTerm, addons, { updateTerminalSize, updateTerminalContainerBackground })); const styleWindow = controlBar.registerWindow(new StyleWindow(typedTerm, addons)); controlBar.registerWindow(new CellInspectorWindow(typedTerm, addons)); @@ -234,43 +234,43 @@ if (document.location.pathname === '/test') { controlBar.setTabVisible('addon-serialize', true); controlBar.setTabVisible('addon-image', true); controlBar.setTabVisible('addon-web-fonts', 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)); + 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); addDomListener(actionElements.findNext, 'keydown', (e) => { if (e.key === 'Enter') { - addons.search.instance.findNext(actionElements.findNext.value, getSearchOptions()); + addons.search.instance!.findNext(actionElements.findNext.value, getSearchOptions()); e.preventDefault(); } }); addDomListener(actionElements.findNext, 'input', (e) => { - addons.search.instance.findNext(actionElements.findNext.value, getSearchOptions()); + addons.search.instance!.findNext(actionElements.findNext.value, getSearchOptions()); }); addDomListener(actionElements.findPrevious, 'keydown', (e) => { if (e.key === 'Enter') { - addons.search.instance.findPrevious(actionElements.findPrevious.value, getSearchOptions()); + addons.search.instance!.findPrevious(actionElements.findPrevious.value, getSearchOptions()); e.preventDefault(); } }); addDomListener(actionElements.findPrevious, 'input', (e) => { - addons.search.instance.findPrevious(actionElements.findPrevious.value, getSearchOptions()); + addons.search.instance!.findPrevious(actionElements.findPrevious.value, getSearchOptions()); }); addDomListener(actionElements.findNext, 'blur', (e) => { - addons.search.instance.clearActiveDecoration(); + addons.search.instance!.clearActiveDecoration(); }); addDomListener(actionElements.findPrevious, 'blur', (e) => { - addons.search.instance.clearActiveDecoration(); + addons.search.instance!.clearActiveDecoration(); }); } function createTerminal(): Terminal { // Clean terminal - while (terminalContainer.children.length) { - terminalContainer.removeChild(terminalContainer.children[0]); + while (terminalContainer!.children.length) { + terminalContainer!.removeChild(terminalContainer!.children[0]); } const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].indexOf(navigator.platform) >= 0; @@ -290,7 +290,7 @@ function createTerminal(): Terminal { addons.search.instance = new SearchAddon(); addons.serialize.instance = new SerializeAddon(); addons.fit.instance = new FitAddon(); - addons.image.instance = new ImageAddon(); + addons.image.instance = new ImageAddon!(); addons.progress.instance = new ProgressAddon(); addons.unicodeGraphemes.instance = new UnicodeGraphemesAddon(); addons.clipboard.instance = new ClipboardAddon(); @@ -311,7 +311,7 @@ function createTerminal(): Terminal { typedTerm.loadAddon(addons.webFonts.instance); typedTerm.loadAddon(addons.clipboard.instance); - window.term = term; // Expose `term` to window for debugging purposes + (window as any).term = term; // Expose `term` to window for debugging purposes term.onResize((size: { cols: number, rows: number }) => { if (!pid) { return; @@ -330,7 +330,7 @@ function createTerminal(): Terminal { if (addons.webgl.instance) { try { typedTerm.loadAddon(addons.webgl.instance); - term.open(terminalContainer); + term.open(terminalContainer!); } catch (e) { console.warn('error during loading webgl addon:', e); addons.webgl.instance.dispose(); @@ -339,7 +339,7 @@ function createTerminal(): Terminal { } if (!typedTerm.element) { // webgl loading failed for some reason, attach with DOM renderer - term.open(terminalContainer); + term.open(terminalContainer!); } term.focus(); @@ -349,13 +349,13 @@ function createTerminal(): Terminal { if (optionsWindow.autoResize) { // In general this should be debounced to avoid excessive work on the main // thread by firing the expensive resize action repeatedly - addons.fit.instance.fit(); + addons.fit.instance!.fit(); } }); - resizeObserver.observe(terminalContainer); + resizeObserver.observe(terminalContainer!); window.addEventListener('resize', () => { - terminalContainer.style.width = document.body.clientWidth + 'px'; + terminalContainer!.style.width = document.body.clientWidth + 'px'; }); // fit is called within a setTimeout, cols and rows need this. @@ -369,7 +369,7 @@ function createTerminal(): Terminal { if (useRealTerminal instanceof HTMLInputElement && !useRealTerminal.checked) { runFakeTerminal(); } else { - const res = await fetch('/terminals?cols=' + term.cols + '&rows=' + term.rows, { method: 'POST' }); + const res = await fetch('/terminals?cols=' + term!.cols + '&rows=' + term!.rows, { method: 'POST' }); const processId = await res.text(); pid = processId; socketURL += processId; @@ -384,52 +384,52 @@ function createTerminal(): Terminal { } function runRealTerminal(): void { - addons.attach.instance = new AttachAddon(socket); - term.loadAddon(addons.attach.instance); - term._initialized = true; - initAddons(term); + addons.attach.instance = new AttachAddon(socket!); + term!.loadAddon(addons.attach.instance); + (term as any)._initialized = true; + initAddons(term!); } function runFakeTerminal(): void { - if (term._initialized) { + if ((term as any)._initialized) { return; } - term._initialized = true; - initAddons(term); + (term as any)._initialized = true; + initAddons(term!); - term.prompt = () => { - term.write('\r\n$ '); + (term as any).prompt = () => { + term!.write('\r\n$ '); }; - term.writeln('Welcome to xterm.js'); - term.writeln('This is a local terminal emulation, without a real terminal in the back-end.'); - term.writeln('Type some keys and commands to play around.'); - term.writeln(''); - term.prompt(); + term!.writeln('Welcome to xterm.js'); + term!.writeln('This is a local terminal emulation, without a real terminal in the back-end.'); + term!.writeln('Type some keys and commands to play around.'); + term!.writeln(''); + (term as any).prompt(); - term.onKey((e: { key: string, domEvent: KeyboardEvent }) => { + term!.onKey((e: { key: string, domEvent: KeyboardEvent }) => { const ev = e.domEvent; const printable = !ev.altKey && !ev.ctrlKey && !ev.metaKey; if (ev.keyCode === 13) { - term.prompt(); + (term as any).prompt(); } else if (ev.keyCode === 8) { // Do not delete the prompt - if (term._core.buffer.x > 2) { - term.write('\b \b'); + if ((term as any)._core.buffer.x > 2) { + term!.write('\b \b'); } } else if (printable) { - term.write(e.key); + term!.write(e.key); } }); } function updateTerminalContainerBackground(): void { - if (term.options.allowTransparency) { - terminalContainer.style.background = 'repeating-conic-gradient(#000000 0% 25%, #101010 0% 50%) 50% / 20px 20px'; + if (term!.options.allowTransparency) { + terminalContainer!.style.background = 'repeating-conic-gradient(#000000 0% 25%, #101010 0% 50%) 50% / 20px 20px'; } else { - terminalContainer.style.background = term.options.theme?.background ?? '#000000'; + terminalContainer!.style.background = term!.options.theme?.background ?? '#000000'; } } @@ -439,19 +439,19 @@ function initAddons(term: Terminal): void { function postInitWebgl(): void { controlBar.setTabVisible('addon-webgl', true); setTimeout(() => { - addonWebglWindow.setTextureAtlas(addons.webgl.instance.textureAtlas); - addons.webgl.instance.onChangeTextureAtlas(e => addonWebglWindow.setTextureAtlas(e)); - addons.webgl.instance.onAddTextureAtlasCanvas(e => addonWebglWindow.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('addon-webgl', false); - if (addons.webgl.instance.textureAtlas) { - addons.webgl.instance.textureAtlas.remove(); + if (addons.webgl.instance!.textureAtlas) { + addons.webgl.instance!.textureAtlas.remove(); } } - Object.keys(addons).forEach((name: AddonType) => { + (Object.keys(addons) as AddonType[]).forEach(name => { const addon = addons[name]; const checkbox = document.createElement('input') as HTMLInputElement; checkbox.type = 'checkbox'; @@ -466,12 +466,12 @@ function initAddons(term: Terminal): void { term.unicode.activeVersion = '15-graphemes'; } if (name === 'search' && checkbox.checked) { - addons[name].instance.onDidChangeResults(e => updateFindResults(e)); + addons[name].instance!.onDidChangeResults(e => updateFindResults(e)); } addDomListener(checkbox, 'change', () => { if (name === 'image') { if (checkbox.checked) { - const ctorOptionsJson = document.querySelector('#image-options').value; + const ctorOptionsJson = document.querySelector('#image-options')!.value; addon.instance = ctorOptionsJson ? new addons[name].ctor(JSON.parse(ctorOptionsJson)) : new addons[name].ctor(); @@ -497,7 +497,7 @@ function initAddons(term: Terminal): void { term.unicode.activeVersion = '15-graphemes'; } else if (name === 'search') { controlBar.setTabVisible('addon-search', true); - addons[name].instance.onDidChangeResults(e => updateFindResults(e)); + addons[name].instance!.onDidChangeResults(e => updateFindResults(e)); } else if (name === 'serialize') { controlBar.setTabVisible('addon-serialize', true); } @@ -587,17 +587,17 @@ function updateFindResults(e: { resultIndex: number, resultCount: number } | und function addDomListener(element: HTMLElement, type: string, handler: (...args: any[]) => any): void { element.addEventListener(type, handler); - term._core._register({ dispose: () => element.removeEventListener(type, handler) }); + (term as any)._core._register({ dispose: () => element.removeEventListener(type, handler) }); } function updateTerminalSize(): void { const width = optionsWindow.autoResize ? '100%' - : (term.dimensions.css.canvas.width + term._core.viewport.scrollBarWidth).toString() + 'px'; + : ((term as any).dimensions.css.canvas.width + (term as any)._core.viewport.scrollBarWidth).toString() + 'px'; const height = optionsWindow.autoResize ? '100%' - : (term.dimensions.css.canvas.height).toString() + 'px'; - terminalContainer.style.width = width; - terminalContainer.style.height = height; - addons.fit.instance.fit(); + : ((term as any).dimensions.css.canvas.height).toString() + 'px'; + terminalContainer!.style.width = width; + terminalContainer!.style.height = height; + addons.fit.instance!.fit(); } (console as any).image = (source: ImageData | HTMLCanvasElement, scale: number = 1) => { diff --git a/demo/client/components/window/addonImageWindow.ts b/demo/client/components/window/addonImageWindow.ts index 6c814622..87ebaff7 100644 --- a/demo/client/components/window/addonImageWindow.ts +++ b/demo/client/components/window/addonImageWindow.ts @@ -10,9 +10,9 @@ 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; + private _imageStorageLimitInput!: HTMLInputElement; + private _imageShowPlaceholderCheckbox!: HTMLInputElement; + private _imageOptionsTextarea!: HTMLTextAreaElement; public build(container: HTMLElement): void { // Storage limit diff --git a/demo/client/components/window/addonSearchWindow.ts b/demo/client/components/window/addonSearchWindow.ts index bb4e15f7..2891fca9 100644 --- a/demo/client/components/window/addonSearchWindow.ts +++ b/demo/client/components/window/addonSearchWindow.ts @@ -10,13 +10,13 @@ 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; + 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 wrapper = document.createElement('div'); diff --git a/demo/client/components/window/addonSerializeWindow.ts b/demo/client/components/window/addonSerializeWindow.ts index ebc3df36..cb8a72ec 100644 --- a/demo/client/components/window/addonSerializeWindow.ts +++ b/demo/client/components/window/addonSerializeWindow.ts @@ -10,10 +10,10 @@ 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; + private _serializeOutputPre!: HTMLPreElement; + private _htmlSerializeOutputPre!: HTMLPreElement; + private _htmlSerializeOutputResult!: HTMLElement; + private _writeToTerminalCheckbox!: HTMLInputElement; public build(container: HTMLElement): void { const wrapper = document.createElement('div'); @@ -64,7 +64,7 @@ export class AddonSerializeWindow extends BaseWindow implements IControlWindow { } private _serializeButtonHandler(): void { - const output = this._addons.serialize.instance.serialize(); + const output = this._addons.serialize.instance!.serialize(); const outputString = JSON.stringify(output); this._serializeOutputPre.innerText = outputString; @@ -75,7 +75,7 @@ export class AddonSerializeWindow extends BaseWindow implements IControlWindow { } private _htmlSerializeButtonHandler(): void { - const output = this._addons.serialize.instance.serializeAsHTML(); + const output = this._addons.serialize.instance!.serializeAsHTML(); this._htmlSerializeOutputPre.innerText = output; // Deprecated, but the most supported for now. diff --git a/demo/client/components/window/addonsWindow.ts b/demo/client/components/window/addonsWindow.ts index c69bf521..8dc31a07 100644 --- a/demo/client/components/window/addonsWindow.ts +++ b/demo/client/components/window/addonsWindow.ts @@ -10,7 +10,7 @@ export class AddonsWindow extends BaseWindow implements IControlWindow { public readonly id = 'addons'; public readonly label = 'Addons'; - private _addonsContainer: HTMLElement; + private _addonsContainer!: HTMLElement; public build(container: HTMLElement): void { // Description diff --git a/demo/client/components/window/cellInspectorWindow.ts b/demo/client/components/window/cellInspectorWindow.ts index 9098fc5e..b7a50bf6 100644 --- a/demo/client/components/window/cellInspectorWindow.ts +++ b/demo/client/components/window/cellInspectorWindow.ts @@ -30,14 +30,14 @@ export class CellInspectorWindow extends BaseWindow implements IControlWindow { public readonly id = 'cell-inspector'; public readonly label = 'Cell Inspector'; - private _container: HTMLElement; - private _positionEl: HTMLElement; - private _charEl: HTMLElement; - private _codeEl: HTMLElement; - private _widthEl: HTMLElement; - private _fgEl: HTMLElement; - private _bgEl: HTMLElement; - private _attrsEl: HTMLElement; + private _container!: HTMLElement; + private _positionEl!: HTMLElement; + private _charEl!: HTMLElement; + private _codeEl!: HTMLElement; + private _widthEl!: HTMLElement; + private _fgEl!: HTMLElement; + private _bgEl!: HTMLElement; + private _attrsEl!: HTMLElement; public build(container: HTMLElement): void { this._container = container; diff --git a/demo/client/components/window/gpuWindow.ts b/demo/client/components/window/gpuWindow.ts index 84bccf11..ac7b2c3d 100644 --- a/demo/client/components/window/gpuWindow.ts +++ b/demo/client/components/window/gpuWindow.ts @@ -10,7 +10,7 @@ export class GpuWindow extends BaseWindow implements IControlWindow { public readonly id = 'gpu'; public readonly label = 'WebGL'; - private _textureAtlasContainer: HTMLElement; + private _textureAtlasContainer!: HTMLElement; public build(container: HTMLElement): void { const zoomCheckbox = document.createElement('input'); diff --git a/demo/client/components/window/optionsWindow.ts b/demo/client/components/window/optionsWindow.ts index 7f8c4e2f..1071cfd5 100644 --- a/demo/client/components/window/optionsWindow.ts +++ b/demo/client/components/window/optionsWindow.ts @@ -80,8 +80,8 @@ export class OptionsWindow extends BaseWindow implements IControlWindow { public readonly id = 'options'; public readonly label = 'Options'; - private _container: HTMLElement; - private _optionsContainer: HTMLElement; + private _container!: HTMLElement; + private _optionsContainer!: HTMLElement; private _autoResize: boolean = true; constructor( @@ -143,7 +143,7 @@ export class OptionsWindow extends BaseWindow implements IControlWindow { const booleanOptions: string[] = []; const numberOptions: string[] = []; options.filter(o => blacklistedOptions.indexOf(o) === -1).forEach(o => { - switch (typeof this._terminal.options[o]) { + switch (typeof (this._terminal.options as Record)[o]) { case 'boolean': booleanOptions.push(o); break; @@ -160,25 +160,25 @@ export class OptionsWindow extends BaseWindow implements IControlWindow { let html = ''; html += '
'; booleanOptions.forEach(o => { - html += `
`; + html += `
`; }); nestedBooleanOptions.forEach(({ label, parent, prop }) => { - const checked = this._terminal.options[parent]?.[prop] ?? false; + const checked = (this._terminal.options as Record | undefined>)[parent]?.[prop] ?? false; html += `
`; }); html += '
'; numberOptions.forEach(o => { - html += `
`; + html += `
`; }); html += '
'; Object.keys(stringOptions).forEach(o => { if (o === 'colsRows') { html += `
`; } else if (stringOptions[o]) { - const selectedOption = o === 'theme' ? 'xtermjs' : this._terminal.options[o]; + const selectedOption = o === 'theme' ? 'xtermjs' : (this._terminal.options as Record)[o]; html += `
`; } else { - html += `
`; + html += `
`; } }); html += '
'; @@ -190,7 +190,7 @@ export class OptionsWindow extends BaseWindow implements IControlWindow { const input = document.getElementById(`opt-${o}`) as HTMLInputElement; addDomListener(input, 'change', () => { console.log('change', o, input.checked); - this._terminal.options[o] = input.checked; + (this._terminal.options as Record)[o] = input.checked; if (o ==='allowTransparency') { this._terminal.options.theme = this._getTheme(); this._handlers.updateTerminalContainerBackground(); @@ -201,7 +201,7 @@ export class OptionsWindow extends BaseWindow implements IControlWindow { const input = document.getElementById(`opt-${label.replace('.', '-')}`) as HTMLInputElement; addDomListener(input, 'change', () => { console.log('change', label, input.checked); - this._terminal.options[parent] = { ...this._terminal.options[parent], [prop]: input.checked }; + (this._terminal.options as Record)[parent] = { ...(this._terminal.options as Record | undefined>)[parent], [prop]: input.checked }; }); }); numberOptions.forEach(o => { @@ -216,7 +216,7 @@ export class OptionsWindow extends BaseWindow implements IControlWindow { this._terminal.options.scrollback = parseInt(input.value); setTimeout(() => this._handlers.updateTerminalSize(), 5); } else { - this._terminal.options[o] = parseInt(input.value); + (this._terminal.options as Record)[o] = parseInt(input.value); } this._handlers.updateTerminalSize(); }); @@ -225,7 +225,7 @@ export class OptionsWindow extends BaseWindow implements IControlWindow { const input = document.getElementById(`opt-${o}`) as HTMLInputElement; addDomListener(input, 'change', () => { console.log('change', o, input.value); - let value: any = input.value; + let value: unknown = input.value; if (o === 'colsRows') { const m = input.value.match(/^([0-9]+)x([0-9]+)$/); if (m) { @@ -239,7 +239,7 @@ export class OptionsWindow extends BaseWindow implements IControlWindow { } else if (o === 'theme') { value = this._getTheme(); } - this._terminal.options[o] = value; + (this._terminal.options as Record)[o] = value; if (o === 'theme') { this._handlers.updateTerminalContainerBackground(); } @@ -259,11 +259,11 @@ export class OptionsWindow extends BaseWindow implements IControlWindow { } private _getTheme(): ITheme { - const input = document.querySelector('#opt-theme'); - let theme: ITheme; + const input = document.querySelector('#opt-theme')!; + let theme: ITheme = {}; switch (input.value) { case 'default': - theme = undefined; + theme = {}; break; case 'xtermjs': theme = { ...xtermjsTheme }; diff --git a/demo/client/components/window/styleWindow.ts b/demo/client/components/window/styleWindow.ts index 94cf6e4c..0a6fbed5 100644 --- a/demo/client/components/window/styleWindow.ts +++ b/demo/client/components/window/styleWindow.ts @@ -10,7 +10,7 @@ export class StyleWindow extends BaseWindow implements IControlWindow { public readonly id = 'style'; public readonly label = 'Style'; - private _paddingElement: HTMLInputElement; + private _paddingElement!: HTMLInputElement; public build(container: HTMLElement): void { const wrapper = document.createElement('div'); diff --git a/demo/client/components/window/testWindow.ts b/demo/client/components/window/testWindow.ts index 12c6e995..e8e9ed66 100644 --- a/demo/client/components/window/testWindow.ts +++ b/demo/client/components/window/testWindow.ts @@ -139,7 +139,9 @@ export class TestWindow extends BaseWindow implements IControlWindow { } dd.appendChild(button); dl.appendChild(dd); - button.addEventListener('click', handler); + if (handler) { + button.addEventListener('click', handler); + } } private _addDdWithCheckbox(dl: HTMLElement, id: string, label: string, title: string, checked: boolean): void { @@ -465,7 +467,7 @@ function customGlyphAlignmentHandler(term: Terminal): void { 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(`${fillChar.codePointAt(0)!.toString(16).toUpperCase().padEnd(5, ' ')} `); } term.write('\n\r'); for (let i = 0; i < 3; i++) { @@ -864,7 +866,7 @@ function addDecoration(term: Terminal, dim: number = 1): void { foregroundColor: '#00FE00', overviewRulerOptions: { color: '#ef292980', position: 'left' } }); - decoration.onRender((e: HTMLElement) => { + decoration?.onRender((e: HTMLElement) => { e.style.right = '100%'; e.style.backgroundColor = '#ef292980'; }); @@ -896,13 +898,16 @@ function decorationStressTest(term: Terminal): void { 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({ + const decoration = term.registerDecoration({ marker: term.registerMarker(cursorOffsetY), x, width: 4, backgroundColor: '#FF0000', overviewRulerOptions: { color: '#FF0000' } - })); + }); + if (decoration) { + decorationStressTestDecorations.push(decoration); + } } } } @@ -919,56 +924,57 @@ function initProgress(term: Terminal, addons: AddonCollection): void { // 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.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'; + 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; + 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 () => { + 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\\')); + 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('#image-storagelimit'); - limitStorageElement.valueAsNumber = addons.image.instance.storageLimit; + const imageAddon = addons.image.instance!; + const DEFAULT_OPTIONS: IImageAddonOptions = (imageAddon as any)._defaultOpts; + const limitStorageElement = document.querySelector('#image-storagelimit')!; + limitStorageElement.valueAsNumber = imageAddon.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); + imageAddon.storageLimit = limitStorageElement.valueAsNumber; + limitStorageElement.valueAsNumber = imageAddon.storageLimit; + console.log('changed storageLimit to', imageAddon.storageLimit); } catch (e) { - limitStorageElement.valueAsNumber = addons.image.instance.storageLimit; - console.log('storageLimit at', addons.image.instance.storageLimit); + limitStorageElement.valueAsNumber = imageAddon.storageLimit; + console.log('storageLimit at', imageAddon.storageLimit); throw e; } }); - const showPlaceholderElement = document.querySelector('#image-showplaceholder'); - showPlaceholderElement.checked = addons.image.instance.showPlaceholder; + const showPlaceholderElement = document.querySelector('#image-showplaceholder')!; + showPlaceholderElement.checked = imageAddon.showPlaceholder; addDomListener(term, showPlaceholderElement, 'change', () => { - addons.image.instance.showPlaceholder = showPlaceholderElement.checked; + imageAddon.showPlaceholder = showPlaceholderElement.checked; }); - const ctorOptionsElement = document.querySelector('#image-options'); + const ctorOptionsElement = document.querySelector('#image-options')!; ctorOptionsElement.value = JSON.stringify(DEFAULT_OPTIONS, null, 2); const sixelDemo = (url: string) => () => fetch(url) @@ -988,16 +994,16 @@ function initImageAddonExposed(term: Terminal, addons: AddonCollection): void { term.write(`\x1b]1337;File=inline=1;size=${data.length}:${btoa(sdata)}\x1b\\`); }); - document.getElementById('image-demo1').addEventListener('click', + document.getElementById('image-demo1')!.addEventListener('click', sixelDemo('https://raw.githubusercontent.com/saitoha/libsixel/master/images/snake.six')); - document.getElementById('image-demo2').addEventListener('click', + document.getElementById('image-demo2')!.addEventListener('click', sixelDemo('https://raw.githubusercontent.com/jerch/node-sixel/master/testfiles/test2.sixel')); - document.getElementById('image-demo3').addEventListener('click', + 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; + term.element!.addEventListener('click', (ev: MouseEvent) => { + if (!ev.ctrlKey || !imageAddon) return; // TODO... // if (ev.altKey) { @@ -1015,10 +1021,10 @@ function initImageAddonExposed(term: Terminal, addons: AddonCollection): void { 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) + ? imageAddon.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')); + : imageAddon.getImageAtBufferCell(x, term.buffer.active.viewportY + y); + canvas?.toBlob(data => data && window.open(URL.createObjectURL(data), '_blank')); }); } diff --git a/demo/client/components/window/vtWindow.ts b/demo/client/components/window/vtWindow.ts index b5d01367..08f481b1 100644 --- a/demo/client/components/window/vtWindow.ts +++ b/demo/client/components/window/vtWindow.ts @@ -11,8 +11,7 @@ export class VtWindow extends BaseWindow implements IControlWindow { public readonly id = 'vt'; public readonly label = 'VT'; - private _container: HTMLElement; - private _term: Terminal | undefined; + private _container!: HTMLElement; public build(container: HTMLElement): void { this._container = container; @@ -65,7 +64,7 @@ export class VtWindow extends BaseWindow implements IControlWindow { const writeCsiSplit = writeCsi.split('|'); const prefix = writeCsiSplit.length === 2 ? writeCsiSplit[0] : ''; const suffix = writeCsiSplit[writeCsiSplit.length - 1]; - element.addEventListener('click', () => this._term?.write(this._csi(`${prefix}${inputs.map(e => e.value).join(';')}${suffix}`))); + element.addEventListener('click', () => this._terminal.write(this._csi(`${prefix}${inputs.map(e => e.value).join(';')}${suffix}`))); const desc = document.createElement('span'); desc.textContent = description; diff --git a/demo/client/components/window/webglWindow.ts b/demo/client/components/window/webglWindow.ts index f4fabf63..b61be231 100644 --- a/demo/client/components/window/webglWindow.ts +++ b/demo/client/components/window/webglWindow.ts @@ -10,7 +10,7 @@ export class WebglWindow extends BaseWindow implements IControlWindow { public readonly id = 'addon-webgl'; public readonly label = 'webgl'; - private _textureAtlasContainer: HTMLElement; + private _textureAtlasContainer!: HTMLElement; public build(container: HTMLElement): void { const zoomCheckbox = document.createElement('input'); diff --git a/demo/client/tsconfig.json b/demo/client/tsconfig.json index 17aae876..6c601531 100644 --- a/demo/client/tsconfig.json +++ b/demo/client/tsconfig.json @@ -6,6 +6,7 @@ "rootDir": ".", "sourceMap": true, "baseUrl": ".", + "strict": true, "paths": { "@xterm/addon-attach": ["../../addons/addon-attach"], "@xterm/addon-clipboard": ["../../addons/addon-clipboard"],