合并主分支并修复画布快速编辑冲突

同步主分支的共享画布、游戏创作资源编辑与完美像素合同。\n保留图片快速编辑业务引用、正向白名单与任务快照门禁。\n修复共享画布普通图片类型、完美像素幂等和游戏创作特例融合回归。\n合并后端契约与项目决策记录,并通过本地完整 CI。
This commit is contained in:
2026-08-12 14:40:14 +08:00
216 changed files with 57128 additions and 6615 deletions
+4 -2
View File
@@ -2385,11 +2385,13 @@ function assertAiGameCreatorShellUserDevBoundary() {
);
}
if (
!aiGameCreatorCargoManifestSource.includes('tauri-plugin-http = "2.5.9"') ||
!aiGameCreatorCargoManifestSource.includes(
'tauri-plugin-http = { version = "2.5.9", default-features = false, features = ["charset", "cookies", "http2", "rustls-tls"] }',
) ||
!aiGameCreatorShellTauriSource.includes('tauri_plugin_http::init()')
) {
throw new Error(
'AI game creator release must register the native HTTP plugin',
'AI game creator release must register the native HTTP plugin without the OS automatic system-proxy feature',
);
}
if (
+91
View File
@@ -0,0 +1,91 @@
import assert from 'node:assert/strict';
import { execFileSync, spawnSync } from 'node:child_process';
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { delimiter, dirname, join, resolve } from 'node:path';
import { test } from 'node:test';
import { fileURLToPath } from 'node:url';
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const packageJson = JSON.parse(
readFileSync(join(repoRoot, 'package.json'), 'utf8'),
);
test('pre-commit hook 仅格式化暂存的 TypeScript 快照', () => {
assert.equal(packageJson.scripts.prepare, 'husky');
assert.equal(packageJson.scripts['format:staged'], 'lint-staged');
assert.deepEqual(packageJson['lint-staged'], {
'*.{ts,tsx}': 'prettier --write',
});
assert.equal(
readFileSync(join(repoRoot, '.husky', 'pre-commit'), 'utf8'),
'npm run format:staged\n',
);
const tempRepo = mkdtempSync(
join(tmpdir(), 'genarrative-pre-commit-format-'),
);
try {
git(tempRepo, 'init', '--quiet');
git(tempRepo, 'config', 'user.email', 'pre-commit-test@example.invalid');
git(tempRepo, 'config', 'user.name', 'Pre-commit Test');
const sourcePath = join(tempRepo, 'sample.ts');
writeFileSync(sourcePath, 'const original = 1;\nconst keep = 2;\n');
git(tempRepo, 'add', 'sample.ts');
git(
tempRepo,
'-c',
'commit.gpgsign=false',
'commit',
'--quiet',
'-m',
'baseline',
);
writeFileSync(sourcePath, 'const original={value:1}\nconst keep = 2;\n');
git(tempRepo, 'add', 'sample.ts');
writeFileSync(
sourcePath,
'const original={value:1}\nconst keep={unstaged:true}\n',
);
const lintStaged = spawnSync(
process.execPath,
[
join(repoRoot, 'node_modules', 'lint-staged', 'bin', 'lint-staged.js'),
'--config',
'-',
],
{
cwd: tempRepo,
encoding: 'utf8',
env: {
...process.env,
PATH: `${join(repoRoot, 'node_modules', '.bin')}${delimiter}${process.env.PATH ?? ''}`,
},
input: JSON.stringify(packageJson['lint-staged']),
},
);
assert.equal(
lintStaged.status,
0,
`${lintStaged.stdout ?? ''}${lintStaged.stderr ?? ''}`,
);
assert.equal(
git(tempRepo, 'show', ':sample.ts'),
'const original = { value: 1 };\nconst keep = 2;\n',
);
assert.equal(
readFileSync(sourcePath, 'utf8'),
'const original = { value: 1 };\nconst keep={unstaged:true}\n',
);
} finally {
rmSync(tempRepo, { force: true, recursive: true });
}
});
function git(cwd, ...args) {
return execFileSync('git', args, { cwd, encoding: 'utf8' });
}