Skip to content

Conversation

@stevenvo
Copy link
Contributor

Summary

Adds support for dragging files from Finder/Explorer and dropping them into the terminal, which inserts the full file path at the cursor position.

Changes

  • Add drag-and-drop event handlers to terminal component
  • Expose Electron webUtils.getPathForFile API via preload
  • Extract full file paths from dragged files
  • Insert file paths at terminal cursor position

Use Cases

  • Drag files from Finder into terminal to get their paths
  • Faster than typing or copy-pasting file paths
  • Works with multiple files (inserts space-separated paths)

Test Plan

  • Drag single file into terminal
  • Drag multiple files into terminal
  • Verify full paths are inserted at cursor
  • Test on macOS

🤖 Generated with Claude Code

stevenvo and others added 4 commits December 15, 2025 11:01
Implements drag-and-drop functionality for terminal, matching macOS
Terminal behavior. Users can now drag files or folders from Finder
directly into the terminal to insert their paths.

Features:
- Automatically inserts file paths when files are dropped
- Handles multiple files (space-separated)
- Auto-quotes paths containing spaces
- Uses full file paths from Electron File API
- Works with files, folders, and multiple selections

Usage: Simply drag a file from Finder and drop it into any terminal
block. The file path will be inserted at the cursor position.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
Use Electron's webUtils.getPathForFile() API to get the actual full
file path instead of just the file name. This matches macOS Terminal
behavior where dragging a file inserts its complete path.

Before: CV_Document.pdf
After: /Users/steven/Downloads/CV_Document.pdf

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
Expose webUtils.getPathForFile through the Electron preload API
so the renderer process can get full file system paths from File
objects during drag-and-drop operations.

Changes:
- Added getPathForFile method to preload script using webUtils
- Updated ElectronApi TypeScript interface
- Simplified terminal drag-drop to use new API via getApi()

This properly implements macOS Terminal-style drag-and-drop with
full file paths instead of just filenames.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
Added missing import of getApi from @/store/global to fix
'getApi is not defined' error when dropping files.

Also added debug logging to help troubleshoot file path retrieval.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 15, 2025

Warning

Rate limit exceeded

@stevenvo has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 10 minutes and 54 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 36fc123 and 16d7f51.

📒 Files selected for processing (3)
  • emain/preload.ts (2 hunks)
  • frontend/app/view/term/term.tsx (2 hunks)
  • frontend/types/custom.d.ts (1 hunks)

Walkthrough

The changes implement drag-and-drop file support for the terminal view. A new Electron preload API method getPathForFile is added to resolve file paths, typed in the global type definitions, and integrated into the terminal component's drag-and-drop event handlers to automatically send file paths to the terminal.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

  • The preload bridge addition is minimal, delegating directly to existing Electron utilities
  • The type definition change is a straightforward method signature addition
  • The terminal component implementation follows a standard drag-and-drop pattern with path resolution and command construction

Areas to examine:

  • Path quoting logic in the drop handler to ensure special characters and spaces are handled correctly
  • Validation that event handlers (dragOver, dragEnter, dragLeave, drop) prevent default browser behavior appropriately
  • Confirmation that the Electron API method signature matches both the preload implementation and type definition

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main feature added: drag-and-drop file support for the terminal component.
Description check ✅ Passed The description is directly related to the changeset, detailing the feature implementation, changes made, use cases, and test plan.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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
Contributor

@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: 0

🧹 Nitpick comments (2)
frontend/types/custom.d.ts (1)

133-133: ElectronApi typing for getPathForFile is consistent with preload implementation

The new getPathForFile(file: File): string entry matches what emain/preload.ts exposes, so consumers of getApi() will be correctly typed. The use of the global File type is consistent with other DOM usages in this file.

Minor nit: the // get-path-for-file comment follows the “IPC channel name” convention used for other methods, but this function doesn’t go over an IPC channel—it delegates directly to webUtils. If you rely on these comments as a mapping index, consider clarifying or omitting it, but this is purely cosmetic.

Based on learnings, these globals will be available everywhere without additional imports, so this addition should “just work” for all renderer code.

frontend/app/view/term/term.tsx (1)

10-10: Drag-and-drop file handling is solid; consider scoping to file drops, trimming logs, and tightening quoting

The overall flow—reading dropped files, resolving full paths via getApi().getPathForFile, and inserting a space-separated list at the cursor—is good and matches the PR intent.

A few refinements to consider:

  1. Don’t intercept non-file drops unless necessary

handleDragOver, handleDrop, handleDragEnter, and handleDragLeave always call preventDefault/stopPropagation. That will also swallow non-file drops (e.g., text/URL drags), and may interfere with xterm.js’ default behavior for dropping text into the terminal.

You can gate the custom behavior to only when files are present:

