New thinking/agent animation + UI (#3302)
* implement new thinking animation ui * implement agent thinking animation
This commit is contained in:
parent
23d5f368d9
commit
6f1938c598
@ -1,10 +1,8 @@
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
CaretDown,
|
||||
CircleNotch,
|
||||
Check,
|
||||
CheckCircle,
|
||||
} from "@phosphor-icons/react";
|
||||
import { CaretDown } from "@phosphor-icons/react";
|
||||
|
||||
import AgentAnimation from "@/media/animations/agent-animation.webm";
|
||||
import AgentStatic from "@/media/animations/agent-static.png";
|
||||
|
||||
export default function StatusResponse({
|
||||
messages = [],
|
||||
@ -21,94 +19,85 @@ export default function StatusResponse({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex justify-center items-end w-full">
|
||||
<div className="py-2 px-4 w-full flex gap-x-5 md:max-w-[80%] flex-col relative">
|
||||
<div
|
||||
onClick={handleExpandClick}
|
||||
className={`${!previousThoughts?.length ? "cursor-text" : "cursor-pointer hover:bg-theme-sidebar-item-hover transition-all duration-200"} bg-theme-bg-chat-input rounded-full py-2 px-4 flex items-center gap-x-2 border border-theme-sidebar-border`}
|
||||
>
|
||||
{isThinking ? (
|
||||
<CircleNotch
|
||||
className="w-4 h-4 text-theme-text-secondary animate-spin"
|
||||
aria-label="Agent is thinking..."
|
||||
/>
|
||||
) : showCheckmark ? (
|
||||
<CheckCircle
|
||||
className="w-4 h-4 text-green-400 transition-all duration-300"
|
||||
aria-label="Thought complete"
|
||||
/>
|
||||
) : null}
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<span
|
||||
key={currentThought.content}
|
||||
className="text-xs text-theme-text-secondary font-mono inline-block w-full animate-thoughtTransition"
|
||||
>
|
||||
{currentThought.content}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2">
|
||||
{previousThoughts?.length > 0 && (
|
||||
<div
|
||||
data-tooltip-id="expand-cot"
|
||||
data-tooltip-content={
|
||||
isExpanded ? "Hide thought chain" : "Show thought chain"
|
||||
}
|
||||
className="border-none text-theme-text-secondary hover:text-theme-text-primary transition-colors p-1 rounded-full hover:bg-theme-sidebar-item-hover"
|
||||
aria-label={
|
||||
isExpanded ? "Hide thought chain" : "Show thought chain"
|
||||
}
|
||||
>
|
||||
<CaretDown
|
||||
className={`w-4 h-4 transform transition-transform duration-200 ${isExpanded ? "rotate-180" : ""}`}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Previous thoughts dropdown */}
|
||||
{previousThoughts?.length > 0 && (
|
||||
<div className="flex justify-center w-full">
|
||||
<div className="w-full max-w-[80%] flex flex-col">
|
||||
<div className=" w-full max-w-[800px]">
|
||||
<div
|
||||
key={`cot-list-${currentThought.uuid}`}
|
||||
className={`mt-2 bg-theme-bg-chat-input backdrop-blur-sm rounded-lg overflow-hidden transition-all duration-300 border border-theme-sidebar-border ${
|
||||
isExpanded
|
||||
? "max-h-[300px] overflow-y-auto opacity-100"
|
||||
: "max-h-0 opacity-0"
|
||||
}`}
|
||||
onClick={handleExpandClick}
|
||||
style={{ borderRadius: "6px" }}
|
||||
className={`${!previousThoughts?.length ? "" : `${previousThoughts?.length ? "hover:bg-theme-sidebar-item-hover" : ""}`} items-start bg-theme-bg-chat-input py-2 px-4 flex gap-x-2`}
|
||||
>
|
||||
<div className="p-2">
|
||||
{previousThoughts.map((thought, index) => (
|
||||
<div
|
||||
key={`cot-${thought.uuid || index}`}
|
||||
className="flex gap-x-2"
|
||||
<div className="w-7 h-7 flex justify-center flex-shrink-0 items-center">
|
||||
{isThinking ? (
|
||||
<video
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
className="w-8 h-8 scale-150 transition-opacity duration-200 light:invert light:opacity-50"
|
||||
data-tooltip-id="agent-thinking"
|
||||
data-tooltip-content="Agent is thinking..."
|
||||
aria-label="Agent is thinking..."
|
||||
>
|
||||
<p className="text-xs text-theme-text-secondary font-mono">
|
||||
{index + 1}/{previousThoughts.length}
|
||||
</p>
|
||||
<div
|
||||
className="flex items-center gap-x-3 p-2 animate-fadeUpIn"
|
||||
style={{ animationDelay: `${index * 50}ms` }}
|
||||
>
|
||||
<span className="text-xs text-theme-text-secondary font-mono">
|
||||
{thought.content}
|
||||
<source src={AgentAnimation} type="video/webm" />
|
||||
</video>
|
||||
) : (
|
||||
<img
|
||||
src={AgentStatic}
|
||||
alt="Agent complete"
|
||||
className="w-6 h-6 transition-opacity duration-200 light:invert light:opacity-50"
|
||||
data-tooltip-id="agent-thinking"
|
||||
data-tooltip-content="Agent has finished thinking"
|
||||
aria-label="Agent has finished thinking"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div
|
||||
className={`overflow-hidden transition-all duration-300 ease-in-out ${isExpanded ? "max-h-[500px]" : "max-h-6"}`}
|
||||
>
|
||||
<div className="text-theme-text-secondary font-mono leading-6">
|
||||
{!isExpanded ? (
|
||||
<span className="block w-full truncate mt-[2px]">
|
||||
{currentThought.content}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{/* Append current thought to the end */}
|
||||
<div key={`cot-${currentThought.uuid}`} className="flex gap-x-2">
|
||||
<p className="text-xs text-theme-text-secondary font-mono">
|
||||
{previousThoughts.length + 1}/{previousThoughts.length + 1}
|
||||
</p>
|
||||
<div className="flex items-center gap-x-3 p-2 animate-fadeUpIn">
|
||||
<span className="text-xs text-theme-text-secondary font-mono">
|
||||
{currentThought.content}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
{previousThoughts.map((thought, index) => (
|
||||
<div
|
||||
key={`cot-${thought.uuid || index}`}
|
||||
className="mb-2"
|
||||
>
|
||||
{thought.content}
|
||||
</div>
|
||||
))}
|
||||
<div>{currentThought.content}</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2">
|
||||
{previousThoughts?.length > 0 && (
|
||||
<button
|
||||
onClick={handleExpandClick}
|
||||
data-tooltip-id="expand-cot"
|
||||
data-tooltip-content={
|
||||
isExpanded ? "Hide thought chain" : "Show thought chain"
|
||||
}
|
||||
className="border-none text-theme-text-secondary hover:text-theme-text-primary transition-colors p-1 rounded-full hover:bg-theme-sidebar-item-hover"
|
||||
aria-label={
|
||||
isExpanded ? "Hide thought chain" : "Show thought chain"
|
||||
}
|
||||
>
|
||||
<CaretDown
|
||||
className={`w-4 h-4 transform transition-transform duration-200 ${isExpanded ? "rotate-180" : ""}`}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { useState, forwardRef, useImperativeHandle } from "react";
|
||||
import renderMarkdown from "@/utils/chat/markdown";
|
||||
import { Brain, CaretDown } from "@phosphor-icons/react";
|
||||
import { CaretDown } from "@phosphor-icons/react";
|
||||
import DOMPurify from "dompurify";
|
||||
import truncate from "truncate";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import ThinkingAnimation from "@/media/animations/thinking-animation.webm";
|
||||
import ThinkingStatic from "@/media/animations/thinking-static.png";
|
||||
|
||||
const THOUGHT_KEYWORDS = ["thought", "thinking", "think", "thought_chain"];
|
||||
const CLOSING_TAGS = [...THOUGHT_KEYWORDS, "response", "answer"];
|
||||
@ -61,46 +62,57 @@ export const ThoughtChainComponent = forwardRef(
|
||||
<div
|
||||
style={{
|
||||
transition: "all 0.1s ease-in-out",
|
||||
borderRadius: isExpanded || autoExpand ? "6px" : "24px",
|
||||
borderRadius: "6px",
|
||||
}}
|
||||
className={`${isExpanded || autoExpand ? "" : `${canExpand ? "hover:bg-theme-sidebar-item-hover" : ""}`} items-start bg-theme-bg-chat-input py-2 px-4 flex gap-x-2 border border-theme-sidebar-border`}
|
||||
className={`${isExpanded || autoExpand ? "" : `${canExpand ? "hover:bg-theme-sidebar-item-hover" : ""}`} items-start bg-theme-bg-chat-input py-2 px-4 flex gap-x-2`}
|
||||
>
|
||||
{isThinking || isComplete ? (
|
||||
<Brain
|
||||
data-tooltip-id="cot-thinking"
|
||||
data-tooltip-content={
|
||||
isThinking
|
||||
? "Model is thinking..."
|
||||
: "Model has finished thinking"
|
||||
}
|
||||
className={`w-4 h-4 mt-1 ${isThinking ? "text-blue-500 animate-pulse" : "text-green-400"}`}
|
||||
aria-label={
|
||||
isThinking
|
||||
? "Model is thinking..."
|
||||
: "Model has finished thinking"
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
<div className="flex-1 overflow-hidden">
|
||||
{!isExpanded && !autoExpand ? (
|
||||
<span
|
||||
className="text-theme-text-secondary font-mono inline-block w-full"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: DOMPurify.sanitize(
|
||||
truncate(tagStrippedContent, THOUGHT_PREVIEW_LENGTH)
|
||||
),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<span
|
||||
className="text-theme-text-secondary font-mono inline-block w-full"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: DOMPurify.sanitize(
|
||||
renderMarkdown(tagStrippedContent)
|
||||
),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={`w-7 h-7 flex justify-center flex-shrink-0 ${!isExpanded && !autoExpand ? "items-center" : "items-start pt-[2px]"}`}
|
||||
>
|
||||
{isThinking || isComplete ? (
|
||||
<>
|
||||
<video
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
className={`w-7 h-7 transition-opacity duration-200 light:invert light:opacity-50 ${isThinking ? "opacity-100" : "opacity-0 hidden"}`}
|
||||
data-tooltip-id="cot-thinking"
|
||||
data-tooltip-content="Model is thinking..."
|
||||
aria-label="Model is thinking..."
|
||||
>
|
||||
<source src={ThinkingAnimation} type="video/webm" />
|
||||
</video>
|
||||
<img
|
||||
src={ThinkingStatic}
|
||||
alt="Thinking complete"
|
||||
className={`w-6 h-6 transition-opacity duration-200 light:invert light:opacity-50 ${!isThinking && isComplete ? "opacity-100" : "opacity-0 hidden"}`}
|
||||
data-tooltip-id="cot-thinking"
|
||||
data-tooltip-content="Model has finished thinking"
|
||||
aria-label="Model has finished thinking"
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div
|
||||
className={`overflow-hidden transition-all transform duration-300 ease-in-out origin-top ${isExpanded || autoExpand ? "max-h-[500px]" : "max-h-6"}`}
|
||||
>
|
||||
<div
|
||||
className={`text-theme-text-secondary font-mono leading-6 ${isExpanded || autoExpand ? "-ml-[5.5px] -mt-[4px]" : "mt-[2px]"}`}
|
||||
>
|
||||
<span
|
||||
className={`block w-full ${!isExpanded && !autoExpand ? "truncate" : ""}`}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: DOMPurify.sanitize(
|
||||
isExpanded || autoExpand
|
||||
? renderMarkdown(tagStrippedContent)
|
||||
: tagStrippedContent
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2">
|
||||
{!autoExpand && canExpand ? (
|
||||
@ -127,3 +139,4 @@ export const ThoughtChainComponent = forwardRef(
|
||||
);
|
||||
}
|
||||
);
|
||||
ThoughtChainComponent.displayName = "ThoughtChainComponent";
|
||||
|
||||
BIN
frontend/src/media/animations/agent-animation.webm
Normal file
BIN
frontend/src/media/animations/agent-animation.webm
Normal file
Binary file not shown.
BIN
frontend/src/media/animations/agent-static.png
Normal file
BIN
frontend/src/media/animations/agent-static.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
BIN
frontend/src/media/animations/thinking-animation.webm
Normal file
BIN
frontend/src/media/animations/thinking-animation.webm
Normal file
Binary file not shown.
BIN
frontend/src/media/animations/thinking-static.png
Normal file
BIN
frontend/src/media/animations/thinking-static.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
Loading…
Reference in New Issue
Block a user