From 95c1e3d8fe345e4f04cb369dfc538e87d8fef088 Mon Sep 17 00:00:00 2001 From: lyon Date: Sun, 21 Jun 2026 04:54:23 +0800 Subject: [PATCH] fix: keep visible workbench duration monotonic --- .../workbench/ConversationPanel.vue | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/web/hwlab-cloud-web/src/components/workbench/ConversationPanel.vue b/web/hwlab-cloud-web/src/components/workbench/ConversationPanel.vue index 993ca790..b88b75a0 100644 --- a/web/hwlab-cloud-web/src/components/workbench/ConversationPanel.vue +++ b/web/hwlab-cloud-web/src/components/workbench/ConversationPanel.vue @@ -26,6 +26,7 @@ const clipboard = useClipboard(); const panelRef = ref(null); const { following, keepBottomAfterUpdate, onScroll: onPanelScroll, scrollToBottom } = useBottomFollowScroll(panelRef, { threshold: 56 }); const detailMessageId = ref(null); +const displayedDurationFloorMs = new Map(); const visibleMessages = computed(() => workbench.sessionDetailLoading ? [] : workbench.activeMessages); const hasRunningAgentMessages = computed(() => visibleMessages.value.some(isRunningMessage)); const { nowMs } = useWorkbenchNowTicker(hasRunningAgentMessages); @@ -56,6 +57,13 @@ watch(() => workbench.sessionDetailLoading, async () => { acknowledgeVisibleMessages(); }); +watch(visibleMessages, (messages) => { + const visibleKeys = new Set(messages.map(messageDurationFloorKey)); + for (const key of Array.from(displayedDurationFloorMs.keys())) { + if (!visibleKeys.has(key)) displayedDurationFloorMs.delete(key); + } +}, { immediate: true }); + function openDetails(message: ChatMessage): void { detailMessageId.value = message.id; } @@ -234,12 +242,25 @@ function traceForDisplay(message: ChatMessage): ChatMessage["runnerTrace"] { function messageDurationMeta(message: ChatMessage): { text: string; label: string } | null { if (message.role !== "agent") return null; const timing = messageTimingForDisplay(message); - const durationMs = isRunningMessage(message) ? durationSince(timing?.startedAt) : finiteDurationMs(timing?.durationMs); - if (durationMs === null) return null; + const rawDurationMs = isRunningMessage(message) ? durationSince(timing?.startedAt) : finiteDurationMs(timing?.durationMs); + if (rawDurationMs === null) return null; + const durationMs = monotonicDisplayDurationMs(message, rawDurationMs); const value = formatDuration(durationMs); return { text: `耗时 ${value}`, label: `总耗时:${value}` }; } +function monotonicDisplayDurationMs(message: ChatMessage, durationMs: number): number { + const key = messageDurationFloorKey(message); + const previous = displayedDurationFloorMs.get(key); + const next = previous === undefined ? durationMs : Math.max(previous, durationMs); + if (next !== previous) displayedDurationFloorMs.set(key, next); + return next; +} + +function messageDurationFloorKey(message: ChatMessage): string { + return firstNonEmptyString(message.id, message.runnerTrace?.traceId, message.traceId) ?? "unknown-agent-message"; +} + function messageActivityMeta(message: ChatMessage): { text: string; label: string } | null { if (!isRunningMessage(message)) return null; const timing = messageTimingForDisplay(message);