#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 方案二:800x480 的**现代网页**版仪表盘(server.py 的 HTML_PATH 每次请求现生成),供 SenseCraft HTML 控件用无头浏览器截图。 与 PNG 版(renderer_e1002)的差别: - 不限点阵字体:系统中文字体栈(PingFang / Microsoft YaHei / Noto Sans SC)+ Google Fonts 的 Noto Sans SC 兜底 (平台截图机若无中文字体会掉字,网络字体是保险;能否加载取决于其出网能力)。 - 纯 HTML+CSS+内联 SVG,**不依赖 JavaScript**(平台是否执行 JS 未证实),数据全部服务端写死进 HTML。 - 颜色只用六色纯值 #000/#fff/#f00/#0f0(黄/蓝未用),抗锯齿边缘交给平台抖动——这正是要和 PNG 版对比的点。 - 版式与 PNG 版同构:顶部时钟/天气;左 Claude Usage、右 ChatGPT Usage;橘喵今日实时三列;BTC 时 K(SVG)。 - 配色规则与 PNG 版一致:用量条 ≥warn 红 / ≤ok 绿 / 其间黑;pace ≥+tol 红 / ≤−tol 绿 / 其间黑;环比同比 涨红 跌黑;K 线 涨绿 跌红。 """ from html import escape as _e RED, GREEN, BLACK = "#f00", "#0f0", "#000" _CSS = """ *{box-sizing:border-box;margin:0;padding:0} html,body{width:800px;height:480px;overflow:hidden;background:#fff;color:#000; font-family:"PingFang SC","Microsoft YaHei","Noto Sans SC","Helvetica Neue",Arial,sans-serif; -webkit-font-smoothing:antialiased} .page{width:800px;height:480px;padding:12px 16px 10px;display:flex;flex-direction:column;gap:6px} .hd{display:flex;justify-content:space-between;align-items:flex-end;height:92px;border-bottom:2px solid #000;padding-bottom:6px} .clock{font-size:72px;font-weight:800;line-height:1;letter-spacing:-2px;font-variant-numeric:tabular-nums} .date{font-size:15px;margin-top:4px;color:#000} .wx{text-align:right;display:flex;align-items:flex-end;gap:14px} .wx .t{font-size:44px;font-weight:800;line-height:1} .wx .c{font-size:15px;line-height:1.45} .wx .loc{font-size:13px;color:#000} .pin{display:inline-block;width:9px;height:9px;border-radius:50% 50% 50% 0;background:#f00;transform:rotate(-45deg);margin-right:4px;vertical-align:-1px} .row{display:flex;gap:16px} .card{flex:1;min-width:0} .card h3{font-size:14px;font-weight:700;display:flex;justify-content:space-between;align-items:baseline;margin-bottom:6px} .card h3 em{font-style:normal;font-weight:400;font-size:12px} .card h3 b::before{content:"";display:inline-block;width:9px;height:9px;background:#000;margin-right:6px;vertical-align:-1px} .u{display:grid;grid-template-columns:64px 1fr 58px 44px;align-items:center;column-gap:8px;height:26px} .u .k{font-size:13px;font-weight:700;font-family:"SF Mono",Menlo,Consolas,monospace} .bar{height:11px;border:1.5px solid #000;position:relative;background:#fff} .bar i{position:absolute;left:0;top:0;bottom:0;background:#000} .u .p{font-size:19px;font-weight:800;text-align:right;font-variant-numeric:tabular-nums;line-height:1} .u .pace{font-size:13px;font-weight:700;font-variant-numeric:tabular-nums;white-space:nowrap} .tri{display:inline-block;width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;vertical-align:1px;margin-right:2px} .tri.up{border-bottom:8px solid currentColor}.tri.dn{border-top:8px solid currentColor} .rt{border-top:2px solid #000;border-bottom:2px solid #000;padding:6px 0 4px} .rt .cols{display:flex} .rt .col{flex:1;text-align:center;border-left:1px dashed #000;padding:2px 0} .rt .col:first-child{border-left:0} .rt .name{font-size:13px;margin-bottom:2px} .rt .grp{display:inline-flex;align-items:center;gap:12px} .rt .v{font-size:36px;font-weight:800;line-height:1;font-variant-numeric:tabular-nums} .rt .cmp{text-align:left;font-size:12px;line-height:1.5;font-variant-numeric:tabular-nums} .btc h3{margin-bottom:2px} .btc svg{display:block} """ def _tier_bar(bar, usage): """用量条颜色三档(与 renderer_e1002 一致)。返回 (填充/边框色, 百分比文字色)。""" pct = bar.get("pct") if not isinstance(pct, (int, float)): return BLACK, BLACK if pct >= usage.get("warn", 80): return RED, RED if pct <= usage.get("ok", 30): return GREEN, BLACK return BLACK, BLACK def _tier_pace(diff, usage): tol = usage.get("pace_tol", 10) return RED if diff >= tol else GREEN if diff <= -tol else BLACK def _usage_card(usage): note = "示例数据" if usage.get("mock") else (f'快照 {usage["updated"]}' if usage.get("updated") else "") rows = [] for b in usage.get("bars", []): pct = b.get("pct") ok = isinstance(pct, (int, float)) fill, ptxt = _tier_bar(b, usage) width = max(0, min(100, int(pct))) if ok else 0 pace = "" if ok and "time_pct" in b: diff = int(pct) - int(b["time_pct"]) col = _tier_pace(diff, usage) tri = '' if diff > 0 else ('' if diff < 0 else "") pace = f'{tri}{abs(diff):02d}' else: pace = '' rows.append( f'