mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-09-20 17:07:59 +00:00
feat(ui): align skeleton height to real KPI cards + add page/tab/card entry animations
- KPI skeleton (.ksk) min-height now matches the real card (186px) with a title/value/chart bar layout so first paint doesn't jump - global entry animations: cards fade+slide-up (fadeUp), tab panes fade+rise (tabIn), chat empty state fades in - goTab restarts the tabIn animation on switch (reset animation + forced reflow) - table-row animation intentionally NOT added (those re-render every 3s poll and would flicker) - deployed (bak .bak.20260811v), server active
This commit is contained in:
@ -102,10 +102,17 @@ pre.configbox { background:var(--card2); border:1px solid var(--line); border-ra
|
||||
|
||||
/* ---------- 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; }
|
||||
.ksk { background:var(--card); border:1px solid var(--line); border-radius:14px; padding:14px 16px; min-height:186px; display:flex; flex-direction:column; gap:10px; }
|
||||
.ksk i { display:block; height:11px; 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:38%; align-self:flex-start; } .ksk i:nth-child(2){ width:68%; height:24px; } .ksk i:nth-child(3){ width:100%; height:86px; margin-top:4px; }
|
||||
@keyframes shimmer { 0%{background-position:100% 0} 100%{background-position:0 0} }
|
||||
/* entry / transition animations */
|
||||
@keyframes fadeUp { from{opacity:0;transform:translateY(8px)} to{opacity:1;transform:none} }
|
||||
@keyframes fadeIn { from{opacity:0} to{opacity:1} }
|
||||
@keyframes tabIn { from{opacity:0;transform:translateY(6px) scale(.995)} to{opacity:1;transform:none} }
|
||||
.card { animation:fadeUp .28s ease both; }
|
||||
.tab-pane { animation:tabIn .25s ease; }
|
||||
.chat-log .chat-empty { animation:fadeIn .3s ease; }
|
||||
.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; }
|
||||
@ -456,12 +463,12 @@ html[data-theme="dark"] .dropzone.dragover, html[data-theme="dark"] .dropzone:ho
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<div id="tab-status"></div>
|
||||
<div id="tab-chat" class="hidden"></div>
|
||||
<div id="tab-keys" class="hidden"></div>
|
||||
<div id="tab-sort" class="hidden"></div>
|
||||
<div id="tab-sources" class="hidden"></div>
|
||||
<div id="tab-adapters" class="hidden"></div>
|
||||
<div id="tab-status" class="tab-pane"></div>
|
||||
<div id="tab-chat" class="tab-pane hidden"></div>
|
||||
<div id="tab-keys" class="tab-pane hidden"></div>
|
||||
<div id="tab-sort" class="tab-pane hidden"></div>
|
||||
<div id="tab-sources" class="tab-pane hidden"></div>
|
||||
<div id="tab-adapters" class="tab-pane hidden"></div>
|
||||
</main>
|
||||
<div id="toast"></div>
|
||||
<script>
|
||||
@ -616,9 +623,14 @@ document.querySelectorAll('nav button').forEach(b => {
|
||||
function goTab(name) {
|
||||
document.querySelectorAll('nav button').forEach(x => x.classList.toggle('active', x.dataset.tab === name));
|
||||
['status','chat','keys','sort','sources','adapters'].forEach(tn => $('#tab-' + tn).classList.toggle('hidden', tn !== name));
|
||||
lastTab = name; refresh(lastTab);
|
||||
const pane = $('#tab-' + name);
|
||||
if (pane) {
|
||||
pane.style.animation = 'none';
|
||||
void pane.offsetHeight; // reflow to restart the CSS transition
|
||||
pane.style.animation = '';
|
||||
}
|
||||
applyI18n();
|
||||
lastTab = name; refresh(lastTab);
|
||||
}applyI18n();
|
||||
|
||||
/* ---------- helpers ---------- */
|
||||
const $ = s => document.querySelector(s);
|
||||
|
||||
Reference in New Issue
Block a user