Skip to content

Drag and drop improvements#903

Open
scriptease wants to merge 5 commits intoRunMaestro:rcfrom
scriptease:drag-and-drop-files
Open

Drag and drop improvements#903
scriptease wants to merge 5 commits intoRunMaestro:rcfrom
scriptease:drag-and-drop-files

Conversation

@scriptease
Copy link
Copy Markdown

@scriptease scriptease commented Apr 25, 2026

Bildschirmfoto 2026-04-25 um 07 36 35

I am working on some more drag and drop functionality:

  • From the file panel -> drag on chat for @ mentions
  • From outside app -> drag on chat or group chat for @mention relative or absolute if not in the project directory
  • Image from file panel -> drag on chat insert as attachment with thumbnail

Summary by CodeRabbit

  • New Features

    • Dragging files from the explorer or dropping external files/folders inserts @references into messages or stages images for upload.
    • File drops now respect group-chat drafts vs AI input, inserting mentions into the correct input.
  • UI/UX Improvements

    • Overlay wording clarified to “Drop file or folder” with guidance about thumbnails vs @references.
    • Overlay is clickable to dismiss if it becomes stuck.
  • Tests

    • Expanded drag-and-drop test coverage for many drop scenarios and edge cases.

noreply and others added 4 commits April 25, 2026 06:21
Tree rows in the Files panel are now draggable. Dropping anywhere in
the AI chat appends the file's relative path as an @mention in the
active AI input. Phase 1 scope: AI mode only (group chat excluded);
external file drops continue to stage as images unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…at (phase 2)

Dragging files or folders from Finder/Explorer into the chat now appends
them as @<path> mentions. Paths inside the session's project root are
inserted relative; outside paths are kept absolute. Multi-file drops are
joined with spaces. Group chat drops update the active chat's draftMessage.

Image drops continue to stage as images unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a secondary line "or drop a file or folder to insert as @reference"
beneath the existing "Drop image to attach" message so users discover
the new path-mention drop target shipped in phase 2.

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

coderabbitai Bot commented Apr 25, 2026

📝 Walkthrough

Walkthrough

Drag-and-drop was generalized from image-only to file/folder handling: internal file-panel drags publish file-paths, external drops and internal drops insert @<path> mentions or stage images via remote read, overlay/state names were renamed from "image" to "file", and tests expanded to cover many drop scenarios and edge cases.

Changes

Cohort / File(s) Summary
Input handlers
src/renderer/hooks/input/useInputHandlers.ts
Renamed drag-state setter to setIsDraggingFile; extended handleDrop to support application/x-maestro-file-path payloads, insert @<path> mentions (project-relative or absolute with slash normalization), stage images via window.maestro.fs.readFile, and append mentions to AI input or group-chat draft with whitespace handling.
App UI / Overlay
src/renderer/App.tsx
Renamed overlay/state from image→file, changed overlay copy to "Drop file or folder" with extra instruction, made overlay container clickable to reset drag counter and clear isDraggingFile.
File Explorer
src/renderer/components/FileExplorerPanel.tsx
Made file rows draggable; on dragstart set dataTransfer.setData('application/x-maestro-file-path', fullPath); panel-level drag handlers stop propagation for that MIME to avoid triggering app-level overlay on internal drags.
App-level handlers API
src/renderer/hooks/ui/useAppHandlers.ts
Renamed exported handlers/state from *Image**File*; drag-enter now detects OS Files types and internal application/x-maestro-file-path; added Escape and window-leave logic to reliably reset overlay.
Tests
src/__tests__/renderer/hooks/useInputHandlers.test.ts
Replaced setIsDraggingImagesetIsDraggingFile in mocks; added dataTransfer.getData stubs; greatly expanded handleDrop tests for internal vs external drops, image vs non-image behavior, group-chat draft updates, Windows path relativization, missing-path/no-op cases, and duplicate/staging edge cases.

Sequence Diagram

sequenceDiagram
    participant User
    participant FileExplorerPanel
    participant DragEvent
    participant AppOverlay
    participant useInputHandlers
    participant Filesystem
    participant ChatDraft

    User->>FileExplorerPanel: Drag file (internal)
    FileExplorerPanel->>DragEvent: setData('application/x-maestro-file-path', path)
    DragEvent->>AppOverlay: dragenter / dragover
    AppOverlay->>useInputHandlers: handleFileDragEnter

    User->>DragEvent: Drop onto input area
    DragEvent->>useInputHandlers: handleDrop(event)

    alt Internal file-panel drop
        useInputHandlers->>useInputHandlers: getData('application/x-maestro-file-path')
        alt Image extension
            useInputHandlers->>Filesystem: readFile(path)
            Filesystem-->>useInputHandlers: dataURL or null
            useInputHandlers->>ChatDraft: stage image if dataURL
        else Non-image
            useInputHandlers->>useInputHandlers: toMentionPath(path)
            useInputHandlers->>ChatDraft: append `@mention` to draft/input
        end
    else External OS drop
        useInputHandlers->>useInputHandlers: extract file.path from DataTransfer
        useInputHandlers->>useInputHandlers: convert to project-relative or normalized absolute
        useInputHandlers->>ChatDraft: append mention(s) or stage images
    end

    useInputHandlers-->>User: update inputValue or draftMessage
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A drag, a drop, a soft-sprung cheer,

paths hop in with whiskered gear,
images staged, mentions race—
files and folders find their place. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Drag and drop improvements' is directly related to the main changes in the PR, which focus on enhancing drag-and-drop functionality across multiple components (file explorer, input handlers, app handlers, and the main App component).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 25, 2026

Greptile Summary

This PR extends the drag-and-drop surface in three ways: file-tree rows in the Files panel are now draggable and carry a custom application/x-maestro-file-path MIME type; dropping them on the chat inserts an @-mention (or stages an image via IPC); and external filesystem drops of non-image files/folders are now relativized against the project root and inserted as @-mentions in both AI-mode and group-chat inputs.

  • P1 – Unhandled promise rejection (useInputHandlers.ts line 634): void window.maestro.fs.readFile(...).then(...) has no .catch(); any IPC failure (SSH disconnect, missing file, permission denied) produces a silent unhandled rejection with no user feedback.
  • P2 – No drop overlay for internal drags: handleImageDragEnter only activates the visual overlay when dataTransfer.types.includes('Files'); internal file-panel drags only carry application/x-maestro-file-path, so users see no overlay when dragging from the Files panel.

Confidence Score: 3/5

Mergeable after fixing the unhandled promise rejection in the image IPC path.

One P1 (missing .catch() on readFile) creates a real unhandled rejection surface in SSH/remote sessions where IPC calls are more likely to fail. Two P2s (overlay not shown for internal drags, misleading overlay text) reduce UX quality but don't break functionality. The new logic is otherwise well-structured and well-tested.

src/renderer/hooks/input/useInputHandlers.ts — the readFile IPC call needs a .catch() handler.

Important Files Changed

Filename Overview
src/renderer/hooks/input/useInputHandlers.ts Core logic for three new drag paths (internal file panel, external file, external image staging); missing .catch() on the readFile IPC promise is a P1 concern.
src/renderer/App.tsx Minor UI additions: updated overlay comment and hint text; overlay not triggered for internal drags (P2 UX gap) and primary heading is misleading for non-image drops (P2).
src/renderer/components/FileExplorerPanel.tsx Adds draggable + onDragStart to each file-tree row, setting the custom MIME application/x-maestro-file-path; straightforward and safe.
src/tests/renderer/hooks/useInputHandlers.test.ts Good coverage added for all new drag-and-drop scenarios including Windows path normalization, group-chat draft updates, and image staging via IPC; existing tests updated with getData stub.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User drags item] --> B{Internal file panel drag?}
    B -- Yes: application/x-maestro-file-path set --> C{Group chat active or not AI mode?}
    C -- Yes --> D[Return early — no action]
    C -- No --> E{Image extension?}
    E -- Yes --> F[readFile via IPC]
    F --> G{Returns data: URL?}
    G -- Yes --> H[Stage as image attachment]
    G -- No --> I[Silent no-op]
    E -- No --> J[appendMentionsToAiInput]
    B -- No: external filesystem drop --> K{Any files in dataTransfer.files?}
    K -- Yes --> L{file.type starts with image/?}
    L -- Yes --> M[FileReader → base64 data URL]
    M --> N{Group chat?}
    N -- Yes --> O[setGroupChatStagedImages]
    N -- No --> P[setStagedImages]
    L -- No --> Q{file.path present?}
    Q -- Yes --> R[toMentionPath: relativize or keep absolute]
    R --> S{Group chat active?}
    S -- Yes --> T[appendMentionsToGroupChatDraft]
    S -- No --> U[appendMentionsToAiInput]
    Q -- No --> V[Skip — browser-only file, no fs path]
Loading

Reviews (1): Last reviewed commit: "feat: drag images from Files panel into ..." | Re-trigger Greptile

Comment on lines +634 to +644
void window.maestro.fs.readFile(absolutePath, sshRemoteId).then((content) => {
if (typeof content !== 'string' || !content.startsWith('data:image/')) return;
setStagedImages((prev) => {
if (prev.includes(content)) {
setSuccessFlashNotification('Duplicate image ignored');
setTimeout(() => setSuccessFlashNotification(null), 2000);
return prev;
}
return [...prev, content];
});
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Unhandled promise rejection on readFile failure

void window.maestro.fs.readFile(...) fires the IPC call with no .catch(). If the call rejects (file not found, permission denied, SSH connection dropped), the rejection is silently swallowed and produces an unhandled promise rejection — no user feedback and potentially noisy DevTools/Sentry logs. A minimal fix is to add an error handler:

void window.maestro.fs.readFile(absolutePath, sshRemoteId)
  .then((content) => {
    if (typeof content !== 'string' || !content.startsWith('data:image/')) return;
    setStagedImages((prev) => {
      if (prev.includes(content)) {
        setSuccessFlashNotification('Duplicate image ignored');
        setTimeout(() => setSuccessFlashNotification(null), 2000);
        return prev;
      }
      return [...prev, content];
    });
  })
  .catch(() => {
    // Optionally surface a notification to the user
  });

Comment thread src/renderer/App.tsx
Comment on lines +2569 to 2604
{/* External File Drop Overlay */}
{isDraggingImage && (
<div
className="fixed inset-0 z-[9999] pointer-events-none flex items-center justify-center"
style={{ backgroundColor: `${theme.colors.accent}20` }}
>
<div
className="pointer-events-none rounded-xl border-2 border-dashed p-8 flex flex-col items-center gap-4"
className="pointer-events-none rounded-xl border-2 border-dashed p-8 flex flex-col items-center gap-3"
style={{
borderColor: theme.colors.accent,
backgroundColor: `${theme.colors.bgMain}ee`,
}}
>
<svg
className="w-16 h-16"
style={{ color: theme.colors.accent }}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
<span className="text-lg font-medium" style={{ color: theme.colors.textMain }}>
Drop image to attach
</span>
<span className="text-sm" style={{ color: theme.colors.textDim }}>
or drop a file or folder to insert as @reference
</span>
</div>
</div>
)}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Drop overlay not shown for internal file-panel drags

handleImageDragEnter only sets isDraggingImage = true when e.dataTransfer.types.includes('Files'). When a user drags an item from the internal Files panel, dataTransfer only carries 'application/x-maestro-file-path' (set via setData in FileExplorerPanel), so 'Files' is absent from types and the overlay is never shown. The user gets no visual feedback that they can drop the item onto the chat area. The new hint text "or drop a file or folder to insert as @reference" is therefore invisible during internal drags.

Comment thread src/renderer/App.tsx
Comment on lines 2596 to +2601
<span className="text-lg font-medium" style={{ color: theme.colors.textMain }}>
Drop image to attach
</span>
<span className="text-sm" style={{ color: theme.colors.textDim }}>
or drop a file or folder to insert as @reference
</span>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Misleading overlay primary text for non-image external drops

The overlay's primary heading says "Drop image to attach" even when the user is dragging a non-image file or folder from the filesystem. In that case the file will be inserted as an @reference instead of being staged as an attachment. The two messages are shown side by side with no conditional logic, so a user dropping a README.md will first see "Drop image to attach", which is incorrect. Consider making the heading context-aware or reordering/rephrasing the copy.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/renderer/App.tsx (1)

2569-2601: ⚠️ Potential issue | 🟡 Minor

This overlay still excludes file-panel drags.

The new copy says users can drop a file or folder here, but isDraggingImage is still only turned on for drag payloads whose dataTransfer.types contain Files in src/renderer/hooks/ui/useAppHandlers.ts:164-210. Internal drags from FileExplorerPanel only carry application/x-maestro-file-path, so this overlay never appears for the new in-app @reference flow.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/App.tsx` around lines 2569 - 2601, The overlay is gated by
isDraggingImage which is only set when dataTransfer.types contains native Files,
so internal drags from FileExplorerPanel (which use
application/x-maestro-file-path) never trigger it; update the drag-detection
logic in useAppHandlers (the handler that sets isDraggingImage) to also treat
dataTransfer.types containing "application/x-maestro-file-path" (or the internal
drag MIME used by FileExplorerPanel) as a file/folder drag and set
isDraggingImage accordingly, or rename/expand that state to isDraggingFile and
include both native "Files" and the internal "application/x-maestro-file-path"
type so the overlay appears for the `@reference` in-app flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/renderer/hooks/input/useInputHandlers.ts`:
- Around line 627-644: The code resolves the drag image path against
activeSession.projectRoot first, but internalPath is relative to the explorer
root (session.fullPath), causing wrong absolute paths; change the root selection
to use activeSession.fullPath before activeSession.projectRoot (i.e. set
treeRoot = activeSession?.fullPath ?? activeSession?.projectRoot) so the
absolutePath built for window.maestro.fs.readFile(absolutePath, sshRemoteId)
matches FileExplorerPanel's resolution for internalPath and the thumbnail attach
flow works correctly.

---

Outside diff comments:
In `@src/renderer/App.tsx`:
- Around line 2569-2601: The overlay is gated by isDraggingImage which is only
set when dataTransfer.types contains native Files, so internal drags from
FileExplorerPanel (which use application/x-maestro-file-path) never trigger it;
update the drag-detection logic in useAppHandlers (the handler that sets
isDraggingImage) to also treat dataTransfer.types containing
"application/x-maestro-file-path" (or the internal drag MIME used by
FileExplorerPanel) as a file/folder drag and set isDraggingImage accordingly, or
rename/expand that state to isDraggingFile and include both native "Files" and
the internal "application/x-maestro-file-path" type so the overlay appears for
the `@reference` in-app flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 73ab7060-3623-4d0a-b828-de24c3a38a57

📥 Commits

Reviewing files that changed from the base of the PR and between 54ef1ad and a91e932.

📒 Files selected for processing (4)
  • src/__tests__/renderer/hooks/useInputHandlers.test.ts
  • src/renderer/App.tsx
  • src/renderer/components/FileExplorerPanel.tsx
  • src/renderer/hooks/input/useInputHandlers.ts

Comment thread src/renderer/hooks/input/useInputHandlers.ts Outdated
Folds in code-review feedback on the Files-panel drag-drop work and
fixes a handful of stuck-overlay edge cases.

Code-review feedback (P1/P2 + inline notes):
* `toMentionPath` no longer duplicates the canonical IMAGE_EXTENSIONS;
  imports the existing Set from `utils/fileExplorerIcons/shared`.
* Tree-root resolution for the image staging path uses
  `fullPath ?? projectRoot` to match `FileExplorerPanel.tsx`'s own
  absolute-path construction (the two diverge in worktree/custom-root
  sessions).
* `fs.readFile` rejection now surfaces a flash notification instead of
  producing a silent unhandled promise rejection.
* The drop overlay now activates for both OS-level `Files` drags and
  internal Files-panel drags carrying `application/x-maestro-file-path`.
* Overlay copy rewritten to be accurate for non-image drops:
  "Drop file or folder / Images attach as thumbnails. Anything else
  becomes an @reference."
* Renamed `isDraggingImage` -> `isDraggingFile` and
  `handleImageDrag{Enter,Leave,Over}` -> `handleFileDrag{Enter,Leave,Over}`
  across the app/hooks/tests so the names match the broader trigger.

Overlay UX fixes:
* Suppressed dragenter/leave bubbling for internal drags inside the
  Files panel so the overlay no longer flashes on row-to-row movement.
* Stuck-overlay fallbacks: ESC keydown listener on document,
  document-level `dragleave` window-exit detection (relatedTarget=null
  or viewport-edge coords), and a click-to-dismiss escape hatch on the
  overlay itself.

Tests: 71/71 in useInputHandlers, 145/145 in FileExplorerPanel.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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

🧹 Nitpick comments (2)
src/renderer/hooks/ui/useAppHandlers.ts (1)

221-260: Solid stuck-overlay fallbacks.

The Escape gate on dragCounterRef.current > 0 correctly avoids interfering with non-drag Escape handling, and the cleanup symmetrically removes both new listeners.

One small consideration on handleDocumentDragLeave: relatedTarget === null can occasionally fire mid-drag when the cursor crosses shadow-DOM or iframe boundaries (e.g., embedded webviews used elsewhere in the app), which would prematurely reset the overlay. The viewport-edge coordinate check is the more reliable signal here. If you observe overlay flicker during normal drags over webview content, consider tightening this to require both relatedTarget === null and a viewport-edge coordinate. Not a blocker — current logic should be fine for the common OS-drag-cancel case.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/hooks/ui/useAppHandlers.ts` around lines 221 - 260,
handleDocumentDragLeave can misfire when relatedTarget === null for shadow
DOM/iframe crossings; tighten the left-window detection in
handleDocumentDragLeave by requiring both relatedTarget === null AND coordinates
at/past the viewport edge (use e.clientX/e.clientY vs
window.innerWidth/innerHeight) before calling handleDragEnd, leaving the
existing coordinate checks intact for normal edge-detection; update the
leftWindow boolean logic inside the handleDocumentDragLeave function
accordingly.
src/__tests__/renderer/hooks/useInputHandlers.test.ts (1)

1618-2010: Comprehensive coverage — LGTM.

The new handleDrop test cases cover the important scenarios: internal Files-panel drags (image vs non-image), group-chat exclusion, external drops with relative/absolute paths, multi-file joins, group-chat draftMessage updates, missing path, non-data-url IPC responses, and Windows backslash normalization.

One optional suggestion: every test fixture sets projectRoot === fullPath === '/test', so the path-resolution asymmetry in the implementation (internal drag uses fullPath ?? projectRoot, external drop uses projectRoot ?? fullPath) isn't exercised. A worktree-style fixture (e.g. projectRoot: '/repo', fullPath: '/repo/.worktrees/feat') for both internal and external drops would lock in the intended behavior and surface any future divergence.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/renderer/hooks/useInputHandlers.test.ts` around lines 1618 -
2010, Tests never exercise the asymmetry between using fullPath vs projectRoot
in handleDrop path-resolution; add at least one test fixture where
createMockSession returns differing projectRoot and fullPath (e.g., projectRoot:
'/repo', fullPath: '/repo/.worktrees/feat') and reuse it for both an internal
Files-panel drag (application/x-maestro-file-path) and an external file drop so
you assert the expected relative path behavior in both branches; update the
relevant tests that call useSessionStore.setState and
renderHook/useInputHandlers to use that worktree-style session so the
implementation’s fullPath ?? projectRoot vs projectRoot ?? fullPath logic is
validated.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/renderer/hooks/input/useInputHandlers.ts`:
- Around line 638-654: The catch currently swallows errors from
window.maestro.fs.readFile; update the promise chain to bind the error parameter
in the .catch and report it to Sentry using the captureException utility from
src/utils/sentry.ts (include contextual extras like absolutePath and
sshRemoteId), and optionally also log to console.error for local dev, while
preserving the existing user-facing flash notifications in useInputHandlers.ts
where the readFile call happens.

---

Nitpick comments:
In `@src/__tests__/renderer/hooks/useInputHandlers.test.ts`:
- Around line 1618-2010: Tests never exercise the asymmetry between using
fullPath vs projectRoot in handleDrop path-resolution; add at least one test
fixture where createMockSession returns differing projectRoot and fullPath
(e.g., projectRoot: '/repo', fullPath: '/repo/.worktrees/feat') and reuse it for
both an internal Files-panel drag (application/x-maestro-file-path) and an
external file drop so you assert the expected relative path behavior in both
branches; update the relevant tests that call useSessionStore.setState and
renderHook/useInputHandlers to use that worktree-style session so the
implementation’s fullPath ?? projectRoot vs projectRoot ?? fullPath logic is
validated.

In `@src/renderer/hooks/ui/useAppHandlers.ts`:
- Around line 221-260: handleDocumentDragLeave can misfire when relatedTarget
=== null for shadow DOM/iframe crossings; tighten the left-window detection in
handleDocumentDragLeave by requiring both relatedTarget === null AND coordinates
at/past the viewport edge (use e.clientX/e.clientY vs
window.innerWidth/innerHeight) before calling handleDragEnd, leaving the
existing coordinate checks intact for normal edge-detection; update the
leftWindow boolean logic inside the handleDocumentDragLeave function
accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5a3900e7-2640-4717-b96d-fafb671f3429

📥 Commits

Reviewing files that changed from the base of the PR and between a91e932 and 22f0b35.

📒 Files selected for processing (5)
  • src/__tests__/renderer/hooks/useInputHandlers.test.ts
  • src/renderer/App.tsx
  • src/renderer/components/FileExplorerPanel.tsx
  • src/renderer/hooks/input/useInputHandlers.ts
  • src/renderer/hooks/ui/useAppHandlers.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/renderer/components/FileExplorerPanel.tsx

Comment on lines +638 to +654
void window.maestro.fs
.readFile(absolutePath, sshRemoteId)
.then((content) => {
if (typeof content !== 'string' || !content.startsWith('data:image/')) return;
setStagedImages((prev) => {
if (prev.includes(content)) {
setSuccessFlashNotification('Duplicate image ignored');
setTimeout(() => setSuccessFlashNotification(null), 2000);
return prev;
}
return [...prev, content];
});
})
.catch(() => {
setSuccessFlashNotification('Could not read image file');
setTimeout(() => setSuccessFlashNotification(null), 2000);
});
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

Report fs.readFile failures via captureException.

The catch block discards the error entirely — no captureException, no console.error, not even the err parameter is bound. As per coding guidelines: "Do not silently swallow errors. Let unhandled exceptions bubble up to Sentry for error tracking in production." and "Use Sentry utilities (captureException, captureMessage) from src/utils/sentry.ts for explicit error reporting with context."

The user-facing flash is fine, but observability needs the error too — especially for SSH remote read failures, which would otherwise be invisible to monitoring.

🛡️ Proposed fix
+import { captureException } from '../../utils/sentry';
@@
 				.then((content) => {
 					if (typeof content !== 'string' || !content.startsWith('data:image/')) return;
 					setStagedImages((prev) => {
 						if (prev.includes(content)) {
 							setSuccessFlashNotification('Duplicate image ignored');
 							setTimeout(() => setSuccessFlashNotification(null), 2000);
 							return prev;
 						}
 						return [...prev, content];
 					});
 				})
-				.catch(() => {
+				.catch((err) => {
+					captureException(err instanceof Error ? err : new Error(String(err)), {
+						extra: { context: 'handleDrop.readImage', absolutePath, sshRemoteId },
+					});
 					setSuccessFlashNotification('Could not read image file');
 					setTimeout(() => setSuccessFlashNotification(null), 2000);
 				});

As per coding guideline src/**/*.{ts,tsx}: "Do not silently swallow errors... Use Sentry utilities (captureException, captureMessage) from src/utils/sentry.ts for explicit error reporting with context."

📝 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
void window.maestro.fs
.readFile(absolutePath, sshRemoteId)
.then((content) => {
if (typeof content !== 'string' || !content.startsWith('data:image/')) return;
setStagedImages((prev) => {
if (prev.includes(content)) {
setSuccessFlashNotification('Duplicate image ignored');
setTimeout(() => setSuccessFlashNotification(null), 2000);
return prev;
}
return [...prev, content];
});
})
.catch(() => {
setSuccessFlashNotification('Could not read image file');
setTimeout(() => setSuccessFlashNotification(null), 2000);
});
void window.maestro.fs
.readFile(absolutePath, sshRemoteId)
.then((content) => {
if (typeof content !== 'string' || !content.startsWith('data:image/')) return;
setStagedImages((prev) => {
if (prev.includes(content)) {
setSuccessFlashNotification('Duplicate image ignored');
setTimeout(() => setSuccessFlashNotification(null), 2000);
return prev;
}
return [...prev, content];
});
})
.catch((err) => {
captureException(err instanceof Error ? err : new Error(String(err)), {
extra: { context: 'handleDrop.readImage', absolutePath, sshRemoteId },
});
setSuccessFlashNotification('Could not read image file');
setTimeout(() => setSuccessFlashNotification(null), 2000);
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/hooks/input/useInputHandlers.ts` around lines 638 - 654, The
catch currently swallows errors from window.maestro.fs.readFile; update the
promise chain to bind the error parameter in the .catch and report it to Sentry
using the captureException utility from src/utils/sentry.ts (include contextual
extras like absolutePath and sshRemoteId), and optionally also log to
console.error for local dev, while preserving the existing user-facing flash
notifications in useInputHandlers.ts where the readFile call happens.

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