From 12c61317fbca26d94e738e7eac8af25b0c44097d Mon Sep 17 00:00:00 2001 From: root Date: Tue, 11 Aug 2026 17:50:23 +0800 Subject: [PATCH] feat(ui): loading skeleton for the KPI row so first paint isn't a sudden blank->cards pop The five KPI cards are built only after /api/stats returns; before that #kpi-row was empty, so the top of the dashboard appeared blank then jumped in. Now the row starts with a shimmer skeleton (5 placeholder cards with a CSS gradient animation) that paintStats replaces with the real cards on first data. Deployed (bak .bak.20260811u), server active. --- internal/gateway/ui/index.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/internal/gateway/ui/index.html b/internal/gateway/ui/index.html index 16f8522..5036d4b 100644 --- a/internal/gateway/ui/index.html +++ b/internal/gateway/ui/index.html @@ -101,6 +101,11 @@ pre.configbox { background:var(--card2); border:1px solid var(--line); border-ra .modelchip { cursor:pointer; color:var(--accent); text-decoration:none; } /* ---------- dashboard / usage metrics ---------- */ +.kpi-skeletons { grid-column:1 / -1; display:grid; grid-template-columns:repeat(auto-fit,minmax(160px,1fr)); gap:12px; } +.ksk { background:var(--card); border:1px solid var(--line); border-radius:14px; padding:14px 16px; min-height:150px; display:flex; flex-direction:column; gap:12px; } +.ksk i { display:block; height:12px; border-radius:6px; background:linear-gradient(90deg,var(--card2) 25%,var(--line) 37%,var(--card2) 63%); background-size:400% 100%; animation:shimmer 1.2s infinite; } +.ksk i:nth-child(1){ width:40%; } .ksk i:nth-child(2){ width:70%; height:22px; } .ksk i:nth-child(3){ width:100%; height:64px; } +@keyframes shimmer { 0%{background-position:100% 0} 100%{background-position:0 0} } .kpis { display:grid; grid-template-columns:repeat(auto-fit,minmax(160px,1fr)); gap:12px; margin-bottom:18px; } .kpi { background:var(--card); border:1px solid var(--line); border-radius:14px; padding:14px 16px; box-shadow:var(--shadow); } .kpi .k-hdr { display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:6px; } @@ -683,7 +688,8 @@ async function renderStatus() { ${esc(x.base_url || '')} ${x.max_concurrent}`).join('') : ''; $('#tab-status').innerHTML = ` -
+

${t('connTitle')}

${t('connHint')}