[PR #15562] app/ui: fix chat content scrolling past viewport during response loading #77501

Open
opened 2026-05-05 10:10:19 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/ollama/ollama/pull/15562
Author: @matteocelani
Created: 4/13/2026
Status: 🔄 Open

Base: mainHead: fix/chat-overscroll-15557


📝 Commits (3)

  • 2831c87 app/ui: fix chat content scrolling past viewport during response loading
  • ae0c4a7 Merge branch 'ollama:main' into fix/chat-overscroll-15557
  • a873f4f app/ui: fix autoscroll fighting user scroll during streaming

📊 Changes

2 files changed (+146 additions, -267 deletions)

View changed files

📝 app/ui/app/src/components/Chat.tsx (+1 -1)
📝 app/ui/app/src/hooks/useMessageAutoscroll.ts (+145 -266)

📄 Description

When sending a message, the loading indicator appears and the user can scroll down past the content, causing the conversation to disappear above the viewport.

The spacer calculation in useMessageAutoscroll did not account for all visible content (dots, download progress, padding), creating extra scrollable space. The hook also had unused refs, state that was not reset between interactions, and observers that were recreated on every message update. Simplified the hook to address these issues.

Changes:

  • app/ui/app/src/hooks/useMessageAutoscroll.ts: simplified spacer calculation and scroll logic
  • app/ui/app/src/components/Chat.tsx: added overflow-anchor: auto to the scroll container

How scroll works now:

  1. User sends a message — it scrolls to the top of the viewport. A spacer div fills the remaining space below so maxScroll lands exactly with the user message at the top.
  2. The response streams below the user message. The spacer shrinks as the response grows. Auto-scroll follows the tail.
  3. If the user scrolls up, auto-scroll pauses. If they return to the bottom (within 50px), it resumes.
  4. When the response fills the viewport the spacer reaches 0 and auto-scroll tracks naturally.
  5. After the response completes the spacer stays — the user message remains at the top until the next message or chat change.

What was fixed:

  • Spacer now uses container.scrollHeight - spacerInDOM for actual content height — accounts for dots, download, padding automatically
  • Removed unused scroll tracking refs (lastScrollHeightRef, lastScrollTopRef)
  • Replaced isActiveInteraction / hasSubmittedMessage useState pair with a single ref (avoids cascading re-renders)
  • Observers no longer recreated on every message update
  • Replaced double requestAnimationFrame with useLayoutEffect + single requestAnimationFrame so the scroll runs after React applies the spacer to the DOM
  • Removed 40% extra spacer height during streaming (containerHeight * 0.4) that amplified overscroll
  • getLastUserIndex depends on messages.length instead of messages — not recreated on every streaming token
  • paddingTop cached in a ref to avoid getComputedStyle reflow

Fixes #15557


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/ollama/ollama/pull/15562 **Author:** [@matteocelani](https://github.com/matteocelani) **Created:** 4/13/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `fix/chat-overscroll-15557` --- ### 📝 Commits (3) - [`2831c87`](https://github.com/ollama/ollama/commit/2831c87d20475be3fb6f603cc5f9caa8e3ed3f49) app/ui: fix chat content scrolling past viewport during response loading - [`ae0c4a7`](https://github.com/ollama/ollama/commit/ae0c4a7fe9f322c356b8cf0ab267ff7c8f180c6c) Merge branch 'ollama:main' into fix/chat-overscroll-15557 - [`a873f4f`](https://github.com/ollama/ollama/commit/a873f4f74f793f6c387d3e7b1d4e9127e476c82f) app/ui: fix autoscroll fighting user scroll during streaming ### 📊 Changes **2 files changed** (+146 additions, -267 deletions) <details> <summary>View changed files</summary> 📝 `app/ui/app/src/components/Chat.tsx` (+1 -1) 📝 `app/ui/app/src/hooks/useMessageAutoscroll.ts` (+145 -266) </details> ### 📄 Description When sending a message, the loading indicator appears and the user can scroll down past the content, causing the conversation to disappear above the viewport. The spacer calculation in `useMessageAutoscroll` did not account for all visible content (dots, download progress, padding), creating extra scrollable space. The hook also had unused refs, state that was not reset between interactions, and observers that were recreated on every message update. Simplified the hook to address these issues. Changes: - `app/ui/app/src/hooks/useMessageAutoscroll.ts`: simplified spacer calculation and scroll logic - `app/ui/app/src/components/Chat.tsx`: added `overflow-anchor: auto` to the scroll container **How scroll works now:** 1. User sends a message — it scrolls to the top of the viewport. A spacer div fills the remaining space below so `maxScroll` lands exactly with the user message at the top. 2. The response streams below the user message. The spacer shrinks as the response grows. Auto-scroll follows the tail. 3. If the user scrolls up, auto-scroll pauses. If they return to the bottom (within 50px), it resumes. 4. When the response fills the viewport the spacer reaches 0 and auto-scroll tracks naturally. 5. After the response completes the spacer stays — the user message remains at the top until the next message or chat change. **What was fixed:** - Spacer now uses `container.scrollHeight - spacerInDOM` for actual content height — accounts for dots, download, padding automatically - Removed unused scroll tracking refs (`lastScrollHeightRef`, `lastScrollTopRef`) - Replaced `isActiveInteraction` / `hasSubmittedMessage` useState pair with a single ref (avoids cascading re-renders) - Observers no longer recreated on every message update - Replaced double `requestAnimationFrame` with `useLayoutEffect` + single `requestAnimationFrame` so the scroll runs after React applies the spacer to the DOM - Removed 40% extra spacer height during streaming (`containerHeight * 0.4`) that amplified overscroll - `getLastUserIndex` depends on `messages.length` instead of `messages` — not recreated on every streaming token - `paddingTop` cached in a ref to avoid `getComputedStyle` reflow --- Fixes #15557 --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-05-05 10:10:19 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/ollama#77501