diff --git a/README.md b/README.md index 3889e15..fbdfc78 100644 --- a/README.md +++ b/README.md @@ -262,6 +262,8 @@ UptimeRobot 的探测来自其多个节点,适合作为**异地视角**与自 指定方式(优先级从高到低):URL 参数 `?view=table|cards` → 浏览器上次选择(localStorage)→ `settings.view` → 表格。 +**移动端**:窄屏(≤700px)在用户没有显式选择过视图时**自动使用卡片**(表格在手机上必须横向拖动,体验差),并在横竖屏切换时重算;顶栏在 ≤820px 改为「标题 / 统计 / 控件」三行,立绘与固定装饰自动撤掉。 + ### 数据源过滤 | 值 | 含义 | @@ -439,6 +441,13 @@ UptimeRobot 的探测来自其多个节点,适合作为**异地视角**与自 - [x] 页脚改为展示「数据更新于 … + 下次检查 Ns」,tooltip 内含服务端更新节奏与快照生成时间 - [x] `scripts/verify.cjs` 适配纯外部数据源部署(自动跳过不适用的探测类断言),并新增「倒计时对齐服务端」断言 +### v0.8.1 已实现 + +- [x] **修复移动端顶栏塌陷**:窄屏(≤820px)改为「标题 / 统计 / 控件」三行布局,统计均分视口宽度,页脚改纵向堆叠 —— 此前统计数字会挤成竖排并压住标题 +- [x] **窄屏默认卡片视图**(≤700px):表格在手机上必须横向拖动,改由卡片呈现;横竖屏切换时重算,用户显式选过视图则尊重其选择 +- [x] 窄屏与横屏手机自动撤掉立绘、公路车、光环水印等固定装饰,避免压住内容或溢出 +- [x] `verify-views` 新增移动端专项断言 7 项(默认卡片 / 无横向溢出 / 顶栏三行 / 统计铺满 / 装饰隐藏 / 页脚不裁切 / 无 JS 错误) + ### v0.8.0 已实现 - [x] **形象换成简约风插画**(AI 生成 + 抠底,非官方美术素材):右下角全身立绘、logo 与 favicon 用同款半身像;官方立绘/头像已从仓库移除 diff --git a/docs/screenshot-desktop.png b/docs/screenshot-desktop.png index 4c92132..1fc34cb 100644 Binary files a/docs/screenshot-desktop.png and b/docs/screenshot-desktop.png differ diff --git a/docs/screenshot-mobile.png b/docs/screenshot-mobile.png index 47ee08e..7be2a74 100644 Binary files a/docs/screenshot-mobile.png and b/docs/screenshot-mobile.png differ diff --git a/docs/screenshot-shiroko.png b/docs/screenshot-shiroko.png index 25d0016..13601f6 100644 Binary files a/docs/screenshot-shiroko.png and b/docs/screenshot-shiroko.png differ diff --git a/docs/screenshot-view-cards.png b/docs/screenshot-view-cards.png index 9e21e61..5e6e774 100644 Binary files a/docs/screenshot-view-cards.png and b/docs/screenshot-view-cards.png differ diff --git a/docs/screenshot-view-table.png b/docs/screenshot-view-table.png index 1bfeb3f..8e8b02e 100644 Binary files a/docs/screenshot-view-table.png and b/docs/screenshot-view-table.png differ diff --git a/edge/cloudflare/worker.js b/edge/cloudflare/worker.js index 5e21f9c..3c8cd7a 100644 --- a/edge/cloudflare/worker.js +++ b/edge/cloudflare/worker.js @@ -11,7 +11,7 @@ import { connect } from 'cloudflare:sockets'; -const VERSION = '0.8.0'; +const VERSION = '0.8.1'; const HISTORY_LIMIT = 48; const UPTIME_DAYS = 90; const USER_AGENT = 'shiroko-monitor/0.3 (lightweight uptime panel)'; diff --git a/prober/node/index.js b/prober/node/index.js index 841e8e6..badf45b 100644 --- a/prober/node/index.js +++ b/prober/node/index.js @@ -35,7 +35,7 @@ const CONCURRENCY = 8; const WRITE_DEBOUNCE_MS = 300; const DEFAULT_INTERVAL_SEC = 60; const PORT = Number(process.env.PORT || 8787); -const VERSION = '0.8.0'; +const VERSION = '0.8.1'; const UR_DEFAULT_POLL_SEC = 300; const UR_MIN_REFRESH_GAP_MS = 60000; diff --git a/public/assets/app.js b/public/assets/app.js index a98cf71..80160af 100644 --- a/public/assets/app.js +++ b/public/assets/app.js @@ -1,4 +1,4 @@ -const VERSION = '0.8.0'; +const VERSION = '0.8.1'; const HB_LIMIT = 48; const UPTIME_DAYS = 90; const BROWSER_CONCURRENCY = 6; @@ -507,14 +507,23 @@ function renderViewSwitch() { } } +// 窄屏(手机)判定:表格在最窄的屏幕上必须横向拖动,不如默认给卡片视图 +function isNarrowScreen() { + return window.matchMedia('(max-width: 700px)').matches; +} + function resolveView() { const q = new URLSearchParams(location.search).get('view'); + if (q && VIEWS[q]) return q; let saved = null; try { saved = localStorage.getItem('shiroko.view'); } catch { /* 隐私模式下 localStorage 不可用 */ } const cfg = state.config?.settings?.view; - return [q, saved, cfg, DEFAULT_VIEW].find((v) => v && VIEWS[v]) ?? DEFAULT_VIEW; + const resolved = [saved, cfg, DEFAULT_VIEW].find((v) => v && VIEWS[v]) ?? DEFAULT_VIEW; + // 用户没在这个设备上显式选过视图时,窄屏自动落到卡片(表格在手机上要横向拖) + if (!saved && resolved === 'table' && isNarrowScreen()) return 'cards'; + return resolved; } function setView(view, { persist = true } = {}) { @@ -802,6 +811,11 @@ function bindEvents() { // 只有手动点「刷新」才会触发一次重新检测;自动刷新是只读的 $('refresh-btn').addEventListener('click', () => refresh({ force: true })); $('theme-btn')?.addEventListener('click', () => applyTheme(state.theme === 'shiroko' ? 'default' : 'shiroko')); + // 横竖屏切换 / 窗口缩放时重算视图(不覆盖用户显式选择,故 persist: false) + window.addEventListener('resize', () => { + const next = resolveView(); + if (next !== state.view) setView(next, { persist: false }); + }); } /* ---------- 启动 ---------- */ diff --git a/public/assets/style.css b/public/assets/style.css index 7336d74..cc559f9 100644 --- a/public/assets/style.css +++ b/public/assets/style.css @@ -91,6 +91,8 @@ body { .brand-text p { margin: 0; font-size: 12px; color: var(--text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .summary { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 16px; } +.stats { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 16px; } +.controls { display: flex; align-items: center; gap: 8px; } .pill { padding: 4px 12px; @@ -323,9 +325,20 @@ body { .demo-chip { padding: 1px 8px; border-radius: 999px; background: var(--pending-bg); color: var(--pending); font-weight: 600; } .warn-chip { padding: 1px 8px; border-radius: 999px; background: var(--down-bg); color: var(--down); font-weight: 600; } +/* 窄屏(手机 / 竖屏平板):顶栏改为三行——标题 / 统计 / 控件,避免挤成一团 */ +@media (max-width: 820px) { + .topbar { flex-direction: column; align-items: stretch; gap: 10px; padding: 12px 14px 14px; } + .summary { flex-direction: column; align-items: stretch; gap: 8px; } + .stats { justify-content: space-between; gap: 4px; } + .stat { min-width: 0; flex: 1 1 0; } + .stat b { font-size: 15px; } + .stat label { font-size: 10px; } + .controls { justify-content: flex-end; gap: 10px; } + .footer { flex-direction: column; align-items: flex-start; gap: 4px; padding-bottom: 20px; } + .footer .version { margin-left: 0; } +} + @media (max-width: 640px) { - .topbar { padding: 10px 14px; } - .summary { gap: 6px 12px; } .grid { grid-template-columns: 1fr; padding: 12px 14px 8px; } .group-tabs { padding: 12px 14px 0; } .footer { padding: 8px 14px 24px; } diff --git a/public/assets/theme-shiroko.css b/public/assets/theme-shiroko.css index a8f3b70..8424de0 100644 --- a/public/assets/theme-shiroko.css +++ b/public/assets/theme-shiroko.css @@ -215,7 +215,14 @@ body[data-theme="shiroko"] .artwork { user-select: none; filter: drop-shadow(0 6px 18px rgb(23 60 100 / 15%)); } -@media (max-width: 1100px) { +/* 窄屏:立绘与固定装饰全部撤掉,避免压住内容或溢出(手机只保留配色与围巾飘带) */ +@media (max-width: 820px) { + body[data-theme="shiroko"] .artwork, + body[data-theme="shiroko"]::before, + body[data-theme="shiroko"]::after { display: none; } +} +/* 横屏手机:可用高度很小,立绘同样撤掉 */ +@media (max-height: 520px) { body[data-theme="shiroko"] .artwork { display: none; } } /* 卡片视图行数多、卡片更靠右下,立绘再收一档,尽量让它落在卡片之外的角落 */ diff --git a/public/index.html b/public/index.html index 7c6a68b..d63db0e 100644 --- a/public/index.html +++ b/public/index.html @@ -20,23 +20,27 @@
检测中… -
–
-
–
-
–
-
–
-
–
-
- - +
+
–
+
–
+
–
+
–
+
–
+
+
+
+ + +
@@ -52,7 +56,7 @@ – – - shiroko-monitor v0.8.0 + shiroko-monitor v0.8.1 diff --git a/public/status.json b/public/status.json index 5efc85f..dba4814 100644 --- a/public/status.json +++ b/public/status.json @@ -1,8 +1,8 @@ { "prober": "shiroko-monitor-node/0.7.0", "demo": false, - "generatedAt": "2026-10-13T11:56:34.148Z", - "dataUpdatedAt": "2026-10-13T11:55:48.247Z", + "generatedAt": "2026-10-13T12:38:37.868Z", + "dataUpdatedAt": "2026-10-13T12:38:32.456Z", "updateIntervalSec": 300, "dataFilter": "uptimerobot", "stats": { @@ -10,12 +10,12 @@ "up": 5, "down": 1, "pending": 0, - "avgLatency": 818, + "avgLatency": 932, "avgUptime90": 83.3, "memory": { - "rssMB": 66.8, - "heapUsedMB": 12.9, - "heapTotalMB": 14.2 + "rssMB": 67, + "heapUsedMB": 10, + "heapTotalMB": 17 } }, "sources": { @@ -23,200 +23,200 @@ "enabled": true, "ok": true, "error": null, - "lastSyncAt": "2026-10-13T11:51:45.159Z", + "lastSyncAt": "2026-10-13T12:36:49.255Z", "count": 6, "syncing": false, - "lastSyncMs": 1791892303862 + "lastSyncMs": 1791895007672 } }, "monitors": { "ur-792059439": { "status": "up", - "latency": 291, + "latency": 415, "code": null, "detail": "UptimeRobot:运行正常", - "lastCheck": "2026-10-13T11:47:00.000Z", + "lastCheck": "2026-10-13T12:32:00.000Z", "history": [ { - "t": "2026-10-12T13:00:00.000Z", + "t": "2026-10-12T13:45:00.000Z", "ok": true, - "ms": 277 + "ms": 724 }, { - "t": "2026-10-12T13:40:00.000Z", + "t": "2026-10-12T14:25:00.000Z", "ok": true, - "ms": 482 + "ms": 671 }, { - "t": "2026-10-12T14:20:00.000Z", + "t": "2026-10-12T15:05:00.000Z", "ok": true, - "ms": 698 + "ms": 388 }, { - "t": "2026-10-12T15:00:00.000Z", + "t": "2026-10-12T15:45:00.000Z", "ok": true, - "ms": 312 + "ms": 392 }, { - "t": "2026-10-12T15:40:00.000Z", + "t": "2026-10-12T16:25:00.000Z", "ok": true, - "ms": 336 + "ms": 288 }, { - "t": "2026-10-12T16:20:00.000Z", + "t": "2026-10-12T17:05:00.000Z", "ok": true, - "ms": 275 + "ms": 273 }, { - "t": "2026-10-12T17:00:00.000Z", + "t": "2026-10-12T17:45:00.000Z", "ok": true, - "ms": 255 + "ms": 391 }, { - "t": "2026-10-12T17:40:00.000Z", + "t": "2026-10-12T18:25:00.000Z", "ok": true, - "ms": 304 + "ms": 326 }, { - "t": "2026-10-12T18:20:00.000Z", + "t": "2026-10-12T19:05:00.000Z", "ok": true, - "ms": 323 + "ms": 333 }, { - "t": "2026-10-12T19:00:00.000Z", + "t": "2026-10-12T19:45:00.000Z", "ok": true, - "ms": 328 + "ms": 249 }, { - "t": "2026-10-12T19:40:00.000Z", + "t": "2026-10-12T20:25:00.000Z", "ok": true, - "ms": 304 + "ms": 262 }, { - "t": "2026-10-12T20:20:00.000Z", + "t": "2026-10-12T21:05:00.000Z", "ok": true, - "ms": 265 + "ms": 290 }, { - "t": "2026-10-12T21:00:00.000Z", + "t": "2026-10-12T21:45:00.000Z", + "ok": true, + "ms": 350 + }, + { + "t": "2026-10-12T22:25:00.000Z", + "ok": true, + "ms": 166 + }, + { + "t": "2026-10-12T23:10:00.000Z", + "ok": true, + "ms": 283 + }, + { + "t": "2026-10-12T23:50:00.000Z", + "ok": true, + "ms": 383 + }, + { + "t": "2026-10-13T00:30:00.000Z", + "ok": true, + "ms": 291 + }, + { + "t": "2026-10-13T01:10:00.000Z", + "ok": true, + "ms": 195 + }, + { + "t": "2026-10-13T01:50:00.000Z", + "ok": true, + "ms": 159 + }, + { + "t": "2026-10-13T02:30:00.000Z", + "ok": true, + "ms": 232 + }, + { + "t": "2026-10-13T03:10:00.000Z", + "ok": true, + "ms": 198 + }, + { + "t": "2026-10-13T03:50:00.000Z", + "ok": true, + "ms": 104 + }, + { + "t": "2026-10-13T04:35:00.000Z", + "ok": true, + "ms": 478 + }, + { + "t": "2026-10-13T05:15:00.000Z", + "ok": true, + "ms": 353 + }, + { + "t": "2026-10-13T05:55:00.000Z", "ok": true, "ms": 294 }, { - "t": "2026-10-12T21:40:00.000Z", + "t": "2026-10-13T06:35:00.000Z", "ok": true, - "ms": 379 + "ms": 238 }, { - "t": "2026-10-12T22:20:00.000Z", + "t": "2026-10-13T07:15:00.000Z", "ok": true, - "ms": 176 + "ms": 259 }, { - "t": "2026-10-12T23:00:00.000Z", + "t": "2026-10-13T07:55:00.000Z", "ok": true, - "ms": 325 + "ms": 227 }, { - "t": "2026-10-12T23:40:00.000Z", + "t": "2026-10-13T08:35:00.000Z", "ok": true, - "ms": 192 + "ms": 434 }, { - "t": "2026-10-13T00:20:00.000Z", + "t": "2026-10-13T09:15:00.000Z", "ok": true, - "ms": 342 + "ms": 333 }, { - "t": "2026-10-13T01:00:00.000Z", + "t": "2026-10-13T09:55:00.000Z", "ok": true, - "ms": 252 + "ms": 427 }, { - "t": "2026-10-13T01:40:00.000Z", + "t": "2026-10-13T10:35:00.000Z", "ok": true, - "ms": 164 + "ms": 233 }, { - "t": "2026-10-13T02:20:00.000Z", + "t": "2026-10-13T11:15:00.000Z", "ok": true, - "ms": 199 + "ms": 388 }, { - "t": "2026-10-13T03:00:00.000Z", + "t": "2026-10-13T11:52:00.000Z", "ok": true, - "ms": 216 + "ms": 326 }, { - "t": "2026-10-13T03:40:00.000Z", + "t": "2026-10-13T12:32:00.000Z", "ok": true, - "ms": 160 - }, - { - "t": "2026-10-13T04:20:00.000Z", - "ok": true, - "ms": 435 - }, - { - "t": "2026-10-13T05:00:00.000Z", - "ok": true, - "ms": 281 - }, - { - "t": "2026-10-13T05:40:00.000Z", - "ok": true, - "ms": 285 - }, - { - "t": "2026-10-13T06:20:00.000Z", - "ok": true, - "ms": 270 - }, - { - "t": "2026-10-13T07:00:00.000Z", - "ok": true, - "ms": 235 - }, - { - "t": "2026-10-13T07:40:00.000Z", - "ok": true, - "ms": 271 - }, - { - "t": "2026-10-13T08:20:00.000Z", - "ok": true, - "ms": 417 - }, - { - "t": "2026-10-13T09:00:00.000Z", - "ok": true, - "ms": 396 - }, - { - "t": "2026-10-13T09:40:00.000Z", - "ok": true, - "ms": 296 - }, - { - "t": "2026-10-13T10:20:00.000Z", - "ok": true, - "ms": 183 - }, - { - "t": "2026-10-13T11:11:00.000Z", - "ok": true, - "ms": 389 - }, - { - "t": "2026-10-13T11:47:00.000Z", - "ok": true, - "ms": 291 + "ms": 415 } ], "daily": { "2026-10-13": { - "total": 4, - "up": 4 + "total": 16, + "up": 16 } }, "meta": { @@ -243,191 +243,186 @@ }, "ur-795497022": { "status": "up", - "latency": 698, + "latency": 738, "code": null, "detail": "UptimeRobot:运行正常", - "lastCheck": "2026-10-13T11:47:00.000Z", + "lastCheck": "2026-10-13T12:35:00.000Z", "history": [ { - "t": "2026-10-12T12:35:00.000Z", + "t": "2026-10-12T13:45:00.000Z", "ok": true, - "ms": 741 + "ms": 1055 }, { - "t": "2026-10-12T13:15:00.000Z", + "t": "2026-10-12T14:25:00.000Z", "ok": true, - "ms": 759 + "ms": 794 }, { - "t": "2026-10-12T13:55:00.000Z", + "t": "2026-10-12T15:05:00.000Z", "ok": true, - "ms": 1017 + "ms": 722 }, { - "t": "2026-10-12T14:35:00.000Z", + "t": "2026-10-12T15:45:00.000Z", "ok": true, - "ms": 814 + "ms": 798 }, { - "t": "2026-10-12T15:15:00.000Z", + "t": "2026-10-12T16:25:00.000Z", "ok": true, - "ms": 710 + "ms": 705 }, { - "t": "2026-10-12T15:55:00.000Z", + "t": "2026-10-12T17:05:00.000Z", "ok": true, - "ms": 792 + "ms": 734 }, { - "t": "2026-10-12T16:35:00.000Z", + "t": "2026-10-12T17:45:00.000Z", "ok": true, - "ms": 700 + "ms": 686 }, { - "t": "2026-10-12T17:15:00.000Z", + "t": "2026-10-12T18:25:00.000Z", "ok": true, - "ms": 689 + "ms": 686 }, { - "t": "2026-10-12T17:55:00.000Z", + "t": "2026-10-12T19:05:00.000Z", "ok": true, - "ms": 725 + "ms": 730 }, { - "t": "2026-10-12T18:35:00.000Z", + "t": "2026-10-12T19:45:00.000Z", "ok": true, - "ms": 744 + "ms": 704 }, { - "t": "2026-10-12T19:15:00.000Z", + "t": "2026-10-12T20:25:00.000Z", "ok": true, - "ms": 723 + "ms": 782 }, { - "t": "2026-10-12T19:55:00.000Z", + "t": "2026-10-12T21:05:00.000Z", "ok": true, - "ms": 698 + "ms": 634 }, { - "t": "2026-10-12T20:35:00.000Z", + "t": "2026-10-12T21:45:00.000Z", "ok": true, - "ms": 768 + "ms": 612 }, { - "t": "2026-10-12T21:15:00.000Z", + "t": "2026-10-12T22:25:00.000Z", "ok": true, - "ms": 616 + "ms": 738 }, { - "t": "2026-10-12T21:55:00.000Z", + "t": "2026-10-12T23:05:00.000Z", "ok": true, - "ms": 626 + "ms": 563 }, { - "t": "2026-10-12T22:35:00.000Z", - "ok": true, - "ms": 674 - }, - { - "t": "2026-10-12T23:15:00.000Z", - "ok": true, - "ms": 624 - }, - { - "t": "2026-10-12T23:55:00.000Z", - "ok": true, - "ms": 640 - }, - { - "t": "2026-10-13T00:35:00.000Z", - "ok": true, - "ms": 673 - }, - { - "t": "2026-10-13T01:15:00.000Z", - "ok": true, - "ms": 609 - }, - { - "t": "2026-10-13T01:55:00.000Z", - "ok": true, - "ms": 759 - }, - { - "t": "2026-10-13T02:35:00.000Z", - "ok": true, - "ms": 581 - }, - { - "t": "2026-10-13T03:15:00.000Z", - "ok": true, - "ms": 666 - }, - { - "t": "2026-10-13T03:55:00.000Z", - "ok": true, - "ms": 662 - }, - { - "t": "2026-10-13T04:35:00.000Z", + "t": "2026-10-12T23:45:00.000Z", "ok": true, "ms": 685 }, { - "t": "2026-10-13T05:15:00.000Z", + "t": "2026-10-13T00:25:00.000Z", "ok": true, - "ms": 647 + "ms": 792 }, { - "t": "2026-10-13T05:55:00.000Z", + "t": "2026-10-13T01:05:00.000Z", "ok": true, - "ms": 588 + "ms": 658 }, { - "t": "2026-10-13T06:40:00.000Z", + "t": "2026-10-13T01:45:00.000Z", "ok": true, - "ms": 625 + "ms": 723 }, { - "t": "2026-10-13T07:20:00.000Z", + "t": "2026-10-13T02:25:00.000Z", "ok": true, - "ms": 673 + "ms": 635 }, { - "t": "2026-10-13T08:00:00.000Z", + "t": "2026-10-13T03:05:00.000Z", "ok": true, - "ms": 763 + "ms": 628 }, { - "t": "2026-10-13T08:40:00.000Z", + "t": "2026-10-13T03:45:00.000Z", "ok": true, - "ms": 826 + "ms": 703 }, { - "t": "2026-10-13T09:20:00.000Z", + "t": "2026-10-13T04:25:00.000Z", "ok": true, - "ms": 749 + "ms": 707 }, { - "t": "2026-10-13T10:33:00.000Z", + "t": "2026-10-13T05:05:00.000Z", "ok": true, - "ms": 609 + "ms": 630 }, { - "t": "2026-10-13T11:10:00.000Z", + "t": "2026-10-13T05:45:00.000Z", "ok": true, - "ms": 770 + "ms": 738 }, { - "t": "2026-10-13T11:47:00.000Z", + "t": "2026-10-13T06:25:00.000Z", "ok": true, - "ms": 698 + "ms": 664 + }, + { + "t": "2026-10-13T07:05:00.000Z", + "ok": true, + "ms": 656 + }, + { + "t": "2026-10-13T07:45:00.000Z", + "ok": true, + "ms": 739 + }, + { + "t": "2026-10-13T08:25:00.000Z", + "ok": true, + "ms": 653 + }, + { + "t": "2026-10-13T09:05:00.000Z", + "ok": true, + "ms": 798 + }, + { + "t": "2026-10-13T09:45:00.000Z", + "ok": true, + "ms": 687 + }, + { + "t": "2026-10-13T10:25:00.000Z", + "ok": true, + "ms": 790 + }, + { + "t": "2026-10-13T11:58:00.000Z", + "ok": true, + "ms": 701 + }, + { + "t": "2026-10-13T12:35:00.000Z", + "ok": true, + "ms": 738 } ], "daily": { "2026-10-13": { - "total": 4, - "up": 4 + "total": 16, + "up": 16 } }, "meta": { @@ -454,191 +449,191 @@ }, "ur-795556828": { "status": "up", - "latency": 380, + "latency": 341, "code": null, "detail": "UptimeRobot:运行正常", - "lastCheck": "2026-10-13T11:47:00.000Z", + "lastCheck": "2026-10-13T12:32:00.000Z", "history": [ { - "t": "2026-10-12T12:50:00.000Z", + "t": "2026-10-12T13:35:00.000Z", "ok": true, - "ms": 401 + "ms": 590 }, { - "t": "2026-10-12T13:30:00.000Z", + "t": "2026-10-12T14:15:00.000Z", "ok": true, - "ms": 690 + "ms": 322 }, { - "t": "2026-10-12T14:10:00.000Z", + "t": "2026-10-12T14:55:00.000Z", "ok": true, - "ms": 356 + "ms": 225 }, { - "t": "2026-10-12T14:50:00.000Z", + "t": "2026-10-12T15:35:00.000Z", "ok": true, - "ms": 204 + "ms": 275 }, { - "t": "2026-10-12T15:30:00.000Z", + "t": "2026-10-12T16:15:00.000Z", "ok": true, - "ms": 314 + "ms": 450 }, { - "t": "2026-10-12T16:10:00.000Z", + "t": "2026-10-12T16:55:00.000Z", "ok": true, - "ms": 498 + "ms": 281 }, { - "t": "2026-10-12T16:50:00.000Z", + "t": "2026-10-12T17:40:00.000Z", "ok": true, - "ms": 203 + "ms": 320 }, { - "t": "2026-10-12T17:35:00.000Z", + "t": "2026-10-12T18:20:00.000Z", "ok": true, - "ms": 334 + "ms": 287 }, { - "t": "2026-10-12T18:15:00.000Z", + "t": "2026-10-12T19:00:00.000Z", "ok": true, - "ms": 282 + "ms": 283 }, { - "t": "2026-10-12T18:55:00.000Z", + "t": "2026-10-12T19:40:00.000Z", "ok": true, - "ms": 309 + "ms": 464 }, { - "t": "2026-10-12T19:35:00.000Z", + "t": "2026-10-12T20:20:00.000Z", "ok": true, - "ms": 403 + "ms": 187 }, { - "t": "2026-10-12T20:15:00.000Z", + "t": "2026-10-12T21:00:00.000Z", "ok": true, - "ms": 315 + "ms": 233 }, { - "t": "2026-10-12T20:55:00.000Z", + "t": "2026-10-12T21:40:00.000Z", "ok": true, - "ms": 243 + "ms": 213 }, { - "t": "2026-10-12T21:35:00.000Z", + "t": "2026-10-12T22:20:00.000Z", "ok": true, - "ms": 206 + "ms": 235 }, { - "t": "2026-10-12T22:15:00.000Z", + "t": "2026-10-12T23:05:00.000Z", "ok": true, - "ms": 237 + "ms": 241 }, { - "t": "2026-10-12T23:00:00.000Z", + "t": "2026-10-12T23:45:00.000Z", "ok": true, - "ms": 237 + "ms": 230 }, { - "t": "2026-10-12T23:40:00.000Z", + "t": "2026-10-13T00:25:00.000Z", "ok": true, - "ms": 231 + "ms": 244 }, { - "t": "2026-10-13T00:20:00.000Z", - "ok": true, - "ms": 249 - }, - { - "t": "2026-10-13T01:00:00.000Z", + "t": "2026-10-13T01:05:00.000Z", "ok": true, "ms": 133 }, { - "t": "2026-10-13T01:40:00.000Z", + "t": "2026-10-13T01:45:00.000Z", "ok": true, - "ms": 143 + "ms": 127 }, { - "t": "2026-10-13T02:20:00.000Z", + "t": "2026-10-13T02:25:00.000Z", "ok": true, - "ms": 179 + "ms": 182 }, { - "t": "2026-10-13T03:00:00.000Z", + "t": "2026-10-13T03:05:00.000Z", "ok": true, - "ms": 409 + "ms": 413 }, { - "t": "2026-10-13T03:40:00.000Z", + "t": "2026-10-13T03:45:00.000Z", "ok": true, - "ms": 231 + "ms": 229 }, { - "t": "2026-10-13T04:20:00.000Z", + "t": "2026-10-13T04:30:00.000Z", "ok": true, - "ms": 250 + "ms": 234 }, { - "t": "2026-10-13T05:00:00.000Z", + "t": "2026-10-13T05:10:00.000Z", "ok": true, - "ms": 134 + "ms": 181 }, { - "t": "2026-10-13T05:40:00.000Z", + "t": "2026-10-13T05:50:00.000Z", "ok": true, - "ms": 193 + "ms": 206 }, { - "t": "2026-10-13T06:20:00.000Z", + "t": "2026-10-13T06:30:00.000Z", "ok": true, - "ms": 260 + "ms": 241 }, { - "t": "2026-10-13T07:00:00.000Z", + "t": "2026-10-13T07:10:00.000Z", "ok": true, - "ms": 254 + "ms": 241 }, { - "t": "2026-10-13T07:40:00.000Z", + "t": "2026-10-13T07:50:00.000Z", "ok": true, - "ms": 559 + "ms": 512 }, { - "t": "2026-10-13T08:20:00.000Z", + "t": "2026-10-13T08:30:00.000Z", "ok": true, - "ms": 205 + "ms": 216 }, { - "t": "2026-10-13T09:00:00.000Z", + "t": "2026-10-13T09:10:00.000Z", "ok": true, - "ms": 264 + "ms": 256 }, { - "t": "2026-10-13T09:40:00.000Z", + "t": "2026-10-13T09:55:00.000Z", "ok": true, - "ms": 305 + "ms": 455 }, { - "t": "2026-10-13T10:20:00.000Z", + "t": "2026-10-13T10:35:00.000Z", "ok": true, - "ms": 420 + "ms": 372 }, { - "t": "2026-10-13T11:11:00.000Z", + "t": "2026-10-13T11:15:00.000Z", "ok": true, - "ms": 198 + "ms": 240 }, { - "t": "2026-10-13T11:47:00.000Z", + "t": "2026-10-13T11:52:00.000Z", "ok": true, - "ms": 380 + "ms": 270 + }, + { + "t": "2026-10-13T12:32:00.000Z", + "ok": true, + "ms": 341 } ], "daily": { "2026-10-13": { - "total": 4, - "up": 4 + "total": 16, + "up": 16 } }, "meta": { @@ -665,16 +660,11 @@ }, "ur-792059414": { "status": "up", - "latency": 1562, + "latency": 2557, "code": null, "detail": "UptimeRobot:运行正常", - "lastCheck": "2026-10-13T11:45:00.000Z", + "lastCheck": "2026-10-13T12:36:00.000Z", "history": [ - { - "t": "2026-10-12T12:45:00.000Z", - "ok": true, - "ms": 2922 - }, { "t": "2026-10-12T13:25:00.000Z", "ok": true, @@ -806,40 +796,45 @@ "ms": 1715 }, { - "t": "2026-10-13T08:20:00.000Z", + "t": "2026-10-13T08:25:00.000Z", "ok": true, - "ms": 1620 + "ms": 1861 }, { - "t": "2026-10-13T09:00:00.000Z", + "t": "2026-10-13T09:10:00.000Z", "ok": true, - "ms": 1898 + "ms": 1991 }, { - "t": "2026-10-13T09:40:00.000Z", + "t": "2026-10-13T09:55:00.000Z", "ok": true, - "ms": 1576 + "ms": 1597 }, { - "t": "2026-10-13T10:20:00.000Z", + "t": "2026-10-13T10:40:00.000Z", "ok": true, - "ms": 1742 + "ms": 2816 }, { - "t": "2026-10-13T11:06:00.000Z", + "t": "2026-10-13T11:20:00.000Z", "ok": true, - "ms": 2132 + "ms": 1960 }, { - "t": "2026-10-13T11:45:00.000Z", + "t": "2026-10-13T11:57:00.000Z", "ok": true, - "ms": 1562 + "ms": 2341 + }, + { + "t": "2026-10-13T12:36:00.000Z", + "ok": true, + "ms": 2557 } ], "daily": { "2026-10-13": { - "total": 4, - "up": 4 + "total": 16, + "up": 16 } }, "meta": { @@ -866,191 +861,191 @@ }, "ur-798788113": { "status": "up", - "latency": 1161, + "latency": 611, "code": null, "detail": "UptimeRobot:运行正常", - "lastCheck": "2026-10-13T11:48:00.000Z", + "lastCheck": "2026-10-13T12:33:00.000Z", "history": [ { - "t": "2026-10-12T12:55:00.000Z", + "t": "2026-10-12T13:45:00.000Z", "ok": true, - "ms": 738 + "ms": 361 }, { - "t": "2026-10-12T13:35:00.000Z", - "ok": true, - "ms": 412 - }, - { - "t": "2026-10-12T14:15:00.000Z", - "ok": true, - "ms": 401 - }, - { - "t": "2026-10-12T14:55:00.000Z", - "ok": true, - "ms": 321 - }, - { - "t": "2026-10-12T15:35:00.000Z", - "ok": true, - "ms": 517 - }, - { - "t": "2026-10-12T16:15:00.000Z", - "ok": true, - "ms": 324 - }, - { - "t": "2026-10-12T16:55:00.000Z", - "ok": true, - "ms": 452 - }, - { - "t": "2026-10-12T17:35:00.000Z", + "t": "2026-10-12T14:25:00.000Z", "ok": true, "ms": 346 }, { - "t": "2026-10-12T18:15:00.000Z", + "t": "2026-10-12T15:05:00.000Z", "ok": true, - "ms": 386 + "ms": 439 }, { - "t": "2026-10-12T18:55:00.000Z", + "t": "2026-10-12T15:45:00.000Z", "ok": true, - "ms": 357 + "ms": 462 }, { - "t": "2026-10-12T19:35:00.000Z", + "t": "2026-10-12T16:25:00.000Z", + "ok": true, + "ms": 288 + }, + { + "t": "2026-10-12T17:05:00.000Z", + "ok": true, + "ms": 409 + }, + { + "t": "2026-10-12T17:50:00.000Z", + "ok": true, + "ms": 312 + }, + { + "t": "2026-10-12T18:30:00.000Z", + "ok": true, + "ms": 472 + }, + { + "t": "2026-10-12T19:10:00.000Z", + "ok": true, + "ms": 421 + }, + { + "t": "2026-10-12T19:50:00.000Z", + "ok": true, + "ms": 332 + }, + { + "t": "2026-10-12T20:30:00.000Z", + "ok": true, + "ms": 310 + }, + { + "t": "2026-10-12T21:10:00.000Z", + "ok": true, + "ms": 436 + }, + { + "t": "2026-10-12T21:50:00.000Z", + "ok": true, + "ms": 338 + }, + { + "t": "2026-10-12T22:30:00.000Z", "ok": true, "ms": 423 }, { - "t": "2026-10-12T20:15:00.000Z", + "t": "2026-10-12T23:10:00.000Z", "ok": true, - "ms": 303 + "ms": 247 }, { - "t": "2026-10-12T20:55:00.000Z", + "t": "2026-10-12T23:50:00.000Z", "ok": true, - "ms": 524 + "ms": 296 }, { - "t": "2026-10-12T21:35:00.000Z", - "ok": true, - "ms": 330 - }, - { - "t": "2026-10-12T22:15:00.000Z", - "ok": true, - "ms": 441 - }, - { - "t": "2026-10-12T23:00:00.000Z", - "ok": true, - "ms": 215 - }, - { - "t": "2026-10-12T23:40:00.000Z", + "t": "2026-10-13T00:30:00.000Z", "ok": true, "ms": 239 }, { - "t": "2026-10-13T00:20:00.000Z", + "t": "2026-10-13T01:10:00.000Z", "ok": true, - "ms": 273 + "ms": 393 }, { - "t": "2026-10-13T01:00:00.000Z", + "t": "2026-10-13T01:50:00.000Z", "ok": true, - "ms": 273 + "ms": 346 }, { - "t": "2026-10-13T01:40:00.000Z", + "t": "2026-10-13T02:30:00.000Z", "ok": true, - "ms": 347 + "ms": 441 }, { - "t": "2026-10-13T02:20:00.000Z", + "t": "2026-10-13T03:10:00.000Z", "ok": true, - "ms": 505 + "ms": 443 }, { - "t": "2026-10-13T03:00:00.000Z", + "t": "2026-10-13T03:50:00.000Z", "ok": true, - "ms": 412 + "ms": 382 }, { - "t": "2026-10-13T03:40:00.000Z", + "t": "2026-10-13T04:30:00.000Z", "ok": true, - "ms": 332 + "ms": 355 }, { - "t": "2026-10-13T04:20:00.000Z", + "t": "2026-10-13T05:10:00.000Z", "ok": true, - "ms": 396 + "ms": 414 }, { - "t": "2026-10-13T05:00:00.000Z", + "t": "2026-10-13T05:50:00.000Z", "ok": true, - "ms": 537 + "ms": 343 }, { - "t": "2026-10-13T05:40:00.000Z", + "t": "2026-10-13T06:30:00.000Z", "ok": true, - "ms": 332 + "ms": 434 }, { - "t": "2026-10-13T06:20:00.000Z", + "t": "2026-10-13T07:10:00.000Z", "ok": true, - "ms": 426 + "ms": 444 }, { - "t": "2026-10-13T07:00:00.000Z", + "t": "2026-10-13T07:50:00.000Z", "ok": true, - "ms": 344 + "ms": 281 }, { - "t": "2026-10-13T07:40:00.000Z", + "t": "2026-10-13T08:30:00.000Z", "ok": true, - "ms": 334 + "ms": 453 }, { - "t": "2026-10-13T08:20:00.000Z", + "t": "2026-10-13T09:15:00.000Z", "ok": true, - "ms": 341 + "ms": 333 }, { - "t": "2026-10-13T09:00:00.000Z", + "t": "2026-10-13T09:55:00.000Z", "ok": true, - "ms": 447 + "ms": 602 }, { - "t": "2026-10-13T09:40:00.000Z", + "t": "2026-10-13T10:35:00.000Z", "ok": true, - "ms": 581 + "ms": 519 }, { - "t": "2026-10-13T10:20:00.000Z", + "t": "2026-10-13T11:15:00.000Z", "ok": true, - "ms": 455 + "ms": 325 }, { - "t": "2026-10-13T11:12:00.000Z", + "t": "2026-10-13T11:53:00.000Z", "ok": true, - "ms": 341 + "ms": 1168 }, { - "t": "2026-10-13T11:48:00.000Z", + "t": "2026-10-13T12:33:00.000Z", "ok": true, - "ms": 1161 + "ms": 611 } ], "daily": { "2026-10-13": { - "total": 4, - "up": 4 + "total": 16, + "up": 16 } }, "meta": { @@ -1090,7 +1085,7 @@ ], "daily": { "2026-10-13": { - "total": 4, + "total": 16, "up": 0 } }, diff --git a/scripts/verify-views.cjs b/scripts/verify-views.cjs index 7f34c68..dea7145 100644 --- a/scripts/verify-views.cjs +++ b/scripts/verify-views.cjs @@ -138,6 +138,43 @@ async function main() { fs.writeFileSync(CONFIG, raw); // 还原 } + // ---------- H. 移动端(窄屏) ---------- + console.log('\n[H] 移动端窄屏'); + const mCtx = await browser.newContext({ viewport: { width: 390, height: 844 }, isMobile: true, deviceScaleFactor: 2 }); + const mp = await mCtx.newPage(); + const mErrors = []; + mp.on('pageerror', (e) => mErrors.push(e.message)); + // 不带 ?view=:窄屏下应自动落到卡片视图(表格在手机上必须横向拖动) + await mp.goto(`${BASE}/?source=api`, { waitUntil: 'domcontentloaded' }); + await mp.waitForSelector('.card', { timeout: 20000 }); + await mp.waitForTimeout(1500); + const mob = await mp.evaluate(() => { + const top = (sel) => { const el = document.querySelector(sel); return el ? Math.round(el.getBoundingClientRect().top) : -1; }; + const statsW = Math.round(document.querySelector('.stats')?.getBoundingClientRect().width ?? 0); + const lastFooter = [...document.querySelectorAll('.footer-item')].pop(); + return { + view: document.body.dataset.view, + overflow: document.documentElement.scrollWidth - document.documentElement.clientWidth, + brandTop: top('.brand'), statsTop: top('.stats'), controlsTop: top('.controls'), + statsW, vw: window.innerWidth, + artwork: getComputedStyle(document.querySelector('.artwork') ?? document.body).display, + beforeDisplay: getComputedStyle(document.body, '::before').display, + afterDisplay: getComputedStyle(document.body, '::after').display, + footerRight: lastFooter ? Math.round(lastFooter.getBoundingClientRect().right) : -1, + cards: document.querySelectorAll('.card').length, + tabsWrapped: getComputedStyle(document.querySelector('.topbar')).flexDirection, + }; + }); + check('窄屏默认使用卡片视图', mob.view === 'cards', `data-view=${mob.view}`); + check('窄屏无横向溢出', mob.overflow <= 0, `溢出 ${mob.overflow}px`); + check('顶栏在窄屏改为纵向三行(标题/统计/控件)', mob.brandTop < mob.statsTop && mob.statsTop < mob.controlsTop && mob.tabsWrapped === 'column', `top: ${mob.brandTop}/${mob.statsTop}/${mob.controlsTop}, flex=${mob.tabsWrapped}`); + check('统计行铺满视口宽度', Math.abs(mob.statsW - mob.vw) <= 32, `stats=${mob.statsW} 视口=${mob.vw}`); + check('窄屏撤掉立绘与固定装饰', mob.artwork === 'none' && mob.beforeDisplay === 'none' && mob.afterDisplay === 'none', `立绘=${mob.artwork} 左装饰=${mob.beforeDisplay} 光环水印=${mob.afterDisplay}`); + check('页脚内容不被裁切', mob.footerRight > 0 && mob.footerRight <= mob.vw, `最后一项右边界 ${mob.footerRight} / 视口 ${mob.vw}`); + check('窄屏渲染无 JS 错误', mErrors.length === 0, mErrors.join(' | ') || '无'); + await mp.screenshot({ path: path.join(__dirname, '..', 'docs', 'screenshot-mobile.png'), fullPage: true }); + await mCtx.close(); + // 429 是刷新限流保护在起作用(快速连续导航各触发一次强制探测),面板已优雅降级为只读数据; // 这里只把 429 以外的错误视为失败 const hardErrors = errors.filter((e) => !/429/.test(e)); diff --git a/scripts/verify.cjs b/scripts/verify.cjs index 1702c0c..238333d 100644 --- a/scripts/verify.cjs +++ b/scripts/verify.cjs @@ -107,12 +107,14 @@ async function main() { const pageG = await browser.newPage({ viewport: { width: 1280, height: 900 } }); await pageG.goto(`${BASE}/?source=api&view=table`, { waitUntil: 'domcontentloaded' }); await pageG.waitForSelector('table.monitor-table tbody tr[data-status]', { timeout: 20000 }); - await pageG.waitForTimeout(1200); + // 等首轮刷新结束(倒计时在刷新完成时才重置),否则读到的是加载中的旧值,偏差会被算进断言 + await pageG.waitForFunction(() => document.getElementById('refresh-btn').textContent === '刷新' && !document.getElementById('refresh-btn').disabled, null, { timeout: 45000 }); const shownText = await pageG.evaluate(() => document.getElementById('countdown').textContent); - const shownSec = Number((shownText.match(/(\d+)/) ?? [])[1]); - // 期望值需在当前时刻重算:面板显示的是「服务端上次更新 + 一个周期 - 现在」 const expectSec = Math.round((updatedMs + cadence * 1000 - Date.now()) / 1000); - check('倒计时对齐服务端下次更新时刻', Math.abs(shownSec - expectSec) <= 4, `面板 ${shownText}(服务端还有约 ${expectSec}s,节奏 ${cadence}s)`); + const shownSec = Number((shownText.match(/(\d+)/) ?? [])[1]); + check('倒计时对齐服务端下次更新时刻', shownSec >= expectSec - 2 && shownSec <= expectSec + 4, `面板 ${shownText}(服务端还有约 ${expectSec}s,节奏 ${cadence}s)`); + // 注:无头浏览器会冻结页面定时器(实测 performance.now() 会在采样间隔内跳跃十几秒), + // 因此不断言「N 秒内递减」,只断言与公式一致、且不退化为整周期重计——这两条不依赖定时器 check('倒计时不会退化为从整周期重新计时', shownSec <= Math.max(5, Math.min(cadence, Number(cfg.settings.refreshInterval) || cadence)), `显示 ${shownSec}s`); await pageG.close();