@@ -3,6 +3,7 @@ import { useAppContext } from '../utils/app.context';
3
3
import { Message , PendingMessage } from '../utils/types' ;
4
4
import { classNames } from '../utils/misc' ;
5
5
import MarkdownDisplay , { CopyButton } from './MarkdownDisplay' ;
6
+ import { ChevronLeftIcon , ChevronRightIcon } from '@heroicons/react/24/outline' ;
6
7
7
8
interface SplitMessage {
8
9
content : PendingMessage [ 'content' ] ;
@@ -12,17 +13,24 @@ interface SplitMessage {
12
13
13
14
export default function ChatMessage ( {
14
15
msg,
16
+ siblingLeafNodeIds,
17
+ siblingCurrIdx,
15
18
id,
16
- scrollToBottom,
19
+ onRegenerateMessage,
20
+ onEditMessage,
21
+ onChangeSibling,
17
22
isPending,
18
23
} : {
19
24
msg : Message | PendingMessage ;
25
+ siblingLeafNodeIds : Message [ 'id' ] [ ] ;
26
+ siblingCurrIdx : number ;
20
27
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 ;
22
31
isPending ?: boolean ;
23
32
} ) {
24
- const { viewingConversation, replaceMessageAndGenerate, config } =
25
- useAppContext ( ) ;
33
+ const { viewingChat, config } = useAppContext ( ) ;
26
34
const [ editingContent , setEditingContent ] = useState < string | null > ( null ) ;
27
35
const timings = useMemo (
28
36
( ) =>
@@ -37,6 +45,8 @@ export default function ChatMessage({
37
45
: null ,
38
46
[ msg . timings ]
39
47
) ;
48
+ const nextSibling = siblingLeafNodeIds [ siblingCurrIdx + 1 ] ;
49
+ const prevSibling = siblingLeafNodeIds [ siblingCurrIdx - 1 ] ;
40
50
41
51
// for reasoning model, we split the message into content and thought
42
52
// TODO: implement this as remark/rehype plugin in the future
@@ -64,13 +74,7 @@ export default function ChatMessage({
64
74
return { content : actualContent , thought, isThinking } ;
65
75
} , [ msg ] ) ;
66
76
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 ;
74
78
75
79
return (
76
80
< div className = "group" id = { id } >
@@ -105,13 +109,12 @@ export default function ChatMessage({
105
109
</ button >
106
110
< button
107
111
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
+ } }
115
118
>
116
119
Submit
117
120
</ button >
@@ -196,10 +199,35 @@ export default function ChatMessage({
196
199
{ msg . content !== null && (
197
200
< div
198
201
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' ,
201
204
} ) }
202
205
>
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
+ ) }
203
231
{ /* user message */ }
204
232
{ msg . role === 'user' && (
205
233
< button
@@ -216,7 +244,11 @@ export default function ChatMessage({
216
244
{ ! isPending && (
217
245
< button
218
246
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
+ } }
220
252
disabled = { msg . content === null }
221
253
>
222
254
🔄 Regenerate
0 commit comments