From 81cd4d175402824f291c532f116524caabdfebe1 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 28 Apr 2026 18:29:33 +0800 Subject: [PATCH] fix: refine detail stitch preview and passthrough flow --- .../AgentDetailReferenceStitchDialog.tsx | 155 +++++++++++------- .../agents/AgentGenerationWorkspaceDarkV2.tsx | 142 +++++++++++----- 2 files changed, 194 insertions(+), 103 deletions(-) diff --git a/components/agents/AgentDetailReferenceStitchDialog.tsx b/components/agents/AgentDetailReferenceStitchDialog.tsx index a1a45503f..72ef8f5f1 100644 --- a/components/agents/AgentDetailReferenceStitchDialog.tsx +++ b/components/agents/AgentDetailReferenceStitchDialog.tsx @@ -246,6 +246,20 @@ export function AgentDetailReferenceStitchDialog({ ), [cards] ); + const validCardLoadKey = useMemo( + () => + validCards + .map((card) => `${card.id}::${String(card.imageUrl || "").trim()}`) + .join("|"), + [validCards] + ); + const setupCardsRef = useRef(validCards); + const setupCardsKeyRef = useRef(validCardLoadKey); + if (setupCardsKeyRef.current !== validCardLoadKey) { + setupCardsKeyRef.current = validCardLoadKey; + setupCardsRef.current = validCards; + } + const setupCards = setupCardsRef.current; const normalizedCutLines = useMemo( () => @@ -301,7 +315,7 @@ export function AgentDetailReferenceStitchDialog({ return; } - if (validCards.length === 0) { + if (setupCards.length === 0) { setSnapshot(null); setCutLines([]); setErrorText(`当前没有可拼合的${sourceLabel}`); @@ -317,11 +331,12 @@ export function AgentDetailReferenceStitchDialog({ setIsLoading(true); setErrorText(null); setWarningText(null); + setLoadingProgressText(`正在加载参考图 0/${setupCards.length}`); const failedCards: string[] = []; + let processedCount = 0; - for (const [index, card] of validCards.entries()) { - setLoadingProgressText(`正在加载参考图 ${index + 1}/${validCards.length}`); + for (const [index, card] of setupCards.entries()) { try { const loaded = await loadImage(card.imageUrl); loadedImages.push({ @@ -340,6 +355,11 @@ export function AgentDetailReferenceStitchDialog({ imageUrl: card.imageUrl, error, }); + } finally { + processedCount += 1; + if (!cancelled) { + setLoadingProgressText(`正在加载参考图 ${processedCount}/${setupCards.length}`); + } } } @@ -381,7 +401,7 @@ export function AgentDetailReferenceStitchDialog({ return () => { cancelled = true; }; - }, [open, sourceLabel, validCards]); + }, [open, setupCards, sourceLabel]); useEffect(() => { const validIndexes = new Set(sliceSegments.map((segment) => segment.index)); @@ -640,6 +660,15 @@ export function AgentDetailReferenceStitchDialog({ { + event.preventDefault(); + }} + onInteractOutside={(event) => { + event.preventDefault(); + }} + onPointerDownOutside={(event) => { + event.preventDefault(); + }} overlayClassName="bg-black/85 backdrop-blur-sm" > @@ -731,70 +760,72 @@ export function AgentDetailReferenceStitchDialog({ {warningText} ) : null} -
- +
+
+ -
- {isAddingCutLine && previewCutLineY !== null ? ( -
-
-
-
-
- 点击落线 -
-
-
-
- ) : null} - {normalizedCutLines.map((lineY, index) => { - const topPercent = (lineY / snapshot.height) * 100; - return ( +
+ {isAddingCutLine && previewCutLineY !== null ? (
-
+
-
+
- 切线 {index + 1} + 点击落线
-
- ); - })} + ) : null} + {normalizedCutLines.map((lineY, index) => { + const topPercent = (lineY / snapshot.height) * 100; + return ( +
+
+
+
+
+ 切线 {index + 1} +
+ +
+
+
+ ); + })} +
@@ -810,7 +841,7 @@ export function AgentDetailReferenceStitchDialog({
切割结果预估
- 应用切割后,下方每一段都会回到详情替换参考图区;标记为“直接应用”的切片会跳过继续生成,未标记的切片再逐屏生成。 + 应用切割后,下方每一段都会回到详情替换参考图区;标记为“不处理直出”的切片会跳过继续生成,未标记的切片再逐屏生成。
@@ -835,7 +866,7 @@ export function AgentDetailReferenceStitchDialog({
{passthroughSegmentIndexSet.has(segment.index) - ? "当前切片会直接进入结果区,不继续生成。" + ? "当前切片不会参与生图,应用后会直接进入结果区。" : "当前切片会继续参与详情替换生成。"}
diff --git a/components/agents/AgentGenerationWorkspaceDarkV2.tsx b/components/agents/AgentGenerationWorkspaceDarkV2.tsx index 566da5ca3..f34f52fc4 100644 --- a/components/agents/AgentGenerationWorkspaceDarkV2.tsx +++ b/components/agents/AgentGenerationWorkspaceDarkV2.tsx @@ -779,6 +779,9 @@ type ReplacementCardStatus = | "fallback" | "missing"; +const MANUAL_PASSTHROUGH_RESULT_TEXT = + "已按人工标记沿用参考图,直接应用当前切片,不参与继续生成。"; + function inferReplacementCardStatusFromText(text: string | null | undefined): ReplacementCardStatus | null { const normalized = normalizeAssistantText(String(text || "")) .replace(/\s+/g, " ") @@ -825,7 +828,7 @@ function getReplacementCardStatusMeta(status: ReplacementCardStatus) { switch (status) { case "passthrough": return { - label: "沿用参考图", + label: "不处理直出", className: "border-cyan-400/30 bg-cyan-500/[0.10] text-cyan-200", }; case "fallback": @@ -942,35 +945,53 @@ function AnimatedGenerationPlaceholderCard(params: { badge: string; badgeClassName?: string; showSpinner?: boolean; + animated?: boolean; }) { + const isAnimated = Boolean(params.animated); return ( -
- -
- -
- - +
+ {isAnimated ? ( + <> + +
+ +
+ + + + ) : ( + <> +
+
+ + )}
@@ -2408,20 +2429,34 @@ export function AgentGenerationWorkspaceDarkV2({ () => detailReplacementReferenceCards.map((card, index) => { const resultMeta = latestReplacementResults[index]; + const localPassthroughResultUrl = + !resultMeta?.url && card.passthrough && card.imageUrl + ? card.imageUrl + : null; const resultUrl = resultMeta?.url && !normalizedDeletedResultCollections.detail_replacement.includes(resultMeta.url) ? resultMeta.url - : null; + : localPassthroughResultUrl && + !normalizedDeletedResultCollections.detail_replacement.includes( + localPassthroughResultUrl + ) + ? localPassthroughResultUrl + : null; + const resultText = resultMeta?.url + ? resultMeta?.text || "" + : localPassthroughResultUrl + ? MANUAL_PASSTHROUGH_RESULT_TEXT + : ""; return { ...card, index, resultUrl, - resultText: resultUrl ? resultMeta?.text || "" : "", + resultText, status: getReplacementCardStatus({ index, resultUrl, - resultText: resultUrl ? resultMeta?.text || "" : "", + resultText, generatedCount: latestReplacementResults.length, isGenerating: isDetailResultModeGenerating, }), @@ -2438,20 +2473,34 @@ export function AgentGenerationWorkspaceDarkV2({ () => skuReplacementReferenceCards.map((card, index) => { const resultMeta = latestReplacementResults[index]; + const localPassthroughResultUrl = + !resultMeta?.url && card.passthrough && card.imageUrl + ? card.imageUrl + : null; const resultUrl = resultMeta?.url && !normalizedDeletedResultCollections.sku_replacement.includes(resultMeta.url) ? resultMeta.url - : null; + : localPassthroughResultUrl && + !normalizedDeletedResultCollections.sku_replacement.includes( + localPassthroughResultUrl + ) + ? localPassthroughResultUrl + : null; + const resultText = resultMeta?.url + ? resultMeta?.text || "" + : localPassthroughResultUrl + ? MANUAL_PASSTHROUGH_RESULT_TEXT + : ""; return { ...card, index, resultUrl, - resultText: resultUrl ? resultMeta?.text || "" : "", + resultText, status: getReplacementCardStatus({ index, resultUrl, - resultText: resultUrl ? resultMeta?.text || "" : "", + resultText, generatedCount: latestReplacementResults.length, isGenerating: isSkuResultModeGenerating, }), @@ -5639,7 +5688,7 @@ export function AgentGenerationWorkspaceDarkV2({
{card.passthrough ? (
- 直出结果 + 不处理直出
) : null} {isActive ? ( @@ -5692,14 +5741,17 @@ export function AgentGenerationWorkspaceDarkV2({ (() => { const isActive = activeDetailReferenceCardId === card.id; const statusMeta = getReplacementCardStatusMeta(card.status); + const hasResult = Boolean(card.resultUrl); return (
{card.resultUrl ? ( @@ -5764,6 +5816,7 @@ export function AgentGenerationWorkspaceDarkV2({ 0 ? ( skuResultCards.map((card) => { const statusMeta = getReplacementCardStatusMeta(card.status); + const hasResult = Boolean(card.resultUrl); return (
{card.resultUrl ? ( @@ -6239,6 +6295,7 @@ export function AgentGenerationWorkspaceDarkV2({
@@ -6432,6 +6491,7 @@ export function AgentGenerationWorkspaceDarkV2({ ? "border-white/12 bg-white/[0.05] text-white/62" : "border-white/10 bg-white/[0.04] text-white/58" } + animated={card.status === "generating"} showSpinner={card.status === "generating"} title={`${getResultModeLabel(activeResultModeType)} 结果图 ${card.index + 1}`} description={