Skip to content

Experience fix pass: placement, selection rotation, roof gable, stairs, capture, Cmd+S, three 0.186 - #807

Merged
wass08 merged 27 commits into
mainfrom
fix/experience-pass-sep9
Sep 10, 2026
Merged

Experience fix pass: placement, selection rotation, roof gable, stairs, capture, Cmd+S, three 0.186#807
wass08 merged 27 commits into
mainfrom
fix/experience-pass-sep9

Conversation

@wass08

@wass08 wass08 commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

Experience fix pass across placement, selection, roof, stairs, capture and shortcuts, plus the three r186 bump.

  • Cmd/Ctrl+S saves instead of opening the browser's page-save dialog. Capture-phase, always-on listener; hosts take the chord over via the new onSaveShortcut prop, the default flushes the autosave. Listed in the shortcuts dialog.
  • Snapshot FOV no longer shows float tails: armCaptureFov rounds and clamps like setCaptureFov, which also fixes the reset button staying enabled.
  • Drone camera slow modifier: hold Alt for 0.2× movement and look sensitivity (Shift stays boost). HUD hint added.
  • Item taken off a shelf drops to the floor again. Regression from editor: add Blender-style custom mesh editing #638: the floor-path Y was frozen at drag start, so an un-hosted item kept the shelf height. Reads the live grid Y now.
  • Placement box / labels on upper storeys: the cursor group, grid surface point and facing pose now add the level mesh's stacked Y (tools mount in the building-local group). Same fix for the registry mover's box/sphere.
  • Modular kitchen (cabinet run) presets land under the cursor: absolute placement maps the cursor to the rotated footprint centre instead of the node origin; R/T while fresh keeps the centre pinned.
  • R/T mid-drag on a box selection keeps each member's facing: translateGroupPatches now carries the rotated yaw for vec3/scalar participants (it was dropped on re-publish and at commit). All sessions pivot on the shared mesh-box centre; R/T while armed engages the session first. 2D floorplan session ported too.
  • Roof gable no longer z-fights the wall top: the CSG degeneracy guard raised the eave instead of sinking the base 4 cm into the wall. Opening-placement frame and shed inset panel mirror the floor.
  • Stairs follow the storey from their elected base: level-destination stairs subtract the slab lift like deck stairs already did; panel exposes "Follows storey / Custom rise"; stair tool and landing toggle seed from the storey instead of 2.5 m.
  • three 0.186.0. No removed export in use; Renderer.dispose() is async now (capability probe handles it). r186's CJS entry re-exports the ESM build, which Bun cannot require() alongside an ESM import of three, so a bun test preload evaluates three first and the R3F packages' CJS require("three") becomes a cache hit. @types/three stays 0.184.1.

How to test

  1. bun install && bun run test — all suites green (core 1436, viewer 308, editor 877, nodes 2370, capture-viewer 25, apps/editor 41).
  2. Cmd/Ctrl+S anywhere in the editor: no browser dialog; standalone app flushes the autosave.
  3. Capture mode: enter with a fractional camera FOV, HUD shows an integer; hold Alt in drone mode for fine moves.
  4. Place a potted plant on a shelf, drag it out over the floor: it lands on the floor. Add Floor 1, place an item there: the green box sits under the item.
  5. Place a modular kitchen preset: the run follows the cursor. Box-select 3 items, drag, press R: layout and facings rotate together.
  6. Room roof (wallHeight 0) over painted walls: no striped band at the wall top.
  7. Set a level to 5.3 m, add stairs: they reach the floor above; put a slab under them: rise shortens by the slab lift.

Screenshots / screen recording

Non-visual verification by unit tests; before/after screenshots are in the private-editor PR description.

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

🤖 Generated with Claude Code

https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc


Note

Medium Risk
Touches scene undo/commits, roof schema v3, and widespread placement math; dependency bump affects all Three/R3F paths but is mostly mechanical with test preloads.

Overview
Broad experience fix pass across core, editor, nodes, and viewer: shortcuts, placement, multi-select transforms, roofs, stairs, capture, plus three 0.186 with Bun test preloads.

Roofs gain support: { kind: 'walls' }: elevation resolves from walls under the footprint (or room), with RoofElevationSystem syncing Y after wall/slab/level edits (undo-aware, coalesced). Room/conical creation defaults to wall-follow; explicit Y or handle moves switch to custom level placement. Gable wall faces keep the base at 0 and raise the eave to at least 5 cm to avoid z-fighting. Footprint/elevation helpers move into @pascal-app/core (schema v3).

