Skip to content

fix(chat): smooth scrollbar behavior during AI streaming output#92

Merged
Israeltheminer merged 2 commits into
mainfrom
update-ui
Jan 5, 2026
Merged

fix(chat): smooth scrollbar behavior during AI streaming output#92
Israeltheminer merged 2 commits into
mainfrom
update-ui

Conversation

@Israeltheminer

@Israeltheminer Israeltheminer commented Jan 5, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

Improvements

  • Enhanced automatic scrolling in the chat interface to intelligently follow new incoming messages, keeping you focused on the conversation
  • Fixed scroll jumping issues that occurred when streaming messages were being delivered
  • Refined the thinking animation display to appear only during message generation, improving visual clarity and user experience

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 5, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This pull request introduces a new useAutoScroll hook that manages intelligent auto-scrolling during streaming chat content and refactors the chat interface to use it. The hook leverages ResizeObserver to detect content growth and intelligently manages scroll state based on user interactions and programmatic scrolls. The chat interface component is updated to replace useThrottledScroll with the new hook, add per-thread initial scroll on load, and refine ThinkingAnimation visibility logic. A CSS update disables browser scroll anchoring on streaming text containers to prevent unwanted scroll jumps.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related issues

Possibly related PRs

  • talecorp/poc#238: Modifies chat-interface's auto-scroll and streaming/loading display logic, including ThinkingAnimation visibility conditions.
  • tale-project/poc2#420: Modifies chat-interface.tsx's auto-scroll and scroll-to-bottom behavior with similar scroll detection and state management patterns.
  • refactor(chat): enhance chat interface and message bubble components #21: Adjusts scroll handling in the same chat-interface.tsx component, including changes to scroll utility hook usage.

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

Fix all issues with AI Agents 🤖
In
@services/platform/app/(app)/dashboard/[id]/chat/components/chat-interface.tsx:
- Around line 552-579: The reset of hasScrolledOnLoadRef is redundant: remove
the inner conditional that resets it inside the main useEffect (delete the if
(!threadId) { hasScrolledOnLoadRef.current = false; } block) and rely on the
separate useEffect that resets on threadId changes; keep the scrolling logic in
the first useEffect and the dedicated reset useEffect ([threadId]) as-is.

In @services/platform/hooks/use-auto-scroll.ts:
- Around line 79-97: The double requestAnimationFrame in scrollToBottom is too
short for smooth scrolling and causes a race where the scroll handler may
disable auto-scroll; change scrollToBottom to set
isProgrammaticScrollRef.current = true and attach a one-time 'scrollend' event
listener on containerRef.current that clears isProgrammaticScrollRef.current and
removes itself when fired; if 'scrollend' isn't supported, fall back to a longer
timeout (e.g. ~400ms) to clear the flag; ensure you still set
autoScrollEnabledRef.current = true and properly clean up the listener/timeouts
to avoid leaks.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between c80250e and 4ee947d.

📒 Files selected for processing (3)
  • services/platform/app/(app)/dashboard/[id]/chat/components/chat-interface.tsx
  • services/platform/app/(app)/globals.css
  • services/platform/hooks/use-auto-scroll.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: ALL pages should be optimized for accessibility (Level AA)
ALWAYS put imports at the top and exports at the bottom. Keep them sorted correctly
PREFER named exports. AVOID default exports (only if needed)

Files:

  • services/platform/app/(app)/dashboard/[id]/chat/components/chat-interface.tsx
  • services/platform/hooks/use-auto-scroll.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: USE implicit typing whenever possible
DO NOT use type casting. Avoid any, and unknown whenever possible

Files:

  • services/platform/app/(app)/dashboard/[id]/chat/components/chat-interface.tsx
  • services/platform/hooks/use-auto-scroll.ts
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{tsx,jsx}: Do NOT hardcode text, use the translation hooks/functions instead for user-facing UI
CONSIDER ALWAYS TO add optimistic updates with withOptimisticUpdate for useMutation. If you decide to NOT add a optimistic update you need to provide a good reason why and comment the hook
USE useMemo, useCallback and memo the right moment
DO NOT overuse useEffect
USE cva if a component has multiple variants
AVOID router.refresh()
CONSIDER TO preload queries with preloadQuery and usePreloadedQuery in React
ALWAYS CONSIDER semantic HTML elements (<button>, <nav>, <main>, <header>, <footer>, <article>, <section>)
ALWAYS provide text alternatives for non-text content (alt for images, aria-label for icon buttons)
ENSURE all interactive elements are keyboard accessible and have visible focus states
USE proper heading hierarchy (h1h2h3), never skip heading levels
ALWAYS associate form labels with inputs using htmlFor or wrapping
PROVIDE clear error messages that identify the field and describe how to fix the issue
AVOID using color alone to convey information
USE aria-live regions for dynamic content updates

Files:

  • services/platform/app/(app)/dashboard/[id]/chat/components/chat-interface.tsx
🧠 Learnings (10)
📚 Learning: 2026-01-05T12:04:10.554Z
Learnt from: CR
Repo: tale-project/tale PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T12:04:10.554Z
Learning: Applies to **/*.{tsx,jsx} : USE `aria-live` regions for dynamic content updates

Applied to files:

  • services/platform/app/(app)/dashboard/[id]/chat/components/chat-interface.tsx
📚 Learning: 2025-11-30T12:29:39.745Z
Learnt from: CR
Repo: tale-project/poc2 PR: 0
File: .cursor/rules/workspace_rules.mdc:0-0
Timestamp: 2025-11-30T12:29:39.745Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use descriptive message as toast `title` (never generic 'Error'), with optional `description` only for additional context

Applied to files:

  • services/platform/app/(app)/dashboard/[id]/chat/components/chat-interface.tsx
📚 Learning: 2025-11-30T12:29:39.745Z
Learnt from: CR
Repo: tale-project/poc2 PR: 0
File: .cursor/rules/workspace_rules.mdc:0-0
Timestamp: 2025-11-30T12:29:39.745Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Implement error handling with try-catch blocks, check for result.error, and display descriptive toast messages with 'destructive' variant

Applied to files:

  • services/platform/app/(app)/dashboard/[id]/chat/components/chat-interface.tsx
📚 Learning: 2025-08-21T15:01:09.405Z
Learnt from: CR
Repo: talecorp/lanserhof PR: 0
File: .cursor/rules/core-rules.mdc:0-0
Timestamp: 2025-08-21T15:01:09.405Z
Learning: Applies to **/*.{tsx,jsx} : Use the required error handling pattern for async UI actions: try/catch with toast on error and result.error checks

Applied to files:

  • services/platform/app/(app)/dashboard/[id]/chat/components/chat-interface.tsx
📚 Learning: 2025-10-11T11:46:02.452Z
Learnt from: CR
Repo: talecorp/poc2 PR: 0
File: .cursorrules:0-0
Timestamp: 2025-10-11T11:46:02.452Z
Learning: Applies to **/*.{ts,tsx} : Follow the required error handling pattern with try/catch and user-facing toast on failure

Applied to files:

  • services/platform/app/(app)/dashboard/[id]/chat/components/chat-interface.tsx
📚 Learning: 2025-10-11T11:46:02.452Z
Learnt from: CR
Repo: talecorp/poc2 PR: 0
File: .cursorrules:0-0
Timestamp: 2025-10-11T11:46:02.452Z
Learning: Applies to **/*.{tsx,jsx} : Toast, error, and success messages must be English only

Applied to files:

  • services/platform/app/(app)/dashboard/[id]/chat/components/chat-interface.tsx
📚 Learning: 2025-07-19T15:29:09.401Z
Learnt from: CR
Repo: talecorp/poc PR: 0
File: .cursorrules:0-0
Timestamp: 2025-07-19T15:29:09.401Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use the provided error handling pattern with try/catch and toast notifications for errors.

Applied to files:

  • services/platform/app/(app)/dashboard/[id]/chat/components/chat-interface.tsx
📚 Learning: 2025-07-19T15:30:00.886Z
Learnt from: CR
Repo: talecorp/poc PR: 0
File: .cursor/rules/core-rules.mdc:0-0
Timestamp: 2025-07-19T15:30:00.886Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use the required error handling pattern: try/catch with toast error reporting and logging as shown in the provided example

