@@ -4,7 +4,7 @@ import { ChatMessage } from '@/components/routes/chat/chat-message'
44import { SharedAccordion } from '@/components/shared/shared-accordion'
55import { ShortMessage } from '@/components/shared/short-message'
66import { Separator } from '@/components/ui/separator'
7- import { useScroll } from '@/lib/hooks/use-scroll'
7+ import { useMBScroll } from '@/lib/hooks/use-mb -scroll'
88import { useThread } from '@/lib/hooks/use-thread'
99import { cn , createMessagePairs } from '@/lib/utils'
1010import type { Message } from 'ai'
@@ -22,7 +22,6 @@ export interface ChatList {
2222 chatTitleClass ?: string
2323 chatArrowClass ?: string
2424 containerRef ?: React . RefObject < HTMLDivElement >
25- isNearBottom ?: boolean
2625 isLoadingMessages ?: boolean
2726 sendMessageFn ?: ( message : string ) => void
2827}
@@ -40,33 +39,42 @@ export function ChatList({
4039 chatContentClass,
4140 chatTitleClass,
4241 chatArrowClass,
43- containerRef,
44- sendMessageFn,
45- isNearBottom,
42+ containerRef : externalContainerRef ,
43+ sendMessageFn
4644} : ChatList ) {
4745 const [ pairs , setPairs ] = React . useState < MessagePair [ ] > ( [ ] )
48- const [ previousConversationPairs , setPreviousConversationPairs ] = React . useState < MessagePair [ ] > ( [ ] )
46+ const [ previousConversationPairs , setPreviousConversationPairs ] =
47+ React . useState < MessagePair [ ] > ( [ ] )
4948 const { isNewResponse, activeThread } = useThread ( )
50- const localContainerRef = useRef < HTMLDivElement > ( null )
51- const effectiveContainerRef = containerRef || localContainerRef
52- const chatMessages = ( messages || activeThread ?. messages || [ ] )
53- . sort ( ( a , b ) => new Date ( a . createdAt ) . getTime ( ) - new Date ( b . createdAt ) . getTime ( ) )
54- const previousChatMessages = ( activeThread ?. thread ?. messages || [ ] )
55- . sort ( ( a , b ) => new Date ( a . createdAt ) . getTime ( ) - new Date ( b . createdAt ) . getTime ( ) )
49+ const chatListRef = useRef < HTMLDivElement > ( null )
50+ const messageContainerRef = useRef < HTMLDivElement > ( null )
5651
57- useScroll ( {
52+ //? Uses the external ref if provided, otherwise it uses our internal refs
53+ const effectiveContainerRef = externalContainerRef || chatListRef
54+ const effectiveThreadRef = messageContainerRef
55+
56+ const chatMessages = ( messages || activeThread ?. messages || [ ] ) . sort (
57+ ( a , b ) => new Date ( a . createdAt ) . getTime ( ) - new Date ( b . createdAt ) . getTime ( )
58+ )
59+ const previousChatMessages = ( activeThread ?. thread ?. messages || [ ] ) . sort (
60+ ( a , b ) => new Date ( a . createdAt ) . getTime ( ) - new Date ( b . createdAt ) . getTime ( )
61+ )
62+
63+ const { isNearBottom } = useMBScroll ( {
5864 containerRef : effectiveContainerRef ,
59- threadRef : effectiveContainerRef ,
65+ threadRef : effectiveThreadRef ,
6066 isNewContent : isNewResponse ,
6167 hasMore : false ,
6268 isLast : true ,
6369 loading : isLoadingMessages ,
64- loadMore : ( ) => { }
70+ loadMore : ( ) => { }
6571 } )
6672
6773 useEffect ( ( ) => {
6874 if ( chatMessages ?. length ) {
69- const prePairs : MessagePair [ ] = createMessagePairs ( chatMessages ) as MessagePair [ ]
75+ const prePairs : MessagePair [ ] = createMessagePairs (
76+ chatMessages
77+ ) as MessagePair [ ]
7078 setPairs ( prevPairs => {
7179 if ( ! isEqual ( prevPairs , prePairs ) ) {
7280 return prePairs
@@ -76,10 +84,12 @@ export function ChatList({
7684 }
7785 } , [ chatMessages ] )
7886
79- // biome-ignore lint/correctness/useExhaustiveDependencies: adding functions to array dep is not needed
87+ // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
8088 useEffect ( ( ) => {
8189 if ( previousChatMessages ?. length ) {
82- const prePairs : MessagePair [ ] = createMessagePairs ( previousChatMessages ) as MessagePair [ ]
90+ const prePairs : MessagePair [ ] = createMessagePairs (
91+ previousChatMessages
92+ ) as MessagePair [ ]
8393 setPreviousConversationPairs ( prevPairs => {
8494 if ( ! isEqual ( prevPairs , prePairs ) ) {
8595 return prePairs
@@ -98,20 +108,22 @@ export function ChatList({
98108 < div
99109 ref = { effectiveContainerRef }
100110 className = { cn (
101- 'relative max-w-3xl px-4 mx-auto' ,
111+ 'relative max-w-3xl px-4 mx-auto overflow-auto scrollbar-thin ' ,
102112 className ,
103113 { 'flex flex-col gap-3' : isThread }
104114 ) }
105115 >
106- < MessagePairs
107- pairs = { pairs }
108- previousPairs = { previousConversationPairs }
109- isThread = { isThread }
110- chatTitleClass = { chatTitleClass }
111- chatArrowClass = { chatArrowClass }
112- chatContentClass = { chatContentClass }
113- sendMessageFn = { sendMessageFn }
114- />
116+ < div ref = { effectiveThreadRef } className = "min-h-full" >
117+ < MessagePairs
118+ pairs = { pairs }
119+ previousPairs = { previousConversationPairs }
120+ isThread = { isThread }
121+ chatTitleClass = { chatTitleClass }
122+ chatArrowClass = { chatArrowClass }
123+ chatContentClass = { chatContentClass }
124+ sendMessageFn = { sendMessageFn }
125+ />
126+ </ div >
115127 </ div >
116128 )
117129}
@@ -123,7 +135,7 @@ function MessagePairs({
123135 chatTitleClass,
124136 chatArrowClass,
125137 chatContentClass,
126- sendMessageFn,
138+ sendMessageFn
127139} : {
128140 pairs : MessagePair [ ]
129141 previousPairs : MessagePair [ ]
@@ -153,8 +165,8 @@ function MessagePairs({
153165 sendMessageFn = { sendMessageFn }
154166 />
155167 ) ) }
156- { ( previousPairs . length > 0 && pairs . length > 0 ) && (
157- < Separator className = "relative mt-6 -bottom-1.5 h-1.5 z-[2] rounded-sm bg-iron dark:bg-mirage" />
168+ { previousPairs . length > 0 && pairs . length > 0 && (
169+ < Separator className = "relative mt-6 -bottom-1.5 h-1.5 z-[2] rounded-sm bg-iron dark:bg-mirage" />
158170 ) }
159171 { pairs . map ( ( pair : MessagePair , key : number , pairsArray ) => (
160172 < MessagePairAccordion
@@ -174,7 +186,15 @@ function MessagePairs({
174186 )
175187}
176188
177- export function MessagePairAccordion ( { pair, isThread, index, arrayLength, isNewResponse, type, ...props } : {
189+ export function MessagePairAccordion ( {
190+ pair,
191+ isThread,
192+ index,
193+ arrayLength,
194+ isNewResponse,
195+ type,
196+ ...props
197+ } : {
178198 pair : MessagePair
179199 isThread : boolean
180200 index : number
@@ -191,20 +211,27 @@ export function MessagePairAccordion({ pair, isThread, index, arrayLength, isNew
191211 return (
192212 < SharedAccordion
193213 key = { `${ pair . userMessage . createdAt } -${ pair . chatGptMessage [ 0 ] ?. id ?? 'pending' } ` }
194- defaultState = { index === 0 || index === arrayLength - 1 || ( index === arrayLength - 2 && isNewResponse ) }
214+ defaultState = {
215+ index === 0 ||
216+ index === arrayLength - 1 ||
217+ ( index === arrayLength - 2 && isNewResponse )
218+ }
195219 className = { cn (
196220 { relative : isThread } ,
197- // Add subtle background tint and left border for previous messages
198- isPrevious && 'bg-accent/25 rounded-[8px] border-l-accent/20' ,
221+ // Adds subtle background tint and left border for previous messages
222+ isPrevious && 'bg-accent/25 rounded-[8px] border-l-accent/20'
199223 ) }
200224 triggerClass = { cn (
201225 'py-[0.4375rem]' ,
202226 {
203- 'sticky top-0 md:-top-10 z-[1] px-3 [&[data-state=open]]:rounded-t-[8px]' : isThread ,
227+ 'sticky top-0 md:-top-10 z-[1] px-3 [&[data-state=open]]:rounded-t-[8px]' :
228+ isThread ,
204229 'px-[calc(32px-0.25rem)]' : ! isThread ,
205- 'hidden' : ! isThread && index === 0 , // Style differences for previous vs current messages
206- 'dark:bg-[#1d283a9a] bg-iron !border-l-[transparent] [&[data-state=open]]:!bg-gray-400/50 dark:[&[data-state=open]]:!bg-mirage' : ! isPrevious ,
207- 'bg-accent/15 dark:bg-accent/15 hover:bg-accent/30 hover:dark:bg-accent/30 border-l-accent/20 dark:border-l-accent/20 [&[data-state=open]]:!bg-accent/25 dark:[&[data-state=open]]:!bg-accent/25' : isPrevious ,
230+ hidden : ! isThread && index === 0 , // Style differences for previous vs current messages
231+ 'dark:bg-[#1d283a9a] bg-iron !border-l-[transparent] [&[data-state=open]]:!bg-gray-400/50 dark:[&[data-state=open]]:!bg-mirage' :
232+ ! isPrevious ,
233+ 'bg-accent/15 dark:bg-accent/15 hover:bg-accent/30 hover:dark:bg-accent/30 border-l-accent/20 dark:border-l-accent/20 [&[data-state=open]]:!bg-accent/25 dark:[&[data-state=open]]:!bg-accent/25' :
234+ isPrevious
208235 } ,
209236 props . chatTitleClass
210237 ) }
@@ -219,14 +246,13 @@ export function MessagePairAccordion({ pair, isThread, index, arrayLength, isNew
219246 variant = "chat"
220247 >
221248 { /* Thread Title with indicator for previous messages */ }
222- { ! isThread && index === 0 ? '' : (
249+ { ! isThread && index === 0 ? (
250+ ''
251+ ) : (
223252 < div
224- className = { cn (
225- 'flex items-start gap-2' ,
226- {
227- '[&_div]:text-sm' : isPrevious ,
228- }
229- ) }
253+ className = { cn ( 'flex items-start gap-2' , {
254+ '[&_div]:text-sm' : isPrevious
255+ } ) }
230256 >
231257 < ChatMessage actionRequired = { false } message = { pair . userMessage } />
232258 </ div >
@@ -238,17 +264,21 @@ export function MessagePairAccordion({ pair, isThread, index, arrayLength, isNew
238264 < div className = "flex-1 px-1 space-y-2 overflow-hidden text-left" >
239265 < ShortMessage content = { pair . chatGptMessage [ 0 ] ?. content } />
240266 </ div >
241- ) : '' }
267+ ) : (
268+ ''
269+ ) }
242270 </ div >
243- ) : < > </ > }
271+ ) : (
272+ < > </ >
273+ ) }
244274
245275 { /* Thread Content */ }
246276 < div
247277 className = { cn (
248278 'mx-4 md:mx-[46px] px-1 py-4 border-transparent dark:border-x-mirage border-x-gray-300 border h-full' ,
249- {
279+ {
250280 '!border-[transparent]' : ! isThread && index === 0 ,
251- '[&>div>div>div_*]:!text-xs' : isPrevious ,
281+ '[&>div>div>div_*]:!text-xs' : isPrevious
252282 } ,
253283 props . chatContentClass
254284 ) }
@@ -258,26 +288,32 @@ export function MessagePairAccordion({ pair, isThread, index, arrayLength, isNew
258288 < span className = "absolute top-1 -left-5 px-1.5 py-0.5 text-[10px] font-medium rounded-md bg-accent text-accent-foreground" >
259289 Previous Thread
260290 </ span >
261- < div className = "opacity-50 pb-3 overflow-hidden text-sm mt-4" >
262- Continued from < b > “{ pair . userMessage . content . trim ( ) } ”</ b > thread{ activeThread ?. thread ?. user ?. username ? `, by ${ activeThread ?. thread ?. user ?. username } .` : '.' }
291+ < div className = "pb-3 mt-4 overflow-hidden text-sm opacity-50" >
292+ Continued from{ ' ' }
293+ < b > “{ pair . userMessage . content . trim ( ) } ”</ b > thread
294+ { activeThread ?. thread ?. user ?. username
295+ ? `, by ${ activeThread ?. thread ?. user ?. username } .`
296+ : '.' }
263297 </ div >
264298 </ >
265- ) : '' }
299+ ) : (
300+ ''
301+ ) }
266302 < ChatLoadingState />
267303 { pair . chatGptMessage . length > 0
268- ? pair . chatGptMessage . map ( ( message ) => (
269- < ChatMessage
270- key = { message . id }
271- actionRequired = { false }
272- message = { message }
273- sendMessageFromResponse = { props . sendMessageFn }
274- />
275- ) )
304+ ? pair . chatGptMessage . map ( message => (
305+ < ChatMessage
306+ key = { message . id }
307+ actionRequired = { false }
308+ message = { message }
309+ sendMessageFromResponse = { props . sendMessageFn }
310+ />
311+ ) )
276312 : '' }
277313 </ div >
278314 </ SharedAccordion >
279- ) ;
280- } ;
315+ )
316+ }
281317
282318export function ChatLoadingState ( ) {
283319 const { activeTool, loadingState } = useThread ( )
@@ -295,7 +331,7 @@ export function ChatLoadingState() {
295331 switch ( activeTool ?. toolName ) {
296332 case 'webSearch' :
297333 return (
298- < div className = ' flex items-center w-full h-20 gap-4 opacity-65' >
334+ < div className = " flex items-center w-full h-20 gap-4 opacity-65" >
299335 < GlobeIcon className = "relative size-6 animate-bounce top-2" />
300336 < p className = "flex flex-col gap-1 leading-none" >
301337 < span >
@@ -323,4 +359,4 @@ export function ChatLoadingState() {
323359 </ div >
324360 )
325361 }
326- }
362+ }
0 commit comments