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 @@
-
+
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();