fix(cli): fixed-width elapsed time below one minute to stop status-line jitter - #6533
Conversation
…ne jitter
The loading indicator ticks at 0.5s resolution, so the time string
alternated between forms like "1s" and "1.5s" every tick. The changing
width shifted everything after it on the status line twice a second,
making it distracting and hard to read. Render one fixed decimal below
the minute mark ("1.0s", "1.5s"); the >=1m path is unchanged.
Fixes QwenLM#6402
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Thanks for the PR! Template looks good ✓ Problem: observed bug with evidence. Issue #6402 documents the jitter with screenshots — the 0.5s-resolution timer alternates between Direction: aligned. This is a small, well-scoped cosmetic fix for a real distraction in the status line. The issue is already labeled Size: 6 production lines (5+1 in Approach: scope is exactly right — one 中文说明感谢贡献! 模板完整 ✓ 问题:已观测到的 bug,有证据。 Issue #6402 用截图记录了抖动现象——0.5 秒精度的计时器在 方向:对齐。这是一个小而聚焦的 UI 修饰修复,解决状态行的实际抖动问题。Issue 已标记 规模:6 行生产代码( 方案:范围恰到好处——一个 — Qwen Code · qwen3.7-max |
Code ReviewIndependent proposal before reading the diff: the fix is a one-liner — apply The diff is minimal and correct:
No correctness bugs, no security concerns, no AGENTS.md violations. No reuse concerns — Real-Scenario TestingBefore (installed qwen v0.19.7, old format): Captured at 0.6s intervals against a real API call. Half-second values are visible; the jitter occurs at whole-second boundaries (e.g., After ( Format comparison (demonstrated via Node.js): Unit tests: 28/28 passed on PR branch ✓ (including dedicated fixed-width test) 中文说明代码审查独立方案(阅读 diff 前):修复只需一行——在 60 秒以下的 Diff 最小且正确:
无正确性 bug、无安全问题、无 AGENTS.md 违规。无复用问题—— 真实场景测试修复前(安装的 qwen v0.19.7,旧格式): 捕获了 修复后(PR 分支上的 单元测试:PR 分支上 28/28 通过 ✓(含专用固定宽度测试) — Qwen Code · qwen3.7-max |
|
This is a textbook example of how a small, focused PR should look. One production line changed, a clear why comment, tests updated and expanded, snapshot regenerated — nothing more, nothing less. The problem is real (Issue #6402 has screenshots of the jitter), the fix is the simplest possible approach ( Unit tests all pass. The before-capture from the installed CLI confirms the old variable-width format; the format comparison demonstrates the constant-width fix. Happy to approve. ✅ 中文说明这是一个教科书级别的小型 PR。一行生产代码改动,一条解释原因的注释,测试更新并扩展,快照重新生成——不多不少。 问题是真实的(Issue #6402 有抖动的截图),修复是最简单的方案( 单元测试全部通过。安装版 CLI 的 before 捕获确认了旧的变宽格式;格式对比展示了修复后的恒宽效果。 批准合并 ✅ — Qwen Code · qwen3.7-max |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. Suggestion-level recommendations are in the Suggestion summary comment below.
|
No new Suggestion-level findings this round — all prior suggestions have been addressed or superseded. |
Review follow-up: useTimer initializes and resets at exactly 0, so assert the "(0.0s · esc to cancel)" frame too. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Added the suggested |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No issues found. LGTM! ✅
— qwen3.7-max via Qwen Code /review
Independent E2E verification — real TUI in tmux, A/B against the merge-baseVerdict: ✅ works as advertised; recommend merge. No correctness issues found. The automated review above noted it couldn't capture "after" frames from a real TUI ("the streaming response filled the tmux viewport and the status line scrolled off-screen"). This comment closes that gap: below is a real Harness
Metric: the column at which Result — 25 s run
BASE's ±2-column oscillation is exactly the Real captured frames, replayed at capture cadence. Red guide = where Result — 68 s run (crosses the one-minute boundary)
Confirms the PR's claim that the ≥ 1 m path is untouched — not just by reading the diff, but observed live in both arms. Width table across every boundary (BASE vs PR)
Tests
Scope audit
Non-blocking observations
Not coveredReal model provider (mock only); Windows/macOS; runtime narrow-width layout (unit-tested only); durations > 1 h. Reproduce# 1. two dist variants of the one changed file
git show 8296ce9:packages/cli/src/ui/components/LoadingIndicator.tsx # BASE
git show ada6c2a:packages/cli/src/ui/components/LoadingIndicator.tsx # PR
# transpile each -> swap into packages/cli/dist/src/ui/components/LoadingIndicator.js
# 2. mock provider that keeps StreamingState.Responding alive
# SSE: role chunk @50ms, tiny content burst @700ms, ": keepalive" every 3s, [DONE] @26s
# 3. drive the real CLI, sample the pane
tmux new-session -d -s arm -x 100 -y 24 \
"env HOME=$ISO OPENAI_BASE_URL=http://127.0.0.1:8799/v1 OPENAI_API_KEY=sk-mock DEV=true \
node packages/cli/dist/index.js --approval-mode yolo"
# settings.json: security.auth.selectedType=openai, model.name=mock-model,
# ui.customWittyPhrases=["Reticulating splines"] <- pins the left half
tmux send-keys -t arm 'hello' Enter
# loop: tmux capture-pane -p -t arm every 150ms
# 4. count column changes of "esc to cancel" across consecutive distinct frames🇨🇳 中文版验证报告独立端到端验证 —— tmux 中运行真实 TUI,与 merge-base 做 A/B 对比结论:✅ 行为与描述一致,建议合并。 未发现正确性问题。 上面的自动化 review 提到它无法从真实 TUI 抓到 "after" 画面("流式输出把 tmux 视口填满,状态行滚出屏幕")。本条评论补上这个缺口:下面是对已构建 CLI 的真实 测试装置
度量指标: 结果 —— 25 秒运行
BASE 的 ±2 列振荡正好是 (图见英文版:GIF 为真实抓帧按原速回放,红色参考线是 PR 中 结果 —— 68 秒运行(跨越 1 分钟边界)
这印证了 PR 所声称的 "≥1 分钟路径未改动" —— 不只是看 diff,而是在两组里实地观测到的。
测试
影响面审计对 不阻塞合并的观察
未覆盖真实模型 provider(仅用 mock);Windows / macOS;运行时窄终端布局(仅单测覆盖);> 1 小时的时长。 Verified against |



What this PR does
Renders the elapsed time in the loading status line with a fixed one-decimal width while it is below one minute (
0.5s,1.0s,1.5s, …). The at-or-above-one-minute path (1m,2m 5s, …) is unchanged.Why it's needed
The timer ticks at 0.5s resolution, so the string alternated between forms like
1sand1.5s— the ±2-character width change shifted everything after it (· esc to cancel, token counts) twice a second, making the status line distracting and hard to read. This is the first of the two approaches suggested in the issue triage (fixed decimal keeps the sub-second precision; rounding to whole seconds would drop it).Reviewer Test Plan
How to verify
Start any request that takes a few seconds and watch the status line during the first minute: the time should render as
1.0s,1.5s,2.0s, … with constant width, so the· esc to cancelsuffix and token counters no longer jump horizontally. Past one minute the display switches to1m,1m 30sexactly as before.Evidence (Before & After)
Before (main): status line alternates
(1.5s · esc to cancel)/(2s · esc to cancel)— suffix shifts left/right every tick.After (this branch, captured from a real tmux session against a mock provider at 0.5s intervals):
All frames render the time at the same width; the suffix stays put.
Tested on
Environment (optional)
npm run dev(tsx from source) driven in tmux against a local mock OpenAI-compatible server with delayed responses; unit tests vianpx vitest run src/ui/components/LoadingIndicator.test.tsx(28 pass).Risk & Scope
.0on whole seconds.Linked Issues
Fixes #6402