Fix allowTransparency in lib and demo

Fixes #5558
Fixes #5560
This commit is contained in:
Daniel Imms
2025-12-31 05:00:33 -08:00
parent 982f5e15ff
commit d143340b60
7 changed files with 100 additions and 64 deletions
+5 -2
View File
@@ -91,8 +91,6 @@
}
.xterm .xterm-viewport {
/* On OS X this is required in order for the scroll bar to appear fully opaque */
background-color: #000;
overflow-y: scroll;
cursor: default;
position: absolute;
@@ -102,6 +100,11 @@
bottom: 0;
}
.xterm:not(.allow-transparency) .xterm-viewport {
/* On OS X this is required in order for the scroll bar to appear fully opaque */
background-color: #000;
}
.xterm .xterm-screen {
position: relative;
}
+6 -3
View File
@@ -277,7 +277,7 @@ function createTerminal(): Terminal {
buildNumber: 22621
} : undefined,
fontFamily: '"Fira Code", monospace, "Powerline Extra Symbols"',
theme: xtermjsTheme
theme: { ...xtermjsTheme }
} as ITerminalOptions);
// Load addons
@@ -419,8 +419,11 @@ function runFakeTerminal(): void {
}
function updateTerminalContainerBackground(): void {
const bg = term.options.theme?.background ?? '#000000';
terminalContainer.style.backgroundColor = bg;
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';
}
}
function initAddons(term: Terminal): void {
+75 -59
View File
@@ -8,7 +8,7 @@ import type { Terminal, ITheme } from '@xterm/xterm';
import type { IControlWindow } from '../controlBar';
import type { AddonCollection } from 'types';
const xtermjsTheme: ITheme = {
const xtermjsTheme: ITheme = Object.freeze({
foreground: '#F8F8F8',
background: '#2D2E2C',
selectionBackground: '#5DA5D533',
@@ -29,7 +29,52 @@ const xtermjsTheme: ITheme = {
brightCyan: '#72F0FF',
white: '#F8F8F8',
brightWhite: '#FFFFFF'
};
});
const sapphireTheme: ITheme = Object.freeze({
background: '#1c2431',
foreground: '#cccccc',
selectionBackground: '#399ef440',
black: '#666666',
blue: '#399ef4',
brightBlack: '#666666',
brightBlue: '#399ef4',
brightCyan: '#21c5c7',
brightGreen: '#4eb071',
brightMagenta: '#b168df',
brightRed: '#da6771',
brightWhite: '#efefef',
brightYellow: '#fff099',
cyan: '#21c5c7',
green: '#4eb071',
magenta: '#b168df',
red: '#da6771',
white: '#efefef',
yellow: '#fff099'
});
const lightTheme: ITheme = Object.freeze({
background: '#ffffff',
foreground: '#333333',
cursor: '#333333',
cursorAccent: '#ffffff',
selectionBackground: '#add6ff',
overviewRulerBorder: '#aaaaaa',
black: '#000000',
blue: '#0451a5',
brightBlack: '#666666',
brightBlue: '#0451a5',
brightCyan: '#0598bc',
brightGreen: '#14ce14',
brightMagenta: '#bc05bc',
brightRed: '#cd3131',
brightWhite: '#a5a5a5',
brightYellow: '#b5ba00',
cyan: '#0598bc',
green: '#00bc00',
magenta: '#bc05bc',
red: '#cd3131',
white: '#555555',
yellow: '#949800'
});
export class OptionsWindow extends BaseWindow implements IControlWindow {
public readonly id = 'options';
@@ -136,6 +181,10 @@ export class OptionsWindow extends BaseWindow implements IControlWindow {
addDomListener(input, 'change', () => {
console.log('change', o, input.checked);
this._terminal.options[o] = input.checked;
if (o ==='allowTransparency') {
this._terminal.options.theme = this._getTheme();
this._handlers.updateTerminalContainerBackground();
}
});
});
numberOptions.forEach(o => {
@@ -171,63 +220,7 @@ export class OptionsWindow extends BaseWindow implements IControlWindow {
this._handlers.updateTerminalSize();
}
} else if (o === 'theme') {
switch (input.value) {
case 'default':
value = undefined;
break;
case 'xtermjs':
value = xtermjsTheme;
break;
case 'sapphire':
value = {
background: '#1c2431',
foreground: '#cccccc',
selectionBackground: '#399ef440',
black: '#666666',
blue: '#399ef4',
brightBlack: '#666666',
brightBlue: '#399ef4',
brightCyan: '#21c5c7',
brightGreen: '#4eb071',
brightMagenta: '#b168df',
brightRed: '#da6771',
brightWhite: '#efefef',
brightYellow: '#fff099',
cyan: '#21c5c7',
green: '#4eb071',
magenta: '#b168df',
red: '#da6771',
white: '#efefef',
yellow: '#fff099'
};
break;
case 'light':
value = {
background: '#ffffff',
foreground: '#333333',
cursor: '#333333',
cursorAccent: '#ffffff',
selectionBackground: '#add6ff',
overviewRulerBorder: '#aaaaaa',
black: '#000000',
blue: '#0451a5',
brightBlack: '#666666',
brightBlue: '#0451a5',
brightCyan: '#0598bc',
brightGreen: '#14ce14',
brightMagenta: '#bc05bc',
brightRed: '#cd3131',
brightWhite: '#a5a5a5',
brightYellow: '#b5ba00',
cyan: '#0598bc',
green: '#00bc00',
magenta: '#bc05bc',
red: '#cd3131',
white: '#555555',
yellow: '#949800'
};
break;
}
value = this._getTheme();
}
this._terminal.options[o] = value;
if (o === 'theme') {
@@ -244,4 +237,27 @@ export class OptionsWindow extends BaseWindow implements IControlWindow {
public set autoResize(value: boolean) {
this._autoResize = value;
}
private _getTheme() {
const input = document.querySelector<HTMLInputElement>('#opt-theme');
let theme: ITheme;
switch (input.value) {
case 'default':
theme = undefined;
break;
case 'xtermjs':
theme = { ...xtermjsTheme };
break;
case 'sapphire':
theme = { ...sapphireTheme };
break;
case 'light':
theme = { ...lightTheme };
break;
}
if (this._terminal.options.allowTransparency) {
theme.background = 'transparent';
}
return theme;
}
}
+2
View File
@@ -436,6 +436,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
this.element.dir = 'ltr'; // xterm.css assumes LTR
this.element.classList.add('terminal');
this.element.classList.add('xterm');
this.element.classList.toggle('allow-transparency', this.options.allowTransparency);
this._register(this.optionsService.onSpecificOptionChange('allowTransparency', value => this.element!.classList.toggle('allow-transparency', value)));
parent.appendChild(this.element);
// Performance: Use a document fragment to build the terminal
+1
View File
@@ -71,6 +71,7 @@ export class Viewport extends Disposable {
this._scrollableElement.setScrollDimensions({ height: 0, scrollHeight: 0 });
this._register(Event.runAndSubscribe(themeService.onChangeColors, () => {
element.style.backgroundColor = themeService.colors.background.css;
this._scrollableElement.getDomNode().style.backgroundColor = themeService.colors.background.css;
}));
element.appendChild(this._scrollableElement.getDomNode());
+3
View File
@@ -303,6 +303,9 @@ describe('Color', () => {
assert.deepEqual(css.toColor('rgba(0, 0, 80, 0.5)'), { css: '#00005080', rgba: 0x00005080 });
assert.deepEqual(css.toColor('rgba(255, 255, 255, 1)'), { css: '#ffffffff', rgba: 0xffffffff });
});
it('should convert "transparent" to an IColor', () => {
assert.deepEqual(css.toColor('transparent'), { css: 'transparent', rgba: 0x00000000 });
});
});
});
+8
View File
@@ -184,6 +184,14 @@ export namespace css {
return channels.toColor($r, $g, $b, $a);
}
// Handle the "transparent" keyword
if (css === 'transparent') {
return {
css: 'transparent',
rgba: 0x00000000
};
}
// Validate the context is available for canvas-based color parsing
if (!$ctx || !$litmusColor) {
throw new Error('css.toColor: Unsupported css format');