feat: Implement background shell commands (#14849)

This commit is contained in:
Gal Zahavi
2026-01-30 09:53:09 -08:00
committed by GitHub
parent fc90f581b2
commit 2eb8dc3042
52 changed files with 3957 additions and 470 deletions
+39 -14
View File
@@ -34,12 +34,12 @@ export const enum ColorMode {
}
class Cell {
private readonly cell: IBufferCell | null;
private readonly x: number;
private readonly y: number;
private readonly cursorX: number;
private readonly cursorY: number;
private readonly attributes: number = 0;
private cell: IBufferCell | null = null;
private x = 0;
private y = 0;
private cursorX = 0;
private cursorY = 0;
private attributes: number = 0;
fg = 0;
bg = 0;
fgColorMode: ColorMode = ColorMode.DEFAULT;
@@ -51,12 +51,23 @@ class Cell {
y: number,
cursorX: number,
cursorY: number,
) {
this.update(cell, x, y, cursorX, cursorY);
}
update(
cell: IBufferCell | null,
x: number,
y: number,
cursorX: number,
cursorY: number,
) {
this.cell = cell;
this.x = x;
this.y = y;
this.cursorX = cursorX;
this.cursorY = cursorY;
this.attributes = 0;
if (!cell) {
return;
@@ -131,7 +142,11 @@ class Cell {
}
}
export function serializeTerminalToObject(terminal: Terminal): AnsiOutput {
export function serializeTerminalToObject(
terminal: Terminal,
startLine?: number,
endLine?: number,
): AnsiOutput {
const buffer = terminal.buffer.active;
const cursorX = buffer.cursorX;
const cursorY = buffer.cursorY;
@@ -140,22 +155,30 @@ export function serializeTerminalToObject(terminal: Terminal): AnsiOutput {
const result: AnsiOutput = [];
for (let y = 0; y < terminal.rows; y++) {
const line = buffer.getLine(buffer.viewportY + y);
// Reuse cell instances
const lastCell = new Cell(null, -1, -1, cursorX, cursorY);
const currentCell = new Cell(null, -1, -1, cursorX, cursorY);
const effectiveStart = startLine ?? buffer.viewportY;
const effectiveEnd = endLine ?? buffer.viewportY + terminal.rows;
for (let y = effectiveStart; y < effectiveEnd; y++) {
const line = buffer.getLine(y);
const currentLine: AnsiLine = [];
if (!line) {
result.push(currentLine);
continue;
}
let lastCell = new Cell(null, -1, -1, cursorX, cursorY);
// Reset lastCell for new line
lastCell.update(null, -1, -1, cursorX, cursorY);
let currentText = '';
for (let x = 0; x < terminal.cols; x++) {
const cellData = line.getCell(x);
const cell = new Cell(cellData || null, x, y, cursorX, cursorY);
currentCell.update(cellData || null, x, y, cursorX, cursorY);
if (x > 0 && !cell.equals(lastCell)) {
if (x > 0 && !currentCell.equals(lastCell)) {
if (currentText) {
const token: AnsiToken = {
text: currentText,
@@ -172,8 +195,10 @@ export function serializeTerminalToObject(terminal: Terminal): AnsiOutput {
}
currentText = '';
}
currentText += cell.getChars();
lastCell = cell;
currentText += currentCell.getChars();
// Copy state from currentCell to lastCell. Since we can't easily deep copy
// without allocating, we just update lastCell with the same data.
lastCell.update(cellData || null, x, y, cursorX, cursorY);
}
if (currentText) {