diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/StatusResponse/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/StatusResponse/index.jsx index 2ceb74406f39b0823953f2f80d306413683c3fde..66482ef619a53c9e4c2ceca71f2a3de9e7f0858c 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/StatusResponse/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/StatusResponse/index.jsx @@ -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> ); diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/ThoughtContainer/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/ThoughtContainer/index.jsx index 75f1d835ce4a3fefd23f398f16837f01284bdf6c..957531171b6661c650ae5313009c682491275c5d 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/ThoughtContainer/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/ThoughtContainer/index.jsx @@ -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"; diff --git a/frontend/src/media/animations/agent-animation.webm b/frontend/src/media/animations/agent-animation.webm new file mode 100644 index 0000000000000000000000000000000000000000..18d17d08c7c562e688fec60a4b8f8ee5a1a3c03e Binary files /dev/null and b/frontend/src/media/animations/agent-animation.webm differ diff --git a/frontend/src/media/animations/agent-static.png b/frontend/src/media/animations/agent-static.png new file mode 100644 index 0000000000000000000000000000000000000000..db06bf745dd281f081b4473b2fe6f8a3bad5025d Binary files /dev/null and b/frontend/src/media/animations/agent-static.png differ diff --git a/frontend/src/media/animations/thinking-animation.webm b/frontend/src/media/animations/thinking-animation.webm new file mode 100644 index 0000000000000000000000000000000000000000..be059402473b66da1365f34ed13ea7917998d787 Binary files /dev/null and b/frontend/src/media/animations/thinking-animation.webm differ diff --git a/frontend/src/media/animations/thinking-static.png b/frontend/src/media/animations/thinking-static.png new file mode 100644 index 0000000000000000000000000000000000000000..220236c03f623be04f7c33dadc615727dc716893 Binary files /dev/null and b/frontend/src/media/animations/thinking-static.png differ