Skip to content

fix(v3): overhaul drag-and-drop for Linux reliability and simplify Windows implementation#4848

Merged
leaanthony merged 17 commits into
v3-alphafrom
v3/fix-dnd-finally-maybe
Jan 4, 2026
Merged

fix(v3): overhaul drag-and-drop for Linux reliability and simplify Windows implementation#4848
leaanthony merged 17 commits into
v3-alphafrom
v3/fix-dnd-finally-maybe

Conversation

@leaanthony

@leaanthony leaanthony commented Jan 2, 2026

Copy link
Copy Markdown
Member

Summary

Complete overhaul of drag-and-drop across all platforms for reliability and consistency.

Changes by Platform

Linux

  • Rewrite GTK drag handlers to properly intercept external file drops
  • Fix HTML5 internal drag-and-drop being broken when file drop was enabled
  • Add hover effects during file drag operations
  • Block file drops when EnableFileDrop is false (prevents browser navigation)
  • Distinguish between external file drags and internal HTML5 drags using target types

Windows

  • Remove native IDropTarget implementation in favor of JavaScript approach
  • Fix DPI scaling issues with drop coordinates
  • File drops now handled via chrome.webview.postMessageWithAdditionalObjects
  • Inject enableFileDrop flag to JS runtime for proper drop handling

macOS

  • Add native drag-and-drop hover effects with Objective-C delegates
  • Implement performance optimizations: 5-pixel threshold, 50ms debounce, pre-allocated buffers
  • Inject enableFileDrop flag to JS runtime

All Platforms

  • Rename EnableDragAndDrop to EnableFileDrop
  • Rename data-wails-drop-target to data-file-drop-target
  • Rename wails-drop-target-active to file-drop-target-active
  • Initialize window._wails.flags in runtime core to prevent undefined errors
  • Fix bare _wails reference to use window._wails

Breaking Changes

Old New
EnableDragAndDrop EnableFileDrop
data-wails-dropzone data-file-drop-target
wails-dropzone-hover file-drop-target-active
DropZoneDetails DropTargetDetails
DropZoneDetails() DropTargetDetails()
WindowDropZoneFilesDropped WindowFilesDropped

Documentation

  • Added /features/drag-and-drop/files - File drop from OS
  • Added /features/drag-and-drop/html - HTML5 drag & drop within app
  • Added drag-and-drop section to /contributing/runtime-internals
  • Updated /features/windows/options with EnableFileDrop documentation

Examples

  • Updated drag-n-drop example to demonstrate both external file drops and internal HTML5 drag-and-drop
  • Removed redundant html-dnd-api example (functionality merged into drag-n-drop)

Testing

  • Linux (Arch, WebKit2GTK)
  • Windows 11
  • macOS

Summary by CodeRabbit

  • Documentation

    • Added comprehensive Drag & Drop docs and guides (file drops, HTML5 drag-and-drop, platform notes, debugging, examples)
  • Breaking Changes

    • Renamed EnableDragAndDrop → EnableFileDrop
    • Renamed WindowDropZoneFilesDropped → WindowFilesDropped
    • Switched dropzone terminology to drop-target (DropZoneDetails → DropTargetDetails)
  • Platform Improvements

    • Improved file-drop handling and drag-state management on macOS, Linux and Windows
  • Examples

    • Updated File Drop example with streamlined UI and clearer usage steps

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

@coderabbitai

coderabbitai Bot commented Jan 2, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Refactors drag-and-drop to a drop-target model: renames EnableDragAndDrop → EnableFileDrop, DropZoneDetails → DropTargetDetails, WindowDropZoneFilesDropped → WindowFilesDropped; moves Windows handling toward JS, adds native lifecycle hooks for macOS/Linux, updates runtime exports, docs, examples, and renumbers several event IDs.

Changes

