authorgravatar for 206951365+cursor[bot]@users.noreply.github.comcursor[bot] <206951365+cursor[bot]@users.noreply.github.com> 2026-05-07 11:54:46-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-05-07 11:54:46-07:00
log2ba58076fa19e11811b021a479400a8810b9930d
tree8ccb325962aa523896db0146cb139a15736c105a
parentc27109cc20cd9c56bec3ce9f396f4c7250429ab2
signature Signed by PGP key B5690EEEBB952194

Avoid timeline timer rerender commits (#2580)

Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Julius Marminge <juliusmarminge@users.noreply.github.com>

1 files changed, 47 insertions(+), 13 deletions(-)

apps/web/src/components/chat/MessagesTimeline.tsx+47-13
......@@ -520,19 +520,27 @@ function WorkingTimelineRow({ row }: { row: Extract<TimelineRow, { kind: "workin
520520}
521521
522522// ---------------------------------------------------------------------------
523// Self-ticking components — bypass LegendList memoisation entirely.
524// Each owns a `nowMs` state value consumed in the render output so the
525// React Compiler cannot elide the re-render as a no-op.
523// Self-ticking labels — update their own text nodes so elapsed-time display
524// does not create a React commit every second while a response is streaming.
526525// ---------------------------------------------------------------------------
527526
528527/** Live "Working for Xs" label. */
529528function WorkingTimer({ createdAt }: { createdAt: string }) {
530 const [nowMs, setNowMs] = useState(() => Date.now());
529 const textRef = useRef<HTMLSpanElement>(null);
530 const initialText = formatWorkingTimerNow(createdAt);
531
531532 useEffect(() => {
532 const id = setInterval(() => setNowMs(Date.now()), 1000);
533 const updateText = () => {
534 if (textRef.current) {
535 textRef.current.textContent = formatWorkingTimerNow(createdAt);
536 }
537 };
538 updateText();
539 const id = setInterval(updateText, 1000);
533540 return () => clearInterval(id);
534541 }, [createdAt]);
535 return <>{formatWorkingTimer(createdAt, new Date(nowMs).toISOString()) ?? "0s"}</>;
542
543 return <span ref={textRef}>{initialText}</span>;
536544}
537545
538546/** Live timestamp + elapsed duration for a streaming assistant message. */
......@@ -545,15 +553,28 @@ function LiveMessageMeta({
545553 durationStart: string | null | undefined;
546554 timestampFormat: TimestampFormat;
547555}) {
548 const [nowMs, setNowMs] = useState(() => Date.now());
556 const textRef = useRef<HTMLSpanElement>(null);
557 const initialText = formatLiveMessageMetaNow(createdAt, durationStart, timestampFormat);
558
549559 useEffect(() => {
550 const id = setInterval(() => setNowMs(Date.now()), 1000);
560 const updateText = () => {
561 if (textRef.current) {
562 textRef.current.textContent = formatLiveMessageMetaNow(
563 createdAt,
564 durationStart,
565 timestampFormat,
566 );
567 }
568 };
569 updateText();
570 if (!durationStart) {
571 return;
572 }
573 const id = setInterval(updateText, 1000);
551574 return () => clearInterval(id);
552 }, [durationStart]);
553 const elapsed = durationStart
554 ? formatElapsed(durationStart, new Date(nowMs).toISOString())
555 : null;
556 return <>{formatMessageMeta(createdAt, elapsed, timestampFormat)}</>;
575 }, [createdAt, durationStart, timestampFormat]);
576
577 return <span ref={textRef}>{initialText}</span>;
557578}
558579
559580// ---------------------------------------------------------------------------
......@@ -860,6 +881,19 @@ function formatWorkingTimer(startIso: string, endIso: string): string | null {
860881 return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
861882}
862883
884function formatWorkingTimerNow(startIso: string): string {
885 return formatWorkingTimer(startIso, new Date().toISOString()) ?? "0s";
886}
887
888function formatLiveMessageMetaNow(
889 createdAt: string,
890 durationStart: string | null | undefined,
891 timestampFormat: TimestampFormat,
892): string {
893 const elapsed = durationStart ? formatElapsed(durationStart, new Date().toISOString()) : null;
894 return formatMessageMeta(createdAt, elapsed, timestampFormat);
895}
896
863897function formatMessageMeta(
864898 createdAt: string,
865899 duration: string | null,