Add preview slot to NodeDefinition; show translucent shape during move
User feedback: the move tool's CursorSphere is just a vertical line,
hard to tell what you're moving. Placement gets a translucent shape
that follows the cursor; move should too.
NodeDefinition.preview?: () => Promise<{ default: ComponentType<{ node }> }>
opt-in lazy component that renders a translucent ghost of the node.
Used by:
- The placement tool (ShelfTool) — renders the preview at the cursor
position so the user sees the shape they're placing.
- The move tool (MoveRegistryNodeTool) — renders the preview at the
drag target alongside the CursorSphere. Plus the original node is
also dragged via live transforms, so the user sees both: the actual
node moving + a translucent ghost at the same spot.
Implementation:
- New nodes/shelf/preview.tsx: ShelfPreview component. Renders the
same shape as ShelfRenderer but `transparent: true, opacity: 0.5`.
- shelfDefinition.preview = () => import('./preview').
- ShelfTool's placement preview now uses <ShelfPreview node={defaults} />
instead of an inline copy of the box geometry.
- MoveRegistryNodeTool lazy-loads `def.preview` (cached by loader,
Suspense-wrapped). If a kind doesn't define `preview`, only the
CursorSphere shows — matches today's behavior.
Phase 4 may merge `preview` with `renderer` behind an `opacity` prop
so kinds don't duplicate JSX between the solid and translucent
versions; until then defining `preview` is opt-in and one extra file.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
a090c22f42
commit
e84b1ec8bc
@@ -0,0 +1,50 @@
|
||||
'use client'
|
||||
|
||||
import { useMemo } from 'react'
|
||||
import { Color } from 'three'
|
||||
import type { ShelfNode } from './schema'
|
||||
|
||||
/**
|
||||
* Translucent preview of a shelf. Used by:
|
||||
* - The placement tool's cursor (ShelfTool) — at the cursor position
|
||||
* - The move tool (MoveRegistryNodeTool) — at the drag target position
|
||||
*
|
||||
* Renders the same primitives as the actual ShelfRenderer, but with
|
||||
* `transparent: true, opacity: 0.5` so the user can see what they're
|
||||
* placing/moving without it being a hard solid.
|
||||
*/
|
||||
const ShelfPreview = ({ node }: { node: ShelfNode }) => {
|
||||
const color = useMemo(() => new Color(node.color), [node.color])
|
||||
const topY = node.height + node.thickness / 2
|
||||
|
||||
const inset = Math.min(0.12, node.width / 6)
|
||||
const bracketHeight = Math.max(0.01, node.height)
|
||||
const bracketWidth =
|
||||
node.bracketStyle === 'industrial'
|
||||
? Math.max(0.04, node.depth * 0.2)
|
||||
: Math.max(0.02, node.depth * 0.12)
|
||||
const bracketDepth = node.bracketStyle === 'industrial' ? node.depth * 0.95 : node.depth * 0.7
|
||||
|
||||
return (
|
||||
<group>
|
||||
<mesh position={[0, topY, 0]}>
|
||||
<boxGeometry args={[node.width, node.thickness, node.depth]} />
|
||||
<meshStandardMaterial color={color} transparent opacity={0.5} />
|
||||
</mesh>
|
||||
{node.bracketStyle !== 'hidden' && (
|
||||
<>
|
||||
<mesh position={[-(node.width / 2 - inset), bracketHeight / 2, 0]}>
|
||||
<boxGeometry args={[bracketWidth, bracketHeight, bracketDepth]} />
|
||||
<meshStandardMaterial color={color} transparent opacity={0.5} />
|
||||
</mesh>
|
||||
<mesh position={[node.width / 2 - inset, bracketHeight / 2, 0]}>
|
||||
<boxGeometry args={[bracketWidth, bracketHeight, bracketDepth]} />
|
||||
<meshStandardMaterial color={color} transparent opacity={0.5} />
|
||||
</mesh>
|
||||
</>
|
||||
)}
|
||||
</group>
|
||||
)
|
||||
}
|
||||
|
||||
export default ShelfPreview
|
||||
Reference in New Issue
Block a user