fix: scroll AI chat to bottom on side panel reopen (#20413)
## Summary - Fix: when reopening the AI chat side panel, users landed at the top of the conversation and had to scroll down to find the latest messages - Root cause: the side panel fully unmounts on close ([SidePanelForDesktop.tsx](packages/twenty-front/src/modules/side-panel/components/SidePanelForDesktop.tsx) clears `shouldShowContent` after the close transition), so on reopen the scroll wrapper is recreated with `scrollTop = 0`. The existing initial-scroll-to-bottom only fires on thread change, but the displayed-thread atom outlives the unmount, so no thread change is detected on a remount and the scroll-to-bottom never runs - Fix: add a tiny `AgentChatScrollToBottomOnMountLayoutEffect` rendered inside the message list that calls `scrollAiChatToBottom()` directly in `useLayoutEffect`. Because the parent returns `null` when there are no messages, the mount only fires when there is content to scroll past ## Why direct scroll, not the existing flag `agentChatIsInitialScrollPendingOnThreadChangeState` is paired with a `MutationObserver` settle that only clears the flag once the subtree is quiet for 150 ms. During a live stream the message subtree mutates on every token, the settle resets indefinitely and `visibility: hidden` never lifts. The thread-change handler avoids this because it is gated on `!agentChatIsStreaming` ([AgentChatStreamSubscriptionEffect.tsx:78-79](packages/twenty-front/src/modules/ai/components/AgentChatStreamSubscriptionEffect.tsx)), but a mount can happen at any time, including mid-stream. Scrolling the DOM directly in `useLayoutEffect` runs synchronously between commit and paint, so the user sees the bottom on the first paint with no flash and no settle dependency. ## Tradeoff A user who scrolled up to read history and then closes/reopens the panel will land back at the bottom instead of where they were. Standard chat UX (Slack, ChatGPT, iMessage); preserving per-thread scroll position would need a new atom and is left out of scope. ## Test plan - [ ] Open AI chat with messages, close the side panel, reopen → lands at the bottom (latest messages visible) - [ ] Reopen the side panel **mid-stream** → lands at the bottom and continues to follow new tokens (chat is not hidden) - [ ] Switch between threads → existing thread-change scroll still works (no double-scroll, no regression) - [ ] Open AI chat with no messages → no flash, no errors - [ ] Rapidly close/reopen the panel a few times → each reopen lands at the bottom --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+10
@@ -0,0 +1,10 @@
|
||||
import { scrollAiChatToBottom } from '@/ai/utils/scrollAiChatToBottom';
|
||||
import { useLayoutEffect } from 'react';
|
||||
|
||||
export const AgentChatScrollToBottomOnMountLayoutEffect = () => {
|
||||
useLayoutEffect(() => {
|
||||
scrollAiChatToBottom();
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -3,6 +3,7 @@ import { AiChatLastMessageWithStreamingState } from '@/ai/components/AiChatLastM
|
||||
import { AiChatNonLastMessageIdsList } from '@/ai/components/AiChatNonLastMessageIdsList';
|
||||
import { AiChatScrollToBottomButton } from '@/ai/components/AiChatScrollToBottomButton';
|
||||
import { AgentChatScrollToBottomOnDisplayedThreadChangeLayoutEffect } from '@/ai/components/AgentChatScrollToBottomOnDisplayedThreadChangeLayoutEffect';
|
||||
import { AgentChatScrollToBottomOnMountLayoutEffect } from '@/ai/components/AgentChatScrollToBottomOnMountLayoutEffect';
|
||||
import { AI_CHAT_SCROLL_WRAPPER_ID } from '@/ai/constants/AiChatScrollWrapperId';
|
||||
import { agentChatHasMessageComponentSelector } from '@/ai/states/selectors/agentChatHasMessageComponentSelector';
|
||||
import { agentChatIsInitialScrollPendingOnThreadChangeState } from '@/ai/states/agentChatIsInitialScrollPendingOnThreadChangeState';
|
||||
@@ -49,6 +50,7 @@ export const AiChatTabMessageList = () => {
|
||||
<AiChatLastMessageWithStreamingState />
|
||||
<AiChatErrorUnderMessageList />
|
||||
<AgentChatScrollToBottomOnDisplayedThreadChangeLayoutEffect />
|
||||
<AgentChatScrollToBottomOnMountLayoutEffect />
|
||||
</ScrollWrapper>
|
||||
<AiChatScrollToBottomButton />
|
||||
</StyledScrollWrapperContainer>
|
||||
|
||||
Reference in New Issue
Block a user