Merge pull request #2056 from pikasTech/fix/revert-ui-timing-fabrication-20260624

revert(workbench): remove UI timing smoothing repairs
This commit is contained in:
Lyon
2026-06-24 19:24:14 +08:00
committed by GitHub
2 changed files with 6 additions and 63 deletions
@@ -28,14 +28,6 @@ const { following, keepBottomAfterUpdate, onScroll: onPanelScroll, scrollToBotto
const detailMessageId = ref<string | null>(null);
const displayedDurationFloorMs = new Map<string, number>();
const sealedDurationMs = new Map<string, number>();
type VisibleTimingSample = {
durationMs?: number;
durationObservedAtMs?: number;
lastEventAgeMs?: number;
lastEventObservedAtMs?: number;
lastEventSourceMs?: number;
};
const visibleTimingSamples = new Map<string, VisibleTimingSample>();
const visibleMessages = computed(() => workbench.sessionDetailLoading ? [] : workbench.activeMessages);
const hasRunningAgentMessages = computed(() => visibleMessages.value.some(isRunningMessage));
const { nowMs } = useWorkbenchNowTicker(hasRunningAgentMessages);
@@ -74,9 +66,6 @@ watch(visibleMessages, (messages) => {
for (const key of Array.from(sealedDurationMs.keys())) {
if (!visibleKeys.has(key)) sealedDurationMs.delete(key);
}
for (const key of Array.from(visibleTimingSamples.keys())) {
if (!visibleKeys.has(key)) visibleTimingSamples.delete(key);
}
}, { immediate: true });
function openDetails(message: ChatMessage): void {
@@ -280,12 +269,8 @@ function messageDurationMeta(message: ChatMessage): { text: string; label: strin
function monotonicDisplayDurationMs(message: ChatMessage, durationMs: number | null): number | null {
if (durationMs === null) return null;
const key = messageDurationFloorKey(message);
const sample = visibleTimingSamples.get(key) ?? {};
const previous = sample.durationMs ?? displayedDurationFloorMs.get(key);
const next = smoothVisibleIncreasingDurationMs(previous, sample.durationObservedAtMs, durationMs, nowMs.value);
sample.durationMs = next;
sample.durationObservedAtMs = nowMs.value;
visibleTimingSamples.set(key, sample);
const previous = displayedDurationFloorMs.get(key);
const next = previous === undefined ? durationMs : Math.max(previous, durationMs);
if (next !== previous) displayedDurationFloorMs.set(key, next);
return next;
}
@@ -296,21 +281,12 @@ function sealedDisplayDurationMs(message: ChatMessage, durationMs: number | null
const sealedFloor = sealedDurationMs.get(key);
const previous = maxDefinedDurationMs(runningFloor, sealedFloor);
displayedDurationFloorMs.delete(key);
visibleTimingSamples.delete(key);
if (durationMs === null) return previous ?? null;
const next = previous === null ? durationMs : Math.max(previous, durationMs);
if (next !== previous) sealedDurationMs.set(key, next);
return next;
}
function smoothVisibleIncreasingDurationMs(previous: number | undefined, previousObservedAtMs: number | undefined, targetMs: number, observedAtMs: number): number {
const target = Math.max(0, Math.trunc(targetMs));
if (previous === undefined || !Number.isFinite(previous)) return target;
const elapsedSincePreviousSample = Number.isFinite(previousObservedAtMs) ? Math.max(0, observedAtMs - Number(previousObservedAtMs)) : 0;
const visibleCeiling = previous + elapsedSincePreviousSample;
return Math.max(previous, Math.min(target, Math.trunc(visibleCeiling)));
}
function maxDefinedDurationMs(...values: Array<number | undefined>): number | null {
let maximum: number | null = null;
for (const value of values) {
@@ -343,34 +319,12 @@ function migrateVisibleTimingAliases(primary: string, aliases: unknown[]): void
function messageActivityMeta(message: ChatMessage): { text: string; label: string } | null {
if (!isRunningMessage(message)) return null;
const timing = messageTimingForDisplay(message);
const ageMs = visibleLastEventAgeMs(message, timing?.lastEventAt);
const ageMs = durationSince(timing?.lastEventAt);
if (ageMs === null) return null;
const value = formatDuration(ageMs) + "前";
return { text: `最近 ${value}`, label: `最近事件:${value}` };
}
function visibleLastEventAgeMs(message: ChatMessage, timestamp: unknown): number | null {
const eventAtMs = timestampMs(timestamp);
if (eventAtMs === null) return null;
const key = messageDurationFloorKey(message);
const sample = visibleTimingSamples.get(key) ?? {};
const now = nowMs.value;
const targetAgeMs = Math.max(0, now - eventAtMs);
let nextAgeMs: number;
if (sample.lastEventAgeMs === undefined || !Number.isFinite(sample.lastEventAgeMs)) {
nextAgeMs = targetAgeMs;
} else if (sample.lastEventSourceMs !== undefined && eventAtMs > sample.lastEventSourceMs) {
nextAgeMs = 0;
} else {
nextAgeMs = smoothVisibleIncreasingDurationMs(sample.lastEventAgeMs, sample.lastEventObservedAtMs, targetAgeMs, now);
}
sample.lastEventAgeMs = nextAgeMs;
sample.lastEventObservedAtMs = now;
sample.lastEventSourceMs = eventAtMs;
visibleTimingSamples.set(key, sample);
return nextAgeMs;
}
function messageTimingForDisplay(message: ChatMessage): ChatMessage["timing"] {
// The main message card consumes only the durable message timing projection.
// Trace timing belongs to TraceTimeline/details; letting trace hydration drive the
@@ -4,7 +4,6 @@
import { onBeforeUnmount, readonly, ref, watch, type Ref } from "vue";
const TICK_INTERVAL_MS = 1000;
const MAX_VISIBLE_TICK_STEP_MS = TICK_INTERVAL_MS;
const nowMs = ref(Date.now());
const activeSubscribers = new Map<symbol, boolean>();
@@ -31,18 +30,8 @@ export function useWorkbenchNowTicker(enabled: Ref<boolean>) {
return { nowMs: readonly(nowMs), refreshNow };
}
function refreshNow(reset = false): void {
const wallNow = Date.now();
if (reset) {
nowMs.value = wallNow;
return;
}
const previous = Number(nowMs.value);
if (!Number.isFinite(previous) || wallNow <= previous) {
nowMs.value = wallNow;
return;
}
nowMs.value = Math.min(wallNow, previous + MAX_VISIBLE_TICK_STEP_MS);
function refreshNow(): void {
nowMs.value = Date.now();
}
function reconcileTimer(): void {
@@ -51,7 +40,7 @@ function reconcileTimer(): void {
// elapsed/recent labels advance monotonically instead of jumping on resume.
const shouldRun = isBrowser() && hasActiveSubscriber();
if (shouldRun && timer === null) {
refreshNow(true);
refreshNow();
timer = window.setInterval(refreshNow, TICK_INTERVAL_MS);
return;
}