'use strict';
(function (root) {
function parseCast(text) {
const src = String(text || '');
const trimmed = src.trimStart();
if (trimmed.startsWith('{') && trimmed.includes('"stdout"')) {
const v1 = tryJson(trimmed);
if (v1 && Array.isArray(v1.stdout)) {
let t = 0;
const events = [];
for (const pair of v1.stdout) {
if (!Array.isArray(pair) || pair.length < 2) continue;
t += Number(pair[0]) || 0;
events.push({ t: t, type: 'o', data: String(pair[1]) });
}
return { cols: num(v1.width, 80), rows: num(v1.height, 24), events: events, duration: t, version: 1, tail: '' };
}
}
let cols = 80, rows = 24, version = 3, abs = 0;
const events = [];
let body = src, tail = '';
const cut = src.lastIndexOf('\n');
const last = src.slice(cut + 1).trim();
if (last && !tryJson(last)) { body = src.slice(0, cut + 1); tail = src.slice(cut + 1); }
for (const raw of body.split('\n')) {
const line = raw.trim();
if (!line || line[0] === '#') continue;
if (line[0] === '{') {
const h = tryJson(line);
if (h) {
version = num(h.version, 3);
if (h.term) { cols = num(h.term.cols, cols); rows = num(h.term.rows, rows); }
cols = num(h.width, cols);
rows = num(h.height, rows);
}
continue;
}
if (line[0] !== '[') continue;
const ev = tryJson(line);
if (!Array.isArray(ev) || ev.length < 2 || typeof ev[0] !== 'number') continue;
abs = version >= 3 ? abs + ev[0] : Math.max(abs, ev[0]);
const type = String(ev[1]);
if (type !== 'o' && type !== 'r' && type !== 'm') continue; events.push({ t: abs, type: type, data: ev.length > 2 ? String(ev[2]) : '' });
}
return { cols: cols, rows: rows, events: events, duration: abs, version: version, tail: tail };
}
function tryJson(s) { try { return JSON.parse(s); } catch (_) { return null; } }
function num(v, dflt) { const n = Number(v); return Number.isFinite(n) && n > 0 ? Math.floor(n) : dflt; }
function appendCast(cast, text) {
const buf = (cast.tail || '') + String(text == null ? '' : text);
if (cast.version === 1) { cast.tail = ''; return 0; }
const cut = buf.lastIndexOf('\n');
if (cut < 0) { cast.tail = buf; return 0; }
cast.tail = buf.slice(cut + 1);
let added = 0;
for (const raw of buf.slice(0, cut).split('\n')) {
const line = raw.trim();
if (!line || line[0] !== '[') continue; const ev = tryJson(line);
if (!Array.isArray(ev) || ev.length < 2 || typeof ev[0] !== 'number') continue;
cast.duration = cast.version >= 3 ? cast.duration + ev[0] : Math.max(cast.duration, ev[0]);
const type = String(ev[1]);
if (type !== 'o' && type !== 'r' && type !== 'm') continue; cast.events.push({ t: cast.duration, type: type, data: ev.length > 2 ? String(ev[2]) : '' });
added++;
}
return added;
}
function buildPacing(events, duration, limit) {
const pacing = { rec: [0], paced: [0], limit: limit == null ? null : limit, pacedDuration: 0 };
extendPacing(pacing, events, 0, duration);
return pacing;
}
function extendPacing(pacing, events, fromIdx, duration) {
let lastRec = pacing.rec[pacing.rec.length - 1];
let lastPaced = pacing.paced[pacing.paced.length - 1];
const push = function (t) {
if (t <= lastRec) return;
const gap = t - lastRec;
lastPaced += pacing.limit != null && gap > pacing.limit ? pacing.limit : gap;
lastRec = t;
pacing.rec.push(lastRec);
pacing.paced.push(lastPaced);
};
for (let i = fromIdx; i < events.length; i++) push(events[i].t);
push(duration);
pacing.pacedDuration = lastPaced;
}
function mapTime(from, to, t) {
if (t <= 0) return 0;
let lo = 0, hi = from.length - 1;
if (t >= from[hi]) return to[hi] + (t - from[hi]);
while (lo + 1 < hi) { const mid = (lo + hi) >> 1; if (from[mid] <= t) lo = mid; else hi = mid; }
const span = from[hi] - from[lo];
const frac = span > 0 ? (t - from[lo]) / span : 0;
return to[lo] + frac * (to[hi] - to[lo]);
}
const A_BOLD = 1, A_DIM = 2, A_ITALIC = 4, A_UNDER = 8, A_INVERSE = 16, A_STRIKE = 32;
const DEC_GRAPHICS = {
'j': '┘', 'k': '┐', 'l': '┌', 'm': '└', 'n': '┼', 'q': '─', 't': '├', 'u': '┤',
'v': '┴', 'w': '┬', 'x': '│', 'a': '▒', '`': '◆', 'f': '°', 'g': '±', 'o': '⎺',
'p': '⎻', 'r': '⎼', 's': '⎽', '~': '·', 'y': '≤', 'z': '≥', '{': 'π', '|': '≠',
'}': '£', '.': '▼', ',': '←', '+': '→', '-': '↑', '0': '█', 'h': '▒', 'i': '␋',
};
function blankCell() { return { ch: ' ', fg: null, bg: null, attrs: 0 }; }
function blankRow(cols) { const r = new Array(cols); for (let x = 0; x < cols; x++) r[x] = blankCell(); return r; }
const GROUND = 0, ESC = 1, CSI = 2, OSC = 3, CHARSET = 4, ESC_IGNORE_ONE = 5;
function Term(cols, rows) {
this.cols = Math.max(1, cols | 0);
this.rows = Math.max(1, rows | 0);
this.reset();
}
Term.prototype.reset = function () {
this.screen = [];
for (let y = 0; y < this.rows; y++) this.screen.push(blankRow(this.cols));
this.altScreen = null; this.x = 0;
this.y = 0;
this.pen = { fg: null, bg: null, attrs: 0 };
this.cursorVisible = true;
this.wrapPending = false; this.autowrap = true;
this.scrollTop = 0; this.scrollBottom = this.rows - 1; this.savedCursor = null; this.charsets = ['B', 'B']; this.charsetIdx = 0; this.state = GROUND;
this.params = '';
this.oscBuf = '';
this.charsetSlot = 0;
};
Term.prototype.resize = function (cols, rows) {
cols = Math.max(1, cols | 0);
rows = Math.max(1, rows | 0);
const next = [];
for (let y = 0; y < rows; y++) {
const row = blankRow(cols);
if (y < this.rows) for (let x = 0; x < Math.min(cols, this.cols); x++) row[x] = this.screen[y][x];
next.push(row);
}
this.screen = next;
this.cols = cols;
this.rows = rows;
this.x = Math.min(this.x, cols - 1);
this.y = Math.min(this.y, rows - 1);
this.scrollTop = 0;
this.scrollBottom = rows - 1;
this.wrapPending = false;
if (this.altScreen) this.altScreen = null; };
Term.prototype.write = function (text) {
const s = String(text);
for (let i = 0; i < s.length; i++) {
const ch = s[i];
switch (this.state) {
case GROUND: this.ground(ch); break;
case ESC: this.escState(ch); break;
case CSI:
if (ch >= '@' && ch <= '~') { this.csi(ch); this.state = GROUND; }
else this.params += ch;
break;
case OSC:
if (ch === '\x07') this.state = GROUND;
else if (ch === '\x1b') this.state = ESC_IGNORE_ONE;
else this.oscBuf += ch;
break;
case ESC_IGNORE_ONE: this.state = GROUND; break; case CHARSET:
this.charsets[this.charsetSlot] = ch;
this.state = GROUND;
break;
}
}
};
Term.prototype.ground = function (ch) {
const code = ch.charCodeAt(0);
if (ch === '\x1b') { this.state = ESC; return; }
if (ch === '\r') { this.x = 0; this.wrapPending = false; return; }
if (ch === '\n' || ch === '\x0b' || ch === '\x0c') { this.lineFeed(); return; }
if (ch === '\x08') { if (this.x > 0) this.x--; this.wrapPending = false; return; }
if (ch === '\t') { this.x = Math.min(this.cols - 1, (Math.floor(this.x / 8) + 1) * 8); this.wrapPending = false; return; }
if (ch === '\x0e') { this.charsetIdx = 1; return; } if (ch === '\x0f') { this.charsetIdx = 0; return; } if (code < 0x20 || code === 0x7f) return; this.print(ch);
};
Term.prototype.print = function (ch) {
if (this.charsets[this.charsetIdx] === '0' && DEC_GRAPHICS[ch]) ch = DEC_GRAPHICS[ch];
if (this.wrapPending) {
if (this.autowrap) { this.x = 0; this.lineFeed(); }
this.wrapPending = false;
}
const cell = this.screen[this.y][this.x];
cell.ch = ch;
cell.fg = this.pen.fg;
cell.bg = this.pen.bg;
cell.attrs = this.pen.attrs;
if (this.x === this.cols - 1) this.wrapPending = true;
else this.x++;
};
Term.prototype.lineFeed = function () {
this.wrapPending = false;
if (this.y === this.scrollBottom) this.scrollUp(1);
else if (this.y < this.rows - 1) this.y++;
};
Term.prototype.scrollUp = function (n) {
for (let k = 0; k < n; k++) {
this.screen.splice(this.scrollTop, 1);
this.screen.splice(this.scrollBottom, 0, blankRow(this.cols));
}
};
Term.prototype.scrollDown = function (n) {
for (let k = 0; k < n; k++) {
this.screen.splice(this.scrollBottom, 1);
this.screen.splice(this.scrollTop, 0, blankRow(this.cols));
}
};
Term.prototype.escState = function (ch) {
switch (ch) {
case '[': this.state = CSI; this.params = ''; return;
case ']': this.state = OSC; this.oscBuf = ''; return;
case '(': this.state = CHARSET; this.charsetSlot = 0; return;
case ')': this.state = CHARSET; this.charsetSlot = 1; return;
case 'D': this.lineFeed(); break; case 'E': this.x = 0; this.lineFeed(); break; case 'M': this.wrapPending = false;
if (this.y === this.scrollTop) this.scrollDown(1);
else if (this.y > 0) this.y--;
break;
case '7': this.saveCursor(); break; case '8': this.restoreCursor(); break; case 'c': { const c = this.cols, r = this.rows; this.reset(); this.cols = c; this.rows = r; this.screen = []; for (let y = 0; y < r; y++) this.screen.push(blankRow(c));
this.scrollBottom = r - 1; break; }
case 'P': case 'X': case '^': case '_': this.state = OSC; this.oscBuf = ''; return; case '=': case '>': break; default: break; }
if (this.state === ESC) this.state = GROUND;
};
Term.prototype.saveCursor = function () {
this.savedCursor = {
x: this.x, y: this.y,
pen: { fg: this.pen.fg, bg: this.pen.bg, attrs: this.pen.attrs },
charsets: this.charsets.slice(), charsetIdx: this.charsetIdx,
};
};
Term.prototype.restoreCursor = function () {
const s = this.savedCursor;
if (!s) { this.x = 0; this.y = 0; return; }
this.x = Math.min(s.x, this.cols - 1);
this.y = Math.min(s.y, this.rows - 1);
this.pen = { fg: s.pen.fg, bg: s.pen.bg, attrs: s.pen.attrs };
this.charsets = s.charsets.slice();
this.charsetIdx = s.charsetIdx;
this.wrapPending = false;
};
Term.prototype.csi = function (final) {
const priv = this.params.startsWith('?');
const raw = priv ? this.params.slice(1) : this.params;
const parts = raw.replace(/:/g, ';').split(';');
const p = [];
for (const part of parts) p.push(part === '' ? 0 : parseInt(part, 10) || 0);
const n = Math.max(1, p[0] || 0);
this.wrapPending = false;
if (priv) { this.decMode(final, p); return; }
switch (final) {
case 'A': this.y = Math.max(this.scrollRegionTopFor(this.y), this.y - n); break; case 'B': case 'e': this.y = Math.min(this.scrollRegionBottomFor(this.y), this.y + n); break; case 'C': case 'a': this.x = Math.min(this.cols - 1, this.x + n); break; case 'D': this.x = Math.max(0, this.x - n); break; case 'E': this.x = 0; this.y = Math.min(this.rows - 1, this.y + n); break; case 'F': this.x = 0; this.y = Math.max(0, this.y - n); break; case 'G': case '`': this.x = clamp((p[0] || 1) - 1, 0, this.cols - 1); break; case 'd': this.y = clamp((p[0] || 1) - 1, 0, this.rows - 1); break; case 'H': case 'f': this.y = clamp((p[0] || 1) - 1, 0, this.rows - 1);
this.x = clamp((p[1] || 1) - 1, 0, this.cols - 1);
break;
case 'J': this.eraseDisplay(p[0] || 0); break; case 'K': this.eraseLine(p[0] || 0); break; case 'L': if (this.inScrollRegion()) this.insertLines(n); break; case 'M': if (this.inScrollRegion()) this.deleteLines(n); break; case 'P': this.deleteChars(n); break; case '@': this.insertChars(n); break; case 'X': this.eraseChars(n); break; case 'S': this.scrollUp(n); break; case 'T': this.scrollDown(n); break; case 'r': this.scrollTop = clamp((p[0] || 1) - 1, 0, this.rows - 1);
this.scrollBottom = clamp((p[1] || this.rows) - 1, this.scrollTop, this.rows - 1);
this.x = 0; this.y = this.scrollTop;
break;
case 'm': this.sgr(p); break;
case 's': this.saveCursor(); break;
case 'u': this.restoreCursor(); break;
case 'h': case 'l': case 'n': case 't': case 'c': case 'g': case 'q': break; default: break;
}
};
Term.prototype.inScrollRegion = function () { return this.y >= this.scrollTop && this.y <= this.scrollBottom; };
Term.prototype.scrollRegionTopFor = function (y) { return y >= this.scrollTop ? this.scrollTop : 0; };
Term.prototype.scrollRegionBottomFor = function (y) { return y <= this.scrollBottom ? this.scrollBottom : this.rows - 1; };
Term.prototype.decMode = function (final, p) {
const set = final === 'h';
for (const mode of p) {
switch (mode) {
case 25: this.cursorVisible = set; break;
case 7: this.autowrap = set; break;
case 47: case 1047: this.switchAltScreen(set, false); break;
case 1049: this.switchAltScreen(set, true); break;
case 1048: if (set) this.saveCursor(); else this.restoreCursor(); break;
default: break; }
}
};
Term.prototype.switchAltScreen = function (on, withCursor) {
if (on && !this.altScreen) {
if (withCursor) this.saveCursor();
this.altScreen = this.screen;
this.screen = [];
for (let y = 0; y < this.rows; y++) this.screen.push(blankRow(this.cols));
} else if (!on && this.altScreen) {
this.screen = this.altScreen;
this.altScreen = null;
if (withCursor) this.restoreCursor();
}
};
Term.prototype.eraseDisplay = function (mode) {
if (mode === 2 || mode === 3) {
for (let y = 0; y < this.rows; y++) this.clearRowRange(y, 0, this.cols);
} else if (mode === 1) {
for (let y = 0; y < this.y; y++) this.clearRowRange(y, 0, this.cols);
this.clearRowRange(this.y, 0, this.x + 1);
} else {
this.clearRowRange(this.y, this.x, this.cols);
for (let y = this.y + 1; y < this.rows; y++) this.clearRowRange(y, 0, this.cols);
}
};
Term.prototype.eraseLine = function (mode) {
if (mode === 2) this.clearRowRange(this.y, 0, this.cols);
else if (mode === 1) this.clearRowRange(this.y, 0, this.x + 1);
else this.clearRowRange(this.y, this.x, this.cols);
};
Term.prototype.clearRowRange = function (y, from, to) {
const row = this.screen[y];
for (let x = from; x < Math.min(to, this.cols); x++) {
row[x] = { ch: ' ', fg: null, bg: this.pen.bg, attrs: 0 };
}
};
Term.prototype.insertLines = function (n) {
for (let k = 0; k < n && this.y <= this.scrollBottom; k++) {
this.screen.splice(this.scrollBottom, 1);
this.screen.splice(this.y, 0, blankRow(this.cols));
}
};
Term.prototype.deleteLines = function (n) {
for (let k = 0; k < n && this.y <= this.scrollBottom; k++) {
this.screen.splice(this.y, 1);
this.screen.splice(this.scrollBottom, 0, blankRow(this.cols));
}
};
Term.prototype.deleteChars = function (n) {
const row = this.screen[this.y];
row.splice(this.x, Math.min(n, this.cols - this.x));
while (row.length < this.cols) row.push({ ch: ' ', fg: null, bg: this.pen.bg, attrs: 0 });
};
Term.prototype.insertChars = function (n) {
const row = this.screen[this.y];
for (let k = 0; k < n; k++) row.splice(this.x, 0, { ch: ' ', fg: null, bg: this.pen.bg, attrs: 0 });
row.length = this.cols;
};
Term.prototype.eraseChars = function (n) {
this.clearRowRange(this.y, this.x, this.x + n);
};
Term.prototype.sgr = function (p) {
if (p.length === 0) p = [0];
for (let i = 0; i < p.length; i++) {
const v = p[i];
if (v === 0) this.pen = { fg: null, bg: null, attrs: 0 };
else if (v === 1) this.pen.attrs |= A_BOLD;
else if (v === 2) this.pen.attrs |= A_DIM;
else if (v === 3) this.pen.attrs |= A_ITALIC;
else if (v === 4) this.pen.attrs |= A_UNDER;
else if (v === 7) this.pen.attrs |= A_INVERSE;
else if (v === 9) this.pen.attrs |= A_STRIKE;
else if (v === 21 || v === 22) this.pen.attrs &= ~(A_BOLD | A_DIM);
else if (v === 23) this.pen.attrs &= ~A_ITALIC;
else if (v === 24) this.pen.attrs &= ~A_UNDER;
else if (v === 27) this.pen.attrs &= ~A_INVERSE;
else if (v === 29) this.pen.attrs &= ~A_STRIKE;
else if (v >= 30 && v <= 37) this.pen.fg = v - 30;
else if (v >= 90 && v <= 97) this.pen.fg = v - 90 + 8;
else if (v === 39) this.pen.fg = null;
else if (v >= 40 && v <= 47) this.pen.bg = v - 40;
else if (v >= 100 && v <= 107) this.pen.bg = v - 100 + 8;
else if (v === 49) this.pen.bg = null;
else if (v === 38 || v === 48) {
const isFg = v === 38;
if (p[i + 1] === 5) {
const idx = clamp(p[i + 2] || 0, 0, 255);
if (isFg) this.pen.fg = idx; else this.pen.bg = idx;
i += 2;
} else if (p[i + 1] === 2) {
const hex = '#' + hex2(p[i + 2]) + hex2(p[i + 3]) + hex2(p[i + 4]);
if (isFg) this.pen.fg = hex; else this.pen.bg = hex;
i += 4;
}
}
}
};
Term.prototype.snapshot = function () {
const rows = [];
for (let y = 0; y < this.rows; y++) {
const runs = [];
let cur = null;
for (let x = 0; x < this.cols; x++) {
const c = this.screen[y][x];
if (cur && cur.fg === c.fg && cur.bg === c.bg && cur.attrs === c.attrs) cur.text += c.ch;
else {
cur = { text: c.ch, fg: c.fg, bg: c.bg, attrs: c.attrs };
runs.push(cur);
}
}
rows.push(runs);
}
return {
cols: this.cols,
rows: rows,
cursor: { x: Math.min(this.x, this.cols - 1), y: this.y, visible: this.cursorVisible },
};
};
Term.prototype.textLines = function () {
const out = [];
for (let y = 0; y < this.rows; y++) {
let line = '';
for (let x = 0; x < this.cols; x++) line += this.screen[y][x].ch;
out.push(line.replace(/\s+$/, ''));
}
return out;
};
function clamp(v, lo, hi) { return v < lo ? lo : v > hi ? hi : v; }
function hex2(v) { return (clamp(v || 0, 0, 255)).toString(16).padStart(2, '0'); }
function color256(idx) {
if (idx < 16) return null; if (idx >= 232) { const g = 8 + (idx - 232) * 10; return '#' + hex2(g) + hex2(g) + hex2(g); }
const v = idx - 16;
const lv = [0, 95, 135, 175, 215, 255];
return '#' + hex2(lv[Math.floor(v / 36)]) + hex2(lv[Math.floor(v / 6) % 6]) + hex2(lv[v % 6]);
}
const api = {
parseCast: parseCast,
appendCast: appendCast,
buildPacing: buildPacing,
extendPacing: extendPacing,
mapTime: mapTime,
Term: Term,
color256: color256,
A_BOLD: A_BOLD, A_DIM: A_DIM, A_ITALIC: A_ITALIC, A_UNDER: A_UNDER, A_INVERSE: A_INVERSE, A_STRIKE: A_STRIKE,
};
root.BeeCastVT = api;
if (typeof module !== 'undefined' && module.exports) module.exports = api;
})(typeof window !== 'undefined' ? window : globalThis);