Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 20 additions & 14 deletions src/coding/proxy/server/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,17 +431,18 @@ def _build_favicon() -> bytes:
.session-table tr.row-detail.open { display: table-row; }
.session-table tr.row-detail td { padding: 0; }
.detail-card {
padding: 14px 20px; margin: 4px 0;
background: rgba(18,22,30,.9); border: 1px solid var(--border);
border-radius: 10px; display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 10px 24px; font-size: 13px;
padding: 16px 24px; margin: 6px 0;
background: linear-gradient(135deg, rgba(30,37,54,.95), rgba(22,28,40,.95));
border: 1px solid rgba(88,166,255,.15); border-radius: 12px;
display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 14px 28px; font-size: 13px;
box-shadow: 0 4px 16px rgba(0,0,0,.3);
}
.detail-card .detail-item { display: flex; flex-direction: column; gap: 2px; }
.detail-card .detail-label { font-size: 11px; color: var(--text-tertiary); text-transform: uppercase; letter-spacing: .3px; }
.detail-card .detail-value { color: var(--text-primary); line-height: 1.4; word-break: break-all; }
.session-table tbody tr[data-row]:not(.row-detail) { cursor: pointer; }
.success-bar { width: 56px; height: 4px; border-radius: 2px; background: rgba(255,255,255,.06); display: inline-block; vertical-align: middle; margin-left: 6px; }
.success-bar { width: 56px; height: 4px; border-radius: 2px; background: rgba(255,255,255,.12); display: inline-block; vertical-align: middle; margin-left: 6px; }
.success-bar-fill { height: 100%; border-radius: 2px; }
/* ── Vendor Bind 选择器 ── */
.bind-select {
Expand Down Expand Up @@ -558,7 +559,7 @@ def _build_favicon() -> bytes:
<!-- 页签导航 -->
<nav class="tabs" role="tablist" aria-label="Dashboard sections">
<button type="button" class="tab-btn active" id="tab-btn-overview" role="tab" aria-controls="tab-pane-overview" aria-selected="true" data-tab="overview" onclick="switchTab('overview')">Overview</button>
<button type="button" class="tab-btn" id="tab-btn-sessions" role="tab" aria-controls="tab-pane-sessions" aria-selected="false" data-tab="sessions" onclick="switchTab('sessions')">Recent Active Sessions</button>
<button type="button" class="tab-btn" id="tab-btn-sessions" role="tab" aria-controls="tab-pane-sessions" aria-selected="false" data-tab="sessions" onclick="switchTab('sessions')">Sessions</button>
</nav>

<!-- Overview 页签 -->
Expand Down Expand Up @@ -654,12 +655,12 @@ def _build_favicon() -> bytes:
</div>
</section>

<!-- Recent Active Sessions 页签 -->
<!-- Sessions 页签 -->
<section class="tab-pane" id="tab-pane-sessions" role="tabpanel" aria-labelledby="tab-btn-sessions" data-tab="sessions">
<!-- Recent Active Sessions -->
<!-- Sessions -->
<div class="card sessions-card">
<div class="card-title">
<span>Recent Active Sessions</span>
<span>Sessions</span>
<span style="font-size:12px;color:var(--text-tertiary)" id="sessions-subtitle">Last 24h</span>
</div>
<div class="session-table-wrap" id="sessions-table-wrap">
Expand Down Expand Up @@ -745,7 +746,8 @@ def _build_favicon() -> bytes:
return String(n);
}
function fmtNum(n) { return n == null ? '–' : n.toLocaleString(); }
function copyText(btn, text) {
function copyFromParent(btn) {
var text = btn.parentElement.getAttribute('data-key') || btn.parentElement.getAttribute('title') || '';
navigator.clipboard.writeText(text).then(function() {
btn.classList.add('copied');
btn.textContent = '✓';
Expand Down Expand Up @@ -1573,9 +1575,9 @@ def _build_favicon() -> bytes:
var sr = s.success_rate != null ? Math.round(s.success_rate) : null;
return '<tr data-row onclick="toggleRow(this)">' +
'<td class="session-key" onclick="event.stopPropagation()">' +
'<div class="session-id" title="' + escapeHtml(s.session_key) + '">' +
'<div class="session-id" data-key="' + escapeHtml(s.session_key) + '" title="' + escapeHtml(s.session_key) + '">' +
'<span class="session-id-text">' + escapeHtml(parsed.session_id || s.session_key) + '</span>' +
'<button class="copy-btn" onclick="copyText(this,\'' + escapeHtml(s.session_key) + '\')" title="Copy Session ID">⧉</button>' +
'<button class="copy-btn" onclick="copyFromParent(this)" title="Copy Session ID">⧉</button>' +
'</div>' +
'<div class="session-meta" title="device: ' + escapeHtml(parsed.device_id) + ' | account: ' + escapeHtml(parsed.account_uuid) + '">' +
'dev:' + escapeHtml(shortId(parsed.device_id, 8)) + ' · acct:' + escapeHtml(shortId(parsed.account_uuid, 8)) +
Expand Down Expand Up @@ -1674,7 +1676,7 @@ def _build_favicon() -> bytes:
let refreshing = false;
let currentTab = 'overview';
const tabLoaded = { overview: false, sessions: false };
const TAB_LABELS = { overview: 'Overview', sessions: 'Recent Active Sessions' };
const TAB_LABELS = { overview: 'Overview', sessions: 'Sessions' };

async function refreshOverview() {
const days = currentDays > 0 ? currentDays : 7;
Expand Down Expand Up @@ -1780,6 +1782,10 @@ def _build_favicon() -> bytes:
currentTab = initial;
applyTabState(initial);
syncTabUrl(initial);
// Load version immediately regardless of active tab
fetchJSON('/api/dashboard/summary?days=7').then(function(s) {
if (s && s.version) document.getElementById('version-badge').textContent = 'v' + s.version;
}).catch(function(){});
refresh(); // 仅加载初始页签的数据
setInterval(refresh, 600000); // 每 10 分钟刷新当前页签
})();
Expand Down
Loading