Windows: fix dropping files on the island - #114
hieudepzai14122007-ship-it wants to merge 4 commits into
Conversation
Dragging a file onto the island did nothing: no drag event ever reached the app. Four things stood in the way. - The native drop hook never fired. The innermost windows a drag passes over (Chrome_WidgetWin_1, Chrome_RenderWidgetHostHWND) belong to the msedgewebview2 process, and OLE stops at WebView2's own target there before reaching the one wry registers on Chrome_WidgetWin_0. Revoking it from our process is not reliable for a window another process owns. dragDropEnabled is now off and the page takes the drop as plain HTML5 drag and drop; the file's contents come over as a raw IPC body to a new ingest_bytes command, which writes them into the inbox like ingest does. unblock_webview_drops is gone. - The wake strip ended up click-through. A cursor-poll tick asleep while the island collapsed recomputed "not on the island" for the new strip and turned click-through back on. The poll is now stopped before the flag is reset, and it treats the collapsed strip as always taking the mouse. - A 6 px strip is too thin to drop on. While the island is hidden and a drag is in flight (button held, press outside the zone, moved 8 px, not a window move or resize), the strip grows into a 720x150 invisible zone at the top centre and shrinks back on release. - An always-on-top status bar along the top edge can sit above the strip. The island re-asserts HWND_TOPMOST when it is placed and every 2 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HTML5 drag and drop says when a drag leaves the page, but not when it is then dropped somewhere else, so dragging a file over the island and releasing it elsewhere left the "Drop your files here" view up for good. The cursor poll now emits pointer-released on the button's falling edge, and the island closes the drop view if the file had already left and was never dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Tested on Windows 11 Pro 26H2 (OS Build 26300.9457), WebView2 154.0.4258.48, built from this branch. Dropping a file straight onto the island now works: the log shows Two things I noticed: 1. The progress bar can stay at 0% after a drop. I logged As a workaround, setting // upload/sequence.ts
performDrop(uploadDuration: number) {
this.uploadDuration = uploadDuration;
this.dropWall = this.now();
this.isActive = true; // the sequence may already have been deactivated when the drop lands
// ...rest unchanged
}2. Minor regression: clicking "+" no longer keeps the drop view (the "PDF / Images" panel) open. I tried a small fix locally (island.ts only) and it works on my machine: private fileDragSeen = false;
private onDragDrop(e: DragDropPayload) {
if (e.type === "enter") this.fileDragSeen = true;
// ...rest unchanged
}
private onPointerReleased() {
const wasFileDrag = this.fileDragSeen;
this.fileDragSeen = false;
if (!wasFileDrag || State.fileDragOver || State.view !== "upload" || UploadSeq.dropped) return;
// ...rest unchanged
}With it:
Tested only on one machine with these scenarios. |
Two issues found while testing this branch (thanks Grizzy8): - Dropping onto a hidden island could leave "Uploading 0%" on screen. Waking the island passes through the home view, and leaving the drop views on the way switches the upload sequence off, so the drop then ran with no sequence. The sequence is switched back on after waking, and performDrop now always activates it. - Clicking "+" opened the drop panel and the button release closed it again. Only a release that ends a file drag which entered the island now closes the drop view. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for testing and for the detailed report! Both confirmed and fixed in 604c093. The 0 % bar came from waking the hidden island: it goes through the home view first, which switches the upload sequence off. The sequence is now turned back on after waking, and |
|
Thanks for the quick fix in 604c093: both the 0 % bar and the "+" panel behave on my machine now. One more thing I ran into while testing the drop flow on Windows: on the "choose" card ("file.pdf is ready. What do you want to do with it?"), the Cancel button does nothing, and Ask a question only reacts on its left part. Cause. The card is painted on the canvas, and the clicks are caught by invisible
Suggested fix (16 lines, tested on Windows): --- a/windows/src/island/island.ts
+++ b/windows/src/island/island.ts
@@ -738,4 +738,6 @@ export class Island {
this.uploadCanvas.el.classList.toggle("on", uploadActive);
this.viewsEl.classList.toggle("hidden-by-upload", uploadActive);
+ // Lets the header stay clickable while #content passes clicks through.
+ this.contentEl.classList.toggle("upload-on", uploadActive);
tickMiniBots(dt);
@@ -860,5 +862,7 @@ export class Island {
this.contentEl.style.opacity = expanded && !greetingActive ? "1" : "0";
- this.contentEl.style.pointerEvents = expanded && !greetingActive ? "auto" : "none";
+ // While the drop sequence owns the body, the content layer lets clicks through
+ // to the invisible hit areas under it (the header opts back in, see style.css).
+ this.contentEl.style.pointerEvents = expanded && !greetingActive && !this.uploadActive ? "auto" : "none";
this.greetingCanvas.style.display = greetingActive ? "block" : "none";
--- a/windows/src/style.css
+++ b/windows/src/style.css
@@ -134,4 +134,15 @@ body {
}
+/* The invisible views must not swallow the clicks meant for the buttons the
+ canvas paints (.upload-hit sits below #content). The header stays clickable. */
+#views.hidden-by-upload * {
+ pointer-events: none;
+}
+
+/* #content itself is switched to pointer-events: none from island.ts (inline). */
+#content.upload-on #header {
+ pointer-events: auto;
+}
+
#bot-glow {
position: absolute;While the drop sequence owns the island, clicks reach the painted buttons; the header tabs stay clickable, and everything goes back to normal once the sequence ends (Ask → chat, Cancel → home). An alternative would be to move the hit areas above |
The choose card is painted on the canvas, and its clicks are caught by invisible .upload-hit buttons inside #upload-layer. #content comes after that layer in the DOM and covers the whole island, so it swallowed the click: Cancel did nothing and Ask only reacted where the hidden DOM button happened to overlap it. While the drop sequence owns the body, #content and the hidden views now let clicks through; the header opts back in. Diagnosis and fix by Grizzy8 in the PR thread. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Confirmed: the canvas hit areas sat under #content, and the hidden view's DOM button was catching part of the click. Your fix is in 4972152 with credit in the commit message. Thanks again for the thorough testing! |
…ndows) On Windows 11 build 26200 a file dragged from Explorer never reached the island: the native drop hook does not fire on the windows WebView2 owns. PR Louis-CFM#114 (open upstream, by hieudepzai14122007-ship-it) takes the drop as plain HTML5 drag and drop instead, and fixes the 0 % progress bar and the choose card's buttons along the way. The one conflict was the command list in lib.rs, where this branch adds send_to_destination and the PR adds ingest_bytes; both are kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bug
On Windows, dragging a file onto the island does nothing: Mochi never turns into a box, and
coucou.logshows nodragline at all. This happens both when the island is open and when it is hidden.Repro: Windows 11 (build 26200), WebView2 Runtime 154.0.4258.53. Drag any file from Explorer onto the island and release.
Causes and fixes
Five separate things stood in the way.
1. The native drop hook never fires
The innermost windows a drag passes over,
Chrome_WidgetWin_1andChrome_RenderWidgetHostHWND, are owned by the msedgewebview2 process, not by Coucou. OLE stops at WebView2's own target on those windows, which wry has told to refuse (AllowExternalDrop(false)). It never reaches the target wry registers one level up, onChrome_WidgetWin_0.unblock_webview_dropstries to revoke WebView2's target from our process, which isn't reliable for a window another process owns.Fix:
dragDropEnabledis off, and the island page takes the drop as a plain HTML5 drag and drop, which WebView2 delivers without trouble.ingest_bytescommand as a raw IPC body (name URI-encoded in a header), and Rust writes them into the inbox exactly likeingestdoes: same naming, same week-long sweep.unblock_webview_dropsis removed.2. The wake strip ended up click-through
In
set_collapsed, a cursor-poll tick that was asleep while the island collapsed would wake up, compute "not on the island" for the new 6 px strip, and turn click-through back on. The hidden strip then ignored hovers and drags alike.Fix: the poll is stopped before the flag is reset, and it treats the collapsed strip as always taking the mouse.
3. A 6 px strip is too thin to drop on
Fix: while the island is hidden and a drag is in flight, the strip grows into an invisible 720×150 zone at the top centre, and shrinks back on release. A drag in flight means:
Plain clicks in that area are never affected.
4. A status bar along the top edge can cover the strip
Another always-on-top window spanning the top of the screen sits above the strip, and drops land on it instead. Fix: the island re-asserts
HWND_TOPMOSTwhenever it is placed, and every 2 s.5. A drag that ends somewhere else left the drop view up
HTML5 drag and drop says when a drag leaves the page, but not when it is then dropped somewhere else. Dragging a file over the island and releasing it elsewhere left "Drop your files here" on screen for good. Fix: the cursor poll emits
pointer-releasedwhen the button goes up. If the file had already left the island and was never dropped, the island closes the drop view.Cost while hidden
Fixes 3 and 4 aren't strictly 0 % CPU while hidden: one
GetAsyncKeyStateevery 50 ms, and oneSetWindowPosevery 2 s. Both are negligible, but I'm open to an event-driven alternative if you'd prefer.Linux
dragDropEnabledis turned off intauri.linux.conf.jsontoo, so both platforms use the same HTML5 path. That is untested on Linux. The new watchers are Windows-only (they're skipped whereCURSOR_POLLis false).Testing
.docxfrom Explorer onto the island now logsdrag enter→drag drop, Mochi swallows it, and the copy lands in%LOCALAPPDATA%\Coucou\inbox.cargo check --release: no warnings.tsc --noEmit: clean.🤖 Generated with Claude Code