Skip to content

Add global drag-and-drop image upload#4

Merged
notkainoa merged 9 commits intomainfrom
codex/add-drag-and-drop-support-for-uploads
Nov 24, 2025
Merged

Add global drag-and-drop image upload#4
notkainoa merged 9 commits intomainfrom
codex/add-drag-and-drop-support-for-uploads

Conversation

@notkainoa
Copy link
Copy Markdown
Owner

@notkainoa notkainoa commented Nov 24, 2025

Summary

  • add a shared image file handler that pushes uploaded images into the history stack and switches to upload mode
  • listen for global drag-and-drop events to accept images anywhere on the page
  • display a drag-over overlay to guide users when they drag files onto the site

Testing

  • npm run build

Codex Task

Summary by CodeRabbit

  • New Features
    • Users can now upload images by dragging and dropping them directly onto the app.
    • A visual drag indicator overlay appears when files are dragged over the app, prompting users to drop the image to upload and automatically switch to the Upload tab.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link
Copy Markdown

vercel bot commented Nov 24, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
emblemic Ready Ready Preview Comment Nov 24, 2025 4:45am

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Nov 24, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Implements drag-and-drop image upload functionality with visual feedback. Introduces isDraggingFile state, handleImageFile function, and drag event listeners (dragenter, dragover, dragleave, drop). Adds a visual overlay indicator for file dragging. Refactors existing handleImageUpload to delegate to the new function. Includes useCallback import and state updates.

Changes

Cohort / File(s) Summary
Drag-and-drop image upload implementation
App.tsx
Adds isDraggingFile state and dragCounterRef; introduces handleImageFile function for file processing and converts files to data URLs; implements drag event listeners (dragenter, dragover, dragleave, drop) with prevention; adds fixed overlay prompting user to drop image; refactors handleImageUpload to delegate to handleImageFile; adds useCallback import; updates history state management to include previous present in past entries on image drops and clears future.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add global drag-and-drop image upload' accurately summarizes the main change: implementing global drag-and-drop functionality for image uploads throughout the app.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e7e2b9 and b64a1d9.

📒 Files selected for processing (1)
  • App.tsx (4 hunks)
🔇 Additional comments (4)
App.tsx (4)

2-2: LGTM: Clean state setup for drag-and-drop.

The import of useCallback and the state additions are appropriate. Using useRef for the drag counter is the right choice—it avoids unnecessary re-renders while tracking nested drag events, which is a standard pattern for handling dragenter/dragleave correctly.

Also applies to: 384-385


655-705: LGTM: Robust drag-and-drop implementation.

The drag event handling is well-structured:

  • The counter pattern correctly handles nested dragenter/dragleave events when dragging over child elements
  • The hasFiles() check ensures the app doesn't interfere with other drag operations (e.g., dragging text)
  • preventDefault() calls are correctly placed in both dragover (required for drop to fire) and drop (prevents browser navigation)
  • When multiple images are dropped, only the first is processed—this is reasonable and keeps the behavior simple

The implementation will inherit the error handling improvements from handleImageFile once those are added.


707-714: LGTM: Excellent refactoring.

Delegating to handleImageFile eliminates code duplication and ensures consistent behavior between drag-and-drop and file input uploads. The input value reset is a nice touch—it allows users to re-select the same file.


1050-1067: LGTM: Polished drag feedback UI.

The overlay provides clear visual feedback during drag operations:

  • pointer-events-none is crucial—it ensures the overlay doesn't interfere with drag events
  • The z-index (90) correctly positions it below the export modal but above all other content
  • The instructional text accurately describes the behavior (switching to Upload mode)
  • The styling is consistent with the app's design language

App.tsx Outdated
Comment on lines +387 to +399
const handleImageFile = useCallback((file: File) => {
const reader = new FileReader();
reader.onload = (event) => {
if (event.target?.result) {
setHistory((curr) => ({
past: [...curr.past, curr.present],
present: { ...curr.present, mode: 'image', imageSrc: event.target?.result as string },
future: []
}));
}
};
reader.readAsDataURL(file);
}, []);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add error handling for file reading and consider file size validation.

