refactor: remove agent mode switch and streamline local agent usage in the canvas assistant panel

This commit is contained in:
HouYunFei
2026-07-05 17:04:02 +08:00
parent ca5faa1f4e
commit 6113986637
5 changed files with 14 additions and 42 deletions
@@ -10,6 +10,7 @@ description: 当前版本已实现但仍需人工验证的变更项
- Codex App 插件文档:新增插件安装教程,区分 AI 自动安装、手动安装和本仓库开发调试安装;同步优化插件 README 与技能说明。
- 顶部导航:缩小桌面顶部导航栏高度,导航项下划线和内容垂直对齐需确认。
- 配置与用户偏好:新增独立顶部导航页面,并在页面和弹窗中复用同一套配置面板;新增 Codex Tab,展示插件安装和本地 Agent 启动步骤,可配置本地 Agent 地址、Connect token 和工具执行确认开关。
- 画布 Agent:右侧 Agent 面板移除网站 / 本机模式切换,固定使用本机 Agent。
- 配置与用户偏好:每个模型渠道新增 OpenAI / Gemini 调用格式选择,默认 OpenAI,切换默认格式时同步更新默认 Base URL;渠道 Tab 增加紧凑醒目的提醒,说明模型是否可选需要到“模型”Tab 选择。
- Gemini 调用格式:支持拉取 Gemini 模型列表,并用于文本问答、在线 Agent 工具调用、基础生图和图生图请求。
- 音频和视频生成:选择 Gemini 调用格式时先给出不支持提示,仍需使用 OpenAI 兼容渠道。
@@ -6,7 +6,6 @@ import { canvasThemes } from "@/lib/canvas-theme";
import type { LocalUser } from "@/stores/use-user-store";
export type CanvasAgentChatAttachment = { id: string; name: string; url: string };
export type CanvasAgentMode = "online" | "local";
export type CanvasAgentChatMessage = {
id: string;
role: "user" | "assistant" | "system" | "tool" | "error";
@@ -228,18 +227,6 @@ export function AgentChatComposer({
);
}
export function AgentModeSwitch({ value, theme, onChange }: { value: CanvasAgentMode; theme: (typeof canvasThemes)[keyof typeof canvasThemes]; onChange: (value: CanvasAgentMode) => void }) {
return (
<div className="inline-flex shrink-0 rounded-lg border p-0.5 text-xs" style={{ borderColor: theme.node.stroke }}>
{(["online", "local"] as const).map((item) => (
<button key={item} type="button" className="rounded-md px-2 py-1 transition" style={{ background: value === item ? theme.node.fill : "transparent", color: value === item ? theme.node.text : theme.node.muted }} onClick={() => onChange(item)}>
{item === "online" ? "网站" : "本机"}
</button>
))}
</div>
);
}
export function AgentPanelTabs<T extends string>({ value, items, theme, right, onChange }: { value: T; items: { value: T; label: string; icon?: ReactNode; count?: number }[]; theme: (typeof canvasThemes)[keyof typeof canvasThemes]; right?: ReactNode; onChange: (value: T) => void }) {
return (
<div className="border-b px-3" style={{ borderColor: theme.node.stroke }}>
@@ -16,7 +16,7 @@ import { imageReferenceLabel } from "@/lib/image-reference-prompt";
import { DiaTextReveal } from "@/components/ui/dia-text-reveal";
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/ui/select";
import { CanvasPromptLibrary } from "./canvas-prompt-library";
import { AgentChatComposer, AgentChatMessage, AgentModeSwitch, AgentPanelTabs, AgentWorkingMessage, type CanvasAgentChatMessage, type CanvasAgentMode } from "./canvas-agent-chat-ui";
import { AgentChatComposer, AgentChatMessage, AgentPanelTabs, AgentWorkingMessage, type CanvasAgentChatMessage } from "./canvas-agent-chat-ui";
import { CanvasLocalAgentPanel } from "./canvas-local-agent-panel";
import { NODE_DEFAULT_SIZE } from "@/constant/canvas";
import { CanvasNodeType, type CanvasAssistantMessage, type CanvasAssistantReference, type CanvasAssistantSession, type CanvasNodeData } from "@/types/canvas";
@@ -133,14 +133,12 @@ type CanvasAssistantPanelProps = {
canUndoOps: boolean;
onUndoOps: () => CanvasAgentSnapshot | null;
onPasteImage: (file: File) => void;
agentMode: CanvasAgentMode;
onAgentModeChange: (mode: CanvasAgentMode) => void;
autoConnectLocal?: boolean;
closing: boolean;
onCollapse: () => void;
};
export function CanvasAssistantPanel({ nodes, selectedNodeIds, snapshot, sessions, activeSessionId, onSelectNodeIds, onSessionsChange, onApplyOps, canUndoOps, onUndoOps, onPasteImage, agentMode, onAgentModeChange, autoConnectLocal, closing, onCollapse }: CanvasAssistantPanelProps) {
export function CanvasAssistantPanel({ nodes, selectedNodeIds, snapshot, sessions, activeSessionId, onSelectNodeIds, onSessionsChange, onApplyOps, canUndoOps, onUndoOps, onPasteImage, autoConnectLocal, closing, onCollapse }: CanvasAssistantPanelProps) {
const theme = canvasThemes[useThemeStore((state) => state.theme)];
const user = useUserStore((state) => state.user);
const effectiveConfig = useEffectiveConfig();
@@ -642,7 +640,6 @@ export function CanvasAssistantPanel({ nodes, selectedNodeIds, snapshot, session
</div>
</div>
<div className="flex shrink-0 items-center gap-2">
<AgentModeSwitch value={agentMode} theme={theme} onChange={onAgentModeChange} />
<label className="flex items-center gap-1.5 text-xs" style={{ color: theme.node.muted }}>
<Switch size="small" checked={confirmTools} onChange={(confirmTools) => setAgentState({ confirmTools })} />
@@ -652,18 +649,14 @@ export function CanvasAssistantPanel({ nodes, selectedNodeIds, snapshot, session
</Tooltip>
</div>
</header>
{agentMode === "local" ? (
<CanvasLocalAgentPanel
embedded
snapshot={snapshot}
canUndoOps={canUndoOps}
onApplyOps={onApplyOps}
onUndoOps={onUndoOps}
autoConnect={autoConnectLocal}
/>
) : (
onlineContent
)}
<CanvasLocalAgentPanel
embedded
snapshot={snapshot}
canUndoOps={canUndoOps}
onApplyOps={onApplyOps}
onUndoOps={onUndoOps}
autoConnect={autoConnectLocal}
/>
</motion.aside>
</motion.div>
);
@@ -47,7 +47,7 @@ const webdavDomainLabels: Record<AppSyncDomainKey, string> = {
};
const codexSetupSteps = [
{ title: "安装 Codex 插件", text: "先在 Codex App 安装 Infinite Canvas 插件,插件会注册 MCP 并尝试启动本地 Canvas Agent。" },
{ title: "打开画布 Agent", text: "回到画布右侧 Agent 面板选择本地模式,网页会自动读取 Local URL 和 Connect token。" },
{ title: "打开画布 Agent", text: "回到画布右侧 Agent 面板,网页会自动读取 Local URL 和 Connect token。" },
{ title: "手动启动备用", text: "如果插件没有自动启动本地服务,在终端运行下面命令。", command: "npx -y @basketikun/canvas-agent" },
];
+2 -11
View File
@@ -45,7 +45,6 @@ import { useCanvasAgentStore } from "@/stores/canvas/use-canvas-agent-store";
import { useCanvasStore } from "@/stores/canvas/use-canvas-store";
import { applyCanvasAgentOps, type CanvasAgentOp, type CanvasAgentSnapshot } from "@/lib/canvas/canvas-agent-ops";
import { buildCanvasResourceReferences, buildNodeMentionReferences } from "@/lib/canvas/canvas-resource-references";
import type { CanvasAgentMode } from "@/components/canvas/canvas-agent-chat-ui";
import {
CanvasNodeType,
type CanvasAssistantImage,
@@ -304,7 +303,6 @@ function InfiniteCanvasPage() {
const [assistantCollapsed, setAssistantCollapsed] = useState(true);
const [assistantMounted, setAssistantMounted] = useState(false);
const [assistantClosing, setAssistantClosing] = useState(false);
const [agentMode, setAgentMode] = useState<CanvasAgentMode>("online");
const [agentUndoSnapshot, setAgentUndoSnapshot] = useState<CanvasAgentSnapshot | null>(null);
const codexAutoConnect = ["new", "recent", "choose"].includes(searchParams.get("mode") || "");
const codexCompactAgent = codexAutoConnect && searchParams.has("agentUrl");
@@ -432,11 +430,7 @@ function InfiniteCanvasPage() {
useEffect(() => {
if (!projectLoaded || !["new", "recent", "choose"].includes(searchParams.get("mode") || "")) return;
if (searchParams.has("agentUrl")) {
setAgentMode("local");
return;
}
openAgent("local");
if (!searchParams.has("agentUrl")) openAgent();
}, [projectLoaded, searchParams]);
useEffect(() => {
@@ -2465,12 +2459,11 @@ function InfiniteCanvasPage() {
);
const assistantOpen = assistantMounted && !assistantCollapsed;
const openAgent = (mode: CanvasAgentMode = agentMode) => {
const openAgent = () => {
if (agentCloseTimerRef.current) {
clearTimeout(agentCloseTimerRef.current);
agentCloseTimerRef.current = null;
}
setAgentMode(mode);
setAssistantMounted(true);
setAssistantClosing(false);
setAssistantCollapsed(false);
@@ -2806,8 +2799,6 @@ function InfiniteCanvasPage() {
canUndoOps={Boolean(agentUndoSnapshot)}
onUndoOps={undoAgentOps}
onPasteImage={pasteAssistantImage}
agentMode={agentMode}
onAgentModeChange={setAgentMode}
autoConnectLocal={codexAutoConnect}
closing={assistantClosing}
onCollapse={closeAgent}