1
+ import "@assistant-ui/react-markdown/styles/dot.css" ;
2
+
3
+ import {
4
+ CodeHeaderProps ,
5
+ MarkdownTextPrimitive ,
6
+ unstable_memoizeMarkdownComponents as memoizeMarkdownComponents ,
7
+ useIsMarkdownCodeBlock ,
8
+ } from "@assistant-ui/react-markdown" ;
9
+ import remarkGfm from "remark-gfm" ;
10
+ import { FC , memo , useState } from "react" ;
11
+ import { CheckIcon , CopyIcon } from "lucide-react" ;
12
+
13
+ import { TooltipIconButton } from "./tooltip-icon-button" ;
14
+ import { cn } from "../../utils/cn" ;
15
+
16
+ const MarkdownTextImpl = ( ) => {
17
+ return (
18
+ < MarkdownTextPrimitive
19
+ remarkPlugins = { [ remarkGfm ] }
20
+ className = "aui-md"
21
+ components = { defaultComponents }
22
+ />
23
+ ) ;
24
+ } ;
25
+
26
+ export const MarkdownText = memo ( MarkdownTextImpl ) ;
27
+
28
+ const CodeHeader : FC < CodeHeaderProps > = ( { language, code } ) => {
29
+ const { isCopied, copyToClipboard } = useCopyToClipboard ( ) ;
30
+ const onCopy = ( ) => {
31
+ if ( ! code || isCopied ) return ;
32
+ copyToClipboard ( code ) ;
33
+ } ;
34
+
35
+ return (
36
+ < div className = "aui-code-header-root" >
37
+ < span className = "aui-code-header-language" > { language } </ span >
38
+ < TooltipIconButton tooltip = "Copy" onClick = { onCopy } >
39
+ { ! isCopied && < CopyIcon /> }
40
+ { isCopied && < CheckIcon /> }
41
+ </ TooltipIconButton >
42
+ </ div >
43
+ ) ;
44
+ } ;
45
+
46
+ const useCopyToClipboard = ( {
47
+ copiedDuration = 3000 ,
48
+ } : {
49
+ copiedDuration ?: number ;
50
+ } = { } ) => {
51
+ const [ isCopied , setIsCopied ] = useState < boolean > ( false ) ;
52
+
53
+ const copyToClipboard = ( value : string ) => {
54
+ if ( ! value ) return ;
55
+
56
+ navigator . clipboard . writeText ( value ) . then ( ( ) => {
57
+ setIsCopied ( true ) ;
58
+ setTimeout ( ( ) => setIsCopied ( false ) , copiedDuration ) ;
59
+ } ) ;
60
+ } ;
61
+
62
+ return { isCopied, copyToClipboard } ;
63
+ } ;
64
+
65
+ const defaultComponents = memoizeMarkdownComponents ( {
66
+ h1 : ( { className, ...props } ) => (
67
+ < h1 className = { cn ( "aui-md-h1" , className ) } { ...props } />
68
+ ) ,
69
+ h2 : ( { className, ...props } ) => (
70
+ < h2 className = { cn ( "aui-md-h2" , className ) } { ...props } />
71
+ ) ,
72
+ h3 : ( { className, ...props } ) => (
73
+ < h3 className = { cn ( "aui-md-h3" , className ) } { ...props } />
74
+ ) ,
75
+ h4 : ( { className, ...props } ) => (
76
+ < h4 className = { cn ( "aui-md-h4" , className ) } { ...props } />
77
+ ) ,
78
+ h5 : ( { className, ...props } ) => (
79
+ < h5 className = { cn ( "aui-md-h5" , className ) } { ...props } />
80
+ ) ,
81
+ h6 : ( { className, ...props } ) => (
82
+ < h6 className = { cn ( "aui-md-h6" , className ) } { ...props } />
83
+ ) ,
84
+ p : ( { className, ...props } ) => (
85
+ < p className = { cn ( "aui-md-p" , className ) } { ...props } />
86
+ ) ,
87
+ a : ( { className, ...props } ) => (
88
+ < a className = { cn ( "aui-md-a" , className ) } { ...props } />
89
+ ) ,
90
+ blockquote : ( { className, ...props } ) => (
91
+ < blockquote className = { cn ( "aui-md-blockquote" , className ) } { ...props } />
92
+ ) ,
93
+ ul : ( { className, ...props } ) => (
94
+ < ul className = { cn ( "aui-md-ul" , className ) } { ...props } />
95
+ ) ,
96
+ ol : ( { className, ...props } ) => (
97
+ < ol className = { cn ( "aui-md-ol" , className ) } { ...props } />
98
+ ) ,
99
+ hr : ( { className, ...props } ) => (
100
+ < hr className = { cn ( "aui-md-hr" , className ) } { ...props } />
101
+ ) ,
102
+ table : ( { className, ...props } ) => (
103
+ < table className = { cn ( "aui-md-table" , className ) } { ...props } />
104
+ ) ,
105
+ th : ( { className, ...props } ) => (
106
+ < th className = { cn ( "aui-md-th" , className ) } { ...props } />
107
+ ) ,
108
+ td : ( { className, ...props } ) => (
109
+ < td className = { cn ( "aui-md-td" , className ) } { ...props } />
110
+ ) ,
111
+ tr : ( { className, ...props } ) => (
112
+ < tr className = { cn ( "aui-md-tr" , className ) } { ...props } />
113
+ ) ,
114
+ sup : ( { className, ...props } ) => (
115
+ < sup className = { cn ( "aui-md-sup" , className ) } { ...props } />
116
+ ) ,
117
+ pre : ( { className, ...props } ) => (
118
+ < pre className = { cn ( "aui-md-pre" , className ) } { ...props } />
119
+ ) ,
120
+ code : function Code ( { className, ...props } ) {
121
+ const isCodeBlock = useIsMarkdownCodeBlock ( ) ;
122
+ return (
123
+ < code
124
+ className = { cn ( ! isCodeBlock && "aui-md-inline-code" , className ) }
125
+ { ...props }
126
+ />
127
+ ) ;
128
+ } ,
129
+ CodeHeader,
130
+ } ) ;
0 commit comments