Carry scene materials through Load Build - #729
Conversation
validateBuildJson dropped the top-level materials table, so every scene:<id> slot ref in an imported file pointed at a material that no longer existed — custom finishes silently reverted to defaults on Load Build. ParsedBuildJson now carries materials, each entry validated individually (a bad material never takes the import down, it is skipped with a warning), and handleConfirmImport hands them to setScene, whose extra.materials support already existed. Normalization here is DELIBERATE and documented in-line: safeParse().data injects defaults and drops unknown keys — the opposite of apiGraphSchema's preserve-unknowns stance — because import feeds the live scene store, which only understands schema-shaped materials. Split out of pascalorg#720 at the maintainer's request.
Aymericr
left a comment
There was a problem hiding this comment.
Thanks for splitting this out — the core change is right, and I checked the resolution path: keying the kept table by record key matches how every scene: ref is resolved at runtime (slot-paint.ts, wall/paint.ts, etc.), and the normalization stance matches how the hosted loader already parses materials, so import parity is correct. The handleConfirmImport retype to ParsedBuildJson also finally matches LoadBuildDialog's onConfirm signature instead of a drifting inline copy. Good.
One thing must change before merge: handleSaveBuild in the same file (settings-panel/index.tsx:234) still exports { nodes, rootNodeIds, installedPlugins }. Paint a finish, Save Build, Load Build that file — the finish reverts to default, which is exactly the bug in your description, still alive in the round-trip through this panel. As it stands the fix only helps files that already carry materials (API exports, hand-edited). Subscribe materials from the store and add it to sceneData; that completes the loop, and a Save→Load round-trip is the natural test for it.
Two smaller notes:
- The
invalid_materialswarning is count-only. Since the audience is hand-edited files, append the skipped ids to the message so the file is repairable — the node-level warnings in this validator all name their node. collectionshave the identical gap on both sides (Save omits them,validateBuildJsondrops them, confirm doesn't pass them — whilesetSceneacceptsextra.collectionsand the hosted loader round-trips them). Don't fix it here; I'll file it as a follow-up unless you want to take it in a separate PR.
With the Save Build line added this is ready. (CI run is approved.)
Review follow-up (pascalorg#729): paint a finish, Save Build, Load Build that file — the finish reverted to default because handleSaveBuild still exported only { nodes, rootNodeIds, installedPlugins }. Materials ride along now, closing the round-trip this PR opened on the import side. Also names the skipped ids in the invalid_materials warning: the audience is hand-edited files, and a bare count leaves nothing to repair by. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Done in 2996a11 — Save Build now subscribes |
Aymericr
left a comment
There was a problem hiding this comment.
Verified the follow-up commit: Save Build now exports the materials table (with the why captured in the comment), and the invalid_materials warning names the skipped ids with a test pinning it. The round-trip is closed. Merging.
Split out of #720 as requested in the review — the materials fix on its own, so it doesn't wait on the import-page discussion.
The bug
validateBuildJsondrops the top-levelmaterialstable: everyscene:<id>slot ref in an imported file points at a material that no longer exists, so custom finishes silently revert to defaults on Load Build. Confirmed against main:handleConfirmImportpasses onlyinstalledPluginstosetScene, whoseextra.materialssupport already exists.The fix
ParsedBuildJsongainsmaterials?: Record<string, SceneMaterial>; entries are validated one by one (SceneMaterial.safeParse), invalid ones skipped with a warning so a bad material never takes the import down.safeParse().datainjects defaults and drops unknown keys — the opposite ofapiGraphSchema's preserve-unknowns stance — because import feeds the live scene store, which only understands schema-shaped materials. The in-line comment states this and why.handleConfirmImportnow takesParsedBuildJsondirectly (no widened record + cast) and handsparsed.materialstosetScene.bun:test(bun test src/validation: 8 pass);tsc --noEmitandbiome checkclean; no lockfile changes.The review's other points (vitest→bun:test in the page test, byte-accurate size cap, capture-protocol seam) are being addressed on #720.
🤖 Generated with Claude Code
Note
Low Risk
Scoped to build JSON validation and editor save/import; invalid materials are warned and skipped without blocking import.
Overview
Load Build no longer drops the top-level
materialstable, which fixes custom finishes silently reverting when nodes referencescene:<id>slot refs.validateBuildJsonnow accepts optionalmaterials, validates each entry withSceneMaterial.safeParse(normalized output for the scene store), skips bad entries withinvalid_materialswarnings that name the ids, and includes valid materials onParsedBuildJson. The editor Save Build export includes scenematerials, and Load Build passesparsed.materialsintosetSceneon confirm. Tests cover valid carry-through, partial invalid entries, and non-objectmaterials.Reviewed by Cursor Bugbot for commit 2996a11. Bugbot is set up for automated code reviews on this repo. Configure here.