'use client' import { type AnyNode, type SpawnNode, useLiveTransforms, useScene } from '@pascal-app/core' import { ActionButton, ActionGroup, PanelSection, PanelWrapper, SliderControl, triggerSFX, useEditor, } from '@pascal-app/editor' import { useViewer } from '@pascal-app/viewer' import { Move, Trash2 } from 'lucide-react' import { useCallback, useEffect, useState } from 'react' export default function SpawnPanel() { const selectedId = useViewer((s) => s.selection.selectedIds[0]) const setSelection = useViewer((s) => s.setSelection) const updateNode = useScene((s) => s.updateNode) const deleteNode = useScene((s) => s.deleteNode) const setMovingNode = useEditor((s) => s.setMovingNode) const node = useScene((s) => selectedId ? (s.nodes[selectedId as AnyNode['id']] as SpawnNode | undefined) : undefined, ) const [draftRotation, setDraftRotation] = useState(null) useEffect(() => { if (!(node && node.type === 'spawn')) { setDraftRotation(null) return } setDraftRotation(node.rotation) useLiveTransforms.getState().clear(node.id) }, [node?.id, node?.rotation, node?.type]) const handleUpdate = useCallback( (updates: Partial) => { if (!(selectedId && node)) return updateNode(selectedId as AnyNode['id'], updates) }, [node, selectedId, updateNode], ) const handleRotationChange = useCallback( (degrees: number) => { if (!(node && selectedId)) return const nextRotation = (degrees * Math.PI) / 180 setDraftRotation(nextRotation) useLiveTransforms.getState().set(selectedId as AnyNode['id'], { position: [...node.position], rotation: nextRotation, }) }, [node, selectedId], ) const commitRotation = useCallback( (degrees: number) => { if (!(node && selectedId)) return const nextRotation = (degrees * Math.PI) / 180 useLiveTransforms.getState().clear(selectedId as AnyNode['id']) setDraftRotation(nextRotation) if (Math.abs(nextRotation - node.rotation) > 1e-6) { updateNode(selectedId as AnyNode['id'], { rotation: nextRotation }) } }, [node, selectedId, updateNode], ) const handleClose = useCallback(() => { setSelection({ selectedIds: [] }) }, [setSelection]) const handleMove = useCallback(() => { if (!node) return triggerSFX('sfx:item-pick') setMovingNode(node) setSelection({ selectedIds: [] }) }, [node, setMovingNode, setSelection]) const handleDelete = useCallback(() => { if (!selectedId) return triggerSFX('sfx:structure-delete') deleteNode(selectedId as AnyNode['id']) setSelection({ selectedIds: [] }) }, [deleteNode, selectedId, setSelection]) if (!(node && node.type === 'spawn' && selectedId)) return null const rotationDegrees = Math.round(((draftRotation ?? node.rotation) * 180) / Math.PI) const storedRotationDegrees = Math.round((node.rotation * 180) / Math.PI) return ( handleUpdate({ position: [value, node.position[1], node.position[2]] }) } precision={2} step={0.01} unit="m" value={Math.round(node.position[0] * 100) / 100} /> handleUpdate({ position: [node.position[0], value, node.position[2]] }) } precision={2} step={0.01} unit="m" value={Math.round(node.position[1] * 100) / 100} /> handleUpdate({ position: [node.position[0], node.position[1], value] }) } precision={2} step={0.01} unit="m" value={Math.round(node.position[2] * 100) / 100} /> } label="Move" onClick={handleMove} /> } label="Delete" onClick={handleDelete} /> ) }