Skip to content

Commit 609e9d2

Browse files
authored
feat: add feature flag for user message auto scroll (#1994)
1 parent e673ac2 commit 609e9d2

File tree

6 files changed

+49
-28
lines changed

6 files changed

+49
-28
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

77
## Unreleased
88

9+
### Added
10+
11+
- The user message auto scroll behavior is not a feature flag `config.features.user_message_autoscroll`
12+
913
### Fixed
1014

1115
- Submounting a Chainlit app to a FastAPI app with a root path should now work

backend/chainlit/config.py

+4
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@
8787
# Process and display mathematical expressions. This can clash with "$" characters in messages.
8888
latex = false
8989
90+
# Autoscroll new user messages at the top of the window
91+
user_message_autoscroll = true
92+
9093
# Automatically tag threads with the current chat profile (if a chat profile is used)
9194
auto_tag_thread = true
9295
@@ -216,6 +219,7 @@ class FeaturesSettings(DataClassJsonMixin):
216219
audio: Optional[AudioFeature] = Field(default_factory=AudioFeature)
217220
mcp: bool = False
218221
latex: bool = False
222+
user_message_autoscroll: bool = True
219223
unsafe_allow_html: bool = False
220224
auto_tag_thread: bool = True
221225
edit_message: bool = True

frontend/src/components/chat/ScrollContainer.tsx

+32-26
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ import { useChatMessages } from '@chainlit/react-client';
1313
import { Button } from '@/components/ui/button';
1414

1515
interface Props {
16+
autoScrollUserMessage?: boolean;
1617
autoScrollRef?: MutableRefObject<boolean>;
1718
children: React.ReactNode;
1819
className?: string;
1920
}
2021

2122
export default function ScrollContainer({
2223
autoScrollRef,
24+
autoScrollUserMessage,
2325
children,
2426
className
2527
}: Props) {
@@ -31,40 +33,42 @@ export default function ScrollContainer({
3133

3234
// Calculate and update spacer height
3335
const updateSpacerHeight = useCallback(() => {
34-
if (!ref.current || !lastUserMessageRef.current) return;
36+
if (!ref.current) return;
3537

36-
const containerHeight = ref.current.clientHeight;
37-
const lastMessageHeight = lastUserMessageRef.current.offsetHeight;
38+
if (autoScrollUserMessage && lastUserMessageRef.current) {
39+
const containerHeight = ref.current.clientHeight;
40+
const lastMessageHeight = lastUserMessageRef.current.offsetHeight;
3841

39-
// Calculate the height of all elements after the last user message
40-
let afterMessagesHeight = 0;
41-
let currentElement = lastUserMessageRef.current.nextElementSibling;
42+
// Calculate the height of all elements after the last user message
43+
let afterMessagesHeight = 0;
44+
let currentElement = lastUserMessageRef.current.nextElementSibling;
4245

43-
// Iterate through all siblings after the last user message
44-
while (currentElement && currentElement !== spacerRef.current) {
45-
afterMessagesHeight += (currentElement as HTMLElement).offsetHeight;
46-
currentElement = currentElement.nextElementSibling;
47-
}
46+
// Iterate through all siblings after the last user message
47+
while (currentElement && currentElement !== spacerRef.current) {
48+
afterMessagesHeight += (currentElement as HTMLElement).offsetHeight;
49+
currentElement = currentElement.nextElementSibling;
50+
}
4851

49-
// Position the last user message at the top with some padding
50-
// Subtract both the message height and the height of any messages after it
51-
const newSpacerHeight =
52-
containerHeight - lastMessageHeight - afterMessagesHeight - 32;
52+
// Position the last user message at the top with some padding
53+
// Subtract both the message height and the height of any messages after it
54+
const newSpacerHeight =
55+
containerHeight - lastMessageHeight - afterMessagesHeight - 32;
5356

54-
// Only set a positive spacer height
55-
if (spacerRef.current) {
56-
spacerRef.current.style.height = `${Math.max(0, newSpacerHeight)}px`;
57-
}
57+
// Only set a positive spacer height
58+
if (spacerRef.current) {
59+
spacerRef.current.style.height = `${Math.max(0, newSpacerHeight)}px`;
60+
}
5861

59-
// Scroll to position the message at the top
60-
if (afterMessagesHeight === 0) {
61-
scrollToPosition();
62-
} else if (autoScrollRef?.current) {
63-
if (ref.current) {
62+
// Scroll to position the message at the top
63+
if (afterMessagesHeight === 0) {
64+
scrollToPosition();
65+
} else if (autoScrollRef?.current) {
6466
ref.current.scrollTop = ref.current.scrollHeight;
6567
}
68+
} else if (autoScrollRef?.current) {
69+
ref.current.scrollTop = ref.current.scrollHeight;
6670
}
67-
}, [autoScrollRef]);
71+
}, [autoScrollUserMessage, autoScrollRef]);
6872

6973
// Find and set a ref to the last user message element
7074
useEffect(() => {
@@ -87,6 +91,8 @@ export default function ScrollContainer({
8791

8892
// Add window resize listener to update spacer height
8993
useEffect(() => {
94+
if (!autoScrollUserMessage) return;
95+
9096
const handleResize = () => {
9197
updateSpacerHeight();
9298
};
@@ -99,7 +105,7 @@ export default function ScrollContainer({
99105
return () => {
100106
window.removeEventListener('resize', handleResize);
101107
};
102-
}, [updateSpacerHeight]);
108+
}, [autoScrollUserMessage, updateSpacerHeight]);
103109

104110
// Check scroll position on mount
105111
useEffect(() => {

frontend/src/components/chat/index.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,10 @@ const Chat = () => {
202202
</div>
203203
) : null}
204204
<ErrorBoundary>
205-
<ScrollContainer autoScrollRef={autoScrollRef}>
205+
<ScrollContainer
206+
autoScrollUserMessage={config?.features?.user_message_autoscroll}
207+
autoScrollRef={autoScrollRef}
208+
>
206209
<div
207210
className="flex flex-col mx-auto w-full flex-grow p-4"
208211
style={{

libs/copilot/src/chat/body.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,10 @@ const Chat = () => {
171171
) : null}
172172
<ChatSettingsModal />
173173
<ErrorBoundary>
174-
<ScrollContainer autoScrollRef={autoScrollRef}>
174+
<ScrollContainer
175+
autoScrollUserMessage={config?.features?.user_message_autoscroll}
176+
autoScrollRef={autoScrollRef}
177+
>
175178
<div
176179
className="flex flex-col mx-auto w-full flex-grow px-4 pt-4"
177180
style={{

libs/react-client/src/types/config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export interface IChainlitConfig {
5050
};
5151
audio: IAudioConfig;
5252
unsafe_allow_html?: boolean;
53+
user_message_autoscroll?: boolean;
5354
latex?: boolean;
5455
edit_message?: boolean;
5556
mcp?: boolean;

0 commit comments

Comments
 (0)