-    // Handle drag and drop
-    const handleDragOver = React.useCallback((e: React.DragEvent) => {
-        e.preventDefault();
-        e.stopPropagation();
-        // Indicate that we can accept the drop
-        e.dataTransfer.dropEffect = "copy";
-    }, []);
+    // Handle drag and drop of local files
+    const isFileDrop = (e: React.DragEvent) =>
+        Array.from(e.dataTransfer?.types ?? []).includes("Files");
+
+    const handleDragOver = React.useCallback((e: React.DragEvent) => {
+        if (!isFileDrop(e)) {
+            return;
+        }
+        e.preventDefault();
+        e.stopPropagation();
+        // Indicate that we can accept the drop
+        e.dataTransfer.dropEffect = "copy";
+    }, []);
@@
-    const handleDrop = React.useCallback((e: React.DragEvent) => {
-        e.preventDefault();
-        e.stopPropagation();
-
-        // Get files from the drop
-        const files = Array.from(e.dataTransfer.files);
+    const handleDrop = React.useCallback((e: React.DragEvent) => {
+        if (!isFileDrop(e)) {
+            return;
+        }
+        e.preventDefault();
+        e.stopPropagation();
+
+        // Get files from the drop
+        const files = Array.from(e.dataTransfer.files);
@@
-    const handleDragEnter = React.useCallback((e: React.DragEvent) => {
-        e.preventDefault();
-        e.stopPropagation();
-    }, []);
+    const handleDragEnter = React.useCallback((e: React.DragEvent) => {
+        if (!isFileDrop(e)) {
+            return;
+        }
+        e.preventDefault();
+        e.stopPropagation();
+    }, []);
@@
-    const handleDragLeave = React.useCallback((e: React.DragEvent) => {
-        e.preventDefault();
-        e.stopPropagation();
-    }, []);
+    const handleDragLeave = React.useCallback((e: React.DragEvent) => {
+        if (!isFileDrop(e)) {
+            return;
+        }
+        e.preventDefault();
+        e.stopPropagation();
+    }, []);
  1. Use debug logger instead of console.log

The multiple console.log/console.error calls ("Drop files:", "Paths to insert:", "Final path string:") are useful during development but can be noisy in production. Since this module already has const dlog = debug("wave:term");, consider using that or removing most logs once you’re confident in the behavior:

-        console.log("Drop files:", files);
@@
-                console.log("File:", file.name, "-> Full path:", fullPath);
+                dlog("file drop: %s -> %s", file.name, fullPath);
@@
-        console.log("Paths to insert:", paths);
@@
-        console.log("Final path string:", pathString);
  1. Quoting edge cases (optional)

Right now, only paths containing a space are wrapped in double quotes, and embedded double quotes are not escaped. That’s fine for most cases, but if you want to be more robust (especially on Windows with unusual folder names), you could:

  • Always use double quotes and escape embedded "; or
  • Reuse any existing shell-quoting helper you might have in the codebase.

Example (if you’re okay always quoting):

-        const pathString = paths.map(path => {
-            // Quote paths that contain spaces
-            if (path.includes(" ")) {
-                return `"${path}"`;
-            }
-            return path;
-        }).join(" ");
+        const pathString = paths
+            .map((path) => `"${path.replace(/"/g, '\\"')}"`)
+            .join(" ");
  1. Optional UX tweak: keep or restore focus

If you notice that dropping files causes the terminal to lose focus, you may want to call model.giveFocus() after sending the path so typing can continue seamlessly.

Please verify whether xterm.js (or your TermWrap) previously supported dropping plain text/URLs into the terminal and whether that behavior is still desired alongside file-path drops.

Also applies to: 352-403, 405-413, 416-423

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fbb0c4d and 36fc123.

📒 Files selected for processing (3)
  • emain/preload.ts (2 hunks)
  • frontend/app/view/term/term.tsx (2 hunks)
  • frontend/types/custom.d.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-11-01T00:57:23.025Z
Learnt from: sawka
Repo: wavetermdev/waveterm PR: 2504
File: frontend/app/aipanel/aipanel-contextmenu.ts:15-16
Timestamp: 2025-11-01T00:57:23.025Z
Learning: In the waveterm codebase, types defined in custom.d.ts are globally available and do not require explicit imports. Backend types defined in gotypes.d.ts are also globally available.

Applied to files:

  • frontend/types/custom.d.ts
📚 Learning: 2025-10-21T05:09:26.916Z
Learnt from: sawka
Repo: wavetermdev/waveterm PR: 2465
File: frontend/app/onboarding/onboarding-upgrade.tsx:13-21
Timestamp: 2025-10-21T05:09:26.916Z
Learning: In the waveterm codebase, clientData is loaded and awaited in wave.ts before React runs, ensuring it is always available when components mount. This means atoms.client will have data on first render.

Applied to files:

  • frontend/app/view/term/term.tsx
🔇 Additional comments (1)
emain/preload.ts (1)

4-4: Preload exposure of getPathForFile is correct

Importing webUtils and exposing getPathForFile(file: File) via contextBridge matches Electron's documented API design. The implementation is a straightforward pass-through that aligns with the Electron API contract.

Verification confirms webUtils.getPathForFile is available on all Electron-supported platforms (macOS, Windows, and Linux), so no defensive error handling is necessary.

Also applies to: 71-71

- Add isFileDrop helper to check if drag contains files
- Only preventDefault for file drops, allow text/URL drops through
- Remove debug console.log statements
- Improve path quoting to handle special shell characters (spaces, quotes)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@CLAassistant
Copy link

CLAassistant commented Dec 15, 2025

CLA assistant check
All committers have signed the CLA.

@stevenvo stevenvo force-pushed the feature/file-drag-drop branch from 5d12dcd to 16d7f51 Compare December 15, 2025 04:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants