Skip to content

Fix node dragging and refine pillars, elevators, and roofs - #1112

Open
sudhir9297 wants to merge 7 commits into
pascalorg:mainfrom
sudhir9297:fix/node-tool-interactions
Open

sudhir9297 wants to merge 7 commits into
pascalorg:mainfrom
sudhir9297:fix/node-tool-interactions

Conversation

@sudhir9297

@sudhir9297 sudhir9297 commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes node placement, movement, and resize interactions and refines pillar, elevator, and roof editing on this branch:

  • Keeps handle previews in live overrides and fixes hosted opening/accessory movement, snapping, and selection cleanup.
  • Adds lower-polygon pillar geometry, consistent bounds and floor-plan representation, paint coverage, property previews, and a single thumbnail preset gallery with a white default material.
  • Routes elevator placement and movement through the registry, hardens service/runtime and opening synchronization, adds inspection controls, and recesses sliding doors behind stationary entrance surrounds so they no longer overlap the buttons.
  • Corrects roof surface queries and accessory cache invalidation, aligns editing limits, moves trim editing into transient interaction scope, and preserves the previous mesh when geometry rebuilding fails with an explicit retry action.
  • Adds WebP preset thumbnails, a four-column roof menu with grayscale-to-color hover, and ghosts matching all nine roof forms. Roof thumbnails are bundled with nodes; geometry failures use a generic viewer API, and Escape in text fields preserves trim mode.

The elevator movement review finding is addressed through the optional registry
movable.resolvePosition hook: the 3D and 2D movers resolve landing height after
snapping and again at commit. Regression coverage checks an elevator crossing a
raised slab in both directions and a mounted mover whose support changes between
preview and drop. One undo still restores the gesture.

How to test

  1. Run the editor locally. Place pillars from several presets, change their shape and dimensions, paint their surfaces, and switch between 2D and 3D. Confirm the preset matches its preview, the default material is white, and movement/resizing can be undone without flicker.
  2. Place an elevator across multiple levels. Request stops, disable a stop, adjust dimensions, and open/close its doors. Confirm openings follow the shaft and door panels remain recessed behind the entrance surround and buttons.
  3. Choose each roof type and draw it. Confirm the ghost matches the selected form, all nine thumbnails render, and the left menu has four columns with grayscale images that regain color on hover.
  4. Select a roof segment, enter footprint trimming, and adjust it. Press Escape inside a numeric field: the field cancels without exiting trim mode. Press Escape over the canvas: trim mode exits. Check undo/redo and dimensions in both views.
  5. Automated checks run on the final branch: bun run check-types (13 tasks passed), bun run check (passed with warnings), and bun test packages/editor/src/components/systems/roof/roof-edit-system.test.tsx packages/nodes/src/roof-segment/renderer.test.tsx packages/nodes/src/roof/type-thumbnail.test.tsx (4 tests passed). The nodes package build also passed. The recovery regression injects a failed rebuild and verifies mesh retention and successful retry.

Full bun run ci passed after fixing test isolation: drag-session tests now restore registry registrations, and the floor-height gesture tests verify undo/redo rather than assuming history can grow past its 50-entry limit. The CI-ordered regression harness also passed all 564 tests.

Latest validation (c3edd17a8): full local bun run ci passed; GitHub quality, cli-smoke, and ci all passed. The focused elevator/controller regression run passed 12 tests.

Screenshots / screen recording

Editor captures from this branch's visual verification:

Four-column roof menu and thumbnails
Roof menu and thumbnails

Roof inspector thumbnails
Roof inspector thumbnails

Barrel roof
Barrel roof

Pillar preset gallery
Pillar preset gallery

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

Medium Risk
Touches elevator runtime/opening sync, roof surface math, and broad column geometry/editing—important building logic with heavy test coverage but wide blast radius.

Overview
This PR tightens placement, pillars, elevators, and roofs end-to-end: interaction fixes, registry-owned tools, and clearer editor UX.

Pillars (columns) get lower-poly procedural geometry, a shared footprint/bounds model for plan view, collision, and handles, shape presets with hover preview and white default slot materials, inspector 3D highlights, and property previews that move hosted children without extra undo steps. Lean-to posts stay rotation-only; radius/width edits stay in sync via columnEditPatch.

