From 1bcd99c949e4d2b4fb1c0813debd50176fa58cb9 Mon Sep 17 00:00:00 2001 From: Thomas Hallock Date: Sun, 2 Nov 2025 07:20:00 -0600 Subject: [PATCH] fix(rithmomachia): fix guide modal resize drift by calculating from initial state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resizing was calculating deltas incrementally (delta-of-delta), which caused the modal to slide away from the cursor. Now we save the initial dimensions and position when resize starts, and always calculate the new size/position from that initial state. Changes: - Added resizeStart state to track initial dimensions and position - handleResizeStart now saves initial state - Mouse move calculations use resizeStart instead of incrementally updating - Removed the buggy dragStart reset that was causing drift Resize now feels smooth and natural - handles follow the cursor exactly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../components/PlayingGuideModal.tsx | 64 +++++++++---------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/apps/web/src/arcade-games/rithmomachia/components/PlayingGuideModal.tsx b/apps/web/src/arcade-games/rithmomachia/components/PlayingGuideModal.tsx index 609b3642..66d87ae2 100644 --- a/apps/web/src/arcade-games/rithmomachia/components/PlayingGuideModal.tsx +++ b/apps/web/src/arcade-games/rithmomachia/components/PlayingGuideModal.tsx @@ -31,6 +31,7 @@ export function PlayingGuideModal({ isOpen, onClose, standalone = false }: Playi const [dragStart, setDragStart] = useState({ x: 0, y: 0 }) const [isResizing, setIsResizing] = useState(false) const [resizeDirection, setResizeDirection] = useState('') + const [resizeStart, setResizeStart] = useState({ width: 0, height: 0, x: 0, y: 0 }) const [isHovered, setIsHovered] = useState(false) const modalRef = useRef(null) @@ -62,6 +63,8 @@ export function PlayingGuideModal({ isOpen, onClose, standalone = false }: Playi setIsResizing(true) setResizeDirection(direction) setDragStart({ x: e.clientX, y: e.clientY }) + // Save initial dimensions and position for resize calculation + setResizeStart({ width: size.width, height: size.height, x: position.x, y: position.y }) } // Bust-out button handler @@ -80,44 +83,37 @@ export function PlayingGuideModal({ isOpen, onClose, standalone = false }: Playi y: e.clientY - dragStart.y, }) } else if (isResizing) { + // Calculate delta from initial resize start position const deltaX = e.clientX - dragStart.x const deltaY = e.clientY - dragStart.y - setSize((prev) => { - let newWidth = prev.width - let newHeight = prev.height - let newX = position.x - let newY = position.y + let newWidth = resizeStart.width + let newHeight = resizeStart.height + let newX = resizeStart.x + let newY = resizeStart.y - // Handle different resize directions - if (resizeDirection.includes('e')) { - newWidth = Math.max(450, Math.min(window.innerWidth * 0.9, prev.width + deltaX)) - } - if (resizeDirection.includes('w')) { - const desiredWidth = prev.width - deltaX - newWidth = Math.max(450, Math.min(window.innerWidth * 0.9, desiredWidth)) - const widthDiff = newWidth - prev.width - newX = position.x - widthDiff - } - if (resizeDirection.includes('s')) { - newHeight = Math.max(600, Math.min(window.innerHeight * 0.8, prev.height + deltaY)) - } - if (resizeDirection.includes('n')) { - const desiredHeight = prev.height - deltaY - newHeight = Math.max(600, Math.min(window.innerHeight * 0.8, desiredHeight)) - const heightDiff = newHeight - prev.height - newY = position.y - heightDiff - } + // Handle different resize directions - calculate from initial state + if (resizeDirection.includes('e')) { + newWidth = Math.max(450, Math.min(window.innerWidth * 0.9, resizeStart.width + deltaX)) + } + if (resizeDirection.includes('w')) { + const desiredWidth = resizeStart.width - deltaX + newWidth = Math.max(450, Math.min(window.innerWidth * 0.9, desiredWidth)) + // Move left edge by the amount we actually changed width + newX = resizeStart.x + (resizeStart.width - newWidth) + } + if (resizeDirection.includes('s')) { + newHeight = Math.max(600, Math.min(window.innerHeight * 0.8, resizeStart.height + deltaY)) + } + if (resizeDirection.includes('n')) { + const desiredHeight = resizeStart.height - deltaY + newHeight = Math.max(600, Math.min(window.innerHeight * 0.8, desiredHeight)) + // Move top edge by the amount we actually changed height + newY = resizeStart.y + (resizeStart.height - newHeight) + } - if (newX !== position.x || newY !== position.y) { - setPosition({ x: newX, y: newY }) - } - - return { width: newWidth, height: newHeight } - }) - - // Reset dragStart to current mouse position for next delta calculation - setDragStart({ x: e.clientX, y: e.clientY }) + setSize({ width: newWidth, height: newHeight }) + setPosition({ x: newX, y: newY }) } } @@ -136,7 +132,7 @@ export function PlayingGuideModal({ isOpen, onClose, standalone = false }: Playi document.removeEventListener('mousemove', handleMouseMove) document.removeEventListener('mouseup', handleMouseUp) } - }, [isDragging, isResizing, dragStart, position, resizeDirection]) + }, [isDragging, isResizing, dragStart, resizeDirection, resizeStart]) if (!isOpen && !standalone) return null