Files
WHCandCursor 00288108cd feat: TUI chrome refresh — steel theme, home card, session topbar (0.9.24)
Cooler brand palette, compact CST-agent home with tagline and framed
composer, session topbar (model + chair chip), sidebar off by default.
Screenshot pipeline in tools/tui_shot.py; docs/screenshots for before/after.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 18:27:06 +08:00

154 lines
4.1 KiB
Python

#!/usr/bin/env python3
"""ANSI pane dump → HTML → PNG (chrome headless). Usage: tui_shot.py in.ansi out.png"""
from __future__ import annotations
import html
import subprocess
import sys
from pathlib import Path
SPECIAL = {
75: "#6ea8fe",
74: "#61b8c7",
216: "#faa06b",
233: "#0e0f12",
234: "#14161e",
235: "#1a1c24",
244: "#80848f",
250: "#bcbcbc",
252: "#d1d4d9",
255: "#ffffff",
242: "#6a6a6a",
114: "#66bf73",
167: "#d95252",
157: "#afff87",
22: "#005f00",
203: "#ff5f5f",
52: "#5f0000",
}
def ansi_to_html(text: str) -> str:
cmap = {
30: "#3c3c46",
31: "#e65a5a",
32: "#5ad278",
33: "#faa06b",
34: "#6ea8fe",
35: "#d282d2",
36: "#5ad2e6",
37: "#bcbcbc",
90: "#80848f",
91: "#ff8282",
92: "#82ff96",
93: "#ffeb82",
94: "#96beff",
95: "#eba0eb",
96: "#82ebff",
97: "#ffffff",
}
out: list[str] = []
i = 0
bold = dim = False
fg = "#d1d4d9"
open_span = False
def close() -> None:
nonlocal open_span
if open_span:
out.append("</span>")
open_span = False
def reopen() -> None:
nonlocal open_span
close()
styles = [f"color:{fg}"]
if bold:
styles.append("font-weight:700")
if dim:
styles.append("opacity:.65")
out.append(f'<span style="{";".join(styles)}">')
open_span = True
reopen()
while i < len(text):
if text[i] == "\x1b" and i + 1 < len(text) and text[i + 1] == "[":
j = i + 2
while j < len(text) and text[j] != "m":
j += 1
seq = text[i + 2 : j]
i = j + 1 if j < len(text) else len(text)
parts = [int(p) for p in seq.split(";") if p.isdigit()] if seq else [0]
k = 0
while k < len(parts):
c = parts[k]
if c == 0:
bold = dim = False
fg = "#d1d4d9"
elif c == 1:
bold = True
elif c == 2:
dim = True
elif c == 22:
bold = dim = False
elif c == 38 and k + 2 < len(parts) and parts[k + 1] == 5:
fg = SPECIAL.get(parts[k + 2], fg)
k += 2
elif c in cmap:
fg = cmap[c]
elif c == 39:
fg = "#d1d4d9"
k += 1
reopen()
continue
if text[i] == "\n":
out.append("\n")
i += 1
continue
if text[i] == "\r":
i += 1
continue
out.append(html.escape(text[i]))
i += 1
close()
body = "".join(out)
return f"""<!doctype html><meta charset=utf-8>
<style>
html,body{{margin:0;background:#07080a}}
pre{{margin:0;padding:18px 22px;background:#0e0f12;color:#d1d4d9;
font:14px/1.35 "JetBrains Mono","Cascadia Mono",Menlo,Consolas,monospace;
white-space:pre; min-height:100vh; letter-spacing:0.01em}}
</style><pre>{body}</pre>"""
def main() -> int:
if len(sys.argv) < 3:
print("usage: tui_shot.py in.ansi out.png", file=sys.stderr)
return 2
ansi = Path(sys.argv[1]).read_text(encoding="utf-8", errors="replace")
html_path = Path(sys.argv[2]).with_suffix(".html")
html_path.write_text(ansi_to_html(ansi), encoding="utf-8")
png = Path(sys.argv[2])
chrome = "google-chrome"
subprocess.run(
[
chrome,
"--headless=new",
"--disable-gpu",
"--no-sandbox",
"--hide-scrollbars",
"--window-size=1280,800",
f"--screenshot={png}",
html_path.resolve().as_uri(),
],
check=False,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
print(png, png.stat().st_size if png.exists() else 0)
return 0 if png.exists() else 1
if __name__ == "__main__":
raise SystemExit(main())