fix(ios): chat snaps back to bottom when scrolling to top via status-bar tap#100502
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed July 5, 2026, 9:27 PM ET / 01:27 UTC. Summary PR surface: Other +26. Total +26 across 3 files. Reproducibility: Source-reproducible, but not live-reproduced here: current main does not release follow mode for Review metrics: none identified. Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Maintainer decision needed
Security Review detailsBest possible solution: Land the narrow phase-policy fix after normal maintainer approval and mergeability gates, keeping the shared SwiftUI helper and focused tests. Do we have a high-confidence way to reproduce the issue? Source-reproducible, but not live-reproduced here: current main does not release follow mode for Is this the best way to solve the issue? Yes, this is the best narrow fix: shared AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against f12312d69b13. Label changesLabel justifications:
Evidence reviewedPR surface: Other +26. Total +26 across 3 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (1 earlier review cycle)
|
56318f9 to
c8478dd
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c8478dd2f4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| /// avoidance). Not releasing there lets the next timeline tick yank the reader back down. | ||
| func chatReaderScrollReleasesFollow(_ phase: ScrollPhase) -> Bool { | ||
| switch phase { | ||
| case .interacting, .animating: |
There was a problem hiding this comment.
Keep own scroll animations from clearing follow state
SwiftUI reports .animating for programmatic scrollPosition/ScrollViewReader moves, and this view uses moveScrollPosition for auto-follow, new-turn positioning, and the jump-to-latest action. When one of those reader-owned scrolls enters this branch, it is treated as user/system departure and clears followTarget, so rapid streaming updates or a jump-to-latest/new-turn scroll can stop following or show the jump pill after our own scroll rather than an actual manual departure. Gate .animating with an applying-scroll flag (as the Android controller does) instead of always releasing follow.
Useful? React with 👍 / 👎.
* origin/main: (1287 commits) fix(android): block loopback canvas navigation (openclaw#99874) fix(hooks): suppress unhandled stdout/stderr stream errors in gmail watcher (openclaw#100519) fix(memory-core): guard qmd mcporter JSON.parse against non-JSON stdout (openclaw#98381) fix(build): fall back to tsx for build TypeScript scripts (openclaw#91262) feat(skills): suggest saving detected reusable workflows by default (openclaw#95477) (openclaw#100692) docs(changelog): remove generated release-note entries feat(telegram): offer BotFather web app flow in setup help and docs (openclaw#100540) fix(ios): unify Talk and Settings row typography on one branded detail row (openclaw#100515) feat(gateway): add persisted crash-loop breaker and fatal-config exit contract refactor(macos): lock and unify PortGuardian tunnel record persistence so concurrent app instances cannot lose orphan records (openclaw#100601) fix: stop reconnecting on protocol mismatch (openclaw#98414) fix(maint): reuse recent hosted gates after rebase (openclaw#100663) fix(ui): reopen web terminals without stale content (openclaw#100665) fix(browser): diagnose empty WSL2 Chrome replies (openclaw#100590) fix(ios): chat snaps back to bottom when scrolling to top via status-bar tap (openclaw#100502) Treat already-compacted CLI compaction as no-op (openclaw#99136) docs(changelog): remove direct main fix entry fix(feishu): strip internal tool-trace banners from outbound text (openclaw#98705) fix(message): thread --limit through to CLI formatter and surface provider pagination hints (openclaw#99089) fix(voyage): close response body stream when batch output JSONL parsing throws (openclaw#98840) ... # Conflicts: # extensions/memory-wiki/package.json
…bar tap (openclaw#100502) * fix(ios): stop chat snapping back to bottom after system scroll-to-top * chore(i18n): sync native inventory after chat scroll fix
…bar tap (openclaw#100502) * fix(ios): stop chat snapping back to bottom after system scroll-to-top * chore(i18n): sync native inventory after chat scroll fix
What Problem This Solves
Fixes an issue where iPhone users on the Chat tab who trigger the system scroll-to-top (tapping the status-bar area at the top of the screen, right next to the gateway status pill) see the transcript start scrolling up and then immediately snap back to the bottom, making older messages unreachable through that gesture. Reported from a device where a tap at the top right of the chat screen produced "a quick scroll that immediately scrolls me back to the bottom."
Why This Change Was Made
Reader-managed chat scrolling (#98258) only releases the auto-follow target on the
.interactingscroll phase, i.e. finger drags. System-driven animated scrolls — the status-bar scroll-to-top, keyboard avoidance, accessibility scroll actions — report.animating, sofollowTargetstayed.latestand the next timeline tick (streaming delta or message refresh) re-issued a scroll to the bottom sentinel, cancelling the system scroll mid-flight. The view's own one-shot positioning always runs in a nil-animation transaction and therefore never reports.animating, so any.animatingphase is reader/system-driven and now releases the follow target. This matches Android, whoseChatReaderScrollControlleralready releases the follow for any scroll it did not apply itself (isScrollInProgressgated byisApplyingScroll); iOS was the outlier.User Impact
Tapping the status bar to scroll to the top of the iOS chat transcript now works: the view stays where the system scroll put it, and the existing "Jump to latest" pill appears for returning to the live edge. The same shared view powers macOS web chat, which picks up the identical behavior for system-animated scrolls.
Evidence
ChatReaderScrollStateTestsprove.interacting/.animatingrelease the follow target while.idle/.tracking/.deceleratingkeep it.swift test --package-path apps/shared/OpenClawKit --parallel: 346 tests in 34 suites pass (rerun green on latestmainafter this change).swiftformat --lint --config config/swiftformatclean on both changed files.