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
);
}
export function extractRuns(rowChain, cols) {
const runs = [];
let cur = null;
for (const line of rowChain) {
if (!line) continue;
for (let x = 0; x < cols; x++) {
const cell = line.getCell(x);
if (!cell) continue;
const ch = cell.getChars();
const text = ch === "" ? " " : ch;
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;
}
function* logicalLines(buffer, endY) {
const total = endY != null ? endY : buffer.length;
let chain = [];
let chainStartY = -1;
for (let y = 0; y < total; y++) {
const line = buffer.getLine(y);
if (!line) continue;
if (line.isWrapped && chain.length > 0) {
chain.push(line);
} else {
if (chain.length > 0) yield { chain, startY: chainStartY };
chain = [line];
chainStartY = y;
}
}
if (chain.length > 0) yield { chain, startY: chainStartY };
}
function oscKindForChain(oscMarkers, startY, len) {
if (!oscMarkers || oscMarkers.size === 0) return null;
for (let y = startY; y < startY + len; y++) {
const k = oscMarkers.get(y);
if (k) return k;
}
return null;
}
export function createTerminalDocument(engine) {
function snapshot() {
const buffer = engine.getActiveBuffer();
const cols = engine.cols;
const total = buffer.length;
const statusEndY = total > 0 ? total - 1 : 0;
const lines = [];
for (const { chain, startY } of logicalLines(buffer, statusEndY)) {
const runs = extractRuns(chain, cols);
const text = runs.map((r) => r.text).join("");
const osc = oscKindForChain(engine.oscMarkers, startY, chain.length);
lines.push({ runs, text, osc });
}
while (
lines.length > 0 &&
(!lines[lines.length - 1].text ||
lines[lines.length - 1].text.trim().length === 0)
) {
lines.pop();
}
let status = null;
if (total > 0) {
const line = buffer.getLine(statusEndY);
if (line) {
const runs = extractRuns([line], cols);
if (runs.some((r) => r.text && r.text.trim().length > 0)) {
status = { runs };
}
}
}
return { lines, status };
}
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;
},
};
}