Spaces:
Sleeping
Sleeping
| // Live elapsed-time counter, mounted while the agent works. | |
| // | |
| // THE RESET BUG: this element re-mounts on every `answer.update()` — and a turn | |
| // updates the message many times (status line, Meshy progress, streamed tokens). | |
| // A React `useState(0)` starts over on each remount, so the timer kept snapping | |
| // back to 00:00 and never showed the real elapsed time. | |
| // | |
| // Fix: keep the START TIME on `window`, keyed by turn id, and derive elapsed from | |
| // it. Remounts then resume from the true start instead of restarting the clock. | |
| // The component becomes stateless with respect to time; only the tick is state. | |
| import { useEffect, useState } from "react"; | |
| export default function TurnTimer() { | |
| const id = props.id; | |
| const [, tick] = useState(0); | |
| useEffect(() => { | |
| window.__fossilTimer = window.__fossilTimer || {}; | |
| if (!window.__fossilTimer[id]) window.__fossilTimer[id] = Date.now(); | |
| const t = setInterval(() => { | |
| if (window.__fossilTimerStopped?.[id]) { clearInterval(t); return; } | |
| tick((v) => v + 1); | |
| }, 1000); | |
| return () => clearInterval(t); | |
| }, [id]); | |
| const start = window.__fossilTimer?.[id] || Date.now(); | |
| const s = Math.floor((Date.now() - start) / 1000); | |
| const mm = String(Math.floor(s / 60)).padStart(2, "0"); | |
| const ss = String(s % 60).padStart(2, "0"); | |
| return ( | |
| <div className="inline-flex items-center gap-2 text-xs text-neutral-500"> | |
| <span className="relative flex h-2 w-2"> | |
| <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75" /> | |
| <span className="relative inline-flex rounded-full h-2 w-2 bg-emerald-500" /> | |
| </span> | |
| <span className="font-mono tabular-nums">{mm}:{ss}</span> | |
| <span>working…</span> | |
| </div> | |
| ); | |
| } |