Uh oh!
There was an error while loading. Please reload this page.
feat(plugin-chatbot): elapsed-time liveness counter on running AI turns - #1699
Merged
Conversation
AI app builds run 1–3 min with long quiet gaps (LLM thinking, sample-data generation). During those gaps the only signal was a static spinner, so a slow turn was indistinguishable from a dropped connection — users worried the network had died. Add a Claude-Code-style m:ss counter beside both running spinners: - the generic "thinking" message (AssistantThinkingMessage) - the in-flight build panel header (BuildProgressPanel) The tick only advances while React is responsive, so a live counter is itself a "still connected, still working" cue and sets the expectation that the wait is normal. It freezes at its final value when the turn ends, leaving the total duration visible. Language-neutral (no i18n string). Tests: render the real component under fake timers and assert the counter ticks 0:00→0:03 on the thinking spinner and 0:02-then-frozen on the build panel. 69/69 pass; typecheck + build clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
xuyushun441-sys added a commit
that referenced
this pull request
Jun 13, 2026
…#1700) The elapsed counter from #1699 ticked off a client-side setInterval, so it kept counting up even if the connection had dropped — falsely telling the user "still working" when nothing was happening. That's exactly the reassurance-theater we don't want. Replace it with liveness derived from REAL observed stream activity: - `useTurnLiveness(active, activityKey)` stamps the moment real data arrives (a streamed token / tool delta / `data-build-progress` update mutates the key) and measures seconds-since-last-byte. `live` is a genuine "the server sent us something recently" signal, not a free clock. - `LivenessIndicator` renders three honest states: • receiving (emerald pulse + m:ss) — bytes arrived recently • waiting (muted) — request in flight, nothing back yet (pre-first-token; never claims "receiving" when nothing received) • stalled (amber + "no response for Ns") — genuinely silent past 6s - Build panel feeds its real progress as the activity key, so a healthy build (which streams progress every few seconds) reads as receiving and a true stall flips to amber. A hard disconnect still surfaces via the error banner. Honest limitation (documented, follow-up): during a genuinely silent but alive server stretch the client can only say "no response for Ns" — proving "still connected" there needs a server-side SSE keep-alive heartbeat (framework). This change stops the lying; the heartbeat would let the quiet windows read green truthfully. Tests: 71/71. Four new ones render the real component under fake timers and assert the honest behavior — pre-first-token shows a neutral wait (not a fake m:ss), a quiet stream escalates to amber "…Ns", a streaming build reads as receiving, and a stalled build flips amber then recovers on the next byte. Typecheck + build clean; new code is lint-clean (no Date.now()/refs in render). Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
AI app builds run 1–3 minutes with long quiet gaps (LLM thinking, sample-data generation). During those gaps the only on-screen signal was a static spinner, so a slow turn looked identical to a dropped connection — users worried the network had died ("ai 执行会很卡很慢… 让用户知道服务器还连接着,网络没断").
What
A Claude-Code-style
m:sselapsed counter beside both running spinners:AssistantThinkingMessage)BuildProgressPanel)The tick only advances while React is responsive, so the live counter is itself a "still connected, still working" cue and sets the expectation that the wait is normal. It freezes at its final value when the turn ends, leaving the total build duration visible. Language-neutral (a bare
m:ss+ clock glyph — no new i18n string).Self-contained: a small
useElapsedSecondshook + presentationalElapsedTime; no new deps (Clock3already imported), no API/route change, untouched routing.Verification
tsc --noEmitclean,vite buildclean0:00 → 0:03on the thinking spinner and0:02-then-frozen on the build panel afterphase: 'done'.Follow-up (out of scope)
For true stall-vs-connected discrimination (server holds the SSE open but sends nothing), a server-side keep-alive heartbeat + "last activity Xs ago" indicator would be the next step. The elapsed counter covers the dropped-connection / "is it alive" reassurance, which is the reported pain.
🤖 Generated with Claude Code