added fade in and color transition

This commit is contained in:
Lucas Oliveira 2025-10-21 16:19:22 -03:00 committed by Mike Fortman
parent a56a39f4c4
commit b65195f0bd
3 changed files with 81 additions and 62 deletions

View file

@ -1,4 +1,5 @@
import { GitBranch } from "lucide-react";
import { motion } from "motion/react";
import DogIcon from "@/components/logo/dog-icon";
import { MarkdownRenderer } from "@/components/markdown-renderer";
import { cn } from "@/lib/utils";
@ -30,44 +31,52 @@ export function AssistantMessage({
isCompleted = false,
}: AssistantMessageProps) {
return (
<Message
icon={
<div className="w-8 h-8 rounded-lg bg-accent/20 flex items-center justify-center flex-shrink-0 select-none">
<DogIcon
className="h-6 w-6"
disabled={isCompleted}
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.2, ease: "easeOut" }}
className={isCompleted ? "opacity-50" : ""}
>
<Message
icon={
<div className="w-8 h-8 rounded-lg bg-accent/20 flex items-center justify-center flex-shrink-0 select-none">
<DogIcon
className="h-6 w-6 transition-colors duration-300"
disabled={isCompleted}
/>
</div>
}
actions={
showForkButton && onFork ? (
<button
type="button"
onClick={onFork}
className="opacity-0 group-hover:opacity-100 transition-opacity p-1 hover:bg-accent rounded text-muted-foreground hover:text-foreground"
title="Fork conversation from here"
>
<GitBranch className="h-3 w-3" />
</button>
) : undefined
}
>
<FunctionCalls
functionCalls={functionCalls}
messageIndex={messageIndex}
expandedFunctionCalls={expandedFunctionCalls}
onToggle={onToggle}
/>
<div className="relative">
<MarkdownRenderer
className={cn("text-sm py-1.5 transition-colors duration-300", isCompleted ? "text-placeholder-foreground" : "text-foreground")}
chatMessage={
isStreaming
? content +
' <span class="inline-block w-1 h-4 bg-primary ml-1 animate-pulse"></span>'
: content
}
/>
</div>
}
actions={
showForkButton && onFork ? (
<button
onClick={onFork}
className="opacity-0 group-hover:opacity-100 transition-opacity p-1 hover:bg-accent rounded text-muted-foreground hover:text-foreground"
title="Fork conversation from here"
>
<GitBranch className="h-3 w-3" />
</button>
) : undefined
}
>
<FunctionCalls
functionCalls={functionCalls}
messageIndex={messageIndex}
expandedFunctionCalls={expandedFunctionCalls}
onToggle={onToggle}
/>
<div className="relative">
<MarkdownRenderer
className={cn("text-sm py-1.5", isCompleted ? "text-placeholder-foreground" : "text-foreground")}
chatMessage={
isStreaming
? content +
' <span class="inline-block w-1 h-4 bg-primary ml-1 animate-pulse"></span>'
: content
}
/>
</div>
</Message>
</Message>
</motion.div>
);
}

View file

@ -1,4 +1,5 @@
import { User } from "lucide-react";
import { motion } from "motion/react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { useAuth } from "@/contexts/auth-context";
import { cn } from "@/lib/utils";
@ -13,29 +14,36 @@ export function UserMessage({ content, isCompleted }: UserMessageProps) {
const { user } = useAuth();
return (
<Message
icon={
<Avatar className="w-8 h-8 rounded-lg flex-shrink-0 select-none">
<AvatarImage draggable={false} src={user?.picture} alt={user?.name} />
<AvatarFallback
className={cn(
isCompleted ? "text-placeholder-foreground" : "text-primary",
"text-sm bg-accent/20 rounded-lg",
)}
>
{user?.name ? user.name.charAt(0).toUpperCase() : <User className="h-4 w-4" />}
</AvatarFallback>
</Avatar>
}
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.2, ease: "easeOut" }}
className={isCompleted ? "opacity-50" : ""}
>
<p
className={cn(
"text-foreground text-sm py-1.5 whitespace-pre-wrap break-words overflow-wrap-anywhere",
isCompleted ? "text-placeholder-foreground" : "text-foreground",
)}
<Message
icon={
<Avatar className="w-8 h-8 rounded-lg flex-shrink-0 select-none">
<AvatarImage draggable={false} src={user?.picture} alt={user?.name} />
<AvatarFallback
className={cn(
isCompleted ? "text-placeholder-foreground" : "text-primary",
"text-sm bg-accent/20 rounded-lg transition-colors duration-300",
)}
>
{user?.name ? user.name.charAt(0).toUpperCase() : <User className="h-4 w-4" />}
</AvatarFallback>
</Avatar>
}
>
{content}
</p>
</Message>
<p
className={cn(
"text-foreground text-sm py-1.5 whitespace-pre-wrap break-words overflow-wrap-anywhere transition-colors duration-300",
isCompleted ? "text-placeholder-foreground" : "text-foreground",
)}
>
{content}
</p>
</Message>
</motion.div>
);
}

View file

@ -45,10 +45,12 @@ export function OnboardingContent({
const handleNudgeClick = async (nudge: string) => {
setSelectedNudge(nudge);
setAssistantMessage(null);
setTimeout(async () => {
await sendMessage({
prompt: nudge,
previousResponseId: responseId || undefined,
});
prompt: nudge,
previousResponseId: responseId || undefined,
});
}, 1500);
};
// Determine which message to show (streaming takes precedence)