diff --git a/src/unified-dashboard.test.ts b/src/unified-dashboard.test.ts index 9a30881..e8a8e89 100644 --- a/src/unified-dashboard.test.ts +++ b/src/unified-dashboard.test.ts @@ -4,6 +4,7 @@ import type { UsageRow } from './dashboard-usage-rows.js'; import { buildWebUsageRowsForSnapshot, formatStatusHeader, + getDashboardDuplicateCleanupIntervalMs, renderUsageTable, shouldPurgeDashboardChannelOnStart, summarizeWatcherTasks, @@ -82,6 +83,13 @@ describe('shouldPurgeDashboardChannelOnStart', () => { }); }); +describe('getDashboardDuplicateCleanupIntervalMs', () => { + it('polls duplicate cleanup faster than the status update interval', () => { + expect(getDashboardDuplicateCleanupIntervalMs(10_000)).toBe(2_000); + expect(getDashboardDuplicateCleanupIntervalMs(1_000)).toBe(1_000); + }); +}); + describe('renderUsageTable', () => { const claudeRow: UsageRow = { name: 'Claude pro', diff --git a/src/unified-dashboard.ts b/src/unified-dashboard.ts index ffe726e..3f581f2 100644 --- a/src/unified-dashboard.ts +++ b/src/unified-dashboard.ts @@ -99,6 +99,7 @@ const USAGE_SNAPSHOT_MAX_AGE_MS = 600_000; * so this only affects how quickly Codex snapshot data is picked up. */ const RENDERER_USAGE_REFRESH_MS = 30_000; +const DASHBOARD_DUPLICATE_CLEANUP_POLL_MS = 2_000; let statusMessageId: string | null = null; let cachedUsageContent = ''; @@ -171,6 +172,12 @@ export function shouldPurgeDashboardChannelOnStart(args: { return args.purgeOnStart === true; } +export function getDashboardDuplicateCleanupIntervalMs( + statusUpdateInterval: number, +): number { + return Math.min(statusUpdateInterval, DASHBOARD_DUPLICATE_CLEANUP_POLL_MS); +} + async function refreshChannelMeta( opts: UnifiedDashboardOptions, ): Promise { @@ -798,6 +805,10 @@ export async function startUnifiedDashboard( }; setInterval(updateStatus, opts.statusUpdateInterval); + setInterval(() => { + if (!isRenderer || !statusMessageId) return; + void cleanupDashboardDuplicateMessages(opts, statusMessageId); + }, getDashboardDuplicateCleanupIntervalMs(opts.statusUpdateInterval)); await updateStatus(); if (isRenderer) {