Elevators drop legacy ElevatorTool / MoveElevatorTool in favor of the registry path. Core adds dimension normalization, shaft-top clearance from cab height and wall thickness, runtime handling for disabled/removed landings and level elevation edits, simpler slab/ceiling opening rules (scoped to the owning building), and door leaf motion that stays inside the opening. New exports and tests cover these paths.

Roofs add the barrel type, shared pitch/size limits, and more accurate getRoofSegmentSurfaceY for hip, gambrel, and mansard. Trim editing no longer uses metadata.showTrimPlanes; it uses interaction scope reshape: 'trim', with Escape ignored while typing in fields. The build tab shows nine roof types in a 4-column grid with RoofTypeThumbnail icons.

Editor polish: slider value controls are proper buttons/labels; right-stack layout keeps the inspector usable on short viewports; roof accessory movers use grid/magnetic snap (Alt to bypass) and strip placement metadata on commit. Column default library materials switch to preset-white; showTrimPlanes is removed from the reference inventory.

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

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

Stale Bugbot comment from a previous run.

Comment thread packages/nodes/src/elevator/definition.ts

Copy link
Copy Markdown
Member

Thanks @sudhir9297, this is a big step up for pillars, elevators and roofs, really appreciate the work.

One bug before we go further: elevator moves now go through the generic move tool (move-tool.tsx no longer routes elevators to MoveElevatorTool), and that only updates x/z. The old mover called resolveElevatorSupportY on every move, so on split levels a moved elevator now keeps its old landing height. Could you recompute the support/landing height on each move (and on commit), with a test for moving an elevator across a split level?

Could you also add screenshots or short clips of the new roof menu, the thumbnails, the barrel roof and the pillars? A lot of this is visual and it's hard to judge from the diff.

Last thing: at 158 files this is tough to review in one go. Would you be up for splitting it into three PRs (pillars, elevators, roofs)? Smaller ones will get reviewed and merged a lot faster.

cursor Bot pushed a commit that referenced this pull request Oct 20, 2026
Conflicts resolved keeping main's behavior, carried into the scene runtime:
- history-control: main's commit authors, write guards and derived-write notes; the
  derived-write notes and write guards are per scene environment, like the PR's other commit
  state, so a detached scene's writes never reach the live commit or end the editor's preview.
- roof elevation: main's wall-top support (#1097) and roofsKeptOverStorey (#1112 era roof
  levels) run in both the editor's system and reconcileDocument; roofsKeptOverStorey selects
  roofs and reads level elevations only once a roof stands over a storey.
- DECISIONS.md: main's E-017 stays; the scene runtime entry is E-018.
- MCP: main's tool executor and recorder wrap the PR's scene scope; compileObject reads the scene
  before its first await and takes a pre-minted id; main's activeLevelId, briefs, bpy and
  materials go through the bridge's runtime.
- agent operations: main's new code selects what it reads (queryNodes) or says why it reads the
  whole scene, per the raw-scene-enumeration rule.

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

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 c3edd17. Configure here.

override: ({ node }) =>
isManagedColumn(node as ColumnNodeType)
? 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.

Managed posts remain 3D movable

Medium Severity

movable.override returns null for lean-to managed posts, but resolveMovable treats null as “use the base config,” so isMovable stays true. 2D move is blocked, yet 3D direct drag and the generic move tool still translate the post and write position without going through columnEditPatch.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c3edd17. Configure here.

@Aymericr Aymericr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks @sudhir9297, the elevator fix through movable.resolvePosition looks right, and the screenshots help a lot.

Two things left before this can go further:

  1. Managed (lean-to) posts can still be dragged in 3D. In packages/nodes/src/column/definition.ts the movable.override returns null for managed columns, but resolveMovable in packages/core/src/services/movement.ts treats null as "use the base config" (return overridden ?? base). So isMovable stays true and the generic move tool and 3D drag still write position without going through columnEditPatch. Returning a config with axes: [] for managed columns (or making resolveMovable treat an explicit null as not movable) would fix it. A test that a managed post isn't movable would lock it in.
  2. Merge conflicts. The branch now conflicts with main, so please merge main in (no force-push needed).

And a gentle follow-up on the earlier question: are you up for splitting this into pillars, elevators and roofs? At 167 files it's still hard to review in one pass.

This branch has not been deployed

No deployments
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