Skip to content

Commit a49321b

Browse files
Completed the AI Chatbox design
1 parent cd4f806 commit a49321b

File tree

1 file changed

+83
-6
lines changed

1 file changed

+83
-6
lines changed

components/ai-chatbox.tsx

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { cn } from '@/lib/utils';
22
import { useChat } from 'ai/react';
3-
import { Send, XCircle } from 'lucide-react';
3+
import { Bot, Send, Trash, XCircle } from 'lucide-react';
44
import { Input } from './ui/input';
55
import { Button } from './ui/button';
66
import { Message } from 'ai';
7+
import { useUser } from '@clerk/nextjs';
8+
import Image from 'next/image';
9+
import { useEffect, useRef } from 'react';
710

811
interface AIChatBoxProps {
912
open: boolean,
@@ -13,6 +16,23 @@ interface AIChatBoxProps {
1316
const AIChatBox = ({ open, onClose }: AIChatBoxProps) => {
1417
const { messages, input, handleInputChange, handleSubmit, setMessages, isLoading, error } = useChat();
1518

19+
const inputRef = useRef<HTMLInputElement>(null);
20+
const scrollRef = useRef<HTMLDivElement>(null);
21+
22+
useEffect(() => {
23+
if (scrollRef.current) {
24+
scrollRef.current.scrollTop = scrollRef.current.scrollHeight
25+
}
26+
}, [messages])
27+
28+
useEffect(() => {
29+
if (open) {
30+
inputRef.current?.focus()
31+
}
32+
}, [open])
33+
34+
const lastMessageIsUser = messages[messages.length - 1]?.role === "user"
35+
1636
return (
1737
<div
1838
className={cn('bottom-0 right-0 md:right-2 md:bottom-2 z-10 h-full md:h-auto w-full max-w-[500px] p-1', open ? "fixed" : "hidden")}
@@ -21,16 +41,54 @@ const AIChatBox = ({ open, onClose }: AIChatBoxProps) => {
2141
<button onClick={onClose} className='mb-1 ms-auto block p-3'>
2242
<XCircle size={30} />
2343
</button>
24-
<div className='h-full p-3'>
44+
<div className='h-full p-3 overflow-y-auto' ref={scrollRef}>
2545
{messages.map((message) => (
2646
<ChatMessage key={message.id} message={message} />
2747
))}
48+
{
49+
isLoading && lastMessageIsUser && (
50+
<ChatMessage
51+
message={{
52+
role: "assistant",
53+
content: "Thinking..."
54+
}}
55+
/>
56+
)
57+
}
58+
{
59+
error && (
60+
<ChatMessage
61+
message={{
62+
role: "assistant",
63+
content: "Something went wrong."
64+
}}
65+
/>
66+
)
67+
}
68+
{
69+
!error && messages.length === 0 && (
70+
<div className='h-full flex items-center justify-center gap-3'>
71+
<Bot />
72+
Ask anything related to your notes!
73+
</div>
74+
)
75+
}
2876
</div>
2977
<form onSubmit={handleSubmit} className='m-3 flex gap-2'>
78+
<Button
79+
variant='outline'
80+
size='icon'
81+
className='shrink-0'
82+
type='button'
83+
onClick={() => setMessages([])}
84+
>
85+
<Trash />
86+
</Button>
3087
<Input
3188
value={input}
3289
onChange={handleInputChange}
3390
placeholder='Message'
91+
ref={inputRef}
3492
/>
3593
<Button type='submit' className='bg-gradient-to-r from-[#0F9E7B] to-[#1a745d]'>
3694
<Send size={20} />
@@ -43,11 +101,30 @@ const AIChatBox = ({ open, onClose }: AIChatBoxProps) => {
43101

44102
export default AIChatBox
45103

46-
function ChatMessage({ message: { role, content } }: { message: Message }) {
104+
function ChatMessage({ message: { role, content } }: { message: Pick<Message, "role" | "content"> }) {
105+
const { user } = useUser()
106+
107+
const isAiMessage = role === "assistant"
108+
47109
return (
48-
<div className='mb-3'>
49-
<div>{role}</div>
50-
<div>{content}</div>
110+
<div className={cn('mb-3 flex items-center', isAiMessage ? "me-5 justify-start" : "ms-5 justify-end")}>
111+
{isAiMessage && <Bot className='mr-2 shrink-0' />}
112+
<p className={cn(
113+
"whitespace-pre-line rounded-md border px-3 py-2", isAiMessage ? "bg-background" : "bg-primary text-primary-foreground"
114+
)}>
115+
{content}
116+
</p>
117+
{
118+
!isAiMessage && user?.imageUrl && (
119+
<Image
120+
src={user.imageUrl}
121+
alt="User Image"
122+
width={80}
123+
height={80}
124+
className='ml-2 rounded-full w-8 h-8 object-cover'
125+
/>
126+
)
127+
}
51128
</div>
52129
)
53130
}

0 commit comments

Comments
 (0)