11'use client' ;
22
3- import { useState , useRef , useEffect } from 'react' ;
4- import { TranscriptSegment } from '@/src/types/memory.types' ;
3+ import { useState , useRef , useEffect , useMemo } from 'react' ;
4+ import { TranscriptSegment , Person } from '@/src/types/memory.types' ;
55import chatWithMemory from '@/src/actions/memories/chat-with-memory' ;
66import { Send , UserCircle , Message , ArrowDown } from 'iconoir-react' ;
77import Markdown from 'markdown-to-jsx' ;
88
99interface ChatProps {
1010 transcript : TranscriptSegment [ ] ;
11+ people ?: Person [ ] ;
1112 onClearChatRef ?: ( clearFn : ( ) => void ) => void ;
1213 onMessagesChange ?: ( hasMessages : boolean ) => void ;
1314}
@@ -17,7 +18,12 @@ interface ChatMessage {
1718 content : string ;
1819}
1920
20- export default function Chat ( { transcript, onClearChatRef, onMessagesChange } : ChatProps ) {
21+ export default function Chat ( {
22+ transcript,
23+ people,
24+ onClearChatRef,
25+ onMessagesChange,
26+ } : ChatProps ) {
2127 const [ messages , setMessages ] = useState < ChatMessage [ ] > ( [ ] ) ;
2228 const [ input , setInput ] = useState ( '' ) ;
2329 const [ isLoading , setIsLoading ] = useState ( false ) ;
@@ -50,13 +56,25 @@ export default function Chat({ transcript, onClearChatRef, onMessagesChange }: C
5056 } ;
5157 } , [ ] ) ;
5258
53- // Convert transcript segments to a readable string
54- const transcriptText = transcript
55- . map ( ( segment ) => {
56- const speaker = segment . is_user ? 'Owner' : `Speaker ${ segment . speaker_id } ` ;
57- return `${ speaker } : ${ segment . text } ` ;
58- } )
59- . join ( '\n\n' ) ;
59+ // Convert transcript segments to a readable string, resolving person names
60+ const transcriptText = useMemo (
61+ ( ) =>
62+ transcript
63+ . map ( ( segment ) => {
64+ let speaker : string ;
65+ if ( segment . is_user ) {
66+ speaker = 'Owner' ;
67+ } else if ( segment . person_id && people ) {
68+ const person = people . find ( ( p ) => p . id === segment . person_id ) ;
69+ speaker = person ? person . name : `Speaker ${ segment . speaker_id } ` ;
70+ } else {
71+ speaker = `Speaker ${ segment . speaker_id } ` ;
72+ }
73+ return `${ speaker } : ${ segment . text } ` ;
74+ } )
75+ . join ( '\n\n' ) ,
76+ [ transcript , people ] ,
77+ ) ;
6078
6179 const scrollToBottom = ( smooth = true ) => {
6280 if ( messagesContainerRef . current ) {
@@ -181,15 +199,17 @@ export default function Chat({ transcript, onClearChatRef, onMessagesChange }: C
181199 < div
182200 ref = { messagesContainerRef }
183201 onScroll = { handleScroll }
184- className = { `chat-messages-container relative overflow-y-auto ${ messages . length === 0 ? 'pb-2 pt-4 px-4 md:pt-6 md:px-6' : 'p-4 md:p-6' } ` }
202+ className = { `chat-messages-container relative overflow-y-auto ${
203+ messages . length === 0 ? 'px-4 pb-2 pt-4 md:px-6 md:pt-6' : 'p-4 md:p-6'
204+ } `}
185205 style = { {
186206 height : '400px' ,
187207 overflowY : 'auto' ,
188208 WebkitOverflowScrolling : 'touch' ,
189209 scrollBehavior : 'smooth' ,
190210 // Custom scrollbar styling for Firefox
191211 scrollbarWidth : 'thin' ,
192- scrollbarColor : '#3f3f46 transparent'
212+ scrollbarColor : '#3f3f46 transparent' ,
193213 } }
194214 >
195215 < div className = { messages . length === 0 ? 'space-y-0' : 'space-y-6' } >
@@ -205,7 +225,9 @@ export default function Chat({ transcript, onClearChatRef, onMessagesChange }: C
205225 < div className = "flex flex-col gap-1" >
206226 < div className = "max-w-[85%] rounded-2xl bg-zinc-800/80 px-4 py-3 text-gray-100 shadow-lg" >
207227 < p className = "text-sm leading-relaxed md:text-base" >
208- Hi! I can help you explore this conversation. Ask me questions about the transcript, key points, or any details you'd like to know more about.
228+ Hi! I can help you explore this conversation. Ask me questions
229+ about the transcript, key points, or any details you'd like to
230+ know more about.
209231 </ p >
210232 </ div >
211233 </ div >
@@ -247,7 +269,8 @@ export default function Chat({ transcript, onClearChatRef, onMessagesChange }: C
247269 ...prev ,
248270 {
249271 role : 'assistant' ,
250- content : 'Sorry, I encountered an error. Please try again.' ,
272+ content :
273+ 'Sorry, I encountered an error. Please try again.' ,
251274 } ,
252275 ] ) ;
253276 }
@@ -274,73 +297,73 @@ export default function Chat({ transcript, onClearChatRef, onMessagesChange }: C
274297 </ >
275298 ) }
276299 { messages . map ( ( message , index ) => (
300+ < div
301+ key = { index }
302+ className = { `flex gap-4 ${
303+ message . role === 'user' ? 'flex-row-reverse' : 'flex-row'
304+ } `}
305+ >
306+ { /* Avatar */ }
277307 < div
278- key = { index }
279- className = { `flex gap-4 ${
280- message . role === 'user' ? 'flex-row-reverse' : 'flex-row'
308+ className = { `flex h-8 w-8 shrink-0 items-center justify-center rounded-full ${
309+ message . role === 'user'
310+ ? 'bg-gradient-to-br from-blue-500 to-blue-600'
311+ : 'bg-gradient-to-br from-purple-500 to-purple-600'
312+ } `}
313+ >
314+ { message . role === 'user' ? (
315+ < UserCircle className = "h-5 w-5 text-white" />
316+ ) : (
317+ < Message className = "h-5 w-5 text-white" />
318+ ) }
319+ </ div >
320+
321+ { /* Message Content */ }
322+ < div
323+ className = { `flex min-w-0 flex-1 flex-col gap-1 ${
324+ message . role === 'user' ? 'items-end' : 'items-start'
281325 } `}
282326 >
283- { /* Avatar */ }
284327 < div
285- className = { `flex h-8 w-8 shrink-0 items-center justify-center rounded-full ${
328+ className = { `max-w-[85%] rounded-2xl px-4 py-3 ${
286329 message . role === 'user'
287- ? 'bg-gradient-to-br from-blue-500 to-blue-600 '
288- : 'bg-gradient-to-br from-purple-500 to-purple-600 '
330+ ? 'bg-gradient-to-br from-blue-600 to-blue-700 text-white shadow-lg '
331+ : 'bg-zinc-800/80 text-gray-100 shadow-lg '
289332 } `}
290333 >
291- { message . role === 'user' ? (
292- < UserCircle className = "h-5 w-5 text-white" />
334+ { message . role === 'assistant' ? (
335+ < div className = "prose prose-sm max-w-none text-gray-100 dark:prose-invert prose-headings:text-gray-100 prose-p:leading-relaxed prose-p:text-gray-100 prose-a:text-blue-400 prose-a:no-underline hover:prose-a:underline prose-blockquote:border-l-blue-500 prose-blockquote:text-gray-100 prose-strong:text-gray-100 prose-code:text-blue-300 prose-pre:bg-zinc-900 prose-pre:text-gray-200 prose-ol:text-gray-100 prose-ul:text-gray-100 prose-li:text-gray-100" >
336+ < Markdown > { message . content } </ Markdown >
337+ </ div >
293338 ) : (
294- < Message className = "h-5 w-5 text-white" />
339+ < p className = "whitespace-pre-wrap text-sm leading-relaxed md:text-base" >
340+ { message . content }
341+ </ p >
295342 ) }
296343 </ div >
297-
298- { /* Message Content */ }
299- < div
300- className = { `flex min-w-0 flex-1 flex-col gap-1 ${
301- message . role === 'user' ? 'items-end' : 'items-start'
302- } `}
303- >
304- < div
305- className = { `max-w-[85%] rounded-2xl px-4 py-3 ${
306- message . role === 'user'
307- ? 'bg-gradient-to-br from-blue-600 to-blue-700 text-white shadow-lg'
308- : 'bg-zinc-800/80 text-gray-100 shadow-lg'
309- } `}
310- >
311- { message . role === 'assistant' ? (
312- < div className = "prose prose-sm max-w-none dark:prose-invert prose-headings:text-gray-100 prose-p:text-gray-100 prose-p:leading-relaxed prose-strong:text-gray-100 prose-ul:text-gray-100 prose-ol:text-gray-100 prose-li:text-gray-100 prose-code:text-blue-300 prose-pre:bg-zinc-900 prose-pre:text-gray-200 prose-a:text-blue-400 prose-a:no-underline hover:prose-a:underline prose-blockquote:text-gray-100 prose-blockquote:border-l-blue-500 text-gray-100" >
313- < Markdown > { message . content } </ Markdown >
314- </ div >
315- ) : (
316- < p className = "whitespace-pre-wrap text-sm leading-relaxed md:text-base" >
317- { message . content }
318- </ p >
319- ) }
320- </ div >
321- </ div >
322344 </ div >
323- ) ) }
324- { isLoading && (
325- < div className = "flex gap-4" >
326- < div className = "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-purple-500 to-purple-600" >
327- < Message className = "h-5 w-5 text-white" />
328- </ div >
329- < div className = "flex flex-col gap-1" >
330- < div className = "rounded-2xl bg-zinc-800/80 px-4 py-3 shadow-lg" >
331- < div className = "flex items-center gap-2" >
332- < div className = "flex gap-1" >
333- < div className = "h-2 w-2 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.3s]" > </ div >
334- < div className = "h-2 w-2 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.15s]" > </ div >
335- < div className = "h-2 w-2 animate-bounce rounded-full bg-gray-400" > </ div >
336- </ div >
337- < span className = "text-sm text-gray-400" > Thinking...</ span >
345+ </ div >
346+ ) ) }
347+ { isLoading && (
348+ < div className = "flex gap-4" >
349+ < div className = "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-purple-500 to-purple-600" >
350+ < Message className = "h-5 w-5 text-white" />
351+ </ div >
352+ < div className = "flex flex-col gap-1" >
353+ < div className = "rounded-2xl bg-zinc-800/80 px-4 py-3 shadow-lg" >
354+ < div className = "flex items-center gap-2" >
355+ < div className = "flex gap-1" >
356+ < div className = "h-2 w-2 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.3s]" > </ div >
357+ < div className = "h-2 w-2 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.15s]" > </ div >
358+ < div className = "h-2 w-2 animate-bounce rounded-full bg-gray-400" > </ div >
338359 </ div >
360+ < span className = "text-sm text-gray-400" > Thinking...</ span >
339361 </ div >
340362 </ div >
341363 </ div >
342- ) }
343- < div ref = { messagesEndRef } />
364+ </ div >
365+ ) }
366+ < div ref = { messagesEndRef } />
344367 </ div >
345368
346369 { /* Scroll to bottom button */ }
@@ -365,13 +388,13 @@ export default function Chat({ transcript, onClearChatRef, onMessagesChange }: C
365388 onChange = { ( e ) => setInput ( e . target . value ) }
366389 onKeyDown = { handleKeyDown }
367390 placeholder = "Ask a question about this conversation..."
368- className = "w-full resize-none rounded-xl border border-zinc-700/50 bg-zinc-900/80 px-3 py-2.5 text-sm text-white placeholder:text-gray-500 focus:border-blue-500/50 focus:bg-zinc-900 focus:outline-none focus:ring-2 focus:ring-blue-500/20 transition-all md:px-4 md:py-3 md:text-base"
391+ className = "w-full resize-none rounded-xl border border-zinc-700/50 bg-zinc-900/80 px-3 py-2.5 text-sm text-white transition-all placeholder:text-gray-500 focus:border-blue-500/50 focus:bg-zinc-900 focus:outline-none focus:ring-2 focus:ring-blue-500/20 md:px-4 md:py-3 md:text-base"
369392 rows = { 1 }
370393 disabled = { isLoading }
371394 style = { {
372395 minHeight : '44px' ,
373396 maxHeight : '120px' ,
374- overflow : 'hidden'
397+ overflow : 'hidden' ,
375398 } }
376399 />
377400 </ div >
@@ -390,4 +413,3 @@ export default function Chat({ transcript, onClearChatRef, onMessagesChange }: C
390413 </ div >
391414 ) ;
392415}
393-
0 commit comments