1export const escapeHtml = (s) => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>') 4 '0': '</span>', '1': '<span style="font-weight:bold">', 5 '31': '<span style="color:var(--red)">', '32': '<span style="color:var(--green)">', 6 '33': '<span style="color:var(--yellow)">', '34': '<span style="color:var(--blue)">', 7 '36': '<span style="color:var(--cyan)">', '37': '<span style="color:var(--white)">', 8 '90': '<span style="color:var(--dim)">', 9 '91': '<span style="color:var(--red)">', '92': '<span style="color:var(--green)">', 10 '93': '<span style="color:var(--yellow)">', '94': '<span style="color:var(--blue)">', 11 '96': '<span style="color:var(--cyan)">', '97': '<span style="color:var(--white)">', 12 '38;5;43': '<span style="color:var(--teal)">', '38;5;212': '<span style="color:var(--pink)">', 13 '38;5;215': '<span style="color:var(--orange)">', 14 '48;5;236': '<span style="background:var(--bg-gray)">', 15 '48;2;17;17;17': '<span style="background:var(--bg-near-black)">', 16 '48;2;157;124;216': '<span style="background:var(--bar-purple)">', 17 '48;2;59;130;246': '<span style="background:var(--bar-blue)">', 18 '48;2;10;14;10': '<span class="diff-add">', '48;2;14;10;10': '<span class="diff-del">', 19 '38;2;140;195;140': '<span style="color:var(--diff-add-text)">', 20 '38;2;220;100;90': '<span style="color:var(--diff-del-text)">', 23export const ansiToHtml = (ansiStr) => { 24 const escaped = escapeHtml(ansiStr) 25 const ansiRegex = /\x1b\[([0-9;]+)m/g 26 let result = '', lastIdx = 0, openSpans = 0, match 27 while ((match = ansiRegex.exec(escaped)) !== null) { 28 result += escaped.slice(lastIdx, match.index) 29 lastIdx = match.index + match[0].length 30 const parts = match[1].split(';') 32 while (i < parts.length) { 34 if (code === '0' || code === '39' || code === '49') { 35 result += '</span>'.repeat(openSpans) 38 } else if (code === '38' || code === '48') { 39 const fullCode = parts.slice(i, i + (parts[i + 1] === '5' ? 3 : 5)).join(';') 40 const span = ansiMap[fullCode] 41 if (span) { result += span; openSpans++ } 42 i += parts[i + 1] === '5' ? 3 : 5 44 const span = ansiMap[code] 45 if (span) { result += span; openSpans++ } 50 result += escaped.slice(lastIdx) 51 result += '</span>'.repeat(openSpans)