diff --git a/web/hwlab-cloud-web/src/composables/useTraceSubscription.ts b/web/hwlab-cloud-web/src/composables/useTraceSubscription.ts index 657e6c8f..df2e3b03 100644 --- a/web/hwlab-cloud-web/src/composables/useTraceSubscription.ts +++ b/web/hwlab-cloud-web/src/composables/useTraceSubscription.ts @@ -133,7 +133,11 @@ export function mergeRunnerTrace(previous: ChatMessage["runnerTrace"], next: Non }; } -function mergeTraceTimingProjection(previous: unknown, next: unknown): TraceSnapshot["timing"] | null { +type TraceTimingMergeOptions = { + preserveVisibleTerminalDuration?: boolean; +}; + +function mergeTraceTimingProjection(previous: unknown, next: unknown, options: TraceTimingMergeOptions = {}): TraceSnapshot["timing"] | null { const previousTiming = traceTimingCandidate(previous); const nextTiming = traceTimingCandidate(next); if (!previousTiming && !nextTiming) return null; @@ -142,12 +146,16 @@ function mergeTraceTimingProjection(previous: unknown, next: unknown): TraceSnap const startedAt = earliestTraceTimestamp(previousTiming.startedAt, nextTiming.startedAt); const lastEventAt = latestTraceTimestamp(previousTiming.lastEventAt, nextTiming.lastEventAt); const finishedAt = latestTraceTimestamp(previousTiming.finishedAt, nextTiming.finishedAt); - const elapsedDurationMs = traceElapsedMs(startedAt, finishedAt ?? lastEventAt); + const runningElapsedDurationMs = traceElapsedMs(startedAt, lastEventAt); + const terminalElapsedDurationMs = traceElapsedMs(startedAt, finishedAt); const terminalTiming = traceTimingIsTerminal(next) || finishedAt !== null; const previousTerminalDurationMs = previousTiming.finishedAt ? previousTiming.durationMs : null; + const preservedVisibleTerminalDurationMs = options.preserveVisibleTerminalDuration && !finishedAt + ? firstTraceDuration(previousTiming.durationMs) + : null; const durationMs = terminalTiming - ? maxTraceDuration(previousTiming.durationMs, nextTiming.durationMs, elapsedDurationMs, previousTerminalDurationMs) - : maxTraceDuration(previousTiming.durationMs, nextTiming.durationMs, elapsedDurationMs); + ? maxTraceDuration(finishedAt ? nextTiming.durationMs : null, terminalElapsedDurationMs, previousTerminalDurationMs, preservedVisibleTerminalDurationMs) + : maxTraceDuration(previousTiming.durationMs, nextTiming.durationMs, runningElapsedDurationMs); return { ...previousTiming, ...nextTiming,