Files
Genarrative/apps/ai-game-creator-shell/src/features/ui-editor/uiDesignResourceBridge.ts
T
k88936 04e5512777 UI 编辑器文档资产的 kind + mediaType 判据收敛到共享契约唯一实现
- packages/shared/src/contracts/gameCreationApp.ts:新增 `isGameCreationAppUiDesignDocAsset(asset)`,`kind` 严格解析后再比文档 kind、`mediaType` 比 `application/json`,这是该判据的唯一定义。
- packages/shared/src/contracts/gameCreationApp.test.ts:补用例钉住判据——只满足一半(kind 对 / mediaType 不符、mediaType 对 / kind 不是文档)与 legacy kind(`UI`、`ui`、带空格、大写)一律返回 false。
- apps/ai-game-creator-shell/src/view/project-development/index.tsx:桥接结果校验与「自动打开 ui-workflow 完成资源」两处改用共享判据,删掉本地 kind/mediaType 双条件。
- apps/ai-game-creator-shell/src/features/ui-editor/uiDesignResourceBridge.ts:`findLinkedUiDesignResource` 的过滤条件改用共享判据。
- apps/ai-game-creator-shell/src/features/project-workspace/ResourceReferenceInput.tsx:缩略图文档分支改用共享判据(原来只看 mediaType,kind 不同也能命中)。
2026-09-17 16:19:26 +08:00

122 lines
3.2 KiB
TypeScript

import type {
GameCreationAppAssetManifestEntry,
GameCreationAppManifest,
} from '../../../../../packages/shared/src/contracts/gameCreationApp';
import {
GAME_CREATION_APP_UI_DESIGN_ASSET_KIND,
isGameCreationAppUiDesignDocAsset,
parseGameCreationAppAssetKind,
} from '../../../../../packages/shared/src/contracts/gameCreationApp';
export type UiDesignResourceBridgeResult = {
asset: GameCreationAppAssetManifestEntry;
manifest: GameCreationAppManifest;
committedProjectRevision: number;
created: boolean;
};
export type UiDesignResourceBridgeInvoke = <T>(
command: string,
args?: Record<string, unknown>,
) => Promise<T>;
function uiWorkflowStagePriority(
generationKind: string | null | undefined,
): number {
switch (generationKind) {
case 'ui-workflow.completed':
return 3;
case 'ui-workflow.binding-ready':
return 2;
case 'ui-workflow.reference-ready':
return 1;
default:
return 0;
}
}
export function findLinkedUiDesignResource(
manifest: GameCreationAppManifest,
prototypeAssetId: string,
) {
const normalizedId = prototypeAssetId.trim();
if (!normalizedId) return null;
const prototype = manifest.assets.find((asset) => asset.id === normalizedId);
const referenceIds = new Set(
[
normalizedId,
prototype?.source.resourceId,
prototype?.source.assetObjectId,
].filter((value): value is string => Boolean(value?.trim())),
);
return (
manifest.assets
.filter(
(asset) =>
isGameCreationAppUiDesignDocAsset(asset) &&
asset.source.referenceResourceIds?.some((reference) =>
referenceIds.has(reference),
),
)
.sort(
(left, right) =>
uiWorkflowStagePriority(right.source.generationKind) -
uiWorkflowStagePriority(left.source.generationKind),
)[0] ?? null
);
}
export async function ensureUiDesignResourceForPrototype({
projectPath,
manifest,
prototypeAssetId,
invoke,
}: {
projectPath: string;
manifest: GameCreationAppManifest;
prototypeAssetId: string;
invoke: UiDesignResourceBridgeInvoke;
}): Promise<UiDesignResourceBridgeResult> {
const normalizedPrototypeAssetId = prototypeAssetId.trim();
if (!normalizedPrototypeAssetId) {
throw new Error('UI 原型资产身份不能为空');
}
const prototype = manifest.assets.find(
(asset) => asset.id === normalizedPrototypeAssetId,
);
if (
!prototype ||
parseGameCreationAppAssetKind(
prototype.kind,
'ui-design-resource-bridge.prototype',
) !== GAME_CREATION_APP_UI_DESIGN_ASSET_KIND
) {
throw new Error('目标资源不是 UI 原型图片');
}
if (!prototype.mediaType.toLowerCase().startsWith('image/')) {
throw new Error('UI 原型资源必须是图片');
}
const linked = findLinkedUiDesignResource(
manifest,
normalizedPrototypeAssetId,
);
if (linked) {
return {
asset: linked,
manifest,
committedProjectRevision: 0,
created: false,
};
}
return invoke<UiDesignResourceBridgeResult>(
'ensure_ui_design_resource_for_prototype',
{
input: {
projectPath,
expectedProjectId: manifest.projectId,
prototypeAssetId: normalizedPrototypeAssetId,
},
},
);
}