Combine ANSI style codes into a single span element
Multiple SGR parameters in one escape sequence (e.g. bold + color) were each opening a new span, losing earlier styles. Collect styles per sequence and emit one span with all of them.
This commit is contained in:
parent
33d21777d3
commit
33d91747af
|
|
@ -17,14 +17,10 @@ const COLORS: Record<number, string> = {
|
||||||
97: '#fff', // bright white
|
97: '#fff', // bright white
|
||||||
}
|
}
|
||||||
|
|
||||||
const ESC = /\x1b\[([0-9;]*)m/g
|
|
||||||
|
|
||||||
const escape = (s: string) =>
|
|
||||||
s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
||||||
|
|
||||||
export function ansiToHtml(text: string): string {
|
export function ansiToHtml(text: string): string {
|
||||||
if (!text.includes('\x1b')) return escape(text)
|
if (!text.includes('\x1b')) return escape(text)
|
||||||
|
|
||||||
|
const ESC = /\x1b\[([0-9;]*)m/g
|
||||||
let result = ''
|
let result = ''
|
||||||
let last = 0
|
let last = 0
|
||||||
let open = false
|
let open = false
|
||||||
|
|
@ -35,24 +31,26 @@ export function ansiToHtml(text: string): string {
|
||||||
last = match.index + match[0].length
|
last = match.index + match[0].length
|
||||||
|
|
||||||
const codes = match[1] ? match[1].split(';').map(Number) : [0]
|
const codes = match[1] ? match[1].split(';').map(Number) : [0]
|
||||||
|
const styles: string[] = []
|
||||||
|
|
||||||
for (const code of codes) {
|
for (const code of codes) {
|
||||||
if (code === 0 || code === 39) {
|
if (code === 0 || code === 39) {
|
||||||
|
styles.length = 0
|
||||||
if (open) { result += '</span>'; open = false }
|
if (open) { result += '</span>'; open = false }
|
||||||
} else if (COLORS[code]) {
|
} else if (COLORS[code]) {
|
||||||
if (open) result += '</span>'
|
styles.push(`color:${COLORS[code]}`)
|
||||||
result += `<span style="color:${COLORS[code]}">`
|
|
||||||
open = true
|
|
||||||
} else if (code === 1) {
|
} else if (code === 1) {
|
||||||
if (open) result += '</span>'
|
styles.push('font-weight:bold')
|
||||||
result += '<span style="font-weight:bold">'
|
|
||||||
open = true
|
|
||||||
} else if (code === 2) {
|
} else if (code === 2) {
|
||||||
if (open) result += '</span>'
|
styles.push('opacity:0.7')
|
||||||
result += '<span style="opacity:0.7">'
|
|
||||||
open = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (styles.length) {
|
||||||
|
if (open) result += '</span>'
|
||||||
|
result += `<span style="${styles.join(';')}">`
|
||||||
|
open = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result += escape(text.slice(last))
|
result += escape(text.slice(last))
|
||||||
|
|
@ -60,3 +58,6 @@ export function ansiToHtml(text: string): string {
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const escape = (s: string) =>
|
||||||
|
s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user