实现资源画布布局持久化 (#116)
Project CI / Frontend tests (push) Failing after 20s
Project CI / Repository checks (push) Successful in 1m2s
Project CI / Backend tests (push) Successful in 2m59s
Project CI / Native shell tests (push) Successful in 11m58s

冻结资源画布布局数据与 CAS 合同
实现双模式本地 sidecar 安全读写
接入二维拖动、默认排版和跨重启恢复
补齐并发冲突、安全边界和界面测试
同步技术文档与共享决策

Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/116
Reviewed-by: 段舒康 <kdletters@qq.com>
Co-authored-by: menghao <mh18530625731@163.com>
Co-committed-by: menghao <mh18530625731@163.com>
This commit was merged in pull request #116.
This commit is contained in:
2026-07-31 12:00:48 +08:00
committed by 段舒康
parent d4075c3423
commit 216407d93e
25 changed files with 4170 additions and 352 deletions
+30 -4
View File
@@ -42,6 +42,14 @@ const aiGameCreatorProjectDevelopmentSource = fs.readFileSync(
'apps/ai-game-creator-shell/src/view/project-development/index.tsx',
'utf8',
);
const aiGameCreatorLocalGamePreviewFrameSource = fs.readFileSync(
'apps/ai-game-creator-shell/src/features/project-workspace/LocalGamePreviewFrame.tsx',
'utf8',
);
const aiGameCreatorSupervisorChatOnlyViewSource = fs.readFileSync(
'apps/ai-game-creator-shell/src/features/project-workspace/SupervisorChatOnlyView.tsx',
'utf8',
);
const aiGameCreatorPreviewRustSource = fs.readFileSync(
'apps/ai-game-creator-shell/src-tauri/src/preview.rs',
'utf8',
@@ -2409,20 +2417,38 @@ function assertAiGameCreatorShellUserDevBoundary() {
}
const clientPreviewFrameCount = [
...aiGameCreatorProjectDevelopmentSource.matchAll(/<iframe\b/g),
...aiGameCreatorLocalGamePreviewFrameSource.matchAll(/<iframe\b/g),
].length;
if (clientPreviewFrameCount !== 1) {
throw new Error(
'AI game creator client workbench must own exactly one local preview frame',
);
}
const projectDevelopmentPreviewMountCount = [
...aiGameCreatorProjectDevelopmentSource.matchAll(
/<LocalGamePreviewFrame\b/g,
),
].length;
const supervisorChatPreviewMountCount = [
...aiGameCreatorSupervisorChatOnlyViewSource.matchAll(
/<LocalGamePreviewFrame\b/g,
),
].length;
if (
projectDevelopmentPreviewMountCount !== 1 ||
supervisorChatPreviewMountCount !== 1
) {
throw new Error(
'AI game creator client views must delegate preview rendering to the shared local preview frame',
);
}
for (const snippet of [
'function resolveEmbeddedPreviewUrl(',
"url.protocol !== 'http:' || url.hostname !== '127.0.0.1'",
'sandbox="allow-scripts allow-same-origin allow-forms allow-pointer-lock"',
'src={embeddedPreviewUrl}',
'src={embeddedUrl}',
]) {
if (!aiGameCreatorProjectDevelopmentSource.includes(snippet)) {
if (!aiGameCreatorLocalGamePreviewFrameSource.includes(snippet)) {
throw new Error(
`AI game creator embedded preview boundary drifted: missing ${snippet}`,
);
@@ -2459,7 +2485,7 @@ function assertAiGameCreatorShellUserDevBoundary() {
}
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())?;',
'#[cfg(all(debug_assertions, not(test)))]\n if game_chat_launch.is_none() {\n open_developer_window(app.handle())?;\n }',
]) {
if (!aiGameCreatorShellTauriSource.includes(snippet)) {
throw new Error(