@@ -6,6 +6,8 @@ import remarkGfm from "remark-gfm";
6
6
import Image from "next/image" ;
7
7
import aptosLogo from "../../public/favicon/favicon.png" ;
8
8
import { cn } from "utils/cn" ;
9
+ import { Prism as SyntaxHighlighter } from "react-syntax-highlighter" ;
10
+ import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism" ;
9
11
10
12
export interface ChatMessageProps {
11
13
message : Message ;
@@ -25,39 +27,155 @@ export function ChatMessage({
25
27
return (
26
28
< div
27
29
className = { cn (
28
- "flex items-start gap-4 rounded-lg p-4 " ,
29
- isUser ? "bg-gray-900 " : "bg-black " ,
30
+ "flex w-full items-start gap-4 p-6 font-['Satoshi'] " ,
31
+ isUser ? "bg-black " : "bg-gray-900 " ,
30
32
className ,
31
33
) }
32
34
>
33
35
{ isUser ? (
34
- < div className = "flex h-8 w-8 items-center justify-center rounded-full bg-blue-600" >
36
+ < div className = "flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-full bg-blue-600" >
35
37
< User className = "h-4 w-4 text-white" />
36
38
</ div >
37
39
) : (
38
- < div className = "flex h-8 w-8 items-center justify-center rounded-full bg-white" >
40
+ < div className = "flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-full bg-white" >
39
41
< Image src = { aptosLogo } alt = "Aptos AI" className = "h-5 w-5" />
40
42
</ div >
41
43
) }
42
44
43
- < div className = "flex-1 space-y-2 " >
44
- < div className = "prose prose-invert max-w-none text-white " >
45
+ < div className = "flex min-w-0 flex-1 flex-col gap-2 overflow-hidden " >
46
+ < div className = "prose prose-invert max-w-none break-words text-gray-100 " >
45
47
< ReactMarkdown
46
48
remarkPlugins = { [ remarkGfm ] }
47
49
components = { {
48
- code ( { className, children } ) {
50
+ code ( props ) {
51
+ const { children, className } = props ;
52
+ // @ts -ignore - inline is actually available in the props
53
+ const isInline = props . inline ;
54
+
55
+ if ( isInline ) {
56
+ return (
57
+ < code className = "rounded bg-gray-800 px-1.5 py-0.5 text-sm text-gray-100 font-mono" >
58
+ { children }
59
+ </ code >
60
+ ) ;
61
+ }
62
+
49
63
const match = / l a n g u a g e - ( \w + ) / . exec ( className || "" ) ;
64
+ const lang = match ? match [ 1 ] : "" ;
65
+
50
66
return (
51
- < pre
52
- className = { cn (
53
- "rounded-lg bg-gray-800 p-4 text-white" ,
54
- match && `language-${ match [ 1 ] } ` ,
55
- ) }
56
- >
57
- < code className = { cn ( "text-white" , className ) } >
67
+ < div className = "relative !mt-4 max-w-full" >
68
+ < div className = "absolute right-2 top-2 z-10" >
69
+ < button
70
+ onClick = { ( ) => {
71
+ navigator . clipboard . writeText ( children as string ) ;
72
+ } }
73
+ className = "rounded bg-gray-700/50 p-1.5 text-gray-400 transition-colors hover:bg-gray-700 hover:text-white"
74
+ >
75
+ < Copy className = "h-4 w-4" />
76
+ </ button >
77
+ </ div >
78
+ < div className = "max-w-full overflow-x-auto" >
79
+ < SyntaxHighlighter
80
+ language = { lang }
81
+ style = { oneDark }
82
+ customStyle = { {
83
+ margin : 0 ,
84
+ borderRadius : "0.5rem" ,
85
+ padding : "1rem" ,
86
+ backgroundColor : "rgb(31 41 55)" ,
87
+ } }
88
+ codeTagProps = { {
89
+ className : "font-mono text-sm" ,
90
+ } }
91
+ wrapLongLines = { true }
92
+ >
93
+ { String ( children ) . replace ( / \n $ / , "" ) }
94
+ </ SyntaxHighlighter >
95
+ </ div >
96
+ </ div >
97
+ ) ;
98
+ } ,
99
+ p ( { children } ) {
100
+ return (
101
+ < p className = "mb-4 text-gray-100 text-[0.9375rem] leading-[1.625] font-normal last:mb-0 break-words" >
102
+ { children }
103
+ </ p >
104
+ ) ;
105
+ } ,
106
+ ul ( { children } ) {
107
+ return (
108
+ < ul className = "mb-4 list-disc pl-4 text-gray-100 text-[0.9375rem] leading-[1.625] last:mb-0" >
109
+ { children }
110
+ </ ul >
111
+ ) ;
112
+ } ,
113
+ ol ( { children } ) {
114
+ return (
115
+ < ol className = "mb-4 list-decimal pl-4 text-gray-100 text-[0.9375rem] leading-[1.625] last:mb-0" >
116
+ { children }
117
+ </ ol >
118
+ ) ;
119
+ } ,
120
+ li ( { children } ) {
121
+ return (
122
+ < li className = "mb-1 text-gray-100 text-[0.9375rem] leading-[1.625] last:mb-0" >
123
+ { children }
124
+ </ li >
125
+ ) ;
126
+ } ,
127
+ table ( { children } ) {
128
+ return (
129
+ < div className = "my-4 w-full overflow-x-auto" >
130
+ < table className = "w-full border-collapse text-left text-gray-100 text-[0.9375rem]" >
58
131
{ children }
59
- </ code >
60
- </ pre >
132
+ </ table >
133
+ </ div >
134
+ ) ;
135
+ } ,
136
+ th ( { children } ) {
137
+ return (
138
+ < th className = "border border-gray-600 bg-gray-800 px-4 py-2 text-left text-gray-100 font-semibold" >
139
+ { children }
140
+ </ th >
141
+ ) ;
142
+ } ,
143
+ td ( { children } ) {
144
+ return (
145
+ < td className = "border border-gray-600 px-4 py-2 text-gray-100 whitespace-normal break-words" >
146
+ { children }
147
+ </ td >
148
+ ) ;
149
+ } ,
150
+ a ( { children, href } ) {
151
+ return (
152
+ < a
153
+ href = { href }
154
+ className = "text-blue-400 hover:text-blue-300 font-medium break-words"
155
+ >
156
+ { children }
157
+ </ a >
158
+ ) ;
159
+ } ,
160
+ h1 ( { children } ) {
161
+ return (
162
+ < h1 className = "mt-6 mb-4 text-2xl font-semibold text-gray-100 break-words" >
163
+ { children }
164
+ </ h1 >
165
+ ) ;
166
+ } ,
167
+ h2 ( { children } ) {
168
+ return (
169
+ < h2 className = "mt-6 mb-4 text-xl font-semibold text-gray-100 break-words" >
170
+ { children }
171
+ </ h2 >
172
+ ) ;
173
+ } ,
174
+ h3 ( { children } ) {
175
+ return (
176
+ < h3 className = "mt-6 mb-4 text-lg font-semibold text-gray-100 break-words" >
177
+ { children }
178
+ </ h3 >
61
179
) ;
62
180
} ,
63
181
} }
@@ -67,13 +185,13 @@ export function ChatMessage({
67
185
</ div >
68
186
69
187
{ ! isUser && (
70
- < div className = "flex items-center gap-4" >
188
+ < div className = "flex items-center gap-4 pt-2 " >
71
189
< Tooltip . Provider >
72
190
< Tooltip . Root >
73
- < Tooltip . Trigger >
191
+ < Tooltip . Trigger asChild >
74
192
< button
75
193
onClick = { ( ) => onCopy ?.( ) }
76
- className = "text-gray-400 hover:text-white"
194
+ className = "rounded p-1 text-gray-400 transition-colors hover:bg-[#1F1F1F] hover:text-white"
77
195
>
78
196
< Copy className = "h-4 w-4" />
79
197
</ button >
@@ -89,11 +207,11 @@ export function ChatMessage({
89
207
</ Tooltip . Root >
90
208
91
209
< Tooltip . Root >
92
- < Tooltip . Trigger >
210
+ < Tooltip . Trigger asChild >
93
211
< button
94
212
onClick = { ( ) => onFeedback ?.( "positive" ) }
95
213
className = { cn (
96
- "text-gray-400 hover:text-green-500" ,
214
+ "rounded p-1 text-gray-400 transition-colors hover:bg-[#1F1F1F] hover:text-green-500" ,
97
215
message . feedback === "positive" && "text-green-500" ,
98
216
) }
99
217
>
@@ -111,11 +229,11 @@ export function ChatMessage({
111
229
</ Tooltip . Root >
112
230
113
231
< Tooltip . Root >
114
- < Tooltip . Trigger >
232
+ < Tooltip . Trigger asChild >
115
233
< button
116
234
onClick = { ( ) => onFeedback ?.( "negative" ) }
117
235
className = { cn (
118
- "text-gray-400 hover:text-red-500" ,
236
+ "rounded p-1 text-gray-400 transition-colors hover:bg-[#1F1F1F] hover:text-red-500" ,
119
237
message . feedback === "negative" && "text-red-500" ,
120
238
) }
121
239
>
0 commit comments