@@ -44,6 +44,8 @@ import { MarkdownRendererProps, RenderFormPreview } from 'contesto'
44
44
import { ErrorPreview , EditorToolPreview , ToolPreviewContainer , Dot } from './chat-tool-previews'
45
45
import { ShowMore } from './show-more'
46
46
import { useDisableBodyScroll } from '../lib/hooks'
47
+ import { RenderNode } from 'safe-mdx'
48
+ import { CodeBlock , Pre } from 'fumadocs-ui/components/codeblock'
47
49
48
50
export function ChatDrawer ( { loaderData } : { loaderData ?: unknown } ) {
49
51
const chatId = usePersistentDocsState ( ( x ) => x . chatId )
@@ -307,7 +309,7 @@ function ChatTopBar() {
307
309
)
308
310
}
309
311
310
- function Chat ( { } ) {
312
+ function Chat ( { } ) {
311
313
return (
312
314
< ScrollArea className = '[&>div>div]:grow -mr-4 [scrollbar-gutter:stable_both-edges] pr-4 relative items-stretch rounded max-h-full flex flex-col grow justify-center ' >
313
315
< div className = 'flex flex-col gap-4 relative h-full justify-center' >
@@ -319,8 +321,57 @@ function Chat({}) {
319
321
)
320
322
}
321
323
324
+
325
+ export const renderChatNode : RenderNode = ( node , transform ) => {
326
+ // TODO only enable colored bold in chat?
327
+ if ( node . type === 'strong' ) {
328
+ return < span className = 'dark:text-rose-200 font-mono' > { node . children ?. map ( ( child ) => transform ( child ) ) } </ span >
329
+ }
330
+ if ( node . type === 'emphasis' ) {
331
+ return < span className = 'dark:text-emerald-200 font-mono' > { node . children ?. map ( ( child ) => transform ( child ) ) } </ span >
332
+ }
333
+ if ( node . type === 'delete' ) {
334
+ return < span className = 'dark:text-red-200 font-mono line-through' > { node . children ?. map ( ( child ) => transform ( child ) ) } </ span >
335
+ }
336
+ if ( node . type === 'inlineCode' ) {
337
+ return (
338
+ < span className = 'dark:text-red-200 dark:bg-red-950/30 px-1 rounded font-mono text-[0.9em]' > { node . value } </ span >
339
+ )
340
+ }
341
+
342
+ if (
343
+ node . type === 'heading'
344
+ ) {
345
+ return (
346
+ < span className = 'font-semibold font-mono dark:text-purple-300' >
347
+ { node . children ?. map ( ( child ) => transform ( child ) ) }
348
+ </ span >
349
+ )
350
+ }
351
+
352
+ if ( node . type === 'code' ) {
353
+ const language = node . lang || ''
354
+
355
+ const html = node . data ?. [ 'html' ]
356
+ const props = {
357
+ title : '' ,
358
+ ...( node . data ?. hProperties ?? { } ) ,
359
+
360
+ lang : language ,
361
+ }
362
+
363
+ return (
364
+ < CodeBlock { ...props } >
365
+ < Pre > { html ? < div className = 'content' dangerouslySetInnerHTML = { { __html : html } } > </ div > : node . value } </ Pre >
366
+ </ CodeBlock >
367
+ )
368
+ }
369
+ }
370
+
371
+
372
+
322
373
export function ChatMarkdown ( { ...rest } : MarkdownRendererProps ) {
323
- return < MarkdownRuntime addMarkdownLineNumbers = { false } isStreaming = { true } { ...rest } extension = 'md' />
374
+ return < MarkdownRuntime addMarkdownLineNumbers = { false } renderNode = { renderChatNode } isStreaming = { true } { ...rest } extension = 'md' />
324
375
}
325
376
326
377
function WelcomeMessage ( ) {
@@ -615,7 +666,7 @@ function Footer() {
615
666
. map ( ( file ) => `@${ file . path . replace ( / \. m d x \? $ / , '' ) } ` )
616
667
617
668
return (
618
- < AnimatePresence custom = { false } onExitComplete = { ( ) => { } } >
669
+ < AnimatePresence custom = { false } onExitComplete = { ( ) => { } } >
619
670
< div className = ' sticky bottom-4 z-50 w-full mt-4' >
620
671
< motion . div
621
672
layoutId = 'textarea'
0 commit comments