Files
caiwuzongzhang/scripts/hel342_shot.py
T
b905cba52c HEL-342: 全站切换霜曜日间与黑金夜间主题
按定稿把运行时 token 换成霜曜/黑金双主题,登录与壳层接入官方 Logo 与主题记忆,不改业务接口与金额计算。

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
2026-09-01 17:44:35 +08:00

127 lines
6.0 KiB
Python

"""HEL-342: static visual shots + computed-style probe (no business data writes)."""
from __future__ import annotations
import json
from functools import partial
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from threading import Thread
ROOT = Path(__file__).resolve().parents[1]
WEB = ROOT / "web"
OUT = ROOT.parent / "hel342-shots"
PROBES = [
(".sidebar", ["width", "backgroundColor", "backdropFilter"]),
(".brand-logo-day", ["width", "height"]),
(".btn-primary", ["backgroundColor", "height", "borderRadius", "color"]),
(".stat-card.gold .stat-value, .stat-card .stat-value", ["color", "fontSize", "fontVariantNumeric"]),
(".page-head h1", ["fontSize", "fontWeight"]),
(".ds-table th", ["fontSize", "color"]),
]
def main() -> None:
from playwright.sync_api import sync_playwright
OUT.mkdir(parents=True, exist_ok=True)
handler = partial(SimpleHTTPRequestHandler, directory=str(WEB))
httpd = ThreadingHTTPServer(("127.0.0.1", 0), handler)
Thread(target=httpd.serve_forever, daemon=True).start()
base = f"http://127.0.0.1:{httpd.server_address[1]}"
report = []
pages = [
("index", "index.html", None, None),
("login-admin", "login-admin.html", None, None),
("login-company", "login-company.html", None, None),
("admin-dashboard", "admin.html", "dashboard", None),
("admin-flows", "admin.html", "flows", None),
("admin-settings", "admin.html", "settings", None),
("admin-audit", "admin.html", "audit", None),
("admin-reminders", "admin.html", "reminders", None),
("admin-period-audit", "admin.html", "period-audit", None),
("admin-companies", "admin.html", "companies", None),
("admin-pair", "admin.html", "pair", None),
("company-workspace", "company.html", "workspace", None),
("company-transfers", "company.html", "transfers", None),
("company-reconcile", "company.html", "reconcile", None),
("company-upload", "company.html", "upload", None),
]
with sync_playwright() as p:
browser = p.chromium.launch(headless=True, args=["--no-sandbox"])
for theme in ("day", "night"):
for width, height, tag in ((1440, 900, "1440"), (820, 900, "820"), (390, 844, "390")):
if tag != "1440" and theme == "night" and width != 390:
continue
page = browser.new_page(viewport={"width": width, "height": height})
page.add_init_script(
f"() => {{ try {{ localStorage.setItem('jinniu-theme', '{theme}'); }} catch (e) {{}} }}"
)
for name, html, view, _ in pages:
if tag != "1440" and name not in ("login-admin", "admin-flows", "company-transfers", "admin-dashboard"):
continue
page.route("**/app.js**", lambda route: route.abort())
page.goto(f"{base}/{html}", wait_until="domcontentloaded")
page.evaluate(
"""(args) => {
document.documentElement.classList.add('v-fusion');
document.documentElement.setAttribute('data-theme', args.theme);
if (args.view) {
document.querySelectorAll('.app-view').forEach((el) => {
el.classList.toggle('is-active', el.dataset.page === args.view);
});
}
}""",
{"theme": theme, "view": view},
)
page.wait_for_timeout(80)
shot = OUT / f"{name}-{theme}-{tag}.png"
page.screenshot(path=str(shot), full_page=False)
if tag == "1440" and name in ("login-admin", "admin-dashboard", "admin-flows"):
probe = page.evaluate(
"""(sels) => sels.map(([sel, props]) => {
const el = document.querySelector(sel);
if (!el) return { sel, missing: true };
const cs = getComputedStyle(el);
const out = { sel };
props.forEach((p) => { out[p] = cs[p]; });
const box = el.getBoundingClientRect();
out.box = { w: Math.round(box.width), h: Math.round(box.height) };
return out;
})""",
PROBES,
)
overflow = page.evaluate(
"() => document.documentElement.scrollWidth > document.documentElement.clientWidth + 1"
)
report.append({"page": name, "theme": theme, "overflow": overflow, "probe": probe})
page.close()
# 390 drawer
page = browser.new_page(viewport={"width": 390, "height": 844})
page.add_init_script("() => { try { localStorage.setItem('jinniu-theme', 'day'); } catch (e) {} }")
page.route("**/app.js**", lambda route: route.abort())
page.goto(f"{base}/company.html", wait_until="domcontentloaded")
page.evaluate(
"""() => {
document.documentElement.classList.add('v-fusion');
document.querySelectorAll('.app-view').forEach((el) => {
el.classList.toggle('is-active', el.dataset.page === 'transfers');
});
const d = document.getElementById('transferEvidenceDrawer');
if (d) d.classList.add('is-open');
}"""
)
page.screenshot(path=str(OUT / "company-transfers-drawer-day-390.png"), full_page=False)
page.close()
browser.close()
httpd.shutdown()
(OUT / "probe.json").write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
print(f"wrote {OUT} ({len(list(OUT.glob('*.png')))} png)")
if __name__ == "__main__":
main()