fix(linux): enable JS resize for frameless windows; fix scrollbar edge detection (#4415, #4680)#5368
Conversation
…e detection Frameless Wails windows on Linux had no resize capability: the drag.ts resize-edge logic was gated behind `!IsWindows()`, so neither the JS-side border detection nor the wails:resize:* messages ever fired on Linux. For decorated (non-frameless) windows the WM provides resize handles, so no change is needed there. Changes: - drag.ts: extend the resize-edge condition to also execute on Linux when `window._wails.flags.frameless` is true, enabling gtk_window_begin_resize_drag to be triggered from the JS path. - drag.ts (both platforms): account for a scrollbar at the window edge by computing rightContentEdge / bottomContentEdge as outerWidth/outerHeight minus the scrollbar width/height (innerWidth - clientWidth). The 5 px resize zone is now placed *just before* the scrollbar rather than on top of it, so the native scrollbar cursor no longer hides the resize cursor and mousedown events reach JavaScript rather than being consumed by the scrollbar. - linux_cgo.go / linux_cgo_gtk4.go: setResizable now calls execJS to keep window._wails.setResizable() in sync (mirrors Windows behaviour). setFrameless propagates window._wails.flags.frameless via execJS for runtime frameless toggles. - webview_window_linux.go: the WindowLoadFinished hook now initialises both flags.frameless and calls setResizable() so the JS runtime has the correct state on first load. Fixes: #4415 Fixes: #4680 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
💤 Files with no reviewable changes (1)
WalkthroughThis PR updates Linux frameless window resizing behavior across the frontend runtime and Go backend. The changes enable dragging edges/corners to resize frameless windows on Linux, compute resize zones accounting for scrollbars, and synchronize resizable/frameless state flags between Go and JavaScript. ChangesLinux Frameless Resize Implementation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Pull request overview
This PR fixes window resizing UX in the v3 desktop runtime by enabling JS-driven resize-edge detection for Linux frameless windows (which otherwise lack WM resize handles) and by adjusting the resize hit-test zone to avoid the native scrollbar strip (which can swallow mouse events at the window edge).
Changes:
- Enable resize-edge detection on Linux when the window is frameless (previously effectively Windows-only).
- Adjust right/bottom edge hit-testing to subtract scrollbar dimensions so resize zones remain mouse-accessible.
- Sync Linux native window state changes (
resizable,frameless) into the JS runtime (including initial values on first load) and rebuild the bundled runtime JS.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
v3/pkg/application/webview_window_linux.go |
Injects initial flags.frameless and calls JS setResizable() at WindowLoadFinished to bootstrap correct runtime state. |
v3/pkg/application/linux_cgo.go |
Updates GTK3 setResizable / setFrameless to also notify the JS runtime. |
v3/pkg/application/linux_cgo_gtk4.go |
Mirrors the GTK3 runtime sync changes for the GTK4 implementation. |
v3/internal/runtime/desktop/@wailsio/runtime/src/drag.ts |
Enables Linux frameless resizing and shifts resize hit-test zones away from scrollbar strips. |
v3/internal/assetserver/bundledassets/runtime.js |
Rebuilt bundled runtime output reflecting the TypeScript changes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
v3/internal/runtime/desktop/@wailsio/runtime/src/drag.ts (1)
237-251: ⚡ Quick win
outerWidth/outerHeightas edge base is incorrect when a native menu bar is present.
event.clientX/Yare viewport coordinates (bounded byinnerWidth/innerHeight). Usingwindow.outerWidthandwindow.outerHeightas the base forrightContentEdge/bottomContentEdgeis fine whenouterWidth == innerWidth, but breaks for a frameless window that includes a native menu bar in its GTK vbox: the menu bar's height causesouterHeight > innerHeight. In that casebottomContentEdge = outerHeight - scrollbarHeight > innerHeight, soevent.clientY(<innerHeight) can never satisfy thebottomContentEdge - event.clientY < resizeHandleHeightcondition — the bottom resize zone becomes permanently unreachable.Using
innerWidth/innerHeight(which already equalclientWidth + scrollbarWidth/clientHeight + scrollbarHeight) is semantically correct regardless of GTK chrome:♻️ Proposed fix
- const rightContentEdge = window.outerWidth - scrollbarWidth; - const bottomContentEdge = window.outerHeight - scrollbarHeight; + const rightContentEdge = window.innerWidth - scrollbarWidth; // == document.documentElement.clientWidth + const bottomContentEdge = window.innerHeight - scrollbarHeight; // == document.documentElement.clientHeight🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@v3/internal/runtime/desktop/`@wailsio/runtime/src/drag.ts around lines 237 - 251, The edge calculations use window.outerWidth/outerHeight which is incorrect when native chrome (e.g. a GTK menu bar) makes outer* > inner*; change the base for rightContentEdge and bottomContentEdge to window.innerWidth and window.innerHeight respectively so viewport-based event.clientX/Y comparisons work; update any dependent checks (rightBorder, bottomBorder, rightCorner, bottomCorner) that reference rightContentEdge/bottomContentEdge to use the new inner-based values and keep existing scrollbarWidth/scrollbarHeight, resizeHandleWidth/resizeHandleHeight, cornerExtra logic unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@v3/internal/runtime/desktop/`@wailsio/runtime/src/drag.ts:
- Around line 237-251: The edge calculations use window.outerWidth/outerHeight
which is incorrect when native chrome (e.g. a GTK menu bar) makes outer* >
inner*; change the base for rightContentEdge and bottomContentEdge to
window.innerWidth and window.innerHeight respectively so viewport-based
event.clientX/Y comparisons work; update any dependent checks (rightBorder,
bottomBorder, rightCorner, bottomCorner) that reference
rightContentEdge/bottomContentEdge to use the new inner-based values and keep
existing scrollbarWidth/scrollbarHeight, resizeHandleWidth/resizeHandleHeight,
cornerExtra logic unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 4682ae93-17cc-44f5-b7bf-b99de005e70e
📒 Files selected for processing (6)
v3/internal/assetserver/bundledassets/runtime.debug.jsv3/internal/assetserver/bundledassets/runtime.jsv3/internal/runtime/desktop/@wailsio/runtime/src/drag.tsv3/pkg/application/linux_cgo.gov3/pkg/application/linux_cgo_gtk4.gov3/pkg/application/webview_window_linux.go
- drag.ts: use innerWidth/innerHeight instead of outerWidth/outerHeight as the base for rightContentEdge/bottomContentEdge; outerWidth can exceed innerWidth when a native menu bar is present (GTK vbox), making the bottom resize zone unreachable via event.clientY - linux_cgo.go/linux_cgo_gtk4.go: guard execJS calls in setResizable and setFrameless with if(window._wails&&...) so they silently no-op before the JS runtime is initialised or after teardown - webview_window_linux.go: same guard for the frameless flag assignment and setResizable call in WindowLoadFinished - Rebuild runtime.js / runtime.debug.js from updated TypeScript Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai>
Cloudflare Pages failure — root cause and fixThe failing check is not caused by this PR's code changes. None of the frameless resize / scrollbar edge files touch docs. Root causeThe i18n batch-2 translations (2026-05-07) introduced four categories of MDX syntax errors across all 7 translated locales (de, fr, ja, ko, pt, ru, zh-tw), causing
FixA dedicated PR with all 22 fixed files (7 locales × 3 files + #5460 — fix(docs/i18n): fix MDX compilation errors causing Cloudflare Pages failure Once #5460 is merged to master, please rebase or merge master into this PR branch so the Cloudflare Pages check picks up the fixed docs. CC @leaanthony Taliesin is an AI agent. CC @leaanthony |
…x/a42a7129 # Conflicts: # v3/internal/assetserver/bundledassets/runtime.debug.js # v3/internal/assetserver/bundledassets/runtime.js
Status update — License Compliance ERROR + rebase neededLicense Compliance: ERROR — This is a transient FOSSA scanner crash, not a real compliance issue. The PR's Merge conflict — This PR was last synced with master before commit Fix is ready — A rebased commit is ready that ports all of this PR's changes to the new file structure:
To apply against master: git checkout -b fix-pr-5368-rebased master
# Apply the source changes (drag.ts + Go files)
# ... (apply patch)
# Rebuild the JS bundles
cd v3 && task runtime:build
git add internal/assetserver/bundledassets/runtime.js \
internal/assetserver/bundledassets/runtime.debug.js
git commit -m "chore: rebuild runtime bundles for frameless resize fix"
git push origin fix-pr-5368-rebased
# Open new PR; close this oneCC @leaanthony Taliesin is an AI agent. CC @leaanthony |
License Compliance: ERROR — Transient FOSSA issue, re-trigger neededThe "License Compliance" status is showing
To resolve: Please re-trigger the FOSSA check via GitHub → PR #5368 → Details → Re-run (or push an empty commit to force a re-scan). No code changes are needed. CC @leaanthony Taliesin is an AI agent. CC @leaanthony |
PR needs rebase — Cloudflare Pages failureThe Cloudflare Pages CI failure is caused by a merge conflict with master. The conflict arose because master merged the GTK4/WebKitGTK 6.0 default-build switch (#5459/#5463) after this PR was last synced. That commit restructured the Linux CGo files:
Rebase instructionsThe changes are small — only 2 lines added per Go file.
func (w *linuxWebviewWindow) setResizable(resizable bool) {
C.gtk_window_set_resizable(w.gtkWindow(), gtkBool(resizable))
w.execJS(fmt.Sprintf("if(window._wails&&window._wails.setResizable)window._wails.setResizable(%v);", resizable))
}
func (w *linuxWebviewWindow) setFrameless(frameless bool) {
C.gtk_window_set_decorated(w.gtkWindow(), gtkBool(!frameless))
w.execJS(fmt.Sprintf("if(window._wails&&window._wails.flags)window._wails.flags.frameless=%v;", frameless))
}
js += fmt.Sprintf("if(window._wails&&window._wails.flags)window._wails.flags.frameless=%v;", w.parent.options.Frameless)
js += fmt.Sprintf("if(window._wails&&window._wails.setResizable)window._wails.setResizable(%v);", !w.parent.options.DisableResize)
After applying, rebuild the runtime bundles: cd v3 && task runtime:build
git add internal/assetserver/bundledassets/runtime.js \
internal/assetserver/bundledassets/runtime.debug.jsThe Linux frameless resize functionality itself is verified working (all 4 edges pass on Ubuntu 24.04 / webkit2gtk-4.1). The PR just needs these conflict-free changes rebased onto the new GTK4-default file structure. CC @leaanthony Taliesin is an AI agent. CC @leaanthony |
|
Status update: License Compliance "3 issues found" The FOSSA "License Compliance: 3 issues found" status on this PR is pre-existing on This PR has been superseded by #5493, which is rebased against current master (after the GTK4/WebKitGTK 6.0 default-build flip in #5459/#5463) and addresses the CodeRabbit review comment about CC @leaanthony Taliesin is an AI agent. CC @leaanthony |
|
The License Compliance: ERROR status here is a transient FOSSA service error — not a real license violation. PR #5368 has zero new Go dependencies (go.mod is identical to master). This PR has been superseded by #5493, which is rebased against the current master (after the GTK4/WebKitGTK 6.0 default-build switch) and addresses the CodeRabbit Requesting closure of this PR as superseded. All the Linux frameless resize work is in #5493. CC @leaanthony Taliesin is an AI agent. CC @leaanthony |
Resolve conflicts from the GTK4-default flip (#5463): - Apply execJS additions in setResizable/setFrameless to the renamed linux_cgo.go (GTK4) and linux_cgo_gtk3.go (GTK3); drop the deleted linux_cgo_gtk4.go - Regenerate bundled runtime.js/runtime.debug.js from merged drag.ts
Bundles content from the failed alpha.99 release (tag pushed but no GitHub release created due to token misconfiguration) together with the alpha.100 cycle. Includes: - MacWebviewPreferences WKWebView extensions (#5549) - generate bindings "Access is denied" fix on Windows (#5561) - Linux frameless JS resize / scrollbar edge detection fix (#5368) - Windows updater cross-volume rename fallback (#5560)
Summary
!IsWindows())mousedownis never seen by JavaScript)Root cause
drag.tscontains the resize-edge cursor andwails:resize:*message logic. It has an early-exit guard:On Linux the guard always fires, so no
wails:resize:*message is ever sent andgtk_window_begin_resize_dragis never called. For decorated windows the WM provides resize handles; for frameless windows nothing does — they are permanently non-resizable in v3.The second issue (scrollbars) affects both Windows and Linux: the native scrollbar occupies the last ≈ 15–17 px at the window edge. The
rightBordercheck used rawwindow.outerWidth - event.clientX, which fired inside the scrollbar strip. The native scrollbar cursor overrides any CSS cursor, hiding the resize hint, and the scrollbar capturesmousedownsoprimaryDown()is never called andcanResizestays false.Changes
drag.tsdrag.tsrightBorder/bottomBorder/rightCorner/bottomCornernow subtract the scrollbar width/height (innerWidth - clientWidth) so the resize zone sits just before the scrollbar, keeping it mouse-event–accessible.linux_cgo.go/linux_cgo_gtk4.gosetResizablecallsexecJSto keepwindow._wails.setResizable()in sync (matching Windows behaviour).setFramelesspropagateswindow._wails.flags.framelessfor runtime frameless toggles.webview_window_linux.goWindowLoadFinishedhook initialisesflags.framelessand callssetResizable()so the JS runtime has correct state on first load.runtime.js/runtime.debug.jsTest plan
ew-resizecursor; click-drag resizes the window; scrollbar still scrolls normally when not at the very edgewindow.SetResizable(false)at runtime correctly disables JS resize on LinuxPlatform notes
Tested with: Ubuntu 24.04 LTS, GTK3 + webkit2gtk-4.0. The GTK4 path (
linux_cgo_gtk4.go) receives the samesetResizable/setFramelessJS notification changes.CC @leaanthony
Summary by CodeRabbit