feat(editor): preset placement polish — params, move tool, shadows, isolation (#357)

* feat(editor): preset placement polish — params, move tool, shadows

- wall/slab/ceiling/roof create paths consume `toolDefaults` so template
  presets build with their saved params; cleared on tool unmount
- wall draw preview reflects the preset's height/thickness (+ HUD labels)
- box-select picks up registry-selectable kinds (shelf) via bbox
- registry + column move tools: snap to the active grid step, R/T rotation,
  and ignore the stray trailing click that armed the move (no double-place)
- shelf geometry casts + receives shadows like fence/slab

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(viewer): expose isIsolationActive() for isolation-aware consumers

Tracks whether an isolation filter is currently applied and exposes it so
hosts can avoid acting on the partial view — e.g. skipping project-thumbnail
autosave while a single subtree is isolated (preset capture).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Wassim SAMAD
2026-06-02 10:17:15 -04:00
committed by GitHub
co-authored by Claude Opus 4.8
parent 8abfc94b99
commit f7ff60561e
11 changed files with 277 additions and 46 deletions
+15 -2
View File
@@ -1,7 +1,13 @@
'use client'
import { emitter, type GridEvent, type LevelNode, useScene } from '@pascal-app/core'
import { CursorSphere, EDITOR_LAYER, markToolCancelConsumed, triggerSFX } from '@pascal-app/editor'
import {
CursorSphere,
EDITOR_LAYER,
markToolCancelConsumed,
triggerSFX,
useEditor,
} from '@pascal-app/editor'
import { useViewer } from '@pascal-app/viewer'
import { useEffect, useMemo, useRef, useState } from 'react'
import { BufferGeometry, DoubleSide, type Group, type Line, Shape, Vector3 } from 'three'
@@ -46,7 +52,10 @@ function commitCeilingDrawing(levelId: LevelNode['id'], points: Array<[number, n
const { createNode, nodes } = useScene.getState()
const ceilingCount = Object.values(nodes).filter((n) => n.type === 'ceiling').length
const name = `Ceiling ${ceilingCount + 1}`
const ceiling = CeilingNode.parse({ name, polygon: points })
// A placed ceiling preset seeds `toolDefaults.ceiling` (thickness, height,
// material, …) before the tool activates; the drawn polygon always wins.
const defaults = useEditor.getState().toolDefaults.ceiling ?? {}
const ceiling = CeilingNode.parse({ ...defaults, name, polygon: points })
createNode(ceiling, levelId)
triggerSFX('sfx:structure-build')
return ceiling.id
@@ -70,6 +79,10 @@ export const CeilingTool: React.FC = () => {
const previousSnappedPointRef = useRef<[number, number] | null>(null)
const shiftPressed = useRef(false)
// Clear preset-seeded defaults on deactivation so a later manual ceiling
// draw isn't built with a stale preset's parameters. Unmount-only.
useEffect(() => () => useEditor.getState().setToolDefaults('ceiling', null), [])
const verticalGeo = useMemo(
() =>
new BufferGeometry().setFromPoints([