Applied to files:

  • services/platform/app/(app)/dashboard/[id]/chat/components/chat-interface.tsx
📚 Learning: 2025-12-26T02:23:20.245Z
Learnt from: larryro
Repo: tale-project/tale PR: 35
File: services/platform/app/(app)/dashboard/[id]/chat/components/integration-approval-card.tsx:58-90
Timestamp: 2025-12-26T02:23:20.245Z
Learning: In services/platform/app/(app)/dashboard/[id]/chat/components/integration-approval-card.tsx, prefer reading the approving user from the authenticated session context rather than passing a userId from the frontend. The hardcoded 'user' string can be acceptable only as a temporary placeholder during the initial implementation until a user profile feature is added; plan to replace with proper user identity via session context or user service once backend supports it.

Applied to files:

  • services/platform/app/(app)/dashboard/[id]/chat/components/chat-interface.tsx
📚 Learning: 2025-12-30T06:21:13.183Z
Learnt from: larryro
Repo: tale-project/tale PR: 37
File: services/platform/convex/model/documents/validators.ts:89-102
Timestamp: 2025-12-30T06:21:13.183Z
Learning: Do not flag a missing trailing newline for TypeScript files in code reviews. POSIX text files should end with a trailing newline and Prettier (or your formatter) will enforce this. Treat the trailing newline as a non-issue in reviews for all TS files.

Applied to files:

  • services/platform/hooks/use-auto-scroll.ts
🧬 Code graph analysis (1)
services/platform/app/(app)/dashboard/[id]/chat/components/chat-interface.tsx (2)
services/platform/app/(app)/dashboard/[id]/chat/hooks/use-workflow-creation-approvals.ts (1)
  • useWorkflowCreationApprovals (23-46)
services/platform/hooks/use-auto-scroll.ts (1)
  • useAutoScroll (51-174)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build Docker Image
🔇 Additional comments (13)
services/platform/app/(app)/globals.css (1)

124-131: LGTM!

The overflow-anchor: none property correctly disables browser scroll anchoring, which is essential for the custom auto-scroll logic to work predictably. The comment clearly explains the purpose. This pairs well with the ResizeObserver-based approach in useAutoScroll.

services/platform/hooks/use-auto-scroll.ts (7)

5-34: LGTM!

Well-documented interfaces with clear JSDoc comments explaining each property's purpose.


51-63: LGTM!

Good use of refs for mutable state that shouldn't trigger re-renders. This pattern correctly avoids stale closure issues in the scroll and resize event handlers.


65-74: LGTM!

Correct calculation for determining scroll position relative to bottom. The threshold parameter provides flexibility for different use cases.


99-126: LGTM!

Clean implementation of user scroll intent detection. Using passive: true is correct for scroll performance, and the cleanup properly removes the listener.


128-157: LGTM!

Solid ResizeObserver implementation. Using behavior: 'instant' for streaming content is the right choice to avoid animation lag. The double condition (autoScrollEnabledRef && isAtBottom()) correctly respects user intent.


159-166: LGTM!

Correctly resets auto-scroll state when streaming begins, ensuring users don't miss content due to a previous manual scroll-away.


168-174: LGTM!

Clean return statement matching the declared interface. Named export follows the coding guidelines.

services/platform/app/(app)/dashboard/[id]/chat/components/chat-interface.tsx (5)

496-501: LGTM!

Good integration of useAutoScroll. Enabling it during isLoading (streaming state) correctly triggers auto-scroll behavior only when relevant.


539-550: LGTM!

Clean implementation for scroll button visibility. The passive listener is appropriate for scroll performance.


656-663: LGTM!

Good error handling pattern with descriptive messages and proper i18n fallback. Based on learnings, this aligns with the toast best practices.


672-681: LGTM!

Correct placement of contentRef on the messages container to enable content-aware auto-scrolling via ResizeObserver.


790-797: LGTM!

Good UX refinement. Hiding the thinking animation once text content begins streaming provides a cleaner transition from "thinking" to "responding" states.

Comment thread services/platform/hooks/use-auto-scroll.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant