openclaw - ✅(Solved) Fix [Bug]: Control UI webchat does not stay pinned to bottom during streaming output [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#76966Fetched 2026-05-04 04:59:56
View on GitHub
Comments
1
Participants
2
Timeline
2
Reactions
2
Author
Timeline (top)
commented ×1cross-referenced ×1

Fix Action

Fixed

PR fix notes

PR #76991: fix(ui): prevent programmatic scrollTo from flipping chatUserNearBott…

Description (problem / solution / changelog)

Summary

  • Problem: During streaming assistant responses in Control UI/webchat, calling scrollTo() to pin the viewport to the bottom fires a synchronous scroll event. Because new content has grown scrollHeight between the scrollTo call and the event, handleChatScroll computes a large distanceFromBottom (≥ 450 px) and flips chatUserNearBottom to false, breaking the auto-scroll loop for all subsequent streaming tokens.
  • Why it matters: Every user running streaming responses in the webchat could see the viewport drift upward mid-generation, forcing a manual scroll-down while the assistant is still responding.
  • What changed: A chatIsProgrammaticScroll boolean flag is set on the host before each programmatic scrollTo/scrollTop write and cleared via requestAnimationFrame after. handleChatScroll returns early when the flag is set, so our own scroll events cannot corrupt the near-bottom state.
  • What did NOT change: Threshold values, user-initiated scroll detection, force semantics, logs scroll logic, or any non-scroll UI behavior are untouched.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause (if applicable)

  • Root cause: scheduleChatScroll calls target.scrollTo({top: scrollHeight}) to pin the viewport. This fires a scroll event that handleChatScroll handles. By the time that event fires, streamed content has already grown scrollHeight, so distanceFromBottom = newScrollHeight - oldScrollTop - clientHeight exceeds NEAR_BOTTOM_THRESHOLD (450 px). handleChatScroll then sets chatUserNearBottom = false, and all subsequent streaming ticks see shouldStick = false — auto-scroll breaks.
  • Missing detection / guardrail: No distinction between scroll events caused by the user versus those caused by our own scrollTo calls. handleChatScroll treated both identically.
  • Contributing context: The bug is intermittent because it depends on how fast content grows between the scrollTo call and the scroll event dispatch — slower models or short responses may not trigger it.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: ui/src/ui/app-scroll.test.ts
  • Scenario the test should lock in: When chatIsProgrammaticScroll is true, calling handleChatScroll with a scroll event whose distanceFromBottom exceeds the threshold must NOT flip chatUserNearBottom to false.
  • Why this is the smallest reliable guardrail: Pure unit test, no DOM rendering needed — just the flag + event handler interaction.
  • Existing test that already covers this: None before this PR.
  • If no new test is added, why not: N/A — three new tests added in the "programmatic scroll guard" suite (16 total, all passing).

User-visible / Behavior Changes

  • Streaming assistant responses in Control UI/webchat now stay pinned to the bottom of the viewport. Previously the chat could scroll upward mid-stream and require manual scroll-down.
  • User-initiated scroll-up while streaming still correctly pauses auto-scroll (behavior unchanged).

Diagram (if applicable)

Before:
[streaming token] -> scheduleChatScroll -> scrollTo(bottom)
                  -> scroll event fires -> handleChatScroll
                  -> distanceFromBottom > 450 (content grew)
                  -> chatUserNearBottom = false
                  -> next token: shouldStick = false -> viewport drifts up

After:
[streaming token] -> scheduleChatScroll -> chatIsProgrammaticScroll = true -> scrollTo(bottom)
                  -> scroll event fires -> handleChatScroll
                  -> chatIsProgrammaticScroll == true -> return early (no state change)
                  -> rAF fires -> chatIsProgrammaticScroll = false
                  -> next token: chatUserNearBottom still true -> stays pinned

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: Linux (Ubuntu, kernel 7.0.0-14-generic) / macOS
  • Runtime/container: npm global install, Node 24
  • Model/provider: OpenAI GPT-5.5
  • Integration/channel: Control UI / webchat
  • Relevant config: default

Steps

  1. Open Control UI webchat
  2. Send a prompt that produces a long streaming assistant response
  3. Watch the chat viewport while tokens stream in

Expected

  • Viewport stays pinned to the newest streamed token throughout generation

Actual (before fix)

  • Viewport scrolls upward intermittently; newest content is no longer visible without manually scrolling down

Evidence

  • Failing test/log before + passing after — app-scroll.test.ts: 16 tests pass after the fix; the new "programmatic scroll guard" suite directly exercises the previously missing guard. Before this PR, handleChatScroll with distanceFromBottom > 450 would unconditionally set chatUserNearBottom = false even during a programmatic scroll.

Human Verification (required)

  • Verified scenarios: Unit test suite for app-scroll.ts passes (16/16). Flag lifecycle (set before scroll, cleared after rAF) verified in "scheduleChatScroll sets chatIsProgrammaticScroll…" test.
  • Edge cases checked: Flag already cleared before user scroll — real user scroll-up correctly flips chatUserNearBottom to false (verified in third new test). resetChatScroll also clears the flag.
  • What you did not verify: Live browser smoke against a running gateway with a real streaming response.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: requestAnimationFrame fires before the browser dispatches the scroll event in some edge cases (e.g. very fast rAF in a headless environment), clearing the flag too early.
    • Mitigation: In that case the behavior degrades to the pre-fix behavior for that single tick — the retry path in scheduleChatScroll (120 ms timeout) re-scrolls and sets the flag again, recovering on the very next cycle.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • ui/src/ui/app-scroll.test.ts (modified, +69/-0)
  • ui/src/ui/app-scroll.ts (modified, +16/-0)
  • ui/src/ui/app.ts (modified, +1/-0)

Code Example

Observed by user in Control UI/webchat: "keep scrolled to bottom when outputting stream to chat (it scrolls up sometimes)".
RAW_BUFFERClick to expand / collapse

Bug type: Behavior bug (incorrect output/state without crash) Beta release blocker: No

Summary: Control UI/webchat does not consistently stay scrolled to the bottom while assistant output streams, causing the transcript to scroll upward during generation.

Steps to reproduce:

  1. Use OpenClaw Control UI/webchat.
  2. Send a prompt that produces a streaming assistant response.
  3. Watch the chat viewport while output is streaming.
  4. In affected cases, the viewport scrolls upward instead of staying pinned to the newest streamed content.

Expected behavior: When the user is already at or near the bottom of the chat, streaming output should keep the viewport pinned to the bottom/newest content until the user manually scrolls away.

Actual behavior: During streaming output, the chat sometimes scrolls upward and the newest content is no longer visible without manually scrolling back down.

OpenClaw version: OpenClaw 2026.4.26 (be8c246)

Operating system: Linux Linux 7.0.0-14-generic #14-Ubuntu SMP PREEMPT_DYNAMIC Mon Apr 13 11:09:53 UTC 2026 x86_64 GNU/Linux

Install method: npm global

Model: openai/gpt-5.5

Provider / routing chain: OpenClaw Control UI/webchat -> local OpenClaw gateway -> OpenAI GPT-5.5

Additional provider/model setup details: The observed symptom is in Control UI/webchat streaming/rendering. NOT_ENOUGH_INFO for whether it correlates with markdown blocks, tool output, message edits, images, or viewport size.

Logs, screenshots, and evidence:

Observed by user in Control UI/webchat: "keep scrolled to bottom when outputting stream to chat (it scrolls up sometimes)".

Impact and severity: Affected: Control UI/webchat users reading streaming assistant responses. Severity: Annoying / usability issue. Frequency: Intermittent based on current observation. Consequence: Users lose the newest streamed output and need to manually scroll back down while the assistant is responding.

Additional information: A common expected behavior is: auto-scroll only while the user is already at/near the bottom; pause auto-scroll if the user intentionally scrolls up.

extent analysis

TL;DR

Implement a consistent auto-scrolling mechanism in the Control UI/webchat to keep the viewport pinned to the bottom during assistant output streaming.

Guidance

  • Review the current scrolling logic to identify why it sometimes fails to keep the viewport at the bottom during streaming output.
  • Verify if the issue is related to the streaming rate, output size, or user interactions (e.g., scrolling up) that might interrupt the auto-scrolling behavior.
  • Consider adding a check to ensure auto-scrolling only occurs when the user is at or near the bottom of the chat viewport.
  • Investigate potential conflicts with other UI elements, such as markdown blocks or images, that might affect the scrolling behavior.

Example

No code snippet is provided due to the lack of specific implementation details in the issue.

Notes

The issue's intermittent nature and limited information about the scrolling logic make it challenging to provide a definitive solution. Further investigation and debugging are necessary to determine the root cause.

Recommendation

Apply a workaround by modifying the scrolling logic to prioritize keeping the viewport at the bottom during streaming output, while also considering user interactions that might intentionally interrupt auto-scrolling.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Bug]: Control UI webchat does not stay pinned to bottom during streaming output [1 pull requests, 1 comments, 2 participants]