From 5141a91bbab1582ff3d865f1aa839ec6a8dd2ac8 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 28 Oct 2025 10:27:00 -0400 Subject: [PATCH 1/5] add range to IHTMLSerializeOptions --- addons/addon-serialize/src/SerializeAddon.test.ts | 15 +++++++++++++++ addons/addon-serialize/src/SerializeAddon.ts | 7 +++++++ .../addon-serialize/typings/addon-serialize.d.ts | 5 +++++ 3 files changed, 27 insertions(+) diff --git a/addons/addon-serialize/src/SerializeAddon.test.ts b/addons/addon-serialize/src/SerializeAddon.test.ts index 0252b201..aacf76ed 100644 --- a/addons/addon-serialize/src/SerializeAddon.test.ts +++ b/addons/addon-serialize/src/SerializeAddon.test.ts @@ -148,6 +148,21 @@ describe('SerializeAddon', () => { assert.equal((output.match(/
<a>&pi;<\/span><\/div>/g) || []).length, 1, output); }); + it('serializes rows within a provided range', async () => { + await writeP(terminal, 'hello\r\nworld'); + + const output = serializeAddon.serializeAsHTML({ + range: { + start: 0, + end: 0 + } + }); + const rowMatches = output.match(/
.*?<\/span><\/div>/g) || []; + assert.equal(rowMatches.length, 1, output); + assert.ok(rowMatches[0]?.includes('hello')); + assert.ok(!output.includes('world')); + }); + it('cells with bold styling', async () => { await writeP(terminal, ' ' + sgr('1') + 'terminal' + sgr('22') + ' '); diff --git a/addons/addon-serialize/src/SerializeAddon.ts b/addons/addon-serialize/src/SerializeAddon.ts index 6f1d5d43..b8c567fc 100644 --- a/addons/addon-serialize/src/SerializeAddon.ts +++ b/addons/addon-serialize/src/SerializeAddon.ts @@ -450,6 +450,13 @@ export class SerializeAddon implements ITerminalAddon , ISerializeApi { const buffer = terminal.buffer.active; const handler = new HTMLSerializeHandler(buffer, terminal, options); const onlySelection = options.onlySelection ?? false; + const range = options.range; + if (range) { + return handler.serialize({ + start: { x: 0, y: typeof range.start === 'number' ? range.start : range.start.line }, + end: { x: terminal.cols, y: typeof range.end === 'number' ? range.end : range.end.line } + }); + } if (!onlySelection) { const maxRows = buffer.length; const scrollback = options.scrollback; diff --git a/addons/addon-serialize/typings/addon-serialize.d.ts b/addons/addon-serialize/typings/addon-serialize.d.ts index 90b8b428..d8d5fe07 100644 --- a/addons/addon-serialize/typings/addon-serialize.d.ts +++ b/addons/addon-serialize/typings/addon-serialize.d.ts @@ -90,6 +90,11 @@ declare module '@xterm/addon-serialize' { * Whether to include the global background of the terminal. False by default. */ includeGlobalBackground: boolean; + + /** + * The row range to serialize. This is prioritized over {@link onlySelection}. + */ + range?: ISerializeRange; } export interface ISerializeRange { From 18bafaf9c95b97d31dd342d057f3e6b57a77d9bc Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 28 Oct 2025 10:30:22 -0400 Subject: [PATCH 2/5] fix indentation --- addons/addon-serialize/src/SerializeAddon.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/addons/addon-serialize/src/SerializeAddon.ts b/addons/addon-serialize/src/SerializeAddon.ts index b8c567fc..317dec8d 100644 --- a/addons/addon-serialize/src/SerializeAddon.ts +++ b/addons/addon-serialize/src/SerializeAddon.ts @@ -452,11 +452,11 @@ export class SerializeAddon implements ITerminalAddon , ISerializeApi { const onlySelection = options.onlySelection ?? false; const range = options.range; if (range) { - return handler.serialize({ - start: { x: 0, y: typeof range.start === 'number' ? range.start : range.start.line }, - end: { x: terminal.cols, y: typeof range.end === 'number' ? range.end : range.end.line } - }); - } + return handler.serialize({ + start: { x: 0, y: typeof range.start === 'number' ? range.start : range.start.line }, + end: { x: terminal.cols, y: typeof range.end === 'number' ? range.end : range.end.line } + }); + } if (!onlySelection) { const maxRows = buffer.length; const scrollback = options.scrollback; From 5a467da137710835a3bba6f64378df61f3f1c852 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 28 Oct 2025 11:44:21 -0400 Subject: [PATCH 3/5] pass in startLine, endLine, startCol --- addons/addon-serialize/src/SerializeAddon.test.ts | 8 +++++--- addons/addon-serialize/src/SerializeAddon.ts | 4 ++-- addons/addon-serialize/typings/addon-serialize.d.ts | 10 ++++++++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/addons/addon-serialize/src/SerializeAddon.test.ts b/addons/addon-serialize/src/SerializeAddon.test.ts index aacf76ed..069a0a04 100644 --- a/addons/addon-serialize/src/SerializeAddon.test.ts +++ b/addons/addon-serialize/src/SerializeAddon.test.ts @@ -149,17 +149,19 @@ describe('SerializeAddon', () => { }); it('serializes rows within a provided range', async () => { - await writeP(terminal, 'hello\r\nworld'); + await writeP(terminal, 'prompt>hello\r\nworld'); const output = serializeAddon.serializeAsHTML({ range: { - start: 0, - end: 0 + startLine: 0, + endLine: 0, + startCol: 7 } }); const rowMatches = output.match(/
.*?<\/span><\/div>/g) || []; assert.equal(rowMatches.length, 1, output); assert.ok(rowMatches[0]?.includes('hello')); + assert.ok(!output.includes('prompt>')); assert.ok(!output.includes('world')); }); diff --git a/addons/addon-serialize/src/SerializeAddon.ts b/addons/addon-serialize/src/SerializeAddon.ts index 317dec8d..c6a5483c 100644 --- a/addons/addon-serialize/src/SerializeAddon.ts +++ b/addons/addon-serialize/src/SerializeAddon.ts @@ -453,8 +453,8 @@ export class SerializeAddon implements ITerminalAddon , ISerializeApi { const range = options.range; if (range) { return handler.serialize({ - start: { x: 0, y: typeof range.start === 'number' ? range.start : range.start.line }, - end: { x: terminal.cols, y: typeof range.end === 'number' ? range.end : range.end.line } + start: { x: range.startCol, y: typeof range.startLine === 'number' ? range.startLine : range.startLine }, + end: { x: terminal.cols, y: typeof range.endLine === 'number' ? range.endLine : range.endLine } }); } if (!onlySelection) { diff --git a/addons/addon-serialize/typings/addon-serialize.d.ts b/addons/addon-serialize/typings/addon-serialize.d.ts index d8d5fe07..06f41752 100644 --- a/addons/addon-serialize/typings/addon-serialize.d.ts +++ b/addons/addon-serialize/typings/addon-serialize.d.ts @@ -92,9 +92,15 @@ declare module '@xterm/addon-serialize' { includeGlobalBackground: boolean; /** - * The row range to serialize. This is prioritized over {@link onlySelection}. + * The range to serialize. This is prioritized over {@link onlySelection}. */ - range?: ISerializeRange; + range?: ISerializeBufferRange; + } + + export interface ISerializeBufferRange { + startLine: number; + endLine: number; + startCol: number; } export interface ISerializeRange { From 5121867bbb90c3465820a964c65e834a3730f226 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 28 Oct 2025 11:56:56 -0400 Subject: [PATCH 4/5] rm line --- addons/addon-serialize/src/SerializeAddon.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/addon-serialize/src/SerializeAddon.test.ts b/addons/addon-serialize/src/SerializeAddon.test.ts index 069a0a04..999863be 100644 --- a/addons/addon-serialize/src/SerializeAddon.test.ts +++ b/addons/addon-serialize/src/SerializeAddon.test.ts @@ -150,7 +150,6 @@ describe('SerializeAddon', () => { it('serializes rows within a provided range', async () => { await writeP(terminal, 'prompt>hello\r\nworld'); - const output = serializeAddon.serializeAsHTML({ range: { startLine: 0, From 4a628561533f1002c32566fa0e86c7e8d19d7da5 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 28 Oct 2025 12:30:54 -0400 Subject: [PATCH 5/5] fix test --- addons/addon-serialize/src/SerializeAddon.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/addon-serialize/src/SerializeAddon.test.ts b/addons/addon-serialize/src/SerializeAddon.test.ts index 999863be..8b0022d1 100644 --- a/addons/addon-serialize/src/SerializeAddon.test.ts +++ b/addons/addon-serialize/src/SerializeAddon.test.ts @@ -149,18 +149,18 @@ describe('SerializeAddon', () => { }); it('serializes rows within a provided range', async () => { - await writeP(terminal, 'prompt>hello\r\nworld'); + await writeP(terminal, 'bye hello\r\nworld'); const output = serializeAddon.serializeAsHTML({ range: { startLine: 0, endLine: 0, - startCol: 7 + startCol: 4 } }); const rowMatches = output.match(/
.*?<\/span><\/div>/g) || []; assert.equal(rowMatches.length, 1, output); assert.ok(rowMatches[0]?.includes('hello')); - assert.ok(!output.includes('prompt>')); + assert.ok(!output.includes('bye')); assert.ok(!output.includes('world')); });