feat: polish canvas and workflow settings
This commit is contained in:
parent
f946d9c1f0
commit
b4529c860c
@ -58,8 +58,10 @@ import {
|
||||
getAgentWorkflowSpecialModuleRootOptions,
|
||||
getEnabledAgentWorkflowOptionTree,
|
||||
getAgentWorkflowCategoryLabels,
|
||||
getAgentWorkflowBusinessModeSettings,
|
||||
getAgentWorkflowConstraintSettings,
|
||||
getAgentWorkflowPageFeatureSettings,
|
||||
normalizeAgentWorkflowBusinessModeSettings,
|
||||
normalizeAgentWorkflowOptionOrdering,
|
||||
flattenAgentWorkflowSteps,
|
||||
normalizeAgentWorkflowStepOrdering,
|
||||
@ -129,6 +131,8 @@ import type {
|
||||
AgentPresetLibraryConfig,
|
||||
AgentPsdTextColorBrandConfig,
|
||||
AgentPresetLibrarySection,
|
||||
AgentWorkflowBusinessModeConfig,
|
||||
AgentWorkflowBusinessModeKey,
|
||||
AgentWorkflowBrandModelConfig,
|
||||
AgentWorkflowBrandStoreEntry,
|
||||
AgentWorkflowCategoryLabels,
|
||||
@ -2046,6 +2050,10 @@ export function AgentWorkflowSettingsClient({
|
||||
() => getAgentWorkflowPageFeatureSettings(config),
|
||||
[config]
|
||||
);
|
||||
const workflowBusinessModeSettings = useMemo(
|
||||
() => getAgentWorkflowBusinessModeSettings(config),
|
||||
[config]
|
||||
);
|
||||
const firstCategoryLabel = categoryLabels.firstCategory;
|
||||
const secondCategoryLabel = categoryLabels.secondCategory;
|
||||
const thirdCategoryLabel = categoryLabels.thirdCategory;
|
||||
@ -3535,6 +3543,61 @@ export function AgentWorkflowSettingsClient({
|
||||
}
|
||||
};
|
||||
|
||||
const updateBusinessMode = (
|
||||
modeKey: AgentWorkflowBusinessModeKey,
|
||||
updater: (current: AgentWorkflowBusinessModeConfig) => AgentWorkflowBusinessModeConfig
|
||||
) => {
|
||||
setConfig((prev) => {
|
||||
const currentSettings = normalizeAgentWorkflowBusinessModeSettings(
|
||||
prev.businessModeSettings
|
||||
);
|
||||
return {
|
||||
...prev,
|
||||
businessModeSettings: {
|
||||
modes: currentSettings.modes.map((mode) =>
|
||||
mode.key === modeKey ? updater(mode) : mode
|
||||
),
|
||||
},
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const handleUploadBusinessModeSvg = async (
|
||||
modeKey: AgentWorkflowBusinessModeKey,
|
||||
file: File
|
||||
) => {
|
||||
const isSvg =
|
||||
file.type === "image/svg+xml" || file.name.toLowerCase().endsWith(".svg");
|
||||
if (!isSvg) {
|
||||
toast.error("请上传 SVG 图标");
|
||||
return;
|
||||
}
|
||||
|
||||
const refKey = `business-mode:${modeKey}`;
|
||||
try {
|
||||
setUploadingKey(refKey);
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
|
||||
const response = await fetchUploadWithRetry(formData);
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok || !result?.url) {
|
||||
throw new Error(result?.error || "上传失败");
|
||||
}
|
||||
|
||||
updateBusinessMode(modeKey, (mode) => ({
|
||||
...mode,
|
||||
customSvgUrl: result.url,
|
||||
}));
|
||||
toast.success("SVG 图标上传成功");
|
||||
} catch (error) {
|
||||
toast.error(error instanceof Error ? error.message : "SVG 图标上传失败");
|
||||
} finally {
|
||||
setUploadingKey(null);
|
||||
}
|
||||
};
|
||||
|
||||
const updateSelectedBrandPsdTextColorConfig = (
|
||||
updater: (current: AgentPsdTextColorBrandConfig | AgentPsdTextColorConfig) => {
|
||||
enabled: boolean;
|
||||
@ -5654,7 +5717,7 @@ export function AgentWorkflowSettingsClient({
|
||||
onChange={(e) =>
|
||||
setShowcaseConfig((prev) => ({ ...prev, subtitle: e.target.value }))
|
||||
}
|
||||
placeholder="选择生图模式、查询商品图、完成风格配置并实时查看结果"
|
||||
placeholder="选择品牌、配置风格化提示词、选择生图模式并查看结果"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -5866,7 +5929,7 @@ export function AgentWorkflowSettingsClient({
|
||||
<div>
|
||||
<CardTitle className="text-base">前台流程功能开关</CardTitle>
|
||||
<CardDescription>
|
||||
控制风格化页面是否展示选择生图模式、选择 SPU / SKU 和套 PSD 模板入口
|
||||
控制风格化页面是否展示固定生图模式、素材选择和套 PSD 模板入口
|
||||
</CardDescription>
|
||||
</div>
|
||||
</div>
|
||||
@ -5874,9 +5937,9 @@ export function AgentWorkflowSettingsClient({
|
||||
<CardContent className="grid gap-3 md:grid-cols-4">
|
||||
<div className="flex items-center justify-between rounded-lg border bg-muted/20 px-4 py-3">
|
||||
<div className="pr-4">
|
||||
<div className="text-sm font-medium">开启第一步:选择生图模式</div>
|
||||
<div className="text-sm font-medium">开启固定生图模式</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
关闭后前台不再要求选择生图模式,直接进入后续配置并选择模型生图。
|
||||
关闭后前台不展示主图/SKU图、详情和买家秀模式入口,直接进入后续配置并选择模型生图。
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
@ -5896,7 +5959,7 @@ export function AgentWorkflowSettingsClient({
|
||||
|
||||
<div className="flex items-center justify-between rounded-lg border bg-muted/20 px-4 py-3">
|
||||
<div className="pr-4">
|
||||
<div className="text-sm font-medium">开启第二步:选择 SPU / SKU</div>
|
||||
<div className="text-sm font-medium">开启素材选择</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
关闭后前台跳过商品素材搜索,不再强制补齐 SPU / SKU 输入图槽位。
|
||||
</div>
|
||||
@ -5963,6 +6026,177 @@ export function AgentWorkflowSettingsClient({
|
||||
</Card>
|
||||
</motion.div>
|
||||
|
||||
<motion.div variants={itemVariants} className="lg:col-span-2">
|
||||
<Card>
|
||||
<CardHeader className="pb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-cyan-500/10">
|
||||
<Layers className="h-4 w-4 text-cyan-600" />
|
||||
</div>
|
||||
<div>
|
||||
<CardTitle className="text-base">风格化固定模式配置</CardTitle>
|
||||
<CardDescription>
|
||||
前台固定展示主图/SKU图、详情和买家秀三个业务模式;图标优先使用管理员上传的 SVG。
|
||||
</CardDescription>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="grid gap-4 lg:grid-cols-3">
|
||||
{workflowBusinessModeSettings.modes.map((mode) => {
|
||||
const refKey = `business-mode:${mode.key}`;
|
||||
const isUploading = uploadingKey === refKey;
|
||||
const Icon =
|
||||
mode.key === "detail"
|
||||
? Layers
|
||||
: mode.key === "buyer_show"
|
||||
? UserCircle
|
||||
: Package;
|
||||
|
||||
return (
|
||||
<div key={mode.key} className="space-y-4 rounded-xl border bg-muted/20 p-4">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-xl border bg-background">
|
||||
{mode.customSvgUrl ? (
|
||||
<img
|
||||
src={mode.customSvgUrl}
|
||||
alt={mode.title}
|
||||
className="h-7 w-7 object-contain"
|
||||
/>
|
||||
) : (
|
||||
<Icon className="h-6 w-6 text-muted-foreground" />
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-semibold">{mode.title}</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{mode.key === "buyer_show" ? "预留模块" : "已接入前台入口"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
checked={mode.enabled}
|
||||
onCheckedChange={(checked) =>
|
||||
updateBusinessMode(mode.key, (current) => ({
|
||||
...current,
|
||||
enabled: checked,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>显示名称</Label>
|
||||
<Input
|
||||
value={mode.title}
|
||||
onChange={(event) =>
|
||||
updateBusinessMode(mode.key, (current) => ({
|
||||
...current,
|
||||
title: event.target.value,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>说明文案</Label>
|
||||
<Textarea
|
||||
value={mode.description || ""}
|
||||
onChange={(event) =>
|
||||
updateBusinessMode(mode.key, (current) => ({
|
||||
...current,
|
||||
description: event.target.value,
|
||||
}))
|
||||
}
|
||||
className="min-h-[90px]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{mode.key === "detail" ? (
|
||||
<div className="grid gap-2 rounded-lg border bg-background/60 p-3">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<span className="text-xs text-muted-foreground">详情图替换</span>
|
||||
<Switch
|
||||
checked={mode.referenceReplaceEnabled !== false}
|
||||
onCheckedChange={(checked) =>
|
||||
updateBusinessMode(mode.key, (current) => ({
|
||||
...current,
|
||||
referenceReplaceEnabled: checked,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<span className="text-xs text-muted-foreground">详情图生成</span>
|
||||
<Switch
|
||||
checked={mode.aiDetailGenerateEnabled !== false}
|
||||
onCheckedChange={(checked) =>
|
||||
updateBusinessMode(mode.key, (current) => ({
|
||||
...current,
|
||||
aiDetailGenerateEnabled: checked,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>自定义 SVG 图标</Label>
|
||||
<input
|
||||
ref={(el) => {
|
||||
fileInputRefs.current[refKey] = el;
|
||||
}}
|
||||
type="file"
|
||||
accept=".svg,image/svg+xml"
|
||||
className="hidden"
|
||||
onChange={(event) => {
|
||||
const file = event.target.files?.[0];
|
||||
if (file) {
|
||||
void handleUploadBusinessModeSvg(mode.key, file);
|
||||
}
|
||||
event.target.value = "";
|
||||
}}
|
||||
/>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={isUploading}
|
||||
onClick={() => fileInputRefs.current[refKey]?.click()}
|
||||
>
|
||||
{isUploading ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<Upload className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
上传 SVG
|
||||
</Button>
|
||||
{mode.customSvgUrl ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
updateBusinessMode(mode.key, (current) => ({
|
||||
...current,
|
||||
customSvgUrl: "",
|
||||
}))
|
||||
}
|
||||
>
|
||||
清除
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
|
||||
<motion.div variants={itemVariants} className="lg:col-span-2">
|
||||
<Card>
|
||||
<CardHeader className="pb-4">
|
||||
|
||||
@ -1170,6 +1170,26 @@ const updateSchema = z.object({
|
||||
batchProcessingEnabled: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
businessModeSettings: z
|
||||
.object({
|
||||
modes: z
|
||||
.array(
|
||||
z.object({
|
||||
key: z.enum(["main_sku", "detail", "buyer_show"]),
|
||||
title: z.string().trim().min(1).max(40).optional(),
|
||||
description: z.string().trim().max(220).nullable().optional(),
|
||||
enabled: z.boolean().optional(),
|
||||
iconKey: z.enum(["main-sku", "detail", "buyer-show"]).optional(),
|
||||
customSvgUrl: z.string().trim().max(2000).nullable().optional(),
|
||||
sortOrder: z.number().min(0).max(99).optional(),
|
||||
referenceReplaceEnabled: z.boolean().optional(),
|
||||
aiDetailGenerateEnabled: z.boolean().optional(),
|
||||
})
|
||||
)
|
||||
.max(3)
|
||||
.optional(),
|
||||
})
|
||||
.optional(),
|
||||
steps: z
|
||||
.array(agentWorkflowStepSchema)
|
||||
.max(20),
|
||||
|
||||
@ -186,6 +186,7 @@ import {
|
||||
defaultAgentWorkflowUiConfig,
|
||||
flattenVisibleAgentWorkflowSteps,
|
||||
getEnabledAgentWorkflowStepTree,
|
||||
getAgentWorkflowBusinessModeSettings,
|
||||
getAgentWorkflowPageFeatureSettings,
|
||||
normalizeAgentWorkflowUiConfig,
|
||||
sanitizeAgentWorkflowSelections,
|
||||
@ -1690,6 +1691,10 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) {
|
||||
() => getAgentWorkflowPageFeatureSettings(resolvedWorkflowUiConfig),
|
||||
[resolvedWorkflowUiConfig]
|
||||
);
|
||||
const workflowBusinessModeSettings = useMemo(
|
||||
() => getAgentWorkflowBusinessModeSettings(resolvedWorkflowUiConfig),
|
||||
[resolvedWorkflowUiConfig]
|
||||
);
|
||||
const isWorkflowPresetStepEnabled =
|
||||
workflowPageFeatureSettings.presetStepEnabled !== false;
|
||||
const isWorkflowMaterialStepEnabled =
|
||||
@ -8635,17 +8640,15 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) {
|
||||
/>
|
||||
}
|
||||
brandStorePanel={
|
||||
isWorkflowPresetStepEnabled ? (
|
||||
<AgentWorkflowBrandStorePanel
|
||||
config={resolvedWorkflowUiConfig}
|
||||
inputs={workflowInputs}
|
||||
onInputsChange={handleWorkflowInputsChange}
|
||||
psdTemplateCategoryTagIndex={
|
||||
siteSettingsData?.agentPsdTemplateCategoryTagIndex
|
||||
}
|
||||
theme="dark"
|
||||
/>
|
||||
) : null
|
||||
<AgentWorkflowBrandStorePanel
|
||||
config={resolvedWorkflowUiConfig}
|
||||
inputs={workflowInputs}
|
||||
onInputsChange={handleWorkflowInputsChange}
|
||||
psdTemplateCategoryTagIndex={
|
||||
siteSettingsData?.agentPsdTemplateCategoryTagIndex
|
||||
}
|
||||
theme="dark"
|
||||
/>
|
||||
}
|
||||
chatId={agentModeChatId}
|
||||
deletedResultCollections={agentProjectDeletedResultCollections}
|
||||
@ -8683,7 +8686,7 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) {
|
||||
isWorkflowPresetStepEnabled ? (
|
||||
<AgentPresetPanelDarkV2
|
||||
embedded
|
||||
embeddedEntityLabel="生图模式"
|
||||
embeddedEntityLabel="风格化模板"
|
||||
onSelectPreset={setSelectedAgentPresetId}
|
||||
selectedPresetId={selectedAgentPresetId}
|
||||
/>
|
||||
@ -8702,6 +8705,7 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) {
|
||||
syncGeneratedResultsToCanvas={false}
|
||||
topNavRightSlot={agentProjectNavSlot}
|
||||
workflowContext={workflowContext}
|
||||
workflowBusinessModeSettings={workflowBusinessModeSettings}
|
||||
workflowFeatureSettings={workflowPageFeatureSettings}
|
||||
// 嵌入的风格化选择面板
|
||||
workflowPromptOptimizationEnabled={appearanceRecognitionEnabled}
|
||||
|
||||
@ -83,6 +83,8 @@ import {
|
||||
normalizeAgentPresetModeConfig,
|
||||
} from "@/lib/agents/preset-mode-config";
|
||||
import {
|
||||
defaultAgentWorkflowBusinessModeSettings,
|
||||
normalizeAgentWorkflowBusinessModeSettings,
|
||||
normalizeAgentWorkflowPageFeatureSettings,
|
||||
} from "@/lib/agents/workflow-ui-config";
|
||||
import { buildAgentSceneImageRolePrompt } from "@/lib/agents/scene-image-role-prompt";
|
||||
@ -102,6 +104,8 @@ import type {
|
||||
AgentTaskDetailReferenceSource,
|
||||
AgentWorkflowShowcaseConfig,
|
||||
AgentWorkflowComposerContext,
|
||||
AgentWorkflowBusinessModeKey,
|
||||
AgentWorkflowBusinessModeSettings,
|
||||
AgentWorkflowMaterialQueryType,
|
||||
AgentWorkflowPageFeatureSettings,
|
||||
AgentWorkflowSelectedBrandModel,
|
||||
@ -120,6 +124,7 @@ interface AgentGenerationWorkspaceDarkV2Props {
|
||||
selectedAgentPresetId?: string | null;
|
||||
selectedBrandModel?: AgentWorkflowSelectedBrandModel | null;
|
||||
workflowContext?: AgentWorkflowComposerContext | null;
|
||||
workflowBusinessModeSettings?: AgentWorkflowBusinessModeSettings | null;
|
||||
workflowFeatureSettings?: AgentWorkflowPageFeatureSettings | null;
|
||||
workflowPromptOptimizationEnabled?: boolean;
|
||||
getLatestWorkflowContext?: () => AgentWorkflowComposerContext | null;
|
||||
@ -257,6 +262,15 @@ function getTaskDetailReferenceSourceLabel(source: AgentTaskDetailReferenceSourc
|
||||
return source === "main" ? "主图" : source === "sku" ? "SKU图" : "详情图";
|
||||
}
|
||||
|
||||
function getBusinessModeKeyByResultMode(
|
||||
modeType: AgentGenerationResultModeType
|
||||
): AgentWorkflowBusinessModeKey {
|
||||
if (modeType === "detail_generation" || modeType === "detail_replacement") {
|
||||
return "detail";
|
||||
}
|
||||
return "main_sku";
|
||||
}
|
||||
|
||||
function normalizeResultCollectionUrls(urls?: string[] | null) {
|
||||
return Array.from(
|
||||
new Set(
|
||||
@ -961,6 +975,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
selectedAgentPresetId,
|
||||
selectedBrandModel = null,
|
||||
workflowContext = null,
|
||||
workflowBusinessModeSettings = null,
|
||||
workflowFeatureSettings = null,
|
||||
workflowPromptOptimizationEnabled = true,
|
||||
getLatestWorkflowContext,
|
||||
@ -1029,6 +1044,10 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
() => normalizeAgentWorkflowPageFeatureSettings(workflowFeatureSettings),
|
||||
[workflowFeatureSettings]
|
||||
);
|
||||
const resolvedBusinessModeSettings = useMemo(
|
||||
() => normalizeAgentWorkflowBusinessModeSettings(workflowBusinessModeSettings),
|
||||
[workflowBusinessModeSettings]
|
||||
);
|
||||
const isPresetStepEnabled = resolvedWorkflowFeatureSettings.presetStepEnabled !== false;
|
||||
const isMaterialStepEnabled = resolvedWorkflowFeatureSettings.materialStepEnabled !== false;
|
||||
const isPsdTemplateEnabled = resolvedWorkflowFeatureSettings.psdTemplateEnabled !== false;
|
||||
@ -1056,6 +1075,8 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
normalizedIncomingWorkspaceState.selectedModelId || "auto"
|
||||
);
|
||||
const [selectedResultUrl, setSelectedResultUrl] = useState<string | null>(null);
|
||||
const [activeBusinessModeKey, setActiveBusinessModeKey] =
|
||||
useState<AgentWorkflowBusinessModeKey>("main_sku");
|
||||
const [activeResultModeType, setActiveResultModeType] =
|
||||
useState<AgentGenerationResultModeType>("scene_generation");
|
||||
const [fullscreenPreview, setFullscreenPreview] = useState<string | null>(null);
|
||||
@ -1234,6 +1255,18 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
: isSkuReplacementPreset
|
||||
? "sku_replacement"
|
||||
: "scene_generation";
|
||||
const businessModeCards = resolvedBusinessModeSettings.modes.length > 0
|
||||
? resolvedBusinessModeSettings.modes
|
||||
: defaultAgentWorkflowBusinessModeSettings.modes;
|
||||
const activeBusinessMode = businessModeCards.find((mode) => mode.key === activeBusinessModeKey) ||
|
||||
businessModeCards[0] ||
|
||||
defaultAgentWorkflowBusinessModeSettings.modes[0];
|
||||
const currentPresetBusinessModeKey = getBusinessModeKeyByResultMode(currentResultModeType);
|
||||
const isSelectedBusinessModeUnavailable = activeBusinessMode?.enabled === false;
|
||||
const isBusinessModePresetMismatch =
|
||||
Boolean(selectedAgentPresetId) &&
|
||||
activeBusinessModeKey !== "buyer_show" &&
|
||||
activeBusinessModeKey !== currentPresetBusinessModeKey;
|
||||
const productSlotCount =
|
||||
selectedAgentPreset?.type === "image"
|
||||
? isSceneGenerationPreset
|
||||
@ -1659,20 +1692,22 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
);
|
||||
const [activeToolStep, setActiveToolStep] = useState<ToolStepKey>("preset");
|
||||
const hasBrandModelStep = Boolean(brandModelPanel) && modelSlotEnabled;
|
||||
const hasWorkflowStyleStep = Boolean(workflowStylePanel) || hasBrandModelStep;
|
||||
const hasBrandStoreStep = Boolean(brandStorePanel);
|
||||
const hasWorkflowStyleStep = Boolean(workflowStylePanel) || hasBrandModelStep || Boolean(presetPanel);
|
||||
const shouldShowDirectUploadInStyleStep =
|
||||
isDirectReferenceUploadEnabled && hasWorkflowStyleStep;
|
||||
const visibleWorkflowSectionKeys = useMemo(
|
||||
() => [
|
||||
...(hasBrandStoreStep ? ["brand"] : []),
|
||||
...(hasWorkflowStyleStep ? ["style"] : []),
|
||||
...(isPresetStepEnabled ? ["mode"] : []),
|
||||
...(hasMaterialStep ? ["materials"] : []),
|
||||
...(hasWorkflowStyleStep ? ["style"] : []),
|
||||
"generate",
|
||||
],
|
||||
[hasMaterialStep, hasWorkflowStyleStep, isPresetStepEnabled]
|
||||
[hasBrandStoreStep, hasMaterialStep, hasWorkflowStyleStep, isPresetStepEnabled]
|
||||
);
|
||||
const getWorkflowSectionBadge = useCallback(
|
||||
(key: "mode" | "materials" | "style" | "generate") => {
|
||||
(key: "brand" | "mode" | "materials" | "style" | "generate") => {
|
||||
const index = visibleWorkflowSectionKeys.indexOf(key);
|
||||
return String(index >= 0 ? index + 1 : visibleWorkflowSectionKeys.length + 1).padStart(
|
||||
2,
|
||||
@ -1681,13 +1716,15 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
},
|
||||
[visibleWorkflowSectionKeys]
|
||||
);
|
||||
const firstWorkflowSectionAnchor = isPresetStepEnabled
|
||||
? "agent-anchor-mode"
|
||||
: hasMaterialStep
|
||||
? "agent-anchor-materials"
|
||||
: hasWorkflowStyleStep
|
||||
? "agent-anchor-style"
|
||||
: "agent-anchor-generate";
|
||||
const firstWorkflowSectionAnchor = hasBrandStoreStep
|
||||
? "agent-anchor-brand"
|
||||
: hasWorkflowStyleStep
|
||||
? "agent-anchor-style"
|
||||
: isPresetStepEnabled
|
||||
? "agent-anchor-mode"
|
||||
: hasMaterialStep
|
||||
? "agent-anchor-materials"
|
||||
: "agent-anchor-generate";
|
||||
|
||||
const { messages, setMessages, sendMessage, status, stop } = useChat<ChatMessage>({
|
||||
id: chatId,
|
||||
@ -2383,6 +2420,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
const mergedSettingsStepDescription = hasMaterialStep
|
||||
? "搜索素材、补充要求、确认槽位并开始生成"
|
||||
: "补充要求并开始生成";
|
||||
const brandStepBadge = getWorkflowSectionBadge("brand");
|
||||
const modeStepBadge = getWorkflowSectionBadge("mode");
|
||||
const materialStepBadge = getWorkflowSectionBadge("materials");
|
||||
const styleStepBadge = getWorkflowSectionBadge("style");
|
||||
@ -2394,8 +2432,8 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
? [
|
||||
{
|
||||
key: "preset" as const,
|
||||
label: "生图模式",
|
||||
description: "先选择一个模式,再继续后续配置",
|
||||
label: "风格模板",
|
||||
description: "先确定通用风格化模板,再选择输出模式",
|
||||
icon: Wand2,
|
||||
disabled: false,
|
||||
completed: Boolean(selectedAgentPresetId),
|
||||
@ -2537,6 +2575,13 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
setActiveResultModeType(currentResultModeType);
|
||||
}, [currentResultModeType]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedAgentPresetId) {
|
||||
return;
|
||||
}
|
||||
setActiveBusinessModeKey(currentPresetBusinessModeKey);
|
||||
}, [currentPresetBusinessModeKey, selectedAgentPresetId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pendingGeneration) {
|
||||
return;
|
||||
@ -2824,12 +2869,20 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
}, [addNode, nodes.length, onOpenCanvasEditor, requestImageNodeAction, selectNodes]);
|
||||
|
||||
const handleSend = useCallback(() => {
|
||||
if (isSelectedBusinessModeUnavailable || activeBusinessModeKey === "buyer_show") {
|
||||
toast.error("当前生图模式暂未开放");
|
||||
return;
|
||||
}
|
||||
if (isBusinessModePresetMismatch) {
|
||||
toast.error("当前风格化模板与生图模式不匹配,请先切换对应模板");
|
||||
return;
|
||||
}
|
||||
if (isDetailReplacementPreset && isDetailAiGenerationMethod) {
|
||||
void handleRunAiDetailGeneration();
|
||||
return;
|
||||
}
|
||||
if (requiresPresetSelection && !selectedAgentPresetId) {
|
||||
toast.error("请先选择生图模式");
|
||||
toast.error("请先选择风格化模板");
|
||||
return;
|
||||
}
|
||||
if (isDetailReplacementPreset && detailReplacementReferenceCards.length === 0) {
|
||||
@ -2960,6 +3013,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
}
|
||||
}, [
|
||||
attachments,
|
||||
activeBusinessModeKey,
|
||||
detailReplacementReferenceCards.length,
|
||||
handleRunAiDetailGeneration,
|
||||
currentResultModeType,
|
||||
@ -2968,6 +3022,8 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
input,
|
||||
isDetailAiGenerationMethod,
|
||||
isDetailReplacementPreset,
|
||||
isBusinessModePresetMismatch,
|
||||
isSelectedBusinessModeUnavailable,
|
||||
isSkuReplacementPreset,
|
||||
modelsData?.defaultModelId,
|
||||
requiredProductUploadCount,
|
||||
@ -3147,7 +3203,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
const nextStep = toolSteps.find((step) => step.key === nextKey);
|
||||
if (!nextStep) return;
|
||||
if (nextStep.disabled) {
|
||||
toast.error(requiresPresetSelection ? "请先选择生图模式" : "当前步骤暂不可用");
|
||||
toast.error(requiresPresetSelection ? "请先选择风格化模板" : "当前步骤暂不可用");
|
||||
return;
|
||||
}
|
||||
setActiveToolStep(nextKey);
|
||||
@ -3483,7 +3539,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
);
|
||||
async function handleRunAiDetailGeneration() {
|
||||
if (!selectedAgentPresetId) {
|
||||
toast.error("请先选择生图模式");
|
||||
toast.error("请先选择风格化模板");
|
||||
return;
|
||||
}
|
||||
if (detailGenerationMode !== "classic") {
|
||||
@ -4330,7 +4386,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
</>
|
||||
) : (
|
||||
<p className="text-xs leading-6 text-white/45">
|
||||
先选择一个生图模式,系统会按模板配置展示对应的产品图和模特图槽位。
|
||||
先选择风格化模板,系统会按模板配置展示对应的产品图和模特图槽位。
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@ -4440,7 +4496,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
) : (
|
||||
<div className="mt-4 flex min-h-[132px] flex-col items-center justify-center rounded-[24px] border border-dashed border-white/12 bg-black/20 px-4 text-center text-sm text-white/45">
|
||||
<ImagePlus className="mb-2 h-5 w-5 opacity-60" />
|
||||
先选择一个生图模式,系统会按模板配置展示对应的产品图和模特图槽位。
|
||||
先选择风格化模板,系统会按模板配置展示对应的产品图和模特图槽位。
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@ -4470,7 +4526,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
>
|
||||
<AgentWorkflowShowcaseBanner
|
||||
config={showcaseConfig}
|
||||
actionLabel={isPresetStepEnabled ? "开始选择模式" : "开始配置"}
|
||||
actionLabel="开始配置"
|
||||
secondaryActionLabel={allGeneratedImages.length > 0 ? "查看结果预览" : undefined}
|
||||
onActionClick={() => scrollToSection(firstWorkflowSectionAnchor)}
|
||||
onSecondaryActionClick={() => scrollToSection("agent-anchor-results")}
|
||||
@ -4485,78 +4541,203 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
className="rounded-[28px] border border-white/12 bg-white/[0.04] p-5 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]"
|
||||
>
|
||||
<div className="space-y-6">
|
||||
{isPresetStepEnabled ? (
|
||||
<section id="agent-anchor-mode" className="scroll-mt-24 rounded-[24px] border border-white/10 bg-black/20 p-4">
|
||||
{hasBrandStoreStep ? (
|
||||
<section id="agent-anchor-brand" className="scroll-mt-24 rounded-[24px] border border-white/10 bg-black/20 p-4">
|
||||
<div className="mb-4 flex flex-col gap-4 border-b border-white/10 pb-4 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="rounded-full bg-emerald-500/18 px-2.5 py-1 text-[11px] font-semibold text-emerald-200">
|
||||
{modeStepBadge}
|
||||
{brandStepBadge}
|
||||
</span>
|
||||
<span className="text-sm font-semibold text-white">选择生图模式</span>
|
||||
<span className="text-sm font-semibold text-white">选择品牌上下文</span>
|
||||
</div>
|
||||
<p className="text-sm text-white/45">
|
||||
先确定本次使用的模式,并完成品牌和店铺选择,后续的商品图、品牌模特、参考图和结果展示都会随之联动。
|
||||
先确定品牌、店铺和类目,让后续风格化提示词、素材搜索和 PSD 模板都能拿到同一套业务上下文。
|
||||
</p>
|
||||
{isDetailReplacementPreset || isSkuReplacementPreset ? (
|
||||
<p className="text-xs text-amber-300/80">
|
||||
{isDetailReplacementPreset
|
||||
? isDetailAiGenerationMethod
|
||||
? "当前模板已切换到 AI详情生成,系统会直接复用智能规划详情链路生成整套详情图。"
|
||||
: "当前模板属于参考图替换模式,结果区会按参考图顺序逐屏展示。"
|
||||
: "当前模板属于 SKU 替换模式,结果区会按参考图顺序一对一展示。"}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
{selectedAgentPreset ? (
|
||||
<div className="flex items-center gap-3 rounded-[20px] border border-white/10 bg-black/20 px-3 py-2">
|
||||
{selectedAgentPreset.avatarUrl ? (
|
||||
<img
|
||||
src={selectedAgentPreset.avatarUrl}
|
||||
alt={selectedAgentPreset.name}
|
||||
className="h-10 w-10 rounded-xl object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-emerald-500/18">
|
||||
<Wand2 className="h-5 w-5 text-emerald-300" />
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<div className="text-sm font-medium text-white">{selectedAgentPreset.name}</div>
|
||||
<div className="text-xs text-white/40">
|
||||
{selectedAgentPreset.type === "image" ? "生图模式" : "视频模式"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{selectedAgentPreset && onClearPreset ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClearPreset}
|
||||
className="rounded-full border border-white/10 bg-white/[0.03] px-3 py-2 text-xs text-emerald-300 transition-colors hover:border-emerald-300/35 hover:text-emerald-200"
|
||||
>
|
||||
更换生图模式
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
{brandStorePanel}
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{presetPanel ? (
|
||||
<div className="agent-dark-scrollbar max-h-[760px] overflow-y-auto pr-1">
|
||||
{presetPanel}
|
||||
{hasWorkflowStyleStep ? (
|
||||
<section id="agent-anchor-style" className="scroll-mt-24 rounded-[24px] border border-white/10 bg-black/20 p-4">
|
||||
<div className="mb-4 space-y-1 border-b border-white/10 pb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="rounded-full bg-emerald-500/18 px-2.5 py-1 text-[11px] font-semibold text-emerald-200">
|
||||
{styleStepBadge}
|
||||
</span>
|
||||
<span className="text-sm font-semibold text-white">风格化提示词配置</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex min-h-[220px] items-center justify-center rounded-[24px] border border-dashed border-white/12 bg-black/20 text-sm text-white/45">
|
||||
暂无可用的生图模式配置。
|
||||
</div>
|
||||
)}
|
||||
<p className="text-sm text-white/45">
|
||||
先完成风格、空间、光线、机位、人群等通用提示词配置;这些配置会被后面的不同生图模式共用。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{brandStorePanel ? (
|
||||
<div className="mt-4 border-t border-white/10 pt-4">
|
||||
{brandStorePanel}
|
||||
<div className="space-y-4">
|
||||
{shouldShowDirectUploadInStyleStep ? attachmentSlotPanel : null}
|
||||
{workflowStylePanel}
|
||||
{isPresetStepEnabled ? (
|
||||
<div className="rounded-[24px] border border-white/10 bg-black/20 p-4">
|
||||
<div className="mb-4 flex flex-col gap-3 border-b border-white/10 pb-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-white">风格化模板</h3>
|
||||
<p className="mt-1 text-xs text-white/42">
|
||||
这里沿用现有模板库作为生成执行配置;后续固定业务模式会接管前台入口展示。
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
{selectedAgentPreset ? (
|
||||
<div className="flex items-center gap-3 rounded-[18px] border border-white/10 bg-white/[0.03] px-3 py-2">
|
||||
{selectedAgentPreset.avatarUrl ? (
|
||||
<img
|
||||
src={selectedAgentPreset.avatarUrl}
|
||||
alt={selectedAgentPreset.name}
|
||||
className="h-9 w-9 rounded-xl object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-9 w-9 items-center justify-center rounded-xl bg-emerald-500/18">
|
||||
<Wand2 className="h-4 w-4 text-emerald-300" />
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<div className="text-xs font-medium text-white">{selectedAgentPreset.name}</div>
|
||||
<div className="text-[11px] text-white/40">
|
||||
{currentPresetBusinessModeKey === "detail"
|
||||
? "详情执行模板"
|
||||
: "主图/SKU执行模板"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{selectedAgentPreset && onClearPreset ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClearPreset}
|
||||
className="rounded-full border border-white/10 bg-white/[0.03] px-3 py-2 text-xs text-emerald-300 transition-colors hover:border-emerald-300/35 hover:text-emerald-200"
|
||||
>
|
||||
更换模板
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{presetPanel ? (
|
||||
<div className="agent-dark-scrollbar max-h-[520px] overflow-y-auto pr-1">
|
||||
{presetPanel}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex min-h-[180px] items-center justify-center rounded-[24px] border border-dashed border-white/12 bg-black/20 text-sm text-white/45">
|
||||
暂无可用的风格化模板配置。
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
{hasBrandModelStep ? brandModelPanel : null}
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{isPresetStepEnabled ? (
|
||||
<section id="agent-anchor-mode" className="scroll-mt-24 rounded-[24px] border border-white/10 bg-black/20 p-4">
|
||||
<div className="mb-4 space-y-1 border-b border-white/10 pb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="rounded-full bg-emerald-500/18 px-2.5 py-1 text-[11px] font-semibold text-emerald-200">
|
||||
{modeStepBadge}
|
||||
</span>
|
||||
<span className="text-sm font-semibold text-white">选择生图模式</span>
|
||||
</div>
|
||||
) : null}
|
||||
<p className="text-sm text-white/45">
|
||||
生图模式只决定输出链路和素材规则,前面配置好的风格化提示词会在不同模式中共用。
|
||||
</p>
|
||||
{isBusinessModePresetMismatch ? (
|
||||
<p className="text-xs text-amber-300/80">
|
||||
当前风格化模板与所选生图模式不匹配,请在上方模板库切换到对应类型后再生成。
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 lg:grid-cols-3">
|
||||
{businessModeCards.map((mode) => {
|
||||
const isActive = activeBusinessModeKey === mode.key;
|
||||
const Icon =
|
||||
mode.key === "detail"
|
||||
? Settings2
|
||||
: mode.key === "buyer_show"
|
||||
? UserRound
|
||||
: ImagePlus;
|
||||
const modeStatusText =
|
||||
mode.key === "buyer_show" && mode.enabled === false
|
||||
? "预留中"
|
||||
: mode.enabled === false
|
||||
? "已关闭"
|
||||
: mode.key === currentPresetBusinessModeKey
|
||||
? "当前模板匹配"
|
||||
: "可选择";
|
||||
|
||||
return (
|
||||
<button
|
||||
key={mode.key}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setActiveBusinessModeKey(mode.key);
|
||||
if (mode.enabled === false || mode.key === "buyer_show") {
|
||||
toast.info("当前生图模式已预留,暂未开放生成");
|
||||
}
|
||||
}}
|
||||
className={cn(
|
||||
"group min-h-[148px] rounded-[22px] border p-4 text-left transition-all",
|
||||
isActive
|
||||
? "border-emerald-300/55 bg-emerald-500/[0.12] shadow-[0_0_0_1px_rgba(16,185,129,0.18)]"
|
||||
: "border-white/10 bg-white/[0.03] hover:border-white/20 hover:bg-white/[0.06]",
|
||||
mode.enabled === false ? "opacity-70" : ""
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-2xl border border-white/10 bg-black/25">
|
||||
{mode.customSvgUrl ? (
|
||||
<img
|
||||
src={mode.customSvgUrl}
|
||||
alt={mode.title}
|
||||
className="h-7 w-7 object-contain"
|
||||
/>
|
||||
) : (
|
||||
<Icon className="h-6 w-6 text-emerald-200/80" />
|
||||
)}
|
||||
</div>
|
||||
<span
|
||||
className={cn(
|
||||
"rounded-full border px-2 py-1 text-[11px]",
|
||||
isActive
|
||||
? "border-emerald-300/35 bg-emerald-400/10 text-emerald-100"
|
||||
: "border-white/10 bg-black/20 text-white/45"
|
||||
)}
|
||||
>
|
||||
{modeStatusText}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-4 text-sm font-semibold text-white">{mode.title}</div>
|
||||
<p className="mt-2 line-clamp-3 text-xs leading-5 text-white/46">
|
||||
{mode.description || "当前模式暂无说明。"}
|
||||
</p>
|
||||
{mode.key === "detail" ? (
|
||||
<div className="mt-3 flex flex-wrap gap-2 text-[11px]">
|
||||
{mode.referenceReplaceEnabled !== false ? (
|
||||
<span className="rounded-full border border-white/10 bg-black/20 px-2 py-1 text-white/55">
|
||||
详情图替换
|
||||
</span>
|
||||
) : null}
|
||||
{mode.aiDetailGenerateEnabled !== false ? (
|
||||
<span className="rounded-full border border-white/10 bg-black/20 px-2 py-1 text-white/55">
|
||||
详情图生成
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
@ -4570,35 +4751,13 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
<span className="text-sm font-semibold text-white">选择 SPU / SKU</span>
|
||||
</div>
|
||||
<p className="text-sm text-white/45">
|
||||
先搜索商品白底图与搭配图,再继续完成风格步骤和生成参数配置。
|
||||
根据当前生图模式选择商品白底图、SKU 图、详情参考图或手动上传素材,素材确认后再进入结果生成。
|
||||
</p>
|
||||
</div>
|
||||
{materialSelectionPanel}
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{hasWorkflowStyleStep ? (
|
||||
<section id="agent-anchor-style" className="scroll-mt-24 rounded-[24px] border border-white/10 bg-black/20 p-4">
|
||||
<div className="mb-4 space-y-1 border-b border-white/10 pb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="rounded-full bg-emerald-500/18 px-2.5 py-1 text-[11px] font-semibold text-emerald-200">
|
||||
{styleStepBadge}
|
||||
</span>
|
||||
<span className="text-sm font-semibold text-white">风格步骤卡片</span>
|
||||
</div>
|
||||
<p className="text-sm text-white/45">
|
||||
先完成风格步骤卡片,再继续选择品牌模特,方便后续做人群联动,不再跳转第二个页面。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{shouldShowDirectUploadInStyleStep ? attachmentSlotPanel : null}
|
||||
{workflowStylePanel}
|
||||
{hasBrandModelStep ? brandModelPanel : null}
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
<section id="agent-anchor-generate" className="scroll-mt-24 rounded-[24px] border border-white/10 bg-black/20 p-4">
|
||||
<div className="mb-4 space-y-1 border-b border-white/10 pb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
@ -6190,12 +6349,12 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
<p className="text-base font-medium text-white/60">
|
||||
{!requiresPresetSelection || selectedAgentPresetId
|
||||
? `${getResultModeLabel(activeResultModeType)}结果暂为空`
|
||||
: "请选择生图模式"}
|
||||
: "请选择风格化模板"}
|
||||
</p>
|
||||
<p className="mt-2 text-sm text-white/30">
|
||||
{!requiresPresetSelection || selectedAgentPresetId
|
||||
? "选择模型并开始生成后,结果会保留在当前项目的对应分类中。"
|
||||
: "请先从顶部流程线选择一个生图模式"}
|
||||
: "请先完成上方风格化模板配置"}
|
||||
</p>
|
||||
{isPsdTemplateEnabled ? (
|
||||
<>
|
||||
|
||||
@ -512,7 +512,7 @@ export function AgentPresetPanelDarkV2({
|
||||
<div className="relative flex-1 max-w-md">
|
||||
<Search className="absolute left-4 top-1/2 h-4 w-4 -translate-y-1/2 text-white/40" />
|
||||
<Input
|
||||
placeholder="搜索生图模式..."
|
||||
placeholder="搜索风格化模板..."
|
||||
value={keyword}
|
||||
onChange={(e) => setKeyword(e.target.value)}
|
||||
className={cn(
|
||||
@ -639,7 +639,7 @@ export function AgentPresetPanelDarkV2({
|
||||
setShowCreateDialog(true);
|
||||
}}
|
||||
initialPreset={editingPreset}
|
||||
entityLabel="生图模式"
|
||||
entityLabel={embeddedEntityLabel}
|
||||
theme="dark"
|
||||
onSaved={async (preset) => {
|
||||
await mutate();
|
||||
|
||||
@ -975,7 +975,7 @@ export function AgentWorkflowMaterialPicker({
|
||||
{disabled ? (
|
||||
<>
|
||||
<ImageIcon className="mb-2 h-5 w-5 opacity-60" />
|
||||
先选择生图模式,再搜索并选择素材图。
|
||||
先选择风格化模板,再搜索并选择素材图。
|
||||
</>
|
||||
) : isLoading ? (
|
||||
<>
|
||||
|
||||
@ -2606,7 +2606,7 @@ export function CanvasChatPanel({
|
||||
tagSpan.contentEditable = 'false';
|
||||
tagSpan.setAttribute('data-attachment-index', String(index));
|
||||
tagSpan.setAttribute('data-attachment-url', url);
|
||||
tagSpan.className = 'inline-flex items-center gap-1 bg-muted/60 hover:bg-muted rounded-full pl-1 pr-2 py-0.5 mx-0.5 align-middle cursor-default select-none';
|
||||
tagSpan.className = 'inline-flex items-center gap-1 bg-muted/60 hover:bg-muted dark:bg-white/10 dark:hover:bg-white/15 dark:text-zinc-100 rounded-full pl-1 pr-2 py-0.5 mx-0.5 align-middle cursor-default select-none';
|
||||
const shortName = name.length > 10 ? name.substring(0, 8) + '...' : name;
|
||||
tagSpan.innerHTML = `
|
||||
<img src="${url}" alt="" class="h-5 w-5 object-cover rounded-full border border-border/50" />
|
||||
@ -2661,7 +2661,7 @@ export function CanvasChatPanel({
|
||||
tagSpan.setAttribute('data-object-name', objectName);
|
||||
tagSpan.setAttribute('data-object-position', objectPosition);
|
||||
// 样式与普通图片标签一致
|
||||
tagSpan.className = 'inline-flex items-center gap-1 bg-muted/60 hover:bg-muted rounded-full pl-1 pr-2 py-0.5 mx-0.5 align-middle cursor-default select-none';
|
||||
tagSpan.className = 'inline-flex items-center gap-1 bg-muted/60 hover:bg-muted dark:bg-white/10 dark:hover:bg-white/15 dark:text-zinc-100 rounded-full pl-1 pr-2 py-0.5 mx-0.5 align-middle cursor-default select-none';
|
||||
// 使用图片缩略图,不使用 emoji 或图标
|
||||
tagSpan.innerHTML = `
|
||||
<img src="${url}" alt="" class="h-5 w-5 object-cover rounded-full border border-border/50" />
|
||||
@ -9040,7 +9040,7 @@ export function CanvasChatPanel({
|
||||
"h-5 w-5 overflow-hidden rounded-full",
|
||||
isVideoWorkspace
|
||||
? "border border-border/60 bg-background"
|
||||
: "border border-[#d4d9df] bg-white dark:border-white/10 dark:bg-white/10"
|
||||
: "border border-[#d4d9df] bg-white dark:border-white/20 dark:bg-white"
|
||||
)}
|
||||
>
|
||||
{model.avatarUrl ? (
|
||||
@ -9182,7 +9182,7 @@ export function CanvasChatPanel({
|
||||
"inline-flex h-5 w-5 items-center justify-center rounded-md",
|
||||
isVideoWorkspace
|
||||
? "border border-border/60 bg-background/80 text-foreground/70"
|
||||
: "bg-[#eef2f7] text-[#6b7280]"
|
||||
: "bg-[#eef2f7] text-[#6b7280] dark:bg-white/12 dark:text-zinc-100 dark:ring-1 dark:ring-white/10"
|
||||
)}
|
||||
>
|
||||
<MessageCircle className="h-3.5 w-3.5" />
|
||||
@ -9190,7 +9190,7 @@ export function CanvasChatPanel({
|
||||
<h2
|
||||
className={cn(
|
||||
"min-w-0 flex-1 truncate text-[14px] font-semibold leading-tight tracking-tight",
|
||||
isVideoWorkspace ? "text-foreground" : "text-[#111827]"
|
||||
isVideoWorkspace ? "text-foreground" : "text-[#111827] dark:text-zinc-100"
|
||||
)}
|
||||
title={displayCanvasName}
|
||||
>
|
||||
@ -9206,7 +9206,7 @@ export function CanvasChatPanel({
|
||||
"h-8 w-8 rounded-full",
|
||||
isVideoWorkspace
|
||||
? "border border-border/60 bg-background/72 hover:bg-background"
|
||||
: "hover:bg-muted"
|
||||
: "text-zinc-600 hover:bg-muted dark:text-zinc-100 dark:hover:bg-white/10"
|
||||
)}
|
||||
onClick={createNewChatSession}
|
||||
title="新建聊天窗口"
|
||||
@ -9221,8 +9221,8 @@ export function CanvasChatPanel({
|
||||
"h-8 w-8 rounded-full",
|
||||
isVideoWorkspace
|
||||
? "border border-border/60 bg-background/72 hover:bg-background"
|
||||
: "hover:bg-muted",
|
||||
isSessionMenuOpen && (isVideoWorkspace ? "bg-background" : "bg-muted")
|
||||
: "text-zinc-600 hover:bg-muted dark:text-zinc-100 dark:hover:bg-white/10",
|
||||
isSessionMenuOpen && (isVideoWorkspace ? "bg-background" : "bg-muted dark:bg-white/10")
|
||||
)}
|
||||
onClick={() => setIsSessionMenuOpen((open) => !open)}
|
||||
title="聊天窗口记录"
|
||||
@ -9235,7 +9235,7 @@ export function CanvasChatPanel({
|
||||
"absolute right-0 top-10 w-64 p-2 shadow-xl",
|
||||
isVideoWorkspace
|
||||
? "rounded-2xl border border-border/60 bg-background/95 backdrop-blur-xl"
|
||||
: "rounded-xl border bg-white dark:bg-zinc-950"
|
||||
: "rounded-xl border bg-white dark:border-white/10 dark:bg-zinc-950 dark:text-zinc-100"
|
||||
)}
|
||||
>
|
||||
<div className="px-2 py-1 text-xs text-muted-foreground">聊天窗口记录</div>
|
||||
@ -9261,7 +9261,7 @@ export function CanvasChatPanel({
|
||||
: "bg-primary/10 text-primary"
|
||||
: isVideoWorkspace
|
||||
? "hover:bg-background"
|
||||
: "hover:bg-muted"
|
||||
: "hover:bg-muted dark:hover:bg-white/10"
|
||||
)}
|
||||
onClick={() => switchToSession(session.id)}
|
||||
>
|
||||
@ -9286,7 +9286,7 @@ export function CanvasChatPanel({
|
||||
"h-8 w-8 rounded-full",
|
||||
isVideoWorkspace
|
||||
? "border border-border/60 bg-background/72 hover:bg-background"
|
||||
: "hover:bg-muted"
|
||||
: "text-zinc-600 hover:bg-muted dark:text-zinc-100 dark:hover:bg-white/10"
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
@ -9307,7 +9307,7 @@ export function CanvasChatPanel({
|
||||
"h-8 w-8 rounded-full",
|
||||
isVideoWorkspace
|
||||
? "border border-border/60 bg-background/72 hover:bg-background"
|
||||
: "hover:bg-muted"
|
||||
: "text-zinc-600 hover:bg-muted dark:text-zinc-100 dark:hover:bg-white/10"
|
||||
)}
|
||||
onClick={onClose}
|
||||
disabled={!onClose}
|
||||
@ -10100,7 +10100,7 @@ export function CanvasChatPanel({
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className="relative flex flex-col bg-card rounded-3xl border transition-all duration-200 focus-within:border-primary/50 focus-within:bg-background"
|
||||
className="relative flex flex-col rounded-3xl border bg-card transition-all duration-200 focus-within:border-primary/50 focus-within:bg-background dark:border-white/12 dark:bg-zinc-950 dark:focus-within:border-white/25 dark:focus-within:bg-zinc-950"
|
||||
data-canvas-chat-input-anchor="true"
|
||||
>
|
||||
{isWorkspacePresentation && !isDetailGenerationMode ? (
|
||||
@ -11191,7 +11191,7 @@ export function CanvasChatPanel({
|
||||
suppressContentEditableWarning
|
||||
className={cn(
|
||||
"min-h-[40px] max-h-[120px] overflow-y-auto text-sm outline-none empty:before:content-[attr(data-placeholder)]",
|
||||
isVideoWorkspace ? "empty:before:text-muted-foreground/70" : "empty:before:text-[#9aa1ad]"
|
||||
isVideoWorkspace ? "empty:before:text-muted-foreground/70" : "text-[#111827] empty:before:text-[#9aa1ad] dark:text-zinc-100 dark:empty:before:text-zinc-500"
|
||||
)}
|
||||
data-placeholder={composerPlaceholder}
|
||||
onInput={(e) => {
|
||||
@ -11630,7 +11630,7 @@ export function CanvasChatPanel({
|
||||
"h-8 w-8 rounded-full text-muted-foreground transition-colors hover:text-foreground mr-1",
|
||||
isVideoWorkspace
|
||||
? "border border-border/60 bg-background hover:bg-background/90"
|
||||
: "hover:bg-muted"
|
||||
: "hover:bg-muted dark:bg-white/8 dark:text-zinc-200 dark:hover:bg-white/12 dark:hover:text-white"
|
||||
)}
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
disabled={isSubmitting}
|
||||
@ -11658,7 +11658,7 @@ export function CanvasChatPanel({
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="min-w-0 shrink pl-1 border-l border-border/40 dark:border-border/60 ml-1">
|
||||
<div className="min-w-0 shrink pl-1 border-l border-border/40 dark:border-white/10 ml-1">
|
||||
<ImageParamsBar
|
||||
modelName={activeComposerMode === "agent" ? agentDisplayName : currentModel?.displayName}
|
||||
initialParams={imageParams}
|
||||
@ -11673,7 +11673,7 @@ export function CanvasChatPanel({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 items-center gap-2 pl-2 border-l border-border/40 dark:border-border/60">
|
||||
<div className="flex shrink-0 items-center gap-2 pl-2 border-l border-border/40 dark:border-white/10">
|
||||
{!isWorkspacePresentation && activeComposerMode === "image" ? (
|
||||
<Button
|
||||
variant="ghost"
|
||||
@ -11687,7 +11687,7 @@ export function CanvasChatPanel({
|
||||
: "text-muted-foreground hover:bg-muted/70"
|
||||
: deepThinkingEnabled
|
||||
? "bg-[#eef4ff] text-[#3b5bcc] hover:bg-[#e5efff] dark:bg-blue-500/15 dark:text-blue-200 dark:hover:bg-blue-500/20"
|
||||
: "text-[#6b7280] hover:bg-[#f4f6f8] dark:text-zinc-400 dark:hover:bg-white/8"
|
||||
: "text-[#6b7280] hover:bg-[#f4f6f8] dark:bg-white/8 dark:text-zinc-200 dark:hover:bg-white/12 dark:hover:text-white"
|
||||
)}
|
||||
onClick={() => setDeepThinkingEnabled((prev) => !prev)}
|
||||
disabled={isSubmitting}
|
||||
@ -11726,14 +11726,14 @@ export function CanvasChatPanel({
|
||||
"h-8 w-8 rounded-full p-0 justify-center [&>svg]:hidden",
|
||||
isVideoWorkspace
|
||||
? "border border-border/60 bg-background"
|
||||
: "border border-[#cfd4dc] bg-white dark:border-white/10 dark:bg-white/8"
|
||||
: "border border-[#cfd4dc] bg-white dark:border-white/20 dark:bg-white dark:text-zinc-950 dark:hover:bg-zinc-100"
|
||||
)}
|
||||
title={getToolbarItem("chatModelSelector").label}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
"flex h-6 w-6 items-center justify-center overflow-hidden rounded-full",
|
||||
isVideoWorkspace ? "bg-background" : "bg-white dark:bg-transparent"
|
||||
isVideoWorkspace ? "bg-background" : "bg-white dark:bg-white"
|
||||
)}
|
||||
>
|
||||
{getToolbarItem("chatModelSelector").iconUrl ? (
|
||||
@ -11759,7 +11759,7 @@ export function CanvasChatPanel({
|
||||
<span
|
||||
className={cn(
|
||||
"flex h-full w-full items-center justify-center text-[10px] font-medium",
|
||||
isVideoWorkspace ? "text-foreground/70" : "text-[#4b5563]"
|
||||
isVideoWorkspace ? "text-foreground/70" : "text-[#4b5563] dark:text-zinc-950"
|
||||
)}
|
||||
>
|
||||
{(selectDisplayModel?.displayName || "A").slice(0, 1)}
|
||||
@ -11827,9 +11827,16 @@ export function CanvasChatPanel({
|
||||
) : isDetailGenerationMode ? (
|
||||
<>
|
||||
<CanvasToolbarIcon
|
||||
className="h-4 w-4"
|
||||
className={cn(
|
||||
"h-4 w-4",
|
||||
canSend &&
|
||||
(isVideoWorkspace || isDetailGenerationMode
|
||||
? "opacity-95"
|
||||
: "opacity-95 invert dark:invert-0")
|
||||
)}
|
||||
fallback={Sparkles}
|
||||
item={getToolbarItem("chatStartGeneration")}
|
||||
active={canSend && (isVideoWorkspace || isDetailGenerationMode)}
|
||||
/>
|
||||
{isWorkspacePresentation ? (
|
||||
<span className="ml-2">
|
||||
@ -11840,13 +11847,20 @@ export function CanvasChatPanel({
|
||||
) : (
|
||||
<>
|
||||
<CanvasToolbarIcon
|
||||
className="h-4 w-4"
|
||||
className={cn(
|
||||
"h-4 w-4",
|
||||
canSend &&
|
||||
(isVideoWorkspace || isDetailGenerationMode
|
||||
? "opacity-95"
|
||||
: "opacity-95 invert dark:invert-0")
|
||||
)}
|
||||
fallback={ArrowUp}
|
||||
item={getToolbarItem(
|
||||
isWorkspacePresentation
|
||||
? "chatStartGeneration"
|
||||
: "chatSend"
|
||||
)}
|
||||
active={canSend && (isVideoWorkspace || isDetailGenerationMode)}
|
||||
/>
|
||||
{isWorkspacePresentation ? (
|
||||
<span className="ml-2">
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
import type {
|
||||
AgentWorkflowBusinessModeConfig,
|
||||
AgentWorkflowBusinessModeIconKey,
|
||||
AgentWorkflowBusinessModeKey,
|
||||
AgentWorkflowBusinessModeSettings,
|
||||
AgentWorkflowBrandStoreEntry,
|
||||
AgentWorkflowCategoryLabels,
|
||||
AgentWorkflowConstraintSettings,
|
||||
@ -34,6 +38,39 @@ export const defaultAgentWorkflowPageFeatureSettings: AgentWorkflowPageFeatureSe
|
||||
psdTemplateEnabled: true,
|
||||
batchProcessingEnabled: true,
|
||||
};
|
||||
export const defaultAgentWorkflowBusinessModeSettings: AgentWorkflowBusinessModeSettings = {
|
||||
modes: [
|
||||
{
|
||||
key: "main_sku",
|
||||
title: "主图/SKU图模式",
|
||||
description: "用于主图、SKU 图和场景化商品图输出,复用当前风格化提示词配置。",
|
||||
enabled: true,
|
||||
iconKey: "main-sku",
|
||||
customSvgUrl: "",
|
||||
sortOrder: 0,
|
||||
},
|
||||
{
|
||||
key: "detail",
|
||||
title: "详情模式",
|
||||
description: "包含详情图替换和详情图生成,适合做商品详情页内容。",
|
||||
enabled: true,
|
||||
iconKey: "detail",
|
||||
customSvgUrl: "",
|
||||
sortOrder: 1,
|
||||
referenceReplaceEnabled: true,
|
||||
aiDetailGenerateEnabled: true,
|
||||
},
|
||||
{
|
||||
key: "buyer_show",
|
||||
title: "买家秀模式",
|
||||
description: "买家秀业务模块已预留,后续可在这里接入专用素材和提示词规则。",
|
||||
enabled: false,
|
||||
iconKey: "buyer-show",
|
||||
customSvgUrl: "",
|
||||
sortOrder: 2,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export interface AgentWorkflowBrandCategoryOption {
|
||||
value: string;
|
||||
@ -189,6 +226,7 @@ export const defaultAgentWorkflowUiConfig: AgentWorkflowUiConfig = {
|
||||
},
|
||||
constraintSettings: defaultAgentWorkflowConstraintSettings,
|
||||
pageFeatureSettings: defaultAgentWorkflowPageFeatureSettings,
|
||||
businessModeSettings: defaultAgentWorkflowBusinessModeSettings,
|
||||
steps: defaultSteps.map((step, index) =>
|
||||
cloneStep({
|
||||
...step,
|
||||
@ -278,6 +316,97 @@ export function normalizeAgentWorkflowPageFeatureSettings(
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeAgentWorkflowBusinessModeKey(
|
||||
value: unknown
|
||||
): AgentWorkflowBusinessModeKey | null {
|
||||
return value === "main_sku" || value === "detail" || value === "buyer_show"
|
||||
? value
|
||||
: null;
|
||||
}
|
||||
|
||||
function normalizeAgentWorkflowBusinessModeIconKey(
|
||||
value: unknown,
|
||||
fallback: AgentWorkflowBusinessModeIconKey
|
||||
): AgentWorkflowBusinessModeIconKey {
|
||||
return value === "main-sku" || value === "detail" || value === "buyer-show"
|
||||
? value
|
||||
: fallback;
|
||||
}
|
||||
|
||||
function normalizeAgentWorkflowBusinessModeConfig(
|
||||
value: unknown,
|
||||
fallback: AgentWorkflowBusinessModeConfig
|
||||
): AgentWorkflowBusinessModeConfig {
|
||||
const record =
|
||||
value && typeof value === "object" && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: {};
|
||||
const title = normalizeText(record.title ?? record.label ?? record.name, 40);
|
||||
const description = normalizeText(record.description, 220);
|
||||
|
||||
const normalized: AgentWorkflowBusinessModeConfig = {
|
||||
key: fallback.key,
|
||||
title: title || fallback.title,
|
||||
description: description || fallback.description || "",
|
||||
enabled: normalizeBoolean(record.enabled, fallback.enabled),
|
||||
iconKey: normalizeAgentWorkflowBusinessModeIconKey(record.iconKey, fallback.iconKey),
|
||||
customSvgUrl: normalizeText(record.customSvgUrl ?? record.iconSvgUrl, 2000),
|
||||
sortOrder: normalizeNumber(record.sortOrder, fallback.sortOrder, 99),
|
||||
};
|
||||
|
||||
if (
|
||||
fallback.referenceReplaceEnabled !== undefined ||
|
||||
record.referenceReplaceEnabled !== undefined
|
||||
) {
|
||||
normalized.referenceReplaceEnabled = normalizeBoolean(
|
||||
record.referenceReplaceEnabled,
|
||||
fallback.referenceReplaceEnabled !== false
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
fallback.aiDetailGenerateEnabled !== undefined ||
|
||||
record.aiDetailGenerateEnabled !== undefined
|
||||
) {
|
||||
normalized.aiDetailGenerateEnabled = normalizeBoolean(
|
||||
record.aiDetailGenerateEnabled,
|
||||
fallback.aiDetailGenerateEnabled !== false
|
||||
);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
export function normalizeAgentWorkflowBusinessModeSettings(
|
||||
value: unknown
|
||||
): AgentWorkflowBusinessModeSettings {
|
||||
const record =
|
||||
value && typeof value === "object" && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: {};
|
||||
const rawModes = Array.isArray(record.modes) ? record.modes : [];
|
||||
const modeByKey = new Map<AgentWorkflowBusinessModeKey, unknown>();
|
||||
|
||||
rawModes.forEach((item) => {
|
||||
const itemRecord =
|
||||
item && typeof item === "object" && !Array.isArray(item)
|
||||
? (item as Record<string, unknown>)
|
||||
: {};
|
||||
const key = normalizeAgentWorkflowBusinessModeKey(itemRecord.key);
|
||||
if (key && !modeByKey.has(key)) {
|
||||
modeByKey.set(key, item);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
modes: defaultAgentWorkflowBusinessModeSettings.modes
|
||||
.map((fallback) =>
|
||||
normalizeAgentWorkflowBusinessModeConfig(modeByKey.get(fallback.key), fallback)
|
||||
)
|
||||
.sort((left, right) => left.sortOrder - right.sortOrder),
|
||||
};
|
||||
}
|
||||
|
||||
export function flattenAgentWorkflowSteps(
|
||||
steps: AgentWorkflowStepUiConfig[],
|
||||
depth = 1,
|
||||
@ -1149,6 +1278,9 @@ export function normalizeAgentWorkflowUiConfig(value: unknown): AgentWorkflowUiC
|
||||
batchProcessingEnabled: raw.batchProcessingEnabled,
|
||||
}
|
||||
),
|
||||
businessModeSettings: normalizeAgentWorkflowBusinessModeSettings(
|
||||
raw.businessModeSettings ?? raw.fixedModeSettings ?? raw.businessModes
|
||||
),
|
||||
steps: sanitizedSteps,
|
||||
};
|
||||
}
|
||||
@ -1175,6 +1307,12 @@ export function getAgentWorkflowPageFeatureSettings(
|
||||
return normalizeAgentWorkflowPageFeatureSettings(config?.pageFeatureSettings);
|
||||
}
|
||||
|
||||
export function getAgentWorkflowBusinessModeSettings(
|
||||
config?: AgentWorkflowUiConfig | null
|
||||
) {
|
||||
return normalizeAgentWorkflowBusinessModeSettings(config?.businessModeSettings);
|
||||
}
|
||||
|
||||
export function getAgentWorkflowCategoryOptionsByBrand(params: {
|
||||
config?: AgentWorkflowUiConfig | null;
|
||||
brand?: string | null;
|
||||
|
||||
@ -116,6 +116,28 @@ export interface AgentWorkflowPageFeatureSettings {
|
||||
batchProcessingEnabled: boolean;
|
||||
}
|
||||
|
||||
export type AgentWorkflowBusinessModeKey = "main_sku" | "detail" | "buyer_show";
|
||||
export type AgentWorkflowBusinessModeIconKey =
|
||||
| "main-sku"
|
||||
| "detail"
|
||||
| "buyer-show";
|
||||
|
||||
export interface AgentWorkflowBusinessModeConfig {
|
||||
key: AgentWorkflowBusinessModeKey;
|
||||
title: string;
|
||||
description?: string | null;
|
||||
enabled: boolean;
|
||||
iconKey: AgentWorkflowBusinessModeIconKey;
|
||||
customSvgUrl?: string | null;
|
||||
sortOrder: number;
|
||||
referenceReplaceEnabled?: boolean;
|
||||
aiDetailGenerateEnabled?: boolean;
|
||||
}
|
||||
|
||||
export interface AgentWorkflowBusinessModeSettings {
|
||||
modes: AgentWorkflowBusinessModeConfig[];
|
||||
}
|
||||
|
||||
export interface AgentWorkflowUiConfig {
|
||||
inputOptions?: {
|
||||
brands: string[];
|
||||
@ -129,6 +151,7 @@ export interface AgentWorkflowUiConfig {
|
||||
};
|
||||
constraintSettings?: AgentWorkflowConstraintSettings;
|
||||
pageFeatureSettings?: AgentWorkflowPageFeatureSettings;
|
||||
businessModeSettings?: AgentWorkflowBusinessModeSettings;
|
||||
steps: AgentWorkflowStepUiConfig[];
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user