@@ -6,6 +6,8 @@ import remarkGfm from "remark-gfm";
66import Image from "next/image" ;
77import aptosLogo from "../../public/favicon/favicon.png" ;
88import { cn } from "utils/cn" ;
9+ import { Prism as SyntaxHighlighter } from "react-syntax-highlighter" ;
10+ import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism" ;
911
1012export interface ChatMessageProps {
1113 message : Message ;
@@ -25,39 +27,155 @@ export function ChatMessage({
2527 return (
2628 < div
2729 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 " ,
3032 className ,
3133 ) }
3234 >
3335 { 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" >
3537 < User className = "h-4 w-4 text-white" />
3638 </ div >
3739 ) : (
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" >
3941 < Image src = { aptosLogo } alt = "Aptos AI" className = "h-5 w-5" />
4042 </ div >
4143 ) }
4244
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 " >
4547 < ReactMarkdown
4648 remarkPlugins = { [ remarkGfm ] }
4749 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+
4963 const match = / l a n g u a g e - ( \w + ) / . exec ( className || "" ) ;
64+ const lang = match ? match [ 1 ] : "" ;
65+
5066 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]" >
58131 { 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 >
61179 ) ;
62180 } ,
63181 } }
@@ -67,13 +185,13 @@ export function ChatMessage({
67185 </ div >
68186
69187 { ! isUser && (
70- < div className = "flex items-center gap-4" >
188+ < div className = "flex items-center gap-4 pt-2 " >
71189 < Tooltip . Provider >
72190 < Tooltip . Root >
73- < Tooltip . Trigger >
191+ < Tooltip . Trigger asChild >
74192 < button
75193 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"
77195 >
78196 < Copy className = "h-4 w-4" />
79197 </ button >
@@ -89,11 +207,11 @@ export function ChatMessage({
89207 </ Tooltip . Root >
90208
91209 < Tooltip . Root >
92- < Tooltip . Trigger >
210+ < Tooltip . Trigger asChild >
93211 < button
94212 onClick = { ( ) => onFeedback ?.( "positive" ) }
95213 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" ,
97215 message . feedback === "positive" && "text-green-500" ,
98216 ) }
99217 >
@@ -111,11 +229,11 @@ export function ChatMessage({
111229 </ Tooltip . Root >
112230
113231 < Tooltip . Root >
114- < Tooltip . Trigger >
232+ < Tooltip . Trigger asChild >
115233 < button
116234 onClick = { ( ) => onFeedback ?.( "negative" ) }
117235 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" ,
119237 message . feedback === "negative" && "text-red-500" ,
120238 ) }
121239 >
0 commit comments