chore: checkpoint web Blender feature-gap work, docs and tests
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

- update native web_engine sources and rebuilt wasm artifacts
- add task cards, status notes and tooling scripts
- add web e2e specs, tutorial UI sources and tutorial assets
- sync golden task manifests and audit records

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
mes123456
2026-11-11 10:46:55 +08:00
parent f3e21c6b98
commit d1e6b63d54
665 changed files with 59149 additions and 39194 deletions

View File

@@ -10,7 +10,7 @@ const hash = (v: unknown) => crypto.createHash("sha256").update(JSON.stringify(v
test("RNA datablock reflection reads native metadata and survives reopen", async ({ page }, testInfo) => {
const receipts = process.env.WBV2_RECEIPT_DIR ?? testInfo.outputPath("receipts"); fs.mkdirSync(receipts, { recursive: true });
await page.goto("/");
await page.goto("/?engine=manual");
const result = await page.evaluate(async (bytes) => {
const { WebEngineClient } = await import("/src/engine-client/WebEngineClient.ts");
const client = new WebEngineClient({ timeoutMs: 30_000 });
@@ -20,10 +20,16 @@ test("RNA datablock reflection reads native metadata and survives reopen", async
const opened = await client.openBlend(Uint8Array.from(bytes).buffer);
stage = "opened";
const dataBlocks = await client.listRNADataBlocks({ editableOnly: false }); stage = "blocks";
const unusedResult = await client.executeEmbeddedPython(`import bpy, json
print("UNUSED:" + json.dumps({kind: [item.name for item in getattr(bpy.data, collection) if item.users == 0 and not item.use_fake_user] for kind, collection in {"Material": "materials", "Mesh": "meshes", "Camera": "cameras", "Light": "lights", "Curve": "curves"}.items()}))`, "UnusedData.py", false);
const unusedLine = unusedResult.python.output.split("\n").find((line) => line.startsWith("UNUSED:"));
const unused = unusedLine ? JSON.parse(unusedLine.slice("UNUSED:".length)) as Record<string, string[]> : {};
const retainedBlocks = dataBlocks.dataBlocks.filter((item) => !(unused[item.type] ?? []).includes(item.name));
const collections = { collections: [] }; stage = "collections";
const properties = { properties: [] }; stage = "properties";
const before = { dataBlocks: dataBlocks.dataBlocks, collections: collections.collections, properties: properties.properties };
const before = { dataBlocks: retainedBlocks, collections: collections.collections, properties: properties.properties };
const saved = await client.saveBlend();
client.terminate();
const reopenedResult = await reopened.openBlend(saved.slice(0));
const reopenedBlocks = await reopened.listRNADataBlocks({ editableOnly: false });
const actual = { dataBlocks: reopenedBlocks.dataBlocks, collections: [], properties: [] };