Files
editor/packages/nodes/src/item/tool.tsx
T
Wassim SAMADandClaude Opus 4.8 68e5c6ca67 feat(editor): unify placement/move facing triangle into one editor-side renderer
Every placement and move path now publishes its ghost pose to a single
`useFacingPose` store, drawn by one editor-side `<FacingPoseIndicator>` overlay,
instead of each path drawing its own triangle (which left the nodes-package and
PlacementBox paths invisible):

- column/shelf presets + all moves (PlacementBox via move-registry, and
  DragBoundingBox) now publish the pose, so the triangle finally shows
- stair create + move use a declarative `facingIndicator: { reversed: true }`
  (new registry resolver) so the triangle sits before the entry pointing out —
  resolved in one place, so create and move match automatically
- stair placement defaults to single and respects the shared `point`
  continuation (C) toggle, like the other placement tools

Checkpoint on the placement-interaction epic: also carries the in-flight
continuation-profile extraction (lib/continuation), grid surface (item #8), and
HUD work. Door/window still render their own legacy inline triangle and are
migrated to the overlay next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 21:48:58 -04:00

52 lines
1.9 KiB
TypeScript

'use client'
import type { AssetInput } from '@pascal-app/core'
import { triggerSFX, useDraftNode, useEditor, usePlacementCoordinator } from '@pascal-app/editor'
/**
* Registry-driven item placement tool. Mounted by `ToolManager` when
* `useEditor.tool === 'item'` (the catalog picker is what selects which
* asset; this tool handles the cursor follow + click-to-commit flow).
*
* Wraps the same `usePlacementCoordinator` + `useDraftNode` primitives
* the move-tool uses. The placement coordinator runs surface strategies
* (floor / wall / ceiling / item-surface) so the same cursor logic
* handles wall-mounted artwork, floor furniture, ceiling fans, and
* nested items on tables.
*
* Replaces the legacy `editor/src/components/tools/item/item-tool.tsx`.
* The `tools` map in `tool-manager.tsx` no longer needs an `item:` entry
* — `getRegistryTool('item')` finds this through `def.tool`.
*/
function ItemPlacementContent({ selectedItem }: { selectedItem: AssetInput }) {
const draftNode = useDraftNode()
const cursor = usePlacementCoordinator({
asset: selectedItem,
draftNode,
initDraft: (gridPosition) => {
// Only floor items get a draft on mount; wall / ceiling items are
// created lazily by the placement coordinator when the cursor
// enters a surface (so the draft doesn't appear at world origin
// before the first move event).
if (selectedItem && !selectedItem.attachTo) {
draftNode.create(gridPosition, selectedItem)
}
},
onCommitted: () => {
triggerSFX('sfx:item-place')
return useEditor.getState().getContinuation('point') === 'repeat'
},
})
return <>{cursor}</>
}
function ItemTool() {
const selectedItem = useEditor((state) => state.selectedItem)
if (!selectedItem) return null
return <ItemPlacementContent selectedItem={selectedItem} />
}
export default ItemTool