yangdx
commited on
Commit
·
f6989e6
1
Parent(s):
29b36fa
Improve scrolling logic
Browse files
lightrag_webui/src/features/RetrievalTesting.tsx
CHANGED
@@ -17,6 +17,10 @@ export default function RetrievalTesting() {
|
|
17 |
)
|
18 |
const [inputValue, setInputValue] = useState('')
|
19 |
const [isLoading, setIsLoading] = useState(false)
|
|
|
|
|
|
|
|
|
20 |
const messagesEndRef = useRef<HTMLDivElement>(null)
|
21 |
const messagesContainerRef = useRef<HTMLDivElement>(null)
|
22 |
|
@@ -61,6 +65,11 @@ export default function RetrievalTesting() {
|
|
61 |
// Add messages to chatbox
|
62 |
setMessages([...prevMessages, userMessage, assistantMessage])
|
63 |
|
|
|
|
|
|
|
|
|
|
|
64 |
// Force scroll to bottom after messages are rendered
|
65 |
setTimeout(() => {
|
66 |
scrollToBottom(true)
|
@@ -72,6 +81,17 @@ export default function RetrievalTesting() {
|
|
72 |
|
73 |
// Create a function to update the assistant's message
|
74 |
const updateAssistantMessage = (chunk: string, isError?: boolean) => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
assistantMessage.content += chunk
|
76 |
setMessages((prev) => {
|
77 |
const newMessages = [...prev]
|
@@ -82,8 +102,20 @@ export default function RetrievalTesting() {
|
|
82 |
}
|
83 |
return newMessages
|
84 |
})
|
85 |
-
|
86 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
}
|
88 |
|
89 |
// Prepare query parameters
|
@@ -128,6 +160,28 @@ export default function RetrievalTesting() {
|
|
128 |
[inputValue, isLoading, messages, setMessages, t, scrollToBottom]
|
129 |
)
|
130 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
131 |
const debouncedMessages = useDebounce(messages, 100)
|
132 |
useEffect(() => scrollToBottom(false), [debouncedMessages, scrollToBottom])
|
133 |
|
|
|
17 |
)
|
18 |
const [inputValue, setInputValue] = useState('')
|
19 |
const [isLoading, setIsLoading] = useState(false)
|
20 |
+
// Reference to track if we should follow scroll during streaming (using ref for synchronous updates)
|
21 |
+
const shouldFollowScrollRef = useRef(true)
|
22 |
+
// Reference to track if this is the first chunk of a streaming response
|
23 |
+
const isFirstChunkRef = useRef(true)
|
24 |
const messagesEndRef = useRef<HTMLDivElement>(null)
|
25 |
const messagesContainerRef = useRef<HTMLDivElement>(null)
|
26 |
|
|
|
65 |
// Add messages to chatbox
|
66 |
setMessages([...prevMessages, userMessage, assistantMessage])
|
67 |
|
68 |
+
// Reset first chunk flag for new streaming response
|
69 |
+
isFirstChunkRef.current = true
|
70 |
+
// Enable follow scroll for new query
|
71 |
+
shouldFollowScrollRef.current = true
|
72 |
+
|
73 |
// Force scroll to bottom after messages are rendered
|
74 |
setTimeout(() => {
|
75 |
scrollToBottom(true)
|
|
|
81 |
|
82 |
// Create a function to update the assistant's message
|
83 |
const updateAssistantMessage = (chunk: string, isError?: boolean) => {
|
84 |
+
// Check if this is the first chunk of the streaming response
|
85 |
+
if (isFirstChunkRef.current) {
|
86 |
+
// Determine scroll behavior based on initial position
|
87 |
+
shouldFollowScrollRef.current = isNearBottom();
|
88 |
+
isFirstChunkRef.current = false;
|
89 |
+
}
|
90 |
+
|
91 |
+
// Save current scroll position before updating content
|
92 |
+
const container = messagesContainerRef.current;
|
93 |
+
const currentScrollPosition = container ? container.scrollTop : 0;
|
94 |
+
|
95 |
assistantMessage.content += chunk
|
96 |
setMessages((prev) => {
|
97 |
const newMessages = [...prev]
|
|
|
102 |
}
|
103 |
return newMessages
|
104 |
})
|
105 |
+
|
106 |
+
// After updating content, check if we should scroll
|
107 |
+
// Use consistent scrolling behavior throughout the streaming response
|
108 |
+
if (shouldFollowScrollRef.current) {
|
109 |
+
scrollToBottom(true);
|
110 |
+
} else if (container) {
|
111 |
+
// If user was not near bottom, restore their scroll position
|
112 |
+
// This needs to be in a setTimeout to work after React updates the DOM
|
113 |
+
setTimeout(() => {
|
114 |
+
if (container) {
|
115 |
+
container.scrollTop = currentScrollPosition;
|
116 |
+
}
|
117 |
+
}, 0);
|
118 |
+
}
|
119 |
}
|
120 |
|
121 |
// Prepare query parameters
|
|
|
160 |
[inputValue, isLoading, messages, setMessages, t, scrollToBottom]
|
161 |
)
|
162 |
|
163 |
+
// Add scroll event listener to detect when user manually scrolls
|
164 |
+
useEffect(() => {
|
165 |
+
const container = messagesContainerRef.current;
|
166 |
+
if (!container) return;
|
167 |
+
|
168 |
+
const handleScroll = () => {
|
169 |
+
const isNearBottomNow = isNearBottom();
|
170 |
+
|
171 |
+
// If user scrolls away from bottom while in auto-scroll mode, disable it
|
172 |
+
if (shouldFollowScrollRef.current && !isNearBottomNow) {
|
173 |
+
shouldFollowScrollRef.current = false;
|
174 |
+
}
|
175 |
+
// If user scrolls back to bottom while not in auto-scroll mode, re-enable it
|
176 |
+
else if (!shouldFollowScrollRef.current && isNearBottomNow) {
|
177 |
+
shouldFollowScrollRef.current = true;
|
178 |
+
}
|
179 |
+
};
|
180 |
+
|
181 |
+
container.addEventListener('scroll', handleScroll);
|
182 |
+
return () => container.removeEventListener('scroll', handleScroll);
|
183 |
+
}, [isNearBottom]); // Remove shouldFollowScroll from dependencies since we're using ref now
|
184 |
+
|
185 |
const debouncedMessages = useDebounce(messages, 100)
|
186 |
useEffect(() => scrollToBottom(false), [debouncedMessages, scrollToBottom])
|
187 |
|