按定稿把运行时 token 换成霜曜/黑金双主题,登录与壳层接入官方 Logo 与主题记忆,不改业务接口与金额计算。 Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: multica-agent <github@multica.ai>
127 lines
6.0 KiB
Python
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()
|