Skip to content

nodes: add modular cabinet workflows#461

Open
sudhir9297 wants to merge 42 commits into
pascalorg:mainfrom
sudhir9297:fix/wed-jul-1
Open

nodes: add modular cabinet workflows#461
sudhir9297 wants to merge 42 commits into
pascalorg:mainfrom
sudhir9297:fix/wed-jul-1

Conversation

@sudhir9297

@sudhir9297 sudhir9297 commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

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:

  • Undermount sink system — new sink compartment 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.
  • Kitchen-drafting floor plans — the 2D view replaces per-module boxes with NKBA/architectural symbols: one countertop outline per run span, sink bowl rects + faucet dot, cooktop burner/zone rings (shared layout tables with 3D), standard appliance labels (DW / REF / OV / MW / PAN), and dashed above-cut-plane outlines for wall cabinets and hoods.
  • E-key interaction — a registry-level keyboardActions.e slot 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

  1. Run bun run check-types and expect all packages to pass type checking.
  2. Run bun run build and expect the production build to complete; note the existing Turbopack NFT warnings around packages/mcp/dist/storage/sqlite-scene-store.js.
  3. Run bun dev, open http://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.
  4. Add a Sink preset module to a run (or switch a compartment to Sink), orbit under the countertop to confirm the bowl opening is cut through the slab, and check the basin/faucet render; try single / double / 60-40 layouts from the panel.
  5. Select a cabinet module or run and press E — doors/drawers should animate open, second press closes; the panel's Play/Stop button should mirror the same animation.
  6. Switch to the 2D floor plan and verify cabinets draw as countertop outlines with sink/cooktop symbols and DW/REF/OV labels; nested wall cabinets and hoods draw as dashed open outlines.

Screenshots / screen recording

Will be added — visual/interactive cabinet workflow change.

Checklist

  • I've tested this locally with bun dev
  • My code follows the existing code style (run bun check to verify)
  • I've updated relevant documentation (if applicable)
  • This PR targets the main branch

Note

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 (cabinet runs and cabinet-module children) with a discriminated compartment stack (doors, drawers, sinks, ovens, cooktops, fridges, hoods, etc.), paint target cabinet, and scene-load migration for removed doorStyle / 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), optional paint.materialTarget, richer dragBounds / handle commit / 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, dispatchSceneAction before select, parent-frame moves in MoveRegistryNodeTool with offset drag bounds, handle drag commit for composite resizes, and highlight/outliner refresh on geometryRevision.

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.

sudhir9297 and others added 30 commits May 19, 2026 02:59
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>
Comment thread packages/nodes/src/cabinet/definition.ts
Comment thread packages/nodes/src/cabinet/scene-action.ts
Comment thread packages/nodes/src/cabinet/move-frame.ts
Comment thread packages/nodes/src/cabinet/floorplan.ts Outdated
sudhir9297 and others added 6 commits July 4, 2026 20:03
- 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
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4b9ba64. Configure here.

sudhir9297 and others added 2 commits July 6, 2026 13:15
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>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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).

Fix All in Cursor

❌ 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
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d528ec1. Configure here.

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.

1 participant