from __future__ import annotations
import re
import sys
from pathlib import Path
PRESETS_FILE = Path("src/tui/render/presets.rs")
GRAY_RAMP_START = 232
def rgb_to_ansi256(r: int, g: int, b: int) -> int:
if r == g == b:
idx = int(round(r * 23.0 / 255.0))
idx = max(0, min(23, idx))
return GRAY_RAMP_START + idx
def axis(v: int) -> int:
levels = [0, 95, 135, 175, 215, 255]
return min(range(6), key=lambda i: abs(v - levels[i]))
ri, gi, bi = axis(r), axis(g), axis(b)
return 16 + 36 * ri + 6 * gi + bi
PRESET_RE = re.compile(
r"pub static (\w+): Theme = Theme \{[^}]*?colors: ThemeColors \{([^}]+)\}",
re.DOTALL,
)
RGB_FIELD_RE = re.compile(r"(\w+):\s*rgb\((0x[0-9A-Fa-f]+)\)")
def parse_preset(body: str) -> list[tuple[str, int, int, int]]:
out = []
for m in RGB_FIELD_RE.finditer(body):
name = m.group(1)
hex_str = m.group(2)
val = int(hex_str, 16)
r = (val >> 16) & 0xFF
g = (val >> 8) & 0xFF
b = val & 0xFF
out.append((name, r, g, b))
return out
def format_ansi_block(fields: list[tuple[str, int, int, int]], indent: str = " ") -> str:
lines = ["ansi: AnsiColors {"]
for name, r, g, b in fields:
idx = rgb_to_ansi256(r, g, b)
lines.append(f"{indent}{indent}{name}: {idx},")
lines.append(f"{indent}}},")
return "\n".join(lines)
def main() -> int:
src = PRESETS_FILE.read_text()
matches = list(PRESET_RE.finditer(src))
if not matches:
print("no presets found", file=sys.stderr)
return 1
print(f"// Generated by {__file__} — do not edit by hand.")
print(f"// Source of truth: src/tui/render/presets.rs (rgb literals)\n")
for m in matches:
name = m.group(1)
fields = parse_preset(m.group(2))
print(f"// {name}: {len(fields)} fields")
print(format_ansi_block(fields, indent=" "))
print()
return 0
if __name__ == "__main__":
sys.exit(main())