1
+ import { LoaderIcon } from 'lucide-react' ;
2
+ import { useContext , useMemo } from 'react' ;
1
3
import * as React from 'react' ;
2
4
import { styled , css , Theme , SxProps } from '@mui/system' ;
5
+ import { CfgContext } from '../../context' ;
3
6
4
7
import { MDMessage } from './Markdown' ;
5
8
import { a11yDark , a11yLight } from '../../theme/code' ;
6
9
10
+ export type AppChatStreamSource = { title : string , uri : string } ;
11
+
12
+ export const enum AppChatStreamState {
13
+ CONNECTING = 'CONNECTING' , // only client side
14
+ CREATING = 'CREATING' ,
15
+ SEARCHING = 'SEARCHING' ,
16
+ RERANKING = 'RERANKING' ,
17
+ GENERATING = 'GENERATING' ,
18
+ FINISHED = 'FINISHED' ,
19
+ ERROR = 'ERROR' ,
20
+ }
21
+
22
+ export type MyChatMessageAnnotation = {
23
+ context ?: AppChatStreamSource [ ] ,
24
+ state ?: AppChatStreamState ,
25
+ stateMessage ?: string
26
+ } ;
27
+
28
+
7
29
export function ChatItem ( props : {
8
30
role ?: 'user' | 'bot' ;
9
31
children : string ;
@@ -50,7 +72,42 @@ export function ChatItem(props: {
50
72
) ;
51
73
}
52
74
53
- export function ChatItemLoading ( ) {
75
+ function getHost ( baseUrl : string ) {
76
+ try {
77
+ return new URL ( baseUrl ) . hostname ;
78
+ } catch {
79
+ return location . hostname ;
80
+ }
81
+ }
82
+
83
+ export function ChatItemLoading ( { annotations } : { annotations : MyChatMessageAnnotation [ ] } ) {
84
+ const annotation = useMemo ( ( ) => {
85
+ return getChatMessageAnnotations ( annotations ) ;
86
+ } , [ annotations ] )
87
+
88
+ const { baseUrl } = useContext ( CfgContext ) ;
89
+ let text : string ;
90
+ switch ( annotation . state ) {
91
+ case undefined :
92
+ case 'CONNECTING' :
93
+ text = `Connecting to ${ getHost ( baseUrl ) } ...` ;
94
+ break ;
95
+ case 'CREATING' :
96
+ text = 'Preparing to ask...' ;
97
+ break ;
98
+ case 'SEARCHING' :
99
+ text = 'Gathering resources...' ;
100
+ break ;
101
+ case 'RERANKING' :
102
+ text = 'Reranking resources...' ;
103
+ break ;
104
+ case 'GENERATING' :
105
+ text = 'Generating answer...' ;
106
+ break ;
107
+ default :
108
+ return null ;
109
+ }
110
+
54
111
return (
55
112
< StyledChatItemRow
56
113
role = 'bot'
@@ -59,48 +116,57 @@ export function ChatItemLoading() {
59
116
} }
60
117
>
61
118
< StyledChatItem role = 'bot' >
62
- < StyledChatMutedText > Gathering Resources...</ StyledChatMutedText >
63
- < StyledTextSkeleton />
119
+ < StyledChatMutedText >
120
+ < StyledLoaderIcon sx = { {
121
+ animation : "spin 2s linear infinite" ,
122
+ "@keyframes spin" : {
123
+ "0%" : {
124
+ transform : "rotate(360deg)" ,
125
+ } ,
126
+ "100%" : {
127
+ transform : "rotate(0deg)" ,
128
+ } ,
129
+ }
130
+ } } />
131
+ { text }
132
+ </ StyledChatMutedText >
64
133
</ StyledChatItem >
65
134
</ StyledChatItemRow >
66
135
) ;
67
136
}
68
137
69
- // export function ChatItemRelatedLinkCard(props: { title: string; uri: string }) {
70
- // const { title, uri } = props;
138
+ export function ChatItemRelatedLinkCard ( props : { title : string ; uri : string } ) {
139
+ const { title, uri } = props ;
71
140
72
- // const hostnameMemo = React.useMemo(() => {
73
- // try {
74
- // return new URL(uri).hostname;
75
- // } catch {
76
- // return '';
77
- // }
78
- // }, [uri]);
141
+ const hostnameMemo = React . useMemo ( ( ) => {
142
+ try {
143
+ return new URL ( uri ) . hostname ;
144
+ } catch {
145
+ return '' ;
146
+ }
147
+ } , [ uri ] ) ;
79
148
80
- // return (
81
- // <StyledChatItemLinkCard href={uri} target='_blank'>
82
- // <div>{title}</div>
83
- // <div>{hostnameMemo}</div>
84
- // </StyledChatItemLinkCard>
85
- // );
86
- // }
149
+ return (
150
+ < StyledChatItemLinkCard href = { uri } target = '_blank' >
151
+ < div > { title } </ div >
152
+ < div > { hostnameMemo } </ div >
153
+ </ StyledChatItemLinkCard >
154
+ ) ;
155
+ }
87
156
88
- // export function ChatItemRelatedLinkCardWrapper(props: {
89
- // items: {
90
- // title: string;
91
- // uri: string;
92
- // }[];
93
- // }) {
94
- // const { items } = props;
157
+ export function ChatItemRelatedLinkCardWrapper ( props : {
158
+ items : AppChatStreamSource [ ] ;
159
+ } ) {
160
+ const { items } = props ;
95
161
96
- // return (
97
- // <StyledChatItemLinkCardWrapper>
98
- // {items.map((item, index) => (
99
- // <ChatItemRelatedLinkCard key={index} {...item} />
100
- // ))}
101
- // </StyledChatItemLinkCardWrapper>
102
- // );
103
- // }
162
+ return (
163
+ < StyledChatItemLinkCardWrapper >
164
+ { items . map ( ( item , index ) => (
165
+ < ChatItemRelatedLinkCard key = { index } { ...item } />
166
+ ) ) }
167
+ </ StyledChatItemLinkCardWrapper >
168
+ ) ;
169
+ }
104
170
105
171
export function ExampleQuestions ( props : {
106
172
items : string [ ] ;
@@ -161,7 +227,9 @@ export const StyledChatItem = styled('div')(({ theme, role }) =>
161
227
162
228
export const StyledChatMutedText = styled ( 'div' ) ( ( { theme } ) =>
163
229
css ( {
164
- display : 'block' ,
230
+ display : 'flex' ,
231
+ alignItems : 'center' ,
232
+ gap : '4px' ,
165
233
fontSize : '12px' ,
166
234
color : theme . palette . mutedForeground ,
167
235
'& > a' : {
@@ -170,49 +238,54 @@ export const StyledChatMutedText = styled('div')(({ theme }) =>
170
238
} )
171
239
) ;
172
240
173
- export const StyledTextSkeleton = styled ( 'div' ) ( ( { theme } ) =>
174
- css ( {
175
- backgroundColor : theme . palette . muted ,
176
- color : theme . palette . primary ,
177
- borderRadius : theme . shape . borderRadius ,
178
- padding : '0.5rem 0.75rem' ,
179
- display : 'inline-block' ,
180
- animation : 'animation-breath 1.5s infinite' ,
181
- '@keyframes animation-breath' : {
182
- '0%' : {
183
- opacity : 1 ,
184
- } ,
185
- '50%' : {
186
- opacity : 0.4 ,
187
- } ,
188
- '100%' : {
189
- opacity : 1 ,
190
- } ,
191
- } ,
192
- } )
193
- ) ;
241
+ const StyledLoaderIcon = styled ( LoaderIcon ) `
242
+ width: 1em;
243
+ height: 1em;
244
+ `
194
245
195
- // export const StyledChatItemLinkCard = styled('a ')(({ theme }) =>
246
+ // export const StyledTextSkeleton = styled('div ')(({ theme }) =>
196
247
// css({
197
- // display: 'block',
198
- // padding: '0.5rem',
199
- // borderRadius: theme.shape.borderRadius,
200
248
// backgroundColor: theme.palette.muted,
201
- // color: theme.palette.accentForeground,
202
- // '&:hover': {
203
- // backgroundColor: theme.palette.muted,
204
- // color: theme.palette.accentForeground,
249
+ // color: theme.palette.primary,
250
+ // borderRadius: theme.shape.borderRadius,
251
+ // padding: '0.5rem 0.75rem',
252
+ // display: 'inline-block',
253
+ // animation: 'animation-breath 1.5s infinite',
254
+ // '@keyframes animation-breath': {
255
+ // '0%': {
256
+ // opacity: 1,
257
+ // },
258
+ // '50%': {
259
+ // opacity: 0.4,
260
+ // },
261
+ // '100%': {
262
+ // opacity: 1,
263
+ // },
205
264
// },
206
265
// })
207
266
// );
208
267
209
- // export const StyledChatItemLinkCardWrapper = styled('div')(() =>
210
- // css({
211
- // display: 'flex',
212
- // gapX: '0.5rem',
213
- // overflowX: 'auto',
214
- // })
215
- // );
268
+ export const StyledChatItemLinkCard = styled ( 'a' ) ( ( { theme } ) =>
269
+ css ( {
270
+ display : 'block' ,
271
+ padding : '0.5rem' ,
272
+ borderRadius : theme . shape . borderRadius ,
273
+ backgroundColor : theme . palette . muted ,
274
+ color : theme . palette . accentForeground ,
275
+ '&:hover' : {
276
+ backgroundColor : theme . palette . muted ,
277
+ color : theme . palette . accentForeground ,
278
+ } ,
279
+ } )
280
+ ) ;
281
+
282
+ export const StyledChatItemLinkCardWrapper = styled ( 'div' ) ( ( ) =>
283
+ css ( {
284
+ display : 'flex' ,
285
+ gapX : '0.5rem' ,
286
+ overflowX : 'auto' ,
287
+ } )
288
+ ) ;
216
289
217
290
export const StyledExampleQuestionItem = styled ( 'div' ) ( ( { theme } ) =>
218
291
css ( {
@@ -273,3 +346,8 @@ export const StyledExampleQuestionWrapper = styled('div')(() =>
273
346
// }),
274
347
// })
275
348
// );
349
+
350
+ function getChatMessageAnnotations ( annotations : MyChatMessageAnnotation [ ] | undefined ) {
351
+ return ( ( annotations ?? [ ] ) as MyChatMessageAnnotation [ ] )
352
+ . reduce ( ( annotation , next ) => Object . assign ( annotation , next ) , { } ) ;
353
+ }
0 commit comments