改为行引用保存画布参考图元数据
生成输入参考图只保存 refType/refId 行引用 生成器布局快照只保存参考图行指针并在加载时恢复临时源 上传参考图优先创建项目资源行并回写 resourceId 元数据弹窗改为展示参考图行引用信息 更新图片画布技术文档和项目决策记录
This commit is contained in:
@@ -190,6 +190,100 @@ type CanvasGenerationDialogSnapshot = EditorProjectLayerSnapshot & {
|
||||
|
||||
export type CanvasLayoutItems = EditorProjectLayerSnapshot[];
|
||||
|
||||
function isPersistedReferenceResourceId(resourceId: string | null | undefined) {
|
||||
const normalizedResourceId = resourceId?.trim();
|
||||
return Boolean(
|
||||
normalizedResourceId &&
|
||||
!normalizedResourceId.startsWith('local-') &&
|
||||
!normalizedResourceId.startsWith('generation-dialog:'),
|
||||
);
|
||||
}
|
||||
|
||||
function isPersistedReferenceAssetId(assetId: string | null | undefined) {
|
||||
const normalizedAssetId = assetId?.trim();
|
||||
return Boolean(normalizedAssetId && !normalizedAssetId.startsWith('upload-'));
|
||||
}
|
||||
|
||||
function resolveReferencePointer(reference: CharacterReferenceImage) {
|
||||
const resourceId = reference.resourceId?.trim();
|
||||
if (isPersistedReferenceResourceId(resourceId)) {
|
||||
return {
|
||||
src: `ref:project-resource:${resourceId}`,
|
||||
resourceId,
|
||||
sourceAssetId: reference.sourceAssetId,
|
||||
};
|
||||
}
|
||||
const sourceAssetId = reference.sourceAssetId?.trim();
|
||||
if (isPersistedReferenceAssetId(sourceAssetId)) {
|
||||
return {
|
||||
src: `ref:asset:${sourceAssetId}`,
|
||||
resourceId: reference.resourceId,
|
||||
sourceAssetId,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function serializeGenerationReference(
|
||||
reference: CharacterReferenceImage | null | undefined,
|
||||
): CharacterReferenceImage | null {
|
||||
if (!reference) {
|
||||
return null;
|
||||
}
|
||||
const pointer = resolveReferencePointer(reference);
|
||||
if (!pointer) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
id: reference.id,
|
||||
label: reference.label,
|
||||
src: pointer.src,
|
||||
mediaType: reference.mediaType,
|
||||
mimeType: reference.mimeType,
|
||||
sizeBytes: reference.sizeBytes,
|
||||
durationSeconds: reference.durationSeconds,
|
||||
resourceId: pointer.resourceId,
|
||||
sourceAssetId: pointer.sourceAssetId,
|
||||
};
|
||||
}
|
||||
|
||||
function serializeGenerationReferences(
|
||||
references: CharacterReferenceImage[] | undefined,
|
||||
) {
|
||||
if (!references) {
|
||||
return undefined;
|
||||
}
|
||||
return references.flatMap((reference) => {
|
||||
const serialized = serializeGenerationReference(reference);
|
||||
return serialized ? [serialized] : [];
|
||||
});
|
||||
}
|
||||
|
||||
function serializeDialogReferences(
|
||||
dialog: CanvasGenerationDialogState,
|
||||
): CanvasGenerationDialogState {
|
||||
return {
|
||||
...dialog,
|
||||
specReference: serializeGenerationReference(dialog.specReference),
|
||||
generationReferences: serializeGenerationReferences(
|
||||
dialog.generationReferences,
|
||||
),
|
||||
characterSpecReference: serializeGenerationReference(
|
||||
dialog.characterSpecReference,
|
||||
),
|
||||
characterReferences: serializeGenerationReferences(
|
||||
dialog.characterReferences,
|
||||
),
|
||||
iconSpecReference: serializeGenerationReference(dialog.iconSpecReference),
|
||||
publicationReferences: serializeGenerationReferences(
|
||||
dialog.publicationReferences,
|
||||
),
|
||||
uiDesignSpecReference: serializeGenerationReference(
|
||||
dialog.uiDesignSpecReference,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export function serializeCanvasGenerationDialog(
|
||||
dialog: CanvasGenerationDialogState,
|
||||
): CanvasGenerationDialogSnapshot {
|
||||
@@ -197,7 +291,7 @@ export function serializeCanvasGenerationDialog(
|
||||
itemType: 'generation-dialog',
|
||||
layerId: `generation-dialog:${dialog.id}`,
|
||||
resourceId: `generation-dialog:${dialog.id}`,
|
||||
dialog,
|
||||
dialog: serializeDialogReferences(dialog),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -229,6 +323,7 @@ export function isCanvasGenerationDialogLayoutItem(
|
||||
|
||||
export function splitCanvasLayoutItems(
|
||||
items: EditorProjectLayerSnapshot[],
|
||||
resourcesById: Map<string, CanvasLayerResourceMetadata> = new Map(),
|
||||
): {
|
||||
layerItems: EditorProjectLayerSnapshot[];
|
||||
generationDialogs: CanvasGenerationDialogState[];
|
||||
@@ -238,7 +333,7 @@ export function splitCanvasLayoutItems(
|
||||
|
||||
items.forEach((item) => {
|
||||
if (isCanvasGenerationDialogLayoutItem(item)) {
|
||||
const dialog = hydrateCanvasGenerationDialog(item.dialog);
|
||||
const dialog = hydrateCanvasGenerationDialog(item.dialog, resourcesById);
|
||||
if (dialog) {
|
||||
generationDialogs.push(dialog);
|
||||
}
|
||||
@@ -252,6 +347,7 @@ export function splitCanvasLayoutItems(
|
||||
|
||||
export function hydrateCanvasGenerationDialog(
|
||||
value: unknown,
|
||||
resourcesById: Map<string, CanvasLayerResourceMetadata> = new Map(),
|
||||
): CanvasGenerationDialogState | null {
|
||||
if (!value || typeof value !== 'object') {
|
||||
return null;
|
||||
@@ -278,17 +374,26 @@ export function hydrateCanvasGenerationDialog(
|
||||
? snapshot.specType
|
||||
: undefined,
|
||||
specValues: hydrateSpecFormValues(snapshot.specValues),
|
||||
specReference: hydrateCharacterReference(snapshot.specReference),
|
||||
specReference: hydrateCharacterReference(
|
||||
snapshot.specReference,
|
||||
resourcesById,
|
||||
),
|
||||
generationReferences: hydrateCharacterReferences(
|
||||
snapshot.generationReferences,
|
||||
resourcesById,
|
||||
),
|
||||
characterSpecReference: hydrateCharacterReference(
|
||||
snapshot.characterSpecReference,
|
||||
resourcesById,
|
||||
),
|
||||
characterReferences: hydrateCharacterReferences(
|
||||
snapshot.characterReferences,
|
||||
resourcesById,
|
||||
),
|
||||
iconSpecReference: hydrateCharacterReference(
|
||||
snapshot.iconSpecReference,
|
||||
resourcesById,
|
||||
),
|
||||
iconSpecReference: hydrateCharacterReference(snapshot.iconSpecReference),
|
||||
iconDescriptions: Array.isArray(snapshot.iconDescriptions)
|
||||
? snapshot.iconDescriptions.filter(
|
||||
(description): description is string =>
|
||||
@@ -303,9 +408,11 @@ export function hydrateCanvasGenerationDialog(
|
||||
),
|
||||
publicationReferences: hydrateCharacterReferences(
|
||||
snapshot.publicationReferences,
|
||||
resourcesById,
|
||||
),
|
||||
uiDesignSpecReference: hydrateCharacterReference(
|
||||
snapshot.uiDesignSpecReference,
|
||||
resourcesById,
|
||||
),
|
||||
imageModel: stringOrUndefined(snapshot.imageModel),
|
||||
videoModel:
|
||||
@@ -707,12 +814,19 @@ export function generationInputsOrNull(
|
||||
const item = reference as {
|
||||
title?: unknown;
|
||||
label?: unknown;
|
||||
src?: unknown;
|
||||
refType?: unknown;
|
||||
refId?: unknown;
|
||||
};
|
||||
const title = stringOrNull(item.title);
|
||||
const label = stringOrNull(item.label);
|
||||
const src = stringOrNull(item.src);
|
||||
return title && label && src ? [{ title, label, src }] : [];
|
||||
const refType: 'project-resource' | 'asset' | null =
|
||||
item.refType === 'project-resource' || item.refType === 'asset'
|
||||
? item.refType
|
||||
: null;
|
||||
const refId = stringOrNull(item.refId);
|
||||
return title && label && refType && refId
|
||||
? [{ title, label, refType, refId }]
|
||||
: [];
|
||||
})
|
||||
: [];
|
||||
|
||||
@@ -829,7 +943,20 @@ function hydrateSpecFormValues(
|
||||
};
|
||||
}
|
||||
|
||||
function hydrateCharacterReference(value: unknown): CharacterReferenceImage | null {
|
||||
function resolveHydratedReferenceResource(
|
||||
resourceId: string | undefined,
|
||||
resourcesById: Map<string, CanvasLayerResourceMetadata>,
|
||||
) {
|
||||
if (resourceId) {
|
||||
return resourcesById.get(resourceId);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function hydrateCharacterReference(
|
||||
value: unknown,
|
||||
resourcesById: Map<string, CanvasLayerResourceMetadata> = new Map(),
|
||||
): CharacterReferenceImage | null {
|
||||
if (!value || typeof value !== 'object') {
|
||||
return null;
|
||||
}
|
||||
@@ -837,6 +964,8 @@ function hydrateCharacterReference(value: unknown): CharacterReferenceImage | nu
|
||||
const id = stringOrNull(snapshot.id);
|
||||
const label = stringOrNull(snapshot.label);
|
||||
const src = stringOrNull(snapshot.src);
|
||||
const resourceId = stringOrUndefined(snapshot.resourceId);
|
||||
const sourceAssetId = stringOrUndefined(snapshot.sourceAssetId);
|
||||
const mediaType: CanvasMediaType =
|
||||
snapshot.mediaType === 'video' || snapshot.mediaType === 'audio'
|
||||
? snapshot.mediaType
|
||||
@@ -845,29 +974,37 @@ function hydrateCharacterReference(value: unknown): CharacterReferenceImage | nu
|
||||
const sizeBytes = numberFromSnapshot(snapshot.sizeBytes, 0) || undefined;
|
||||
const durationSeconds =
|
||||
numberFromSnapshot(snapshot.durationSeconds, 0) || undefined;
|
||||
const objectKey = stringOrUndefined(snapshot.objectKey);
|
||||
const assetObjectId = stringOrUndefined(snapshot.assetObjectId);
|
||||
return id && label && src
|
||||
const resource = resolveHydratedReferenceResource(resourceId, resourcesById);
|
||||
const objectKey =
|
||||
stringOrUndefined(snapshot.objectKey) ?? stringOrUndefined(resource?.objectKey);
|
||||
const assetObjectId =
|
||||
stringOrUndefined(snapshot.assetObjectId) ??
|
||||
stringOrUndefined(resource?.assetObjectId);
|
||||
const resolvedSrc = resource?.imageSrc || objectKey || src;
|
||||
return id && label && resolvedSrc
|
||||
? {
|
||||
id,
|
||||
label,
|
||||
src,
|
||||
src: resolvedSrc,
|
||||
mediaType,
|
||||
mimeType,
|
||||
sizeBytes,
|
||||
durationSeconds,
|
||||
objectKey,
|
||||
assetObjectId,
|
||||
resourceId,
|
||||
sourceAssetId,
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
function hydrateCharacterReferences(
|
||||
value: unknown,
|
||||
resourcesById: Map<string, CanvasLayerResourceMetadata> = new Map(),
|
||||
): CharacterReferenceImage[] | undefined {
|
||||
return Array.isArray(value)
|
||||
? value.flatMap((reference) => {
|
||||
const hydrated = hydrateCharacterReference(reference);
|
||||
const hydrated = hydrateCharacterReference(reference, resourcesById);
|
||||
return hydrated ? [hydrated] : [];
|
||||
})
|
||||
: undefined;
|
||||
|
||||
Reference in New Issue
Block a user