refactor: release-review cleanup for roof wall openings

Dual review pass (Claude multi-angle + Codex release-quality). One
correctness fix and the agreed do-now cleanups:

- fix: clone-scene-graph remaps roofSegmentId like wallId in both
  clone paths — duplicated scenes/levels kept pointing roof-hosted
  children at the original segments.
- extract the settled, stateless roof target/cursor math shared by the
  four door/window tools into shared/roof-wall-opening-placement.ts
  (resolveRoofWallOpeningTarget + getRoofWallOpeningCursorPose +
  worldToSelectedBuildingLocal); tools keep the stateful lifecycle
  (drafts, undo/temporal, commit field lists). −199 net lines.
- rename host-generic state: currentWallId→currentHostId,
  markWallDirty→markHostDirty (they hold segment ids too); capability
  cascadesViaHostSegment→dirtyHandledByOwnSystem (behavior-facing,
  before the public API hardens).
- drop getRoofAccessoryKinds from core's public API — its only caller
  was the standalone Build tab, which now enumerates the registry
  inline with its app-specific filter.
- window move-tool uses the shared stripPlacementMetadataFlags; stale
  "segment-local" comment fixed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Aymeric Rabot
2026-06-10 14:33:13 -04:00
co-authored by Claude Fable 5
parent b8dfa90762
commit aa3b0ef758
16 changed files with 349 additions and 435 deletions
@@ -55,6 +55,7 @@ import {
NO_RAYCAST,
} from './handles/handle-arrow'
import { type HandleDragControls, useHandleDrag } from './handles/use-handle-drag'
// Pooled scratch for the handle rig's world-relative pose mapping.
const _rigRelative = new Matrix4()
const _rigScratchScale = new Vector3()
@@ -363,7 +363,7 @@ type RoofWallTarget = {
segment: RoofSegmentNode
faceId: RoofWallFaceId
faceYaw: number
/** Stored node position: segment-local, y = bottom edge. */
/** Stored node position: FACE-LOCAL, y = bottom edge. */
position: [number, number, number]
/** Face-coord center of the placed rect (for the overlap guard). */
centerU: number
@@ -1062,9 +1062,8 @@ export function usePlacementCoordinator(config: PlacementCoordinatorConfig): Rea
// push (z = thickness/2 off the face frame's mid-plane) so the
// drag preview doesn't sink into the wall until commit.
if (asset.attachTo === 'wall-side' && placementState.current.roofSegmentId) {
const segment = useScene.getState().nodes[
placementState.current.roofSegmentId as AnyNodeId
]
const segment =
useScene.getState().nodes[placementState.current.roofSegmentId as AnyNodeId]
if (segment?.type === 'roof-segment') {
mesh.position.z = (segment.wallThickness ?? 0.1) / 2
}