diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/StatusResponse/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/StatusResponse/index.jsx deleted file mode 100644 index c06fb35ad1ea0d493668aceef223d3e59e54854d..0000000000000000000000000000000000000000 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/StatusResponse/index.jsx +++ /dev/null @@ -1,113 +0,0 @@ -import React, { useState } from "react"; -import { - CaretDown, - CircleNotch, - Check, - CheckCircle, -} from "@phosphor-icons/react"; - -export default function StatusResponse({ - messages = [], - isThinking = false, - showCheckmark = false, -}) { - const [isExpanded, setIsExpanded] = useState(false); - const currentThought = messages[messages.length - 1]; - const previousThoughts = messages.slice(0, -1); - - function handleExpandClick() { - if (!previousThoughts.length > 0) return; - setIsExpanded(!isExpanded); - } - - 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 - 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] opacity-100" : "max-h-0 opacity-0" - }`} - > - <div className="p-2"> - {previousThoughts.map((thought, index) => ( - <div - key={`cot-${thought.uuid || index}`} - className="flex gap-x-2" - > - <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} - </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> - </div> - </div> - </div> - </div> - )} - </div> - </div> - ); -} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx index 819a6f1c261481a268005c16616d33777260c798..9bf8eeebbef4a007740645bf0dfd74cd1ca11347 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx @@ -1,7 +1,6 @@ -import { useEffect, useRef, useState, useMemo, useCallback } from "react"; +import React, { useEffect, useRef, useState } from "react"; import HistoricalMessage from "./HistoricalMessage"; import PromptReply from "./PromptReply"; -import StatusResponse from "./StatusResponse"; import { useManageWorkspaceModal } from "../../../Modals/ManageWorkspace"; import ManageWorkspace from "../../../Modals/ManageWorkspace"; import { ArrowDown } from "@phosphor-icons/react"; @@ -13,7 +12,6 @@ import { useParams } from "react-router-dom"; import paths from "@/utils/paths"; import Appearance from "@/models/appearance"; import useTextSize from "@/hooks/useTextSize"; -import { v4 } from "uuid"; export default function ChatHistory({ history = [], @@ -176,52 +174,63 @@ export default function ChatHistory({ ); } - const compiledHistory = useMemo( - () => - buildMessages({ - workspace, - history, - regenerateAssistantMessage, - saveEditedMessage, - forkThread, - }), - [ - workspace, - history, - regenerateAssistantMessage, - saveEditedMessage, - forkThread, - ] - ); - const lastMessageInfo = useMemo(() => getLastMessageInfo(history), [history]); - const renderStatusResponse = useCallback( - (item, index) => { - const hasSubsequentMessages = index < compiledHistory.length - 1; - return ( - <StatusResponse - key={`status-group-${index}`} - messages={item} - isThinking={!hasSubsequentMessages && lastMessageInfo.isAnimating} - showCheckmark={ - hasSubsequentMessages || - (!lastMessageInfo.isAnimating && !lastMessageInfo.isStatusResponse) - } - /> - ); - }, - [compiledHistory.length, lastMessageInfo] - ); - return ( <div - className={`markdown text-white/80 light:text-theme-text-primary font-light ${textSizeClass} h-full md:h-[83%] pb-[100px] pt-6 md:pt-0 md:pb-20 md:mx-0 overflow-y-scroll flex flex-col justify-start ${showScrollbar ? "show-scrollbar" : "no-scroll"}`} + className={`markdown text-white/80 light:text-theme-text-primary font-light ${textSizeClass} h-full md:h-[83%] pb-[100px] pt-6 md:pt-0 md:pb-20 md:mx-0 overflow-y-scroll flex flex-col justify-start ${ + showScrollbar ? "show-scrollbar" : "no-scroll" + }`} id="chat-history" ref={chatHistoryRef} onScroll={handleScroll} > - {compiledHistory.map((item, index) => - Array.isArray(item) ? renderStatusResponse(item, index) : item - )} + {history.map((props, index) => { + const isLastBotReply = + index === history.length - 1 && props.role === "assistant"; + + if (props?.type === "statusResponse" && !!props.content) { + return <StatusResponse key={props.uuid} props={props} />; + } + + if (props.type === "rechartVisualize" && !!props.content) { + return ( + <Chartable key={props.uuid} workspace={workspace} props={props} /> + ); + } + + if (isLastBotReply && props.animate) { + return ( + <PromptReply + key={props.uuid} + uuid={props.uuid} + reply={props.content} + pending={props.pending} + sources={props.sources} + error={props.error} + workspace={workspace} + closed={props.closed} + /> + ); + } + + return ( + <HistoricalMessage + key={index} + message={props.content} + role={props.role} + workspace={workspace} + sources={props.sources} + feedbackScore={props.feedbackScore} + chatId={props.chatId} + error={props.error} + attachments={props.attachments} + regenerateMessage={regenerateAssistantMessage} + isLastMessage={isLastBotReply} + saveEditedMessage={saveEditedMessage} + forkThread={forkThread} + metrics={props.metrics} + /> + ); + })} {showing && ( <ManageWorkspace hideModal={hideModal} providedSlug={workspace.slug} /> )} @@ -244,13 +253,21 @@ export default function ChatHistory({ ); } -const getLastMessageInfo = (history) => { - const lastMessage = history?.[history.length - 1] || {}; - return { - isAnimating: lastMessage?.animate, - isStatusResponse: lastMessage?.type === "statusResponse", - }; -}; +function StatusResponse({ props }) { + 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"> + <div className="flex gap-x-5"> + <span + className={`text-xs inline-block p-2 rounded-lg text-white/60 font-mono whitespace-pre-line`} + > + {props.content} + </span> + </div> + </div> + </div> + ); +} function WorkspaceChatSuggestions({ suggestions = [], sendSuggestion }) { if (suggestions.length === 0) return null; @@ -269,78 +286,3 @@ function WorkspaceChatSuggestions({ suggestions = [], sendSuggestion }) { </div> ); } - -/** - * Builds the history of messages for the chat. - * This is mostly useful for rendering the history in a way that is easy to understand. - * as well as compensating for agent thinking and other messages that are not part of the history, but - * are still part of the chat. - * - * @param {Object} param0 - The parameters for building the messages. - * @param {Array} param0.history - The history of messages. - * @param {Object} param0.workspace - The workspace object. - * @param {Function} param0.regenerateAssistantMessage - The function to regenerate the assistant message. - * @param {Function} param0.saveEditedMessage - The function to save the edited message. - * @param {Function} param0.forkThread - The function to fork the thread. - * @returns {Array} The compiled history of messages. - */ -function buildMessages({ - history, - workspace, - regenerateAssistantMessage, - saveEditedMessage, - forkThread, -}) { - return history.reduce((acc, props, index) => { - const isLastBotReply = - index === history.length - 1 && props.role === "assistant"; - - if (props?.type === "statusResponse" && !!props.content) { - if (acc.length > 0 && Array.isArray(acc[acc.length - 1])) { - acc[acc.length - 1].push(props); - } else { - acc.push([props]); - } - return acc; - } - - if (props.type === "rechartVisualize" && !!props.content) { - acc.push( - <Chartable key={props.uuid} workspace={workspace} props={props} /> - ); - } else if (isLastBotReply && props.animate) { - acc.push( - <PromptReply - key={props.uuid || v4()} - uuid={props.uuid} - reply={props.content} - pending={props.pending} - sources={props.sources} - error={props.error} - workspace={workspace} - closed={props.closed} - /> - ); - } else { - acc.push( - <HistoricalMessage - key={index} - message={props.content} - role={props.role} - workspace={workspace} - sources={props.sources} - feedbackScore={props.feedbackScore} - chatId={props.chatId} - error={props.error} - attachments={props.attachments} - regenerateMessage={regenerateAssistantMessage} - isLastMessage={isLastBotReply} - saveEditedMessage={saveEditedMessage} - forkThread={forkThread} - metrics={props.metrics} - /> - ); - } - return acc; - }, []); -} diff --git a/frontend/src/index.css b/frontend/src/index.css index 539b5866582af97f734f612b6e8643eab014a68e..96d01d41cb1498f33e4b1013487ab387dbf72603 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -583,6 +583,22 @@ dialog::backdrop { animation: slideDown 0.3s ease-out forwards; } +@keyframes slideUp { + from { + max-height: 400px; + opacity: 1; + } + + to { + max-height: 0; + opacity: 0; + } +} + +.slide-up { + animation: slideUp 0.3s ease-out forwards; +} + .input-label { @apply text-[14px] font-bold text-white; } @@ -930,51 +946,3 @@ does not extend the close button beyond the viewport. */ .rti--container { @apply !bg-theme-settings-input-bg !text-white !placeholder-white !placeholder-opacity-60 !text-sm !rounded-lg !p-2.5; } - -@keyframes fadeUpIn { - 0% { - opacity: 0; - transform: translateY(5px); - } - - 100% { - opacity: 1; - transform: translateY(0); - } -} - -.animate-fadeUpIn { - animation: fadeUpIn 0.3s ease-out forwards; -} - -@keyframes bounce-subtle { - 0%, - 100% { - transform: translateY(0); - } - - 50% { - transform: translateY(-2px); - } -} - -@keyframes thoughtTransition { - 0% { - opacity: 0; - transform: translateY(10px); - } - - 30% { - opacity: 1; - transform: translateY(0); - } - - 100% { - opacity: 1; - transform: translateY(0); - } -} - -.animate-thoughtTransition { - animation: thoughtTransition 0.5s ease-out forwards; -} diff --git a/frontend/src/utils/chat/agent.js b/frontend/src/utils/chat/agent.js index ad1193d304c953fe0d9095d99791bbab5f5fc2b4..babe55605f48dff91d0eaaeffdc89286171b7751 100644 --- a/frontend/src/utils/chat/agent.js +++ b/frontend/src/utils/chat/agent.js @@ -99,7 +99,7 @@ export default function handleSocketResponse(event, setChatHistory) { sources: [], closed: true, error: null, - animate: data?.animate || false, + animate: false, pending: false, }, ]; diff --git a/frontend/src/utils/chat/index.js b/frontend/src/utils/chat/index.js index abde54fe9c2da03391c655cb3fcc8e6d62f3936e..86c1cd4208ca166f80f08ba70e6f669b1bdd5638 100644 --- a/frontend/src/utils/chat/index.js +++ b/frontend/src/utils/chat/index.js @@ -17,7 +17,6 @@ export default function handleChat( sources = [], error, close, - animate = false, chatId = null, action = null, metrics = {}, @@ -35,7 +34,7 @@ export default function handleChat( sources, closed: true, error, - animate, + animate: false, pending: false, metrics, }, @@ -48,7 +47,7 @@ export default function handleChat( sources, closed: true, error, - animate, + animate: false, pending: false, metrics, }); diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 517af2ea167f7feb5765f019aa74093eec5e2df1..8ecdbcdc0a4d72f71b7c4af04ab28744ccfbe644 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -141,10 +141,7 @@ export default { }, animation: { sweep: "sweep 0.5s ease-in-out", - "pulse-glow": "pulse-glow 1.5s infinite", - 'fade-in': 'fade-in 0.3s ease-out', - 'slide-up': 'slide-up 0.4s ease-out forwards', - 'bounce-subtle': 'bounce-subtle 2s ease-in-out infinite' + "pulse-glow": "pulse-glow 1.5s infinite" }, keyframes: { sweep: { @@ -178,18 +175,6 @@ export default { boxShadow: "0 0 0 rgba(255, 255, 255, 0.0)", backgroundColor: "rgba(255, 255, 255, 0.0)" } - }, - 'fade-in': { - '0%': { opacity: '0' }, - '100%': { opacity: '1' } - }, - 'slide-up': { - '0%': { transform: 'translateY(10px)', opacity: '0' }, - '100%': { transform: 'translateY(0)', opacity: '1' } - }, - 'bounce-subtle': { - '0%, 100%': { transform: 'translateY(0)' }, - '50%': { transform: 'translateY(-2px)' } } } }