Skip to content

Carry scene materials through Load Build - #729

Merged
Aymericr merged 2 commits into
pascalorg:mainfrom
alxbouchard:materials-through-import
Aug 31, 2026
Merged

Carry scene materials through Load Build#729
Aymericr merged 2 commits into
pascalorg:mainfrom
alxbouchard:materials-through-import

Conversation

@alxbouchard

@alxbouchard alxbouchard commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

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

validateBuildJson drops the top-level materials table: every scene:<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: handleConfirmImport passes only installedPlugins to setScene, whose extra.materials support already exists.

The fix

  • ParsedBuildJson gains materials?: 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.
  • Normalization is a deliberate, documented choice (per the review): 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. The in-line comment states this and why.
  • handleConfirmImport now takes ParsedBuildJson directly (no widened record + cast) and hands parsed.materials to setScene.
  • Tests are bun:test (bun test src/validation: 8 pass); tsc --noEmit and biome check clean; 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 materials table, which fixes custom finishes silently reverting when nodes reference scene:<id> slot refs.

validateBuildJson now accepts optional materials, validates each entry with SceneMaterial.safeParse (normalized output for the scene store), skips bad entries with invalid_materials warnings that name the ids, and includes valid materials on ParsedBuildJson. The editor Save Build export includes scene materials, and Load Build passes parsed.materials into setScene on confirm. Tests cover valid carry-through, partial invalid entries, and non-object materials.

Reviewed by Cursor Bugbot for commit 2996a11. Bugbot is set up for automated code reviews on this repo. Configure here.

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 Aymericr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_materials warning 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.
  • collections have the identical gap on both sides (Save omits them, validateBuildJson drops them, confirm doesn't pass them — while setScene accepts extra.collections and 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>
@alxbouchard

Copy link
Copy Markdown
Contributor Author

Done in 2996a11 — Save Build now subscribes materials from the store and exports it, closing the round-trip (paint a finish → Save → Load keeps it). The invalid_materials warning now names the skipped ids. Agreed on leaving collections to your follow-up.

@Aymericr Aymericr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@Aymericr
Aymericr merged commit 8315192 into pascalorg:main Aug 31, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants