fix(v3): overhaul drag-and-drop for Linux reliability and simplify Windows implementation#4848
Conversation
WalkthroughRefactors 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
Sequence Diagram(s)mermaid Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (1)
🧰 Additional context used🧠 Learnings (3)📓 Common learnings📚 Learning: 2024-09-30T06:13:46.595ZApplied to files:
📚 Learning: 2024-09-30T06:14:32.602ZApplied to files:
🧬 Code graph analysis (1)v3/pkg/application/application.go (1)
⏰ 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)
🔇 Additional comments (5)
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. Comment |
223f0a0 to
61d7d61
Compare
Deploying wails with
|
| 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 |
6ece20d to
1268d46
Compare
1268d46 to
020c618
Compare
…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)
020c618 to
2c65491
Compare
- 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
37a8263 to
e25284d
Compare
- 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
1ad2203 to
533ff8d
Compare
The drag-n-drop example now demonstrates both external file drops and internal HTML5 drag-and-drop, making this separate example redundant.
- 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
There was a problem hiding this comment.
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 ofencoding/json.The
encoding/jsonpackage 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 oneventListenersmapThis method reads
w.eventListeners[uint(events.Common.WindowFilesDropped)]without takingeventListenersLock, whileOnWindowEventmutates the same map under that lock andHandleWindowEventclones listeners under anRLock.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
HandleWindowEventpattern 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
appvariable instead ofapplication.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 movingGApplicationNonUniqueto 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 << 1Then 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_enteredandinternal_drag_activeflags 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, thedrag_enteredflag 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 howdrop-x/drop-yare 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 != 2uses a magic number. While it works, adding a comment or constant explaining that2corresponds to thetext/uri-listtarget atom position in the target list would improve maintainability.
1078-1101: Edge case:writeIntcould overflow withmath.MinInt.The recursive call
writeInt(buf[1:], -n)at line 1083 would overflow ifn == math.MinInt64since-math.MinInt64cannot 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. Appendingwindow._wails.flags.enableFileDrop=...;as a separate statement works but creates a redundant pattern. TheenableFileDropflag could be added to the flags map before callingCore().🔎 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.flagsexistsTargeting the nearest drop target via
getDropTargetElement(elementFromPoint(x, y))and sendingFilesDroppedwith fullelementDetailsis the right contract for the backend, and the earlyenableFileDrop === falseguard matches the rest of the runtime.If you expect any third‑party code to run custom JS before
runtime.Coreinitialises_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” DnDThe global
dragenter/over/leave/drophandlers correctly:
- Filter to
dataTransfer.types.includes('Files')so non‑file HTML5 DnD is left alone.- Always
preventDefault()for file drags to avoid navigation.- Respect
enableFileDropby forcing a “no drop” cursor when disabled.- Keep hover state in
currentDropTargetand 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 thatdata-file-drop-targetis 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 runtimeThe comment still says “TODO: Pass EnableFileDrop setting to JS runtime…”, but
navigationCompletednow 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:yhandling and coord usage are sound; clean up comments when convenientThe new
file:drop:branch:
- Correctly extracts
ICoreWebView2Fileobjects and their paths intofilenames.- Parses
x/yfrom the"file:drop:x:y"message parts.- Treats those coords as already webview‑relative (matching
event.clientX/Yin JS) and forwards them directly toInitiateFrontendDropProcessing.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.
convertWindowToWebviewCoordinatesappears 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(), andruntimeLoaded.- Reset
dragHoverat the start of a drag session.- Throttle
HandleDragOverupdates to movements of ≥5px in either axis.- Use a zero‑alloc
execJSDragOverpath 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
dragHoverstruct is process‑wide; if you ever support concurrent drags across multiple windows, you might want to move that state ontoWebviewWindow, 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 = 50andwindowMethodNames[WindowFilesDropped] = "FilesDropped"match the TS/runtime method ID.fileDropPayload’sfilenames/x/y/elementDetailsfields mirror the JS payload fromHandlePlatformFileDrop.- The new
DropTargetDetailsis correctly populated fromElementDetailsPayload, and the resultingdragAndDropMessageincludeswindowId,filenames, andDropTargetas 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
⛔ Files ignored due to path filters (1)
v3/internal/runtime/desktop/@wailsio/runtime/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (47)
docs/astro.config.mjsdocs/src/content/docs/contributing/runtime-internals.mdxdocs/src/content/docs/features/drag-and-drop/files.mdxdocs/src/content/docs/features/drag-and-drop/html.mdxdocs/src/content/docs/features/windows/options.mdxdocs/src/content/docs/quick-start/next-steps.mdxv3/UNRELEASED_CHANGELOG.mdv3/examples/drag-n-drop/README.mdv3/examples/drag-n-drop/assets/index.htmlv3/examples/drag-n-drop/main.gov3/examples/html-dnd-api/README.mdv3/examples/html-dnd-api/assets/index.htmlv3/examples/html-dnd-api/main.gov3/examples/liquid-glass/main.gov3/internal/assetserver/bundledassets/runtime.debug.jsv3/internal/assetserver/bundledassets/runtime.jsv3/internal/generator/collect/known_events.gov3/internal/runtime/Taskfile.yamlv3/internal/runtime/desktop/@wailsio/runtime/src/event_types.tsv3/internal/runtime/desktop/@wailsio/runtime/src/index.tsv3/internal/runtime/desktop/@wailsio/runtime/src/system.tsv3/internal/runtime/desktop/@wailsio/runtime/src/window.tsv3/internal/runtime/runtime.gov3/pkg/application/application.gov3/pkg/application/application_darwin.gov3/pkg/application/context_window_event.gov3/pkg/application/linux_cgo.gov3/pkg/application/linux_purego.gov3/pkg/application/messageprocessor_window.gov3/pkg/application/webview_window.gov3/pkg/application/webview_window_darwin.gov3/pkg/application/webview_window_darwin.mv3/pkg/application/webview_window_darwin_drag.mv3/pkg/application/webview_window_linux.gov3/pkg/application/webview_window_options.gov3/pkg/application/webview_window_options_test.gov3/pkg/application/webview_window_windows.gov3/pkg/application/window.gov3/pkg/events/events.gov3/pkg/events/events.txtv3/pkg/events/events_darwin.hv3/pkg/events/events_ios.hv3/pkg/events/events_linux.hv3/pkg/events/known_events.gov3/test/dnd-test/assets/index.htmlv3/test/dnd-test/dnd-testv3/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.mdv3/pkg/application/webview_window_linux.gov3/pkg/application/webview_window_darwin_drag.mv3/pkg/application/webview_window_options.gov3/internal/generator/collect/known_events.gov3/pkg/application/webview_window_windows.gov3/internal/runtime/desktop/@wailsio/runtime/src/window.tsv3/pkg/events/events.gov3/pkg/application/webview_window.gov3/pkg/application/webview_window_darwin.gov3/internal/runtime/desktop/@wailsio/runtime/src/index.tsv3/examples/liquid-glass/main.gov3/internal/assetserver/bundledassets/runtime.jsv3/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.mdxdocs/src/content/docs/contributing/runtime-internals.mdxdocs/src/content/docs/features/drag-and-drop/html.mdxdocs/src/content/docs/features/drag-and-drop/files.mdxdocs/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.gov3/pkg/application/linux_purego.gov3/pkg/application/webview_window.gov3/pkg/application/webview_window_darwin.gov3/pkg/application/linux_cgo.gov3/internal/runtime/desktop/@wailsio/runtime/src/index.tsv3/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.gov3/pkg/application/webview_window_darwin_drag.mv3/pkg/application/webview_window_windows.gov3/pkg/application/webview_window.gov3/pkg/application/webview_window_darwin.gov3/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.gov3/internal/runtime/runtime.gov3/pkg/application/linux_purego.gov3/examples/drag-n-drop/main.gov3/pkg/application/webview_window_options.gov3/test/dnd-test/main.gov3/pkg/application/webview_window_windows.gov3/pkg/events/events.gov3/pkg/application/webview_window.gov3/pkg/application/webview_window_darwin.gov3/pkg/application/messageprocessor_window.gov3/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.gov3/pkg/application/webview_window_options.godocs/src/content/docs/features/windows/options.mdxv3/pkg/application/webview_window.gov3/pkg/application/webview_window_darwin.gov3/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.gov3/test/dnd-test/main.gov3/pkg/application/application.gov3/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.gov3/test/dnd-test/main.gov3/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.tsv3/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()andDropTargetDetails()in the example code match the actual implementation inv3/pkg/application/context_window_event.go, and both return the expected types ([]stringand*DropTargetDetailsrespectively).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 fromEnableDragAndDropin this PR.
56-59: LGTM! Appropriate error handling.Using
log.Fatalfor 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 withgtk_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.flagsas 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-activeclass 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 inv3/examples/drag-n-drop/main.go:application.Get().Event.Emit("files-dropped", ...). WhileWindowFilesDroppedis 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:alltask correctly sequences the generation and build steps, and thecleantask 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
onUriListfunction appropriately callsInitiateFrontendDropProcessingwhich aligns with the new drop-target architecture.
1021-1031: LGTM!The
enableDNDanddisableDNDmethods correctly pass the window ID using the established pattern (casting touintptras 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_UNIQUEaligns 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._wailsto enable GTK's native drag event interception on Linux. This pattern is consistent with other platform handlers likehandlePlatformFileDrop.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
EnableDragAndDroptoEnableFileDropis correct, and the explicit handling of both enabled and disabled cases withdisableDND()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 Optionssection (line 549) with a single### EnableFileDropentry (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 onDropTargetDetails.The nil check before accessing
details.ElementIDis appropriate since drops outside defined drop targets may not have element details.
26-44: AddShow()call to display the window.The window is created with
NewWithOptions()but never shown. All examples in the codebase follow the pattern of callingShow()after window creation. Addwin.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 newWindowFilesDroppedevent.The common events section correctly includes
common:WindowFilesDropped(line 17), replacing the previousWindowDropZoneFilesDropped. 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 whypreventDefault()ondragoveris required is helpful for developers unfamiliar with the HTML5 DnD API.
127-133: Correct solution for thedragleaveflickering 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
ElementIDare 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 *DropZoneDetailstodropTarget *DropTargetDetailshas been properly applied across the codebase. The interface definition, theWebviewWindowimplementation, and all usage sites correctly reference the new parameter name and type.v3/pkg/application/application_darwin.go (4)
420-440: LGTM!The
macosOnDragEnterandmacosOnDragExithandlers 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
writeInthelper correctly converts integers to byte representation with proper bounds checking and edge case handling for zero and negative numbers.
577-644: LGTM!The
sendDragUpdatefunction correctly implements window caching with proper invalidation and zero-allocation JS string construction. The bounds checks prevent buffer overflows.
462-473: LGTM!The
clearWindowDragCachefunction 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
EnableFileDropfield is well-documented, clearly describing its purpose and referencing thedata-file-drop-targetattribute. This rename aligns with the PR's API consolidation goals.v3/pkg/application/webview_window_darwin.go (4)
355-359: LGTM!The
windowExecJSNoAllocC 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
execJSDragOvermethod 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
EnableFileDropoption.
1499-1503: LGTM!Calling
clearWindowDragCacheduring 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 withmalloc/freefor the C array.v3/examples/liquid-glass/main.go (2)
63-83: LGTM!The example correctly uses the renamed
EnableFileDropfield. Explicitly setting it tofalsedocuments intent clearly.
86-221: LGTM!All seven window configurations consistently use the renamed
EnableFileDropfield with proper formatting.v3/pkg/application/context_window_event.go (2)
5-8: LGTM!The constant rename from
dropZoneDetailsKeytodropTargetDetailsKeyaligns with the broader API rename.
45-73: LGTM!The
setDropTargetDetailsandDropTargetDetailsmethods correctly handle the renamed type with proper nil checks and type assertions.v3/pkg/application/application.go (3)
242-250: LGTM!The
DropTargetDetailsstruct is well-documented, clearly explaining its purpose and thedata-file-drop-targetattribute relationship.
252-268: LGTM!The
dragAndDropMessagestruct andaddDragAndDropMessagefunction correctly use the renamedDropTargetfield.
712-722: LGTM!The
handleDragAndDropMessagecorrectly passesevent.DropTargetto 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 consistentThe new helpers (
getDropTargetElement,canResolveFilePaths,resolveFilePaths) and native drag handlers (handleDragEnter/Leave/Over) line up with the Windows/WebView2 and Linux/macOS flows and respectwindow._wails.flags.enableFileDrop. Thefile:drop:x:ymessage shape also matches the Go side parsing, andHandlePlatformFileDropcorrectly builds theelementDetailspayload 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 consistentThe per‑platform enums (Linux/Mac/Windows/iOS) and the
eventToJSmap all use the same shifted IDs, andWindowFilesDroppedis wired as1032 → "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 correctInjecting
window._wails.flags.enableFileDropimmediately afterruntime.Core(...)ensures the front‑end drop logic (canResolveFilePaths, drag listeners) sees the right flag state for each window.Given
_wails.flagsis 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.dispatchWailsEventhookSwitching 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 correctMarshalling
filenamesto 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
runtimeLoadedis false also mirrors other ExecJS flows.v3/pkg/events/events_linux.h (1)
9-21: Linux event IDs and MAX_EVENTS match Go definitionsThe Linux header’s event IDs (1050–1060) and
MAX_EVENTS 1061line up withnewLinuxEvents()inevents.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 fileThis file is a minified, generated bundle. The drag‑and‑drop logic (
oi/hconstants,Y/io/oo,HandlePlatformFileDrop, globalro()listeners) matches the TypeScript we’ve already reviewed, and the event names (includingWindowFilesDropped) line up withevents.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.
- 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
There was a problem hiding this comment.
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, yBased 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 asmacosOnDragEnter.This function has the same pattern as
macosOnDragEnterand the same potential issue with callingExecJS()before the runtime is loaded.
🧹 Nitpick comments (1)
v3/pkg/application/application_darwin.go (1)
480-516: Consider usingstrconv.AppendIntfor simpler zero-allocation integer formatting.While the custom
writeIntimplementation works,strconv.AppendIntprovides 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
📒 Files selected for processing (5)
AGENTS.mddocs/src/content/docs/features/drag-and-drop/files.mdxv3/Taskfile.yamlv3/pkg/application/application_darwin.gov3/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.godocs/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.godocs/src/content/docs/features/drag-and-drop/files.mdxv3/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.mdxv3/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 (
syncandtime) 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 indragThrottleState(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 usesInvokeSync(line 572) to ensure main-thread execution. This correctly addresses the race conditions flagged in previous reviews.
463-478: Confirm thatclearWindowDragCacheis 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
windowImplCacheanddragThrottleentries.
419-428: The code is correct. TheExecJS()method already handles runtime loading checks internally: if the runtime is not loaded, the JavaScript call is queued inpendingJSand 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. TheexecJSDragOvermethod 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.
|
…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.


Summary
Complete overhaul of drag-and-drop across all platforms for reliability and consistency.
Changes by Platform
Linux
EnableFileDropis false (prevents browser navigation)Windows
chrome.webview.postMessageWithAdditionalObjectsenableFileDropflag to JS runtime for proper drop handlingmacOS
enableFileDropflag to JS runtimeAll Platforms
EnableDragAndDroptoEnableFileDropdata-wails-drop-targettodata-file-drop-targetwails-drop-target-activetofile-drop-target-activewindow._wails.flagsin runtime core to prevent undefined errors_wailsreference to usewindow._wailsBreaking Changes
EnableDragAndDropEnableFileDropdata-wails-dropzonedata-file-drop-targetwails-dropzone-hoverfile-drop-target-activeDropZoneDetailsDropTargetDetailsDropZoneDetails()DropTargetDetails()WindowDropZoneFilesDroppedWindowFilesDroppedDocumentation
/features/drag-and-drop/files- File drop from OS/features/drag-and-drop/html- HTML5 drag & drop within app/contributing/runtime-internals/features/windows/optionswithEnableFileDropdocumentationExamples
drag-n-dropexample to demonstrate both external file drops and internal HTML5 drag-and-drophtml-dnd-apiexample (functionality merged intodrag-n-drop)Testing
Summary by CodeRabbit
Documentation
Breaking Changes
Platform Improvements
Examples
✏️ Tip: You can customize this high-level summary in your review settings.