Wassim SAMAD 76794ceb7d Wire shelf + spawn placement polish: SFX, cursor, sidebar, selection
User-visible follow-ups after first running the Phase 2 spike.

Spawn tool now matches legacy UX:
- CursorSphere from @pascal-app/editor for the placement indicator
  (ring + line + tool-icon tooltip) — was a plain sphere mesh.
- Emits sfx:structure-build on commit + setTool(null) + setMode
  ('select') to exit build mode, matching legacy spawn-tool.

Shelf tool placement:
- Emits sfx:structure-build on commit.
- Cursor preview now shows top board + brackets (was just the top),
  matching what gets placed.

Shelf selectable from the 3D canvas:
- ShelfEvent type added to @pascal-app/core/events/bus.
- 'shelf' added to NodeConfig in useNodeEvents.
- ShelfRenderer wires `useNodeEvents(node, 'shelf')` handlers onto
  every mesh. Clicks/hovers now bubble through the editor's selection
  manager and update useViewer.selection.

Shelf appears in the sidebar:
- ShelfTreeNode component (mirrors spawn-tree-node's shape +
  selection/hover/rename wiring; lucide Layers icon).
- TreeNode dispatcher adds a `case 'shelf':` arm.

Framework changes:
- @pascal-app/editor exports CursorSphere alongside triggerSFX.
- @pascal-app/nodes now declares @pascal-app/editor as peer/dev dep.

Pre-existing typecheck errors in @pascal-app/editor (ceiling-tree-node,
fence-tree-node, slab-tree-node, scene.ts) are unchanged — present on
main and not introduced by this commit.

630 tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 14:55:03 -04:00

171 lines
4.7 KiB
TypeScript

export type {
BuildingEvent,
CameraControlEvent,
CameraControlFitSceneEvent,
CeilingEvent,
ColumnEvent,
DoorEvent,
ElevatorEvent,
EventSuffix,
FenceEvent,
GridEvent,
ItemEvent,
LevelEvent,
NodeEvent,
RoofEvent,
RoofSegmentEvent,
ShelfEvent,
SiteEvent,
SlabEvent,
SpawnEvent,
StairEvent,
StairSegmentEvent,
WallEvent,
WindowEvent,
ZoneEvent,
} from './events/bus'
export { emitter, eventSuffixes } from './events/bus'
export {
sceneRegistry,
useRegistry,
} from './hooks/scene-registry/scene-registry'
export { pointInPolygon, spatialGridManager } from './hooks/spatial-grid/spatial-grid-manager'
export {
initSpatialGridSync,
resolveLevelId,
} from './hooks/spatial-grid/spatial-grid-sync'
export { useSpatialQuery } from './hooks/spatial-grid/use-spatial-query'
export { loadAssetUrl, saveAsset } from './lib/asset-storage'
export {
clampDoorOperationState,
getDoorRenderOpenAmount,
getGarageVisibleOpeningRatio,
isOperationDoorType,
SECTIONAL_GARAGE_RENDER_OPEN_SCALE,
} from './lib/door-operation'
export { getRenderableSlabPolygon } from './lib/slab-polygon'
export {
type AutoSlabSyncPlan,
detectSpacesForLevel,
initSpaceDetectionSync,
planAutoSlabsForLevel,
type Space,
wallTouchesOthers,
} from './lib/space-detection'
export {
getCatalogMaterialById,
getLibraryMaterialIdFromRef,
getMaterialPresetByRef,
getMaterialsForCategory,
LIBRARY_MATERIAL_REF_PREFIX,
MATERIAL_CATALOG,
MATERIAL_CATEGORIES,
type MaterialCatalogItem,
type MaterialCategory,
toLibraryMaterialRef,
} from './material-library'
export * from './registry'
export * from './schema'
export * from './services'
export {
getSceneHistoryPauseDepth,
pauseSceneHistory,
resetSceneHistoryPauseDepth,
resumeSceneHistory,
} from './store/history-control'
export {
type ControlValue,
type DoorAnimationState,
type DoorInteractiveState,
type ElevatorInteractiveState,
type ElevatorPhase,
type ItemInteractiveState,
useInteractive,
type WindowAnimationState,
type WindowInteractiveState,
} from './store/use-interactive'
export {
default as useLiveNodeOverrides,
type LiveNodeOverrides,
} from './store/use-live-node-overrides'
export { default as useLiveTransforms, type LiveTransform } from './store/use-live-transforms'
export { clearSceneHistory, default as useScene } from './store/use-scene'
export { resolveElevatorDispatchTarget } from './systems/elevator/elevator-dispatch'
export {
type ElevatorDoorSide,
getElevatorCabCenterZ,
getElevatorCabDepth,
getElevatorCabWidth,
getElevatorDoorLeafSides,
getElevatorDoorLeafWidth,
getElevatorDoorLeafX,
getElevatorShaftDepth,
getElevatorShaftWallThickness,
getElevatorShaftWidth,
getResolvedElevatorDoorPanelStyle,
getResolvedElevatorDoorStyle,
getResolvedElevatorShaftStyle,
} from './systems/elevator/elevator-geometry'
export { syncAutoElevatorOpenings } from './systems/elevator/elevator-opening-sync'
export { ElevatorOpeningSystem } from './systems/elevator/elevator-opening-system'
export {
createElevatorInteractiveState,
openElevatorDoor,
openElevatorDoorState,
queueElevatorRequest,
requestElevatorLevel,
stepElevatorRuntimeState,
stepElevatorRuntimes,
} from './systems/elevator/elevator-runtime'
export { ElevatorRuntimeSystem } from './systems/elevator/elevator-runtime-system'
export {
DEFAULT_ELEVATOR_LEVEL_HEIGHT,
type ElevatorLevelEntry,
getElevatorLevelHeight,
resolveElevatorBuildingLevels,
resolveElevatorLevels,
resolveElevatorServiceLevelIds,
resolveElevatorServiceLevels,
} from './systems/elevator/elevator-service'
export { syncAutoStairOpenings } from './systems/stair/stair-opening-sync'
export {
getClampedWallCurveOffset,
getMaxWallCurveOffset,
getWallChordFrame,
getWallCurveFrameAt,
getWallCurveLength,
getWallMidpointHandlePoint,
getWallStraightSnapOffset,
getWallSurfacePolygon,
isCurvedWall,
normalizeWallCurveOffset,
sampleWallCenterline,
} from './systems/wall/wall-curve'
export {
DEFAULT_WALL_HEIGHT,
DEFAULT_WALL_THICKNESS,
getWallPlanFootprint,
getWallThickness,
} from './systems/wall/wall-footprint'
export {
calculateLevelMiters,
getAdjacentWallIds,
getWallMiterBoundaryPoints,
type Point2D,
pointToKey,
type WallMiterBoundaryPoints,
type WallMiterData,
} from './systems/wall/wall-mitering'
export {
constrainWallMoveDeltaToAxis,
getPerpendicularWallMoveAxis,
planWallMoveJunctions,
type WallMoveAxis,
type WallMoveBridgePlan,
type WallMoveJunctionPlan,
type WallPlanPoint,
} from './systems/wall/wall-move'
export type { SceneGraph } from './utils/clone-scene-graph'
export { cloneLevelSubtree, cloneSceneGraph, forkSceneGraph } from './utils/clone-scene-graph'
export { isObject } from './utils/types'