1
- import React , { useEffect , useMemo } from "react" ;
1
+ import React , { useCallback , useEffect , useMemo } from "react" ;
2
2
import {
3
3
chatDbMessageSliceActions ,
4
4
CMessageNode ,
5
5
isUserCMessageNode ,
6
- UserCMessageNode ,
7
6
} from "../../features/ChatDB/chatDbMessagesSlice" ;
8
7
import { UserInput } from "../ChatContent/UserInput" ;
9
8
import { AssistantInput } from "../ChatContent/AssistantInput" ;
@@ -15,7 +14,7 @@ import {
15
14
isPlainTextMessage ,
16
15
isUserMessage ,
17
16
} from "../../services/refact" ;
18
- import { IconButton } from "@radix-ui/themes" ;
17
+ import { Box , Flex , IconButton } from "@radix-ui/themes" ;
19
18
import { ArrowLeftIcon , ArrowRightIcon } from "@radix-ui/react-icons" ;
20
19
import { PlainText } from "../ChatContent/PlainText" ;
21
20
import { ContextFiles } from "../ChatContent/ContextFiles" ;
@@ -87,74 +86,93 @@ export const MessageNode: React.FC<MessageNodeProps> = ({ children }) => {
87
86
) ;
88
87
} ;
89
88
89
+ function makeDummyNode ( lastMessage ?: CMessageNode ) : CMessageNode {
90
+ return {
91
+ message : {
92
+ cmessage_usage_model : lastMessage ?. message . cmessage_usage_model ?? "" ,
93
+ cmessage_usage_prompt : lastMessage ?. message . cmessage_usage_prompt ?? 0 ,
94
+ cmessage_usage_completion :
95
+ lastMessage ?. message . cmessage_usage_completion ?? 0 ,
96
+ cmessage_belongs_to_cthread_id :
97
+ lastMessage ?. message . cmessage_belongs_to_cthread_id ?? "" ,
98
+ cmessage_num : lastMessage ?. message . cmessage_num ?? 0 ,
99
+
100
+ cmessage_alt : ( lastMessage ?. message . cmessage_alt ?? 0 ) + 1 ,
101
+ cmessage_prev_alt : lastMessage ?. message . cmessage_alt ?? 0 ,
102
+ cmessage_json : {
103
+ role : "user" ,
104
+ content : "dummy text about making a new message" ,
105
+ } , // TODO: use a different type of message
106
+ } ,
107
+ children : [ ] ,
108
+ } ;
109
+ }
110
+
90
111
const MessageNodeChildren : React . FC < { children : CMessageNode [ ] } > = ( {
91
112
children,
92
113
} ) => {
93
- const userMessages : UserCMessageNode [ ] = children . filter ( isUserCMessageNode ) ;
114
+ const [ selectedNodeIndex , setSelectedNodeIndex ] = React . useState < number > ( 0 ) ;
94
115
95
- if ( userMessages . length === 0 ) {
96
- return children . map ( ( node , index ) => {
97
- const key = ` ${ node . message . cmessage_belongs_to_cthread_id } _ ${ node . message . cmessage_num } _ ${ node . message . cmessage_alt } _ ${ index } ` ;
98
- return < MessageNode key = { key } > { node } </ MessageNode > ;
116
+ const goBack = useCallback ( ( ) => {
117
+ setSelectedNodeIndex ( ( prev ) => {
118
+ if ( prev === 0 ) return prev ;
119
+ return prev - 1 ;
99
120
} ) ;
100
- } else {
101
- return < UserMessageNode > { userMessages } </ UserMessageNode > ;
102
- }
103
- } ;
121
+ } , [ ] ) ;
104
122
105
- const UserMessageNode : React . FC < { children : UserCMessageNode [ ] } > = ( {
106
- children,
107
- } ) => {
108
- // info about the node may need to be shared with the user input
109
- const dispatch = useAppDispatch ( ) ;
110
- const [ selectedNodeIndex , setSelectedNodeIndex ] = React . useState < number > ( 0 ) ;
111
-
112
- const selectedNode = children [ selectedNodeIndex ] ;
123
+ const goForward = useCallback ( ( ) => {
124
+ setSelectedNodeIndex ( ( prev ) => {
125
+ if ( prev === children . length ) return prev ;
126
+ return prev + 1 ;
127
+ } ) ;
128
+ } , [ children . length ] ) ;
113
129
114
- useEffect ( ( ) => {
115
- if ( selectedNode . children . length === 0 ) {
116
- const action = chatDbMessageSliceActions . setEnd ( {
117
- number : selectedNode . message . cmessage_num ,
118
- alt : selectedNode . message . cmessage_alt ,
119
- } ) ;
120
- dispatch ( action ) ;
130
+ const canBranch = useMemo ( ( ) => {
131
+ if ( children . length > 1 ) return true ;
132
+ if ( selectedNodeIndex >= children . length && selectedNodeIndex > 0 ) {
133
+ return true ;
121
134
}
122
- } , [
123
- selectedNode . children . length ,
124
- selectedNode . message . cmessage_num ,
125
- selectedNode . message . cmessage_alt ,
126
- dispatch ,
127
- ] ) ;
135
+ if (
136
+ children [ selectedNodeIndex ] &&
137
+ isUserCMessageNode ( children [ selectedNodeIndex ] )
138
+ ) {
139
+ return true ;
140
+ }
141
+ return false ;
142
+ } , [ children , selectedNodeIndex ] ) ;
143
+
144
+ const nodeToRender = useMemo ( ( ) => {
145
+ return (
146
+ children [ selectedNodeIndex ] ??
147
+ makeDummyNode ( children [ children . length - 1 ] )
148
+ ) ;
149
+ } , [ children , selectedNodeIndex ] ) ;
150
+
151
+ if ( ! canBranch ) {
152
+ return < MessageNode > { children [ selectedNodeIndex ] } </ MessageNode > ;
153
+ }
154
+
128
155
return (
129
- < >
130
- < IconButton
131
- variant = "outline"
132
- size = "1"
133
- disabled = { selectedNodeIndex === 0 }
134
- onClick = { ( ) =>
135
- setSelectedNodeIndex ( ( prev ) => {
136
- if ( prev === 0 ) return prev ;
137
- return prev - 1 ;
138
- } )
139
- }
140
- >
141
- < ArrowLeftIcon />
142
- </ IconButton >
143
- < IconButton
144
- variant = "outline"
145
- size = "1"
146
- disabled = { selectedNodeIndex === children . length - 1 }
147
- onClick = { ( ) => {
148
- setSelectedNodeIndex ( ( prev ) => {
149
- if ( prev === children . length - 1 ) return prev ;
150
- return prev + 1 ;
151
- } ) ;
152
- } }
153
- >
154
- < ArrowRightIcon />
155
- </ IconButton >
156
- < UserInput > { selectedNode . message . cmessage_json . content } </ UserInput >
157
- < MessageNodeChildren > { selectedNode . children } </ MessageNodeChildren >
158
- </ >
156
+ < Box >
157
+ < Flex gap = "4" justify = "end" >
158
+ < IconButton
159
+ variant = "outline"
160
+ size = "1"
161
+ disabled = { selectedNodeIndex === 0 }
162
+ onClick = { goBack }
163
+ >
164
+ < ArrowLeftIcon />
165
+ </ IconButton >
166
+ < IconButton
167
+ variant = "outline"
168
+ size = "1"
169
+ disabled = { selectedNodeIndex === children . length }
170
+ onClick = { goForward }
171
+ >
172
+ < ArrowRightIcon />
173
+ </ IconButton >
174
+ </ Flex >
175
+ < MessageNode > { nodeToRender } </ MessageNode >
176
+ </ Box >
159
177
) ;
160
178
} ;
0 commit comments