From 639de8114f9eb80f94b0b4420e3e9f22ebdd564d Mon Sep 17 00:00:00 2001 From: Hiep Le <69354317+hieptl@users.noreply.github.com> Date: Mon, 24 Nov 2025 21:36:30 +0700 Subject: [PATCH] feat(frontend): add blue border to Planning Agent events (#11788) --- frontend/src/components/features/chat/chat-message.tsx | 3 +++ .../user-assistant-event-message.tsx | 9 ++++++++- frontend/src/components/v1/chat/event-message.tsx | 6 +++++- frontend/src/contexts/conversation-websocket-context.tsx | 7 ++++++- frontend/src/stores/use-event-store.ts | 4 +++- 5 files changed, 25 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/features/chat/chat-message.tsx b/frontend/src/components/features/chat/chat-message.tsx index b822220c4d..a3dc934475 100644 --- a/frontend/src/components/features/chat/chat-message.tsx +++ b/frontend/src/components/features/chat/chat-message.tsx @@ -19,6 +19,7 @@ interface ChatMessageProps { onClick: () => void; tooltip?: string; }>; + isFromPlanningAgent?: boolean; } export function ChatMessage({ @@ -26,6 +27,7 @@ export function ChatMessage({ message, children, actions, + isFromPlanningAgent = false, }: React.PropsWithChildren) { const [isHovering, setIsHovering] = React.useState(false); const [isCopy, setIsCopy] = React.useState(false); @@ -59,6 +61,7 @@ export function ChatMessage({ "flex flex-col gap-2", type === "user" && " p-4 bg-tertiary self-end", type === "agent" && "mt-6 w-full max-w-full bg-transparent", + isFromPlanningAgent && "border border-[#597ff4] bg-tertiary p-4", )} >
; isLastMessage: boolean; + isFromPlanningAgent: boolean; } export function UserAssistantEventMessage({ @@ -31,6 +32,7 @@ export function UserAssistantEventMessage({ microagentPRUrl, actions, isLastMessage, + isFromPlanningAgent, }: UserAssistantEventMessageProps) { const message = parseMessageFromEvent(event); @@ -46,7 +48,12 @@ export function UserAssistantEventMessage({ return ( <> - + {imageUrls.length > 0 && ( )} diff --git a/frontend/src/components/v1/chat/event-message.tsx b/frontend/src/components/v1/chat/event-message.tsx index dbe327b31b..637a0063c5 100644 --- a/frontend/src/components/v1/chat/event-message.tsx +++ b/frontend/src/components/v1/chat/event-message.tsx @@ -19,7 +19,7 @@ import { } from "./event-message-components"; interface EventMessageProps { - event: OpenHandsEvent; + event: OpenHandsEvent & { isFromPlanningAgent?: boolean }; messages: OpenHandsEvent[]; isLastMessage: boolean; microagentStatus?: MicroagentStatus | null; @@ -51,6 +51,9 @@ export function EventMessage({ const feedbackData = { exists: false }; const isCheckingFeedback = false; + // Read isFromPlanningAgent directly from the event object + const isFromPlanningAgent = event.isFromPlanningAgent || false; + // Common props for components that need them const commonProps = { microagentStatus, @@ -62,6 +65,7 @@ export function EventMessage({ config, isCheckingFeedback, feedbackData, + isFromPlanningAgent, }; // Agent error events diff --git a/frontend/src/contexts/conversation-websocket-context.tsx b/frontend/src/contexts/conversation-websocket-context.tsx index 685f6c93ab..79f0cbf8df 100644 --- a/frontend/src/contexts/conversation-websocket-context.tsx +++ b/frontend/src/contexts/conversation-websocket-context.tsx @@ -343,7 +343,12 @@ export function ConversationWebSocketProvider({ // Use type guard to validate v1 event structure if (isV1Event(event)) { - addEvent(event); + // Mark this event as coming from the planning agent + const eventWithPlanningFlag = { + ...event, + isFromPlanningAgent: true, + }; + addEvent(eventWithPlanningFlag); // Handle AgentErrorEvent specifically if (isAgentErrorEvent(event)) { diff --git a/frontend/src/stores/use-event-store.ts b/frontend/src/stores/use-event-store.ts index 307f4ced0d..2d8ecf0a3b 100644 --- a/frontend/src/stores/use-event-store.ts +++ b/frontend/src/stores/use-event-store.ts @@ -5,7 +5,9 @@ import { OpenHandsParsedEvent } from "#/types/core"; import { isV1Event } from "#/types/v1/type-guards"; // While we transition to v1 events, our store can handle both v0 and v1 events -type OHEvent = OpenHandsEvent | OpenHandsParsedEvent; +type OHEvent = (OpenHandsEvent | OpenHandsParsedEvent) & { + isFromPlanningAgent?: boolean; +}; interface EventState { events: OHEvent[];