Skip to content

Panel-owned X-ray wall-mode takeover; export the headless engine surface - #2

Open
JonathanNus wants to merge 1 commit into
pascalorg:masterfrom
JonathanNus:fix/xray-wall-mode-lifecycle
Open

Panel-owned X-ray wall-mode takeover; export the headless engine surface#2
JonathanNus wants to merge 1 commit into
pascalorg:masterfrom
JonathanNus:fix/xray-wall-mode-lifecycle

Conversation

@JonathanNus

Copy link
Copy Markdown

The bug

Leaving the Bones panel keeps the host stuck in the X-ray view. The wall-mode takeover in framing/renderer.tsx writes useViewer.setWallMode('down') from an effect whose cleanup is tied to the node's lifetime, but the intent ("get the walls out of the way while I'm looking at the X-ray") is tied to the panel's. Sidebar tab switches unmount the panel, not the renderer, so nothing restores — and since wallMode is a persisted viewer preference, the stuck view survives page reloads. The sharpest edge: merely opening a saved scene that contains a bones:framing node blanks the host's walls before the user touches anything.

This also runs against the plugin appearance contract (#625): the renderer was writing host presentation state, which the contract says plugins subscribe to read-only.

The fix

  • The save/set/restore state machine moves to src/view-takeover.ts, viewer-import-free so it tests headlessly. Behaviours pinned by unit tests: restores the previous mode on release; takes no ownership when the user already had walls down; never stomps a mode the user picked while the panel was open; double-engage keeps the original mode.
  • panel.tsx engages it while a live X-ray exists (seeThrough !== false) and releases on unmount — i.e. exactly when the user leaves.
  • framing/renderer.tsx no longer touches host state; it only draws.

Long term, a scoped, owner-tokened presentation override on useViewer (push/pop) would be the contract-sanctioned way to do this — happy to discuss; this PR just makes the current behaviour match the current contract.

Also: headless engine exports

index.ts now exports the pure derivation surface (computeLevel, computeLevelUncached, computeTakeoff, cutList, row/member types, extractLevels, extractRoofs). The pipeline is already pure and fully tested headlessly; the exports let a host estimator compute the same quantities the panel shows — per level, without mounting anything. computeLevelUncached is exported so a whole-building rollup loop doesn't thrash the 1-deep memo the panel and 3D renderer share. No behaviour change for existing consumers.

Tests

499 pass, 0 fail (bun test); tsc --noEmit clean. New: src/view-takeover.test.ts (6 tests).

…ss engine

Two changes a host needs:

1. The wall-mode takeover ('down' while X-raying) moves from the 3D
   renderer to the panel. The renderer lives as long as the bones:framing
   node does, so leaving the Bones tab kept the host's walls hidden — and
   because wallMode is a persisted viewer preference, the stuck view
   survived reloads, and merely OPENING a scene that contained an X-ray
   node blanked the walls. The panel unmounts exactly when the user
   leaves, which is the lifetime this intent actually has. The state
   machine lives in view-takeover.ts (viewer-import-free, unit tested:
   restore on leave, never stomp a user-picked mode, no ownership when
   walls were already down). Also aligns with the plugin appearance
   contract (#625): the renderer no longer writes host presentation state.

2. index.ts exports the pure derivation surface — computeLevel,
   computeLevelUncached, computeTakeoff, cutList, the row/member types,
   extractLevels/extractRoofs — so a host estimator can compute the same
   quantities the panel shows without mounting anything.
   computeLevelUncached is exported because a whole-building rollup loop
   would otherwise thrash the 1-deep memo the panel and renderer share.
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