diff --git a/addons/xterm-addon-serialize/src/SerializeAddon.test.ts b/addons/xterm-addon-serialize/src/SerializeAddon.test.ts index 4c5dd645..75148ab9 100644 --- a/addons/xterm-addon-serialize/src/SerializeAddon.test.ts +++ b/addons/xterm-addon-serialize/src/SerializeAddon.test.ts @@ -74,7 +74,7 @@ describe('xterm-addon-serialize', () => { terminal = new Terminal({ cols: 10, rows: 2, allowProposedApi: true }); terminal.loadAddon(serializeAddon); - (terminal as any)._core._themeService = new ThemeService(new OptionsService({})); + (terminal as any)._core._themeService = new ThemeService((terminal as any)._core.optionsService); (terminal as any)._core._selectionService = new TestSelectionService((terminal as any)._core._bufferService); }); @@ -205,5 +205,25 @@ describe('xterm-addon-serialize', () => { }); assert.equal((output.match(/color: #ffffff; background-color: #000000; font-family: courier-new, courier, monospace; font-size: 15px;/g) || []).length, 1, output); }); + + it('cells with custom color styling', async () => { + terminal.options.theme.black = '#ffa500'; + terminal.options.theme = { ... terminal.options.theme }; + + await writeP(terminal, ' ' + sgr('38;5;0') + 'terminal' + sgr('39') + ' '); + + const output = serializeAddon.serializeAsHTML(); + assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output); + }); + + it('cells with color styling - xterm headless', async () => { + // a headless terminal doesn't have a themeservice + (terminal as any)._core._themeService = undefined; + + await writeP(terminal, ' ' + sgr('38;5;46') + 'terminal' + sgr('39') + ' '); + + const output = serializeAddon.serializeAsHTML(); + assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output); + }); }); }); diff --git a/addons/xterm-addon-serialize/src/SerializeAddon.ts b/addons/xterm-addon-serialize/src/SerializeAddon.ts index b83ff160..50c200a3 100644 --- a/addons/xterm-addon-serialize/src/SerializeAddon.ts +++ b/addons/xterm-addon-serialize/src/SerializeAddon.ts @@ -7,7 +7,8 @@ import { Terminal, ITerminalAddon, IBuffer, IBufferCell, IBufferRange } from 'xterm'; import { IColorSet } from 'browser/Types'; -import { IAttributeData } from 'common/Types'; +import { IAttributeData, IColor } from 'common/Types'; +import { DEFAULT_ANSI_COLORS } from 'browser/services/ThemeService'; function constrain(value: number, low: number, high: number): number { return Math.max(low, Math.min(value, high)); @@ -534,7 +535,7 @@ export class HTMLSerializeHandler extends BaseSerializeHandler { private _htmlContent = ''; - private _colors: IColorSet; + private _ansiColors: Readonly; constructor( buffer: IBuffer, @@ -543,8 +544,13 @@ export class HTMLSerializeHandler extends BaseSerializeHandler { ) { super(buffer); - // https://github.com/xtermjs/xterm.js/issues/3601 - this._colors = (_terminal as any)._core._themeService.colors; + // For xterm headless: fallback to ansi colors + if ((_terminal as any)._core._themeService) { + this._ansiColors = (_terminal as any)._core._themeService.colors.ansi; + } + else { + this._ansiColors = DEFAULT_ANSI_COLORS; + } } private _padStart(target: string, targetLength: number, padString: string): string { @@ -600,7 +606,7 @@ export class HTMLSerializeHandler extends BaseSerializeHandler { return rgb.map(x => this._padStart(x.toString(16), 2, '0')).join(''); } if (isFg ? cell.isFgPalette() : cell.isBgPalette()) { - return this._colors.ansi[color].css; + return this._ansiColors[color].css; } return undefined; }