Merge branch 'gzy' into 'master'

feat: iframe增加智能体显示

See merge request knowledge-base-bj/deerflow-server!157
This commit is contained in:
g zy committed 2026-06-29 08:43:11 +00:00
commit d8af6c8f48
3 files changed
+107 -56

No files matched your search

@@ -20,6 +20,7 @@ import {
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { PromptInputButton } from "@/components/ai-elements/prompt-input"; import { PromptInputButton } from "@/components/ai-elements/prompt-input";
import { AgentManageDialog } from "@/components/workspace/agents/agent-manage-dialog"; import { AgentManageDialog } from "@/components/workspace/agents/agent-manage-dialog";
import { Tooltip } from "@/components/workspace/tooltip";
import { useAgentSelector, useAgentsPaginated } from "@/core/agents"; import { useAgentSelector, useAgentsPaginated } from "@/core/agents";
import type { Agent } from "@/core/agents"; import type { Agent } from "@/core/agents";
import { getStoredAuth } from "@/core/auth"; import { getStoredAuth } from "@/core/auth";
@@ -86,6 +87,7 @@ export function AgentSelector({
}; };
const triggerLabel = selectedAgent ? desensitize(selectedAgent.name) : "选择 Agent"; const triggerLabel = selectedAgent ? desensitize(selectedAgent.name) : "选择 Agent";
const iconTooltipLabel = selectedAgent ? triggerLabel : "未选择";
const iconMode = triggerVariant === "icon"; const iconMode = triggerVariant === "icon";
return ( return (
@@ -93,38 +95,40 @@ export function AgentSelector({
<Dialog open={open} onOpenChange={setOpen}> <Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
{iconMode ? ( {iconMode ? (
<Button <Tooltip content={iconTooltipLabel}>
type="button" <Button
variant="ghost" type="button"
size="icon" variant="ghost"
disabled={disabled} size="icon"
aria-label={selectedAgent ? `已选择智能体:${triggerLabel}` : "选择智能体"} disabled={disabled}
className={cn( aria-label={selectedAgent ? `已选择智能体:${triggerLabel}` : "选择智能体"}
"relative size-9 rounded-full text-muted-foreground shadow-none hover:bg-muted/60 hover:text-foreground", className={cn(
selectedAgent && "text-amber-600 hover:text-amber-700", "relative size-9 rounded-full text-muted-foreground shadow-none hover:bg-muted/60 hover:text-foreground",
selectedAgent && iconActiveClassName, selectedAgent && "text-amber-600 hover:text-amber-700",
iconButtonClassName, selectedAgent && iconActiveClassName,
)} iconButtonClassName,
> )}
<BotIcon className="size-4" /> >
{selectedAgent && ( <BotIcon className="size-4" />
<span {selectedAgent && (
role="button" <span
tabIndex={0} role="button"
aria-label="清除已选 agent" tabIndex={0}
className="absolute -right-1 -top-1 inline-flex size-4 items-center justify-center rounded-full border bg-background text-foreground shadow-sm hover:bg-muted" aria-label="清除已选 agent"
onClick={handleClear} className="absolute -right-1 -top-1 inline-flex size-4 items-center justify-center rounded-full border bg-background text-foreground shadow-sm hover:bg-muted"
onKeyDown={(e) => { onClick={handleClear}
if (e.key === "Enter" || e.key === " ") { onKeyDown={(e) => {
e.preventDefault(); if (e.key === "Enter" || e.key === " ") {
onAgentChange(null); e.preventDefault();
} onAgentChange(null);
}} }
> }}
<XIcon className="size-2.5" /> >
</span> <XIcon className="size-2.5" />
)} </span>
</Button> )}
</Button>
</Tooltip>
) : ( ) : (
<PromptInputButton <PromptInputButton
type="button" type="button"
@@ -166,3 +166,25 @@ test("mapped domain overrides the user's manual selection for that incoming mess
}, },
); );
}); });
test("unmapped incoming url resets auto-selected agent back to default when there is no manual selection", () => {
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,
},
);
});
+49 -24
View File
@@ -83,6 +83,13 @@ type BrainyChatInterruptMessage = {
}; };
requestId?: string; requestId?: string;
}; };
type BrainyUrlContextMessage = {
source?: string;
type?: string;
payload?: {
url?: string;
};
};
type PendingIframeSubmit = { type PendingIframeSubmit = {
message: PromptInputMessage; message: PromptInputMessage;
externalContext?: Record<string, unknown>; externalContext?: Record<string, unknown>;
@@ -429,25 +436,10 @@ export default function IframeChatPage() {
[thread], [thread],
); );
useEffect(() => { const applyIncomingUrlSelection = useCallback(
const handleMessage = ( (pageUrl: string | null | undefined) => {
event: MessageEvent<BrainyChatSubmitMessage | BrainyChatInterruptMessage>,
) => {
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 nextSelection = resolveIncomingIframeSelection({ const nextSelection = resolveIncomingIframeSelection({
pageUrl: submitMessage.payload?.pageContext?.url, pageUrl,
mappings: pluginManagementSettings?.domainAgentMappings ?? [], mappings: pluginManagementSettings?.domainAgentMappings ?? [],
availableAgentIds: selectorLoading availableAgentIds: selectorLoading
? undefined ? undefined
@@ -456,6 +448,40 @@ export default function IframeChatPage() {
}); });
setSelectedAgentId(nextSelection.selectedAgentId); setSelectedAgentId(nextSelection.selectedAgentId);
setForceDefaultAssistant(nextSelection.forceDefaultAssistant); 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("收到插件消息", { console.log("收到插件消息", {
text: submitMessage.payload?.text, text: submitMessage.payload?.text,
@@ -473,8 +499,8 @@ export default function IframeChatPage() {
message: { text: submitText, files }, message: { text: submitText, files },
externalContext: iframeContext, externalContext: iframeContext,
options: additionalKwargs ? { additionalKwargs } : undefined, options: additionalKwargs ? { additionalKwargs } : undefined,
targetSelectedAgentId: nextSelection.selectedAgentId, targetSelectedAgentId: selectedAgentId,
targetForceDefaultAssistant: nextSelection.forceDefaultAssistant, targetForceDefaultAssistant: forceDefaultAssistant,
}); });
}; };
@@ -483,11 +509,10 @@ export default function IframeChatPage() {
window.removeEventListener("message", handleMessage); window.removeEventListener("message", handleMessage);
}; };
}, [ }, [
applyIncomingUrlSelection,
forceDefaultAssistant,
handleInterrupt, handleInterrupt,
manualSelectedAgentId, selectedAgentId,
pluginManagementSettings?.domainAgentMappings,
selectorAgents,
selectorLoading,
]); ]);
const isLandingState = isNewThread && !hasStartedConversation; const isLandingState = isNewThread && !hasStartedConversation;