Stairs: level-destination totalRise subtracts the elected floor/slab base (matching deck stairs); tool/preview seed flight height from the storey; shared createDefaultStairSegment in core.

Editor placement & selection: Cmd/Ctrl+S saves (host onSaveShortcut or saveNow); floor-path items use live grid Y and level mesh offset on upper storeys; modular runs use footprint-centre absolute cursor placement; group move/rotate pivots on the mesh box, preserves yaw through translate, and engages R/T before threshold. Drone Alt = slow look/move; capture FOV clamped on arm; straight stairs without segments keep the merged body visible in edit mode.

Deps: monorepo three → 0.186.0; bunfig.toml preloads evaluate three before R3F CJS require in tests.

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

@pascal

pascal Bot commented Sep 9, 2026

Copy link
Copy Markdown

I hit an error while handling your request (Model unavailable on AI Gateway free tier: Free tier users do not have access to this model. Upgrade to paid credits at https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%3Fmodal%3Dtop-up for unrestricted…).

Please try again, rephrase, or reach out if it keeps failing.

Error id: 13d2cf37-d670-4189-9d4d-bd278918139c

@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/viewer/src/systems/roof/roof-system.tsx
@wass08
wass08 force-pushed the fix/experience-pass-sep9 branch from 2818400 to 504e546 Compare September 9, 2026 20:50

@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/viewer/src/systems/roof/roof-system.tsx Outdated

@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/stair/panel.tsx

@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/editor/src/components/tools/stair/stair-tool.tsx Outdated
Comment thread packages/editor/src/components/editor/group-move-3d.ts

@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/editor/src/components/tools/stair/stair-tool.tsx
wass08 and others added 17 commits September 9, 2026 18:14
…mera

armCaptureFov stored the live camera FOV verbatim, so fractional pose FOVs
printed float tails in the HUD and left the reset button enabled. Both
writers now share clampCaptureFov.

Alt holds the drone at 0.2x speed and look sensitivity for fine framing;
Shift stays the boost.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
The CSG degeneracy guard enforced its 5 cm minimum by lowering the shell
base, which for wallHeight-0 room roofs put the gable 4 cm inside the
wall and z-fought its faces. Raise the eave instead; mirror the floor in
the opening-placement frame and the shed inset panel.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
Capture-phase, always-on listener so the page-save dialog never appears.
Hosts can take the chord over via onSaveShortcut; the default flushes the
autosave through the existing executeSave path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
…previews to the level

Fresh (absolute) placement mapped the cursor to the node origin, so a
cabinet run landed |bounds.center| away from the pointer. Subtract the
rotated centre and keep it under the cursor across R/T. The registry
mover's box/sphere now ride the target level's stacked Y like the other
placement tools.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
Level-destination stairs returned the full floor-to-floor height even
when a slab lifted their base, so the top overshot the storey plane. The
resolver now subtracts the elected base for both destinations. The panel
exposes Follows storey / Custom rise for level stairs, and the stair tool
and landing toggle seed from the storey instead of a 2.5 m constant.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
…on the right storey

