shortcuts and deletion

This commit is contained in:
wass08
2026-01-23 10:30:24 +09:00
parent cc9d9916b7
commit 4f20586ae9
4 changed files with 132 additions and 9 deletions
+3
View File
@@ -9,6 +9,7 @@ import { MathUtils, type Mesh } from 'three'
import { color, float, fract, fwidth, mix, positionLocal } from 'three/tsl'
import { MeshBasicNodeMaterial } from 'three/webgpu'
import { useKeyboard } from '@/hooks/use-keyboard'
import { ToolManager } from '../tools/tool-manager'
import { ActionMenu } from '../ui/action-menu'
import { SidebarProvider } from '../ui/primitives/sidebar'
@@ -20,6 +21,8 @@ initSpatialGridSync()
useScene.getState().loadScene()
export default function Editor() {
useKeyboard()
return (
<div className="w-full h-full">
{/* <LevelModeSwitcher /> */}
@@ -243,6 +243,36 @@ export const ItemTool: React.FC = () => {
emitter.on('wall:click', onWallClick)
emitter.on('wall:leave', onWallLeave)
// Keyboard rotation handlers
const ROTATION_STEP = Math.PI / 2 // 90 degrees
const onKeyDown = (event: KeyboardEvent) => {
if (!draftItem.current) return
let rotationDelta = 0
if (event.key === 'r' || event.key === 'R') {
rotationDelta = ROTATION_STEP // Counter-clockwise
} else if (event.key === 't' || event.key === 'T') {
rotationDelta = -ROTATION_STEP // Clockwise
}
if (rotationDelta !== 0) {
event.preventDefault()
const currentRotation = draftItem.current.rotation
const newRotationY = (currentRotation[1] ?? 0) + rotationDelta
draftItem.current.rotation = [currentRotation[0], newRotationY, currentRotation[2]]
useScene.getState().updateNode(draftItem.current.id, {
rotation: draftItem.current.rotation,
})
// Update cursor rotation to match
cursorRef.current.rotation.y = newRotationY
checkCanPlace()
}
}
window.addEventListener('keydown', onKeyDown)
const setupBoundingBox = () => {
const boxGeometry = new BoxGeometry(
selectedItem.dimensions[0],
@@ -265,6 +295,7 @@ export const ItemTool: React.FC = () => {
emitter.off('wall:leave', onWallLeave)
emitter.off('wall:click', onWallClick)
emitter.off('wall:move', onWallMove)
window.removeEventListener('keydown', onKeyDown)
}
}, [selectedItem, canPlaceOnFloor, canPlaceOnWall])
+59
View File
@@ -0,0 +1,59 @@
import { useScene } from '@pascal-app/core'
import { useViewer } from '@pascal-app/viewer'
import { useEffect } from 'react'
import useEditor from '@/store/use-editor'
export const useKeyboard = () => {
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
// Don't handle shortcuts if user is typing in an input
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) {
return
}
if (e.key === 'Escape') {
e.preventDefault()
// Emit tool:cancel event - each tool handles its own cancellation logic
// if (useEditor.getState().controlMode === 'building') {
// emitter.emit('tool:cancel', undefined)
// }
// if (selectedNodeIds.length > 0) {
// handleClear()
// }
} else if (e.key === '1' && !e.metaKey && !e.ctrlKey) {
e.preventDefault()
useEditor.getState().setPhase('site')
useEditor.getState().setMode('select')
} else if (e.key === '2' && !e.metaKey && !e.ctrlKey) {
e.preventDefault()
useEditor.getState().setPhase('structure')
useEditor.getState().setMode('select')
} else if (e.key === '3' && !e.metaKey && !e.ctrlKey) {
e.preventDefault()
useEditor.getState().setPhase('furnish')
useEditor.getState().setMode('select')
} if (e.key === 'v' && !e.metaKey && !e.ctrlKey) {
e.preventDefault()
useEditor.getState().setMode('select')
} else if (e.key === 'z' && (e.metaKey || e.ctrlKey)) {
e.preventDefault()
useScene.temporal.getState().undo()
} else if (e.key === 'Z' && e.shiftKey && (e.metaKey || e.ctrlKey)) {
e.preventDefault()
useScene.temporal.getState().redo()
} else if (e.key === 'Delete' || e.key === 'Backspace') {
e.preventDefault()
const selectedNodeIds = useViewer.getState().selection.selectedIds
if (selectedNodeIds.length > 0) {
useScene.getState().deleteNodes(selectedNodeIds)
}
}
}
window.addEventListener('keydown', handleKeyDown)
return () => window.removeEventListener('keydown', handleKeyDown)
}, [])
return null
}