diff --git a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-carbon-1440x900.png b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-carbon-1440x900.png index a5425b5..6fb25b3 100644 Binary files a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-carbon-1440x900.png and b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-carbon-1440x900.png differ diff --git a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-cinder-1440x900.png b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-cinder-1440x900.png index a7f38f7..3c5c31e 100644 Binary files a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-cinder-1440x900.png and b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-cinder-1440x900.png differ diff --git a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-coolvibe-1440x900.png b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-coolvibe-1440x900.png index 10962a3..8ed79ee 100644 Binary files a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-coolvibe-1440x900.png and b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-coolvibe-1440x900.png differ diff --git a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-dawn-1440x900.png b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-dawn-1440x900.png index e05e2b7..9e2f39f 100644 Binary files a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-dawn-1440x900.png and b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-dawn-1440x900.png differ diff --git a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-editorial-1440x900.png b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-editorial-1440x900.png index fe33fd1..ce7cc3e 100644 Binary files a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-editorial-1440x900.png and b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-editorial-1440x900.png differ diff --git a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-gilded-1440x900.png b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-gilded-1440x900.png index 61e4429..f532ae9 100644 Binary files a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-gilded-1440x900.png and b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-gilded-1440x900.png differ diff --git a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-ink-1440x900.png b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-ink-1440x900.png index bca7e5d..127634c 100644 Binary files a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-ink-1440x900.png and b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-ink-1440x900.png differ diff --git a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-nocturne-1440x900.png b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-nocturne-1440x900.png index c3c39ee..a76a675 100644 Binary files a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-nocturne-1440x900.png and b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-nocturne-1440x900.png differ diff --git a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-sage-1440x900.png b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-sage-1440x900.png index 9f3bb1e..ac3daad 100644 Binary files a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-sage-1440x900.png and b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-sage-1440x900.png differ diff --git a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-washi-1440x900.png b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-washi-1440x900.png index 3e6fa07..dbaba03 100644 Binary files a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-washi-1440x900.png and b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-washi-1440x900.png differ diff --git a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-wasteland-1440x900.png b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-wasteland-1440x900.png index c5c1d94..58c5c02 100644 Binary files a/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-wasteland-1440x900.png and b/.planning/usage-statistics-reference-refit/screenshots/dashboard-theme-wasteland-1440x900.png differ diff --git a/.planning/usage-statistics-reference-refit/screenshots/dashboard-washi-1024x768.png b/.planning/usage-statistics-reference-refit/screenshots/dashboard-washi-1024x768.png index 1be5031..20ec551 100644 Binary files a/.planning/usage-statistics-reference-refit/screenshots/dashboard-washi-1024x768.png and b/.planning/usage-statistics-reference-refit/screenshots/dashboard-washi-1024x768.png differ diff --git a/.planning/usage-statistics-reference-refit/screenshots/dashboard-washi-1467x943.png b/.planning/usage-statistics-reference-refit/screenshots/dashboard-washi-1467x943.png index 0f1b388..ce4f683 100644 Binary files a/.planning/usage-statistics-reference-refit/screenshots/dashboard-washi-1467x943.png and b/.planning/usage-statistics-reference-refit/screenshots/dashboard-washi-1467x943.png differ diff --git a/.planning/usage-statistics-reference-refit/screenshots/dashboard-washi-detail-1467x943.png b/.planning/usage-statistics-reference-refit/screenshots/dashboard-washi-detail-1467x943.png index 211afb3..7bdc0f7 100644 Binary files a/.planning/usage-statistics-reference-refit/screenshots/dashboard-washi-detail-1467x943.png and b/.planning/usage-statistics-reference-refit/screenshots/dashboard-washi-detail-1467x943.png differ diff --git a/.planning/usage-statistics-reference-refit/visual-check.js b/.planning/usage-statistics-reference-refit/visual-check.js index 3fbb2ce..e8d9187 100644 --- a/.planning/usage-statistics-reference-refit/visual-check.js +++ b/.planning/usage-statistics-reference-refit/visual-check.js @@ -411,6 +411,29 @@ async function main() { await waitFor(() => evaluate(cdp, "!document.querySelector('#usage-dashboard-panel').hidden"), 10000); await waitFor(() => evaluate(cdp, "document.querySelector('#usage-dashboard-panel').dataset.state !== 'loading'"), 30000); await sleep(320); + const loadingLayout = await evaluate(cdp, `(() => { + const metricGrid = document.querySelector('.usage-dashboard__metric-grid'); + const beforeTop = metricGrid.getBoundingClientRect().top; + document.querySelector('#usage-dashboard-refresh').click(); + const afterTop = metricGrid.getBoundingClientRect().top; + return { + state: document.querySelector('#usage-dashboard-panel').dataset.state, + label: document.querySelector('#usage-dashboard-refresh-label').textContent, + busy: document.querySelector('#usage-dashboard-refresh').getAttribute('aria-busy'), + contentShift: Math.round(Math.abs(afterTop - beforeTop) * 10) / 10, + standaloneLoading: !!document.querySelector('#usage-dashboard-loading'), + }; + })()`); + if (loadingLayout.state !== 'loading' + || loadingLayout.label !== '整理中' + || loadingLayout.busy !== 'true' + || loadingLayout.contentShift > 0.5 + || loadingLayout.standaloneLoading) { + throw new Error(`加载状态仍引发布局变化:${JSON.stringify(loadingLayout)}`); + } + await waitFor(() => evaluate(cdp, `document.querySelector('#usage-dashboard-panel').dataset.state !== 'loading' + && document.querySelector('#usage-dashboard-refresh-label').textContent === '刷新'`), 30000); + await sleep(300); await evaluate(cdp, `(() => { const from = document.querySelector('#usage-dashboard-from'); const to = document.querySelector('#usage-dashboard-to'); diff --git a/.planning/usage-statistics-reference-refit/visual-report.json b/.planning/usage-statistics-reference-refit/visual-report.json index a1d9bc9..18af8de 100644 --- a/.planning/usage-statistics-reference-refit/visual-report.json +++ b/.planning/usage-statistics-reference-refit/visual-report.json @@ -1,5 +1,5 @@ { - "generatedAt": "2026-08-03T14:15:13.710Z", + "generatedAt": "2026-08-05T01:06:45.134Z", "chrome": "/home/hdzx/.cache/ms-playwright/chromium-1228/chrome-linux64/chrome", "views": [ { diff --git a/dist-exe/cc-web-bun-linux-x64-baseline.tar.gz b/dist-exe/cc-web-bun-linux-x64-baseline.tar.gz index fb2177b..b487988 100644 Binary files a/dist-exe/cc-web-bun-linux-x64-baseline.tar.gz and b/dist-exe/cc-web-bun-linux-x64-baseline.tar.gz differ diff --git a/lib/session-search-index.js b/lib/session-search-index.js index aecadec..8cfc165 100644 --- a/lib/session-search-index.js +++ b/lib/session-search-index.js @@ -322,6 +322,17 @@ function updatedTime(doc) { return Number.isFinite(time) ? time : 0; } +// “最新”按本次命中的消息时间排序;没有消息时间时才回退到会话更新时间。 +function resultTime(result) { + const matchTimes = (result?.matches || []) + .map((match) => new Date(match?.timestamp || 0).getTime()) + .filter((time) => Number.isFinite(time) && time > 0); + if (matchTimes.length > 0) return Math.max(...matchTimes); + const directTime = new Date(result?.timestamp || 0).getTime(); + if (Number.isFinite(directTime) && directTime > 0) return directTime; + return updatedTime(result); +} + function buildQuery(rawQuery, options) { const maxQueryChars = clampInteger( options.maxQueryChars, @@ -735,6 +746,8 @@ function createSessionSearchIndex(options = {}) { messageIndex: firstMatch ? firstMatch.messageIndex : null, role: firstMatch ? firstMatch.role : null, timestamp: firstMatch ? firstMatch.timestamp : null, + matchTimestamp: firstMatch ? firstMatch.timestamp : null, + sessionUpdated: doc.updated, title: doc.title, projectName: doc.projectName, updated: doc.updated, @@ -773,8 +786,8 @@ function createSessionSearchIndex(options = {}) { } results.sort((a, b) => { - if (sort === 'newest') return updatedTime(b) - updatedTime(a) || b.score - a.score || a.sessionId.localeCompare(b.sessionId); - return b.score - a.score || updatedTime(b) - updatedTime(a) || a.sessionId.localeCompare(b.sessionId); + if (sort === 'newest') return resultTime(b) - resultTime(a) || b.score - a.score || a.sessionId.localeCompare(b.sessionId); + return b.score - a.score || resultTime(b) - resultTime(a) || a.sessionId.localeCompare(b.sessionId); }); return { diff --git a/public/app.js b/public/app.js index b566c54..8e8d570 100644 --- a/public/app.js +++ b/public/app.js @@ -349,13 +349,13 @@ const usageDashboardStatus = $('#usage-dashboard-status'); const usageDashboardGeneratedAt = $('#usage-dashboard-generated-at'); const usageDashboardRefresh = $('#usage-dashboard-refresh'); + const usageDashboardRefreshLabel = $('#usage-dashboard-refresh-label'); const usageDashboardPeriodWeek = $('#usage-dashboard-period-week'); const usageDashboardPeriodMonth = $('#usage-dashboard-period-month'); const usageDashboardPeriodCustom = $('#usage-dashboard-period-custom'); const usageDashboardFrom = $('#usage-dashboard-from'); const usageDashboardTo = $('#usage-dashboard-to'); const usageDashboardApply = $('#usage-dashboard-apply'); - const usageDashboardLoading = $('#usage-dashboard-loading'); const usageDashboardError = $('#usage-dashboard-error'); const usageDashboardTrend = $('#usage-dashboard-trend'); const usageDashboardMcpStatus = $('#usage-dashboard-mcp-status'); @@ -4142,7 +4142,8 @@ title: result?.title || match?.title || '未命名会话', projectName: result?.projectName || match?.projectName || '', cwd: result?.cwd || match?.cwd || '', - updated: match?.timestamp || result?.updated || null, + updated: match?.timestamp || result?.matchTimestamp || result?.updated || null, + sessionUpdated: result?.sessionUpdated || result?.updated || null, snippet: match?.snippet || result?.snippet || '', messageIndex: Number.isFinite(Number(match?.messageIndex)) ? Number(match.messageIndex) : null, }); @@ -4459,19 +4460,23 @@ } function setUsageDashboardState(state, message = '') { - usageDashboardState.loading = state === 'loading'; + const loading = state === 'loading'; + usageDashboardState.loading = loading; usageDashboardState.error = state === 'error' ? String(message || '统计查询失败') : ''; if (usageDashboardPanel) usageDashboardPanel.dataset.state = state; - if (usageDashboardLoading) usageDashboardLoading.hidden = state !== 'loading'; if (usageDashboardError) { usageDashboardError.hidden = state !== 'error'; usageDashboardError.textContent = state === 'error' ? usageDashboardState.error : ''; } if (usageDashboardRefresh) { - usageDashboardRefresh.disabled = state === 'loading'; - usageDashboardRefresh.setAttribute('aria-busy', state === 'loading' ? 'true' : 'false'); + const refreshText = loading ? '整理中' : '刷新'; + usageDashboardRefresh.disabled = loading; + usageDashboardRefresh.setAttribute('aria-busy', loading ? 'true' : 'false'); + usageDashboardRefresh.setAttribute('aria-label', loading ? '正在整理统计数据' : '刷新统计'); + usageDashboardRefresh.title = loading ? '正在整理统计数据' : '刷新统计'; + if (usageDashboardRefreshLabel) usageDashboardRefreshLabel.textContent = refreshText; } - if (usageDashboardApply) usageDashboardApply.disabled = state === 'loading'; + if (usageDashboardApply) usageDashboardApply.disabled = loading; } function formatUsageNumber(value) { diff --git a/public/index.html b/public/index.html index ab7bcf3..42ec2c0 100644 --- a/public/index.html +++ b/public/index.html @@ -24,7 +24,7 @@ document.documentElement.dataset.dividerTime = dividerTime; })(); - +
@@ -164,7 +164,7 @@ - 刷新 + 刷新