The FileReader implementation lacks error handling—if reading fails (corrupted file, memory issue, etc.), the user receives no feedback. Additionally, very large images converted to data URLs can consume significant memory, especially when stored in the history stack.

Apply this diff to add error handling:

 const handleImageFile = useCallback((file: File) => {
+    if (file.size > 10 * 1024 * 1024) {  // 10MB limit
+        alert('Image file is too large. Please upload an image smaller than 10MB.');
+        return;
+    }
+
     const reader = new FileReader();
     reader.onload = (event) => {
         if (event.target?.result) {
             setHistory((curr) => ({
                 past: [...curr.past, curr.present],
                 present: { ...curr.present, mode: 'image', imageSrc: event.target?.result as string },
                 future: []
             }));
         }
     };
+    reader.onerror = () => {
+        alert('Failed to read image file. Please try a different image.');
+    };
     reader.readAsDataURL(file);
 }, []);
📝 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
const handleImageFile = useCallback((file: File) => {
const reader = new FileReader();
reader.onload = (event) => {
if (event.target?.result) {
setHistory((curr) => ({
past: [...curr.past, curr.present],
present: { ...curr.present, mode: 'image', imageSrc: event.target?.result as string },
future: []
}));
}
};
reader.readAsDataURL(file);
}, []);
const handleImageFile = useCallback((file: File) => {
if (file.size > 10 * 1024 * 1024) { // 10MB limit
alert('Image file is too large. Please upload an image smaller than 10MB.');
return;
}
const reader = new FileReader();
reader.onload = (event) => {
if (event.target?.result) {
setHistory((curr) => ({
past: [...curr.past, curr.present],
present: { ...curr.present, mode: 'image', imageSrc: event.target?.result as string },
future: []
}));
}
};
reader.onerror = () => {
alert('Failed to read image file. Please try a different image.');
};
reader.readAsDataURL(file);
}, []);
🤖 Prompt for AI Agents
In App.tsx around lines 387 to 399, the FileReader usage lacks error handling
and no file-size validation which can lead to silent failures or OOM when large
images are converted to data URLs and pushed into history; before reading,
validate file.size (e.g., reject above a configurable threshold like 5MB) and
notify the user if too large, and replace direct data-URL storage with a safer
approach (prefer creating a blob URL via URL.createObjectURL(file) or store the
File/Blob reference instead of embedding a large base64 string) to reduce memory
usage; additionally attach reader.onerror and reader.onabort handlers to surface
errors (update component state or call an error/toast handler) and ensure any
created blob URLs are revoked when no longer needed or when popping history.

@notkainoa
Copy link
Copy Markdown
Owner Author

@copilot can you fix all the bugs and make it work

Copy link
Copy Markdown
Contributor

Copilot AI commented Nov 24, 2025

@KainoaNewton I've opened a new pull request, #8, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI and others added 4 commits November 24, 2025 02:35
Co-authored-by: KainoaNewton <123281048+KainoaNewton@users.noreply.github.com>
Co-authored-by: KainoaNewton <123281048+KainoaNewton@users.noreply.github.com>
Co-authored-by: KainoaNewton <123281048+KainoaNewton@users.noreply.github.com>
Fix missing CropSuggestionModal component, add SVG color override, and preserve SVG format on upload
@notkainoa
Copy link
Copy Markdown
Owner Author

@copilot I have this conflict in app.tsx. Which do I keep:
imageColor: f.config.imageColor || INITIAL_CONFIG.imageColor,
radialGlareOpacity: f.config.radialGlareOpacity ?? 0,
backgroundTransitioning: false,

or

radialGlareOpacity: f.config.radialGlareOpacity ?? 0

Copy link
Copy Markdown
Contributor

Copilot AI commented Nov 24, 2025

@KainoaNewton I've opened a new pull request, #12, to work on those changes. Once the pull request is ready, I'll request review from you.

@notkainoa notkainoa merged commit d18b771 into main Nov 24, 2025
3 checks passed
@notkainoa notkainoa deleted the codex/add-drag-and-drop-support-for-uploads branch November 24, 2025 04:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants