fix: persist PSD editor uploads and results
This commit is contained in:
parent
ded97bef00
commit
aa885a3d48
@ -1046,6 +1046,8 @@ export function AgentGenerationWorkspaceDarkV2({
|
|||||||
const [isPsdTemplateDialogOpen, setIsPsdTemplateDialogOpen] = useState(false);
|
const [isPsdTemplateDialogOpen, setIsPsdTemplateDialogOpen] = useState(false);
|
||||||
const [psdTemplateDialogSourceUrl, setPsdTemplateDialogSourceUrl] = useState<string | null>(null);
|
const [psdTemplateDialogSourceUrl, setPsdTemplateDialogSourceUrl] = useState<string | null>(null);
|
||||||
const [psdTemplateDialogMode, setPsdTemplateDialogMode] = useState<AgentPsdTemplateMode>("main");
|
const [psdTemplateDialogMode, setPsdTemplateDialogMode] = useState<AgentPsdTemplateMode>("main");
|
||||||
|
const [psdTemplateDialogResultModeType, setPsdTemplateDialogResultModeType] =
|
||||||
|
useState<AgentGenerationResultModeType>("scene_generation");
|
||||||
const [batchDownloadState, setBatchDownloadState] = useState<null | "scene" | "detail" | "sku">(null);
|
const [batchDownloadState, setBatchDownloadState] = useState<null | "scene" | "detail" | "sku">(null);
|
||||||
const [detailReplacementSearchState, setDetailReplacementSearchState] = useState<{
|
const [detailReplacementSearchState, setDetailReplacementSearchState] = useState<{
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
@ -1078,11 +1080,13 @@ export function AgentGenerationWorkspaceDarkV2({
|
|||||||
setIsPsdTemplateDialogOpen(false);
|
setIsPsdTemplateDialogOpen(false);
|
||||||
setPsdTemplateDialogSourceUrl(null);
|
setPsdTemplateDialogSourceUrl(null);
|
||||||
setPsdTemplateDialogMode("main");
|
setPsdTemplateDialogMode("main");
|
||||||
|
setPsdTemplateDialogResultModeType("scene_generation");
|
||||||
}, []);
|
}, []);
|
||||||
const openPsdTemplateDialog = useCallback(
|
const openPsdTemplateDialog = useCallback(
|
||||||
(params?: {
|
(params?: {
|
||||||
sourceImageUrl?: string | null;
|
sourceImageUrl?: string | null;
|
||||||
mode?: AgentPsdTemplateMode | null;
|
mode?: AgentPsdTemplateMode | null;
|
||||||
|
resultModeType?: AgentGenerationResultModeType | null;
|
||||||
}) => {
|
}) => {
|
||||||
setPsdTemplateDialogSourceUrl(
|
setPsdTemplateDialogSourceUrl(
|
||||||
isValidChatHttpUrl(params?.sourceImageUrl)
|
isValidChatHttpUrl(params?.sourceImageUrl)
|
||||||
@ -1092,9 +1096,14 @@ export function AgentGenerationWorkspaceDarkV2({
|
|||||||
setPsdTemplateDialogMode(
|
setPsdTemplateDialogMode(
|
||||||
params?.mode === "sku" ? "sku" : params?.mode === "main" ? "main" : "main"
|
params?.mode === "sku" ? "sku" : params?.mode === "main" ? "main" : "main"
|
||||||
);
|
);
|
||||||
|
setPsdTemplateDialogResultModeType(
|
||||||
|
params?.resultModeType ||
|
||||||
|
activeResultModeType ||
|
||||||
|
"scene_generation"
|
||||||
|
);
|
||||||
setIsPsdTemplateDialogOpen(true);
|
setIsPsdTemplateDialogOpen(true);
|
||||||
},
|
},
|
||||||
[]
|
[activeResultModeType]
|
||||||
);
|
);
|
||||||
const normalizedDeletedResultCollections = useMemo(
|
const normalizedDeletedResultCollections = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@ -2952,6 +2961,39 @@ export function AgentGenerationWorkspaceDarkV2({
|
|||||||
},
|
},
|
||||||
[mergedResultCollections.scene_generation, onResultPreviewUrlsChange, uploadBase64ResultToStorage]
|
[mergedResultCollections.scene_generation, onResultPreviewUrlsChange, uploadBase64ResultToStorage]
|
||||||
);
|
);
|
||||||
|
const appendResultUrlToCollection = useCallback(
|
||||||
|
(modeType: AgentGenerationResultModeType, imageUrl: string) => {
|
||||||
|
const normalizedUrl = String(imageUrl || "").trim();
|
||||||
|
if (!normalizedUrl || !onResultPreviewUrlsChange) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextUrls = Array.from(
|
||||||
|
new Set([
|
||||||
|
...normalizeResultCollectionUrls(mergedResultCollections[modeType]),
|
||||||
|
normalizedUrl,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
onResultPreviewUrlsChange(modeType, nextUrls);
|
||||||
|
if (activeResultModeType === modeType) {
|
||||||
|
setSelectedResultUrl(normalizedUrl);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
[activeResultModeType, mergedResultCollections, onResultPreviewUrlsChange]
|
||||||
|
);
|
||||||
|
const handlePsdDerivedImageCreated = useCallback(
|
||||||
|
(imageUrl: string) => {
|
||||||
|
appendResultUrlToCollection(psdTemplateDialogResultModeType, imageUrl);
|
||||||
|
},
|
||||||
|
[appendResultUrlToCollection, psdTemplateDialogResultModeType]
|
||||||
|
);
|
||||||
|
const handlePsdRenderedResultCreated = useCallback(
|
||||||
|
(imageUrl: string) => {
|
||||||
|
appendResultUrlToCollection(psdTemplateDialogResultModeType, imageUrl);
|
||||||
|
},
|
||||||
|
[appendResultUrlToCollection, psdTemplateDialogResultModeType]
|
||||||
|
);
|
||||||
const handleDeleteResultImage = useCallback(
|
const handleDeleteResultImage = useCallback(
|
||||||
(modeType: AgentGenerationResultModeType, imageUrl: string) => {
|
(modeType: AgentGenerationResultModeType, imageUrl: string) => {
|
||||||
const normalizedUrl = String(imageUrl || "").trim();
|
const normalizedUrl = String(imageUrl || "").trim();
|
||||||
@ -6085,6 +6127,12 @@ export function AgentGenerationWorkspaceDarkV2({
|
|||||||
selectedCategoryNames={[workflowContext?.fifthCategory || ""].filter(Boolean)}
|
selectedCategoryNames={[workflowContext?.fifthCategory || ""].filter(Boolean)}
|
||||||
sizeInfoText={workflowContext?.summaryText || null}
|
sizeInfoText={workflowContext?.summaryText || null}
|
||||||
templateMode={psdTemplateDialogMode}
|
templateMode={psdTemplateDialogMode}
|
||||||
|
onDerivedImageCreated={
|
||||||
|
onResultPreviewUrlsChange ? handlePsdDerivedImageCreated : undefined
|
||||||
|
}
|
||||||
|
onRenderedResultCreated={
|
||||||
|
onResultPreviewUrlsChange ? handlePsdRenderedResultCreated : undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</ClientErrorBoundary>
|
</ClientErrorBoundary>
|
||||||
|
|
||||||
|
|||||||
@ -429,6 +429,231 @@ function createInitialState(
|
|||||||
return { imageAssignments, imagePlacements, textAssignments };
|
return { imageAssignments, imagePlacements, textAssignments };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeLayerTransferKey(value: string) {
|
||||||
|
return String(value || "").trim().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLayerTransferKeys(layer: AgentPsdTemplateLayerSlot) {
|
||||||
|
const keys = new Set<string>();
|
||||||
|
const addKey = (value?: string | null) => {
|
||||||
|
const normalized = normalizeLayerTransferKey(String(value || ""));
|
||||||
|
if (normalized) {
|
||||||
|
keys.add(normalized);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
addKey(layer.id);
|
||||||
|
addKey(layer.layerId);
|
||||||
|
addKey(layer.layerPath);
|
||||||
|
addKey(layer.layerName);
|
||||||
|
|
||||||
|
if (isAgentPsdImageLayerSlot(layer)) {
|
||||||
|
addKey(`image:${layer.layerPath}`);
|
||||||
|
addKey(`image:${layer.layerName}`);
|
||||||
|
addKey(`image:${layer.layerPath}:${layer.width}x${layer.height}`);
|
||||||
|
addKey(`image:${layer.layerName}:${layer.width}x${layer.height}`);
|
||||||
|
if (layer.isDefaultResultSource) {
|
||||||
|
addKey("__default_result_source__");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAgentPsdTextLayerSlot(layer)) {
|
||||||
|
addKey(`text:${layer.layerPath}`);
|
||||||
|
addKey(`text:${layer.layerName}`);
|
||||||
|
addKey(`text:${layer.layerPath}:${layer.width}x${layer.height}`);
|
||||||
|
addKey(`text:${layer.layerName}:${layer.width}x${layer.height}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
function findMatchingLayer<TLayer extends AgentPsdTemplateLayerSlot>(params: {
|
||||||
|
nextLayer: TLayer;
|
||||||
|
previousLayers: TLayer[];
|
||||||
|
usedPreviousLayerIds?: Set<string>;
|
||||||
|
}) {
|
||||||
|
const { nextLayer, previousLayers, usedPreviousLayerIds } = params;
|
||||||
|
const nextLayerKeySet = new Set(getLayerTransferKeys(nextLayer));
|
||||||
|
for (const previousLayer of previousLayers) {
|
||||||
|
if (usedPreviousLayerIds?.has(previousLayer.id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousKeys = getLayerTransferKeys(previousLayer);
|
||||||
|
if (previousKeys.some((key) => nextLayerKeySet.has(key))) {
|
||||||
|
return previousLayer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveTransferredSelectedLayerId(params: {
|
||||||
|
nextTemplate: AgentPsdTemplate | null;
|
||||||
|
previousTemplate: AgentPsdTemplate | null;
|
||||||
|
previousSelectedLayerId: string;
|
||||||
|
}) {
|
||||||
|
const { nextTemplate, previousTemplate, previousSelectedLayerId } = params;
|
||||||
|
if (!nextTemplate) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!previousTemplate || !previousSelectedLayerId) {
|
||||||
|
return getPreferredSelectedLayerId(nextTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousLayer =
|
||||||
|
previousTemplate.layers.find((layer) => layer.id === previousSelectedLayerId) ||
|
||||||
|
null;
|
||||||
|
if (!previousLayer) {
|
||||||
|
return getPreferredSelectedLayerId(nextTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextCandidateLayers = nextTemplate.layers.filter(
|
||||||
|
(layer) =>
|
||||||
|
layer.isEditable &&
|
||||||
|
((isAgentPsdImageLayerSlot(previousLayer) && isAgentPsdImageLayerSlot(layer)) ||
|
||||||
|
(isAgentPsdTextLayerSlot(previousLayer) && isAgentPsdTextLayerSlot(layer)))
|
||||||
|
);
|
||||||
|
const previousLayerKeySet = new Set(getLayerTransferKeys(previousLayer));
|
||||||
|
const matchedLayer = nextCandidateLayers.find((layer) =>
|
||||||
|
getLayerTransferKeys(layer).some((key) => previousLayerKeySet.has(key))
|
||||||
|
);
|
||||||
|
|
||||||
|
return matchedLayer?.id || getPreferredSelectedLayerId(nextTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTemplateEditorState(params: {
|
||||||
|
nextTemplate: AgentPsdTemplate | null;
|
||||||
|
previousTemplate: AgentPsdTemplate | null;
|
||||||
|
sourceImageUrl: string;
|
||||||
|
previousImageAssignments: Record<string, string>;
|
||||||
|
previousImagePlacements: Record<string, AgentPsdImagePlacement>;
|
||||||
|
previousTextAssignments: Record<string, string>;
|
||||||
|
previousTextColorAssignments: Record<string, string>;
|
||||||
|
}) {
|
||||||
|
const {
|
||||||
|
nextTemplate,
|
||||||
|
previousTemplate,
|
||||||
|
sourceImageUrl,
|
||||||
|
previousImageAssignments,
|
||||||
|
previousImagePlacements,
|
||||||
|
previousTextAssignments,
|
||||||
|
previousTextColorAssignments,
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
const initialState = createInitialState(nextTemplate, sourceImageUrl);
|
||||||
|
const nextState = {
|
||||||
|
imageAssignments: { ...initialState.imageAssignments },
|
||||||
|
imagePlacements: { ...initialState.imagePlacements },
|
||||||
|
textAssignments: { ...initialState.textAssignments },
|
||||||
|
textColorAssignments: {} as Record<string, string>,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!nextTemplate || !previousTemplate) {
|
||||||
|
return nextState;
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousEditableImageLayers = previousTemplate.layers.filter(
|
||||||
|
(layer): layer is AgentPsdTemplateImageLayerSlot =>
|
||||||
|
layer.isEditable && isAgentPsdImageLayerSlot(layer)
|
||||||
|
);
|
||||||
|
const nextEditableImageLayers = nextTemplate.layers.filter(
|
||||||
|
(layer): layer is AgentPsdTemplateImageLayerSlot =>
|
||||||
|
layer.isEditable && isAgentPsdImageLayerSlot(layer)
|
||||||
|
);
|
||||||
|
const usedPreviousImageLayerIds = new Set<string>();
|
||||||
|
const usedNextImageLayerIds = new Set<string>();
|
||||||
|
|
||||||
|
for (const nextLayer of nextEditableImageLayers) {
|
||||||
|
const matchedPreviousLayer = findMatchingLayer({
|
||||||
|
nextLayer,
|
||||||
|
previousLayers: previousEditableImageLayers,
|
||||||
|
usedPreviousLayerIds: usedPreviousImageLayerIds,
|
||||||
|
});
|
||||||
|
if (!matchedPreviousLayer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousImageUrl = String(
|
||||||
|
previousImageAssignments[matchedPreviousLayer.id] || ""
|
||||||
|
).trim();
|
||||||
|
if (!previousImageUrl) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextState.imageAssignments[nextLayer.id] = previousImageUrl;
|
||||||
|
nextState.imagePlacements[nextLayer.id] = normalizeAgentPsdImagePlacement(
|
||||||
|
previousImagePlacements[matchedPreviousLayer.id]
|
||||||
|
);
|
||||||
|
usedPreviousImageLayerIds.add(matchedPreviousLayer.id);
|
||||||
|
usedNextImageLayerIds.add(nextLayer.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const unmatchedPreviousImageLayers = previousEditableImageLayers.filter(
|
||||||
|
(layer) =>
|
||||||
|
!usedPreviousImageLayerIds.has(layer.id) &&
|
||||||
|
Boolean(String(previousImageAssignments[layer.id] || "").trim())
|
||||||
|
);
|
||||||
|
if (unmatchedPreviousImageLayers.length === 1) {
|
||||||
|
const fallbackNextLayer =
|
||||||
|
nextEditableImageLayers.find(
|
||||||
|
(layer) =>
|
||||||
|
!usedNextImageLayerIds.has(layer.id) && layer.isDefaultResultSource
|
||||||
|
) ||
|
||||||
|
nextEditableImageLayers.find(
|
||||||
|
(layer) => !usedNextImageLayerIds.has(layer.id)
|
||||||
|
);
|
||||||
|
if (fallbackNextLayer) {
|
||||||
|
const fallbackPreviousLayer = unmatchedPreviousImageLayers[0];
|
||||||
|
nextState.imageAssignments[fallbackNextLayer.id] = String(
|
||||||
|
previousImageAssignments[fallbackPreviousLayer.id] || ""
|
||||||
|
).trim();
|
||||||
|
nextState.imagePlacements[fallbackNextLayer.id] =
|
||||||
|
normalizeAgentPsdImagePlacement(
|
||||||
|
previousImagePlacements[fallbackPreviousLayer.id]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousEditableTextLayers = previousTemplate.layers.filter(
|
||||||
|
(layer): layer is AgentPsdTemplateTextLayerSlot =>
|
||||||
|
layer.isEditable && isAgentPsdTextLayerSlot(layer)
|
||||||
|
);
|
||||||
|
const nextEditableTextLayers = nextTemplate.layers.filter(
|
||||||
|
(layer): layer is AgentPsdTemplateTextLayerSlot =>
|
||||||
|
layer.isEditable && isAgentPsdTextLayerSlot(layer)
|
||||||
|
);
|
||||||
|
const usedPreviousTextLayerIds = new Set<string>();
|
||||||
|
|
||||||
|
for (const nextLayer of nextEditableTextLayers) {
|
||||||
|
const matchedPreviousLayer = findMatchingLayer({
|
||||||
|
nextLayer,
|
||||||
|
previousLayers: previousEditableTextLayers,
|
||||||
|
usedPreviousLayerIds: usedPreviousTextLayerIds,
|
||||||
|
});
|
||||||
|
if (!matchedPreviousLayer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousTextValue = previousTextAssignments[matchedPreviousLayer.id];
|
||||||
|
if (typeof previousTextValue === "string") {
|
||||||
|
nextState.textAssignments[nextLayer.id] = previousTextValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousTextColor = String(
|
||||||
|
previousTextColorAssignments[matchedPreviousLayer.id] || ""
|
||||||
|
).trim();
|
||||||
|
if (previousTextColor) {
|
||||||
|
nextState.textColorAssignments[nextLayer.id] = previousTextColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
usedPreviousTextLayerIds.add(matchedPreviousLayer.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextState;
|
||||||
|
}
|
||||||
|
|
||||||
function getLayerLabel(layer: AgentPsdTemplateLayerSlot) {
|
function getLayerLabel(layer: AgentPsdTemplateLayerSlot) {
|
||||||
return isAgentPsdImageLayerSlot(layer) ? "可替换图片" : "可编辑文字";
|
return isAgentPsdImageLayerSlot(layer) ? "可替换图片" : "可编辑文字";
|
||||||
}
|
}
|
||||||
@ -725,6 +950,8 @@ export function AgentPsdTemplateEditorDialog({
|
|||||||
selectedCategoryNames,
|
selectedCategoryNames,
|
||||||
sizeInfoText,
|
sizeInfoText,
|
||||||
templateMode,
|
templateMode,
|
||||||
|
onDerivedImageCreated,
|
||||||
|
onRenderedResultCreated,
|
||||||
}: {
|
}: {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
@ -733,6 +960,8 @@ export function AgentPsdTemplateEditorDialog({
|
|||||||
selectedCategoryNames?: string[] | null;
|
selectedCategoryNames?: string[] | null;
|
||||||
sizeInfoText?: string | null;
|
sizeInfoText?: string | null;
|
||||||
templateMode?: AgentPsdTemplateMode | null;
|
templateMode?: AgentPsdTemplateMode | null;
|
||||||
|
onDerivedImageCreated?: (imageUrl: string) => void;
|
||||||
|
onRenderedResultCreated?: (imageUrl: string) => void;
|
||||||
}) {
|
}) {
|
||||||
const normalizedBrandName = String(brandName || "").trim();
|
const normalizedBrandName = String(brandName || "").trim();
|
||||||
const normalizedSelectedCategoryNames = useMemo(
|
const normalizedSelectedCategoryNames = useMemo(
|
||||||
@ -831,6 +1060,7 @@ export function AgentPsdTemplateEditorDialog({
|
|||||||
const previewViewportRef = useRef<HTMLDivElement | null>(null);
|
const previewViewportRef = useRef<HTMLDivElement | null>(null);
|
||||||
const uploadInputRef = useRef<HTMLInputElement | null>(null);
|
const uploadInputRef = useRef<HTMLInputElement | null>(null);
|
||||||
const inlineTextEditorRef = useRef<HTMLTextAreaElement | null>(null);
|
const inlineTextEditorRef = useRef<HTMLTextAreaElement | null>(null);
|
||||||
|
const previousSelectedTemplateRef = useRef<AgentPsdTemplate | null>(null);
|
||||||
const imageDragStateRef = useRef<{
|
const imageDragStateRef = useRef<{
|
||||||
layerId: string;
|
layerId: string;
|
||||||
pointerId: number;
|
pointerId: number;
|
||||||
@ -839,6 +1069,18 @@ export function AgentPsdTemplateEditorDialog({
|
|||||||
startPlacement: AgentPsdImagePlacement;
|
startPlacement: AgentPsdImagePlacement;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
||||||
|
const imageAssignmentsRef = useRef(imageAssignments);
|
||||||
|
const imagePlacementsRef = useRef(imagePlacements);
|
||||||
|
const textAssignmentsRef = useRef(textAssignments);
|
||||||
|
const textColorAssignmentsRef = useRef(textColorAssignments);
|
||||||
|
const selectedLayerIdRef = useRef(selectedLayerId);
|
||||||
|
|
||||||
|
imageAssignmentsRef.current = imageAssignments;
|
||||||
|
imagePlacementsRef.current = imagePlacements;
|
||||||
|
textAssignmentsRef.current = textAssignments;
|
||||||
|
textColorAssignmentsRef.current = textColorAssignments;
|
||||||
|
selectedLayerIdRef.current = selectedLayerId;
|
||||||
|
|
||||||
const selectedTemplate = useMemo(
|
const selectedTemplate = useMemo(
|
||||||
() =>
|
() =>
|
||||||
templates.find((template) => template.id === selectedTemplateId) || null,
|
templates.find((template) => template.id === selectedTemplateId) || null,
|
||||||
@ -1269,6 +1511,14 @@ export function AgentPsdTemplateEditorDialog({
|
|||||||
|
|
||||||
setSelectedTemplateId("");
|
setSelectedTemplateId("");
|
||||||
setSelectedLayerId("");
|
setSelectedLayerId("");
|
||||||
|
setImageAssignments({});
|
||||||
|
setImagePlacements({});
|
||||||
|
setImageSizes({});
|
||||||
|
setTextAssignments({});
|
||||||
|
setTextColorAssignments({});
|
||||||
|
setRenderedResultUrl("");
|
||||||
|
setRightPanelView("editor");
|
||||||
|
previousSelectedTemplateRef.current = null;
|
||||||
}, [open]);
|
}, [open]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -1291,15 +1541,26 @@ export function AgentPsdTemplateEditorDialog({
|
|||||||
if (!open) return;
|
if (!open) return;
|
||||||
setRenderedResultUrl("");
|
setRenderedResultUrl("");
|
||||||
setRightPanelView("editor");
|
setRightPanelView("editor");
|
||||||
const initialState = createInitialState(
|
const initialState = createTemplateEditorState({
|
||||||
selectedTemplate,
|
nextTemplate: selectedTemplate,
|
||||||
sourceImageUrl || ""
|
previousTemplate: previousSelectedTemplateRef.current,
|
||||||
);
|
sourceImageUrl: sourceImageUrl || "",
|
||||||
|
previousImageAssignments: imageAssignmentsRef.current,
|
||||||
|
previousImagePlacements: imagePlacementsRef.current,
|
||||||
|
previousTextAssignments: textAssignmentsRef.current,
|
||||||
|
previousTextColorAssignments: textColorAssignmentsRef.current,
|
||||||
|
});
|
||||||
setImageAssignments(initialState.imageAssignments);
|
setImageAssignments(initialState.imageAssignments);
|
||||||
setImagePlacements(initialState.imagePlacements);
|
setImagePlacements(initialState.imagePlacements);
|
||||||
setTextAssignments(initialState.textAssignments);
|
setTextAssignments(initialState.textAssignments);
|
||||||
setTextColorAssignments({});
|
setTextColorAssignments(initialState.textColorAssignments);
|
||||||
setSelectedLayerId(getPreferredSelectedLayerId(selectedTemplate));
|
setSelectedLayerId(
|
||||||
|
resolveTransferredSelectedLayerId({
|
||||||
|
nextTemplate: selectedTemplate,
|
||||||
|
previousTemplate: previousSelectedTemplateRef.current,
|
||||||
|
previousSelectedLayerId: selectedLayerIdRef.current,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
if (!selectedTemplate || typeof window === "undefined") {
|
if (!selectedTemplate || typeof window === "undefined") {
|
||||||
return;
|
return;
|
||||||
@ -1327,6 +1588,10 @@ export function AgentPsdTemplateEditorDialog({
|
|||||||
};
|
};
|
||||||
}, [open, selectedTemplate?.id]);
|
}, [open, selectedTemplate?.id]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
previousSelectedTemplateRef.current = selectedTemplate;
|
||||||
|
}, [selectedTemplate]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!selectedTemplate) {
|
if (!selectedTemplate) {
|
||||||
setImageSizes((current) => {
|
setImageSizes((current) => {
|
||||||
@ -1596,7 +1861,12 @@ export function AgentPsdTemplateEditorDialog({
|
|||||||
delete next[slotId];
|
delete next[slotId];
|
||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
toast.success("图片拓展完成,已替换当前槽位");
|
onDerivedImageCreated?.(nextImageUrl);
|
||||||
|
toast.success(
|
||||||
|
onDerivedImageCreated
|
||||||
|
? "图片拓展完成,已替换当前槽位并同步保存到结果区"
|
||||||
|
: "图片拓展完成,已替换当前槽位"
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(error instanceof Error ? error.message : "图片拓展失败");
|
toast.error(error instanceof Error ? error.message : "图片拓展失败");
|
||||||
} finally {
|
} finally {
|
||||||
@ -1727,7 +1997,12 @@ export function AgentPsdTemplateEditorDialog({
|
|||||||
|
|
||||||
setRenderedResultUrl(payload.result.url);
|
setRenderedResultUrl(payload.result.url);
|
||||||
setRightPanelView("export");
|
setRightPanelView("export");
|
||||||
toast.success("模板替换结果已生成");
|
onRenderedResultCreated?.(payload.result.url);
|
||||||
|
toast.success(
|
||||||
|
onRenderedResultCreated
|
||||||
|
? "模板替换结果已生成,并已加入结果区"
|
||||||
|
: "模板替换结果已生成"
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(error instanceof Error ? error.message : "PSD 模板渲染失败");
|
toast.error(error instanceof Error ? error.message : "PSD 模板渲染失败");
|
||||||
} finally {
|
} finally {
|
||||||
@ -1787,6 +2062,15 @@ export function AgentPsdTemplateEditorDialog({
|
|||||||
<DialogContent
|
<DialogContent
|
||||||
className="flex h-[96vh] w-[96vw] max-w-[1700px] flex-col overflow-hidden rounded-[24px] border border-white/12 bg-[#0a0a0a] p-0 text-white shadow-[0_40px_100px_rgba(0,0,0,0.8)]"
|
className="flex h-[96vh] w-[96vw] max-w-[1700px] flex-col overflow-hidden rounded-[24px] border border-white/12 bg-[#0a0a0a] p-0 text-white shadow-[0_40px_100px_rgba(0,0,0,0.8)]"
|
||||||
hideCloseButton
|
hideCloseButton
|
||||||
|
onEscapeKeyDown={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
}}
|
||||||
|
onInteractOutside={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
}}
|
||||||
|
onPointerDownOutside={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
}}
|
||||||
overlayClassName="bg-black/80 backdrop-blur-md"
|
overlayClassName="bg-black/80 backdrop-blur-md"
|
||||||
>
|
>
|
||||||
{fontFaceCss ? <style>{fontFaceCss}</style> : null}
|
{fontFaceCss ? <style>{fontFaceCss}</style> : null}
|
||||||
@ -2032,19 +2316,6 @@ export function AgentPsdTemplateEditorDialog({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
|
||||||
className="h-12 w-full rounded-xl bg-gradient-to-r from-emerald-500 to-teal-400 font-medium text-white shadow-[0_4px_20px_rgba(16,185,129,0.25)] ring-1 ring-emerald-500/50 transition-all hover:from-emerald-400 hover:to-teal-300 hover:shadow-[0_6px_25px_rgba(16,185,129,0.35)] disabled:opacity-50"
|
|
||||||
disabled={isRendering}
|
|
||||||
onClick={handleRender}
|
|
||||||
>
|
|
||||||
{isRendering ? (
|
|
||||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
||||||
) : (
|
|
||||||
<Sparkles className="mr-2 h-4 w-4" />
|
|
||||||
)}
|
|
||||||
应用模板并导出
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
{sourceImageUrl ? null : (
|
{sourceImageUrl ? null : (
|
||||||
<div className="rounded-xl border border-white/10 border-dashed bg-white/[0.02] px-4 py-3 text-white/50 text-xs leading-relaxed shadow-inner">
|
<div className="rounded-xl border border-white/10 border-dashed bg-white/[0.02] px-4 py-3 text-white/50 text-xs leading-relaxed shadow-inner">
|
||||||
当前未从生图结果带入图片。请先在下方图片图层中点击“上传替换图片”,再开始套用
|
当前未从生图结果带入图片。请先在下方图片图层中点击“上传替换图片”,再开始套用
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user