/**
 * CLI formatting helpers (Claude Code-style): headings, bullets, paths, emphasis.
 * Uses ANSI when stdout is a TTY and NO_COLOR is not set; otherwise plain text.
 */
const ANSI = {
    reset: "\u001b[0m",
    bold: "\u001b[1m",
    dim: "\u001b[2m",
    cyan: "\u001b[36m",
    green: "\u001b[32m",
    yellow: "\u001b[33m",
    red: "\u001b[31m",
    /** Warm brown/orange for borders and sub-headings (Claude Code-style panel). */
    accent: "\u001b[38;5;94m",
};
function useColor() {
    if (process.env.NO_COLOR !== undefined && process.env.NO_COLOR !== "")
        return false;
    return Boolean(process.stdout?.isTTY);
}
let _colorEnabled = null;
function colorEnabled() {
    if (_colorEnabled === null)
        _colorEnabled = useColor();
    return _colorEnabled;
}
/** Bold text (or plain if no color). */
export function bold(text) {
    return colorEnabled() ? `${ANSI.bold}${text}${ANSI.reset}` : text;
}
/** Dimmed/secondary text. */
export function dim(text) {
    return colorEnabled() ? `${ANSI.dim}${text}${ANSI.reset}` : text;
}
/** Section heading (bold, with optional underline-style spacing). */
export function heading(text, level = 1) {
    if (level === 1)
        return bold(text);
    return bold(text);
}
/** Bullet line with optional indentation. */
export function bullet(text, indent = 0) {
    const pad = indent > 0 ? "  ".repeat(indent) : "";
    return `${pad}• ${text}`;
}
/** Numbered list item. */
export function numbered(index, text, indent = 0) {
    const pad = indent > 0 ? "  ".repeat(indent) : "";
    return `${pad}${index}. ${text}`;
}
/** File path or code (dimmed so it stands out from prose). */
export function pathStyle(text) {
    return dim(text);
}
/** Success / positive (e.g. "up", "created"). */
export function success(text) {
    return colorEnabled() ? `${ANSI.green}${text}${ANSI.reset}` : text;
}
/** Warning / highlight (e.g. "down", "skipped"). */
export function warn(text) {
    return colorEnabled() ? `${ANSI.yellow}${text}${ANSI.reset}` : text;
}
/** Error / missing (e.g. "not set"). */
export function error(text) {
    return colorEnabled() ? `${ANSI.red}${text}${ANSI.reset}` : text;
}
/** Label for a command or tool (e.g. "Bash", "neotoma"). */
export function label(text) {
    return bold(text);
}
/** Empty line for spacing between sections. */
export function nl() {
    return "\n";
}
/** Write a section: title + optional bullet list. */
export function section(title, bullets) {
    const out = [heading(title), ""];
    if (bullets?.length) {
        for (const b of bullets)
            out.push(bullet(b));
        out.push("");
    }
    return out.join("\n");
}
/** Format key: value with optional path-style value. */
export function keyValue(key, value, valueAsPath = false) {
    const v = valueAsPath ? pathStyle(value) : value;
    return `  ${dim(key + ":")} ${v}`;
}
/** Accent color for panel title, sub-headings, and illustration (warm brown). */
export function accent(text) {
    return colorEnabled() ? `${ANSI.accent}${text}${ANSI.reset}` : text;
}
/** Sub-heading in accent color (e.g. "Tips for getting started"). */
export function subHeading(text) {
    return accent(text);
}
/** Strip ANSI escape sequences for measuring visible length. */
export function visibleLength(s) {
    // eslint-disable-next-line no-control-regex
    return s.replace(/\u001b\[[0-9;]*m/g, "").length;
}
/** Terminal display width: most chars 1, fullwidth/CJK/symbols 2. Use for aligning columns. */
export function displayWidth(s) {
    // eslint-disable-next-line no-control-regex -- strip ANSI escape sequences
    const plain = s.replace(/\u001b\[[0-9;]*m/g, "");
    let w = 0;
    for (let i = 0; i < plain.length; i++) {
        const code = plain.charCodeAt(i);
        if (code >= 0x3000 && code <= 0x303f)
            w += 2; // CJK punct, fullwidth space
        else if (code >= 0x3040 && code <= 0x309f)
            w += 2; // Hiragana
        else if (code >= 0x3130 && code <= 0x318f)
            w += 2; // Hangul Compatibility Jamo (e.g. ㅅ)
        else if (code >= 0x30a0 && code <= 0x30ff)
            w += 2; // Katakana
        else if (code >= 0x4e00 && code <= 0x9fff)
            w += 2; // CJK unified
        else if (code >= 0xff00 && code <= 0xffef)
            w += 2; // Fullwidth forms
        else if (code >= 0x2600 && code <= 0x27bf)
            w += 2; // Misc symbols (✓ ✗ ✅ ❌) — terminals render as 2 cols
        else
            w += 1;
    }
    return w;
}
/** Pad a line to target display width (for aligned columns in terminal). */
export function padToDisplayWidth(line, width) {
    const need = Math.max(0, width - displayWidth(line));
    return line + " ".repeat(need);
}
/** Terminal width for box and layout sizing. Respects viewport; use for capping box width. */
export function getTerminalWidth(margin = 2) {
    const cols = typeof process.stdout?.columns === "number" ? process.stdout.columns : 80;
    return Math.max(1, cols - margin);
}
/** Truncate string to display width with ellipsis. */
function truncateToDisplayWidth(s, maxWidth) {
    if (displayWidth(s) <= maxWidth)
        return s;
    if (maxWidth <= 1)
        return "…";
    let len = s.length;
    while (len > 0 && displayWidth(s.slice(0, len) + "…") > maxWidth)
        len--;
    return (s.slice(0, len) || s.slice(0, 1)) + "…";
}
/** Box-drawing: single-line panel around content. Content is an array of lines; width is max visible length + padding, capped to terminal. */
export function panel(lines, options = {}) {
    const pad = options.padding ?? 2;
    const title = options.title ?? "";
    const rawContentWidth = options.width ?? Math.max(visibleLength(title), ...lines.map((l) => visibleLength(l)), 44);
    const contentWidth = Math.min(rawContentWidth, getTerminalWidth(2 * pad));
    const w = contentWidth + pad * 2;
    const top = "┌" + "─".repeat(w - 2) + "┐";
    const bottom = "└" + "─".repeat(w - 2) + "┘";
    const border = (s) => (colorEnabled() ? accent(s) : s);
    const out = [border(top)];
    if (title) {
        const titleLen = visibleLength(title);
        const titleLine = "│" + " ".repeat(pad) + title + " ".repeat(w - pad * 2 - titleLen) + "│";
        out.push(border(titleLine));
        out.push(border("├" + "─".repeat(w - 2) + "┤"));
    }
    const maxContent = contentWidth;
    // eslint-disable-next-line no-control-regex
    const stripAnsi = (s) => s.replace(/\u001b\[[0-9;]*m/g, "");
    for (const line of lines) {
        let outLine = line;
        if (visibleLength(line) > maxContent) {
            const plain = stripAnsi(line);
            outLine = plain.slice(0, maxContent - 1) + "…";
        }
        const len = visibleLength(outLine);
        const padded = outLine + " ".repeat(Math.max(0, contentWidth - len));
        out.push("│" + " ".repeat(pad) + padded + " ".repeat(pad) + "│");
    }
    out.push(border(bottom));
    return out.join("\n");
}
/** Pad a line to width (for side-by-side columns). */
export function padLine(line, width) {
    return line + " ".repeat(Math.max(0, width - line.length));
}
const ANSI_FG_BLACK = "\u001b[30m";
const ANSI_RESET = "\u001b[0m";
/** Black foreground (e.g. for borders and panel title). */
export function black(text) {
    return colorEnabled() ? `${ANSI_FG_BLACK}${text}${ANSI_RESET}` : text;
}
/** Rounded box-drawing chars (Claude Code-style). */
const BOX_ROUND = {
    topLeft: "╭",
    topRight: "╮",
    bottomLeft: "╰",
    bottomRight: "╯",
    horizontal: "─",
    vertical: "│",
};
function boxBorderStyle(color) {
    if (!colorEnabled())
        return (s) => s;
    const codes = {
        black: ANSI_FG_BLACK,
        accent: ANSI.accent,
        cyan: ANSI.cyan,
        green: ANSI.green,
        yellow: ANSI.yellow,
    };
    const code = codes[color];
    return (s) => (code ? `${code}${s}${ANSI.reset}` : s);
}
/**
 * Compute the inner width a box would have (for aligning multiple boxes to the same width).
 */
export function computeBoxInnerWidth(lines, options = {}) {
    const pad = Math.max(0, options.padding ?? 0);
    const minWidth = Math.max(0, options.minWidth ?? 0);
    const rawContentWidth = Math.max(0, ...lines.map((l) => displayWidth(l)));
    const contentWidth = rawContentWidth + 2 * pad;
    const title = options.title ?? "";
    const titleLen = title ? visibleLength(title) : 0;
    return Math.max(contentWidth, titleLen + 2, minWidth + 2 * pad);
}
/**
 * Draw a closed box with side lines and rounded corners. Optional title is
 * placed inside the top border (e.g. " Neotoma v0.2.15 ").
 * Use borderColor to give each box a distinct color; default is black when
 * borderBlack is true, otherwise accent.
 * Use minWidth (or sessionBoxWidth) so all session boxes share the same width.
 */
export function blackBox(lines, options = {}) {
    const pad = Math.max(0, options.padding ?? 0);
    const title = options.title ?? "";
    const titleLen = title ? visibleLength(title) : 0;
    const rawContentWidth = Math.max(0, ...lines.map((l) => displayWidth(l)));
    const contentWidth = rawContentWidth + 2 * pad;
    const innerWidth = options.sessionBoxWidth != null
        ? options.sessionBoxWidth
        : Math.max(contentWidth, titleLen + 2, (options.minWidth ?? 0) + 2 * pad);
    const color = options.borderColor ?? (options.borderBlack ? "black" : "accent");
    const borderStyle = boxBorderStyle(color);
    // Reserve space for two borders and left/right padding so the full line does not wrap
    const marginForLine = 2 + 2 * pad;
    const maxWidth = getTerminalWidth(marginForLine);
    const cappedInnerWidth = Math.max(1, Math.min(innerWidth, maxWidth));
    const padLeft = " ".repeat(pad);
    const padRight = " ".repeat(pad);
    const out = [];
    // Top border with title on the left
    if (title) {
        const titleOut = truncateToDisplayWidth(title, cappedInnerWidth);
        const rightDashes = Math.max(0, cappedInnerWidth - displayWidth(titleOut));
        const topLine = BOX_ROUND.topLeft + titleOut + BOX_ROUND.horizontal.repeat(rightDashes) + BOX_ROUND.topRight;
        out.push(borderStyle(topLine));
    }
    else {
        const topLine = BOX_ROUND.topLeft + BOX_ROUND.horizontal.repeat(cappedInnerWidth) + BOX_ROUND.topRight;
        out.push(borderStyle(topLine));
    }
    // Content lines with side borders and padding (use displayWidth so right border aligns)
    const maxContentWidth = Math.max(1, cappedInnerWidth - 2 * pad);
    // eslint-disable-next-line no-control-regex
    const stripAnsi = (s) => s.replace(/\u001b\[[0-9;]*m/g, "");
    for (const line of lines) {
        let outLine = line;
        if (displayWidth(line) > maxContentWidth) {
            const plain = stripAnsi(line);
            let len = plain.length;
            while (len > 0 && displayWidth(plain.slice(0, len) + "…") > maxContentWidth)
                len--;
            outLine = (plain.slice(0, len) || plain.slice(0, 1)) + "…";
        }
        const len = displayWidth(outLine);
        const contentPadded = outLine + " ".repeat(Math.max(0, maxContentWidth - len));
        out.push(borderStyle(BOX_ROUND.vertical) +
            padLeft +
            contentPadded +
            padRight +
            borderStyle(BOX_ROUND.vertical));
    }
    // Bottom border
    const bottomLine = BOX_ROUND.bottomLeft + BOX_ROUND.horizontal.repeat(cappedInnerWidth) + BOX_ROUND.bottomRight;
    out.push(borderStyle(bottomLine));
    return out.join("\n");
}
//# sourceMappingURL=format.js.map