Files
Genarrative/apps/ai-game-creator-shell/src/features/ui-editor/uiDesignResourceBridge.ts
T
kdletters 10786c9672 打通多模态 UI 工作流与 Agent Runtime 路由
- 接入 Codex app-server 原生图片输入与隔离暂存

- 统一 UI 识别、合并、绑定到 Agent Runtime Provider

- 增加页面自动发现、UI 资源桥接与阶段持久化

- 同步画布编辑器投影、测试与技术文档
2026-08-25 13:06:46 +08:00

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,
},
},
);
}