onPointerUp is bound only to the Konva Stage (ResultPanel.tsx:1217-1294, SheetPanel.tsx:298-345); there is no setPointerCapture and no window-level fallback. Releasing the mouse button outside the panel means the gesture never ends:
- Pan sticks "on": start panning (space / middle-click / pan tool), drag off the panel, release →
vp.endPan() never runs → isPanning ref stays true (useViewport.ts:195-212) → when the cursor re-enters, the canvas pans with no button held until the user clicks again.
- Marquee / box / pencil stick:
drawingBox, marqueeBox, pencilPoints keep following the bare cursor after release.
- Sheet rotation corrupts undo granularity:
rotatingPieceId sticks — every subsequent move keeps writing skipHistory: true rotation updates, and the committing (history-recording) update at SheetPanel.tsx:341 never happens, so the undo history silently misses the rotation.
Fix: call setPointerCapture on the stage container on pointerdown (or register a one-shot window pointerup/pointercancel listener while a gesture is active).
Related stale-ref bug: useViewport.ts:224 returns isPanning: isPanning.current — a render-time ref read. endPan() mutates the ref without re-rendering, so after a middle-click pan with a piece selected, isInteracting stays true and the PieceProperties popover remains opacity: 0; pointer-events: none until some unrelated re-render (ResultPanel.tsx:2217-2228). Also the cursor doesn't switch to "grabbing" until the first move. Make isPanning state (or bump a render counter on start/end).
onPointerUpis bound only to the Konva Stage (ResultPanel.tsx:1217-1294,SheetPanel.tsx:298-345); there is nosetPointerCaptureand no window-level fallback. Releasing the mouse button outside the panel means the gesture never ends:vp.endPan()never runs →isPanningref stays true (useViewport.ts:195-212) → when the cursor re-enters, the canvas pans with no button held until the user clicks again.drawingBox,marqueeBox,pencilPointskeep following the bare cursor after release.rotatingPieceIdsticks — every subsequent move keeps writingskipHistory: truerotation updates, and the committing (history-recording) update atSheetPanel.tsx:341never happens, so the undo history silently misses the rotation.Fix: call
setPointerCaptureon the stage container on pointerdown (or register a one-shotwindowpointerup/pointercancellistener while a gesture is active).Related stale-ref bug:
useViewport.ts:224returnsisPanning: isPanning.current— a render-time ref read.endPan()mutates the ref without re-rendering, so after a middle-click pan with a piece selected,isInteractingstaystrueand the PieceProperties popover remainsopacity: 0; pointer-events: noneuntil some unrelated re-render (ResultPanel.tsx:2217-2228). Also the cursor doesn't switch to "grabbing" until the first move. MakeisPanningstate (or bump a render counter on start/end).