Cohort / File(s) Summary
Core API & Types
v3/pkg/application/webview_window_options.go, v3/pkg/application/application.go, v3/pkg/application/context_window_event.go, v3/pkg/application/messageprocessor_window.go, v3/pkg/application/webview_window.go, v3/pkg/application/window.go
Rename: EnableDragAndDropEnableFileDrop, DropZoneDetailsDropTargetDetails; rename params/fields and change Window interface method visibility/signature; remove DragEffect type and related fields.
Event System & IDs
v3/pkg/events/*, v3/internal/generator/collect/known_events.go, v3/internal/runtime/desktop/@wailsio/runtime/src/event_types.ts
Remove WindowDropZoneFilesDropped, introduce/normalize WindowFilesDropped, and decrement many event numeric IDs (global renumbering); update event mappings and known-events registry.
Frontend Runtime (JS) Changes
v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts, .../index.ts, .../system.ts, v3/internal/assetserver/bundledassets/runtime.js, v3/internal/runtime/runtime.go
Replace dropzone logic with drop-targets; add DROP_TARGET_ATTRIBUTE/DROP_TARGET_ACTIVE_CLASS; export handleDragEnter/Leave/Over; remove HandlePlatformFileDrop; initialize window._wails.flags.enableFileDrop.
macOS: drag lifecycle & zero-allocation JS
v3/pkg/application/application_darwin.go, v3/pkg/application/webview_window_darwin.go, v3/pkg/application/webview_window_darwin.m, v3/pkg/application/webview_window_darwin_drag.m, v3/pkg/application/webview_window.go
Add macOS callbacks macosOnDragEnter/Exit/Over, per-window throttle/caching, windowExecJSNoAlloc path for drag-over; call JS handlers and reduce NSLog noise.
Linux: GTK DND & handlers
v3/pkg/application/linux_cgo.go, v3/pkg/application/linux_purego.go, v3/pkg/application/webview_window_linux.go
Add GTK callbacks onDragEnter/onDragLeave/onDragOver, distinguish external file drops vs HTML5 drags, URI->path extraction, enable/disable DND wiring, and execJSDragOver helper.
Windows: remove native OLE DnD
v3/pkg/application/webview_window_windows.go
Remove IDropTarget/OLE-based drop handling; inject enableFileDrop JS flag and switch to JS-driven file-drop messages (file:drop: format); remove convertEffect.
Message routing & processing
v3/pkg/application/messageprocessor_window.go, v3/pkg/application/webview_window.go, v3/pkg/application/webview_window_darwin.go
Rename/repurpose files-dropped processing to use DropTargetDetails and updated message paths; reduce debug logging; add frontend handlers (HandleDragEnter/Over/Leave).
Examples: add/update/remove
v3/examples/drag-n-drop/*, v3/examples/html-dnd-api/*, v3/examples/liquid-glass/main.go
Overhauled drag-n-drop example to File Drop Example (HTML + simplified Go), removed html-dnd-api example files, updated liquid-glass windows to use EnableFileDrop.
Docs & Navigation
docs/astro.config.mjs, docs/src/content/docs/features/drag-and-drop/*, docs/src/content/docs/contributing/runtime-internals.mdx, docs/src/content/docs/features/windows/options.mdx, docs/src/content/docs/quick-start/next-steps.mdx
Add extensive drag-and-drop docs (files/html/runtime internals), update sidebar to autogenerate drag-and-drop entries, update links and Windows option docs (note: duplicate block present).
Build / Tasks / Changelog / Misc
v3/internal/runtime/Taskfile.yaml, v3/Taskfile.yaml, v3/UNRELEASED_CHANGELOG.md, AGENTS.md, v3/internal/assetserver/bundledassets/runtime.js
Add doc build tasks and build:all; update Taskfile and Taskfile lists; add changelog entries about renames/fixes; update runtime export alias mappings; add coderabbit instruction to AGENTS.md.

Sequence Diagram(s)

mermaid
sequenceDiagram
autonumber
participant Browser as Browser DOM
participant Runtime as Runtime JS (window._wails)
participant Go as Go backend
participant OS as Native Platform (Win/mac/Linux)
rect rgba(0,128,128,0.08)
Note over Browser,Runtime: User drags files or internal item
end
Browser->>Runtime: dragenter / dragover / drop (drop-target attrs)
alt Native file drop (OS provides file list)
OS->>Runtime: platform drop callback (handleDragEnter/Over/Leave)
Runtime->>Browser: apply active class to drop target
Browser->>Runtime: ondrop -> collects details (target id)
Runtime->>Go: emit FilesDropped / call WindowFilesDropped with filenames + dropTarget
Go->>Go: addDragAndDropMessage / queue processing
Go->>Application: handleDragAndDropMessage(dropTarget)
else Internal HTML5 drag
Browser->>Browser: dataTransfer moves items between zones (no Go)
end
Note over Go,Runtime: cleanup / remove active class after drop

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

Documentation, Windows, MacOS, Linux, v3-alpha, runtime, Bug, Enhancement, go, size:XXL, lgtm

Suggested reviewers

  • atterpac

Poem

🐰 I hopped from dropzone to target today,
nibbling names that needed a new way.
Files fall in place with a gentle thud,
JS, Go, and native all working in stud—
a rabbit's small cheer for a tidy codeplay.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.42% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(v3): overhaul drag-and-drop for Linux reliability and simplify Windows implementation' clearly and specifically describes the main change—a comprehensive drag-and-drop overhaul across platforms with focus on Linux reliability and Windows simplification.
Description check ✅ Passed The PR description is comprehensive, covering all major changes by platform, breaking changes, documentation updates, examples, and testing. It aligns well with the template structure and provides clear context for the overhaul.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e74ab6b and 1e40e88.

📒 Files selected for processing (3)
  • v3/pkg/application/application.go
  • v3/pkg/application/webview_window.go
  • v3/pkg/application/window.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • v3/pkg/application/webview_window.go
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: leaanthony
Repo: wailsapp/wails PR: 4783
File: v3/pkg/events/events.go:72-100
Timestamp: 2025-12-13T19:52:13.812Z
Learning: In Wails v3, the linux:WindowLoadChanged event was intentionally removed as a breaking change and replaced with four granular WebKit2 load events: linux:WindowLoadStarted, linux:WindowLoadRedirected, linux:WindowLoadCommitted, and linux:WindowLoadFinished. Users should migrate to linux:WindowLoadFinished for detecting when the WebView has finished loading.
📚 Learning: 2024-09-30T06:13:46.595Z
Learnt from: leaanthony
Repo: wailsapp/wails PR: 3763
File: v3/examples/window/main.go:472-475
Timestamp: 2024-09-30T06:13:46.595Z
Learning: In `v3/examples/window/main.go`, `time.Sleep` is used within a goroutine and does not block the UI thread.

Applied to files:

  • v3/pkg/application/application.go
📚 Learning: 2024-09-30T06:14:32.602Z
Learnt from: leaanthony
Repo: wailsapp/wails PR: 3763
File: v3/internal/commands/appimage_testfiles/main.go:295-299
Timestamp: 2024-09-30T06:14:32.602Z
Learning: In `v3/internal/commands/appimage_testfiles/main.go`, `time.Sleep` is used within a goroutine and does not block the UI thread.

Applied to files:

  • v3/pkg/application/application.go
🧬 Code graph analysis (1)
v3/pkg/application/application.go (1)
v3/pkg/w32/idroptarget.go (1)
  • DropTarget (69-77)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Run Go Tests v3 (macos-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
  • GitHub Check: semgrep/ci
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: Analyze (go)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (5)
v3/pkg/application/application.go (4)

242-250: LGTM - Clean rename from DropZoneDetails to DropTargetDetails.

The type rename and documentation update correctly reflect the new "drop target" terminology, consistent with the attribute rename from data-wails-drop-target to data-file-drop-target.


252-258: LGTM - Struct field rename is consistent.

The DropZone field has been correctly renamed to DropTarget to match the new terminology.


262-268: LGTM - Function signature and usage updated consistently.

The function parameter and struct field population have been updated to use dropTarget and DropTarget respectively, maintaining consistency with the broader refactor.


712-722: Migration from DropZone to DropTarget is complete.

The code correctly uses event.DropTarget. Verification confirms all references to the old DropZone terminology have been removed from the v3 codebase. The remaining references to "drop-zone" in the example HTML are CSS class names for styling only, not part of the refactored terminology.

v3/pkg/application/window.go (1)

24-24: No external usage of the method detected.

The method handleDragAndDropMessage is properly unexported and has no external references outside the application package. It's used internally by the application event loop and the Window interface, with implementations in the webview and platform-specific windows.


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.

@leaanthony leaanthony force-pushed the v3/fix-dnd-finally-maybe branch from 223f0a0 to 61d7d61 Compare January 2, 2026 21:51
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jan 2, 2026

Copy link
Copy Markdown

Deploying wails with  Cloudflare Pages  Cloudflare Pages

Latest commit: 1e40e88
Status: ✅  Deploy successful!
Preview URL: https://b1bf2801.wails.pages.dev
Branch Preview URL: https://v3-fix-dnd-finally-maybe.wails.pages.dev

View logs

@leaanthony leaanthony force-pushed the v3/fix-dnd-finally-maybe branch 2 times, most recently from 6ece20d to 1268d46 Compare January 2, 2026 21:58
@leaanthony leaanthony changed the title fix(v3): overhaul drag-and-drop for Windows DPI and Linux reliability fix(v3): overhaul drag-and-drop for Linux reliability and simplify Windows implementation Jan 2, 2026
@leaanthony leaanthony force-pushed the v3/fix-dnd-finally-maybe branch from 1268d46 to 020c618 Compare January 2, 2026 22:05
…ndows

This commit fixes drag-and-drop reliability on Linux and simplifies the
Windows implementation.

## Linux
- Rewrite GTK drag handlers to properly intercept external file drops
- Fix HTML5 internal drag-and-drop being broken when file drop enabled
- Add hover effects during file drag operations
- Fix multiple app instances interfering with each other

## Windows
- Remove native IDropTarget in favor of JavaScript approach (matches v2)
- File drops now handled via chrome.webview.postMessageWithAdditionalObjects

## All Platforms
- Rename EnableDragAndDrop to EnableFileDrop
- Rename data-wails-drop-target to data-file-drop-target
- Rename wails-drop-target-active to file-drop-target-active
- Add comprehensive drag-and-drop documentation

## Breaking Changes
- EnableDragAndDrop -> EnableFileDrop
- data-wails-dropzone -> data-file-drop-target
- wails-dropzone-hover -> file-drop-target-active
- DropZoneDetails -> DropTargetDetails
- Remove WindowDropZoneFilesDropped event (use WindowFilesDropped)
@leaanthony leaanthony force-pushed the v3/fix-dnd-finally-maybe branch from 020c618 to 2c65491 Compare January 3, 2026 01:29
- Add 50ms debouncing to limit drag events to 20/sec (was 120/sec)
- Implement window implementation caching to avoid repeated lookups
- Maintain existing 5-pixel threshold for immediate response
- Keep zero-allocation path with pre-allocated buffers
- Rename linuxDragActive to nativeDragActive for clarity
- Update IMPLEMENTATION.md with optimization details and Windows guidance

Performance improvements:
- 83% reduction in event frequency
- ~6x reduction in CPU/memory usage during drag operations
- Maintains smooth visual feedback with InvokeSync for timer callbacks
@leaanthony leaanthony force-pushed the v3/fix-dnd-finally-maybe branch from 37a8263 to e25284d Compare January 3, 2026 03:10
- Remove incorrect AllowExternalDrag(false) call that was blocking file drops
- Fix message prefix from 'FilesDropped' to 'file:drop:' to match JS runtime
- Fix coordinate parsing for 'file:drop:x:y' format (indices 2,3 not 1,2)
- Add enableFileDrop flag injection to JS runtime during navigation
- Update JS runtime to check enableFileDrop flag before processing drops
- Always call preventDefault() to stop browser navigation on file drags
- Show 'no drop' cursor when file drops are disabled
- Update example to filter file drags from HTML drop zone handlers
- Add documentation for combining file drop with HTML drag-and-drop
- Add disableDND() to intercept and reject external file drags at GTK level
- Show 'no drop' cursor when files are dragged over window
- Allow internal HTML5 drag-and-drop to work normally
- Initialize _wails.flags object in runtime core to prevent undefined errors
- Inject enableFileDrop flag on Linux and macOS (matching Windows)
- Fix bare _wails reference to use window._wails
- Update docs with info about blocked drops and combining with HTML DnD
…izations

- Added draggingUpdated: handler to track mouse movement during drag operations
- Implemented macosOnDragEnter/Exit/Over export functions for real-time hover state
- Fixed JS function call from '_wails.handlePlatformFileDrop' to correct 'wails.Window.HandlePlatformFileDrop'
- Added EnableFileDrop flag checks to prevent hover effects when file drops are disabled
- Renamed linuxDragActive to nativeDragActive for cross-platform consistency

Performance optimizations:
- Added 50ms debounce to reduce event frequency from ~120/sec to ~20/sec
- Implemented 5-pixel movement threshold for immediate response
- Added window caching with sync.Map to avoid repeated lookups
- Zero-allocation JavaScript calls with pre-allocated 128-byte buffer
- Reduced memory usage to ~18 bytes per event (6x reduction)

Build improvements:
- Updated runtime Taskfile to include documentation generation
- Added docs:build task to runtime build process
- Fixed build order: events → docs → runtime

Documentation:
- Added IMPLEMENTATION.md with optimization details
- Included guidance for Windows implementation
@leaanthony leaanthony force-pushed the v3/fix-dnd-finally-maybe branch from 1ad2203 to 533ff8d Compare January 3, 2026 20:31
The drag-n-drop example now demonstrates both external file drops
and internal HTML5 drag-and-drop, making this separate example redundant.
@leaanthony leaanthony marked this pull request as ready for review January 3, 2026 21:18
- Add drag-and-drop section to contributing/runtime-internals.mdx
- Remove IMPLEMENTATION.md from example (content now in proper docs)
- Covers platform differences, debugging tips, and key files

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

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

⚠️ Outside diff range comments (2)
v3/pkg/application/application_darwin.go (1)

198-209: Duplicate import of encoding/json.

The encoding/json package is imported twice: once on line 199 (standard library) and again on line 204 (github.com/goccy/go-json). This will cause a compilation error due to import shadowing without aliasing.

🔎 Proposed fix
 import "C"
 import (
-	"encoding/json"
 	"sync"
 	"time"
 	"unsafe"

 	json "github.com/goccy/go-json"
v3/pkg/application/webview_window.go (1)

1208-1224: HandleDragAndDropMessage: potential data race on eventListeners map

This method reads w.eventListeners[uint(events.Common.WindowFilesDropped)] without taking eventListenersLock, while OnWindowEvent mutates the same map under that lock and HandleWindowEvent clones listeners under an RLock.

In applications that register or remove window listeners at runtime, this unlocked read can race with writes and, in the worst case, panic the program due to concurrent map access.

Consider mirroring the HandleWindowEvent pattern here:

  • Take eventListenersLock.RLock().
  • Clone the slice (e.g. with slices.Clone).
  • Release the lock before iterating.

That would bring FilesDropped handling in line with the rest of the event system.

🧹 Nitpick comments (12)
v3/examples/drag-n-drop/main.go (1)

38-54: Event handling looks correct.

The event handler correctly uses the new API names (WindowFilesDropped, DropTargetDetails) and properly handles the nil case for details.

One minor consistency suggestion: Line 50 could use the captured app variable instead of application.Get() for consistency with the rest of the code.

🔎 Optional: use captured app variable
-		application.Get().Event.Emit("files-dropped", map[string]any{
+		app.Event.Emit("files-dropped", map[string]any{
 			"files":   files,
 			"details": details,
 		})
v3/pkg/application/linux_purego.go (1)

399-399: Consider moving the constant to the package-level const block.

For consistency with other GTK constants defined in lines 28-63 (e.g., GdkHintMinSize, GtkButtonsNone), consider moving GApplicationNonUnique to the package-level const block. This improves discoverability and follows the file's existing pattern.

🔎 Proposed refactor

Move the constant to the package-level const block:

 const (
 	nilPointer pointer = 0
 )
 
 const (
 	GSourceRemove int = 0
 
+	// https://docs.gtk.org/gio/flags.ApplicationFlags.html
+	GApplicationNonUnique = 32 // (1 << 5) - allows multiple instances
+
 	// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/gdkwindow.h#L121
 	GdkHintMinSize = 1 << 1

Then simplify the function:

 func appNew(name string) pointer {
 	// Use NON_UNIQUE to allow multiple instances of the application to run
 	// This matches the behavior of gtk_init/gtk_main used in v2
-	// G_APPLICATION_NON_UNIQUE = (1 << 5) = 32
-	GApplicationNonUnique := uint(32)
 
 	// Name is already sanitized by sanitizeAppName() in application_linux.go
 	identifier := fmt.Sprintf("org.wails.%s", name)
 
 	return pointer(gtkApplicationNew(identifier, GApplicationNonUnique))
 }
v3/pkg/application/linux_cgo.go (3)

287-303: Global drag state may cause issues with multiple windows.

The drag_entered and internal_drag_active flags are global (static) rather than per-window. If an application has multiple windows, dragging files could lead to inconsistent state. For example, if a user starts dragging over window A, then moves to window B, the drag_entered flag would still be set from window A's perspective.

Consider storing this state per-widget using g_object_set_data/g_object_get_data (similar to how drop-x/drop-y are stored), or verify that GTK's drag mechanics inherently prevent this scenario.


233-237: Consider documenting the magic number for target_type.

The check target_type != 2 uses a magic number. While it works, adding a comment or constant explaining that 2 corresponds to the text/uri-list target atom position in the target list would improve maintainability.


1078-1101: Edge case: writeInt could overflow with math.MinInt.

The recursive call writeInt(buf[1:], -n) at line 1083 would overflow if n == math.MinInt64 since -math.MinInt64 cannot be represented. While screen coordinates will never reach this value, the function as a general utility has this edge case.

Also, there's no bounds checking on the buffer slice, though the 64-byte buffer is more than sufficient for the expected use case.

v3/pkg/application/webview_window_darwin.go (1)

1127-1132: Consider appending to existing flags instead of string concatenation.

The runtime flags are already processed by runtime.Core() which marshals flags to JSON. Appending window._wails.flags.enableFileDrop=...; as a separate statement works but creates a redundant pattern. The enableFileDrop flag could be added to the flags map before calling Core().

🔎 Proposed alternative approach

If you want the flag to be part of the unified flags object:

// In the hook registration, before calling Core:
flags := globalApplication.impl.GetFlags(globalApplication.options)
flags["enableFileDrop"] = result.parent.options.EnableFileDrop
js := runtime.Core(flags)
result.execJS(js)

This would result in a single window._wails.flags = {..., enableFileDrop: true} assignment instead of two separate statements.

v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts (2)

629-672: HandlePlatformFileDrop correctly targets [data-file-drop-target] but assumes _wails.flags exists

Targeting the nearest drop target via getDropTargetElement(elementFromPoint(x, y)) and sending FilesDropped with full elementDetails is the right contract for the backend, and the early enableFileDrop === false guard matches the rest of the runtime.

If you expect any third‑party code to run custom JS before runtime.Core initialises _wails.flags, you might want to defensively ensure the object exists before assignment/reads (e.g. window._wails.flags = window._wails.flags || {} in the core bootstrap), but given the existing flag usage this is more of a hardening tweak than a bug.


694-829: Global drag listeners: good isolation to file drags; minor risk only for custom “Files” DnD

The global dragenter/over/leave/drop handlers correctly:

  • Filter to dataTransfer.types.includes('Files') so non‑file HTML5 DnD is left alone.
  • Always preventDefault() for file drags to avoid navigation.
  • Respect enableFileDrop by forcing a “no drop” cursor when disabled.
  • Keep hover state in currentDropTarget and clean it up on leave/drop.

Only potential edge case is apps that perform custom in‑app HTML5 drag‑and‑drop that also uses the "Files" type; those flows will inherit this global behavior. If that’s a supported scenario, it might be worth documenting that data-file-drop-target is reserved for OS file drops and that internal DnD should avoid advertising "Files" unless they want this pipeline.

v3/pkg/application/webview_window_windows.go (2)

1975-1990: Stale TODO around passing EnableFileDrop to JS runtime

The comment still says “TODO: Pass EnableFileDrop setting to JS runtime…”, but navigationCompleted now does exactly that via:

w.execJS(fmt.Sprintf("window._wails.flags.enableFileDrop = %v;", w.parent.options.EnableFileDrop))

Consider updating or removing the TODO to avoid confusion for future maintainers.


2217-2284: processMessageWithAdditionalObjects: file:drop:x:y handling and coord usage are sound; clean up comments when convenient

The new file:drop: branch:

  • Correctly extracts ICoreWebView2File objects and their paths into filenames.
  • Parses x/y from the "file:drop:x:y" message parts.
  • Treats those coords as already webview‑relative (matching event.clientX/Y in JS) and forwards them directly to InitiateFrontendDropProcessing.

This matches the runtime.js behavior and avoids the previous, more complex window→webview coordinate conversion.

Two minor nits you might address later:

  • The nearby comments still mention converting between window and webview coordinates, but the current code just passes through the values.
  • convertWindowToWebviewCoordinates appears unused after this change; if so, consider removing it in a follow‑up to keep the implementation tidy.
v3/pkg/application/webview_window.go (1)

1464-1521: New drag handlers and throttle are reasonable; consider per‑window hover state only if multi‑window drags become an issue

HandleDragEnter/Over/Leave:

  • Guard on impl != nil, !isDestroyed(), and runtimeLoaded.
  • Reset dragHover at the start of a drag session.
  • Throttle HandleDragOver updates to movements of ≥5px in either axis.
  • Use a zero‑alloc execJSDragOver path when the platform impl provides it, falling back to formatted JS otherwise.

This is a solid, low‑overhead way to keep CSS hover state in sync with native drag coordinates. The shared dragHover struct is process‑wide; if you ever support concurrent drags across multiple windows, you might want to move that state onto WebviewWindow, but for current desktop behavior it’s acceptable.

v3/pkg/application/messageprocessor_window.go (1)

60-61: WindowFilesDropped handling and DropTarget payload decoding are aligned with the runtime

  • WindowFilesDropped = 50 and windowMethodNames[WindowFilesDropped] = "FilesDropped" match the TS/runtime method ID.
  • fileDropPayload’s filenames/x/y/elementDetails fields mirror the JS payload from HandlePlatformFileDrop.
  • The new DropTargetDetails is correctly populated from ElementDetailsPayload, and the resulting dragAndDropMessage includes windowId, filenames, and DropTarget as expected.
  • The error paths for invalid payloads and non‑WebviewWindow targets are reasonable.

Only minor consideration: the [DragDropDebug] info log may be noisy in production; if that becomes an issue, you could guard it behind a debug flag or lower log level later.

Also applies to: 114-117, 356-387, 402-415

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 53cd30a and a0d56d8.

⛔ Files ignored due to path filters (1)
  • v3/internal/runtime/desktop/@wailsio/runtime/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (47)
  • docs/astro.config.mjs
  • docs/src/content/docs/contributing/runtime-internals.mdx
  • docs/src/content/docs/features/drag-and-drop/files.mdx
  • docs/src/content/docs/features/drag-and-drop/html.mdx
  • docs/src/content/docs/features/windows/options.mdx
  • docs/src/content/docs/quick-start/next-steps.mdx
  • v3/UNRELEASED_CHANGELOG.md
  • v3/examples/drag-n-drop/README.md
  • v3/examples/drag-n-drop/assets/index.html
  • v3/examples/drag-n-drop/main.go
  • v3/examples/html-dnd-api/README.md
  • v3/examples/html-dnd-api/assets/index.html
  • v3/examples/html-dnd-api/main.go
  • v3/examples/liquid-glass/main.go
  • v3/internal/assetserver/bundledassets/runtime.debug.js
  • v3/internal/assetserver/bundledassets/runtime.js
  • v3/internal/generator/collect/known_events.go
  • v3/internal/runtime/Taskfile.yaml
  • v3/internal/runtime/desktop/@wailsio/runtime/src/event_types.ts
  • v3/internal/runtime/desktop/@wailsio/runtime/src/index.ts
  • v3/internal/runtime/desktop/@wailsio/runtime/src/system.ts
  • v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts
  • v3/internal/runtime/runtime.go
  • v3/pkg/application/application.go
  • v3/pkg/application/application_darwin.go
  • v3/pkg/application/context_window_event.go
  • v3/pkg/application/linux_cgo.go
  • v3/pkg/application/linux_purego.go
  • v3/pkg/application/messageprocessor_window.go
  • v3/pkg/application/webview_window.go
  • v3/pkg/application/webview_window_darwin.go
  • v3/pkg/application/webview_window_darwin.m
  • v3/pkg/application/webview_window_darwin_drag.m
  • v3/pkg/application/webview_window_linux.go
  • v3/pkg/application/webview_window_options.go
  • v3/pkg/application/webview_window_options_test.go
  • v3/pkg/application/webview_window_windows.go
  • v3/pkg/application/window.go
  • v3/pkg/events/events.go
  • v3/pkg/events/events.txt
  • v3/pkg/events/events_darwin.h
  • v3/pkg/events/events_ios.h
  • v3/pkg/events/events_linux.h
  • v3/pkg/events/known_events.go
  • v3/test/dnd-test/assets/index.html
  • v3/test/dnd-test/dnd-test
  • v3/test/dnd-test/main.go
💤 Files with no reviewable changes (8)
  • v3/examples/html-dnd-api/main.go
  • v3/internal/runtime/desktop/@wailsio/runtime/src/event_types.ts
  • v3/pkg/events/events.txt
  • v3/pkg/application/webview_window_options_test.go
  • v3/internal/runtime/desktop/@wailsio/runtime/src/system.ts
  • v3/examples/html-dnd-api/README.md
  • v3/pkg/application/webview_window_darwin.m
  • v3/examples/html-dnd-api/assets/index.html
🧰 Additional context used
🧠 Learnings (13)
📓 Common learnings
Learnt from: leaanthony
Repo: wailsapp/wails PR: 4783
File: v3/pkg/events/events.go:72-100
Timestamp: 2025-12-13T19:52:13.812Z
Learning: In Wails v3, the linux:WindowLoadChanged event was intentionally removed as a breaking change and replaced with four granular WebKit2 load events: linux:WindowLoadStarted, linux:WindowLoadRedirected, linux:WindowLoadCommitted, and linux:WindowLoadFinished. Users should migrate to linux:WindowLoadFinished for detecting when the WebView has finished loading.
📚 Learning: 2025-12-13T19:52:13.812Z
Learnt from: leaanthony
Repo: wailsapp/wails PR: 4783
File: v3/pkg/events/events.go:72-100
Timestamp: 2025-12-13T19:52:13.812Z
Learning: In Wails v3, the linux:WindowLoadChanged event was intentionally removed as a breaking change and replaced with four granular WebKit2 load events: linux:WindowLoadStarted, linux:WindowLoadRedirected, linux:WindowLoadCommitted, and linux:WindowLoadFinished. Users should migrate to linux:WindowLoadFinished for detecting when the WebView has finished loading.

Applied to files:

  • v3/UNRELEASED_CHANGELOG.md
  • v3/pkg/application/webview_window_linux.go
  • v3/pkg/application/webview_window_darwin_drag.m
  • v3/pkg/application/webview_window_options.go
  • v3/internal/generator/collect/known_events.go
  • v3/pkg/application/webview_window_windows.go
  • v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts
  • v3/pkg/events/events.go
  • v3/pkg/application/webview_window.go
  • v3/pkg/application/webview_window_darwin.go
  • v3/internal/runtime/desktop/@wailsio/runtime/src/index.ts
  • v3/examples/liquid-glass/main.go
  • v3/internal/assetserver/bundledassets/runtime.js
  • v3/pkg/events/known_events.go
📚 Learning: 2025-12-29T08:02:06.122Z
Learnt from: popaprozac
Repo: wailsapp/wails PR: 4839
File: docs/src/content/docs/reference/window.mdx:616-620
Timestamp: 2025-12-29T08:02:06.122Z
Learning: In Wails v3, document window creation using app.Window.New() and app.Window.NewWithOptions(...). Do not show or reference app.NewWebviewWindow() or app.NewWebviewWindowWithOptions(...). The Application struct exposes a Window field of type *WindowManager that provides these methods. When updating docs, replace examples accordingly and mention that WindowManager methods create and configure new windows.

Applied to files:

  • docs/src/content/docs/quick-start/next-steps.mdx
  • docs/src/content/docs/contributing/runtime-internals.mdx
  • docs/src/content/docs/features/drag-and-drop/html.mdx
  • docs/src/content/docs/features/drag-and-drop/files.mdx
  • docs/src/content/docs/features/windows/options.mdx
📚 Learning: 2025-10-17T23:16:11.570Z
Learnt from: Sammy-T
Repo: wailsapp/wails PR: 4570
File: v2/internal/frontend/desktop/linux/window_webkit6.go:97-108
Timestamp: 2025-10-17T23:16:11.570Z
Learning: For webkit_6/GTK4 builds in v2/internal/frontend/desktop/linux/window_webkit6.go, GTK widget creation should not be wrapped in invokeOnMainThread. The activation mechanism (activateWg + onActivate export) already handles thread safety, and additional wrapping would cause issues.

Applied to files:

  • v3/pkg/application/webview_window_linux.go
  • v3/pkg/application/linux_purego.go
  • v3/pkg/application/webview_window.go
  • v3/pkg/application/webview_window_darwin.go
  • v3/pkg/application/linux_cgo.go
  • v3/internal/runtime/desktop/@wailsio/runtime/src/index.ts
  • v3/examples/liquid-glass/main.go
📚 Learning: 2025-08-08T09:13:16.916Z
Learnt from: APshenkin
Repo: wailsapp/wails PR: 4480
File: v2/internal/frontend/desktop/darwin/message.h:17-19
Timestamp: 2025-08-08T09:13:16.916Z
Learning: In Wails v2 bindings origin verification, processBindingMessage intentionally has different signatures across platforms: Darwin includes an isMainFrame bool (WKWebKit provides it), Linux uses two params (message, source) as WebKitGTK doesn’t expose main-frame info there, and Windows handles origin checks in Go via WebView2 sender/args without a C bridge. This divergence is acceptable/expected per maintainer (APshenkin).

Applied to files:

  • v3/pkg/application/webview_window_linux.go
  • v3/pkg/application/webview_window_darwin_drag.m
  • v3/pkg/application/webview_window_windows.go
  • v3/pkg/application/webview_window.go
  • v3/pkg/application/webview_window_darwin.go
  • v3/internal/runtime/desktop/@wailsio/runtime/src/index.ts
📚 Learning: 2025-12-29T08:02:06.122Z
Learnt from: popaprozac
Repo: wailsapp/wails PR: 4839
File: docs/src/content/docs/reference/window.mdx:616-620
Timestamp: 2025-12-29T08:02:06.122Z
Learning: In Wails v3, the correct API for creating windows is `app.Window.New()` and `app.Window.NewWithOptions(...)`, not `app.NewWebviewWindow()` or `app.NewWebviewWindowWithOptions(...)`. The Application struct exposes a Window field of type *WindowManager that provides these methods.

Applied to files:

  • v3/pkg/application/webview_window_linux.go
  • v3/internal/runtime/runtime.go
  • v3/pkg/application/linux_purego.go
  • v3/examples/drag-n-drop/main.go
  • v3/pkg/application/webview_window_options.go
  • v3/test/dnd-test/main.go
  • v3/pkg/application/webview_window_windows.go
  • v3/pkg/events/events.go
  • v3/pkg/application/webview_window.go
  • v3/pkg/application/webview_window_darwin.go
  • v3/pkg/application/messageprocessor_window.go
  • v3/examples/liquid-glass/main.go
📚 Learning: 2024-09-20T23:34:29.841Z
Learnt from: nixpare
Repo: wailsapp/wails PR: 3763
File: v3/examples/keybindings/main.go:16-17
Timestamp: 2024-09-20T23:34:29.841Z
Learning: In the codebase, `application.Options.KeyBindings` uses the `application.Window` type, whereas `application.WebviewWindowOptions.KeyBindings` uses `*application.WebviewWindow`. This is intentional and acceptable.

Applied to files:

  • v3/pkg/application/webview_window_linux.go
  • v3/pkg/application/webview_window_options.go
  • docs/src/content/docs/features/windows/options.mdx
  • v3/pkg/application/webview_window.go
  • v3/pkg/application/webview_window_darwin.go
  • v3/examples/liquid-glass/main.go
📚 Learning: 2024-09-30T06:13:46.595Z
Learnt from: leaanthony
Repo: wailsapp/wails PR: 3763
File: v3/examples/window/main.go:472-475
Timestamp: 2024-09-30T06:13:46.595Z
Learning: In `v3/examples/window/main.go`, `time.Sleep` is used within a goroutine and does not block the UI thread.

Applied to files:

  • v3/examples/drag-n-drop/main.go
  • v3/test/dnd-test/main.go
  • v3/pkg/application/application.go
  • v3/examples/liquid-glass/main.go
📚 Learning: 2024-09-30T06:14:32.602Z
Learnt from: leaanthony
Repo: wailsapp/wails PR: 3763
File: v3/internal/commands/appimage_testfiles/main.go:295-299
Timestamp: 2024-09-30T06:14:32.602Z
Learning: In `v3/internal/commands/appimage_testfiles/main.go`, `time.Sleep` is used within a goroutine and does not block the UI thread.

Applied to files:

  • v3/examples/drag-n-drop/main.go
  • v3/test/dnd-test/main.go
  • v3/pkg/application/application.go
📚 Learning: 2024-12-03T07:49:16.593Z
Learnt from: stavros-k
Repo: wailsapp/wails PR: 3917
File: docs/src/assets/contributors.astro:0-0
Timestamp: 2024-12-03T07:49:16.593Z
Learning: The table in 'docs/src/assets/contributors.astro' is autogenerated by another bot, so manual changes to it would be overwritten and are unnecessary.

Applied to files:

  • docs/astro.config.mjs
📚 Learning: 2025-01-15T22:33:30.639Z
Learnt from: fbbdev
Repo: wailsapp/wails PR: 4001
File: v3/internal/generator/testdata/output/lang=TS/UseInterfaces=true/UseNames=false/github.com/wailsapp/wails/v3/internal/generator/testcases/function_single_internal/greetservice.ts:0-0
Timestamp: 2025-01-15T22:33:30.639Z
Learning: In the Wails framework's TypeScript bindings, the import path "/wails/runtime.js" is intentionally absolute and should not be changed to a relative path.

Applied to files:

  • v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts
  • v3/internal/runtime/desktop/@wailsio/runtime/src/index.ts
📚 Learning: 2025-01-15T22:30:08.796Z
Learnt from: fbbdev
Repo: wailsapp/wails PR: 4001
File: v3/internal/generator/testcases/no_bindings_here/person.go:9-9
Timestamp: 2025-01-15T22:30:08.796Z
Learning: When dealing with fixed-length arrays in Go structs, encoding/json automatically handles bounds checking during unmarshaling, returning an error if the JSON array length exceeds the Go array length. No additional runtime validation is needed.

Applied to files:

  • v3/pkg/application/application_darwin.go
📚 Learning: 2025-02-13T01:05:02.267Z
Learnt from: fbbdev
Repo: wailsapp/wails PR: 4066
File: v3/pkg/application/messageprocessor_call.go:174-174
Timestamp: 2025-02-13T01:05:02.267Z
Learning: When handling JSON marshaling errors in Wails v3, the error message from json.Marshal provides sufficient debugging context. Logging raw data is unnecessary and could make logs harder to read.

Applied to files:

  • v3/pkg/application/messageprocessor_window.go
🧬 Code graph analysis (13)
v3/pkg/application/webview_window_linux.go (1)
v3/internal/runtime/runtime.go (1)
  • Core (11-21)
v3/test/dnd-test/main.go (3)
v3/pkg/application/application.go (1)
  • DropTargetDetails (244-250)
v3/pkg/application/window.go (1)
  • Window (9-104)
v3/pkg/application/messageprocessor_window.go (1)
  • WindowFilesDropped (60-60)
v3/pkg/application/application.go (1)
v3/pkg/w32/idroptarget.go (1)
  • DropTarget (69-77)
v3/pkg/application/context_window_event.go (1)
v3/pkg/application/application.go (1)
  • DropTargetDetails (244-250)
v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts (3)
v3/internal/assetserver/bundledassets/runtime.debug.js (10)
  • element (1154-1154)
  • element (1324-1324)
  • targetElement (751-751)
  • dropTarget (752-752)
  • dropTarget (1155-1155)
  • elementDetails (1159-1163)
  • i (67-67)
  • i (1164-1164)
  • i (1561-1561)
  • attr (1165-1165)
v3/internal/assetserver/bundledassets/runtime.js (12)
  • y (1-1)
  • i (1-1)
  • i (1-1)
  • i (1-1)
  • i (1-1)
  • i (1-1)
  • i (1-1)
  • i (1-1)
  • i (1-1)
  • i (1-1)
  • i (1-1)
  • i (1-1)
v2/internal/frontend/runtime/desktop/draganddrop.js (1)
  • files (145-145)
v3/pkg/events/events_linux.h (1)
v3/pkg/events/events_android.go (1)
  • MAX_EVENTS (7-7)
v3/pkg/application/webview_window.go (3)
v3/pkg/application/application.go (1)
  • DropTargetDetails (244-250)
v3/pkg/events/events.go (1)
  • Common (6-6)
v3/pkg/application/messageprocessor_window.go (1)
  • WindowFilesDropped (60-60)
v3/pkg/application/application_darwin.go (3)
v3/pkg/application/window.go (1)
  • Window (9-104)
v3/pkg/application/mainthread.go (1)
  • InvokeSync (23-32)
v3/pkg/application/webview_window.go (1)
  • WebviewWindow (142-176)
v3/pkg/application/webview_window_darwin.go (1)
v3/internal/runtime/runtime.go (1)
  • Core (11-21)
v3/pkg/application/linux_cgo.go (2)
v3/pkg/application/window.go (1)
  • Window (9-104)
v3/pkg/application/webview_window.go (1)
  • WebviewWindow (142-176)
v3/internal/runtime/desktop/@wailsio/runtime/src/index.ts (1)
v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts (3)
  • handleDragEnter (169-169)
  • handleDragLeave (169-169)
  • handleDragOver (169-169)
v3/examples/liquid-glass/main.go (2)
v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts (2)
  • Width (597-599)
  • Height (309-311)
v3/pkg/application/webview_window_options.go (1)
  • WindowXY (29-29)
v3/internal/assetserver/bundledassets/runtime.js (1)
v3/internal/assetserver/bundledassets/runtime.debug.js (4)
  • i (67-67)
  • i (1164-1164)
  • i (1561-1561)
  • j (1564-1564)
🪛 Biome (2.1.2)
v3/internal/assetserver/bundledassets/runtime.js

[error] 1-1: This array contains an empty slots..

The presences of empty slots may cause incorrect information and might be a typo.
Unsafe fix: Replace hole with undefined

(lint/suspicious/noSparseArray)


[error] 1-1: Do not add then to a class.

(lint/suspicious/noThenProperty)


[error] 1-1: Shouldn't redeclare 'i'. Consider to delete it or rename it.

'i' is defined here:

(lint/suspicious/noRedeclare)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)

🪛 LanguageTool
docs/src/content/docs/features/drag-and-drop/files.mdx

[style] ~8-~8: Try using a synonym here to strengthen your writing.
Context: ...ich only works within the browser, this gives you access to actual file paths on disk. #...

(GIVE_PROVIDE)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (66)
v3/examples/drag-n-drop/README.md (3)

67-73: Clarify platform testing status in the Status table.

The PR objectives indicate that "Linux verified (Arch, WebKit2GTK); Windows and macOS still need testing," but the Status table marks all platforms as "Working." This creates a discrepancy between the PR summary and the documentation. Either the table should reflect the testing status (e.g., "In Progress" for Windows/macOS), or the PR objectives need updating if these platforms have been tested since.


1-73: Documentation quality and structure is solid.

The README effectively communicates the file drop feature across all platforms, clearly distinguishes it from HTML5 internal drag-and-drop (via the "See also" section), and provides practical code examples that reflect the renamed APIs (EnableFileDrop, data-file-drop-target, WindowFilesDropped, DropTargetDetails). The four-step "How it works" process and styling documentation are well-organized and easy to follow.


21-23: No issues found - example code API method names are correct.

The method names DroppedFiles() and DropTargetDetails() in the example code match the actual implementation in v3/pkg/application/context_window_event.go, and both return the expected types ([]string and *DropTargetDetails respectively).

Likely an incorrect or invalid review comment.

v3/examples/drag-n-drop/main.go (3)

1-12: LGTM! Clean setup.

The imports and embedded assets follow standard Wails v3 patterns.


14-36: LGTM! Correct usage of the new API.

The window configuration correctly uses EnableFileDrop: true, which aligns with the API rename from EnableDragAndDrop in this PR.


56-59: LGTM! Appropriate error handling.

Using log.Fatal for the app run error is suitable for this example application.

v3/pkg/application/linux_purego.go (1)

396-404: Implementation is correct and consistent with the CGO version.

Both the CGO implementation (linux_cgo.go) and purego implementation (linux_purego.go) use G_APPLICATION_NON_UNIQUE (value 32). This change aligns both backends and intentionally matches the v2 behavior with gtk_init/gtk_main.

The application-level single-instance feature (via SingleInstanceOptions) is independent of this GTK flag and uses DBus on Linux, so it continues to work unchanged. No breaking changes or user communication needed.

v3/internal/runtime/runtime.go (1)

9-9: LGTM!

The initialization of window._wails.flags as an empty object ensures the flags namespace exists before any subsequent code attempts to access it, preventing potential undefined access errors in JavaScript.

v3/test/dnd-test/assets/index.html (1)

132-222: LGTM!

The test page correctly demonstrates both internal HTML5 drag-and-drop and external file drop scenarios. The event handlers properly distinguish between the two use cases, and the logging provides useful debugging output.

v3/UNRELEASED_CHANGELOG.md (1)

23-45: LGTM!

The changelog thoroughly documents all breaking changes, bug fixes, and removed features. The entries are clear and actionable for users migrating to the new API.

v3/internal/generator/collect/known_events.go (1)

8-222: LGTM!

The known events map is properly updated to reflect the new event naming convention (common:WindowFilesDropped) and includes the new platform-specific drag lifecycle events for macOS (WindowFileDraggingEntered, WindowFileDraggingExited, WindowFileDraggingPerformed).

v3/examples/drag-n-drop/assets/index.html (3)

63-71: LGTM!

The CSS correctly uses the new file-drop-target-active class name that aligns with the PR's documented breaking changes.


387-432: LGTM!

The internal drop zone handlers correctly filter out external file drags by checking e.dataTransfer?.types.includes('Files'). This ensures clean separation between OS file drops (handled by Wails) and in-page HTML5 drag-and-drop.


341-365: No action required. Event name is correct.

The JavaScript code correctly listens for 'files-dropped', which matches the custom event emitted by the Go backend in v3/examples/drag-n-drop/main.go: application.Get().Event.Emit("files-dropped", ...). While WindowFilesDropped is the system event type, this example implements custom drag-and-drop handling via explicit event emission, not the system event.

v3/internal/runtime/Taskfile.yaml (1)

38-112: LGTM!

The new tasks are well-organized with proper dependencies and clear summaries. The build:all task correctly sequences the generation and build steps, and the clean task provides useful artifact cleanup.

v3/pkg/application/linux_cgo.go (4)

1802-1862: LGTM!

The Go exports for drag callbacks correctly retrieve the window by ID and use type assertions with proper nil/ok checks. The onUriList function appropriately calls InitiateFrontendDropProcessing which aligns with the new drop-target architecture.


1021-1031: LGTM!

The enableDND and disableDND methods correctly pass the window ID using the established pattern (casting to uintptr as a pointer value). The comments clearly explain this approach matches other signal handlers in the file.


398-426: LGTM!

The blocked handlers correctly distinguish between external file drags (which are consumed to prevent navigation) and internal HTML5 drags (which pass through to WebKit). The gdk_drag_status(context, 0, time) call appropriately shows a "no drop" cursor for disabled file drop zones.


26-28: LGTM!

The change to G_APPLICATION_NON_UNIQUE aligns with v2 behavior as noted in the comment, allowing multiple instances of the application to run simultaneously.

docs/src/content/docs/quick-start/next-steps.mdx (1)

27-27: LGTM!

The updated link correctly points to the new drag-and-drop documentation structure introduced in this PR.

v3/internal/runtime/desktop/@wailsio/runtime/src/index.ts (2)

29-29: LGTM!

The import correctly brings in the drag event handlers needed for Linux/GTK drag event interception.


73-76: LGTM!

The drag handlers are correctly exposed through window._wails to enable GTK's native drag event interception on Linux. This pattern is consistent with other platform handlers like handlePlatformFileDrop.

v3/pkg/events/events_ios.h (1)

9-32: LGTM!

The event ID renumbering is consistent with the PR-wide realignment following the removal of WindowDropZoneFilesDropped. All IDs are systematically decremented by one while preserving the macro names.

v3/pkg/application/webview_window_linux.go (1)

285-289: LGTM!

The rename from EnableDragAndDrop to EnableFileDrop is correct, and the explicit handling of both enabled and disabled cases with disableDND() is good defensive programming.

docs/src/content/docs/features/windows/options.mdx (1)

549-602: No duplication issue exists.

The file contains only one ## Input Options section (line 549) with a single ### EnableFileDrop entry (line 551). The documentation is correctly structured without duplicates.

Likely an incorrect or invalid review comment.

docs/src/content/docs/contributing/runtime-internals.mdx (1)

173-245: Solid documentation for the new drag-and-drop architecture.

This section provides a clear explanation of the JavaScript-first approach and platform-specific implementations. The flow diagram, platform comparison table, and debugging tips are particularly helpful for contributors.

A few minor observations:

  • Line 232: The TypeScript file path references src/window.ts, but verify this matches the actual location in the runtime after the PR changes.
  • The C code snippet for Linux drag detection is a helpful reference for understanding the GTK signal handling.
v3/test/dnd-test/main.go (2)

35-44: Good defensive null check on DropTargetDetails.

The nil check before accessing details.ElementID is appropriate since drops outside defined drop targets may not have element details.


26-44: Add Show() call to display the window.

The window is created with NewWithOptions() but never shown. All examples in the codebase follow the pattern of calling Show() after window creation. Add win.Show() after line 44.

Diff
	// Listen for file drop events
	win.OnWindowEvent(events.Common.WindowFilesDropped, func(event *application.WindowEvent) {
		files := event.Context().DroppedFiles()
		details := event.Context().DropTargetDetails()

		log.Printf("[Go] Files dropped: %v", files)
		if details != nil {
			log.Printf("[Go] Drop target: id=%s, classes=%v, x=%d, y=%d",
				details.ElementID, details.ClassList, details.X, details.Y)
		}
	})
+	win.Show()

	err := app.Run()
⛔ Skipped due to learnings
Learnt from: popaprozac
Repo: wailsapp/wails PR: 4839
File: docs/src/content/docs/reference/window.mdx:616-620
Timestamp: 2025-12-29T08:02:06.122Z
Learning: In Wails v3, the correct API for creating windows is `app.Window.New()` and `app.Window.NewWithOptions(...)`, not `app.NewWebviewWindow()` or `app.NewWebviewWindowWithOptions(...)`. The Application struct exposes a Window field of type *WindowManager that provides these methods.
Learnt from: leaanthony
Repo: wailsapp/wails PR: 3763
File: v3/examples/window/main.go:472-475
Timestamp: 2024-09-30T06:13:46.595Z
Learning: In `v3/examples/window/main.go`, `time.Sleep` is used within a goroutine and does not block the UI thread.
Learnt from: leaanthony
Repo: wailsapp/wails PR: 3763
File: v3/examples/window/main.go:472-475
Timestamp: 2024-10-08T22:11:37.054Z
Learning: In `v3/examples/window/main.go`, `time.Sleep` is used within a goroutine and does not block the UI thread.
Learnt from: nixpare
Repo: wailsapp/wails PR: 3763
File: v3/examples/keybindings/main.go:16-17
Timestamp: 2024-09-20T23:34:29.841Z
Learning: In the codebase, `application.Options.KeyBindings` uses the `application.Window` type, whereas `application.WebviewWindowOptions.KeyBindings` uses `*application.WebviewWindow`. This is intentional and acceptable.
v3/pkg/events/known_events.go (2)

9-34: Common events updated with new WindowFilesDropped event.

The common events section correctly includes common:WindowFilesDropped (line 17), replacing the previous WindowDropZoneFilesDropped. The event set aligns with the PR's naming conventions.


138-140: New macOS drag lifecycle events added.

These three events (WindowFileDraggingEntered, WindowFileDraggingExited, WindowFileDraggingPerformed) enable the hover effects and drag handling documented in the PR for macOS.

docs/src/content/docs/features/drag-and-drop/html.mdx (3)

1-79: Well-structured HTML5 drag-and-drop documentation.

The documentation covers the fundamentals clearly: making elements draggable, defining drop zones with preventDefault(), and styling drag hover states. The explanation of why preventDefault() on dragover is required is helpful for developers unfamiliar with the HTML5 DnD API.


127-133: Correct solution for the dragleave flickering issue.

Using zone.contains(e.relatedTarget) to check if the pointer is actually leaving the zone (rather than entering a child element) is the correct approach. This is a common gotcha with HTML5 drag-and-drop.


177-207: Clear guidance on distinguishing file drags from internal drags.

The pattern of checking e.dataTransfer?.types.includes('Files') is correct and well-documented. This is essential for apps using both OS file drops and internal drag-and-drop.

docs/astro.config.mjs (1)

174-178: Sidebar now auto-generates from the drag-and-drop directory.

This change aligns with the pattern used for other feature sections (Clipboard, Browser, Keyboard, etc.) and will automatically include new documentation pages added to features/drag-and-drop/.

docs/src/content/docs/features/drag-and-drop/files.mdx (2)

59-101: Clear documentation for the WindowFilesDropped event and drop target routing.

The examples for detecting dropped files and routing based on ElementID are well-structured and demonstrate the new API effectively. The switch-case pattern for routing drops to different handlers is practical.


180-208: Good guidance on combining file drops with HTML5 drag-and-drop.

The documentation correctly explains how to distinguish external file drags from internal HTML5 drags using dataTransfer.types.includes('Files'), enabling both features to coexist in the same application.

v3/pkg/application/window.go (1)

24-24: HandleDragAndDropMessage signature change is complete and consistent.

The parameter rename from dropZone *DropZoneDetails to dropTarget *DropTargetDetails has been properly applied across the codebase. The interface definition, the WebviewWindow implementation, and all usage sites correctly reference the new parameter name and type.

v3/pkg/application/application_darwin.go (4)

420-440: LGTM!

The macosOnDragEnter and macosOnDragExit handlers correctly look up the window and dispatch JavaScript calls for UI updates. The early returns on missing windows prevent crashes gracefully.


475-511: LGTM!

The writeInt helper correctly converts integers to byte representation with proper bounds checking and edge case handling for zero and negative numbers.


577-644: LGTM!

The sendDragUpdate function correctly implements window caching with proper invalidation and zero-allocation JS string construction. The bounds checks prevent buffer overflows.


462-473: LGTM!

The clearWindowDragCache function properly cleans up cached window references and stops pending timers when a window is destroyed.

v3/pkg/application/webview_window_options.go (1)

107-110: LGTM!

The EnableFileDrop field is well-documented, clearly describing its purpose and referencing the data-file-drop-target attribute. This rename aligns with the PR's API consolidation goals.

v3/pkg/application/webview_window_darwin.go (4)

355-359: LGTM!

The windowExecJSNoAlloc C function provides a zero-allocation path for high-frequency drag-over JS execution. The comment clearly documents that the buffer is NOT freed, placing responsibility on the caller.


1104-1113: LGTM!

The execJSDragOver method correctly passes the pre-allocated buffer to C without additional allocations. The nil check prevents crashes on destroyed windows.


1265-1266: LGTM!

The window creation correctly uses the renamed EnableFileDrop option.


1499-1503: LGTM!

Calling clearWindowDragCache during window destruction ensures proper cleanup of cached window references and pending timers.

v3/pkg/application/webview_window_darwin_drag.m (5)

10-12: LGTM!

The extern declarations correctly expose the Go drag lifecycle callbacks for use in Objective-C.


37-58: LGTM!

The draggingUpdated: method correctly implements continuous drag position tracking with proper coordinate conversion from macOS's bottom-left origin to the web's top-left origin.


26-35: LGTM!

The draggingEntered: method correctly notifies both the window event system and JavaScript when a file drag enters the window.


60-65: LGTM!

The draggingExited: method correctly cleans up hover effects by notifying JavaScript when the drag leaves the window.


72-105: LGTM!

The performDragOperation: correctly extracts file paths, computes drop coordinates with proper Y-axis conversion, and passes them to the Go handler. Memory management is correct with malloc/free for the C array.

v3/examples/liquid-glass/main.go (2)

63-83: LGTM!

The example correctly uses the renamed EnableFileDrop field. Explicitly setting it to false documents intent clearly.


86-221: LGTM!

All seven window configurations consistently use the renamed EnableFileDrop field with proper formatting.

v3/pkg/application/context_window_event.go (2)

5-8: LGTM!

The constant rename from dropZoneDetailsKey to dropTargetDetailsKey aligns with the broader API rename.


45-73: LGTM!

The setDropTargetDetails and DropTargetDetails methods correctly handle the renamed type with proper nil checks and type assertions.

v3/pkg/application/application.go (3)

242-250: LGTM!

The DropTargetDetails struct is well-documented, clearly explaining its purpose and the data-file-drop-target attribute relationship.


252-268: LGTM!

The dragAndDropMessage struct and addDragAndDropMessage function correctly use the renamed DropTarget field.


712-722: LGTM!

The handleDragAndDropMessage correctly passes event.DropTarget to the window handler, completing the rename propagation.

v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts (1)

72-104: Cross‑platform drag hover and Windows file‑drop pipeline look consistent

The new helpers (getDropTargetElement, canResolveFilePaths, resolveFilePaths) and native drag handlers (handleDragEnter/Leave/Over) line up with the Windows/WebView2 and Linux/macOS flows and respect window._wails.flags.enableFileDrop. The file:drop:x:y message shape also matches the Go side parsing, and HandlePlatformFileDrop correctly builds the elementDetails payload against the nearest [data-file-drop-target] element.

No functional issues stand out here.

Also applies to: 120-165

v3/pkg/events/events.go (1)

37-66: Event ID renumbering and WindowFilesDropped mapping are internally consistent

The per‑platform enums (Linux/Mac/Windows/iOS) and the eventToJS map all use the same shifted IDs, and WindowFilesDropped is wired as 1032 → "common:WindowFilesDropped". Linux IDs also match the updated C header values.

This keeps the public event surface coherent after removing the old drop‑zone event.

Also applies to: 84-98, 237-371, 423-470, 499-524, 526-766

v3/pkg/application/webview_window_windows.go (1)

2116-2119: EnableFileDrop flag injection timing looks correct

Injecting window._wails.flags.enableFileDrop immediately after runtime.Core(...) ensures the front‑end drop logic (canResolveFilePaths, drag listeners) sees the right flag state for each window.

Given _wails.flags is already used by other code paths, this order is appropriate; no changes needed here.

v3/pkg/application/webview_window.go (2)

1182-1194: DispatchWailsEvent now matches runtime’s _wails.dispatchWailsEvent hook

Switching to window._wails.dispatchWailsEvent(...) aligns this helper with the runtime.js assignment (window._wails.dispatchWailsEvent = ...) and avoids relying on a global symbol.

This fixes the mismatch cleanly.


1435-1462: InitiateFrontendDropProcessing wiring to Window.HandlePlatformFileDrop is correct

Marshalling filenames to JSON and invoking:

window.wails.Window.HandlePlatformFileDrop([...], x, y)

matches the runtime surface (Window.HandlePlatformFileDrop on window.wails.Window) and cleanly funnels both Windows and native (Linux/macOS) drops through the same JS entry point.

The pending‑JS handling when runtimeLoaded is false also mirrors other ExecJS flows.

v3/pkg/events/events_linux.h (1)

9-21: Linux event IDs and MAX_EVENTS match Go definitions

The Linux header’s event IDs (1050–1060) and MAX_EVENTS 1061 line up with newLinuxEvents() in events.go, so C↔Go event routing remains consistent after the renumbering.

No changes needed here.

v3/internal/assetserver/bundledassets/runtime.js (1)

1-9999: Bundled runtime.js mirrors the TS sources; prefer fixing issues in the TypeScript, not this file

This file is a minified, generated bundle. The drag‑and‑drop logic (oi/h constants, Y/io/oo, HandlePlatformFileDrop, global ro() listeners) matches the TypeScript we’ve already reviewed, and the event names (including WindowFilesDropped) line up with events.go.

The static‑analysis warnings here (sparse arrays, variable reuse, use‑before‑declaration) are typical artifacts of minification and should be addressed, if at all, by adjusting the TS/rollup build rather than hand‑editing this bundle.

v3/pkg/events/events_darwin.h (1)

9-142: Event ID sequence and new drag events verified correctly.

Event IDs are contiguous from 1061 to 1192 with no gaps or duplicates. MAX_EVENTS (1193) correctly reflects the highest event ID plus one. The three new drag events (EventWindowFileDraggingEntered, EventWindowFileDraggingExited, EventWindowFileDraggingPerformed) are properly implemented at IDs 1153–1155 and consistently mapped in events.go. The removal of WindowDropZoneFilesDropped has been confirmed. Cross-platform event spacing is maintained across Darwin, iOS, and Linux headers.

Comment thread docs/src/content/docs/features/drag-and-drop/files.mdx
Comment thread docs/src/content/docs/features/drag-and-drop/files.mdx
Comment thread v3/pkg/application/application_darwin.go
Comment thread v3/pkg/application/application_darwin.go
Comment thread v3/pkg/application/webview_window_linux.go
Comment thread v3/test/dnd-test/assets/index.html Outdated
- Fix docs to use app.Window.NewWithOptions() instead of deprecated API
- Add mutex protection to dragOverJSBuffer to prevent race conditions
- Add mutex protection to dragThrottleState fields for thread safety
This was a development test file that shouldn't be in the PR.
The drag-n-drop example serves as the proper test case.
Remove stale TODO - enableFileDrop flag is now injected in navigationCompleted

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (1)
v3/pkg/application/webview_window_windows.go (1)

2262-2282: Clarify or remove misleading comment about coordinate conversion.

The comment at line 2276 states "Convert webview-relative coordinates to window-relative coordinates, then to webview-relative coordinates" but the code simply assigns the values without any conversion:

webviewX, webviewY := x, y

Based on the debug log at line 2280 ("Using coordinates as-is (already webview-relative)"), it appears the coordinates from WebView2 are already in the correct format and no conversion is needed. The comment should be updated to reflect this.

🔎 Suggested clarification
-		// Convert webview-relative coordinates to window-relative coordinates, then to webview-relative coordinates
-		// Note: The coordinates from WebView2 are already webview-relative, but let's log them for debugging
+		// The coordinates from WebView2 are already webview-relative, so we use them directly
 		webviewX, webviewY := x, y
♻️ Duplicate comments (1)
v3/pkg/application/application_darwin.go (1)

430-439: Same runtime verification concern as macosOnDragEnter.

This function has the same pattern as macosOnDragEnter and the same potential issue with calling ExecJS() before the runtime is loaded.

🧹 Nitpick comments (1)
v3/pkg/application/application_darwin.go (1)

480-516: Consider using strconv.AppendInt for simpler zero-allocation integer formatting.

While the custom writeInt implementation works, strconv.AppendInt provides the same zero-allocation benefit with less complexity and better edge-case handling. This would simplify the code while maintaining the performance optimization.

🔎 Proposed refactor using strconv.AppendInt
+import "strconv"
+
-// writeInt writes an integer to a byte slice and returns the number of bytes written
-func writeInt(buf []byte, n int) int {
-	if n < 0 {
-		if len(buf) == 0 {
-			return 0
-		}
-		buf[0] = '-'
-		return 1 + writeInt(buf[1:], -n)
-	}
-	if n == 0 {
-		if len(buf) == 0 {
-			return 0
-		}
-		buf[0] = '0'
-		return 1
-	}
-
-	// Count digits
-	tmp := n
-	digits := 0
-	for tmp > 0 {
-		digits++
-		tmp /= 10
-	}
-
-	// Bounds check
-	if digits > len(buf) {
-		return 0
-	}
-
-	// Write digits in reverse
-	for i := digits - 1; i >= 0; i-- {
-		buf[i] = byte('0' + n%10)
-		n /= 10
-	}
-	return digits
-}
+// writeInt writes an integer to a byte slice and returns the number of bytes written
+func writeInt(buf []byte, n int) int {
+	result := strconv.AppendInt(buf[:0], int64(n), 10)
+	return len(result)
+}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0d56d8 and e74ab6b.

📒 Files selected for processing (5)
  • AGENTS.md
  • docs/src/content/docs/features/drag-and-drop/files.mdx
  • v3/Taskfile.yaml
  • v3/pkg/application/application_darwin.go
  • v3/pkg/application/webview_window_windows.go
💤 Files with no reviewable changes (1)
  • v3/Taskfile.yaml
✅ Files skipped from review due to trivial changes (1)
  • AGENTS.md
🧰 Additional context used
🧠 Learnings (12)
📓 Common learnings
Learnt from: leaanthony
Repo: wailsapp/wails PR: 4783
File: v3/pkg/events/events.go:72-100
Timestamp: 2025-12-13T19:52:13.812Z
Learning: In Wails v3, the linux:WindowLoadChanged event was intentionally removed as a breaking change and replaced with four granular WebKit2 load events: linux:WindowLoadStarted, linux:WindowLoadRedirected, linux:WindowLoadCommitted, and linux:WindowLoadFinished. Users should migrate to linux:WindowLoadFinished for detecting when the WebView has finished loading.
📚 Learning: 2025-10-17T23:16:11.570Z
Learnt from: Sammy-T
Repo: wailsapp/wails PR: 4570
File: v2/internal/frontend/desktop/linux/window_webkit6.go:97-108
Timestamp: 2025-10-17T23:16:11.570Z
Learning: For webkit_6/GTK4 builds in v2/internal/frontend/desktop/linux/window_webkit6.go, GTK widget creation should not be wrapped in invokeOnMainThread. The activation mechanism (activateWg + onActivate export) already handles thread safety, and additional wrapping would cause issues.

Applied to files:

  • v3/pkg/application/application_darwin.go
  • docs/src/content/docs/features/drag-and-drop/files.mdx
📚 Learning: 2025-08-08T09:13:16.916Z
Learnt from: APshenkin
Repo: wailsapp/wails PR: 4480
File: v2/internal/frontend/desktop/darwin/message.h:17-19
Timestamp: 2025-08-08T09:13:16.916Z
Learning: In Wails v2 bindings origin verification, processBindingMessage intentionally has different signatures across platforms: Darwin includes an isMainFrame bool (WKWebKit provides it), Linux uses two params (message, source) as WebKitGTK doesn’t expose main-frame info there, and Windows handles origin checks in Go via WebView2 sender/args without a C bridge. This divergence is acceptable/expected per maintainer (APshenkin).

Applied to files:

  • v3/pkg/application/application_darwin.go
  • docs/src/content/docs/features/drag-and-drop/files.mdx
  • v3/pkg/application/webview_window_windows.go
📚 Learning: 2025-01-15T22:30:08.796Z
Learnt from: fbbdev
Repo: wailsapp/wails PR: 4001
File: v3/internal/generator/testcases/no_bindings_here/person.go:9-9
Timestamp: 2025-01-15T22:30:08.796Z
Learning: When dealing with fixed-length arrays in Go structs, encoding/json automatically handles bounds checking during unmarshaling, returning an error if the JSON array length exceeds the Go array length. No additional runtime validation is needed.

Applied to files:

  • v3/pkg/application/application_darwin.go
📚 Learning: 2025-12-29T08:02:06.122Z
Learnt from: popaprozac
Repo: wailsapp/wails PR: 4839
File: docs/src/content/docs/reference/window.mdx:616-620
Timestamp: 2025-12-29T08:02:06.122Z
Learning: In Wails v3, document window creation using app.Window.New() and app.Window.NewWithOptions(...). Do not show or reference app.NewWebviewWindow() or app.NewWebviewWindowWithOptions(...). The Application struct exposes a Window field of type *WindowManager that provides these methods. When updating docs, replace examples accordingly and mention that WindowManager methods create and configure new windows.

Applied to files:

  • docs/src/content/docs/features/drag-and-drop/files.mdx
📚 Learning: 2024-09-20T23:34:29.841Z
Learnt from: nixpare
Repo: wailsapp/wails PR: 3763
File: v3/examples/keybindings/main.go:16-17
Timestamp: 2024-09-20T23:34:29.841Z
Learning: In the codebase, `application.Options.KeyBindings` uses the `application.Window` type, whereas `application.WebviewWindowOptions.KeyBindings` uses `*application.WebviewWindow`. This is intentional and acceptable.

Applied to files:

  • docs/src/content/docs/features/drag-and-drop/files.mdx
📚 Learning: 2025-12-13T19:52:13.812Z
Learnt from: leaanthony
Repo: wailsapp/wails PR: 4783
File: v3/pkg/events/events.go:72-100
Timestamp: 2025-12-13T19:52:13.812Z
Learning: In Wails v3, the linux:WindowLoadChanged event was intentionally removed as a breaking change and replaced with four granular WebKit2 load events: linux:WindowLoadStarted, linux:WindowLoadRedirected, linux:WindowLoadCommitted, and linux:WindowLoadFinished. Users should migrate to linux:WindowLoadFinished for detecting when the WebView has finished loading.

Applied to files:

  • docs/src/content/docs/features/drag-and-drop/files.mdx
  • v3/pkg/application/webview_window_windows.go
📚 Learning: 2024-10-08T22:11:37.054Z
Learnt from: nixpare
Repo: wailsapp/wails PR: 3763
File: v3/pkg/application/application_options.go:93-93
Timestamp: 2024-10-08T22:11:37.054Z
Learning: The `KeyBindings` property of `application.Option` uses the `application.Window` type, while `application.WebviewWindowOptions` still accepts `*application.WebviewWindow`, and this is intentional.

Applied to files:

  • docs/src/content/docs/features/drag-and-drop/files.mdx
📚 Learning: 2024-09-21T13:34:24.145Z
Learnt from: nixpare
Repo: wailsapp/wails PR: 3763
File: v3/pkg/application/webview_window_bindings_darwin.h:0-0
Timestamp: 2024-09-21T13:34:24.145Z
Learning: In this codebase, typedefs for window references (like replacing `void *` with `WindowRef`) are not preferred; using `void *` is acceptable.

Applied to files:

  • docs/src/content/docs/features/drag-and-drop/files.mdx
📚 Learning: 2025-04-29T23:54:07.488Z
Learnt from: popaprozac
Repo: wailsapp/wails PR: 4256
File: v2/internal/frontend/desktop/linux/notifications.go:27-28
Timestamp: 2025-04-29T23:54:07.488Z
Learning: In Wails v2, unlike v3-alpha which has a `ServiceShutdown` method for services, there is no standardized teardown pattern for frontend implementations. When implementing features that require cleanup (like goroutines or resources), add explicit cleanup methods (e.g., `CleanupNotifications()`) that handle resource release, context cancellation, and connection closure.

Applied to files:

  • docs/src/content/docs/features/drag-and-drop/files.mdx
📚 Learning: 2024-09-30T06:13:46.595Z
Learnt from: leaanthony
Repo: wailsapp/wails PR: 3763
File: v3/examples/window/main.go:472-475
Timestamp: 2024-09-30T06:13:46.595Z
Learning: In `v3/examples/window/main.go`, `time.Sleep` is used within a goroutine and does not block the UI thread.

Applied to files:

  • docs/src/content/docs/features/drag-and-drop/files.mdx
📚 Learning: 2025-12-29T08:02:06.122Z
Learnt from: popaprozac
Repo: wailsapp/wails PR: 4839
File: docs/src/content/docs/reference/window.mdx:616-620
Timestamp: 2025-12-29T08:02:06.122Z
Learning: In Wails v3, the correct API for creating windows is `app.Window.New()` and `app.Window.NewWithOptions(...)`, not `app.NewWebviewWindow()` or `app.NewWebviewWindowWithOptions(...)`. The Application struct exposes a Window field of type *WindowManager that provides these methods.

Applied to files:

  • v3/pkg/application/webview_window_windows.go
🧬 Code graph analysis (1)
v3/pkg/application/application_darwin.go (3)
v3/pkg/application/window.go (1)
  • Window (9-104)
v3/pkg/application/mainthread.go (1)
  • InvokeSync (23-32)
v3/pkg/application/webview_window.go (1)
  • WebviewWindow (142-176)
🪛 LanguageTool
docs/src/content/docs/features/drag-and-drop/files.mdx

[style] ~8-~8: Try using a synonym here to strengthen your writing.
Context: ...ich only works within the browser, this gives you access to actual file paths on disk. #...

(GIVE_PROVIDE)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
  • GitHub Check: semgrep/ci
  • GitHub Check: Analyze (go)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (8)
v3/pkg/application/webview_window_windows.go (2)

1975-1988: LGTM! Clear documentation of the Windows drag-and-drop implementation.

The documentation block clearly explains the WebView2 constraints and how the two-path approach (EnableFileDrop true/false) works around the AllowExternalDrop limitations while preserving HTML5 drag-and-drop functionality.


2115-2117: LGTM! Proper injection of EnableFileDrop flag.

The flag is correctly injected into the JavaScript runtime after the core runtime is installed, allowing the frontend to check the file drop status before processing drops.

v3/pkg/application/application_darwin.go (6)

199-200: LGTM!

The new imports (sync and time) are necessary for the mutex protection and throttling logic in the drag-and-drop implementation.


441-461: LGTM! Race conditions properly addressed.

The mutex protection for dragOverJSBuffer (line 444) and the per-field mutex in dragThrottleState (line 455) correctly address the race conditions flagged in previous reviews. The synchronization strategy is sound: the global buffer mutex prevents concurrent buffer corruption, while per-window throttle state mutexes ensure thread-safe access to position tracking and timer management.


518-591: LGTM! Throttling and synchronization correctly implemented.

The function implements a sophisticated throttling strategy (5-pixel immediate threshold + 50ms debounce) with proper mutex protection. The locking pattern is correct: locks are released before calling sendDragUpdate (line 564) to avoid holding locks during potentially slow JS execution, and the timer callback uses InvokeSync (line 572) to ensure main-thread execution. This correctly addresses the race conditions flagged in previous reviews.


463-478: Confirm that clearWindowDragCache is called during window cleanup.

The function correctly clears per-window caches and stops pending timers with proper mutex synchronization. However, ensure this function is invoked when a window is destroyed to prevent memory leaks from accumulated windowImplCache and dragThrottle entries.


419-428: The code is correct. The ExecJS() method already handles runtime loading checks internally: if the runtime is not loaded, the JavaScript call is queued in pendingJS and executed automatically once the runtime becomes ready (rather than failing silently). No changes are needed.


593-665: LGTM! Zero-allocation optimization well-implemented.

The function uses effective caching and a protected pre-allocated buffer to achieve zero allocations in the hot path. The 128-byte buffer is appropriately sized (max usage ~54 bytes for int32 coordinates with the window._wails.handleDragOver() prefix), and buffer overflow is safely handled with an early return. The window lookup with fallback is robust. The execJSDragOver method is properly implemented to pass the buffer directly to C without additional allocations.

Internal method only called by application event loop, not part of public API.
@sonarqubecloud

sonarqubecloud Bot commented Jan 3, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
5.4% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@leaanthony leaanthony merged commit 53c2275 into v3-alpha Jan 4, 2026
54 of 55 checks passed
@leaanthony leaanthony deleted the v3/fix-dnd-finally-maybe branch January 4, 2026 00:08
Grantmartin2002 pushed a commit to Grantmartin2002/wails that referenced this pull request Apr 29, 2026
…ndows implementation (wailsapp#4848)

* fix(v3): overhaul drag-and-drop for Linux reliability and simplify Windows

This commit fixes drag-and-drop reliability on Linux and simplifies the
Windows implementation.

## Linux
- Rewrite GTK drag handlers to properly intercept external file drops
- Fix HTML5 internal drag-and-drop being broken when file drop enabled
- Add hover effects during file drag operations
- Fix multiple app instances interfering with each other

## Windows
- Remove native IDropTarget in favor of JavaScript approach (matches v2)
- File drops now handled via chrome.webview.postMessageWithAdditionalObjects

## All Platforms
- Rename EnableDragAndDrop to EnableFileDrop
- Rename data-wails-drop-target to data-file-drop-target
- Rename wails-drop-target-active to file-drop-target-active
- Add comprehensive drag-and-drop documentation

## Breaking Changes
- EnableDragAndDrop -> EnableFileDrop
- data-wails-dropzone -> data-file-drop-target
- wails-dropzone-hover -> file-drop-target-active
- DropZoneDetails -> DropTargetDetails
- Remove WindowDropZoneFilesDropped event (use WindowFilesDropped)

* feat(macos): optimize drag event performance with debouncing and caching

- Add 50ms debouncing to limit drag events to 20/sec (was 120/sec)
- Implement window implementation caching to avoid repeated lookups
- Maintain existing 5-pixel threshold for immediate response
- Keep zero-allocation path with pre-allocated buffers
- Rename linuxDragActive to nativeDragActive for clarity
- Update IMPLEMENTATION.md with optimization details and Windows guidance

Performance improvements:
- 83% reduction in event frequency
- ~6x reduction in CPU/memory usage during drag operations
- Maintains smooth visual feedback with InvokeSync for timer callbacks

* fix(windows): implement proper file drop support for Windows

- Remove incorrect AllowExternalDrag(false) call that was blocking file drops
- Fix message prefix from 'FilesDropped' to 'file:drop:' to match JS runtime
- Fix coordinate parsing for 'file:drop:x:y' format (indices 2,3 not 1,2)
- Add enableFileDrop flag injection to JS runtime during navigation
- Update JS runtime to check enableFileDrop flag before processing drops
- Always call preventDefault() to stop browser navigation on file drags
- Show 'no drop' cursor when file drops are disabled
- Update example to filter file drags from HTML drop zone handlers
- Add documentation for combining file drop with HTML drag-and-drop

* fix(v3): block file drops on Linux when EnableFileDrop is false

- Add disableDND() to intercept and reject external file drags at GTK level
- Show 'no drop' cursor when files are dragged over window
- Allow internal HTML5 drag-and-drop to work normally
- Initialize _wails.flags object in runtime core to prevent undefined errors
- Inject enableFileDrop flag on Linux and macOS (matching Windows)
- Fix bare _wails reference to use window._wails
- Update docs with info about blocked drops and combining with HTML DnD

* fix(darwin): add missing fmt import in webview_window_darwin.go

* fix(macOS): implement hover effects for file drag-and-drop with optimizations

- Added draggingUpdated: handler to track mouse movement during drag operations
- Implemented macosOnDragEnter/Exit/Over export functions for real-time hover state
- Fixed JS function call from '_wails.handlePlatformFileDrop' to correct 'wails.Window.HandlePlatformFileDrop'
- Added EnableFileDrop flag checks to prevent hover effects when file drops are disabled
- Renamed linuxDragActive to nativeDragActive for cross-platform consistency

Performance optimizations:
- Added 50ms debounce to reduce event frequency from ~120/sec to ~20/sec
- Implemented 5-pixel movement threshold for immediate response
- Added window caching with sync.Map to avoid repeated lookups
- Zero-allocation JavaScript calls with pre-allocated 128-byte buffer
- Reduced memory usage to ~18 bytes per event (6x reduction)

Build improvements:
- Updated runtime Taskfile to include documentation generation
- Added docs:build task to runtime build process
- Fixed build order: events → docs → runtime

Documentation:
- Added IMPLEMENTATION.md with optimization details
- Included guidance for Windows implementation

* chore(v3/examples): remove html-dnd-api example

The drag-n-drop example now demonstrates both external file drops
and internal HTML5 drag-and-drop, making this separate example redundant.

* docs(v3): move drag-and-drop implementation details to runtime-internals

- Add drag-and-drop section to contributing/runtime-internals.mdx
- Remove IMPLEMENTATION.md from example (content now in proper docs)
- Covers platform differences, debugging tips, and key files

* fix(v3): remove html-dnd-api from example build list

* fix(v3): remove duplicate json import in application_darwin.go

* fix(v3): address CodeRabbit review feedback

- Fix docs to use app.Window.NewWithOptions() instead of deprecated API
- Add mutex protection to dragOverJSBuffer to prevent race conditions
- Add mutex protection to dragThrottleState fields for thread safety

* docs: add coderabbit pre-push requirement to AGENTS.md

* fix(v3/test): use correct CSS class name file-drop-target-active

* chore(v3/test): remove dnd-test directory

This was a development test file that shouldn't be in the PR.
The drag-n-drop example serves as the proper test case.

* docs(v3): update Windows file drop comment to reflect implemented fix

Remove stale TODO - enableFileDrop flag is now injected in navigationCompleted

* refactor(v3): make handleDragAndDropMessage unexported

Internal method only called by application event loop, not part of public API.
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.

1 participant