nodes: add modular cabinet workflows#461
Conversation
Items (e.g. solar panels) can now be placed on sloped roof surfaces. The placement system computes euler rotation from the roof surface normal so items sit flush on the slope instead of going inside. - Add roofStrategy to placement-strategies with enter/move/click/leave - Wire roof:enter/move/click/leave events in the placement coordinator - Add calculateRoofRotation in placement-math using surface normals - Support full 3D cursor rotation for sloped surfaces - Items on roofs are parented to the level with world-space rotation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fridge exterior is now brushed silver with brass accents and the interior is all white. Door/drawer animation no longer rebuilds the full cabinet geometry per frame — animated parts carry pose metadata that a new per-frame cabinet system applies directly to transforms. Co-Authored-By: Claude <noreply@anthropic.com>
Port the curved-flame look from reference gas-burner photos: each burner gets a ring of vertex-coloured tube flames (blue body, orange-yellow tips) whose spines breathe and flicker per frame at ~30fps, plus a flat ignition glow and a faded heat halo. Fix the knob pointer notch to rotate with the knob instead of drifting sideways. Also includes cooktop compartment presets/panels and cabinet selection/move affordance work. Co-Authored-By: Claude <noreply@anthropic.com>
- Flag shared cabinet appliance materials (and the viewer's cached material factories) as cached so geometry rebuilds no longer dispose materials still referenced by other nodes, forcing scene-wide shader recompiles. - Route the door/drawer open animation through useLiveNodeOverrides with a single final commit, so one play is one undo step instead of ~20. - Add a cabinet-module floorplanMoveTarget: 2D drags now convert through planToLocal + magneticSnap like the 3D path, instead of writing plan coords into the run-local position (teleporting modules on rotated runs). - Re-key sibling runs (cabinetAdjacencyRevision) when a run's neighbor-affecting inputs change, so countertop overhang joins re-trim when a neighbor moves/resizes/deletes. - Narrow the cabinet panel's scene subscriptions (useShallow module selector) so it stops re-rendering on every scene mutation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Prepares the cabinet node for the queued specialty units (corner L-shape, sink base, appliance gap, open shelving): - Add a `moduleKind` discriminator (default 'standard') on CabinetModuleNode so new unit types extend an enum instead of overloading the stack, and split CabinetCompartment into a z.discriminatedUnion so invalid field combos (drawer with rack style, fridge with burner state) are unrepresentable. Shared box fields now come from one `cabinetBoxFields` object so run/module schemas can't drift. - Extract the straight-line run assumption (sort-by-x, edges, adjacency, spans, side-insert, reflow, frame transforms) into run-layout.ts — previously spread across definition/geometry/quick-actions/stack/ move-frame, so corner support would have meant five parallel edits. - Consolidate the run mutations (add module, wall cabinet/hood above, base↔tall switch, layout-revision bump) into run-ops.ts on SceneApi. Panel and quick-actions had drifted copies: the panel's add-module skipped gap checks, revision-bump scope differed per surface. - Remove dead code: node-level doorStyle (with a migrateNodes entry — geometry only ever read per-compartment doorType), the unreachable slot-handle mesh block, the no-op handlePosition 'edge' enum value, wallLocalX, and duplicate totalCabinetHeight definitions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- New tests: cabinetModuleParentFrame.magneticSnap (flush snap, threshold, Z alignment, nearest-edge) and wall-snap's resolveCabinetWallFaceOffset / collectCabinetWallSnapNeighbors (straight face, miter taper, ray-miss fallback, yaw/face/parent filtering) — 87 cabinet tests, up from 71. - De-brittle geometry test lookups: coordinate-encoded mesh names are now matched by pattern, so dimension-default changes don't break them. - Split panel.tsx (1,541 → 679 lines): CompartmentCard + option constants into compartment-card.tsx, CabinetRunPanel + reflow wiring into run-panel.tsx, and the compartment type-transition tables into a pure resolveCompartmentTransition in stack-transitions.ts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pure code motion: geometry.ts (4,728 lines) becomes a 336-line orchestrator dispatching into geometry/ — shared helpers + cached appliance materials (shared.ts), CSG fronts/handles/doors/drawers (fronts.ts), run spans + countertop (run.ts), and self-contained hood/fridge/cooktop/dishwasher/ oven-microwave/pantry builders. No mesh names, userData stamps, materials, or math changed; the './geometry' import path is unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New 'sink' compartment type (single / double / 60-40 bowl layouts) following the cooktop pattern: zero stack height, countertop-plane geometry, panel card with a bowl-layout selector, and a Sink Base preset. Bowl openings are subtracted from both module- and run-owned countertop slabs via three-bvh-csg; the basin shells, drain plumbing, and a spec-sheet-proportioned gooseneck faucet (Grohe Minta-style pin lever) render beneath the cut. Sink modules skip the carcass top panel and the deck under the sink row so the basin hangs through. Also scope the viewer mock in roof-surface-placement-guides.test.ts to only stub useViewer — the bare module stub leaked into later suites and broke real CSG imports. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Regenerated by `next typegen` during check-types; points at .next/types instead of the dev-mode .next/dev/types path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| return <Plus className="h-3.5 w-3.5" /> | ||
| default: | ||
| return null | ||
| } |
There was a problem hiding this comment.
Convert quick action missing icon
Low Severity
Cabinet quick actions use the convert icon for Tall/Base switches, but QuickActionIcon only handles add-left, add-right, and add. Those actions render with text only and no icon.
Reviewed by Cursor Bugbot for commit 4b9ba64. Configure here.
Replace the per-module box soup with NKBA/architectural-convention symbols: the run draws one heavier countertop outline per span (extended by the overhang), base modules draw their front edge plus compartment symbols — rounded bowl rects + faucet dot for sinks, burner/zone rings for cooktops (reusing the 3D layout tables so 2D and 3D always agree) — and appliance modules carry standard upright labels (DW / REF / OV / MW / PAN). Nested wall cabinets and hood-only modules draw as dashed open outlines per the above-cut-plane convention.
Add an `e` slot to registry KeyboardActions and dispatch it in the keyboard hook ahead of the legacy door/window arms, so kinds opt into the E interaction on their NodeDefinition. Cabinets register it: E on a module eases its doors/drawers open or closed, E on a run swings every child module together, and hood-only modules fall through. The panel's rAF animator moves into cabinet/interaction.ts (live-override frames, single undo commit) so the Play button and E share one animation, and the shortcuts dialog now documents E. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d528ec1. Configure here.
| }) | ||
| } | ||
| patchedNodes[id] = next | ||
| } |
There was a problem hiding this comment.
Cabinet migration skips stack validation
Medium Severity
The v2→v3 cabinet migration strips doorStyle, remaps handlePosition, and fixes cooktopLayout strings, but it does not normalize legacy stack entries for the new discriminated compartment union. Scenes with old loose compartment shapes can still fail validation or lose data on load after this change.
Reviewed by Cursor Bugbot for commit d528ec1. Configure here.


