Skip to content

fix(ui): fix drag-and-drop on Windows and catalog rail layout - #24

Merged
thcp merged 1 commit into
mainfrom
fix/dnd-folder-drop-webview2
May 9, 2026
Merged

thcp merged 1 commit into
mainfrom
fix/dnd-folder-drop-webview2

Conversation

@thcp

@thcp thcp commented May 9, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • dragDropEnabled: false in tauri.conf.json — WebView2 was intercepting OS-level drag events before they reached the HTML page, silently swallowing all dragover/drop events. This is the documented requirement for HTML5 DnD on Windows in Tauri v2.
  • Catalog rail background fills full height — the rail column was auto-placing catalog-main into an implicit grid row in the collapsed state, stealing height from the 1fr row and leaving the rail background short. Fixed by pinning both children to explicit grid-column/grid-row and adding grid-template-rows: 1fr to the catalog grid.
  • dropOnFolder / getDraggedTrackId hardening — accepts an explicit trackId argument and a text/plain fallback for WebView2's cross-origin dataTransfer restrictions.
  • Restore from trash — dragging a track from the trash view onto the Library rail button now restores it to the first non-trash folder and switches back to library view.

Test plan

  • Drag a song card from Unsorted to a user folder — moves cleanly
  • Drag a song card to the Trash rail button — moves to trash
  • Switch to Trash view, drag a track back onto the Library rail button — restores to Unsorted, view switches to Library
  • Collapse the library panel — rail background fills full height with no lighter strip below About
  • Expand the library panel — layout correct, no regression

- Set dragDropEnabled: false in tauri.conf.json so WebView2 doesn't
  intercept OS-level drag events, allowing HTML5 dragover/drop to fire
- Fix catalog-rail background not filling full panel height by adding
  grid-template-rows: 1fr and explicit grid-column/row placement on
  rail and main, preventing catalog-main from wrapping to an implicit
  row in the collapsed state
- Restore dropOnFolder and getDraggedTrackId to accept explicit trackId
  and text/plain fallback for cross-origin dataTransfer in WebView2
- Add wireRailLibraryDrop so tracks can be dragged from trash back to
  library by dropping on the Library rail button
@thcp
thcp merged commit 13ce0c9 into main May 9, 2026
1 check passed
@thcp
thcp deleted the fix/dnd-folder-drop-webview2 branch May 9, 2026 23:33
thcp added a commit that referenced this pull request Sep 23, 2026
…hem (#680)

Dropping a file on the Linux desktop app turned the window into a bare media
player playing it, with only a right click and Back to get out. Dropping it
on the URL box typed its file:// URI into the box, and pressing Process then
failed with "URL must use http or https" (#672).

Both come from one place. WebKitGTK reports a drag from a GTK file manager as
text/uri-list and text/html, never as "Files", so the drop guard added in
#612, which keys on "Files", never recognised it and WebKit's default ran:
navigate to the file, or insert the URI into an editable target. The guard
works on Windows, macOS and in Chromium, which do report "Files", which is
why it was never seen there.

Widening the guard was not a fix. Measured against a real drag into
WebKitGTK 2.52, a cancelled drop reaches the page with no File objects, no
file items, and the path blanked out of text/uri-list. The page cannot get
the file at all, only stop the navigation.

So on Linux the main window's native drop handler is on and the drop is taken
in the shell. Tauri's handler returns true on a drop, which consumes it before
WebKit's default runs, and wry only claims drags carrying a file uri-list, so
the library's own HTML5 drags between folders, lanes and the trash are left
alone. That was the reason the handler is off everywhere (#24): on Windows,
WebView2 stops delivering HTML5 drags while it is on. It stays off on Windows
and macOS, where the page already gets the files.

The window is now built in setup from its tauri.conf.json entry, marked
"create": false, with that one field set per platform. A per-platform config
file would have needed a second full copy of the window, since its merge
replaces arrays rather than patching them.

No command takes a path from JavaScript. The page is a remote origin to
Tauri, so app commands are callable from it without an ACL, and dragout and
download_to_path already keep paths on the Rust side for that reason. A drop
is recorded against opaque ids; the page is told a name and a size for each,
and can read back a file the user dropped, once, by id. Only the latest drop
is readable, a folder or a vanished file is refused, and the read is capped
at the upload limit against the open handle so it cannot be raced past.

The page cannot listen to Tauri events without a remote capability, and
granting one would open every core event command to it. It already talks to
the shell only through commands, so it waits on next_drop_signal instead. The
signals are a log read by cursor rather than a queue that is popped: a reload
leaves the old page's call waiting with nobody to answer, and a popped queue
would hand it the next drop.

On the page, applyFiles is split into screening and staging. A native drop is
screened on its names and sizes first, so a file that will be refused as not
audio or too large is never read, and only accepted files are fetched, one at
a time, as raw bytes. From there it is the same path as the picker: same
refusals, same pill, same staging. A file that has gone by the time it is read
says so by name, in all ten languages.

The upload limit now exists in three languages, so a test holds the server,
the page and the shell to the same number. The first two were already
duplicated with only a comment saying they must match.

Verified in a real Tauri 2.11 shell around this module on Linux, driving
genuine XDND drags from a separate GTK process: a drop on the page and a drop
on a text box both arrive as ids with the file's real bytes, nothing
navigates, nothing is typed into the box, a second read of an id is refused,
and an internal drag with the library's own data types still lands on its
target. The Windows build still opens its window.

Co-authored-by: Thales <>
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