合并 master 最新变更
同步远端 master 至游戏场景功能分支 保留场景自定义画风控件并接入 master 的自适应文本输入组件 解决图片画布基础生成表单的唯一合并冲突
This commit is contained in:
@@ -40,6 +40,7 @@ import {
|
||||
generationInputsOrNull,
|
||||
isInlineEditorMediaSource,
|
||||
resolveContextMenuPosition,
|
||||
resolveLayerResourceAssetKind,
|
||||
} from './ImageCanvasEditorModel';
|
||||
import { ImageCanvasEditorShellView } from './ImageCanvasEditorShellView';
|
||||
import type {
|
||||
@@ -66,6 +67,7 @@ import {
|
||||
} from './ImageCanvasGenerationModel';
|
||||
import { formatCanvasHistoryAction } from './ImageCanvasHistoryModel';
|
||||
import { fitViewportToBounds } from './ImageCanvasInteractionModel';
|
||||
import type { CanvasLayerCopyBlockReason } from './ImageCanvasLayerCommandModel';
|
||||
import {
|
||||
isCanvasGenerationComposerVisible,
|
||||
resolveQuickEditFocusViewport,
|
||||
@@ -325,15 +327,7 @@ export function ImageCanvasEditorView({
|
||||
const discardCanvasHistoryEntriesContainingLayerRef = useRef<
|
||||
(matchesLayer: (layer: CanvasLayer) => boolean) => void
|
||||
>(() => {});
|
||||
const persistRestoredAssetKindRef = useRef<
|
||||
(layer: CanvasLayer, historyEntryId?: number) => void
|
||||
>(() => {});
|
||||
const layerResourcePersistenceVersionRef = useRef(new Map<string, number>());
|
||||
const assetKindRollbackLayerByIdRef = useRef(new Map<string, CanvasLayer>());
|
||||
const assetKindHistoryEntryIdsByLayerIdRef = useRef(
|
||||
new Map<string, Set<number>>(),
|
||||
);
|
||||
const persistingAssetKindLayerIdsRef = useRef(new Set<string>());
|
||||
const resetCanvasInteractionStateRef = useRef<() => void>(() => {});
|
||||
const closeGenerationTransientStateRef = useRef<() => void>(() => {});
|
||||
const specToolWrapRef = useRef<HTMLSpanElement | null>(null);
|
||||
@@ -798,35 +792,24 @@ export function ImageCanvasEditorView({
|
||||
}),
|
||||
[],
|
||||
);
|
||||
const canvasHistoryEffects = useMemo(
|
||||
() => ({
|
||||
persistRestoredAssetKind: (layer: CanvasLayer, historyEntryId?: number) =>
|
||||
persistRestoredAssetKindRef.current(layer, historyEntryId),
|
||||
}),
|
||||
[],
|
||||
);
|
||||
const {
|
||||
canUndo,
|
||||
canRedo,
|
||||
getCanvasHistorySnapshot,
|
||||
captureCanvasHistory,
|
||||
discardCanvasHistoryEntriesContainingLayer,
|
||||
discardCanvasHistoryEntriesById,
|
||||
undoCanvasChange,
|
||||
redoCanvasChange,
|
||||
} = useCanvasHistory({
|
||||
refs: canvasHistoryRefs,
|
||||
setters: canvasHistorySetters,
|
||||
resetters: canvasHistoryResetters,
|
||||
effects: canvasHistoryEffects,
|
||||
});
|
||||
captureCanvasHistoryRef.current = captureCanvasHistory;
|
||||
discardCanvasHistoryEntriesContainingLayerRef.current =
|
||||
discardCanvasHistoryEntriesContainingLayer;
|
||||
const [historyFeedback, setHistoryFeedback] =
|
||||
useState<CanvasHistoryFeedback | null>(null);
|
||||
const [persistingAssetKindLayerIds, setPersistingAssetKindLayerIds] =
|
||||
useState<ReadonlySet<string>>(() => new Set());
|
||||
useEffect(() => {
|
||||
if (!historyFeedback) {
|
||||
return;
|
||||
@@ -834,6 +817,21 @@ export function ImageCanvasEditorView({
|
||||
const timer = window.setTimeout(() => setHistoryFeedback(null), 3000);
|
||||
return () => window.clearTimeout(timer);
|
||||
}, [historyFeedback]);
|
||||
const showCanvasLayerCopyWarning = useCallback(
|
||||
(reason: CanvasLayerCopyBlockReason) => {
|
||||
setHistoryFeedback((currentFeedback) => ({
|
||||
id: (currentFeedback?.id ?? 0) + 1,
|
||||
tone: 'warning',
|
||||
text:
|
||||
reason === 'self-contained-local-unsupported'
|
||||
? '历史序列帧素材暂不支持复制'
|
||||
: reason === 'unresolved-resource'
|
||||
? '素材资源尚未登记,暂不能复制'
|
||||
: '素材仍在保存,请稍后重试',
|
||||
}));
|
||||
},
|
||||
[],
|
||||
);
|
||||
const handleUndoCanvasChange = useCallback(() => {
|
||||
const result = undoCanvasChange();
|
||||
if (result.status === 'empty') {
|
||||
@@ -1240,105 +1238,19 @@ export function ImageCanvasEditorView({
|
||||
.then(applyGeneratedProjectSnapshot)
|
||||
.catch(() => undefined);
|
||||
}, [applyGeneratedProjectSnapshot, projectId]);
|
||||
const setLayerAssetKindPersistencePending = useCallback(
|
||||
(layerId: string, isPending: boolean) => {
|
||||
const nextLayerIds = new Set(persistingAssetKindLayerIdsRef.current);
|
||||
if (isPending) {
|
||||
nextLayerIds.add(layerId);
|
||||
} else {
|
||||
nextLayerIds.delete(layerId);
|
||||
}
|
||||
persistingAssetKindLayerIdsRef.current = nextLayerIds;
|
||||
setPersistingAssetKindLayerIds(nextLayerIds);
|
||||
},
|
||||
[],
|
||||
);
|
||||
const persistUpdatedLayerResource = useCallback(
|
||||
(
|
||||
layer: CanvasLayer,
|
||||
rollbackLayer?: CanvasLayer,
|
||||
historyEntryId?: number,
|
||||
) => {
|
||||
(layer: CanvasLayer) => {
|
||||
const requestVersion =
|
||||
(layerResourcePersistenceVersionRef.current.get(layer.id) ?? 0) + 1;
|
||||
layerResourcePersistenceVersionRef.current.set(layer.id, requestVersion);
|
||||
const confirmedRollbackLayer = rollbackLayer
|
||||
? (assetKindRollbackLayerByIdRef.current.get(layer.id) ?? rollbackLayer)
|
||||
: undefined;
|
||||
if (confirmedRollbackLayer) {
|
||||
assetKindRollbackLayerByIdRef.current.set(
|
||||
layer.id,
|
||||
confirmedRollbackLayer,
|
||||
);
|
||||
if (historyEntryId !== undefined) {
|
||||
const historyEntryIds = new Set(
|
||||
assetKindHistoryEntryIdsByLayerIdRef.current.get(layer.id),
|
||||
);
|
||||
historyEntryIds.add(historyEntryId);
|
||||
assetKindHistoryEntryIdsByLayerIdRef.current.set(
|
||||
layer.id,
|
||||
historyEntryIds,
|
||||
);
|
||||
}
|
||||
setLayerAssetKindPersistencePending(layer.id, true);
|
||||
}
|
||||
const rollbackLatestRequest = (error: unknown) => {
|
||||
if (
|
||||
layerResourcePersistenceVersionRef.current.get(layer.id) !==
|
||||
requestVersion ||
|
||||
!confirmedRollbackLayer
|
||||
) {
|
||||
return;
|
||||
}
|
||||
setLayers((currentLayers) => {
|
||||
const nextLayers = currentLayers.map((currentLayer) =>
|
||||
currentLayer.id === layer.id
|
||||
? {
|
||||
...currentLayer,
|
||||
assetKind: confirmedRollbackLayer.assetKind,
|
||||
resourceId: confirmedRollbackLayer.resourceId,
|
||||
}
|
||||
: currentLayer,
|
||||
);
|
||||
layersRef.current = nextLayers;
|
||||
return nextLayers;
|
||||
});
|
||||
setMetadataLayer((currentLayer) =>
|
||||
currentLayer?.id === layer.id
|
||||
? {
|
||||
...currentLayer,
|
||||
assetKind: confirmedRollbackLayer.assetKind,
|
||||
resourceId: confirmedRollbackLayer.resourceId,
|
||||
}
|
||||
: currentLayer,
|
||||
);
|
||||
const historyEntryIds =
|
||||
assetKindHistoryEntryIdsByLayerIdRef.current.get(layer.id);
|
||||
if (historyEntryIds) {
|
||||
discardCanvasHistoryEntriesById(historyEntryIds);
|
||||
}
|
||||
assetKindHistoryEntryIdsByLayerIdRef.current.delete(layer.id);
|
||||
assetKindRollbackLayerByIdRef.current.delete(layer.id);
|
||||
setLayerAssetKindPersistencePending(layer.id, false);
|
||||
const reason =
|
||||
error instanceof Error && error.message.trim()
|
||||
? error.message.trim()
|
||||
: '请稍后重试';
|
||||
setHistoryFeedback((currentFeedback) => ({
|
||||
id: (currentFeedback?.id ?? 0) + 1,
|
||||
tone: 'warning',
|
||||
text: `素材类型保存失败,已恢复原标签:${reason}`,
|
||||
}));
|
||||
};
|
||||
if (!projectId) {
|
||||
rollbackLatestRequest(new Error('项目尚未就绪'));
|
||||
return;
|
||||
}
|
||||
const imageSrc = resolveLayerProjectResourceImageSrc(layer);
|
||||
if (!imageSrc) {
|
||||
rollbackLatestRequest(new Error('素材图片尚未就绪'));
|
||||
return;
|
||||
}
|
||||
const resourceAssetKind = resolveLayerResourceAssetKind(layer);
|
||||
void createEditorProjectResource(projectId, {
|
||||
imageSrc,
|
||||
objectKey: layer.objectKey,
|
||||
@@ -1353,7 +1265,7 @@ export function ImageCanvasEditorView({
|
||||
taskId: layer.taskId,
|
||||
durationSeconds: layer.durationSeconds,
|
||||
sourceResourceId: layer.sourceResourceId,
|
||||
assetKind: layer.assetKind,
|
||||
assetKind: resourceAssetKind,
|
||||
generationInputs: layer.generationInputs,
|
||||
})
|
||||
.then((resource) => {
|
||||
@@ -1363,21 +1275,19 @@ export function ImageCanvasEditorView({
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
confirmedRollbackLayer &&
|
||||
(resource.assetKind !== layer.assetKind ||
|
||||
!resource.resourceId.trim())
|
||||
) {
|
||||
rollbackLatestRequest(new Error('服务器未确认新的素材类型'));
|
||||
return;
|
||||
}
|
||||
const confirmedResourceAssetKind =
|
||||
canvasAssetKindOrNull(resource.assetKind) ?? resourceAssetKind;
|
||||
setLayers((currentLayers) => {
|
||||
const nextLayers = currentLayers.map((currentLayer) =>
|
||||
currentLayer.id === layer.id
|
||||
? {
|
||||
...currentLayer,
|
||||
assetKind: layer.assetKind,
|
||||
resourceId: resource.resourceId,
|
||||
resourcePersistenceState: 'registered' as const,
|
||||
resourceAssetKind: confirmedResourceAssetKind,
|
||||
assetKind:
|
||||
currentLayer.assetKindOverride ??
|
||||
confirmedResourceAssetKind,
|
||||
}
|
||||
: currentLayer,
|
||||
);
|
||||
@@ -1388,31 +1298,20 @@ export function ImageCanvasEditorView({
|
||||
currentLayer?.id === layer.id
|
||||
? {
|
||||
...currentLayer,
|
||||
assetKind: layer.assetKind,
|
||||
resourceId: resource.resourceId,
|
||||
resourcePersistenceState: 'registered' as const,
|
||||
resourceAssetKind: confirmedResourceAssetKind,
|
||||
assetKind:
|
||||
currentLayer.assetKindOverride ??
|
||||
confirmedResourceAssetKind,
|
||||
}
|
||||
: currentLayer,
|
||||
);
|
||||
if (confirmedRollbackLayer) {
|
||||
assetKindHistoryEntryIdsByLayerIdRef.current.delete(layer.id);
|
||||
assetKindRollbackLayerByIdRef.current.delete(layer.id);
|
||||
setLayerAssetKindPersistencePending(layer.id, false);
|
||||
}
|
||||
})
|
||||
.catch(rollbackLatestRequest);
|
||||
.catch(() => undefined);
|
||||
},
|
||||
[
|
||||
discardCanvasHistoryEntriesById,
|
||||
projectId,
|
||||
setLayerAssetKindPersistencePending,
|
||||
],
|
||||
[projectId],
|
||||
);
|
||||
persistRestoredAssetKindRef.current = (layer, historyEntryId) => {
|
||||
const rollbackLayer = layersRef.current.find(
|
||||
(currentLayer) => currentLayer.id === layer.id,
|
||||
);
|
||||
persistUpdatedLayerResource(layer, rollbackLayer ?? layer, historyEntryId);
|
||||
};
|
||||
const {
|
||||
assetExportStatus,
|
||||
isExportingAssets,
|
||||
@@ -1520,11 +1419,7 @@ export function ImageCanvasEditorView({
|
||||
}
|
||||
refreshEditorWalletState();
|
||||
},
|
||||
[
|
||||
projectId,
|
||||
refreshEditorWalletState,
|
||||
showGenerationWarning,
|
||||
],
|
||||
[projectId, refreshEditorWalletState, showGenerationWarning],
|
||||
);
|
||||
const effectiveIsAgentConversationOpen =
|
||||
isAgentConversationEnabled && isAgentConversationOpen;
|
||||
@@ -1781,6 +1676,9 @@ export function ImageCanvasEditorView({
|
||||
: null;
|
||||
const {
|
||||
canvasClipboard,
|
||||
canCopyContextLayers,
|
||||
contextLayerCopyBlockReason,
|
||||
isCanvasClipboardCopyBlocked,
|
||||
pasteCanvasClipboard,
|
||||
copyContextLayers,
|
||||
copySelectedLayers,
|
||||
@@ -1805,6 +1703,7 @@ export function ImageCanvasEditorView({
|
||||
selectedLayerIds,
|
||||
canvasGenerationDialogs,
|
||||
setLayers,
|
||||
appendCanvasLayersWithResources,
|
||||
setSelectedLayerId,
|
||||
setSelectedLayerIds,
|
||||
setHoveredLayerId,
|
||||
@@ -1817,6 +1716,7 @@ export function ImageCanvasEditorView({
|
||||
onDeleteLayerSideEffects: clearDeletedLayerGenerationState,
|
||||
onDeleteGenerationDialogSideEffects: removeCanvasGenerationDialogById,
|
||||
exportLayerImage,
|
||||
onCanvasLayerCopyBlocked: showCanvasLayerCopyWarning,
|
||||
});
|
||||
const {
|
||||
canvasMarquee,
|
||||
@@ -1961,18 +1861,23 @@ export function ImageCanvasEditorView({
|
||||
if (!targetLayer || targetLayer.assetKind === assetKind) {
|
||||
return;
|
||||
}
|
||||
const historyEntryId = captureCanvasHistory({
|
||||
captureCanvasHistory({
|
||||
type: 'change-asset-kind',
|
||||
count: 1,
|
||||
layerIds: [layerId],
|
||||
});
|
||||
const resourceAssetKind = resolveLayerResourceAssetKind(targetLayer);
|
||||
const assetKindOverride =
|
||||
assetKind === resourceAssetKind ? null : assetKind;
|
||||
const nextLayer = {
|
||||
...targetLayer,
|
||||
assetKind,
|
||||
resourceAssetKind,
|
||||
assetKindOverride,
|
||||
assetKind: assetKindOverride ?? resourceAssetKind,
|
||||
};
|
||||
setLayers((currentLayers) => {
|
||||
const nextLayers = currentLayers.map((layer) =>
|
||||
layer.id === layerId ? { ...layer, assetKind } : layer,
|
||||
layer.id === layerId ? nextLayer : layer,
|
||||
);
|
||||
layersRef.current = nextLayers;
|
||||
return nextLayers;
|
||||
@@ -1980,11 +1885,10 @@ export function ImageCanvasEditorView({
|
||||
setMetadataLayer((currentLayer) =>
|
||||
currentLayer?.id === layerId ? nextLayer : currentLayer,
|
||||
);
|
||||
persistUpdatedLayerResource(nextLayer, targetLayer, historyEntryId);
|
||||
setImageContextMenu(null);
|
||||
setContextMenu(null);
|
||||
},
|
||||
[captureCanvasHistory, persistUpdatedLayerResource],
|
||||
[captureCanvasHistory],
|
||||
);
|
||||
const selectAllCanvasObjects = useCallback(() => {
|
||||
const layerSelectionIds = layersRef.current
|
||||
@@ -2387,7 +2291,6 @@ export function ImageCanvasEditorView({
|
||||
generationComposerStyle,
|
||||
selectedToolbarStyle,
|
||||
splittingIconSpritesheetLayerIds,
|
||||
persistingAssetKindLayerIds,
|
||||
uploadDropTarget,
|
||||
contextMenu,
|
||||
canvasClipboard,
|
||||
@@ -2395,6 +2298,9 @@ export function ImageCanvasEditorView({
|
||||
imageContextMenuLayer,
|
||||
contextShouldShowLayer,
|
||||
contextShouldUnlockLayer,
|
||||
canCopyContextLayers,
|
||||
contextLayerCopyBlockReason,
|
||||
isCanvasClipboardCopyBlocked,
|
||||
canUndo,
|
||||
canRedo,
|
||||
historyFeedback,
|
||||
@@ -2438,10 +2344,9 @@ export function ImageCanvasEditorView({
|
||||
onOpenCropExpandPanel: openCropExpandPanel,
|
||||
onRemoveBackground: removeSelectedLayerBackground,
|
||||
onSplitIconSpritesheet: (layer: CanvasLayer) => {
|
||||
if (persistingAssetKindLayerIdsRef.current.has(layer.id)) {
|
||||
return;
|
||||
}
|
||||
void splitSelectedIconSpritesheet(layer);
|
||||
void flushProjectPersistence().then(() =>
|
||||
splitSelectedIconSpritesheet(layer),
|
||||
);
|
||||
},
|
||||
onExtractUiDesignAssets: extractUiDesignAssets,
|
||||
onUiAssetExtractionToolChange: changeUiAssetExtractionTool,
|
||||
|
||||
Reference in New Issue
Block a user