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 attrsAreDefault(a) {
return a.fg === null && a.bg === null && !a.bold && !a.italic
&& !a.underline && !a.dim && !a.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;
}
const PROMPT_RE = /(?:^|\s)([~/][^$#❯➜›▶›⟩>]*)?\s*[#$❯➜›▶➤⟩>]\s*$/u;
const HEADER_BRACKET_RE = /^\s*\[[A-Za-z][A-Za-z0-9 _-]*\]\s*$/;
const HEADER_HASH_RE = /^\s*#{1,4}\s+\S/;
const BOX_DRAW_RE = /[\u2500-\u257F=─━═]/g;
const FENCE_RE = /^\s*```/;
function isRule(text) {
const trimmed = text.trim();
if (trimmed.length < 8) return false;
const hits = (trimmed.match(BOX_DRAW_RE) || []).length;
return hits / trimmed.length > 0.7;
}
function isPrompt(text) {
if (text.length === 0) return false;
const trimmedRight = text.replace(/\s+$/u, '');
const last = trimmedRight.slice(-1);
if ('#$>❯➜›▶➤⟩'.indexOf(last) === -1) return false;
return PROMPT_RE.test(trimmedRight);
}
function isHeader(text) {
return HEADER_BRACKET_RE.test(text) || HEADER_HASH_RE.test(text);
}
function lineBubbleBg(runs) {
let bg = null;
let sawContent = false;
for (const r of runs) {
if (!r.text || r.text.trim().length === 0) continue;
sawContent = true;
if (r.attrs.bg === null) return null;
if (bg === null) bg = r.attrs.bg;
else if (bg !== r.attrs.bg) return null;
}
return sawContent ? bg : null;
}
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 tokenize(buffer, cols, opts) {
const endY = opts && opts.endY != null ? opts.endY : buffer.length;
const oscMarkers = opts && opts.oscMarkers;
const blocks = [];
let inFence = false;
let codeLines = [];
function flushCode() {
if (codeLines.length === 0) return;
blocks.push({ type: 'code', lines: codeLines });
codeLines = [];
}
function pushTextLine(line) {
const last = blocks[blocks.length - 1];
if (last && last.type === 'text') last.lines.push(line);
else blocks.push({ type: 'text', lines: [line] });
}
for (const { chain, startY } of logicalLines(buffer, endY)) {
const runs = extractRuns(chain, cols);
const text = runs.map((r) => r.text).join('');
const oscKind = oscKindForChain(oscMarkers, startY, chain.length);
if (FENCE_RE.test(text)) {
if (inFence) { flushCode(); inFence = false; }
else inFence = true;
continue;
}
if (inFence) {
codeLines.push({ runs, text, bubbleBg: lineBubbleBg(runs) });
continue;
}
if (text.trim().length === 0) {
blocks.push({ type: 'blank' });
continue;
}
const isOscPrompt = oscKind === 'A' || oscKind === 'B';
if (isRule(text)) {
blocks.push({ type: 'rule' });
continue;
}
if (isHeader(text)) {
blocks.push({ type: 'header', runs, text });
continue;
}
if (isOscPrompt || isPrompt(text)) {
blocks.push({ type: 'prompt', runs, text });
continue;
}
pushTextLine({ runs, text, bubbleBg: lineBubbleBg(runs) });
}
if (inFence) flushCode();
return blocks;
}
export const _internals = {
isRule, isPrompt, isHeader, attrsEqual, attrsAreDefault,
paletteColour, rgbColour, lineBubbleBg, oscKindForChain,
};