Skip to content

fix(chat): Prevent image loads and content reflows from accidentally disabling autoscroll#2966

Open
PEyacher wants to merge 1 commit into
Chainlit:mainfrom
PEyacher:frontend-image-scroll-fix
Open

fix(chat): Prevent image loads and content reflows from accidentally disabling autoscroll#2966
PEyacher wants to merge 1 commit into
Chainlit:mainfrom
PEyacher:frontend-image-scroll-fix

Conversation

@PEyacher

@PEyacher PEyacher commented Jun 18, 2026

Copy link
Copy Markdown

Fix for #2965.

Summary:

  • Improved chat autoscroll behavior so content reflows, image loading, and spacer recalculations do not accidentally disable autoscroll.
  • Added explicit wheel and touch handling to detect when the user intentionally scrolls upward, disabling autoscroll only in that case.
  • Added a ResizeObserver around chat content so spacer height and scroll button visibility stay in sync as message content changes size.
  • Updated last-user-message spacer handling to recalculate even when there is no user message, making autoscroll more reliable across different message streams.
  • Kept the scroll-to-bottom button mounted and toggled visibility via CSS for more stable UI behavior.

Summary by cubic

Prevents chat autoscroll from being turned off by image loads or layout shifts. Autoscroll now only disables when the user intentionally scrolls up.

  • Bug Fixes
    • Disable autoscroll only on upward wheel/touch gestures; re-enable at bottom.
    • Ignore content reflows and image loads when updating autoscroll state.
    • Use a ResizeObserver to keep spacer and button visibility in sync.
    • Keep the scroll-to-bottom button mounted; toggle visibility via CSS.
    • Recalculate spacer even without a last user message for reliable scrolling.

Written for commit b5341ef. Summary will update on new commits.

Review in cubic

…disabling chat autoscroll. Detect intentional upward scrolling via wheel and touch events
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. frontend Pertains to the frontend. labels Jun 18, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="frontend/src/components/chat/ScrollContainer.tsx">

<violation number="1" location="frontend/src/components/chat/ScrollContainer.tsx:49">
P2: Using `flushSync` in `syncScrollButtonVisibility` forces unnecessary synchronous React commits from high-frequency callbacks (`ResizeObserver`, wheel, touch handlers). The state only toggles a CSS class on a single element, and other call sites in this same file already update it directly without `flushSync`.</violation>

<violation number="2" location="frontend/src/components/chat/ScrollContainer.tsx:242">
P1: `handleScroll` no longer disables autoscroll when the user scrolls away from the bottom, relying solely on `wheel` and `touchmove` to detect upward intent. This misses scrollbar drag and keyboard scrolling (PageUp, Home, Space, arrow keys), which do not emit `wheel` events. When a user scrolls up via those methods, `autoScrollRef.current` stays `true`, and subsequent message arrivals or content resizes in `updateSpacerHeight` force-scroll back to the bottom, overriding the user's intent to read earlier messages.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

checkScrollEnd();
};

// onScroll event handler - ONLY re-enables autoscroll when at bottom

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: handleScroll no longer disables autoscroll when the user scrolls away from the bottom, relying solely on wheel and touchmove to detect upward intent. This misses scrollbar drag and keyboard scrolling (PageUp, Home, Space, arrow keys), which do not emit wheel events. When a user scrolls up via those methods, autoScrollRef.current stays true, and subsequent message arrivals or content resizes in updateSpacerHeight force-scroll back to the bottom, overriding the user's intent to read earlier messages.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/components/chat/ScrollContainer.tsx, line 242:

<comment>`handleScroll` no longer disables autoscroll when the user scrolls away from the bottom, relying solely on `wheel` and `touchmove` to detect upward intent. This misses scrollbar drag and keyboard scrolling (PageUp, Home, Space, arrow keys), which do not emit `wheel` events. When a user scrolls up via those methods, `autoScrollRef.current` stays `true`, and subsequent message arrivals or content resizes in `updateSpacerHeight` force-scroll back to the bottom, overriding the user's intent to read earlier messages.</comment>

<file context>
@@ -182,42 +239,105 @@ export default function ScrollContainer({
     checkScrollEnd();
   };
 
+  // onScroll event handler - ONLY re-enables autoscroll when at bottom
+  // Disabling autoscroll is handled by wheel/touch events to avoid false triggers from content reflow
   const handleScroll = () => {
</file context>


const syncScrollButtonVisibility = useCallback(() => {
const autoScrollDisabled = autoScrollRef ? !autoScrollRef.current : false;
flushSync(() => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Using flushSync in syncScrollButtonVisibility forces unnecessary synchronous React commits from high-frequency callbacks (ResizeObserver, wheel, touch handlers). The state only toggles a CSS class on a single element, and other call sites in this same file already update it directly without flushSync.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/components/chat/ScrollContainer.tsx, line 49:

<comment>Using `flushSync` in `syncScrollButtonVisibility` forces unnecessary synchronous React commits from high-frequency callbacks (`ResizeObserver`, wheel, touch handlers). The state only toggles a CSS class on a single element, and other call sites in this same file already update it directly without `flushSync`.</comment>

<file context>
@@ -28,12 +29,28 @@ export default function ScrollContainer({
+
+  const syncScrollButtonVisibility = useCallback(() => {
+    const autoScrollDisabled = autoScrollRef ? !autoScrollRef.current : false;
+    flushSync(() => {
+      setShowScrollButton(autoScrollDisabled);
+    });
</file context>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

frontend Pertains to the frontend. size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant