@@ -4,7 +4,14 @@ import React, { useEffect, useState, useRef } from "react";
44import { useSession } from "next-auth/react" ;
55import { Textarea } from "@/components/ui/textarea" ;
66import { Button } from "@/components/ui/button" ;
7- import { MicrophoneIcon } from "@phosphor-icons/react" ;
7+ import {
8+ CopyIcon ,
9+ MicrophoneIcon ,
10+ RobotIcon ,
11+ ThumbsDownIcon ,
12+ ThumbsUpIcon ,
13+ UserIcon ,
14+ } from "@phosphor-icons/react" ;
815import { useParams } from "next/navigation" ;
916
1017import { SpinnerGapIcon } from "@phosphor-icons/react/dist/ssr" ;
@@ -196,152 +203,182 @@ const Chat = () => {
196203
197204 return (
198205 < div className = "h-[96vh] w-full" >
199- < div className = "relative flex h-full w-full flex-col border " >
200- < div className = "flex flex-1 flex-col overflow-y-auto px-4 pb-40 md:px-8 lg:px-16 " >
206+ < div className = "relative flex h-full w-full flex-col" >
207+ < div className = "flex flex-1 flex-col overflow-y-auto px-4 pb-40 md:px-4 " >
201208 < div className = "mx-auto w-full max-w-4xl py-4" >
202209 { messages . length === 0 ? (
203210 < div className = "text-muted-foreground flex h-[50vh] items-center justify-center" >
204211 Start a conversation by typing a message below
205212 </ div >
206213 ) : (
207- < >
208- { messages . map ( ( message ) => (
209- < div
210- key = { message . id }
211- className = { `mb-8 flex w-fit flex-col gap-2` }
212- >
213- < div className = "font-medium" >
214- { message . role === "user" ? "You" : "AI" }
215- </ div >
216- < div className = "prose dark:prose-invert max-w-none" >
217- < ReactMarkdown
218- remarkPlugins = { [ remarkGfm ] }
219- components = { {
220- code ( props ) {
221- const { children, className } = props ;
222- const match = / l a n g u a g e - ( \w + ) / . exec (
223- className ?? "" ,
224- ) ;
225- const isInline = ! match ;
226-
227- return isInline ? (
228- < code
229- className = { cn (
230- "rounded-sm bg-[#231f2e] px-1 py-0.5 text-zinc-300" ,
231- geistMono . className ,
232- ) }
233- >
234- { children }
235- </ code >
236- ) : (
237- < div className = "my-4 overflow-hidden rounded-md" >
238- < div className = "bg-[#231f2e] px-4 py-2 text-sm text-zinc-400" >
239- { match ? match [ 1 ] : "bash" }
240- </ div >
241- < SyntaxHighlighter
242- language = { match ? match [ 1 ] : "bash" }
243- style = { {
244- hljs : { background : "#231f2e" } ,
245- "hljs-comment" : { color : "#6c7086" } ,
246- "hljs-keyword" : { color : "#9d7cd8" } ,
247- "hljs-built_in" : { color : "#7aa2f7" } ,
248- "hljs-string" : { color : "#c4a7e7" } ,
249- "hljs-variable" : { color : "#7dcfff" } ,
250- "hljs-title" : { color : "#7aa2f7" } ,
251- "hljs-attr" : { color : "#ff9e64" } ,
252- "hljs-symbol" : { color : "#bb9af7" } ,
253- "hljs-bullet" : { color : "#73daca" } ,
254- "hljs-literal" : { color : "#ff9e64" } ,
255- "hljs-number" : { color : "#ff9e64" } ,
256- "hljs-regexp" : { color : "#b4f9f8" } ,
257- "hljs-meta" : { color : "#7dcfff" } ,
258- } }
259- customStyle = { {
260- background : "#231f2e" ,
261- padding : "1rem" ,
262- margin : 0 ,
263- borderBottomLeftRadius : "0.375rem" ,
264- borderBottomRightRadius : "0.375rem" ,
265- fontSize : "0.9rem" ,
266- } }
267- codeTagProps = { {
268- className : geistMono . className ,
269- } }
270- PreTag = "div"
271- showLineNumbers = { false }
214+ < div className = "no-scrollbar mt-6 flex h-full w-full flex-1 flex-col gap-4 overflow-y-auto px-4 pt-4 pb-10 md:px-8" >
215+ < div className = "mx-auto h-full w-full max-w-4xl" >
216+ { messages . map ( ( message ) => (
217+ < div
218+ key = { message . id }
219+ className = { `mb-8 flex w-fit flex-col gap-2` }
220+ >
221+ < div className = "font-medium" >
222+ { message . role === "user" ? (
223+ < div className = "flex w-fit items-center gap-2 text-base font-semibold" >
224+ < div className = "bg-accent flex size-6 items-center justify-center rounded-md" >
225+ < UserIcon weight = "bold" />
226+ </ div >
227+ < div > You</ div >
228+ </ div >
229+ ) : (
230+ < div className = "flex w-fit items-center gap-2 text-base font-semibold" >
231+ < div className = "bg-accent flex size-6 items-center justify-center rounded-md" >
232+ < RobotIcon weight = "bold" />
233+ </ div >
234+ < div > AI</ div >
235+ </ div >
236+ ) }
237+ </ div >
238+ < div
239+ className = { cn (
240+ "prose dark:prose-invert max-w-none rounded-lg px-4 py-2" ,
241+ message . role === "user"
242+ ? "bg-primary w-fit max-w-full font-bold"
243+ : "bg-muted w-full border" ,
244+ ) }
245+ >
246+ < ReactMarkdown
247+ remarkPlugins = { [ remarkGfm ] }
248+ components = { {
249+ code ( props ) {
250+ const { children, className, ...rest } = props ;
251+ const match = / l a n g u a g e - ( \w + ) / . exec (
252+ className ?? "" ,
253+ ) ;
254+ const isInline = ! match ;
255+
256+ return isInline ? (
257+ < code
258+ className = { cn (
259+ "rounded-sm bg-[#231f2e] px-1 py-0.5 text-zinc-300" ,
260+ geistMono . className ,
261+ ) }
262+ { ...rest }
272263 >
273- { Array . isArray ( children )
274- ? children . join ( "" )
275- : typeof children === "string"
276- ? children
277- : "" }
278- </ SyntaxHighlighter >
279- </ div >
280- ) ;
281- } ,
282- strong ( props ) {
283- return (
264+ { children }
265+ </ code >
266+ ) : (
267+ < div className = "my-4 overflow-hidden rounded-md" >
268+ < div className = "bg-[#231f2e] px-4 py-2 text-sm text-zinc-400" >
269+ { match ? match [ 1 ] : "bash" }
270+ </ div >
271+ < SyntaxHighlighter
272+ language = { match ? match [ 1 ] : "bash" }
273+ style = { {
274+ hljs : { background : "#231f2e" } ,
275+ "hljs-comment" : { color : "#6c7086" } ,
276+ "hljs-keyword" : { color : "#9d7cd8" } ,
277+ "hljs-built_in" : { color : "#7aa2f7" } ,
278+ "hljs-string" : { color : "#c4a7e7" } ,
279+ "hljs-variable" : { color : "#7dcfff" } ,
280+ "hljs-title" : { color : "#7aa2f7" } ,
281+ "hljs-attr" : { color : "#ff9e64" } ,
282+ "hljs-symbol" : { color : "#bb9af7" } ,
283+ "hljs-bullet" : { color : "#73daca" } ,
284+ "hljs-literal" : { color : "#ff9e64" } ,
285+ "hljs-number" : { color : "#ff9e64" } ,
286+ "hljs-regexp" : { color : "#b4f9f8" } ,
287+ "hljs-meta" : { color : "#7dcfff" } ,
288+ } }
289+ customStyle = { {
290+ background : "#231f2e" ,
291+ padding : "1rem" ,
292+ margin : 0 ,
293+ borderBottomLeftRadius : "0.375rem" ,
294+ borderBottomRightRadius : "0.375rem" ,
295+ fontSize : "0.9rem" ,
296+ } }
297+ codeTagProps = { {
298+ className : geistMono . className ,
299+ } }
300+ PreTag = "div"
301+ showLineNumbers = { false }
302+ >
303+ { Array . isArray ( children )
304+ ? children . join ( "" )
305+ : typeof children === "string"
306+ ? children
307+ : "" }
308+ </ SyntaxHighlighter >
309+ </ div >
310+ ) ;
311+ } ,
312+ strong : ( props ) => (
284313 < span className = "font-bold" >
285314 { props . children }
286315 </ span >
287- ) ;
288- } ,
289- h1 ( props ) {
290- return (
316+ ) ,
317+ a : ( props ) => (
318+ < a
319+ className = "text-primary underline"
320+ href = { props . href }
321+ >
322+ { props . children }
323+ </ a >
324+ ) ,
325+ h1 : ( props ) => (
291326 < h1 className = "my-4 text-2xl font-bold" >
292327 { props . children }
293328 </ h1 >
294- ) ;
295- } ,
296- h2 ( props ) {
297- return (
329+ ) ,
330+ h2 : ( props ) => (
298331 < h2 className = "my-3 text-xl font-bold" >
299332 { props . children }
300333 </ h2 >
301- ) ;
302- } ,
303- h3 ( props ) {
304- return (
334+ ) ,
335+ h3 : ( props ) => (
305336 < h3 className = "my-2 text-lg font-bold" >
306337 { props . children }
307338 </ h3 >
308- ) ;
309- } ,
310- a ( props ) {
311- return (
312- < a
313- className = "text-primary underline"
314- href = { props . href }
315- >
316- { props . children }
317- </ a >
318- ) ;
319- } ,
320- } }
321- >
322- { message . content }
323- </ ReactMarkdown >
339+ ) ,
340+ } }
341+ >
342+ { message . content }
343+ </ ReactMarkdown >
344+ </ div >
345+ < div className = "font-medium" >
346+ { message . role === "assistant" && (
347+ < div className = "flex w-fit items-center text-base font-semibold" >
348+ < div className = "hover:bg-accent flex size-7 items-center justify-center rounded-lg" >
349+ < ThumbsUpIcon weight = "bold" />
350+ </ div >
351+ < div className = "hover:bg-accent flex size-7 items-center justify-center rounded-lg" >
352+ < ThumbsDownIcon weight = "bold" />
353+ </ div >
354+ < div className = "hover:bg-accent flex size-7 items-center justify-center rounded-lg" >
355+ < CopyIcon weight = "bold" />
356+ </ div >
357+ </ div >
358+ ) }
359+ </ div >
324360 </ div >
325- </ div >
326- ) ) }
327- { isLoading && (
328- < div className = "bg-muted mb-4 flex w-full items-start gap-2 self-start rounded-lg p-4 md:w-5/6 lg:w-3/4" >
329- < div className = "font-medium" > AI</ div >
330- < SpinnerGapIcon className = "h-5 w-5 animate-spin" />
331- </ div >
332- ) }
333- </ >
361+ ) ) }
362+ { isLoading && (
363+ < div className = "bg-muted mb-4 flex w-full items-start gap-2 self-start rounded-lg p-4 md:w-5/6 lg:w-3/4" >
364+ < div className = "font-medium" > AI</ div >
365+ < SpinnerGapIcon className = "h-5 w-5 animate-spin" />
366+ </ div >
367+ ) }
368+ < div ref = { messagesEndRef } />
369+ </ div >
370+ </ div >
334371 ) }
335372 < div ref = { messagesEndRef } />
336373 </ div >
337374 </ div >
338375
339376 { /* Input Form */ }
340- < div className = "bg-muted border-border/20 absolute bottom-0 w-full border-t p-2" >
377+ < div className = "bg-muted border-border/20 absolute bottom-0 w-full rounded-xl border-t p-2" >
341378 < div className = "mx-auto w-full max-w-4xl" >
342379 < form
343380 onSubmit = { handleSubmit }
344- className = "bg-accent/30 flex w-full flex-col rounded-xl p-3 pb-6 "
381+ className = "bg-accent/30 flex w-full flex-col rounded-xl p-3 pb-3 "
345382 >
346383 < Textarea
347384 value = { input }
0 commit comments