From 1ab4216ccbdb525e8815b77bfa77b36bdf3c4c2e Mon Sep 17 00:00:00 2001 From: Rocky Kwok Date: Mon, 29 Jun 2026 16:42:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20iframe=E5=A2=9E=E5=8A=A0=E6=99=BA?= =?UTF-8?q?=E8=83=BD=E4=BD=93=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workspace/agents/agent-selector.tsx | 68 +++++++++-------- .../plugin-management/domain-agent.test.mjs | 22 ++++++ frontend-web/src/pages/IframeChatPage.tsx | 73 +++++++++++++------ 3 files changed, 107 insertions(+), 56 deletions(-) diff --git a/frontend-web/src/components/workspace/agents/agent-selector.tsx b/frontend-web/src/components/workspace/agents/agent-selector.tsx index a3539beb..3bb0a56b 100644 --- a/frontend-web/src/components/workspace/agents/agent-selector.tsx +++ b/frontend-web/src/components/workspace/agents/agent-selector.tsx @@ -20,6 +20,7 @@ import { } from "@/components/ui/dialog"; import { PromptInputButton } from "@/components/ai-elements/prompt-input"; import { AgentManageDialog } from "@/components/workspace/agents/agent-manage-dialog"; +import { Tooltip } from "@/components/workspace/tooltip"; import { useAgentSelector, useAgentsPaginated } from "@/core/agents"; import type { Agent } from "@/core/agents"; import { getStoredAuth } from "@/core/auth"; @@ -86,6 +87,7 @@ export function AgentSelector({ }; const triggerLabel = selectedAgent ? desensitize(selectedAgent.name) : "选择 Agent"; + const iconTooltipLabel = selectedAgent ? triggerLabel : "未选择"; const iconMode = triggerVariant === "icon"; return ( @@ -93,38 +95,40 @@ export function AgentSelector({ {iconMode ? ( - + + + ) : ( { + assert.deepEqual( + resolveIncomingIframeSelection({ + pageUrl: "https://another-unknown.example.com/article", + mappings: [ + { + id: "m1", + domain: "foo.example.com", + agentId: "agent-1", + enabled: true, + }, + ], + availableAgentIds: ["agent-1", "agent-2"], + manualSelectedAgentId: null, + }), + { + selectedAgentId: null, + forceDefaultAssistant: true, + }, + ); +}); diff --git a/frontend-web/src/pages/IframeChatPage.tsx b/frontend-web/src/pages/IframeChatPage.tsx index b847d3a0..a45384c8 100644 --- a/frontend-web/src/pages/IframeChatPage.tsx +++ b/frontend-web/src/pages/IframeChatPage.tsx @@ -83,6 +83,13 @@ type BrainyChatInterruptMessage = { }; requestId?: string; }; +type BrainyUrlContextMessage = { + source?: string; + type?: string; + payload?: { + url?: string; + }; +}; type PendingIframeSubmit = { message: PromptInputMessage; externalContext?: Record; @@ -429,25 +436,10 @@ export default function IframeChatPage() { [thread], ); - useEffect(() => { - const handleMessage = ( - event: MessageEvent, - ) => { - const data = event.data; - if (data?.source !== "brainy-extension") return; - if (data?.type === "brainy:chat-interrupt") { - void handleInterrupt(data as BrainyChatInterruptMessage); - return; - } - if (data?.type !== "brainy:chat-submit") return; - const submitMessage = data as BrainyChatSubmitMessage; - const { submitText, files, iframeContext, additionalKwargs } = - parseBrainyChatSubmitMessage(submitMessage); - - if (!submitText && files.length === 0) return; - + const applyIncomingUrlSelection = useCallback( + (pageUrl: string | null | undefined) => { const nextSelection = resolveIncomingIframeSelection({ - pageUrl: submitMessage.payload?.pageContext?.url, + pageUrl, mappings: pluginManagementSettings?.domainAgentMappings ?? [], availableAgentIds: selectorLoading ? undefined @@ -456,6 +448,40 @@ export default function IframeChatPage() { }); setSelectedAgentId(nextSelection.selectedAgentId); setForceDefaultAssistant(nextSelection.forceDefaultAssistant); + return nextSelection; + }, + [ + manualSelectedAgentId, + pluginManagementSettings?.domainAgentMappings, + selectorAgents, + selectorLoading, + ], + ); + + useEffect(() => { + const handleMessage = ( + event: MessageEvent< + BrainyChatSubmitMessage | BrainyChatInterruptMessage | BrainyUrlContextMessage + >, + ) => { + const data = event.data; + if (data?.source !== "brainy-extension") return; + if (data?.type === "brainy:chat-interrupt") { + void handleInterrupt(data as BrainyChatInterruptMessage); + return; + } + if (data?.type === "brainy:context-url") { + applyIncomingUrlSelection( + (data as BrainyUrlContextMessage).payload?.url ?? null, + ); + return; + } + if (data?.type !== "brainy:chat-submit") return; + const submitMessage = data as BrainyChatSubmitMessage; + const { submitText, files, iframeContext, additionalKwargs } = + parseBrainyChatSubmitMessage(submitMessage); + + if (!submitText && files.length === 0) return; console.log("收到插件消息", { text: submitMessage.payload?.text, @@ -473,8 +499,8 @@ export default function IframeChatPage() { message: { text: submitText, files }, externalContext: iframeContext, options: additionalKwargs ? { additionalKwargs } : undefined, - targetSelectedAgentId: nextSelection.selectedAgentId, - targetForceDefaultAssistant: nextSelection.forceDefaultAssistant, + targetSelectedAgentId: selectedAgentId, + targetForceDefaultAssistant: forceDefaultAssistant, }); }; @@ -483,11 +509,10 @@ export default function IframeChatPage() { window.removeEventListener("message", handleMessage); }; }, [ + applyIncomingUrlSelection, + forceDefaultAssistant, handleInterrupt, - manualSelectedAgentId, - pluginManagementSettings?.domainAgentMappings, - selectorAgents, - selectorLoading, + selectedAgentId, ]); const isLandingState = isNewThread && !hasStartedConversation;