fix raycaster layers, polygon drag, slider undo, and grid events

- Enable EDITOR_LAYER on R3F raycaster for proper hit detection
- Fix polygon editor drag using refs and direct position passing
- Handle pointercancel events and fix pointerId access in polygon editor
- Fix slider undo by reverting to start value before resuming temporal state
- Allow grid:move during camera drag so polygon editor works while panning
- Bump better-auth to ^1.5.2 and turbo to ^2.8.12
- Remove explicit return type on createAuth for better inference

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Aymeric Rabot
2026-03-03 14:41:19 -05:00
co-authored by Claude Opus 4.6
parent efeea1719c
commit 48979ec014
9 changed files with 251 additions and 43 deletions
@@ -16,9 +16,11 @@ export const CustomCameraControls = () => {
const firstLoad = useRef(true)
const camera = useThree((state) => state.camera)
const raycaster = useThree((state) => state.raycaster)
useEffect(() => {
camera.layers.enable(EDITOR_LAYER)
}, [camera])
raycaster.layers.enable(EDITOR_LAYER)
}, [camera, raycaster])
useEffect(() => {
let targetY = 0
@@ -1,4 +1,5 @@
import { emitter, type GridEvent, sceneRegistry } from '@pascal-app/core'
import { useViewer } from '@pascal-app/viewer'
import { createPortal } from '@react-three/fiber'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { BufferGeometry, Float32BufferAttribute, type Mesh } from 'three'
@@ -49,6 +50,13 @@ export const PolygonEditor: React.FC<PolygonEditorProps> = ({
// Local state for dragging
const [dragState, setDragState] = useState<DragState | null>(null)
const [previewPolygon, setPreviewPolygon] = useState<Array<[number, number]> | null>(null)
const previewPolygonRef = useRef<Array<[number, number]> | null>(null)
// Keep ref in sync
useEffect(() => {
previewPolygonRef.current = previewPolygon
}, [previewPolygon])
const [hoveredVertex, setHoveredVertex] = useState<number | null>(null)
const [hoveredMidpoint, setHoveredMidpoint] = useState<number | null>(null)
const [cursorPosition, setCursorPosition] = useState<[number, number]>([0, 0])
@@ -80,23 +88,25 @@ export const PolygonEditor: React.FC<PolygonEditorProps> = ({
// Update vertex position using grid cursor position
const handleVertexDrag = useCallback(
(vertexIndex: number) => {
const basePolygon = previewPolygon ?? polygon
const newPolygon = [...basePolygon]
newPolygon[vertexIndex] = cursorPosition
setPreviewPolygon(newPolygon)
(vertexIndex: number, position: [number, number]) => {
setPreviewPolygon((prev) => {
const basePolygon = prev ?? polygon
const newPolygon = [...basePolygon]
newPolygon[vertexIndex] = position
return newPolygon
})
},
[cursorPosition, previewPolygon, polygon],
[polygon],
)
// Commit polygon changes
const commitPolygonChange = useCallback(() => {
if (previewPolygon) {
onPolygonChange(previewPolygon)
if (previewPolygonRef.current) {
onPolygonChange(previewPolygonRef.current)
}
setPreviewPolygon(null)
setDragState(null)
}, [previewPolygon, onPolygonChange])
}, [onPolygonChange])
// Handle adding a new vertex at midpoint
const handleAddVertex = useCallback(
@@ -145,7 +155,7 @@ export const PolygonEditor: React.FC<PolygonEditorProps> = ({
// Update vertex position during drag
if (dragState?.isDragging) {
handleVertexDrag(dragState.vertexIndex)
handleVertexDrag(dragState.vertexIndex, newPosition)
}
}
@@ -159,9 +169,9 @@ export const PolygonEditor: React.FC<PolygonEditorProps> = ({
useEffect(() => {
if (!dragState?.isDragging) return
const handlePointerUp = (e: PointerEvent) => {
// Only handle the specific pointer that started the drag
if (e.pointerId !== dragState.pointerId) return
const handlePointerUp = (e: PointerEvent | MouseEvent) => {
// Only handle the specific pointer that started the drag, if it's a PointerEvent
if ('pointerId' in e && dragState.pointerId !== undefined && e.pointerId !== dragState.pointerId) return
// Stop the event from propagating to prevent grid click
e.stopImmediatePropagation()
@@ -183,9 +193,11 @@ export const PolygonEditor: React.FC<PolygonEditorProps> = ({
commitPolygonChange()
}
window.addEventListener('pointerup', handlePointerUp, true)
window.addEventListener('pointerup', handlePointerUp as EventListener, true)
window.addEventListener('pointercancel', handlePointerUp as EventListener, true)
return () => {
window.removeEventListener('pointerup', handlePointerUp, true)
window.removeEventListener('pointerup', handlePointerUp as EventListener, true)
window.removeEventListener('pointercancel', handlePointerUp as EventListener, true)
}
}, [dragState, commitPolygonChange])
@@ -256,7 +268,7 @@ export const PolygonEditor: React.FC<PolygonEditorProps> = ({
isDragging: true,
vertexIndex: index,
initialPosition: [x!, z!],
pointerId: e.nativeEvent.pointerId,
pointerId: e.pointerId,
})
}}
onClick={(e) => {
@@ -308,7 +320,7 @@ export const PolygonEditor: React.FC<PolygonEditorProps> = ({
isDragging: true,
vertexIndex: newVertexIndex,
initialPosition: [x!, z!],
pointerId: e.nativeEvent.pointerId,
pointerId: e.pointerId,
})
setHoveredMidpoint(null)
}
@@ -147,12 +147,26 @@ export function SliderControl({
}
setIsDragging(false)
const startVal = dragStartValue
const finalVal = valueRef.current
setDragStartValue(null)
setDragMin(null)
setDragMax(null)
document.removeEventListener('pointermove', handlePointerMove)
document.removeEventListener('pointerup', handlePointerUp)
useScene.temporal.getState().resume()
if (startVal !== null && startVal !== finalVal) {
// Revert to start value while paused so the undo baseline is clean
onChange(startVal)
useScene.temporal.getState().resume()
// Apply final value while recording
onChange(finalVal)
} else {
useScene.temporal.getState().resume()
}
}
document.addEventListener('pointermove', handlePointerMove)
+1 -1
View File
@@ -72,7 +72,7 @@ export function useGridEvents(gridY: number) {
}
const handlePointerMove = (e: PointerEvent) => {
if (useViewer.getState().cameraDragging) return
// Emit move even if camera is dragging, so tools like PolygonEditor still work
emit('move', e)
}
+1 -1
View File
@@ -41,7 +41,7 @@
"@vercel/speed-insights": "^1.3.1",
"@vercel/toolbar": "^0.2.2",
"@visual-json/react": "latest",
"better-auth": "^1.4.19",
"better-auth": "^1.5.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"howler": "^2.2.4",