What does this PR do?
Adds the modular cabinet node workflow: cabinet runs/modules, wall snapping, paint slots, drawer/appliance/fridge/cooktop/range-hood geometry, floorplan/sidebar support, quick actions, move-frame handling, and cabinet animation systems. It also fixes cabinet material UV scaling for countertops/drawer fronts and keeps refrigerator tall-cabinet stacks filled with proper drawer fronts.
On top of that base, the branch now also includes:
sinkcompartment type (single / double / 60-40 bowl layouts) whose bowl openings are CSG-cut from module- and run-owned countertops via three-bvh-csg, with basin shells, drain plumbing, and a spec-sheet-proportioned gooseneck faucet (Grohe Minta-style pin lever). Sink bases skip the carcass top panel and deck so the basin hangs through.keyboardActions.eslot dispatched ahead of the legacy door/window arms; cabinets register it so E eases a module's doors/drawers open/closed (whole run swings together), sharing one animator with the panel's Play button (live-override frames, single undo commit).How to test
bun run check-typesand expect all packages to pass type checking.bun run buildand expect the production build to complete; note the existing Turbopack NFT warnings aroundpackages/mcp/dist/storage/sqlite-scene-store.js.bun dev, openhttp://localhost:3002, add cabinet runs/modules, switch a base module to tall/fridge/cooktop variants, and verify wall snapping, quick actions, paint slots, UV-scaled countertop/drawer textures, and fridge drawer filler behavior.Screenshots / screen recording
Will be added — visual/interactive cabinet workflow change.
Checklist
bun devbun checkto verify)mainbranchNote
High Risk
Large new node kind touching selection, move, handles, paint, and scene graph composition; incorrect parent-frame or host-selection behavior could mis-route edits or corrupt module layouts.
Overview
Introduces parametric modular cabinets as first-class scene nodes (
cabinetruns andcabinet-modulechildren) with a discriminated compartment stack (doors, drawers, sinks, ovens, cooktops, fridges, hoods, etc.), paint targetcabinet, and scene-load migration for removeddoorStyle/handlePosition: 'edge'and cooktop layout mismatches.Core / registry extends the node platform so cabinets can plug in without editor special-cases:
movable.parentFrame(plan ↔ parent-local move, magnetic module snap, floorplan live transform),quickActions,sceneAction(in-scene clicks e.g. cooktop knobs), optionalpaint.materialTarget, richerdragBounds/ handlecommit/visible/ off-center guide rings, and registry-driven E keyboard actions.Editor wires those capabilities: catalog cabinet build tool, site-tree
CabinetTreeNode, floating-menu quick actions, selection that keeps interaction on the parent run when the run alone is selected,dispatchSceneActionbefore select, parent-frame moves inMoveRegistryNodeToolwith offset drag bounds, handle dragcommitfor composite resizes, and highlight/outliner refresh ongeometryRevision.Nodes package adds procedural geometry and floorplan (run countertops/plinth with sibling trimming, appliance-heavy meshes, world-scaled UVs), stack/preset/reflow helpers, animation system, and broad unit tests.
Reviewed by Cursor Bugbot for commit d528ec1. Bugbot is set up for automated code reviews on this repo. Configure here.