10786c9672
- 接入 Codex app-server 原生图片输入与隔离暂存 - 统一 UI 识别、合并、绑定到 Agent Runtime Provider - 增加页面自动发现、UI 资源桥接与阶段持久化 - 同步画布编辑器投影、测试与技术文档
112 lines
2.9 KiB
TypeScript
112 lines
2.9 KiB
TypeScript
import type {
|
|
GameCreationAppAssetManifestEntry,
|
|
GameCreationAppManifest,
|
|
} 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) =>
|
|
asset.kind === 'UI' &&
|
|
asset.mediaType === 'application/json' &&
|
|
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 || prototype.kind !== 'ui-prototype') {
|
|
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,
|
|
},
|
|
},
|
|
);
|
|
}
|