/**
 * ANSI escape code to HTML converter.
 *
 * Converts terminal ANSI color/style codes to HTML with inline styles.
 * Supports:
 * - Standard foreground colors (30-37) and bright variants (90-97)
 * - Standard background colors (40-47) and bright variants (100-107)
 * - 256-color palette (38;5;N and 48;5;N)
 * - RGB true color (38;2;R;G;B and 48;2;R;G;B)
 * - Text styles: bold (1), dim (2), italic (3), underline (4)
 * - Reset (0)
 */
// Standard ANSI color palette (0-15)
const ANSI_COLORS = [
    "#000000", // 0: black
    "#800000", // 1: red
    "#008000", // 2: green
    "#808000", // 3: yellow
    "#000080", // 4: blue
    "#800080", // 5: magenta
    "#008080", // 6: cyan
    "#c0c0c0", // 7: white
    "#808080", // 8: bright black
    "#ff0000", // 9: bright red
    "#00ff00", // 10: bright green
    "#ffff00", // 11: bright yellow
    "#0000ff", // 12: bright blue
    "#ff00ff", // 13: bright magenta
    "#00ffff", // 14: bright cyan
    "#ffffff", // 15: bright white
];
/**
 * Convert 256-color index to hex.
 */
function color256ToHex(index) {
    // Standard colors (0-15)
    if (index < 16) {
        return ANSI_COLORS[index];
    }
    // Color cube (16-231): 6x6x6 = 216 colors
    if (index < 232) {
        const cubeIndex = index - 16;
        const r = Math.floor(cubeIndex / 36);
        const g = Math.floor((cubeIndex % 36) / 6);
        const b = cubeIndex % 6;
        const toComponent = (n) => (n === 0 ? 0 : 55 + n * 40);
        const toHex = (n) => toComponent(n).toString(16).padStart(2, "0");
        return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
    }
    // Grayscale (232-255): 24 shades
    const gray = 8 + (index - 232) * 10;
    const grayHex = gray.toString(16).padStart(2, "0");
    return `#${grayHex}${grayHex}${grayHex}`;
}
/**
 * Escape HTML special characters.
 */
function escapeHtml(text) {
    return text
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#039;");
}
function createEmptyStyle() {
    return {
        fg: null,
        bg: null,
        bold: false,
        dim: false,
        italic: false,
        underline: false,
    };
}
function styleToInlineCSS(style) {
    const parts = [];
    if (style.fg)
        parts.push(`color:${style.fg}`);
    if (style.bg)
        parts.push(`background-color:${style.bg}`);
    if (style.bold)
        parts.push("font-weight:bold");
    if (style.dim)
        parts.push("opacity:0.6");
    if (style.italic)
        parts.push("font-style:italic");
    if (style.underline)
        parts.push("text-decoration:underline");
    return parts.join(";");
}
function hasStyle(style) {
    return style.fg !== null || style.bg !== null || style.bold || style.dim || style.italic || style.underline;
}
/**
 * Parse ANSI SGR (Select Graphic Rendition) codes and update style.
 */
function applySgrCode(params, style) {
    let i = 0;
    while (i < params.length) {
        const code = params[i];
        if (code === 0) {
            // Reset all
            style.fg = null;
            style.bg = null;
            style.bold = false;
            style.dim = false;
            style.italic = false;
            style.underline = false;
        }
        else if (code === 1) {
            style.bold = true;
        }
        else if (code === 2) {
            style.dim = true;
        }
        else if (code === 3) {
            style.italic = true;
        }
        else if (code === 4) {
            style.underline = true;
        }
        else if (code === 22) {
            // Reset bold/dim
            style.bold = false;
            style.dim = false;
        }
        else if (code === 23) {
            style.italic = false;
        }
        else if (code === 24) {
            style.underline = false;
        }
        else if (code >= 30 && code <= 37) {
            // Standard foreground colors
            style.fg = ANSI_COLORS[code - 30];
        }
        else if (code === 38) {
            // Extended foreground color
            if (params[i + 1] === 5 && params.length > i + 2) {
                // 256-color: 38;5;N
                style.fg = color256ToHex(params[i + 2]);
                i += 2;
            }
            else if (params[i + 1] === 2 && params.length > i + 4) {
                // RGB: 38;2;R;G;B
                const r = params[i + 2];
                const g = params[i + 3];
                const b = params[i + 4];
                style.fg = `rgb(${r},${g},${b})`;
                i += 4;
            }
        }
        else if (code === 39) {
            // Default foreground
            style.fg = null;
        }
        else if (code >= 40 && code <= 47) {
            // Standard background colors
            style.bg = ANSI_COLORS[code - 40];
        }
        else if (code === 48) {
            // Extended background color
            if (params[i + 1] === 5 && params.length > i + 2) {
                // 256-color: 48;5;N
                style.bg = color256ToHex(params[i + 2]);
                i += 2;
            }
            else if (params[i + 1] === 2 && params.length > i + 4) {
                // RGB: 48;2;R;G;B
                const r = params[i + 2];
                const g = params[i + 3];
                const b = params[i + 4];
                style.bg = `rgb(${r},${g},${b})`;
                i += 4;
            }
        }
        else if (code === 49) {
            // Default background
            style.bg = null;
        }
        else if (code >= 90 && code <= 97) {
            // Bright foreground colors
            style.fg = ANSI_COLORS[code - 90 + 8];
        }
        else if (code >= 100 && code <= 107) {
            // Bright background colors
            style.bg = ANSI_COLORS[code - 100 + 8];
        }
        // Ignore unrecognized codes
        i++;
    }
}
// Match ANSI escape sequences: ESC[ followed by params and ending with 'm'
const ANSI_REGEX = /\x1b\[([\d;]*)m/g;
/**
 * Convert ANSI-escaped text to HTML with inline styles.
 */
export function ansiToHtml(text) {
    const style = createEmptyStyle();
    let result = "";
    let lastIndex = 0;
    let inSpan = false;
    // Reset regex state
    ANSI_REGEX.lastIndex = 0;
    let match = ANSI_REGEX.exec(text);
    while (match !== null) {
        // Add text before this escape sequence
        const beforeText = text.slice(lastIndex, match.index);
        if (beforeText) {
            result += escapeHtml(beforeText);
        }
        // Parse SGR parameters
        const paramStr = match[1];
        const params = paramStr ? paramStr.split(";").map((p) => parseInt(p, 10) || 0) : [0];
        // Close existing span if we have one
        if (inSpan) {
            result += "</span>";
            inSpan = false;
        }
        // Apply the codes
        applySgrCode(params, style);
        // Open new span if we have any styling
        if (hasStyle(style)) {
            result += `<span style="${styleToInlineCSS(style)}">`;
            inSpan = true;
        }
        lastIndex = match.index + match[0].length;
        match = ANSI_REGEX.exec(text);
    }
    // Add remaining text
    const remainingText = text.slice(lastIndex);
    if (remainingText) {
        result += escapeHtml(remainingText);
    }
    // Close any open span
    if (inSpan) {
        result += "</span>";
    }
    return result;
}
/**
 * Convert array of ANSI-escaped lines to HTML.
 * Each line is wrapped in a div element.
 */
export function ansiLinesToHtml(lines) {
    return lines.map((line) => `<div class="ansi-line">${ansiToHtml(line) || "&nbsp;"}</div>`).join("\n");
}
//# sourceMappingURL=ansi-to-html.js.map