import { bufferLines, lineCells } from "./terminal-lines.js";
const ANSI_BASIC_VARS = [
"var(--ansi-0)",
"var(--ansi-1)",
"var(--ansi-2)",
"var(--ansi-3)",
"var(--ansi-4)",
"var(--ansi-5)",
"var(--ansi-6)",
"var(--ansi-7)",
"var(--ansi-8)",
"var(--ansi-9)",
"var(--ansi-10)",
"var(--ansi-11)",
"var(--ansi-12)",
"var(--ansi-13)",
"var(--ansi-14)",
"var(--ansi-15)",
];
function buildExtendedPalette() {
const palette = [];
const cube = [0, 95, 135, 175, 215, 255];
for (let r = 0; r < 6; r++) {
for (let g = 0; g < 6; g++) {
for (let b = 0; b < 6; b++) {
palette.push(`rgb(${cube[r]},${cube[g]},${cube[b]})`);
}
}
}
for (let i = 0; i < 24; i++) {
const v = 8 + i * 10;
palette.push(`rgb(${v},${v},${v})`);
}
return palette;
}
const ANSI_EXTENDED = buildExtendedPalette();
function paletteColour(idx) {
if (idx < 0) return null;
if (idx < 16) return ANSI_BASIC_VARS[idx];
if (idx < 256) return ANSI_EXTENDED[idx - 16];
return null;
}
function rgbColour(packed) {
const r = (packed >> 16) & 0xff;
const g = (packed >> 8) & 0xff;
const b = packed & 0xff;
return `rgb(${r},${g},${b})`;
}
function cellColour(cell, kind) {
const isDefault = kind === "fg" ? cell.isFgDefault() : cell.isBgDefault();
if (isDefault) return null;
const isRGB = kind === "fg" ? cell.isFgRGB() : cell.isBgRGB();
const isPalette = kind === "fg" ? cell.isFgPalette() : cell.isBgPalette();
const value = kind === "fg" ? cell.getFgColor() : cell.getBgColor();
if (isRGB) return rgbColour(value);
if (isPalette) return paletteColour(value);
return null;
}
function cellAttrs(cell) {
return {
fg: cellColour(cell, "fg"),
bg: cellColour(cell, "bg"),
bold: !!cell.isBold(),
italic: !!cell.isItalic(),
underline: !!cell.isUnderline(),
dim: !!cell.isDim(),
inverse: !!cell.isInverse(),
};
}
function attrsEqual(a, b) {
return (
a.fg === b.fg &&
a.bg === b.bg &&
a.bold === b.bold &&
a.italic === b.italic &&
a.underline === b.underline &&
a.dim === b.dim &&
a.inverse === b.inverse
);
}
function extractRuns(segments) {
const runs = [];
let cur = null;
for (const cell of lineCells(segments)) {
const text = cell.getChars() || " ";
const attrs = cellAttrs(cell);
if (cur && attrsEqual(cur.attrs, attrs)) {
cur.text += text;
} else {
if (cur) runs.push(cur);
cur = { text, attrs };
}
}
if (cur) runs.push(cur);
while (runs.length > 0) {
const last = runs[runs.length - 1];
last.text = last.text.replace(/\s+$/u, "");
if (last.text.length === 0) {
runs.pop();
continue;
}
break;
}
return runs;
}
export function createTerminalDocument(engine) {
function snapshot() {
const { buffer } = engine;
const markers = engine.oscMarkers;
const lines = bufferLines(buffer).map(({ segments, key }) => {
const runs = extractRuns(segments);
const text = runs.map((r) => r.text).join("");
return { runs, text, osc: markers.get(key) || null };
});
while (lines.length > 0 && lines[lines.length - 1].text.trim() === "") {
lines.pop();
}
const statusRows = buffer
.statusRows()
.map((row) => ({ runs: extractRuns([{ rows: [row], trim: false }]) }))
.filter(({ runs }) => runs.length > 0);
return { lines, status: statusRows.length ? { rows: statusRows } : null };
}
function onOscDetected(cb) {
const handler = () => cb();
engine.addEventListener("osc-detected", handler);
return {
dispose() {
engine.removeEventListener("osc-detected", handler);
},
};
}
return {
snapshot,
subscribe(cb) {
return engine.onBufferChanged(cb);
},
onOscDetected,
get oscDetected() {
return engine.oscDetected;
},
};
}