105 lines
5.1 KiB
TypeScript
105 lines
5.1 KiB
TypeScript
import type { ThemeColors } from './theme.js'
|
|
|
|
type Line = [string, string]
|
|
|
|
// ── Rich markup parser ──────────────────────────────────────────────
|
|
// Parses Python Rich markup like "[bold #A3261F]text[/]" into Line[].
|
|
const RICH_RE = /\[(?:bold\s+)?(?:dim\s+)?(#(?:[0-9a-fA-F]{3,8}))\]([\s\S]*?)(\[\/\])/g
|
|
|
|
export function parseRichMarkup(markup: string): Line[] {
|
|
const lines: Line[] = []
|
|
|
|
for (const raw of markup.split('\n')) {
|
|
const trimmed = raw.trimEnd()
|
|
|
|
if (!trimmed) {
|
|
lines.push(['', ' '])
|
|
|
|
continue
|
|
}
|
|
|
|
let lastIndex = 0
|
|
let matched = false
|
|
let m: RegExpExecArray | null
|
|
|
|
RICH_RE.lastIndex = 0
|
|
|
|
while ((m = RICH_RE.exec(trimmed)) !== null) {
|
|
matched = true
|
|
const before = trimmed.slice(lastIndex, m.index)
|
|
|
|
if (before) {
|
|
lines.push(['', before])
|
|
}
|
|
|
|
lines.push([m[1]!, m[2]!])
|
|
lastIndex = m.index + m[0].length
|
|
}
|
|
|
|
if (!matched) {
|
|
lines.push(['', trimmed])
|
|
} else if (lastIndex < trimmed.length) {
|
|
lines.push(['', trimmed.slice(lastIndex)])
|
|
}
|
|
}
|
|
|
|
return lines
|
|
}
|
|
|
|
const LOGO_ART = [
|
|
'██╗ ██╗███████╗██████╗ ███╗ ███╗███████╗███████╗ █████╗ ██████╗ ███████╗███╗ ██╗████████╗',
|
|
'██║ ██║██╔════╝██╔══██╗████╗ ████║██╔════╝██╔════╝ ██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝',
|
|
'███████║█████╗ ██████╔╝██╔████╔██║█████╗ ███████╗█████╗███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║ ',
|
|
'██╔══██║██╔══╝ ██╔══██╗██║╚██╔╝██║██╔══╝ ╚════██║╚════╝██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ',
|
|
'██║ ██║███████╗██║ ██║██║ ╚═╝ ██║███████╗███████║ ██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ',
|
|
'╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ '
|
|
]
|
|
|
|
const CADUCEUS_ART = [
|
|
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⡀⠀⣀⣀⠀⢀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀',
|
|
'⠀⠀⠀⠀⠀⠀⢀⣠⣴⣾⣿⣿⣇⠸⣿⣿⠇⣸⣿⣿⣷⣦⣄⡀⠀⠀⠀⠀⠀⠀',
|
|
'⠀⢀⣠⣴⣶⠿⠋⣩⡿⣿⡿⠻⣿⡇⢠⡄⢸⣿⠟⢿⣿⢿⣍⠙⠿⣶⣦⣄⡀⠀',
|
|
'⠀⠀⠉⠉⠁⠶⠟⠋⠀⠉⠀⢀⣈⣁⡈⢁⣈⣁⡀⠀⠉⠀⠙⠻⠶⠈⠉⠉⠀⠀',
|
|
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⣿⡿⠛⢁⡈⠛⢿⣿⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀',
|
|
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠿⣿⣦⣤⣈⠁⢠⣴⣿⠿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀',
|
|
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠻⢿⣿⣦⡉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀',
|
|
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢷⣦⣈⠛⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀',
|
|
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣴⠦⠈⠙⠿⣦⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀',
|
|
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⣤⡈⠁⢤⣿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀',
|
|
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠷⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀',
|
|
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⠑⢶⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀',
|
|
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠁⢰⡆⠈⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀',
|
|
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⠈⣡⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀',
|
|
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀'
|
|
]
|
|
|
|
const LOGO_GRADIENT = [0, 0, 1, 1, 2, 2] as const
|
|
const CADUC_GRADIENT = [2, 2, 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3, 3] as const
|
|
|
|
function colorize(art: string[], gradient: readonly number[], c: ThemeColors): Line[] {
|
|
const palette = [c.gold, c.amber, c.bronze, c.dim]
|
|
|
|
return art.map((text, i) => [palette[gradient[i]] ?? c.dim, text])
|
|
}
|
|
|
|
export const LOGO_WIDTH = 98
|
|
export const CADUCEUS_WIDTH = 30
|
|
|
|
export const logo = (c: ThemeColors, customLogo?: string): Line[] =>
|
|
customLogo ? parseRichMarkup(customLogo) : colorize(LOGO_ART, LOGO_GRADIENT, c)
|
|
|
|
export const caduceus = (c: ThemeColors, customHero?: string): Line[] =>
|
|
customHero ? parseRichMarkup(customHero) : colorize(CADUCEUS_ART, CADUC_GRADIENT, c)
|
|
|
|
export function artWidth(lines: Line[]): number {
|
|
let max = 0
|
|
|
|
for (const [, text] of lines) {
|
|
if (text.length > max) {
|
|
max = text.length
|
|
}
|
|
}
|
|
|
|
return max
|
|
}
|