animate chat dog on new chat and focus input
This commit is contained in:
parent
f7007b625d
commit
c76e1d1b91
3 changed files with 85 additions and 18 deletions
|
|
@ -20,6 +20,7 @@ interface AssistantMessageProps {
|
||||||
isInactive?: boolean;
|
isInactive?: boolean;
|
||||||
animate?: boolean;
|
animate?: boolean;
|
||||||
delay?: number;
|
delay?: number;
|
||||||
|
isInitialGreeting?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AssistantMessage({
|
export function AssistantMessage({
|
||||||
|
|
@ -35,6 +36,7 @@ export function AssistantMessage({
|
||||||
isInactive = false,
|
isInactive = false,
|
||||||
animate = true,
|
animate = true,
|
||||||
delay = 0.2,
|
delay = 0.2,
|
||||||
|
isInitialGreeting = false,
|
||||||
}: AssistantMessageProps) {
|
}: AssistantMessageProps) {
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
@ -50,10 +52,31 @@ export function AssistantMessage({
|
||||||
<Message
|
<Message
|
||||||
icon={
|
icon={
|
||||||
<div className="w-8 h-8 flex items-center justify-center flex-shrink-0 select-none">
|
<div className="w-8 h-8 flex items-center justify-center flex-shrink-0 select-none">
|
||||||
<DogIcon
|
{/* Dog icon with bark animation when greeting */}
|
||||||
className="h-6 w-6 transition-colors duration-300"
|
<motion.div
|
||||||
disabled={isCompleted || isInactive}
|
animate={
|
||||||
/>
|
isInitialGreeting
|
||||||
|
? {
|
||||||
|
rotate: [-5, -8, -5, 0],
|
||||||
|
y: [-1, -2, -1, 0],
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
}
|
||||||
|
transition={
|
||||||
|
isInitialGreeting
|
||||||
|
? {
|
||||||
|
duration: 0.8,
|
||||||
|
times: [0, 0.4, 0.7, 1],
|
||||||
|
ease: "easeInOut",
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<DogIcon
|
||||||
|
className="h-6 w-6 transition-colors duration-300"
|
||||||
|
disabled={isCompleted || isInactive}
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
actions={
|
actions={
|
||||||
|
|
@ -76,20 +99,41 @@ export function AssistantMessage({
|
||||||
onToggle={onToggle}
|
onToggle={onToggle}
|
||||||
/>
|
/>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<MarkdownRenderer
|
{/* Slide animation for initial greeting */}
|
||||||
className={cn(
|
{isInitialGreeting ? (
|
||||||
"text-sm py-1.5 transition-colors duration-300",
|
<motion.div
|
||||||
isCompleted ? "text-placeholder-foreground" : "text-foreground",
|
initial={{ opacity: 0, x: -16 }}
|
||||||
)}
|
animate={{
|
||||||
chatMessage={
|
opacity: [0, 0, 1, 1],
|
||||||
isStreaming
|
x: [-16, -8, 0, 0],
|
||||||
? content.trim()
|
}}
|
||||||
? content +
|
transition={{
|
||||||
' <span class="inline-block w-1 h-4 bg-primary ml-1 animate-pulse"></span>'
|
duration: 0.8,
|
||||||
: '<span class="text-muted-foreground italic">Thinking<span class="thinking-dots"></span></span>'
|
times: [0, 0.3, 0.6, 1],
|
||||||
: content
|
ease: "easeOut",
|
||||||
}
|
}}
|
||||||
/>
|
>
|
||||||
|
<MarkdownRenderer
|
||||||
|
className="text-sm py-1.5 text-foreground"
|
||||||
|
chatMessage={content}
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
) : (
|
||||||
|
<MarkdownRenderer
|
||||||
|
className={cn(
|
||||||
|
"text-sm py-1.5 transition-colors duration-300",
|
||||||
|
isCompleted ? "text-placeholder-foreground" : "text-foreground",
|
||||||
|
)}
|
||||||
|
chatMessage={
|
||||||
|
isStreaming
|
||||||
|
? content.trim()
|
||||||
|
? content +
|
||||||
|
' <span class="inline-block w-1 h-4 bg-primary ml-1 animate-pulse"></span>'
|
||||||
|
: '<span class="text-muted-foreground italic">Thinking<span class="thinking-dots"></span></span>'
|
||||||
|
: content
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Message>
|
</Message>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
|
||||||
|
|
@ -293,6 +293,11 @@ function ChatPage() {
|
||||||
setIsFilterHighlighted(false);
|
setIsFilterHighlighted(false);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
lastLoadedConversationRef.current = null;
|
lastLoadedConversationRef.current = null;
|
||||||
|
|
||||||
|
// Focus input after a short delay to ensure rendering is complete
|
||||||
|
setTimeout(() => {
|
||||||
|
chatInputRef.current?.focusInput();
|
||||||
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFocusInput = () => {
|
const handleFocusInput = () => {
|
||||||
|
|
@ -480,6 +485,11 @@ function ChatPage() {
|
||||||
...prev,
|
...prev,
|
||||||
[conversationData.endpoint]: conversationData.response_id,
|
[conversationData.endpoint]: conversationData.response_id,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Focus input when loading a conversation
|
||||||
|
setTimeout(() => {
|
||||||
|
chatInputRef.current?.focusInput();
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
conversationData,
|
conversationData,
|
||||||
|
|
@ -500,6 +510,11 @@ function ChatPage() {
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
lastLoadedConversationRef.current = null;
|
lastLoadedConversationRef.current = null;
|
||||||
|
|
||||||
|
// Focus input when starting a new conversation
|
||||||
|
setTimeout(() => {
|
||||||
|
chatInputRef.current?.focusInput();
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
}, [placeholderConversation, currentConversationId]);
|
}, [placeholderConversation, currentConversationId]);
|
||||||
|
|
||||||
|
|
@ -1061,6 +1076,11 @@ function ChatPage() {
|
||||||
onFork={(e) => handleForkConversation(index, e)}
|
onFork={(e) => handleForkConversation(index, e)}
|
||||||
animate={false}
|
animate={false}
|
||||||
isInactive={index < messages.length - 1}
|
isInactive={index < messages.length - 1}
|
||||||
|
isInitialGreeting={
|
||||||
|
index === 0 &&
|
||||||
|
messages.length === 1 &&
|
||||||
|
message.content === "How can I assist?"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ const DogIcon = ({ disabled = false, stroke, ...props }: DogIconProps) => {
|
||||||
fill={fillColor}
|
fill={fillColor}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
|
<title>Dog Icon</title>
|
||||||
<path d="M8 18H2V16H8V18Z" />
|
<path d="M8 18H2V16H8V18Z" />
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
|
|
@ -58,7 +59,9 @@ const DogIcon = ({ disabled = false, stroke, ...props }: DogIconProps) => {
|
||||||
viewBox="0 0 105 77"
|
viewBox="0 0 105 77"
|
||||||
fill="none"
|
fill="none"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...props}
|
||||||
>
|
>
|
||||||
|
<title>Dog Icon</title>
|
||||||
<defs>
|
<defs>
|
||||||
<style dangerouslySetInnerHTML={{ __html: animationCSS }} />
|
<style dangerouslySetInnerHTML={{ __html: animationCSS }} />
|
||||||
</defs>
|
</defs>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue