feat(web): finish RE 5000PBR lesson video and fix non-ASCII GLB import
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

- Add 11:07 Chinese-narrated lesson video, VTT, poster and 13 chapter screenshots.
- Regenerate 20-render WebGL2 matrix and 15 operation captures on RTX 4060.
- Fix WASM staging for non-ASCII import filenames (猴头.glb); rebuild web_engine.
- Harden lesson video recording pipeline; fix stale lesson-link E2E selector.
- Add RE PBR derived libraries, lesson page, evidence and status report.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
mes123456
2026-11-12 21:49:24 +08:00
parent a078648297
commit d5dd647881
276 changed files with 9632 additions and 314 deletions

View File

@@ -472,3 +472,52 @@ export async function createAssetCatalogManifestV2FromDesktop(value: unknown, re
const manifest = { schemaVersion: ASSET_CATALOG_SCHEMA, revision, catalogs, assets, libraries: [] };
return parseAssetCatalogManifestV2(manifest);
}
export interface AssetCatalogMergeResult {
manifest: AssetCatalogManifestV2IR;
conflicts: string[];
added: { catalogs: number; assets: number; libraries: number };
}
/** Merge an independently generated library into the existing global catalog. */
export async function mergeAssetCatalogManifests(existingValue: unknown, incomingValue: unknown): Promise<AssetCatalogMergeResult> {
const existing = await parseAssetCatalogManifestV2(existingValue);
const incoming = await parseAssetCatalogManifestV2(incomingValue);
const conflicts: string[] = [];
const mergeList = <T>(base: readonly T[], extra: readonly T[], key: (value: T) => string, label: string): T[] => {
const merged = new Map(base.map((value) => [key(value), value]));
for (const value of extra) {
const id = key(value);
const current = merged.get(id);
if (!current) {
merged.set(id, structuredClone(value) as T);
} else if (JSON.stringify(current) !== JSON.stringify(value)) {
conflicts.push(`${label}:${id}`);
}
}
return [...merged.values()];
};
const catalogs = mergeList(existing.catalogs, incoming.catalogs, (value) => value.catalogId, "catalog");
const assets = mergeList(existing.assets, incoming.assets, (value) => value.assetId, "asset");
const libraries = mergeList(existing.libraries, incoming.libraries, (value) => value.libraryId, "library");
const revision = Math.max(existing.revision, incoming.revision) + 1;
if (revision > Number.MAX_SAFE_INTEGER) {
throw new AssetCatalogV2ValidationError("ASSET_BUDGET_EXCEEDED", "asset catalog revision overflow");
}
const manifest = await parseAssetCatalogManifestV2({
schemaVersion: ASSET_CATALOG_SCHEMA,
revision,
catalogs,
assets,
libraries,
});
return {
manifest,
conflicts,
added: {
catalogs: catalogs.length - existing.catalogs.length,
assets: assets.length - existing.assets.length,
libraries: libraries.length - existing.libraries.length,
},
};
}