Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 35 additions & 25 deletions frontend/src/components/Media/ImageViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface ImageViewerProps {
onMouseUp: () => void;
onMouseLeave: () => void;
onClick?: (e: React.MouseEvent) => void;
onWheel?: (e: React.WheelEvent) => void;
}

export const ImageViewer: React.FC<ImageViewerProps> = ({
Expand All @@ -27,33 +28,42 @@ export const ImageViewer: React.FC<ImageViewerProps> = ({
onMouseUp,
onMouseLeave,
onClick,
onWheel,
}) => {
return (
<div
id="zoomable-image"
onClick={onClick}
onMouseDown={onMouseDown}
onMouseMove={onMouseMove}
onMouseUp={onMouseUp}
onMouseLeave={onMouseLeave}
className="relative flex h-full w-full items-center justify-center overflow-hidden"
>
<img
src={convertFileSrc(imagePath) || '/placeholder.svg'}
alt={alt}
draggable={false}
className="h-full w-full object-contain select-none"
onError={(e) => {
const img = e.target as HTMLImageElement;
img.onerror = null;
img.src = '/placeholder.svg';
}}
style={{
transform: `translate(${position.x}px, ${position.y}px) scale(${scale}) rotate(${rotation}deg)`,
transition: isDragging ? 'none' : 'transform 0.2s ease-in-out',
cursor: isDragging ? 'grabbing' : 'grab',
}}
/>
<div className="relative flex h-full w-full items-center justify-center overflow-hidden">
<div
id="zoomable-image"
className="relative"
onClick={onClick}
onMouseDown={onMouseDown}
onMouseMove={onMouseMove}
onMouseUp={onMouseUp}
onMouseLeave={onMouseLeave}
>
<img
src={convertFileSrc(imagePath) || '/placeholder.svg'}
alt={alt}
draggable={false}
onError={(e) => {
const img = e.target as HTMLImageElement;
img.onerror = null;
img.src = '/placeholder.svg';
}}
onWheel={onWheel}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Move onWheel to the parent div for consistent cursor position handling.

The onWheel handler is attached to the img element (line 52), while all other interaction handlers (onClick, onMouseDown, onMouseMove, onMouseUp, onMouseLeave) are attached to the parent div (lines 37-41). This inconsistency creates coordination problems because cursor position calculations will be relative to different elements. For zoom-to-cursor functionality to work correctly, the wheel event must capture the cursor position relative to the same element as the other handlers.

Apply this diff to move onWheel to the parent div:

       <div
         id="zoomable-image"
         className="relative"
         onClick={onClick}
         onMouseDown={onMouseDown}
         onMouseMove={onMouseMove}
         onMouseUp={onMouseUp}
         onMouseLeave={onMouseLeave}
+        onWheel={onWheel}
       >
         <img
           src={convertFileSrc(imagePath) || '/placeholder.svg'}
           alt={alt}
           draggable={false}
           onError={(e) => {
             const img = e.target as HTMLImageElement;
             img.onerror = null;
             img.src = '/placeholder.svg';
           }}
-          onWheel={onWheel}
           style={{
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div
id="zoomable-image"
className="relative"
onClick={onClick}
onMouseDown={onMouseDown}
onMouseMove={onMouseMove}
onMouseUp={onMouseUp}
onMouseLeave={onMouseLeave}
>
<img
src={convertFileSrc(imagePath) || '/placeholder.svg'}
alt={alt}
draggable={false}
onError={(e) => {
const img = e.target as HTMLImageElement;
img.onerror = null;
img.src = '/placeholder.svg';
}}
onWheel={onWheel}
<div
id="zoomable-image"
className="relative"
onClick={onClick}
onMouseDown={onMouseDown}
onMouseMove={onMouseMove}
onMouseUp={onMouseUp}
onMouseLeave={onMouseLeave}
onWheel={onWheel}
>
<img
src={convertFileSrc(imagePath) || '/placeholder.svg'}
alt={alt}
draggable={false}
onError={(e) => {
const img = e.target as HTMLImageElement;
img.onerror = null;
img.src = '/placeholder.svg';
}}
🤖 Prompt for AI Agents
In frontend/src/components/Media/ImageViewer.tsx around lines 34 to 52, the
onWheel handler is attached to the <img> while all other mouse handlers are on
the parent <div>, causing inconsistent cursor position references for
zoom-to-cursor; move the onWheel prop from the <img> to the parent <div> so it
sits alongside onClick/onMouseDown/onMouseMove/onMouseUp/onMouseLeave, remove
onWheel from the <img>, and ensure event types/handler signature remain
unchanged (pass the same handler reference and update any refs/logic if it
relied on event.target being the image).

style={{
transform: `translate(${position.x}px, ${position.y}px) scale(${scale}) rotate(${rotation}deg)`,
transformOrigin: '0 0',
transition: isDragging ? 'none' : 'transform 0.2s ease-in-out',
cursor: isDragging ? 'grabbing' : 'grab',
maxWidth: '100%',
maxHeight: '100%',
userSelect: 'none',
objectFit: 'contain',
display: 'block',
}}
/>
</div>
</div>
);
};
11 changes: 4 additions & 7 deletions frontend/src/components/Media/MediaView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export function MediaView({ onClose, images, type = 'image' }: MediaViewProps) {
}
return null;
}, [images, currentViewIndex]);
console.log(currentViewIndex);

// Local UI state
const [showInfo, setShowInfo] = useState(false);
Expand Down Expand Up @@ -104,7 +103,7 @@ export function MediaView({ onClose, images, type = 'image' }: MediaViewProps) {

const currentImagePath = currentImage.path;
const currentImageAlt = `image-${currentViewIndex}`;

// const [mouselocation, setMouselocation] = useState({x:0,y:0});
return (
<div className="fixed inset-0 z-50 mt-0 flex flex-col bg-gradient-to-b from-black/95 to-black/98 backdrop-blur-lg">
{/* Controls */}
Expand All @@ -121,7 +120,7 @@ export function MediaView({ onClose, images, type = 'image' }: MediaViewProps) {

{/* Main viewer area */}
<div
className="relative flex h-full w-full items-center justify-center"
className="relativflex h-full w-full"
onClick={(e) => {
if (e.target === e.currentTarget) handleClose();
}}
Expand All @@ -139,13 +138,11 @@ export function MediaView({ onClose, images, type = 'image' }: MediaViewProps) {
onMouseUp={handlers.handleMouseUp}
onMouseLeave={handlers.handleMouseUp}
onClick={(e) => {
if (e.target === e.currentTarget) {
handleClose();
}
if (e.target === e.currentTarget) handleClose();
}}
onWheel={handlers.handleWheelZoom}
/>
)}

{/* Navigation buttons */}
<NavigationButtons
onPrevious={handlePreviousImage}
Expand Down
28 changes: 28 additions & 0 deletions frontend/src/hooks/useImageViewControls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,33 @@ export const useImageViewControls = () => {
dragStart: { x: 0, y: 0 },
});

// handlewheel zoom
const handleWheelZoom = useCallback((e: React.WheelEvent) => {
e.preventDefault();
e.stopPropagation();
const container = e.currentTarget.parentElement;
if (!container) return;
const containerRect = container.getBoundingClientRect();
const mouseX = e.clientX - containerRect.left;
const mouseY = e.clientY - containerRect.top;
const deltaY = e.deltaY;
setViewState((prev) => {
const delta = deltaY > 0 ? -0.1 : 0.1;
const newScale = Math.min(4, Math.max(0.5, prev.scale + delta));
if (newScale === prev.scale) return prev;
const scaleDiff = newScale - prev.scale;
const newPosX =
prev.position.x - (mouseX - prev.position.x) * (scaleDiff / prev.scale);
const newPosY =
prev.position.y - (mouseY - prev.position.y) * (scaleDiff / prev.scale);
return {
...prev,
scale: newScale,
position: { x: newPosX, y: newPosY },
};
});
}, []);

const handleZoomIn = useCallback(() => {
setViewState((prev) => ({
...prev,
Expand Down Expand Up @@ -89,6 +116,7 @@ export const useImageViewControls = () => {
handleMouseDown,
handleMouseMove,
handleMouseUp,
handleWheelZoom,
},
};
};