@@ -35,13 +35,13 @@ export interface BaseDisplayObject {
35
35
36
36
// Text display object interface
37
37
export interface TextDisplayObject extends BaseDisplayObject {
38
- type : ' text' ;
38
+ type : " text" ;
39
39
content : string ; // Markdown content
40
40
}
41
41
42
42
// Image display object interface
43
43
export interface ImageDisplayObject extends BaseDisplayObject {
44
- type : ' image' ;
44
+ type : " image" ;
45
45
content : string ; // Base64 encoded image
46
46
caption : string ; // Text describing the image
47
47
}
@@ -55,7 +55,7 @@ export interface SourceObject {
55
55
documentName : string ;
56
56
documentId : string ;
57
57
content ?: string ; // Content from the source
58
- contentType ?: ' text' | ' image' ; // Type of content
58
+ contentType ?: " text" | " image" ; // Type of content
59
59
}
60
60
61
61
// Define interface for the Tool Call
@@ -230,7 +230,7 @@ const MarkdownContent: React.FC<{ content: string }> = ({ content }) => {
230
230
// Helper function to ensure image content is properly formatted
231
231
const formatImageSource = ( content : string ) : string => {
232
232
// If already a data URI, use as is
233
- if ( content . startsWith ( ' data:image/' ) ) {
233
+ if ( content . startsWith ( " data:image/" ) ) {
234
234
return content ;
235
235
}
236
236
@@ -246,47 +246,37 @@ const formatImageSource = (content: string): string => {
246
246
// Component to render display objects
247
247
const DisplayObjectRenderer : React . FC < { object : DisplayObject ; isInSourceView ?: boolean } > = ( {
248
248
object,
249
- isInSourceView = false
249
+ isInSourceView = false ,
250
250
} ) => {
251
- if ( object . type === ' text' ) {
251
+ if ( object . type === " text" ) {
252
252
return (
253
253
< div className = { isInSourceView ? "my-1" : "my-2" } >
254
254
< MarkdownContent content = { object . content } />
255
- { ! isInSourceView && (
256
- < div className = "mt-1 text-xs text-muted-foreground" >
257
- Source: { object . source }
258
- </ div >
259
- ) }
255
+ { ! isInSourceView && < div className = "mt-1 text-xs text-muted-foreground" > Source: { object . source } </ div > }
260
256
</ div >
261
257
) ;
262
- } else if ( object . type === ' image' ) {
258
+ } else if ( object . type === " image" ) {
263
259
// Try to determine if the content has the image format information
264
- const hasImagePrefix = object . content . startsWith ( ' data:image' ) ;
260
+ const hasImagePrefix = object . content . startsWith ( " data:image" ) ;
265
261
266
262
return (
267
263
< div className = { isInSourceView ? "my-1" : "my-2" } >
268
264
< div className = "overflow-hidden rounded-md" >
269
265
< img
270
266
src = { hasImagePrefix ? object . content : `data:image/png;base64,${ object . content } ` }
271
- alt = { object . caption || ' Image' }
272
- className = "max-w-full h-auto "
273
- onError = { ( e ) => {
267
+ alt = { object . caption || " Image" }
268
+ className = "h-auto max-w-full"
269
+ onError = { e => {
274
270
// Fallback chain: try JPEG if PNG fails
275
271
const target = e . target as HTMLImageElement ;
276
- if ( ! hasImagePrefix && target . src . includes ( ' image/png' ) ) {
272
+ if ( ! hasImagePrefix && target . src . includes ( " image/png" ) ) {
277
273
target . src = `data:image/jpeg;base64,${ object . content } ` ;
278
274
}
279
275
} }
280
276
/>
281
277
</ div >
282
- { object . caption && (
283
- < div className = "mt-1 text-sm text-muted-foreground" > { object . caption } </ div >
284
- ) }
285
- { ! isInSourceView && (
286
- < div className = "mt-1 text-xs text-muted-foreground" >
287
- Source: { object . source }
288
- </ div >
289
- ) }
278
+ { object . caption && < div className = "mt-1 text-sm text-muted-foreground" > { object . caption } </ div > }
279
+ { ! isInSourceView && < div className = "mt-1 text-xs text-muted-foreground" > Source: { object . source } </ div > }
290
280
</ div >
291
281
) ;
292
282
}
@@ -298,38 +288,38 @@ const detectImageFormat = (base64String: string): string => {
298
288
// Check the first few bytes of the base64 to determine image format
299
289
// See: https://en.wikipedia.org/wiki/List_of_file_signatures
300
290
try {
301
- if ( ! base64String || base64String . length < 10 ) return ' png' ;
291
+ if ( ! base64String || base64String . length < 10 ) return " png" ;
302
292
303
293
// Decode the first few bytes of the base64 string
304
294
const firstBytes = atob ( base64String . substring ( 0 , 20 ) ) ;
305
295
306
296
// Check for common image signatures
307
- if ( firstBytes . startsWith ( ' \xFF\xD8\xFF' ) ) return ' jpeg' ;
308
- if ( firstBytes . startsWith ( ' \x89PNG\r\n\x1A\n' ) ) return ' png' ;
309
- if ( firstBytes . startsWith ( ' GIF87a' ) || firstBytes . startsWith ( ' GIF89a' ) ) return ' gif' ;
310
- if ( firstBytes . startsWith ( ' RIFF' ) && firstBytes . substring ( 8 , 12 ) === ' WEBP' ) return ' webp' ;
297
+ if ( firstBytes . startsWith ( " \xFF\xD8\xFF" ) ) return " jpeg" ;
298
+ if ( firstBytes . startsWith ( " \x89PNG\r\n\x1A\n" ) ) return " png" ;
299
+ if ( firstBytes . startsWith ( " GIF87a" ) || firstBytes . startsWith ( " GIF89a" ) ) return " gif" ;
300
+ if ( firstBytes . startsWith ( " RIFF" ) && firstBytes . substring ( 8 , 12 ) === " WEBP" ) return " webp" ;
311
301
312
302
// Default to PNG if signature not recognized
313
- return ' png' ;
303
+ return " png" ;
314
304
} catch ( e ) {
315
305
// If any error in detection, default to PNG
316
- return ' png' ;
306
+ return " png" ;
317
307
}
318
308
} ;
319
309
320
310
// Component to render sources as tags with dropdown content
321
311
const SourcesRenderer : React . FC < { sources : SourceObject [ ] ; displayObjects ?: DisplayObject [ ] } > = ( {
322
312
sources,
323
- displayObjects = [ ]
313
+ displayObjects = [ ] ,
324
314
} ) => {
325
315
const [ selectedSource , setSelectedSource ] = useState < string | null > ( null ) ;
326
- const [ animation , setAnimation ] = useState < ' open' | ' close' | null > ( null ) ;
316
+ const [ animation , setAnimation ] = useState < " open" | " close" | null > ( null ) ;
327
317
const [ visibleContent , setVisibleContent ] = useState < string | null > ( null ) ;
328
318
329
319
useEffect ( ( ) => {
330
320
// Set animation state when source is selected
331
321
if ( selectedSource ) {
332
- setAnimation ( ' open' ) ;
322
+ setAnimation ( " open" ) ;
333
323
setVisibleContent ( selectedSource ) ;
334
324
}
335
325
} , [ selectedSource ] ) ;
@@ -340,7 +330,7 @@ const SourcesRenderer: React.FC<{ sources: SourceObject[]; displayObjects?: Disp
340
330
const toggleSource = ( sourceId : string ) => {
341
331
if ( selectedSource === sourceId ) {
342
332
// Close animation
343
- setAnimation ( ' close' ) ;
333
+ setAnimation ( " close" ) ;
344
334
setTimeout ( ( ) => {
345
335
setSelectedSource ( null ) ;
346
336
setVisibleContent ( null ) ;
@@ -355,26 +345,21 @@ const SourcesRenderer: React.FC<{ sources: SourceObject[]; displayObjects?: Disp
355
345
// Render source content
356
346
const renderSourceContent = ( source : SourceObject ) => {
357
347
if ( ! source . content ) {
358
- return (
359
- < div className = "text-xs text-muted-foreground" >
360
- No content available for this source.
361
- </ div >
362
- ) ;
348
+ return < div className = "text-xs text-muted-foreground" > No content available for this source.</ div > ;
363
349
}
364
350
365
351
// Render based on contentType
366
- if ( source . contentType === ' image' ) {
352
+ if ( source . contentType === " image" ) {
367
353
const content = source . content ;
368
354
369
355
// Handle different image formats similar to ChatMessages.tsx
370
- if ( content . startsWith ( 'data:image/' ) ||
371
- // If it looks like base64, we'll try as PNG
372
- / ^ [ A - Z a - z 0 - 9 + / = ] + $ / . test ( content . substring ( 0 , 20 ) ) ) {
373
-
356
+ if (
357
+ content . startsWith ( "data:image/" ) ||
358
+ // If it looks like base64, we'll try as PNG
359
+ / ^ [ A - Z a - z 0 - 9 + / = ] + $ / . test ( content . substring ( 0 , 20 ) )
360
+ ) {
374
361
// Format similar to how ChatMessages.tsx does it
375
- const imageUrl = content . startsWith ( 'data:image/' )
376
- ? content
377
- : `data:image/png;base64,${ content } ` ;
362
+ const imageUrl = content . startsWith ( "data:image/" ) ? content : `data:image/png;base64,${ content } ` ;
378
363
379
364
return (
380
365
< div className = "flex justify-center rounded-md bg-muted p-4" >
@@ -387,16 +372,12 @@ const SourcesRenderer: React.FC<{ sources: SourceObject[]; displayObjects?: Disp
387
372
) ;
388
373
} else {
389
374
// If not recognizable as an image format, show as text
390
- return (
391
- < div className = "whitespace-pre-wrap rounded-md bg-muted p-4 font-mono text-sm" >
392
- { content }
393
- </ div >
394
- ) ;
375
+ return < div className = "whitespace-pre-wrap rounded-md bg-muted p-4 font-mono text-sm" > { content } </ div > ;
395
376
}
396
377
} else {
397
378
// Default to text/markdown with scrollable area
398
379
return (
399
- < div className = "max-h-[300px] overflow-y-auto pr-1 custom-scrollbar " >
380
+ < div className = "custom-scrollbar max-h-[300px] overflow-y-auto pr-1" >
400
381
< MarkdownContent content = { source . content } />
401
382
</ div >
402
383
) ;
@@ -409,7 +390,7 @@ const SourcesRenderer: React.FC<{ sources: SourceObject[]; displayObjects?: Disp
409
390
< style > { scrollbarStyles } </ style >
410
391
411
392
< div className = "mb-2 flex flex-wrap gap-2" >
412
- { sources . map ( ( source ) => (
393
+ { sources . map ( source => (
413
394
< Badge
414
395
key = { source . sourceId }
415
396
variant = { selectedSource === source . sourceId ? "default" : "outline" }
@@ -425,8 +406,11 @@ const SourcesRenderer: React.FC<{ sources: SourceObject[]; displayObjects?: Disp
425
406
{ visibleContent && (
426
407
< div
427
408
className = { `mt-3 overflow-hidden rounded-md border bg-card shadow-sm transition-all duration-200 ease-in-out ${
428
- animation === 'open' ? 'max-h-[400px] opacity-100 p-3' :
429
- animation === 'close' ? 'max-h-0 opacity-0 p-0' : 'p-3'
409
+ animation === "open"
410
+ ? "max-h-[400px] p-3 opacity-100"
411
+ : animation === "close"
412
+ ? "max-h-0 p-0 opacity-0"
413
+ : "p-3"
430
414
} `}
431
415
>
432
416
< div className = "mb-2 flex items-center justify-between" >
0 commit comments