diff --git a/components/canvas/CanvasChatPanel.tsx b/components/canvas/CanvasChatPanel.tsx index 329d64890..35c91a7e9 100644 --- a/components/canvas/CanvasChatPanel.tsx +++ b/components/canvas/CanvasChatPanel.tsx @@ -6025,6 +6025,9 @@ export function CanvasChatPanel({ const activeComposerMode = unifiedImageAgent && (rawComposerMode === "image" || rawComposerMode === "video") ? "agent" : rawComposerMode; const isImageAgentComposer = unifiedImageAgent && activeComposerMode === "agent"; + const [imageAgentEnabled, setImageAgentEnabled] = useState(false); + const imageAgentEnabledRef = useRef(imageAgentEnabled); + imageAgentEnabledRef.current = imageAgentEnabled; const isVideoToolbarDark = isVideoWorkspace || activeComposerMode === "video"; const isClawdMode = activeComposerMode === "clawd" && isClawdModeEnabled; const activeChatSessionMode = getCanvasChatSessionMode( @@ -6652,6 +6655,32 @@ export function CanvasChatPanel({ () => videoOnlyModels.find((model) => model.selectable !== false)?.id || null, [videoOnlyModels] ); + useEffect(() => { + if (!unifiedImageAgent || imageAgentEnabled) return; + const selectedModel = allModels.find((model) => model.id === selectedModelId); + if (isCanvasImageModel(selectedModel)) return; + const fallbackImageModelId = [ + lastImageModelId, + firstImageModelId, + ...visualModels.map((model) => model.id), + ].find((modelId) => { + const model = allModels.find((item) => item.id === modelId); + return Boolean(modelId && modelId !== "auto" && isCanvasImageModel(model) && !isCanvasVideoModel(model)); + }); + if (!fallbackImageModelId) return; + currentModelIdRef.current = fallbackImageModelId; + setSelectedModelId(fallbackImageModelId); + }, [ + allModels, + firstImageModelId, + imageAgentEnabled, + isCanvasImageModel, + isCanvasVideoModel, + lastImageModelId, + selectedModelId, + unifiedImageAgent, + visualModels, + ]); const activeModeLabel = activeComposerMode === "clawd" ? CLAW_MODE_LABEL @@ -7940,7 +7969,13 @@ export function CanvasChatPanel({ contextPayload: capturedContextPayload, lastMessage: request.messages.at(-1), }); - const effectiveComposerMode = activeComposerModeRef.current || activeComposerMode; + const activeRequestComposerMode = activeComposerModeRef.current || activeComposerMode; + const effectiveComposerMode = + unifiedImageAgent && + activeRequestComposerMode === "agent" && + !imageAgentEnabledRef.current + ? "image" + : activeRequestComposerMode; const effectiveAgentEntryMode = agentEntryModeRef.current; const hiddenPrompt = hiddenPromptRef.current || undefined; const autoContext = autoContextRef.current || undefined; @@ -9727,7 +9762,13 @@ export function CanvasChatPanel({ isValidChatHttpUrl(getCanvasImageNodeUrl(node)), ); - const effectiveComposerMode = activeComposerModeRef.current || activeComposerMode; + const activeRequestComposerMode = activeComposerModeRef.current || activeComposerMode; + const effectiveComposerMode = + unifiedImageAgent && + activeRequestComposerMode === "agent" && + !imageAgentEnabledRef.current + ? "image" + : activeRequestComposerMode; const shouldIsolateDirectModeContext = effectiveComposerMode !== "agent"; const shouldPreserveCanvasControlContext = shouldBypassCanvasImagePointsPrecheckForAgentControl({ @@ -9737,7 +9778,7 @@ export function CanvasChatPanel({ hasSkillTag: Boolean(selectedSkillTag), hasWorkflowContext, isDetailGenerationMode, - agentEnabled: effectiveAgentEnabled, + agentEnabled: effectiveAgentEnabled && imageAgentEnabled, }); const selectedAgentSlotCountForContext = nodes.filter( (node) => String(node.type) === "agent-slot" && selectedNodeIdSet.has(node.id) @@ -9751,7 +9792,7 @@ export function CanvasChatPanel({ hasSkillTag: Boolean(selectedSkillTag), hasWorkflowContext, isDetailGenerationMode, - agentEnabled: effectiveAgentEnabled, + agentEnabled: effectiveAgentEnabled && imageAgentEnabled, }); const MAX_CONTEXT_IMAGES = 12; const MAX_AGENT_CANDIDATE_NODES = 30; @@ -12259,16 +12300,16 @@ export function CanvasChatPanel({ ]); const ensureImagePointsBeforeSend = useCallback(() => { - if (isVideoModel || activeComposerMode === "agent") return true; + if (isVideoModel || (activeComposerMode === "agent" && imageAgentEnabled)) return true; if ( shouldBypassCanvasImagePointsPrecheckForAgentControl({ text: input, - activeComposerMode, + activeComposerMode: isImageAgentComposer && !imageAgentEnabled ? "image" : activeComposerMode, attachmentCount: attachments.length, hasSkillTag: Boolean(selectedSkillTag), hasWorkflowContext, isDetailGenerationMode, - agentEnabled: effectiveAgentEnabled, + agentEnabled: effectiveAgentEnabled && imageAgentEnabled, }) ) { return true; @@ -12314,6 +12355,7 @@ export function CanvasChatPanel({ attachments.length, currentModel?.imageGenConfig, effectiveAgentEnabled, + imageAgentEnabled, hasWorkflowContext, imageParams?.imageCount, imageParams?.imageSize, @@ -12325,21 +12367,22 @@ export function CanvasChatPanel({ membershipSummary?.pointsSystemConfig?.enabled, membershipSummary?.summary?.currentCredits, selectedSkillTag, + isImageAgentComposer, ]); const estimatedPointsCost = useMemo(() => { if (membershipSummary?.isHighVip) return null; if (membershipSummary?.pointsSystemConfig?.enabled !== true) return null; - if (activeComposerMode === "agent") return null; + if (activeComposerMode === "agent" && imageAgentEnabled) return null; if ( shouldBypassCanvasImagePointsPrecheckForAgentControl({ text: input, - activeComposerMode, + activeComposerMode: isImageAgentComposer && !imageAgentEnabled ? "image" : activeComposerMode, attachmentCount: attachments.length, hasSkillTag: Boolean(selectedSkillTag), hasWorkflowContext, isDetailGenerationMode, - agentEnabled: effectiveAgentEnabled, + agentEnabled: effectiveAgentEnabled && imageAgentEnabled, }) ) { return null; @@ -12382,6 +12425,7 @@ export function CanvasChatPanel({ attachments.length, currentModel?.imageGenConfig, effectiveAgentEnabled, + imageAgentEnabled, hasWorkflowContext, imageParams?.imageCount, imageParams?.imageSize, @@ -12391,6 +12435,7 @@ export function CanvasChatPanel({ membershipSummary?.isHighVip, membershipSummary?.pointsSystemConfig?.enabled, selectedSkillTag, + isImageAgentComposer, videoParams?.duration, videoParams?.resolution, ]); @@ -12401,7 +12446,7 @@ export function CanvasChatPanel({ sensitiveInput?: OpenClawSensitiveInput ) => { const supplement = typeof overrideInput === "string" ? overrideInput.trim() : input.trim(); - if (isCanvasAgentRunning && !isClawdMode) { + if (isCanvasAgentRunning && !isClawdMode && imageAgentEnabled) { if (supplement && await canvasAgentTask.control("steer", supplement)) { setInput(""); const editor = textareaRef.current as HTMLDivElement | null; @@ -13048,7 +13093,13 @@ export function CanvasChatPanel({ if (!textContent && hasWorkflowContext && workflowContext?.summaryText) { textContent = `请基于当前工作流设定生成方案:${workflowContext.summaryText}`; } - const effectiveComposerMode = activeComposerModeRef.current || activeComposerMode; + const activeRequestComposerMode = activeComposerModeRef.current || activeComposerMode; + const effectiveComposerMode = + unifiedImageAgent && + activeRequestComposerMode === "agent" && + !imageAgentEnabledRef.current + ? "image" + : activeRequestComposerMode; const effectiveAgentEntryMode = agentEntryModeRef.current || agentEntryMode; if (!textContent && hasAgentUploadSlots && selectedAgentPreset) { textContent = `请按「${selectedAgentPreset.name}」风格化配置顺序处理产品图1、产品图2和模特图。`; @@ -13066,7 +13117,7 @@ export function CanvasChatPanel({ hasSkillTag: Boolean(selectedSkillTag), hasWorkflowContext, isDetailGenerationMode, - agentEnabled: effectiveAgentEnabled, + agentEnabled: effectiveAgentEnabled && imageAgentEnabled, }); const shouldShowOptimisticImageGeneration = !shouldTreatAsCanvasAgentControlIntent && @@ -13247,6 +13298,8 @@ export function CanvasChatPanel({ canvasAgentTask.control, activeChatId, activeComposerMode, + unifiedImageAgent, + imageAgentEnabled, agentEntryMode, agentRequiredUploadCount, canvasId, @@ -18775,7 +18828,10 @@ export function CanvasChatPanel({ > {(effectiveAgentEnabled || unifiedImageAgent) ? ( handleSelectGenerationMode("agent")} + onClick={() => { + setImageAgentEnabled(true); + handleSelectGenerationMode("agent"); + }} className={getComposerModeMenuItemClass("agent")} > ) : null} + {unifiedImageAgent && activeComposerMode === "agent" ? ( + + ) : null} {isVideoModel ? (