diff --git a/components/agents/AgentProjectListSheet.tsx b/components/agents/AgentProjectListSheet.tsx index 90d85e32f..e20289f8a 100644 --- a/components/agents/AgentProjectListSheet.tsx +++ b/components/agents/AgentProjectListSheet.tsx @@ -17,23 +17,85 @@ import { } from "@/components/ui/sheet"; import { cn } from "@/lib/utils"; import { buildImageProxyUrl } from "@/lib/image-delivery"; -import type { AgentGenerationProject } from "@/types/agent"; +import type { + AgentGenerationProject, + AgentWorkflowMaterialImage, + AgentWorkflowMaterialSelectionState, +} from "@/types/agent"; + +function normalizePreviewUrl(value?: string | null) { + const normalizedValue = String(value || "").trim(); + return normalizedValue || null; +} + +function sortMaterialImages(images: AgentWorkflowMaterialImage[]) { + return [...images].sort((left, right) => { + const sortOrderDiff = (left.sortOrder || 0) - (right.sortOrder || 0); + if (sortOrderDiff !== 0) { + return sortOrderDiff; + } + return left.id.localeCompare(right.id); + }); +} + +function getImageUrlFromCandidates(images: AgentWorkflowMaterialImage[]) { + for (const image of images) { + const imageUrl = normalizePreviewUrl(image.imageUrl); + if (imageUrl) { + return imageUrl; + } + } + return null; +} + +function getMaterialSelectionPreviewUrl(selection?: AgentWorkflowMaterialSelectionState | null) { + const images = selection?.result?.images ?? []; + if (!images.length) { + return null; + } + + const sortedImages = sortMaterialImages(images); + const selectedImageIds = new Set(selection?.selectedImageIds ?? []); + + const selectedImages = sortedImages.filter((image) => selectedImageIds.has(image.id)); + const selectedUploadedImages = selectedImages.filter((image) => image.sourceType === "upload"); + const uploadedImages = sortedImages.filter((image) => image.sourceType === "upload"); + const searchImages = sortedImages.filter((image) => image.sourceType !== "upload"); + + return ( + getImageUrlFromCandidates(selectedUploadedImages) || + getImageUrlFromCandidates(selectedImages) || + getImageUrlFromCandidates(uploadedImages) || + getImageUrlFromCandidates(searchImages) || + getImageUrlFromCandidates(sortedImages) + ); +} function getProjectPreviewUrl(project: AgentGenerationProject) { - const explicitThumbnail = String(project.thumbnail || "").trim(); + const explicitThumbnail = normalizePreviewUrl(project.thumbnail); if (explicitThumbnail) { return explicitThumbnail; } const resultCollections = project.projectState?.resultCollections; - const fallbackUrl = + const resultPreviewUrl = resultCollections?.scene_generation?.[0] || resultCollections?.detail_generation?.[0] || resultCollections?.detail_replacement?.[0] || resultCollections?.sku_replacement?.[0] || ""; + const normalizedResultPreviewUrl = normalizePreviewUrl(resultPreviewUrl); + if (normalizedResultPreviewUrl) { + return normalizedResultPreviewUrl; + } - return String(fallbackUrl || "").trim() || null; + const materials = project.projectState?.workflowState?.materials; + + return ( + getMaterialSelectionPreviewUrl(materials?.primary) || + getMaterialSelectionPreviewUrl(materials?.secondary) || + null + ); } interface AgentProjectListSheetProps { diff --git a/components/agents/AgentWorkflowPanelDarkV2.tsx b/components/agents/AgentWorkflowPanelDarkV2.tsx index 9164285a8..bec8e2407 100644 --- a/components/agents/AgentWorkflowPanelDarkV2.tsx +++ b/components/agents/AgentWorkflowPanelDarkV2.tsx @@ -1,6 +1,6 @@ "use client"; -import { Fragment, useEffect, useMemo, useState, type ReactNode } from "react"; +import { useEffect, useMemo, useState, type ReactNode } from "react"; import { motion } from "framer-motion"; import { ArrowRight, Check, Copy, Sparkles, ChevronRight } from "lucide-react"; import { toast } from "sonner"; @@ -958,7 +958,7 @@ export function AgentWorkflowPanelDarkV2({ >
-
+
{flattenedResolvedSteps.map(({ step }, stepIndex) => { const completed = isAgentWorkflowStepSelectionComplete({ step, @@ -975,8 +975,18 @@ export function AgentWorkflowPanelDarkV2({ }); return ( - -
+
+
-
+
{stepSelectedPath.length > 0 ? ( -
+
{stepSelectedPath.map((option, optionIndex) => { const optionDepth = optionIndex + 1; const optionLevelActive = @@ -1037,7 +1049,7 @@ export function AgentWorkflowPanelDarkV2({ setActiveEmbeddedOptionDepth(optionDepth); }} className={cn( - "inline-flex h-7 max-w-full shrink-0 items-center gap-1 rounded-full border px-2.5 text-[10px] leading-none transition-colors", + "inline-flex min-h-[24px] max-w-full items-center gap-1 rounded-full border px-2 py-1 text-[10px] leading-[1.15] transition-colors", optionLevelActive ? "border-emerald-300/35 bg-emerald-400/12 text-emerald-100" : active || completed @@ -1049,7 +1061,7 @@ export function AgentWorkflowPanelDarkV2({ {getWorkflowOptionDepthShortLabel(optionDepth)} - + {option.buttonName} @@ -1057,23 +1069,13 @@ export function AgentWorkflowPanelDarkV2({ })}
) : ( - + 待选择 )}
- {stepIndex < flattenedResolvedSteps.length - 1 ? ( -
- ) : null} - +
); })}