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=\u2500\u2501\u2550]/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;
}
export function tokenize(lines) {
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 { runs, text, osc } of lines) {
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 = osc === "A";
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,
lineBubbleBg,
};