export const ESC = '\x1b['; export const colors = { cyan: 36, yellow: 33, green: 32, magenta: 35, blue: 34, white: 37, red: 31, brightBlack: 90 }; export const color = (name, text) => `\x1b[${colors[name] || colors.cyan}m${text}\x1b[0m`; export const clear = '\x1b[0m\x1b[2J\x1b[3J\x1b[H'; export const crlf = text => String(text).replace(/\r?\n/g, '\r\n'); export const visibleLength = text => String(text).replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, '').length; export const truncate = (text, width) => Array.from(String(text)).slice(0, Math.max(0, width)).join(''); export function header(left, right, width, leftColor = 'cyan', rightColor = 'brightBlack') { const safeWidth = Math.max(30, width || 80); const safeRight = truncate(right, Math.max(0, safeWidth - 2)); const safeLeft = truncate(left, Math.max(1, safeWidth - safeRight.length - 2)); return `${color(leftColor, safeLeft)}${' '.repeat(Math.max(1, safeWidth - safeLeft.length - safeRight.length))}${color(rightColor, safeRight)}`; } export function box(title, lines, width, theme = {}) { const outer = Math.max(30, width || 80); const inner = outer - 4; const primary = theme.primary || 'cyan'; const muted = theme.muted || 'brightBlack'; const titleText = ` ${truncate(title, inner - 2)} `; const topFill = Math.max(0, outer - titleText.length - 2); let output = `${color(muted, '╭─')}${color(primary, titleText)}${color(muted, `${'─'.repeat(topFill)}╮`)}\r\n`; for (const source of lines) { const wrapped = wrap(source, inner); for (const line of wrapped) output += `${color(muted, '│')} ${line}${' '.repeat(Math.max(0, inner - visibleLength(line)))} ${color(muted, '│')}\r\n`; } return output + color(muted, `╰${'─'.repeat(outer - 2)}╯`); } export function wrap(text, width = 78) { const out = []; for (const paragraph of String(text).split('\n')) { if (!paragraph) { out.push(''); continue; } let line = ''; for (const word of paragraph.split(/\s+/)) { if (line && visibleLength(line) + visibleLength(word) + 1 > width) { out.push(line); line = word; } else line += (line ? ' ' : '') + word; } out.push(line); } return out; }