Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ div {
/* Scroll to top button */
.scroll-to-top {
position: fixed;
bottom: 5rem;
right: 2%;
bottom: 1.5rem;
right: 6rem;
z-index: 1000;
}

Expand Down
61 changes: 40 additions & 21 deletions src/components/ai-chat/ChatWidgetClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
{isOpen && (
<div className="mb-4 flex h-[500px] w-[360px] flex-col overflow-hidden rounded-2xl border border-divider bg-white dark:bg-zinc-900 shadow-2xl transition-all text-foreground">
{/* Header */}
<div className="flex items-center justify-between bg-teal-600 px-4 py-3 text-white">
<div className="flex items-center justify-between bg-primary px-4 py-3 text-white">
<h3 className="text-sm font-semibold">Ask about Amr</h3>
<button
onClick={toggleChat}
Expand All @@ -94,14 +94,10 @@
Ask me anything about Amr's experience, projects, or skills 🙂
</div>
)}
{messages.map((m) => {
const content =
m.parts
?.filter((p) => p.type === "text")
.map((p) => p.text)
.join("") || "";

if (!content && m.role === "assistant" && isLoading) return null;
{messages.map((m, index) => {
const isLast = index === messages.length - 1;
const showTypingIndicator =
isLast && m.role === "assistant" && isLoading;

return (
<div
Expand All @@ -111,26 +107,49 @@
<div
className={`max-w-[85%] rounded-2xl px-4 py-2 text-sm ${
m.role === "user"
? "bg-teal-600 text-white"
? "bg-primary text-white"
: "bg-default-100 text-default-900"
}`}
>
<div className="prose prose-sm dark:prose-invert max-w-none">
<ReactMarkdown>{content}</ReactMarkdown>
{m.parts?.map((part, i) => {
if (part.type === "text") {
return <ReactMarkdown key={i}>{part.text}</ReactMarkdown>;

Check warning on line 117 in src/components/ai-chat/ChatWidgetClient.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not use Array index in keys

See more on https://sonarcloud.io/project/issues?id=amrabed.github.io&issues=AZ6lHDATD-z0Gq6gr1wF&open=AZ6lHDATD-z0Gq6gr1wF&pullRequest=75
}
if (part.type === "reasoning") {
return (
<div
key={i}

Check warning on line 122 in src/components/ai-chat/ChatWidgetClient.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not use Array index in keys

See more on https://sonarcloud.io/project/issues?id=amrabed.github.io&issues=AZ6lHDATD-z0Gq6gr1wG&open=AZ6lHDATD-z0Gq6gr1wG&pullRequest=75
className="mb-2 italic text-default-500 border-l-2 border-default-300 pl-2 text-xs"
>
Thinking: {part.text}
</div>
);
}
return null;
})}
{showTypingIndicator && (
<div className="flex items-center gap-1 mt-2">
<div className="h-1 w-1 animate-bounce rounded-full bg-default-400 [animation-delay:-0.3s]"></div>
<div className="h-1 w-1 animate-bounce rounded-full bg-default-400 [animation-delay:-0.15s]"></div>
<div className="h-1 w-1 animate-bounce rounded-full bg-default-400"></div>
</div>
)}
</div>
</div>
</div>
);
})}
{isLoading && (
<div className="flex justify-start">
<div className="flex items-center gap-1 bg-default-100 rounded-2xl px-4 py-3">
<div className="h-1.5 w-1.5 animate-bounce rounded-full bg-default-400 [animation-delay:-0.3s]"></div>
<div className="h-1.5 w-1.5 animate-bounce rounded-full bg-default-400 [animation-delay:-0.15s]"></div>
<div className="h-1.5 w-1.5 animate-bounce rounded-full bg-default-400"></div>
{status === "submitted" &&
messages[messages.length - 1]?.role !== "assistant" && (
<div className="flex justify-start">
<div className="flex items-center gap-1 bg-default-100 rounded-2xl px-4 py-3">
<div className="h-1.5 w-1.5 animate-bounce rounded-full bg-default-400 [animation-delay:-0.3s]"></div>
<div className="h-1.5 w-1.5 animate-bounce rounded-full bg-default-400 [animation-delay:-0.15s]"></div>
<div className="h-1.5 w-1.5 animate-bounce rounded-full bg-default-400"></div>
</div>
</div>
</div>
)}
)}
{error && (
<div className="text-center text-xs text-danger px-4">
{isRateLimited
Expand Down Expand Up @@ -163,7 +182,7 @@
isIconOnly
type="submit"
variant="primary"
className="bg-teal-600 text-white min-w-8 w-8 h-8"
className="bg-primary text-white min-w-8 w-8 h-8"
isDisabled={isLoading || !input?.trim()}
>
<Send size={16} />
Expand All @@ -178,7 +197,7 @@
isIconOnly
onClick={toggleChat}
aria-label="Open AI assistant"
className="h-14 w-14 rounded-full bg-teal-600 text-white shadow-lg hover:scale-110 transition-transform"
className="h-14 w-14 rounded-full bg-primary text-white shadow-lg hover:scale-110 transition-transform"
>
{isOpen ? <X size={24} /> : <MessageCircle size={24} />}
</Button>
Expand Down