Refactor ANSI parser to support SGR reset (code 39) and bold/dim styles
Merge color and style maps into a unified STYLES table, hoist the regex to module scope, export stripAnsi for use in log parsing, and handle SGR 39 (default foreground) by removing only color styles instead of clearing all styles. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
33d91747af
commit
a824d62058
|
|
@ -1,26 +1,36 @@
|
||||||
const COLORS: Record<number, string> = {
|
const ESC = /\x1b\[([0-9;]*)m/g
|
||||||
30: '#666', // black (brightened for dark bg)
|
|
||||||
31: '#f87171', // red
|
const STYLES: Record<number, string> = {
|
||||||
32: '#4ade80', // green
|
1: 'font-weight:bold',
|
||||||
33: '#facc15', // yellow
|
2: 'opacity:0.7',
|
||||||
34: '#60a5fa', // blue
|
30: 'color:#666',
|
||||||
35: '#c084fc', // magenta
|
31: 'color:#f87171',
|
||||||
36: '#22d3ee', // cyan
|
32: 'color:#4ade80',
|
||||||
37: '#e5e5e5', // white
|
33: 'color:#facc15',
|
||||||
90: '#999', // bright black
|
34: 'color:#60a5fa',
|
||||||
91: '#fca5a5', // bright red
|
35: 'color:#c084fc',
|
||||||
92: '#86efac', // bright green
|
36: 'color:#22d3ee',
|
||||||
93: '#fde047', // bright yellow
|
37: 'color:#e5e5e5',
|
||||||
94: '#93c5fd', // bright blue
|
90: 'color:#999',
|
||||||
95: '#d8b4fe', // bright magenta
|
91: 'color:#fca5a5',
|
||||||
96: '#67e8f9', // bright cyan
|
92: 'color:#86efac',
|
||||||
97: '#fff', // bright white
|
93: 'color:#fde047',
|
||||||
|
94: 'color:#93c5fd',
|
||||||
|
95: 'color:#d8b4fe',
|
||||||
|
96: 'color:#67e8f9',
|
||||||
|
97: 'color:#fff',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const escape = (s: string) =>
|
||||||
|
s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||||
|
|
||||||
|
export const stripAnsi = (s: string) =>
|
||||||
|
s.replace(/\x1b\[[0-9;]*m/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
|
ESC.lastIndex = 0
|
||||||
let result = ''
|
let result = ''
|
||||||
let last = 0
|
let last = 0
|
||||||
let open = false
|
let open = false
|
||||||
|
|
@ -34,15 +44,16 @@ export function ansiToHtml(text: string): string {
|
||||||
const styles: string[] = []
|
const styles: string[] = []
|
||||||
|
|
||||||
for (const code of codes) {
|
for (const code of codes) {
|
||||||
if (code === 0 || code === 39) {
|
if (code === 0) {
|
||||||
styles.length = 0
|
styles.length = 0
|
||||||
if (open) { result += '</span>'; open = false }
|
if (open) { result += '</span>'; open = false }
|
||||||
} else if (COLORS[code]) {
|
} else if (code === 39) {
|
||||||
styles.push(`color:${COLORS[code]}`)
|
const filtered = styles.filter(s => !s.startsWith('color:'))
|
||||||
} else if (code === 1) {
|
styles.length = 0
|
||||||
styles.push('font-weight:bold')
|
styles.push(...filtered)
|
||||||
} else if (code === 2) {
|
if (open) { result += '</span>'; open = false }
|
||||||
styles.push('opacity:0.7')
|
} else if (STYLES[code]) {
|
||||||
|
styles.push(STYLES[code])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,6 +69,3 @@ export function ansiToHtml(text: string): string {
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
const escape = (s: string) =>
|
|
||||||
s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { ansiToHtml } from '../ansi'
|
import { ansiToHtml, stripAnsi } from '../ansi'
|
||||||
import { isNarrow } from '../state'
|
import { isNarrow } from '../state'
|
||||||
import {
|
import {
|
||||||
LogApp,
|
LogApp,
|
||||||
|
|
@ -59,7 +59,7 @@ function parseLogText(text: string): { method?: string, path?: string, status?:
|
||||||
}
|
}
|
||||||
|
|
||||||
function LogLineEntry({ log }: { log: UnifiedLogLine }) {
|
function LogLineEntry({ log }: { log: UnifiedLogLine }) {
|
||||||
const parsed = parseLogText(log.text)
|
const parsed = parseLogText(stripAnsi(log.text))
|
||||||
const statusColor = getStatusColor(parsed.status)
|
const statusColor = getStatusColor(parsed.status)
|
||||||
const narrow = isNarrow || undefined
|
const narrow = isNarrow || undefined
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user