fix: improve agent project previews and step wrapping
This commit is contained in:
parent
a197a21337
commit
5037a1a1eb
@ -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 {
|
||||
|
||||
@ -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({
|
||||
>
|
||||
<div className="mb-5 space-y-4">
|
||||
<div className="mt-3">
|
||||
<div className="agent-dark-scrollbar flex items-center gap-4 overflow-x-auto pb-2">
|
||||
<div className="flex flex-wrap items-stretch gap-3 pb-1">
|
||||
{flattenedResolvedSteps.map(({ step }, stepIndex) => {
|
||||
const completed = isAgentWorkflowStepSelectionComplete({
|
||||
step,
|
||||
@ -975,8 +975,18 @@ export function AgentWorkflowPanelDarkV2({
|
||||
});
|
||||
|
||||
return (
|
||||
<Fragment key={step.key}>
|
||||
<div className="group flex shrink-0 items-center gap-3 text-left">
|
||||
<div
|
||||
key={step.key}
|
||||
className={cn(
|
||||
"group flex min-w-[220px] max-w-full flex-1 basis-[280px] flex-col gap-3 rounded-[20px] border px-3 py-3 text-left shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]",
|
||||
active
|
||||
? "border-emerald-300/28 bg-emerald-400/[0.08]"
|
||||
: completed
|
||||
? "border-white/14 bg-white/[0.05]"
|
||||
: "border-white/10 bg-white/[0.025]"
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-wrap items-start gap-3 text-left">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
@ -985,11 +995,11 @@ export function AgentWorkflowPanelDarkV2({
|
||||
setActiveEmbeddedStepKey(step.key);
|
||||
setActiveEmbeddedOptionDepth(1);
|
||||
}}
|
||||
className="flex shrink-0 items-center gap-3 self-center text-left transition-transform duration-200 hover:-translate-y-[1px]"
|
||||
className="flex min-w-0 flex-1 items-start gap-2.5 text-left transition-transform duration-200 hover:-translate-y-[1px]"
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
"flex h-7 w-7 shrink-0 items-center justify-center rounded-full border text-[11px] font-semibold transition-colors",
|
||||
"flex h-6 w-6 shrink-0 items-center justify-center rounded-full border text-[10px] font-semibold transition-colors",
|
||||
active
|
||||
? "border-emerald-300 bg-emerald-300 text-black shadow-[0_0_18px_rgba(52,211,153,0.28)]"
|
||||
: completed
|
||||
@ -999,27 +1009,29 @@ export function AgentWorkflowPanelDarkV2({
|
||||
>
|
||||
{stepIndex + 1}
|
||||
</span>
|
||||
<span
|
||||
className={cn(
|
||||
"truncate text-[13px] font-semibold leading-none transition-colors",
|
||||
active
|
||||
? "text-white"
|
||||
: completed
|
||||
? "text-white/82"
|
||||
: "text-white/52 group-hover:text-white/76"
|
||||
)}
|
||||
>
|
||||
{step.title}
|
||||
</span>
|
||||
{active ? (
|
||||
<span className="inline-flex h-7 shrink-0 items-center rounded-full border border-emerald-300/30 bg-emerald-400/10 px-2.5 text-[10px] font-medium leading-none text-emerald-200">
|
||||
当前步骤
|
||||
</span>
|
||||
) : null}
|
||||
<div className="min-w-0 flex-1 space-y-1">
|
||||
<div
|
||||
className={cn(
|
||||
"text-[12px] font-semibold leading-5 transition-colors",
|
||||
active
|
||||
? "text-white"
|
||||
: completed
|
||||
? "text-white/82"
|
||||
: "text-white/52 group-hover:text-white/76"
|
||||
)}
|
||||
>
|
||||
{step.title}
|
||||
</div>
|
||||
{active ? (
|
||||
<span className="inline-flex min-h-[24px] items-center rounded-full border border-emerald-300/30 bg-emerald-400/10 px-2.5 py-1 text-[10px] font-medium leading-none text-emerald-200">
|
||||
当前步骤
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</button>
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<div className="flex min-w-0 flex-1 items-start">
|
||||
{stepSelectedPath.length > 0 ? (
|
||||
<div className="flex shrink-0 items-center gap-1.5">
|
||||
<div className="flex min-w-0 flex-wrap items-start gap-1.5">
|
||||
{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({
|
||||
<span className="text-[9px] opacity-70">
|
||||
{getWorkflowOptionDepthShortLabel(optionDepth)}
|
||||
</span>
|
||||
<span className="max-w-[9rem] truncate whitespace-nowrap">
|
||||
<span className="max-w-[11rem] break-words whitespace-normal">
|
||||
{option.buttonName}
|
||||
</span>
|
||||
</button>
|
||||
@ -1057,23 +1069,13 @@ export function AgentWorkflowPanelDarkV2({
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<span className="shrink-0 text-[10px] leading-none text-white/32">
|
||||
<span className="text-[10px] leading-none text-white/32">
|
||||
待选择
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{stepIndex < flattenedResolvedSteps.length - 1 ? (
|
||||
<div
|
||||
className={cn(
|
||||
"h-px w-8 shrink-0 rounded-full",
|
||||
stepIndex < activeEmbeddedStepIndex
|
||||
? "bg-emerald-400/35"
|
||||
: "bg-white/10"
|
||||
)}
|
||||
/>
|
||||
) : null}
|
||||
</Fragment>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user