feat(canvas): default image composer to direct generation
This commit is contained in:
parent
76bf04c221
commit
34e6459fe8
@ -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) ? (
|
||||
<DropdownMenuItem
|
||||
onClick={() => handleSelectGenerationMode("agent")}
|
||||
onClick={() => {
|
||||
setImageAgentEnabled(true);
|
||||
handleSelectGenerationMode("agent");
|
||||
}}
|
||||
className={getComposerModeMenuItemClass("agent")}
|
||||
>
|
||||
<CanvasToolbarIcon
|
||||
@ -18858,6 +18914,64 @@ export function CanvasChatPanel({
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
) : null}
|
||||
{unifiedImageAgent && activeComposerMode === "agent" ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className={cn(
|
||||
"h-8 w-8 shrink-0 rounded-full transition-colors",
|
||||
imageAgentEnabled
|
||||
? "text-[#5b6472] hover:bg-[#f3f4f6] hover:text-[#20242a] dark:text-zinc-300 dark:hover:bg-white/10"
|
||||
: "bg-[#eeeeef] text-[#20242a] hover:bg-[#e2e3e5] dark:bg-white/10 dark:text-zinc-100 dark:hover:bg-white/15"
|
||||
)}
|
||||
onClick={() => {
|
||||
if (imageAgentEnabled) {
|
||||
const selectedImageModel = allModels.find(
|
||||
(model) => model.id === selectedModelId
|
||||
);
|
||||
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 (!isCanvasImageModel(selectedImageModel)) {
|
||||
if (!fallbackImageModelId) {
|
||||
toast.error("当前没有可用的生图模型");
|
||||
return;
|
||||
}
|
||||
currentModelIdRef.current = fallbackImageModelId;
|
||||
setSelectedModelId(fallbackImageModelId);
|
||||
}
|
||||
setImageAgentEnabled(false);
|
||||
} else {
|
||||
setImageAgentEnabled(true);
|
||||
}
|
||||
}}
|
||||
disabled={isSubmitting}
|
||||
title={
|
||||
imageAgentEnabled
|
||||
? "关闭 Agent,直接使用生图模型"
|
||||
: "启用 Agent 任务处理"
|
||||
}
|
||||
aria-label={
|
||||
imageAgentEnabled
|
||||
? "关闭 Agent,直接使用生图模型"
|
||||
: "启用 Agent 任务处理"
|
||||
}
|
||||
aria-pressed={imageAgentEnabled}
|
||||
>
|
||||
{imageAgentEnabled ? <X className="h-4 w-4" /> : <Bot className="h-4 w-4" />}
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
{isVideoModel ? (
|
||||
<div className="min-w-0 flex-1 overflow-x-auto scrollbar-hide">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user