@@ -3,6 +3,7 @@ import { useAppContext } from '../utils/app.context';
33import { Message , PendingMessage } from '../utils/types' ;
44import { classNames } from '../utils/misc' ;
55import MarkdownDisplay , { CopyButton } from './MarkdownDisplay' ;
6+ import { ChevronLeftIcon , ChevronRightIcon } from '@heroicons/react/24/outline' ;
67
78interface SplitMessage {
89 content : PendingMessage [ 'content' ] ;
@@ -12,17 +13,24 @@ interface SplitMessage {
1213
1314export default function ChatMessage ( {
1415 msg,
16+ siblingLeafNodeIds,
17+ siblingCurrIdx,
1518 id,
16- scrollToBottom,
19+ onRegenerateMessage,
20+ onEditMessage,
21+ onChangeSibling,
1722 isPending,
1823} : {
1924 msg : Message | PendingMessage ;
25+ siblingLeafNodeIds : Message [ 'id' ] [ ] ;
26+ siblingCurrIdx : number ;
2027 id ?: string ;
21- scrollToBottom : ( requiresNearBottom : boolean ) => void ;
28+ onRegenerateMessage ( msg : Message ) : void ;
29+ onEditMessage ( msg : Message , content : string ) : void ;
30+ onChangeSibling ( sibling : Message [ 'id' ] ) : void ;
2231 isPending ?: boolean ;
2332} ) {
24- const { viewingConversation, replaceMessageAndGenerate, config } =
25- useAppContext ( ) ;
33+ const { viewingChat, config } = useAppContext ( ) ;
2634 const [ editingContent , setEditingContent ] = useState < string | null > ( null ) ;
2735 const timings = useMemo (
2836 ( ) =>
@@ -37,6 +45,8 @@ export default function ChatMessage({
3745 : null ,
3846 [ msg . timings ]
3947 ) ;
48+ const nextSibling = siblingLeafNodeIds [ siblingCurrIdx + 1 ] ;
49+ const prevSibling = siblingLeafNodeIds [ siblingCurrIdx - 1 ] ;
4050
4151 // for reasoning model, we split the message into content and thought
4252 // TODO: implement this as remark/rehype plugin in the future
@@ -64,13 +74,7 @@ export default function ChatMessage({
6474 return { content : actualContent , thought, isThinking } ;
6575 } , [ msg ] ) ;
6676
67- if ( ! viewingConversation ) return null ;
68-
69- const regenerate = async ( ) => {
70- replaceMessageAndGenerate ( viewingConversation . id , msg . id , undefined , ( ) =>
71- scrollToBottom ( true )
72- ) ;
73- } ;
77+ if ( ! viewingChat ) return null ;
7478
7579 return (
7680 < div className = "group" id = { id } >
@@ -105,13 +109,12 @@ export default function ChatMessage({
105109 </ button >
106110 < button
107111 className = "btn mt-2"
108- onClick = { ( ) =>
109- replaceMessageAndGenerate (
110- viewingConversation . id ,
111- msg . id ,
112- editingContent
113- )
114- }
112+ onClick = { ( ) => {
113+ if ( msg . content !== null ) {
114+ setEditingContent ( null ) ;
115+ onEditMessage ( msg as Message , editingContent ) ;
116+ }
117+ } }
115118 >
116119 Submit
117120 </ button >
@@ -196,10 +199,35 @@ export default function ChatMessage({
196199 { msg . content !== null && (
197200 < div
198201 className = { classNames ( {
199- 'mx-4 mt-2 mb-2' : true ,
200- 'text-right ' : msg . role === 'user' ,
202+ 'flex items-center gap-2 mx-4 mt-2 mb-2' : true ,
203+ 'flex-row-reverse ' : msg . role === 'user' ,
201204 } ) }
202205 >
206+ { siblingLeafNodeIds && siblingLeafNodeIds . length > 1 && (
207+ < div className = "flex gap-1 items-center opacity-60 text-sm" >
208+ < button
209+ className = { classNames ( {
210+ 'btn btn-sm btn-ghost p-1' : true ,
211+ 'opacity-20' : ! prevSibling ,
212+ } ) }
213+ onClick = { ( ) => prevSibling && onChangeSibling ( prevSibling ) }
214+ >
215+ < ChevronLeftIcon className = "h-4 w-4" />
216+ </ button >
217+ < span >
218+ { siblingCurrIdx + 1 } / { siblingLeafNodeIds . length }
219+ </ span >
220+ < button
221+ className = { classNames ( {
222+ 'btn btn-sm btn-ghost p-1' : true ,
223+ 'opacity-20' : ! nextSibling ,
224+ } ) }
225+ onClick = { ( ) => nextSibling && onChangeSibling ( nextSibling ) }
226+ >
227+ < ChevronRightIcon className = "h-4 w-4" />
228+ </ button >
229+ </ div >
230+ ) }
203231 { /* user message */ }
204232 { msg . role === 'user' && (
205233 < button
@@ -216,7 +244,11 @@ export default function ChatMessage({
216244 { ! isPending && (
217245 < button
218246 className = "badge btn-mini show-on-hover mr-2"
219- onClick = { regenerate }
247+ onClick = { ( ) => {
248+ if ( msg . content !== null ) {
249+ onRegenerateMessage ( msg as Message ) ;
250+ }
251+ } }
220252 disabled = { msg . content === null }
221253 >
222254 🔄 Regenerate
0 commit comments