The floor-path Y was frozen at drag start (#638), so an item pulled off a
shelf kept the shelf height after reparenting. Read the live grid Y
instead. The cursor group, grid surface and facing pose now add the
level mesh's stacked Y, which the building-local tool group lacks.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
translateGroupPatches dropped the snapshots' yaw after a mid-gesture
rotation, so the layout orbited while every item kept its old facing and
the commit wrote the same. Carry rotation for vec3/scalar participants,
pivot every session on the shared mesh-box centre the idle shortcut
uses, and engage an armed session before rotating.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
No removed export is used and every peer range admits r186. Two
adjustments: Renderer.dispose() is async now, so the capability probe
swallows its rejection; and r186's CommonJS entry re-exports the ES
module, which Bun cannot require() while the same process imports three
as ESM. A bun test preload steers fiber/drei/maath/meshline (no exports
map, CJS main) to their module builds, the way bundlers already resolve
them. Types stay on 0.184.1 (0.185 types OOM tsgo).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
Bun's plugin onResolve does not run for static imports, so steering the
R3F packages to their module builds never applied in CI (isolated linker)
and the CJS require("three") kept racing the ESM import. Evaluating the
package's own three copy first makes the later require() a cache hit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
The 5 cm CSG floor lifted only the outer shell, so a flat zero-height
roof would have ended up with a solid cap under the deck. Compute the
lift once and apply it to every volume.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
…andle Cmd+S

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
…s eave

A shell-derived lift left overhanging deck cutters with a negative eave.
Clamp each volume's top the way main did, just at 5 cm and without the
base sink, so cutters stay level with the shells they carve.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
The rise-mode block already exposes the Rise field in custom mode; the
geometry copy wrote totalRise behind the Follows storey toggle.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
…re when R/T cannot engage

The stair tool seeded the flight from the storey height alone, a slab
thickness too tall until syncStairRises caught up; it now subtracts the
drop point's elected base like the resolver. A failed engage() on R/T no
longer tears down the pointer listeners.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
@wass08
wass08 force-pushed the fix/experience-pass-sep9 branch from 5e77007 to 5526120 Compare September 9, 2026 22:14

@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/editor/src/components/tools/stair/stair-tool.tsx
Comment thread packages/viewer/src/lib/renderer-capability.ts Outdated
wass08 and others added 2 commits September 9, 2026 18:30
…() before the WebGL fallback

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc

@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/editor/src/components/editor/group-transform-shared.ts
…r each R/T

Rotating the previous axis-aligned fit inflated the anchors every step.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
wass08 and others added 2 commits September 10, 2026 09:44
… use the shared display name

A curved stair switched to straight had no stair-segment child and drew
nothing (and vanished on select). The type change now creates a default
flight in the same history step and the viewer falls back to that flight
for already-broken scenes. Stair and elevator panels label levels the way
the level switcher does, and the rise toggle reads Follows level like walls.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
Room roofs computed their elevation once at creation, so a later custom
wall height left the roof at the storey plane. Roofs now remember their
source walls and a core system re-derives position[1] (highest top,
clamped to the level floor) on wall/slab/level edits, history-paused like
the stair rise sync. Moving the roof by hand detaches it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc

@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/core/src/systems/stair/stair-rise.ts
Comment thread packages/core/src/utils/clone-scene-graph.ts Outdated
wass08 and others added 2 commits September 10, 2026 10:02
…al rise

On a follows-level stair the sync handed the edited height straight back,
so the segment slider did nothing. The edit now also writes totalRise
(the stair becomes Custom rise, as editing Rise on its own panel does).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
Walls shorter than the level (2.5 m in a 3 m storey) left a gap because
the roof elevation was clamped to its level floor. Follow the wall top.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc

@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/stair-segment/panel.tsx
Replace the source-wall id list with support.kind 'walls': room and
conical roofs are created following, the system resolves the enclosure
under the roof centre on the level below and writes the highest wall top
(unclamped), an explicit Y edit or vertical handle drag flips the roof to
custom, and the panel offers Follows walls / Custom like walls do. No
migration; existing roofs stay custom until the user opts in.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc

@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 2 potential issues.

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 947c439. Configure here.

Comment thread packages/core/src/systems/roof/roof-elevation.ts Outdated
node?.type === 'building' ||
node?.type === 'site'
)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Roof sync skips segment-only edits

Low Severity

isElevationRelevant does not treat roof-segment as relevant, but conical wall-follow matching uses each segment's position, width, and type. Resizing or moving a conical segment therefore does not reschedule elevation sync, so the roof Y can stay locked to a wall it no longer matches.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 947c439. Configure here.

wass08 and others added 2 commits September 10, 2026 10:57
…m membership

A room missing a wall, or an L-room whose centre falls outside, left the
roof frozen. Walls whose band overlaps a segment footprint on the level
below now count; segment-less roofs keep the point-in-room lookup.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
…walls on the roof's own level too

Armed on the walls' level, the tool parented the roof to that level and
the follow rule only looked one storey down, so a Floor 1 roof dropped to
the Level 0 wall tops. The roof now goes to the level above the walls
when one exists (top floor keeps it on the walls' level), and the
resolver considers walls on the roof's level and the one below.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hQVFZ62S1qJDsBM9ferYc
@wass08
wass08 merged commit 69f2552 into main Sep 10, 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.

1 participant