fix(ios): use working spinner for jump-to-bottom state

Merged
TE
8d ago
+17-21

Leo’s request (2026-10-29): “i also want all those ui fixes/improvements in and stuff too along with more think tanks to improve stuff too after learning all those ui dev things”.

The iOS jump-to-bottom button uses an opacity-pulsing ring to indicate an unfinished agent turn. Replace that ring with a standard SwiftUI working spinner in a small corner badge. The badge separates the spinner from the jump arrow, preserving the arrow, action, 36-point glass disc, 44-point target, transition and accessibility label.

The badge uses the new workingIndicatorBadge design token and the existing control-size tokens. It is decorative and ignores hit testing and accessibility. SwiftUI owns the spinner lifecycle: it exists only while the jump control is visible and store.isRunning is true, and is removed when either condition ends. The custom repeat-forever opacity animation is removed; no timer or observer is added.

BeforeAfter
Jump arrow surrounded by a pulsing outline. The baseline was captured in the identical focused UI test.Jump arrow with a separate mini working spinner at the upper trailing corner. Exact-head light and dark captures show full symbol separation and no clipping.

Checklist: spinner for agent working state; only this working indicator changes; design tokens for badge and control sizes; animation stops when its view disappears.

Validation at f43f5bbaa9edb63a20536333f7019a793f9d3ae3: git diff --check and python3 scripts/cmux-next/prepush-gate.py pass. Independent Astra review returned SHIP at this exact head. Fleet iOS compile step b7e5d83b4715ffe24f6bfb27 succeeded (exit 0); its only Theme.swift warning is identical on the baseline. The focused AgentChatJumpCaptureUITests light/dark run passed 2/2 at this head (step 1bd93dc4). The identical baseline run passed 2/2 (step 3cd3c71c). A slow-host 0/2 run failed at the test's menu-dismiss timing assertion and is test timing, not product; it is excluded from evidence.

The fixture is an unfinished kbd-chat waiting turn, not active streaming. PNGs were exported by the snapshot-only attachment hook in the test file. Before/after below (fleet simulator, iPhone 17 Pro / iOS 26.5, AgentChatJumpCaptureUITests 2/2 on each side; before = baseline 83fb73e7, step 3cd3c71c04a1438c7964ae1b; after = head f43f5bba, step 1bd93dc42cb9bf1f20fd4548; light and dark):

StateBeforeAfter
Working, scrolled away
Back at the bottom

Scrolled away, the after arrow carries the spinner as a small top-trailing badge, clear of the arrow glyph; the before arrow has no running indicator. Back at the bottom the arrow is gone on both sides.

Risk: the badge must remain distinct from the arrow and fit within the control in light and dark appearance. The reviewed captures show the badge fits the 44-point frame without clipping; the dark backing merges slightly with the dark background, but the spinner remains legible. The PR is ready for landing after public media attachment.

Agent session: agent session