Skip to content

fix(linux): enable JS resize for frameless windows; fix scrollbar edge detection (#4415, #4680)#5368

Open
leaanthony wants to merge 3 commits intomasterfrom
agent/engineer-linux/a42a7129
Open

fix(linux): enable JS resize for frameless windows; fix scrollbar edge detection (#4415, #4680)#5368
leaanthony wants to merge 3 commits intomasterfrom
agent/engineer-linux/a42a7129

Conversation

@leaanthony
Copy link
Copy Markdown
Member

@leaanthony leaanthony commented May 8, 2026

Summary

  • Frameless windows on Linux could not be resized at all (JS resize-edge logic was gated behind !IsWindows())
  • Scrollbar at window edge blocked resize on all platforms (the 5 px resize zone overlapped with the native scrollbar strip, whose mousedown is never seen by JavaScript)

Root cause

drag.ts contains the resize-edge cursor and wails:resize:* message logic. It has an early-exit guard:

if (!resizable || !IsWindows()) { ... return; }

On Linux the guard always fires, so no wails:resize:* message is ever sent and gtk_window_begin_resize_drag is 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 rightBorder check used raw window.outerWidth - event.clientX, which fired inside the scrollbar strip. The native scrollbar cursor overrides any CSS cursor, hiding the resize hint, and the scrollbar captures mousedown so primaryDown() is never called and canResize stays false.

Changes

File Change
drag.ts Guard condition extended: Linux frameless windows now enter the resize-detection path.
drag.ts rightBorder / bottomBorder / rightCorner / bottomCorner now 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.go setResizable calls execJS to keep window._wails.setResizable() in sync (matching Windows behaviour). setFrameless propagates window._wails.flags.frameless for runtime frameless toggles.
webview_window_linux.go WindowLoadFinished hook initialises flags.frameless and calls setResizable() so the JS runtime has correct state on first load.
runtime.js / runtime.debug.js Rebuilt from updated TypeScript.

Test plan

  • Frameless window on Linux (X11 and Wayland): resize from all 8 edges works
  • Frameless window with a right vertical scrollbar on Linux: hover near content right edge shows ew-resize cursor; click-drag resizes the window; scrollbar still scrolls normally when not at the very edge
  • Frameless window on Windows: existing resize behaviour unchanged; scrollbar-at-edge resize confirmed fixed
  • Decorated (non-frameless) window on Linux: WM-provided resize still works; no regression
  • window.SetResizable(false) at runtime correctly disables JS resize on Linux

Platform notes

Tested with: Ubuntu 24.04 LTS, GTK3 + webkit2gtk-4.0. The GTK4 path (linux_cgo_gtk4.go) receives the same setResizable/setFrameless JS notification changes.

CC @leaanthony

Summary by CodeRabbit

  • Bug Fixes

    • Improved window edge detection for resize when scrollbars consume edge events.
    • Allowed frameless resizing on Linux (broader gating).
    • Ensure Linux toggles for frameless and resizable windows are reflected in the UI.
    • Inject startup flags so frontend accurately knows frameless/resizable state.
  • Chores

    • Regenerated client runtime for internal reliability and wiring improvements.

Review Change Stack

…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>
Copilot AI review requested due to automatic review settings May 8, 2026 02:31
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 8, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f1fa0a79-d37f-4fa9-b9a1-aaa4100d3fbb

📥 Commits

Reviewing files that changed from the base of the PR and between ca808cd and 89fdda3.

📒 Files selected for processing (6)
  • v3/internal/assetserver/bundledassets/runtime.debug.js
  • v3/internal/assetserver/bundledassets/runtime.js
  • v3/internal/runtime/desktop/@wailsio/runtime/src/drag.ts
  • v3/pkg/application/linux_cgo.go
  • v3/pkg/application/linux_cgo_gtk4.go
  • v3/pkg/application/webview_window_linux.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • v3/pkg/application/webview_window_linux.go
  • v3/pkg/application/linux_cgo_gtk4.go
  • v3/pkg/application/linux_cgo.go
  • v3/internal/runtime/desktop/@wailsio/runtime/src/drag.ts

Walkthrough

This PR enables frameless window resizing on Linux by coordinating three areas: regenerating the frontend runtime bundle, updating drag/resize detection to be scrollbar-aware and allow frameless resizing on Linux, and syncing frameless/resizable flags from the Go backend to the frontend (including startup injection).

Changes

Linux Frameless Resize Implementation

Layer / File(s) Summary
Frontend Runtime Contract
v3/internal/assetserver/bundledassets/runtime.js
Minified JavaScript runtime bundle is regenerated with updated internal symbol bindings while preserving the same exported API surface and behavior for transport, events, dialogs, window methods, and call machinery.
Drag/Resize Detection with Scrollbar Awareness
v3/internal/runtime/desktop/@wailsio/runtime/src/drag.ts
Resize-edge detection now permits frameless windows on Linux, computes content edges by subtracting scrollbar sizes from viewport dimensions, and applies those adjusted edges to border and corner zone detection.
Backend-to-Frontend Window State Synchronization
v3/pkg/application/linux_cgo.go, v3/pkg/application/linux_cgo_gtk4.go
Go backend methods setResizable() and setFrameless() now execute guarded frontend JS via execJS to update window._wails.setResizable(...) and window._wails.flags.frameless, synchronizing GTK window properties with frontend state.
Window Initialization with Runtime Flags
v3/pkg/application/webview_window_linux.go
During WindowLoadFinished hook on Linux, injected startup JavaScript is extended to include window._wails.flags.frameless and call window._wails.setResizable() based on DisableResize.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • wailsapp/wails#5159: Implements native Linux frameless startResize behavior that complements this PR's drag-detection and frameless-window-state changes.
  • wailsapp/wails#5340: Modifies the Linux WindowLoadFinished startup/JS injection path; touches the same initialization flow.
  • wailsapp/wails#4848: Also changes runtime/frontend wiring and desktop drag/resize code with overlapping edits to flags and resize handling.

Suggested labels

Bug, Linux, runtime, v3, go, size:M, lgtm

Suggested reviewers

  • atterpac

Poem

🐰 Frameless windows hop with grace,
Scrollbars stepped aside in place,
Linux edges learn to feel the drag,
Backend whispers flags to the stag,
Resize cursors find their pace.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main changes: enabling JS resize for frameless windows on Linux and fixing scrollbar edge detection issues.
Description check ✅ Passed The PR description is comprehensive, covering root causes, specific file changes, test plan, and platform notes. However, it does not include an explicit 'Fixes #' reference or completion of the template's Type of change checklist and test verification sections.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/engineer-linux/a42a7129

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.1)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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.

Comment thread v3/pkg/application/linux_cgo.go Outdated
Comment thread v3/pkg/application/linux_cgo.go Outdated
C.gtk_window_set_decorated(w.gtkWindow(), gtkBool(!frameless))
// TODO: Deal with transparency for the titlebar if possible when !frameless
// Perhaps we just make it undecorated and add a menu bar inside?
w.execJS(fmt.Sprintf("window._wails.flags.frameless=%v;", frameless))
Comment thread v3/pkg/application/linux_cgo_gtk4.go Outdated
Comment thread v3/pkg/application/linux_cgo_gtk4.go Outdated

func (w *linuxWebviewWindow) setFrameless(frameless bool) {
C.gtk_window_set_decorated(w.gtkWindow(), gtkBool(!frameless))
w.execJS(fmt.Sprintf("window._wails.flags.frameless=%v;", frameless))
Comment thread v3/pkg/application/webview_window_linux.go Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
v3/internal/runtime/desktop/@wailsio/runtime/src/drag.ts (1)

237-251: ⚡ Quick win

outerWidth/outerHeight as edge base is incorrect when a native menu bar is present.

event.clientX/Y are viewport coordinates (bounded by innerWidth/innerHeight). Using window.outerWidth and window.outerHeight as the base for rightContentEdge/bottomContentEdge is fine when outerWidth == innerWidth, but breaks for a frameless window that includes a native menu bar in its GTK vbox: the menu bar's height causes outerHeight > innerHeight. In that case bottomContentEdge = outerHeight - scrollbarHeight > innerHeight, so event.clientY (< innerHeight) can never satisfy the bottomContentEdge - event.clientY < resizeHandleHeight condition — the bottom resize zone becomes permanently unreachable.

Using innerWidth/innerHeight (which already equal clientWidth + 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

📥 Commits

Reviewing files that changed from the base of the PR and between d1be17b and ca808cd.

📒 Files selected for processing (6)
  • v3/internal/assetserver/bundledassets/runtime.debug.js
  • v3/internal/assetserver/bundledassets/runtime.js
  • v3/internal/runtime/desktop/@wailsio/runtime/src/drag.ts
  • v3/pkg/application/linux_cgo.go
  • v3/pkg/application/linux_cgo_gtk4.go
  • v3/pkg/application/webview_window_linux.go

taliesin-ai and others added 2 commits May 9, 2026 18:36
- 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>
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.

3 participants