合并 GameAgent V1 UI 分支

引入游戏创作工作台与资源预览界面
同步 UI 原型生成和视觉验收约束

# Conflicts:
#	apps/ai-game-creator-shell/src/App.tsx
#	docs/README.md
#	docs/project-memory/shared-memory/decision-log.md
#	server-rs/crates/api-server/src/editor_project.rs
This commit is contained in:
AIGameCreator App
2026-07-21 11:04:31 +08:00
37 changed files with 12330 additions and 620 deletions
+42 -15
View File
@@ -26,6 +26,14 @@ const aiGameCreatorShellAppSource = fs.readFileSync(
'apps/ai-game-creator-shell/src/App.tsx',
'utf8',
);
const aiGameCreatorProjectDevelopmentSource = fs.readFileSync(
'apps/ai-game-creator-shell/src/view/project-development/index.tsx',
'utf8',
);
const aiGameCreatorPreviewRustSource = fs.readFileSync(
'apps/ai-game-creator-shell/src-tauri/src/preview.rs',
'utf8',
);
const aiGameCreatorShellTauriConfig = JSON.parse(
fs.readFileSync('apps/ai-game-creator-shell/src-tauri/tauri.conf.json', 'utf8'),
);
@@ -1918,8 +1926,8 @@ function assertAiGameCreatorShellUserDevBoundary() {
.split(';')
.map((directive) => directive.trim())
.find((directive) => directive.startsWith('frame-src ')) ?? '';
if (releaseFrameSrc.includes('http://127.0.0.1:*')) {
throw new Error('AI game creator release shell must not allow embedded local preview frames');
if (!releaseFrameSrc.includes('http://127.0.0.1:*')) {
throw new Error('AI game creator release shell must allow the client-local preview frame');
}
if (!devFrameSrc.includes('http://127.0.0.1:*')) {
throw new Error('AI game creator dev shell must keep local preview iframe access for the developer window');
@@ -1944,7 +1952,7 @@ function assertAiGameCreatorShellUserDevBoundary() {
const devModeBranchIndex = aiGameCreatorShellAppSource.indexOf('{devMode ? (');
const developerPaneIndex = aiGameCreatorShellAppSource.indexOf('className="developer-pane"');
if (previewFrameIndexes.length !== 1) {
throw new Error('AI game creator user shell must not embed game preview frames');
throw new Error('AI game creator developer pane preview frame count drifted');
}
if (
devModeBranchIndex < 0 ||
@@ -1956,26 +1964,45 @@ function assertAiGameCreatorShellUserDevBoundary() {
throw new Error('AI game creator preview iframe must stay inside the dev-only pane');
}
const clientPreviewFrameCount = [
...aiGameCreatorProjectDevelopmentSource.matchAll(/<iframe\b/g),
].length;
if (clientPreviewFrameCount !== 1) {
throw new Error('AI game creator client workbench must own exactly one local preview frame');
}
for (const snippet of [
'async function openPreviewInExternalBrowser(',
"await invoke<LocalPreviewStatus>('open_local_game_preview'",
"return '已交给外部浏览器打开。';",
'const openMessage = await openPreviewInExternalBrowser(',
"appendLocalPermissionLog(\n openedProject.projectPath,\n 'permission.confirm',\n 'project.create',",
'function resolveEmbeddedPreviewUrl(',
"url.protocol !== 'http:' || url.hostname !== '127.0.0.1'",
'sandbox="allow-scripts allow-same-origin allow-forms allow-pointer-lock"',
'src={embeddedPreviewUrl}',
]) {
if (!aiGameCreatorShellAppSource.includes(snippet)) {
throw new Error(`AI game creator external preview boundary drifted: missing ${snippet}`);
if (!aiGameCreatorProjectDevelopmentSource.includes(snippet)) {
throw new Error(`AI game creator embedded preview boundary drifted: missing ${snippet}`);
}
}
for (const snippet of [
'fn open_local_game_preview(',
'.open_url(&url, None::<&str>)',
"await invoke<LocalPreviewStatus>(\n 'activate_local_game_preview'",
'已切换到客户端运行视图',
]) {
if (!aiGameCreatorShellTauriSource.includes(snippet)) {
throw new Error(`AI game creator external preview boundary drifted: missing ${snippet}`);
if (!aiGameCreatorShellAppSource.includes(snippet)) {
throw new Error(`AI game creator client preview activation drifted: missing ${snippet}`);
}
}
for (const snippet of [
'fn activate_local_game_preview(',
'preview_open_url(&status)?;',
]) {
if (!aiGameCreatorPreviewRustSource.includes(snippet)) {
throw new Error(`AI game creator client preview command drifted: missing ${snippet}`);
}
}
if (
aiGameCreatorShellAppSource.includes('openPreviewInExternalBrowser') ||
aiGameCreatorShellAppSource.includes('open_local_game_preview') ||
aiGameCreatorPreviewRustSource.includes('.open_url(&url')
) {
throw new Error('AI game creator preview must not invoke the external browser');
}
for (const snippet of [
'#[cfg(all(debug_assertions, not(test)))]\npub(crate) fn open_developer_window(',
'#[cfg(all(debug_assertions, not(test)))]\n open_developer_window(app.handle())?;',