Skip to main content
ZipDrop’s drag-and-drop interface provides instant visual feedback and seamless file handling through Tauri’s native event system.

How It Works

The app uses three Tauri event listeners to handle the complete drag-and-drop lifecycle:

Event Listeners

// Listen for file drop
listen<TauriDropEvent>("tauri://drag-drop", (event) => {
  if (event.payload.paths?.length > 0) {
    processFiles(event.payload.paths);
  }
});

// Visual feedback on drag over
listen("tauri://drag-over", (event) => {
  if (!isProcessing.current) {
    setState("drag-over");
  }
});

// Reset state on drag leave
listen("tauri://drag-leave", (event) => {
  if (!isProcessing.current) {
    setState("idle");
  }
});
These listeners are registered in App.tsx:272-291 and provide real-time feedback to users.

Drop Zone States

The drop zone UI adapts to five different states:
StateVisualDescription
idleUpload iconReady to receive files
drag-overHighlightedFiles hovering over window
processingSpinnerConverting/zipping files
successCheckmarkUpload complete
errorError iconValidation or upload failed
The app automatically prevents duplicate processing with debouncing (300ms) and a processing lock to ensure files are only handled once.

File Validation

When files are dropped, ZipDrop immediately validates them before processing:
pub fn validate_files(paths: &[PathBuf]) -> Result<(), ValidationError> {
    // Check file count
    if paths.len() > MAX_FILES {
        return Err(ValidationError {
            message: format!("Too many files. Maximum is {} files.", MAX_FILES),
            file: None,
        });
    }
    
    // Check each file size and extension
    for path in paths {
        // Verify file exists
        // Check it's not a directory
        // Validate file size < 500MB
        // Check extension is allowed
    }
    
    // Check total size < 1GB
    if total_size > MAX_TOTAL_SIZE {
        return Err(...);
    }
}
Validation happens in processor.rs:58-145 before any processing begins.

Visual Feedback

The drop zone provides instant visual feedback:
<div className={`drop-zone ${state}`}>
  {state === "processing" ? (
    <>
      <div className="drop-icon spinning"><SpinnerIcon /></div>
      <div className="drop-text">Processing...</div>
      <div className="drop-subtext">{currentFileName}</div>
    </>
  ) : state === "drag-over" ? (
    <>
      <div className="drop-icon"><UploadIcon /></div>
      <div className="drop-text">Drop to upload</div>
    </>
  ) : (
    // Idle state
  )}
</div>
The window automatically hides after 10 seconds of successful upload, keeping your workspace clean.

Error Handling

If validation fails, users see a clear error message:
if (!validation.valid) {
  setState("error");
  setStatusText(validation.error!);
  setTimeout(() => {
    setState("idle");
    setStatusText("");
  }, 3000);
}
Errors automatically clear after 3 seconds, returning the UI to the idle state.

Debouncing

To prevent duplicate processing from multiple drop events:
const DEBOUNCE_MS = 300;
const lastDropTime = useRef(0);

const processFiles = async (paths: string[]) => {
  const now = Date.now();
  if (now - lastDropTime.current < DEBOUNCE_MS) {
    return; // Ignore duplicate
  }
  lastDropTime.current = now;
  // ... continue processing
};
This ensures smooth operation even if macOS fires multiple events for a single drop.
Directories are not supported. If you need to upload a folder, compress it into a ZIP file first.

Build docs developers (and LLMs) love