init claude-code
This commit is contained in:
@@ -0,0 +1,308 @@
|
||||
/**
|
||||
* SGR (Select Graphic Rendition) Parser
|
||||
*
|
||||
* Parses SGR parameters and applies them to a TextStyle.
|
||||
* Handles both semicolon (;) and colon (:) separated parameters.
|
||||
*/
|
||||
|
||||
import type { NamedColor, TextStyle, UnderlineStyle } from './types.js'
|
||||
import { defaultStyle } from './types.js'
|
||||
|
||||
const NAMED_COLORS: NamedColor[] = [
|
||||
'black',
|
||||
'red',
|
||||
'green',
|
||||
'yellow',
|
||||
'blue',
|
||||
'magenta',
|
||||
'cyan',
|
||||
'white',
|
||||
'brightBlack',
|
||||
'brightRed',
|
||||
'brightGreen',
|
||||
'brightYellow',
|
||||
'brightBlue',
|
||||
'brightMagenta',
|
||||
'brightCyan',
|
||||
'brightWhite',
|
||||
]
|
||||
|
||||
const UNDERLINE_STYLES: UnderlineStyle[] = [
|
||||
'none',
|
||||
'single',
|
||||
'double',
|
||||
'curly',
|
||||
'dotted',
|
||||
'dashed',
|
||||
]
|
||||
|
||||
type Param = { value: number | null; subparams: number[]; colon: boolean }
|
||||
|
||||
function parseParams(str: string): Param[] {
|
||||
if (str === '') return [{ value: 0, subparams: [], colon: false }]
|
||||
|
||||
const result: Param[] = []
|
||||
let current: Param = { value: null, subparams: [], colon: false }
|
||||
let num = ''
|
||||
let inSub = false
|
||||
|
||||
for (let i = 0; i <= str.length; i++) {
|
||||
const c = str[i]
|
||||
if (c === ';' || c === undefined) {
|
||||
const n = num === '' ? null : parseInt(num, 10)
|
||||
if (inSub) {
|
||||
if (n !== null) current.subparams.push(n)
|
||||
} else {
|
||||
current.value = n
|
||||
}
|
||||
result.push(current)
|
||||
current = { value: null, subparams: [], colon: false }
|
||||
num = ''
|
||||
inSub = false
|
||||
} else if (c === ':') {
|
||||
const n = num === '' ? null : parseInt(num, 10)
|
||||
if (!inSub) {
|
||||
current.value = n
|
||||
current.colon = true
|
||||
inSub = true
|
||||
} else {
|
||||
if (n !== null) current.subparams.push(n)
|
||||
}
|
||||
num = ''
|
||||
} else if (c >= '0' && c <= '9') {
|
||||
num += c
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function parseExtendedColor(
|
||||
params: Param[],
|
||||
idx: number,
|
||||
): { r: number; g: number; b: number } | { index: number } | null {
|
||||
const p = params[idx]
|
||||
if (!p) return null
|
||||
|
||||
if (p.colon && p.subparams.length >= 1) {
|
||||
if (p.subparams[0] === 5 && p.subparams.length >= 2) {
|
||||
return { index: p.subparams[1]! }
|
||||
}
|
||||
if (p.subparams[0] === 2 && p.subparams.length >= 4) {
|
||||
const off = p.subparams.length >= 5 ? 1 : 0
|
||||
return {
|
||||
r: p.subparams[1 + off]!,
|
||||
g: p.subparams[2 + off]!,
|
||||
b: p.subparams[3 + off]!,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const next = params[idx + 1]
|
||||
if (!next) return null
|
||||
if (
|
||||
next.value === 5 &&
|
||||
params[idx + 2]?.value !== null &&
|
||||
params[idx + 2]?.value !== undefined
|
||||
) {
|
||||
return { index: params[idx + 2]!.value! }
|
||||
}
|
||||
if (next.value === 2) {
|
||||
const r = params[idx + 2]?.value
|
||||
const g = params[idx + 3]?.value
|
||||
const b = params[idx + 4]?.value
|
||||
if (
|
||||
r !== null &&
|
||||
r !== undefined &&
|
||||
g !== null &&
|
||||
g !== undefined &&
|
||||
b !== null &&
|
||||
b !== undefined
|
||||
) {
|
||||
return { r, g, b }
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export function applySGR(paramStr: string, style: TextStyle): TextStyle {
|
||||
const params = parseParams(paramStr)
|
||||
let s = { ...style }
|
||||
let i = 0
|
||||
|
||||
while (i < params.length) {
|
||||
const p = params[i]!
|
||||
const code = p.value ?? 0
|
||||
|
||||
if (code === 0) {
|
||||
s = defaultStyle()
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 1) {
|
||||
s.bold = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 2) {
|
||||
s.dim = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 3) {
|
||||
s.italic = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 4) {
|
||||
s.underline = p.colon
|
||||
? (UNDERLINE_STYLES[p.subparams[0]!] ?? 'single')
|
||||
: 'single'
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 5 || code === 6) {
|
||||
s.blink = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 7) {
|
||||
s.inverse = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 8) {
|
||||
s.hidden = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 9) {
|
||||
s.strikethrough = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 21) {
|
||||
s.underline = 'double'
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 22) {
|
||||
s.bold = false
|
||||
s.dim = false
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 23) {
|
||||
s.italic = false
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 24) {
|
||||
s.underline = 'none'
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 25) {
|
||||
s.blink = false
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 27) {
|
||||
s.inverse = false
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 28) {
|
||||
s.hidden = false
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 29) {
|
||||
s.strikethrough = false
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 53) {
|
||||
s.overline = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 55) {
|
||||
s.overline = false
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if (code >= 30 && code <= 37) {
|
||||
s.fg = { type: 'named', name: NAMED_COLORS[code - 30]! }
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 39) {
|
||||
s.fg = { type: 'default' }
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code >= 40 && code <= 47) {
|
||||
s.bg = { type: 'named', name: NAMED_COLORS[code - 40]! }
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code === 49) {
|
||||
s.bg = { type: 'default' }
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code >= 90 && code <= 97) {
|
||||
s.fg = { type: 'named', name: NAMED_COLORS[code - 90 + 8]! }
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (code >= 100 && code <= 107) {
|
||||
s.bg = { type: 'named', name: NAMED_COLORS[code - 100 + 8]! }
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if (code === 38) {
|
||||
const c = parseExtendedColor(params, i)
|
||||
if (c) {
|
||||
s.fg =
|
||||
'index' in c
|
||||
? { type: 'indexed', index: c.index }
|
||||
: { type: 'rgb', ...c }
|
||||
i += p.colon ? 1 : 'index' in c ? 3 : 5
|
||||
continue
|
||||
}
|
||||
}
|
||||
if (code === 48) {
|
||||
const c = parseExtendedColor(params, i)
|
||||
if (c) {
|
||||
s.bg =
|
||||
'index' in c
|
||||
? { type: 'indexed', index: c.index }
|
||||
: { type: 'rgb', ...c }
|
||||
i += p.colon ? 1 : 'index' in c ? 3 : 5
|
||||
continue
|
||||
}
|
||||
}
|
||||
if (code === 58) {
|
||||
const c = parseExtendedColor(params, i)
|
||||
if (c) {
|
||||
s.underlineColor =
|
||||
'index' in c
|
||||
? { type: 'indexed', index: c.index }
|
||||
: { type: 'rgb', ...c }
|
||||
i += p.colon ? 1 : 'index' in c ? 3 : 5
|
||||
continue
|
||||
}
|
||||
}
|
||||
if (code === 59) {
|
||||
s.underlineColor = { type: 'default' }
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
return s
|
||||
}
|
||||
Reference in New Issue
Block a user