diff --git a/components/canvas/CanvasChatPanel.tsx b/components/canvas/CanvasChatPanel.tsx index 8d6324889..13d6f2aa5 100644 --- a/components/canvas/CanvasChatPanel.tsx +++ b/components/canvas/CanvasChatPanel.tsx @@ -6101,6 +6101,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( @@ -6728,6 +6731,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 @@ -8016,7 +8045,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; @@ -9819,7 +9854,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({ @@ -9829,7 +9870,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) @@ -9843,7 +9884,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; @@ -12351,16 +12392,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; @@ -12406,6 +12447,7 @@ export function CanvasChatPanel({ attachments.length, currentModel?.imageGenConfig, effectiveAgentEnabled, + imageAgentEnabled, hasWorkflowContext, imageParams?.imageCount, imageParams?.imageSize, @@ -12417,21 +12459,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; @@ -12474,6 +12517,7 @@ export function CanvasChatPanel({ attachments.length, currentModel?.imageGenConfig, effectiveAgentEnabled, + imageAgentEnabled, hasWorkflowContext, imageParams?.imageCount, imageParams?.imageSize, @@ -12483,6 +12527,7 @@ export function CanvasChatPanel({ membershipSummary?.isHighVip, membershipSummary?.pointsSystemConfig?.enabled, selectedSkillTag, + isImageAgentComposer, videoParams?.duration, videoParams?.resolution, ]); @@ -12493,7 +12538,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; @@ -13140,7 +13185,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和模特图。`; @@ -13158,7 +13209,7 @@ export function CanvasChatPanel({ hasSkillTag: Boolean(selectedSkillTag), hasWorkflowContext, isDetailGenerationMode, - agentEnabled: effectiveAgentEnabled, + agentEnabled: effectiveAgentEnabled && imageAgentEnabled, }); const shouldShowOptimisticImageGeneration = !shouldTreatAsCanvasAgentControlIntent && @@ -13339,6 +13390,8 @@ export function CanvasChatPanel({ canvasAgentTask.control, activeChatId, activeComposerMode, + unifiedImageAgent, + imageAgentEnabled, agentEntryMode, agentRequiredUploadCount, canvasId, @@ -18885,7 +18938,10 @@ export function CanvasChatPanel({ > {(effectiveAgentEnabled || unifiedImageAgent) ? ( handleSelectGenerationMode("agent")} + onClick={() => { + setImageAgentEnabled(true); + handleSelectGenerationMode("agent"); + }} className={getComposerModeMenuItemClass("agent")} > ) : null} + {unifiedImageAgent && activeComposerMode === "agent" ? ( + + ) : null} {isVideoModel ? (