+
+
+ {}}
+ onStopServer={
+ conversation?.status === "RUNNING" ? handleStopServer : undefined
+ }
+ onStartServer={
+ conversation?.status === "STOPPED" ? handleStartServer : undefined
+ }
+ conversationStatus={conversation?.status ?? null}
+ position="bottom"
+ className="opacity-0 invisible pointer-events-none group-hover:opacity-100 group-hover:visible group-hover:pointer-events-auto bottom-full left-0 mt-0 min-h-fit"
+ isPausing={false}
+ />
+
+
+
+ );
+}
diff --git a/frontend/src/components/features/conversation/conversation-name.tsx b/frontend/src/components/features/conversation/conversation-name.tsx
index 127a63582d..2b5c06398c 100644
--- a/frontend/src/components/features/conversation/conversation-name.tsx
+++ b/frontend/src/components/features/conversation/conversation-name.tsx
@@ -124,7 +124,7 @@ export function ConversationName() {
return (
<>
{titleMode === "edit" ? (
diff --git a/frontend/src/routes/conversation.tsx b/frontend/src/routes/conversation.tsx
index 16edacde66..ec19051530 100644
--- a/frontend/src/routes/conversation.tsx
+++ b/frontend/src/routes/conversation.tsx
@@ -22,7 +22,7 @@ import { ConversationSubscriptionsProvider } from "#/context/conversation-subscr
import { useUserProviders } from "#/hooks/use-user-providers";
import { ConversationMain } from "#/components/features/conversation/conversation-main/conversation-main";
-import { ConversationName } from "#/components/features/conversation/conversation-name";
+import { ConversationNameWithStatus } from "#/components/features/conversation/conversation-name-with-status";
import { ConversationTabs } from "#/components/features/conversation/conversation-tabs/conversation-tabs";
import { WebSocketProviderWrapper } from "#/contexts/websocket-provider-wrapper";
@@ -160,7 +160,7 @@ function AppContent() {
className="p-3 md:p-0 flex flex-col h-full gap-3"
>
-
+
diff --git a/frontend/src/utils/utils.ts b/frontend/src/utils/utils.ts
index 8603804ecc..a7fe39e463 100644
--- a/frontend/src/utils/utils.ts
+++ b/frontend/src/utils/utils.ts
@@ -6,6 +6,7 @@ import { ConversationStatus } from "#/types/conversation-status";
import { GitRepository } from "#/types/git";
import { sanitizeQuery } from "#/utils/sanitize-query";
import { PRODUCT_URL } from "#/utils/constants";
+import { AgentState } from "#/types/agent-state";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
@@ -609,3 +610,66 @@ export const buildSessionHeaders = (
}
return headers;
};
+
+/**
+ * Get the appropriate color based on agent status
+ * @param options Configuration object for status color calculation
+ * @param options.isPausing Whether the agent is currently pausing
+ * @param options.isTask Whether we're polling a task
+ * @param options.taskStatus The task status string (e.g., "ERROR", "READY")
+ * @param options.isStartingStatus Whether the agent is in a starting state (LOADING or INIT)
+ * @param options.isStopStatus Whether the conversation status is STOPPED
+ * @param options.curAgentState The current agent state
+ * @returns The hex color code for the status
+ *
+ * @example
+ * getStatusColor({
+ * isPausing: false,
+ * isTask: false,
+ * taskStatus: undefined,
+ * isStartingStatus: false,
+ * isStopStatus: false,
+ * curAgentState: AgentState.RUNNING
+ * }) // Returns "#BCFF8C"
+ */
+export const getStatusColor = (options: {
+ isPausing: boolean;
+ isTask: boolean;
+ taskStatus?: string | null;
+ isStartingStatus: boolean;
+ isStopStatus: boolean;
+ curAgentState: AgentState;
+}): string => {
+ const {
+ isPausing,
+ isTask,
+ taskStatus,
+ isStartingStatus,
+ isStopStatus,
+ curAgentState,
+ } = options;
+
+ // Show pausing status
+ if (isPausing) {
+ return "#FFD600";
+ }
+
+ // Show task status if we're polling a task
+ if (isTask && taskStatus) {
+ if (taskStatus === "ERROR") {
+ return "#FF684E";
+ }
+ return "#FFD600";
+ }
+
+ if (isStartingStatus) {
+ return "#FFD600";
+ }
+ if (isStopStatus) {
+ return "#ffffff";
+ }
+ if (curAgentState === AgentState.ERROR) {
+ return "#FF684E";
+ }
+ return "#BCFF8C";
+};