mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 06:19:43 -10:00
* fix(cli): break Ink useBoxMetrics commit-phase setState loop (React #185) * fix(cli): scope the ink useBoxMetrics loop guard per instance The guard added to the vendored Ink patch kept its 32-entry budget in module scope, so every useBoxMetrics instance shared one budget, and the budget counted invocations rather than oscillation cycles. With 40 boxes mounting in a single commit, instances past the 32nd invocation never measured: hasMeasured stayed false for good, because no later commit re-fires their layout listener. Hold the budget in a ref so each instance refills on its own. Cancel the pending budget-refill timer on unmount. Vitest fakes setImmediate by default and vi.useRealTimers() discards a pending faked immediate, so the stored handle stayed non-null after the timer was gone and the counter never refilled. Drop the functional same-value bail for setHasMeasured. The useState setter already bails for an identical primitive, and the functional form cannot be eagerly bailed - React has to schedule the render to evaluate the updater. Reverted to upstream's setHasMeasured(Boolean(ref.current)). Regenerate patches/ink+7.0.3.patch with patch-package instead of hand-appending the hunk, so it lands in path order with an index line and function-context headers. The regeneration also reformats the pre-existing hunks (index hashes, hunk-context labels, offsets); that churn is cosmetic, and the only content change outside the new hunk is one "\ No newline at end of file" marker. Adds packages/cli/src/ui/use-box-metrics-loop-guard.test.tsx, which renders the real hook and pins three behaviours: an oscillating box settles without React #185, a later resize still re-measures once an oscillation tripped the guard, and 40 instances all measure within one commit. * fix(cli): refill the ink useBoxMetrics budget from elapsed time The measurement budget added to the vendored ink hook refilled itself from a setImmediate callback. A test that installs fake timers only runs that callback when it advances the clock, and the InputPrompt suite flushes renders with microtask yields inside act(), so the refill stayed pending and React's act queue never drained with it. Ten tests in that file each burned their full 15s timeout; on CI, where the suite runs with --retry=2, that single file outlived the 60 minute job limit and the lane was cancelled without printing a summary. Refilling from elapsed wall-clock time keeps the same per-instance budget and the same protection against the commit -> layout listener -> setState loop, and leaves no handle for a test to drain, so the unmount cleanup is no longer needed either. --------- Co-authored-by: yiliang114 <[email protected]> Co-authored-by: yiliang114 <[email protected]>