Skip to content

nodes: improve block mesh editing UX - #691

Open
sudhir9297 wants to merge 45 commits into
pascalorg:mainfrom
sudhir9297:t3code/improve-block-node-ux
Open

nodes: improve block mesh editing UX#691
sudhir9297 wants to merge 45 commits into
pascalorg:mainfrom
sudhir9297:t3code/improve-block-node-ux

Conversation

@sudhir9297

@sudhir9297 sudhir9297 commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

  • Adds Blender-style block mesh editing with modal move, rotate, and scale; axis and plane constraints; numeric input; precision and snapping feedback; live values; X-Ray selection; and adjustable/repeatable operations.
  • Generalizes topology operations across multi-component selections, simplifies face material slots with automatic assignment and accent materials, and fixes item detachment from block faces.
  • Isolates block edit-mode scene/history services and extracts focused modal and face-operation controllers, with updated architecture documentation and tests.

How to test

  1. Run bun dev, add and select a Block, then press Tab to enter mesh edit mode.
  2. Select vertices, edges, or faces and verify G, R, and S; constrain with X/Y/Z or Shift+axis, enter numeric values, hold Shift for precision, and confirm/cancel with click, Enter, Escape, or right-click.
  3. Verify multi-component extrude, inset, bevel, loop cut, merge, dissolve, transform, and delete operations; use F9 to adjust the last operation and Shift+R to repeat it.
  4. Select one or more faces, add a material slot, and confirm the faces are assigned immediately with a visible accent material; clicking another slot should reassign the selection.
  5. Move a wall/ceiling/floor item from a block face and confirm it detaches cleanly when leaving the host surface.
  6. Run bun run check, bun run lint, bun run check-types, and bun run build; all should pass.

Screenshots / screen recording

Recording to be added — this PR changes visual and interactive block-editing behavior.

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 changes to block topology commands, scene history replacement, and mesh-edit interaction; incorrect behavior could corrupt geometry or undo stacks.

Overview
Block mesh editing gains modal move/rotate/scale and face ops (extrude/inset) via new modal-transform, modal-face-operation, modal-session, and geometry snapping (geometry-snap). The edit session tracks lastOperation (replacing per-slot material focus) so committed ops can be adjusted in-place on the undo stack or repeated (last-operation + history replaceLatest). Topology commands are renamed and pluralized (extrude-faces, inset-faces, bevel-edges, dissolve-edges, plus dissolve-faces) with multi-ID support, connected-region extrude, and merge keeping the last-selected vertex.

Editor integration: SelectionAffordanceManager injects node, sceneApi, readOnly, and historyApi into selection affordances (exported as SelectionAffordanceProps). Global shortcuts yield during mesh edit: R is reserved in mesh mode; Shift snap cycling is disabled while a mesh operating modal is active.

Materials panel is streamlined: Add slot creates a slot, assigns selected faces, and applies a default accent material; rows apply a slot to the selection (reusable-material picker and select/deselect-by-slot removed). createAssignedBlockMaterialSlot and unpainted-slot tint helpers support this.

Item on block faces: shouldDetachBlockFaceOnLeave now detaches wall/ceiling-hosted items when the pointer leaves the face (no longer sticky across transient leave).

Reviewed by Cursor Bugbot for commit 6c7452d. 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>
Comment thread packages/nodes/src/block/selection.tsx
Comment thread packages/nodes/src/block/geometry-snap.ts
const localPoint = target.worldToLocal(
worldOrigin.clone().add(currentHit.clone().sub(startPlaneHit)),
)
delta = [localPoint.x - origin[0], localPoint.y - origin[1], localPoint.z - origin[2]]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Plane lock ignored when ray misses

Medium Severity

Shift+axis plane lock only applies when startRay hits the constraint plane. A miss leaves lockedTranslationInitialHit null, so preview falls through to unconstrained view-plane movement while the HUD still shows the plane lock.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6c7452d. Configure here.

} else if (key === 'd') {
actions.dissolveSelection()
} else if (event.key === 'Delete' || event.key === 'Backspace' || key === 'x') {
} else if (event.key === 'Delete' || key === 'x') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Mesh edit swallows modifier G shortcut

Medium Severity

The mesh-edit capture handler treats every g key as handled, including Ctrl/Cmd+G and presses with an empty selection. It always preventDefaults and stopImmediatePropagations, so the editor’s capture-phase group shortcut never runs while a block is in edit mode.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6c7452d. Configure here.

)
if (!result.ok) {
setError(result.error)
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Failed preview still commits prior value

Medium Severity

When a live modal command fails validation, the error is shown but latestTopology / latestCommand / latestValue are left on the last successful preview. Confirm still commits that older result even though the current typed or pointer value is invalid.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6c7452d. 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.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

There are 4 total unresolved issues (including 3 from previous reviews).

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 384eb92. Configure here.

return (
<div className="space-y-1">
{(['X', 'Y', 'Z'] as const).map((axis, index) =>
input(`${axis} distance`, command.delta[index]!, (value) => ({

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Adjust panel rejects typed values

Medium Severity

The F9 last-operation inputs call onChange on every keystroke with Number(event.target.value). Empty text, a lone -, or 0. become NaN or 0, so applyBlockCommand fails and adjustLastOperation closes the panel. Typed negatives and in-progress decimals cannot be entered.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 384eb92. 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