fix(chat): smooth scrollbar behavior during AI streaming output#92
Conversation
📝 WalkthroughWalkthroughThis pull request introduces a new Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related issues
Possibly related PRs
Comment |
There was a problem hiding this comment.
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)
📒 Files selected for processing (3)
services/platform/app/(app)/dashboard/[id]/chat/components/chat-interface.tsxservices/platform/app/(app)/globals.cssservices/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.tsxservices/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. Avoidany, andunknownwhenever possible
Files:
services/platform/app/(app)/dashboard/[id]/chat/components/chat-interface.tsxservices/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 withwithOptimisticUpdateforuseMutation. If you decide to NOT add a optimistic update you need to provide a good reason why and comment the hook
USEuseMemo,useCallbackandmemothe right moment
DO NOT overuseuseEffect
USEcvaif a component has multiple variants
AVOIDrouter.refresh()
CONSIDER TO preload queries withpreloadQueryandusePreloadedQueryin React
ALWAYS CONSIDER semantic HTML elements (<button>,<nav>,<main>,<header>,<footer>,<article>,<section>)
ALWAYS provide text alternatives for non-text content (altfor images,aria-labelfor icon buttons)
ENSURE all interactive elements are keyboard accessible and have visible focus states
USE proper heading hierarchy (h1→h2→h3), never skip heading levels
ALWAYS associate form labels with inputs usinghtmlForor wrapping
PROVIDE clear error messages that identify the field and describe how to fix the issue
AVOID using color alone to convey information
USEaria-liveregions 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: noneproperty 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 inuseAutoScroll.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: trueis 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 duringisLoading(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
contentRefon 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.
Summary by CodeRabbit
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.