-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
feat(v3): Add WebviewPanel for embedding multiple webviews in a window #4880
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v3-alpha
Are you sure you want to change the base?
Conversation
This implements the feature requested in wailsapp#1997 - allowing multiple webview containers (panels) within a single window. ## Features - WebviewPanel: A new type for embedded webview panels within a window - Cross-platform support: Windows (WebView2), macOS (WKWebView), Linux (WebKit2GTK) - Absolute positioning with X, Y, Width, Height coordinates - Z-index support for panel stacking order - URL and HTML content loading - JavaScript execution in panel context - Show/Hide/Focus/Zoom controls - Developer tools support - Layout helper methods: DockLeft, DockRight, DockTop, DockBottom, FillWindow, FillBeside ## API Go: ```go panel := window.NewPanel(application.WebviewPanelOptions{ Name: "sidebar", X: 0, Y: 50, Width: 200, Height: 600, URL: "https://example.com", }) panel.SetBounds(rect).SetURL(url).Show() ``` TypeScript: ```typescript const panel = Panel.Get("sidebar"); await panel.SetURL("https://wails.io"); ``` ## Files Added - webview_panel.go - Core panel struct and methods - webview_panel_options.go - Panel configuration options - webview_panel_windows.go - Windows implementation - webview_panel_darwin.go - macOS implementation - webview_panel_linux.go - Linux implementation - webview_panel_android.go - Android stub - webview_panel_ios.go - iOS stub - messageprocessor_panel.go - Frontend-to-backend communication - panel.ts - TypeScript runtime API - examples/webview-panel/ - Demo application Closes wailsapp#1997
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds WebView Panel support: multi-panel webview containers inside a single window with Go core abstractions, platform-specific implementations (Windows, macOS, Linux, iOS/Android stubs), TypeScript runtime bindings, message-processing plumbing, examples, and docs. Changes
Sequence Diagram(s)sequenceDiagram
participant Frontend as Frontend (TypeScript)
participant Caller as Runtime Caller
participant MP as Message Processor
participant Impl as Platform Impl
Frontend->>Caller: Panel.SetBounds({x,y,w,h})
Caller->>MP: runtime call (panelRequest, PanelSetBounds)
MP->>MP: processPanelMethod -> resolve panel
MP->>Impl: setBounds(bounds)
Impl-->>MP: success
MP-->>Caller: void
Caller-->>Frontend: Promise resolved
sequenceDiagram
participant App as Go App
participant Window as WebviewWindow
participant Panel as WebviewPanel
participant Impl as Platform Impl
App->>Window: NewPanel(options)
Window->>Panel: create WebviewPanel
alt Window running
Panel->>Impl: create native panel
Impl-->>Panel: created
else
Panel->>Window: queued for runPanels
end
Window-->>App: *WebviewPanel
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 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
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
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/messageprocessor.go (1)
169-202: Add missingpanelRequestcase inlogRuntimeCallfor complete panel request logging.The
logRuntimeCallswitch statement doesn't handlepanelRequest, which is a valid request type processed elsewhere in the codebase. Panel-related runtime calls will log with an emptymethodName, reducing debugging visibility.Fix
case androidRequest: methodName = androidMethodNames[req.Method] + case panelRequest: + methodName = panelMethodNames[req.Method] } m.Debug("Runtime call:", "method", objectName+"."+methodName, "args", req.Args.String())
🤖 Fix all issues with AI agents
In `@v3/pkg/application/webview_panel_darwin.go`:
- Around line 45-77: The WebviewPanelDelegate instance created by
WebviewPanelDelegate* delegate = [[WebviewPanelDelegate alloc] init]; is missing
an autorelease call and will leak because WKUserContentController retains script
message handlers; add an autorelease for delegate (i.e., call [delegate
autorelease]; immediately after its init) to match the webview_window_darwin.go
pattern so the initial alloc is balanced and the delegate is released when the
autorelease pool drains.
In `@v3/pkg/application/webview_panel_linux.go`:
- Around line 186-190: The panel is being packed into the parent vbox (using
p.parent.vbox and gtk_box_pack_start) which places panels in the vertical layout
rather than layered overlays; change the window/container root to use a
GtkOverlay in windowNew() and add the main webview as the base child and panels
(p.fixed) as overlay children via gtk_overlay_add_overlay so
panel_fixed_put(p.fixed, p.webview, ...) positions remain absolute and panels
float above the webview; update any places that assume a vbox root (e.g., where
p.parent.vbox is set or used) to reference the new overlay container.
In `@v3/pkg/application/webview_panel_windows.go`:
- Around line 192-198: The CSS injection into JS in the navigation callback
currently only escapes single quotes and will break on backslashes or newlines;
update the code around p.execJS(...) that builds js to produce a properly
escaped JS string (e.g., use strconv.Quote or json.Marshal on
p.panel.options.CSS to get a safely quoted string) and then insert that quoted
value into the generated script so backslashes and newlines are preserved and
the injected style is robust.
- Around line 162-177: The CSS injection is unsafe because loadHTMLWithScripts
builds a JS string by only escaping double quotes; instead properly produce a
JS-safe string for p.panel.options.CSS (and similarly for JS/HTML if needed) by
using a robust string-quoting/escaping function (e.g. strconv.Quote or
encoding/json.Marshal) rather than manual ReplaceAll; update loadHTMLWithScripts
in windowsPanelImpl to call that quoting function on p.panel.options.CSS, insert
the quoted value into the injected script passed to p.chromium.Init, and ensure
backslashes, newlines, carriage returns and other special characters are escaped
so the injected JS cannot be broken out of.
- Around line 204-210: The destroy() method on windowsPanelImpl currently
destroys the native window without notifying the embedded Chromium instance;
call p.chromium.ShuttingDown() (guarded by a nil check) before invoking
w32.DestroyWindow to mirror the cleanup used in the main window; ensure you call
p.chromium.ShuttingDown() only if p.chromium != nil, then proceed to
DestroyWindow(p.hwnd) and clear p.hwnd and p.chromium as before.
In `@v3/pkg/application/webview_panel.go`:
- Around line 355-363: markRuntimeLoaded mutates runtimeLoaded and iterates
pendingJS without synchronization causing races with ExecJS and other accessors;
add a mutex on WebviewPanel (e.g., p.mu sync.Mutex or sync.RWMutex) and use it
to guard reads/writes to runtimeLoaded and pendingJS: in markRuntimeLoaded
acquire lock, set p.runtimeLoaded = true, copy p.pendingJS to a local slice and
set p.pendingJS = nil, then release the lock and iterate/local-execute the
copied slice (call p.ExecJS or execute directly) outside the lock; likewise
update ExecJS to acquire the mutex to check p.runtimeLoaded and append to
p.pendingJS when not loaded (or read the flag and decide to execute) ensuring no
long-running execution happens while holding the lock.
- Around line 222-233: Concurrent calls to ExecJS can race on the pendingJS
slice; add a mutex in the WebviewPanel struct (e.g., pendingMu sync.Mutex) and
use it to protect all accesses to pendingJS in ExecJS (lock before
checking/append/unlock) and wherever pendingJS is drained or read (e.g., the
code path that sets runtimeLoaded and invokes pending JS), ensuring you still
call impl.execJS under InvokeSync and preserve existing checks (p.impl,
p.isDestroyed, p.runtimeLoaded).
In `@v3/pkg/application/webview_window.go`:
- Around line 1636-1645: The runPanels method currently holds w.panelsLock.RLock
while iterating and calling panel.run(), which may deadlock because panel.run()
(and its InvokeSync) can contend for other locks or writers; change runPanels
(method on WebviewWindow) to snapshot the relevant panels while holding the
RLock into a local slice (filtering by panel.impl == nil if desired), release
the RLock, then iterate over that snapshot and call panel.run() outside the lock
so panel.run/InvokeSync cannot block writers trying to acquire panelsLock.
- Around line 1647-1660: The window's panels are never cleaned up because
destroyAllPanels() is never invoked; update the WindowClosing event handler to
call w.destroyAllPanels() before or as part of the window shutdown sequence
(e.g., alongside any existing cleanup/close logic in the WindowClosing handler)
so panels are destroyed and resources released; ensure you call the method on
the correct receiver (w.destroyAllPanels()) and keep the call synchronous with
the existing lock/unlock semantics already implemented by destroyAllPanels().
🧹 Nitpick comments (10)
v3/pkg/application/webview_panel_options.go (1)
82-100: AnchorType constant values may be unexpected due to iota positioning.The
iotaresets at the start of the const block, so:
AnchorNone = 0(iota=0)AnchorTop = 1 << iota= 1 << 1 = 2 (iota=1)AnchorBottom= 1 << 2 = 4AnchorLeft= 1 << 3 = 8AnchorRight= 1 << 4 = 16This works correctly for bitwise operations, but if you intended the typical pattern (1, 2, 4, 8), consider restructuring:
♻️ Optional: Standard bitflag pattern
const ( // AnchorNone - panel uses absolute positioning only (default) AnchorNone AnchorType = 0 - // AnchorTop - panel maintains distance from top edge - AnchorTop AnchorType = 1 << iota + // AnchorTop - panel maintains distance from top edge + AnchorTop AnchorType = 1 << iota // iota=0 here, so 1 - // AnchorBottom - panel maintains distance from bottom edge - AnchorBottom + // AnchorBottom - panel maintains distance from bottom edge + AnchorBottom // 2 - // AnchorLeft - panel maintains distance from left edge - AnchorLeft + // AnchorLeft - panel maintains distance from left edge + AnchorLeft // 4 - // AnchorRight - panel maintains distance from right edge - AnchorRight + // AnchorRight - panel maintains distance from right edge + AnchorRight // 8 // AnchorFill - panel fills the entire window (anchored to all edges) AnchorFill AnchorType = AnchorTop | AnchorBottom | AnchorLeft | AnchorRight )Or keep as-is if the current values (2, 4, 8, 16) are intentional.
v3/examples/webview-panel/main.go (1)
201-203: Consider using a shared pointer helper if available.The
boolPtrhelper is a common pattern. If theapplicationpackage provides a generic pointer utility (e.g.,application.Ptr[T]or similar), consider using it for consistency. Otherwise, this inline helper is fine for an example.v3/pkg/application/webview_panel_darwin.go (1)
252-257: Empty navigation callback could be removed or implemented.
panelNavigationCompletedis exported and called from Objective-C on navigation completion but performs no action. Consider either implementing the callback (e.g., to update panel state) or removing the delegate method call to avoid unnecessary cross-language overhead.v3/pkg/application/webview_panel_windows.go (1)
254-257: Unused variableclientPoint.The
clientPointvariable is declared and its fields are set to 0, but the values are never read. The actual conversion usesw32.ClientToScreendirectly.Remove unused variable
// Calculate position relative to parent's client area - var clientPoint w32.POINT - clientPoint.X = 0 - clientPoint.Y = 0 parentClientX, parentClientY := w32.ClientToScreen(p.parent.hwnd, 0, 0)v3/internal/runtime/desktop/@wailsio/runtime/src/panel.ts (2)
95-103: Consider adding input validation for bounds.The
SetBoundsandGetBoundsmethods don't validate that bounds values are non-negative. While the backend may handle this, frontend validation could provide better error messages.Also applies to: 110-114
277-281:Name()method makes unnecessary backend call.The panel name is already stored locally in
this[panelNameSym]. Consider returning it directly instead of making a round-trip to the backend.Suggested optimization
Name(): Promise<string> { - return this[callerSym](NameMethod, { - panel: this[panelNameSym] - }); + return Promise.resolve(this[panelNameSym]); }v3/pkg/application/webview_panel_android.go (1)
1-33: Clean stub implementation for unsupported platform.The Android stub correctly implements all
webviewPanelImplinterface methods with sensible defaults. Consider adding a log warning increate()to inform developers that panels are not supported on Android.Optional: Add warning log
-func (p *androidPanelImpl) create() {} +func (p *androidPanelImpl) create() { + globalApplication.warning("WebviewPanel is not yet supported on Android") +}v3/pkg/application/messageprocessor_panel.go (1)
31-50:panelMethodNamesmap is defined but never used.The map could improve the error message in the default case for unknown methods.
Use panelMethodNames in error message
default: - return nil, fmt.Errorf("unknown panel method: %d", req.Method) + methodName, ok := panelMethodNames[req.Method] + if ok { + return nil, fmt.Errorf("unhandled panel method: %s (%d)", methodName, req.Method) + } + return nil, fmt.Errorf("unknown panel method: %d", req.Method) }Also applies to: 184-186
v3/pkg/application/webview_panel.go (2)
44-51: Redundant locking for atomic operation.
panelIDLockmutex is used to protect anatomic.AddUint32operation. The atomic operation is already thread-safe, making the mutex unnecessary.Simplify ID generation
var panelID uint32 -var panelIDLock sync.Mutex func getNextPanelID() uint { - panelIDLock.Lock() - defer panelIDLock.Unlock() return uint(atomic.AddUint32(&panelID, 1)) }
420-462:FillBesideuses magic strings for direction.Consider using a typed constant for the
directionparameter to provide compile-time safety and better documentation.Optional: Use typed direction constant
type DockDirection string const ( DockDirectionRight DockDirection = "right" DockDirectionLeft DockDirection = "left" DockDirectionBelow DockDirection = "below" DockDirectionAbove DockDirection = "above" ) func (p *WebviewPanel) FillBeside(refPanel *WebviewPanel, direction DockDirection) *WebviewPanel { // ... }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
v3/examples/webview-panel/README.mdv3/examples/webview-panel/main.gov3/internal/runtime/desktop/@wailsio/runtime/src/index.tsv3/internal/runtime/desktop/@wailsio/runtime/src/panel.tsv3/internal/runtime/desktop/@wailsio/runtime/src/runtime.tsv3/pkg/application/messageprocessor.gov3/pkg/application/messageprocessor_panel.gov3/pkg/application/webview_panel.gov3/pkg/application/webview_panel_android.gov3/pkg/application/webview_panel_darwin.gov3/pkg/application/webview_panel_ios.gov3/pkg/application/webview_panel_linux.gov3/pkg/application/webview_panel_options.gov3/pkg/application/webview_panel_windows.gov3/pkg/application/webview_window.go
🧰 Additional context used
🧠 Learnings (12)
📓 Common learnings
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).
Learnt from: nixpare
Repo: wailsapp/wails PR: 3763
File: v3/pkg/application/webview_panel_darwin.go:88-89
Timestamp: 2024-09-21T09:56:48.126Z
Learning: Safety checks for `p.nsPanel` are performed in the `SetFloating` method of `WebviewPanel`, following the `WebviewWindow` and `macosWebviewWindow` implementations and code style.
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.
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-29T08:02:15.134Z
Learnt from: popaprozac
Repo: wailsapp/wails PR: 4839
File: docs/src/content/docs/reference/window.mdx:616-620
Timestamp: 2025-12-29T08:02:15.134Z
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/examples/webview-panel/main.gov3/pkg/application/webview_panel_options.gov3/examples/webview-panel/README.mdv3/pkg/application/webview_panel_ios.gov3/pkg/application/webview_window.gov3/pkg/application/webview_panel_windows.gov3/pkg/application/webview_panel_darwin.gov3/pkg/application/webview_panel.gov3/pkg/application/webview_panel_linux.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/webview-panel/main.gov3/examples/webview-panel/README.md
📚 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/examples/webview-panel/main.gov3/pkg/application/webview_panel_ios.gov3/pkg/application/webview_window.gov3/pkg/application/webview_panel_windows.gov3/pkg/application/webview_panel_darwin.gov3/pkg/application/webview_panel.gov3/pkg/application/webview_panel_linux.go
📚 Learning: 2024-10-08T22:11:37.054Z
Learnt from: leaanthony
Repo: wailsapp/wails PR: 3763
File: v3/internal/commands/appimage_testfiles/main.go:295-299
Timestamp: 2024-10-08T22:11:37.054Z
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/webview-panel/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/examples/webview-panel/main.gov3/pkg/application/webview_panel_options.gov3/examples/webview-panel/README.mdv3/pkg/application/webview_window.gov3/pkg/application/webview_panel_windows.gov3/pkg/application/webview_panel_darwin.gov3/pkg/application/webview_panel.go
📚 Learning: 2026-01-04T08:01:00.038Z
Learnt from: symball
Repo: wailsapp/wails PR: 4853
File: v2/internal/system/system.go:128-152
Timestamp: 2026-01-04T08:01:00.038Z
Learning: In v2/internal/system/system.go, shared functions like checkLibrary are defined without build tags but are only invoked from platform-specific files (system_linux.go, system_windows.go, system_darwin.go) that have build constraints. Reviewers should ensure there are no runtime OS checks in system.go and that platform-specific behavior is controlled via build tags. If runtime switches exist, remove them in favor of compile-time platform constraints to reduce overhead and improve correctness.
Applied to files:
v3/examples/webview-panel/main.gov3/pkg/application/webview_panel_options.gov3/pkg/application/messageprocessor.gov3/pkg/application/webview_panel_ios.gov3/pkg/application/messageprocessor_panel.gov3/pkg/application/webview_panel_android.gov3/pkg/application/webview_window.gov3/pkg/application/webview_panel_windows.gov3/pkg/application/webview_panel_darwin.gov3/pkg/application/webview_panel.gov3/pkg/application/webview_panel_linux.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:
v3/pkg/application/webview_panel_options.go
📚 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/index.ts
📚 Learning: 2024-09-21T09:56:48.126Z
Learnt from: nixpare
Repo: wailsapp/wails PR: 3763
File: v3/pkg/application/webview_panel_darwin.go:88-89
Timestamp: 2024-09-21T09:56:48.126Z
Learning: Safety checks for `p.nsPanel` are performed in the `SetFloating` method of `WebviewPanel`, following the `WebviewWindow` and `macosWebviewWindow` implementations and code style.
Applied to files:
v3/examples/webview-panel/README.mdv3/internal/runtime/desktop/@wailsio/runtime/src/panel.tsv3/pkg/application/webview_window.gov3/pkg/application/webview_panel_windows.gov3/pkg/application/webview_panel_darwin.gov3/pkg/application/webview_panel.gov3/pkg/application/webview_panel_linux.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_panel_windows.gov3/pkg/application/webview_panel_darwin.gov3/pkg/application/webview_panel_linux.go
📚 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/pkg/application/webview_panel_windows.gov3/pkg/application/webview_panel_darwin.gov3/pkg/application/webview_panel_linux.go
🧬 Code graph analysis (10)
v3/examples/webview-panel/main.go (3)
v3/pkg/application/webview_window_options.go (3)
WebviewWindowOptions(32-145)BackgroundType(176-176)BackgroundTypeSolid(179-179)v3/pkg/application/webview_panel.go (1)
NewPanel(75-105)v3/pkg/application/webview_panel_options.go (1)
WebviewPanelOptions(5-76)
v3/pkg/application/webview_panel_options.go (2)
v3/internal/runtime/desktop/@wailsio/runtime/src/panel.ts (1)
Name(277-281)v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts (2)
Width(597-599)Height(309-311)
v3/pkg/application/webview_panel_ios.go (1)
v3/pkg/application/webview_panel.go (1)
WebviewPanel(57-71)
v3/internal/runtime/desktop/@wailsio/runtime/src/panel.ts (2)
v3/internal/runtime/desktop/@wailsio/runtime/src/index.ts (2)
Panel(41-41)objectNames(63-63)v3/internal/runtime/desktop/@wailsio/runtime/src/runtime.ts (2)
newRuntimeCaller(98-102)objectNames(19-34)
v3/pkg/application/webview_panel_android.go (1)
v3/pkg/application/webview_panel.go (1)
WebviewPanel(57-71)
v3/pkg/application/webview_window.go (4)
v3/pkg/application/webview_panel.go (2)
WebviewPanel(57-71)NewPanel(75-105)v3/pkg/application/webview_panel_options.go (1)
WebviewPanelOptions(5-76)v3/pkg/application/mainthread.go (1)
InvokeSync(23-32)v3/internal/runtime/desktop/@wailsio/runtime/src/panel.ts (1)
Destroy(266-270)
v3/pkg/application/webview_panel_windows.go (4)
v3/pkg/application/webview_panel.go (1)
WebviewPanel(57-71)v3/pkg/application/screenmanager.go (2)
DipToPhysicalRect(853-855)PhysicalToDipRect(857-859)v3/pkg/w32/window.go (1)
MustStringToUTF16Ptr(173-180)v3/internal/assetserver/assetserver.go (1)
GetStartURL(149-175)
v3/pkg/application/webview_panel_darwin.go (2)
v3/pkg/application/webview_panel.go (1)
WebviewPanel(57-71)v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts (2)
Width(597-599)Height(309-311)
v3/pkg/application/webview_panel.go (4)
v3/pkg/application/webview_panel_options.go (1)
WebviewPanelOptions(5-76)v3/pkg/application/webview_window.go (1)
WebviewWindow(142-180)v3/internal/runtime/desktop/@wailsio/runtime/src/panel.ts (18)
Name(277-281)SetBounds(95-103)Bounds(36-45)SetZIndex(121-126)SetURL(133-138)SetHTML(145-150)ExecJS(157-162)Reload(167-171)ForceReload(176-180)Show(185-189)Hide(194-198)IsVisible(205-209)SetZoom(216-221)GetZoom(228-232)OpenDevTools(257-261)Focus(237-241)IsFocused(248-252)Destroy(266-270)v3/pkg/application/mainthread.go (2)
InvokeSync(23-32)InvokeSyncWithResult(34-44)
v3/pkg/application/webview_panel_linux.go (2)
v3/pkg/application/webview_panel.go (1)
WebviewPanel(57-71)v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts (2)
Width(597-599)Height(309-311)
⏰ 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). (3)
- GitHub Check: CodeQL analysis (go)
- GitHub Check: Agent
- GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (28)
v3/pkg/application/webview_panel_options.go (1)
3-76: Well-structured options type with clear documentation.The
WebviewPanelOptionsstruct is well-designed:
- Pointer types for
VisibleandDevToolsEnabledappropriately distinguish "unset" fromfalse- Defaults are clearly documented in comments
- Field naming is consistent with
WebviewWindowOptionsv3/internal/runtime/desktop/@wailsio/runtime/src/runtime.ts (1)
32-33: Object IDs correctly align with Go backend.The
Android: 12andPanel: 13entries match the corresponding constants inmessageprocessor.go(androidRequest = 12,panelRequest = 13), ensuring proper IPC routing.v3/examples/webview-panel/main.go (1)
9-198: Well-structured example demonstrating the WebviewPanel feature.The example effectively showcases:
- Creating a main window with embedded HTML header
- Two panels with different content types (HTML sidebar, external URL content)
- Explicit coordinate-based positioning
- Commented alternatives showing layout helpers
The panel dimensions (Height: 650 + Y: 50 = 700) correctly fit within the window height (700).
v3/pkg/application/messageprocessor.go (3)
30-31: Constant correctly added for panel request routing.The
panelRequest = 13constant is properly defined and sequential with other request types.
47-48: Object name mapping correctly added.The
panelRequest: "Panel"entry enables proper object name resolution for logging.
132-133: Panel request dispatch correctly integrated.The routing to
m.processPanelMethod(req, targetWindow)follows the established pattern for other request types.v3/internal/runtime/desktop/@wailsio/runtime/src/index.ts (2)
29-29: Panel import correctly added.The named import
{ Panel }from"./panel.js"follows the established pattern for class/object exports in this codebase.
41-41: Panel correctly exported in public API.The
Panelexport is properly positioned in the export block, making it available to consumers of the runtime API.v3/pkg/application/webview_panel_linux.go (3)
279-283: Z-index is a no-op on Linux - documented limitation.The comment explains that GTK lacks a direct z-index concept. This is acceptable for the initial implementation. If z-ordering becomes important,
gtk_box_reorder_childor switching to GtkOverlay with proper child ordering could be explored.
168-240: Panel creation implementation looks correct.The create flow properly initializes the WebView, sets configuration options (size, devtools, background color, zoom, visibility), and loads initial content. Memory management with
defer C.freefor C strings is correct.
249-379: Panel lifecycle and manipulation methods are well implemented.All methods include proper nil checks before operating on the webview/fixed pointers. The implementation follows the same patterns seen in other platform implementations.
v3/pkg/application/webview_panel_ios.go (1)
1-33: iOS stub implementation is correct.This provides a proper no-op implementation for iOS where panels are not yet supported. The stub maintains API surface consistency and allows the codebase to compile for iOS without feature support.
v3/pkg/application/webview_panel_darwin.go (2)
193-198: DevTools limitation documented - acceptable for initial implementation.Opening the WebKit inspector programmatically requires private APIs. The comment clearly documents that users can use right-click → Inspect Element if devtools are enabled.
259-448: Darwin panel implementation is well structured.The Go wrapper methods properly:
- Check for nil webview before CGO calls
- Handle coordinate conversion for macOS bottom-left origin
- Free C strings with defer
- Follow the established patterns from WebviewWindow
v3/pkg/application/webview_panel_windows.go (3)
41-82: Panel creation implementation looks correct.The child window is properly created with
WS_CHILD | WS_CLIPSIBLINGSstyle, DIP-to-physical conversion is applied, and error handling is in place.
84-160: WebView2 setup is well implemented.The chromium configuration properly handles:
- Error callback registration
- Data and browser paths from global options
- Message and navigation callbacks
- Settings for context menus and devtools
- Zoom and background color
- Initial content loading
320-327: The review comment's recommendation cannot be implemented. While Microsoft's WebView2 API does have a nativeReload()method on ICoreWebView2, the go-webview2 v1.0.22 package used in this project does not expose it on theedge.Chromiumbinding. The available methods onedge.ChromiumincludeNavigate(),Eval(),NavigateToString(), and others, but notReload(). The current implementation using JavaScript injection (p.execJS("window.location.reload();")) is the appropriate approach given the available bindings.Likely an incorrect or invalid review comment.
v3/examples/webview-panel/README.md (1)
1-143: Documentation is comprehensive and well-structured, with minor whitespace cleanup needed.The README provides a clear overview of the WebviewPanel feature with practical examples covering Go and TypeScript APIs, use cases, and a helpful comparison table. The documentation accurately reflects the implemented API, including the
GetPanelByIDmethod and all other operations shown.Minor nit: Line 26 has trailing whitespace that should be removed.
⛔ 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:15.134Z 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: nixpare Repo: wailsapp/wails PR: 3763 File: v3/pkg/application/webview_panel_darwin.go:88-89 Timestamp: 2024-09-21T09:56:48.126Z Learning: Safety checks for `p.nsPanel` are performed in the `SetFloating` method of `WebviewPanel`, following the `WebviewWindow` and `macosWebviewWindow` implementations and code style.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: 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).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.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.v3/internal/runtime/desktop/@wailsio/runtime/src/panel.ts (3)
13-31: Method constants alignment looks correct.The method constants (0-17) match the corresponding
Panel*constants inmessageprocessor_panel.go. This is critical for the frontend-backend bridge to function correctly.
71-76: Method binding loop iterates over all prototype methods.This pattern binds all instance methods to
this, which is useful for event handlers. However, it usesObject.getOwnPropertyNames(Panel.prototype)which only gets own properties, not inherited ones—this is correct for this use case.
36-45: Bounds interface is well-defined.The interface properly documents the position and size fields with JSDoc comments.
v3/pkg/application/messageprocessor_panel.go (3)
10-29: Constants match TypeScript frontend.The
Panel*constants correctly match the method constants inpanel.ts, ensuring proper frontend-backend communication.
64-81: Panel lookup has redundant fallback logic.The code first looks up by name, then falls back to ID lookup. However, if
panelNameis empty, the error at line 67 would already be returned before reaching the ID fallback. The ID fallback only activates when a name is provided but no panel is found with that name.This is acceptable behavior but the comment at line 73 ("Try by ID") could be misleading since this isn't a true fallback for missing names.
83-93:PanelSetBoundsvalidates all required parameters correctly.The validation for
x,y,width, andheightbeing non-nil is appropriate.v3/pkg/application/webview_window.go (2)
176-179: Panel management fields properly added.The
panelsmap andpanelsLockRWMutex follow the existing pattern used for other window resources likeeventListenersandmenuBindings.
1557-1571:NewPanelcorrectly handles panel lifecycle.The method properly:
- Creates the panel via
NewPanel(options)- Sets the parent reference
- Adds to the panels map under lock
- Starts the panel immediately if window is already running
v3/pkg/application/webview_panel.go (2)
318-336:Destroymethod is well-implemented.The method correctly:
- Checks if already destroyed (idempotent)
- Sets destroyed flag under lock
- Destroys the impl via
InvokeSync- Removes from parent
73-105:NewPanelfactory has sensible defaults.Good defensive defaults for Width (400), Height (300), ZIndex (1), Zoom (1.0), auto-generated Name, and Visible (true).
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this 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 implements the WebviewPanel feature, enabling multiple independent webview containers to be embedded within a single window. This addresses issue #1997 and provides functionality similar to Electron's BrowserView/WebContentsView.
Changes:
- Added core WebviewPanel API with platform-specific implementations for Windows, macOS, and Linux
- Integrated panel management into WebviewWindow with methods to create, retrieve, and remove panels
- Added TypeScript runtime API for controlling panels from the frontend
- Included a comprehensive example application demonstrating multi-panel layouts
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| webview_panel.go | Core panel struct and methods with lifecycle management |
| webview_panel_options.go | Panel configuration options including positioning and appearance |
| webview_panel_windows.go | Windows implementation using WebView2 in child windows |
| webview_panel_darwin.go | macOS implementation using WKWebView as subviews |
| webview_panel_linux.go | Linux implementation using WebKit2GTK with GtkFixed containers |
| webview_panel_ios.go, webview_panel_android.go | Stub implementations for mobile platforms |
| webview_window.go | Window integration with panel management methods |
| messageprocessor_panel.go | Backend message handling for frontend panel API calls |
| messageprocessor.go | Added panel request routing |
| panel.ts | TypeScript Panel class for frontend control |
| runtime.ts, index.ts | TypeScript runtime integration |
| examples/webview-panel/main.go | Comprehensive example demonstrating panel usage |
| examples/webview-panel/README.md | Example documentation with API overview |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Fix SonarCloud issues: - Add comments for empty stub functions on Android/iOS - Prefix unused parameters with underscore - Refactor processPanelMethod to use handler map (reduce cognitive complexity) - Use export...from syntax for Panel re-export in TypeScript - Fix CodeRabbit/Copilot review issues: - Add autorelease for WebviewPanelDelegate in Darwin implementation - Add thread-safe access to pendingJS and runtimeLoaded with runtimeLock mutex - Add synchronization to panel.run() method - Call destroyAllPanels() in WindowClosing event handler - Call chromium.ShuttingDown() before DestroyWindow on Windows - Fix CSS escaping to handle backslashes and newlines - Remove redundant mutex around atomic operation in getNextPanelID - Fix runPanels to not hold lock during panel.run() - Fix documentation: HTML takes precedence over URL when both are set
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🤖 Fix all issues with AI agents
In `@v3/pkg/application/webview_panel_darwin.go`:
- Around line 297-310: The code currently calls p.panel.markRuntimeLoaded()
immediately after starting URL navigation (when options.URL != ""), causing
runtime to be marked loaded before navigation finishes; move the
markRuntimeLoaded() call out of this init path and instead invoke
p.panel.markRuntimeLoaded() from the panelNavigationCompleted callback that runs
when navigation finishes (ensure panelNavigationCompleted handles both HTML and
URL cases or only call markRuntimeLoaded for URL loads), so any ExecJS queued
before page load will run after navigationCompleted; update or add logic in
panelNavigationCompleted to detect the relevant p.panel and call
markRuntimeLoaded() for URL-based loads.
- Around line 115-130: panelSetZIndex currently treats zIndex as binary
(above/below) which prevents granular stacking; change it to place the WKWebView
at a specific stack position based on the integer zIndex by inspecting and
manipulating the parent contentView's subviews. Locate panelSetZIndex
(WKWebView* webView, WebviewWindow* window, NSView* contentView) and replace the
binary removeFromSuperview/addSubview logic with code that: 1) clamp/normalize
the zIndex to a valid range (e.g., 0..(subviews.count)), 2) compute the target
sibling index (or target sibling view) corresponding to that zIndex, and 3)
insert the webView using addSubview:positioned:relativeTo: with the appropriate
relativeTo sibling (or reorder the subviews array) so multiple panels are
ordered according to their integer zIndex rather than only above/below.
- Around line 253-258: panelNavigationCompleted is a no-op and should locate the
panel by the exported ID and mark its runtime loaded like the Windows path does;
update the function to convert the C.uint ID to the Go panel ID type, look up
the panel via the shared registry/lookup (e.g., the same mechanism used
elsewhere to find panels), check the panel is non-nil, and call
p.panel.markRuntimeLoaded() (or the equivalent method on the panel object),
handling missing panels gracefully (log or ignore) and preserving any required
main-thread dispatch semantics for UI callbacks.
In `@v3/pkg/application/webview_panel_windows.go`:
- Around line 267-270: The variable clientPoint and its assignments (clientPoint
w32.POINT; clientPoint.X = 0; clientPoint.Y = 0) are unused dead code; remove
the clientPoint declaration and the two assignments and leave the existing call
that assigns parentClientX, parentClientY via w32.ClientToScreen(p.parent.hwnd,
0, 0) unchanged so only the needed variables remain.
♻️ Duplicate comments (2)
v3/pkg/application/webview_panel_options.go (1)
61-76: Frameless and Anchor fields are defined but not yet implemented.These fields are documented but the platform implementations don't use them. This is acceptable if they're planned for future implementation.
v3/pkg/application/webview_panel_linux.go (1)
186-190: Panel overlay positioning doesn't work as intended.The current implementation adds the GtkFixed container to the parent's vbox using
gtk_box_pack_start, which places panels in the vertical layout flow rather than as true overlays above the main webview. The code comments acknowledge this limitation ("For now, let's use the overlay approach with GtkFixed"), but the implementation doesn't achieve absolute positioning relative to the window.This means panels will be laid out sequentially in the vbox rather than floating at specific X,Y coordinates over the main content.
Consider refactoring to use
GtkOverlayas the root container in the window implementation, adding the main webview as the base child and panels as overlay children viagtk_overlay_add_overlay.
🧹 Nitpick comments (4)
v3/pkg/application/webview_panel_options.go (1)
82-100: AnchorType bit values start at 2 instead of 1.Due to
AnchorNone = 0consuming the first iota position,AnchorTop = 1 << iotaevaluates to1 << 1 = 2. The resulting values are:
AnchorTop= 2,AnchorBottom= 4,AnchorLeft= 8,AnchorRight= 16This works correctly for bitwise operations but is unconventional. If canonical bit flag values (1, 2, 4, 8) are preferred:
Alternative pattern for conventional bit values
const ( - // AnchorNone - panel uses absolute positioning only (default) - AnchorNone AnchorType = 0 - // AnchorTop - panel maintains distance from top edge AnchorTop AnchorType = 1 << iota // AnchorBottom - panel maintains distance from bottom edge AnchorBottom // AnchorLeft - panel maintains distance from left edge AnchorLeft // AnchorRight - panel maintains distance from right edge AnchorRight + // AnchorNone - panel uses absolute positioning only (default) + AnchorNone AnchorType = 0 + // AnchorFill - panel fills the entire window (anchored to all edges) AnchorFill AnchorType = AnchorTop | AnchorBottom | AnchorLeft | AnchorRight )v3/pkg/application/webview_panel.go (1)
432-477: Consider using typed constants for direction parameter.The
FillBesidemethod uses raw strings for thedirectionparameter. While functional, using typed constants would provide better compile-time safety and discoverability.💡 Suggested improvement
type DockDirection string const ( DockRight DockDirection = "right" DockLeft DockDirection = "left" DockBelow DockDirection = "below" DockAbove DockDirection = "above" ) func (p *WebviewPanel) FillBeside(refPanel *WebviewPanel, direction DockDirection) *WebviewPanel { // ... }v3/pkg/application/webview_panel_linux.go (1)
279-283: Z-index is a no-op on Linux.The comment correctly explains that GTK doesn't have a direct z-index concept. This is acceptable for now, but consider documenting this platform limitation in the public API documentation so users know that
SetZIndexhas no effect on Linux.v3/pkg/application/messageprocessor_panel.go (1)
89-105: Panel lookup requires name first, panelId is only a fallback.The current logic requires the
panelname argument to be non-empty (line 91-93). ThepanelIdis only used as a fallback when lookup by name fails (lines 97-101). This means if a frontend sends onlypanelIdwithout apanelname, it will error with "panel name is required".Consider whether this is intentional. If
panelIdshould be an alternative toname, the logic should allow either:💡 Allow lookup by ID when name is empty
// Get panel name from args panelName := args.String("panel") - if panelName == nil || *panelName == "" { - return nil, errs.NewInvalidRuntimeCallErrorf("panel name is required") - } // Get the panel by name or ID - panel := ww.GetPanel(*panelName) - if panel == nil { + var panel *WebviewPanel + if panelName != nil && *panelName != "" { + panel = ww.GetPanel(*panelName) + } + if panel == nil { panelID := args.UInt("panelId") if panelID != nil && *panelID > 0 { panel = ww.GetPanelByID(uint(*panelID)) } } if panel == nil { - return nil, errs.NewInvalidRuntimeCallErrorf("panel not found: %s", *panelName) + return nil, errs.NewInvalidRuntimeCallErrorf("panel not found") }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
v3/internal/runtime/desktop/@wailsio/runtime/src/index.tsv3/pkg/application/messageprocessor_panel.gov3/pkg/application/webview_panel.gov3/pkg/application/webview_panel_android.gov3/pkg/application/webview_panel_darwin.gov3/pkg/application/webview_panel_ios.gov3/pkg/application/webview_panel_linux.gov3/pkg/application/webview_panel_options.gov3/pkg/application/webview_panel_windows.gov3/pkg/application/webview_window.go
🚧 Files skipped from review as they are similar to previous changes (1)
- v3/internal/runtime/desktop/@wailsio/runtime/src/index.ts
🧰 Additional context used
🧠 Learnings (15)
📓 Common learnings
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).
Learnt from: nixpare
Repo: wailsapp/wails PR: 3763
File: v3/pkg/application/webview_panel_darwin.go:88-89
Timestamp: 2024-09-21T09:56:48.126Z
Learning: Safety checks for `p.nsPanel` are performed in the `SetFloating` method of `WebviewPanel`, following the `WebviewWindow` and `macosWebviewWindow` implementations and code style.
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.
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.
Learnt from: popaprozac
Repo: wailsapp/wails PR: 4839
File: docs/src/content/docs/reference/window.mdx:616-620
Timestamp: 2025-12-29T08:02:15.134Z
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.
📚 Learning: 2026-01-04T08:01:00.038Z
Learnt from: symball
Repo: wailsapp/wails PR: 4853
File: v2/internal/system/system.go:128-152
Timestamp: 2026-01-04T08:01:00.038Z
Learning: In v2/internal/system/system.go, shared functions like checkLibrary are defined without build tags but are only invoked from platform-specific files (system_linux.go, system_windows.go, system_darwin.go) that have build constraints. Reviewers should ensure there are no runtime OS checks in system.go and that platform-specific behavior is controlled via build tags. If runtime switches exist, remove them in favor of compile-time platform constraints to reduce overhead and improve correctness.
Applied to files:
v3/pkg/application/messageprocessor_panel.gov3/pkg/application/webview_panel_android.gov3/pkg/application/webview_panel_options.gov3/pkg/application/webview_panel_ios.gov3/pkg/application/webview_panel_windows.gov3/pkg/application/webview_panel_darwin.gov3/pkg/application/webview_window.gov3/pkg/application/webview_panel_linux.gov3/pkg/application/webview_panel.go
📚 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_panel_android.gov3/pkg/application/webview_panel_ios.gov3/pkg/application/webview_panel_windows.gov3/pkg/application/webview_panel_darwin.gov3/pkg/application/webview_window.gov3/pkg/application/webview_panel_linux.gov3/pkg/application/webview_panel.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_panel_options.gov3/pkg/application/webview_panel_windows.gov3/pkg/application/webview_panel_darwin.gov3/pkg/application/webview_window.gov3/pkg/application/webview_panel.go
📚 Learning: 2025-12-29T08:02:15.134Z
Learnt from: popaprozac
Repo: wailsapp/wails PR: 4839
File: docs/src/content/docs/reference/window.mdx:616-620
Timestamp: 2025-12-29T08:02:15.134Z
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_panel_options.gov3/pkg/application/webview_panel_ios.gov3/pkg/application/webview_panel_windows.gov3/pkg/application/webview_panel_darwin.gov3/pkg/application/webview_window.gov3/pkg/application/webview_panel_linux.gov3/pkg/application/webview_panel.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:
v3/pkg/application/webview_panel_options.go
📚 Learning: 2024-09-21T09:56:48.126Z
Learnt from: nixpare
Repo: wailsapp/wails PR: 3763
File: v3/pkg/application/webview_panel_darwin.go:88-89
Timestamp: 2024-09-21T09:56:48.126Z
Learning: Safety checks for `p.nsPanel` are performed in the `SetFloating` method of `WebviewPanel`, following the `WebviewWindow` and `macosWebviewWindow` implementations and code style.
Applied to files:
v3/pkg/application/webview_panel_options.gov3/pkg/application/webview_panel_windows.gov3/pkg/application/webview_panel_darwin.gov3/pkg/application/webview_window.gov3/pkg/application/webview_panel_linux.gov3/pkg/application/webview_panel.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_panel_windows.gov3/pkg/application/webview_panel_darwin.gov3/pkg/application/webview_panel_linux.go
📚 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/pkg/application/webview_panel_windows.gov3/pkg/application/webview_panel_darwin.gov3/pkg/application/webview_panel_linux.go
📚 Learning: 2025-08-08T13:15:37.785Z
Learnt from: APshenkin
Repo: wailsapp/wails PR: 4484
File: v2/internal/frontend/utils/urlValidator_test.go:152-155
Timestamp: 2025-08-08T13:15:37.785Z
Learning: In v2/internal/frontend/utils/urlValidator_test.go, the project accepts a literal '|' in the URL path (e.g., "https://example.com/user/123|admin") as valid for BrowserOpenURL. Maintainer (APshenkin) clarified that browsers will escape it automatically; thus, tests should not require percent-encoding '%7C' nor expect an error from utils.ValidateURL for this case. Avoid suggesting rejection or forced encoding of '|' in path in future reviews.
Applied to files:
v3/pkg/application/webview_panel_windows.go
📚 Learning: 2025-02-24T06:08:55.645Z
Learnt from: popaprozac
Repo: wailsapp/wails PR: 4098
File: v3/pkg/services/notifications/notifications_windows.go:91-113
Timestamp: 2025-02-24T06:08:55.645Z
Learning: The JSON quote handling in Windows notifications' activation arguments (v3/pkg/services/notifications/notifications_windows.go) has a known limitation with single-quote collisions that needs to be addressed after initial testing.
Applied to files:
v3/pkg/application/webview_panel_windows.go
📚 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:
v3/pkg/application/webview_panel_windows.gov3/pkg/application/webview_window.go
📚 Learning: 2025-01-25T04:10:54.008Z
Learnt from: leaanthony
Repo: wailsapp/wails PR: 4031
File: v3/pkg/application/menuitem_darwin.go:299-302
Timestamp: 2025-01-25T04:10:54.008Z
Learning: Memory release operations in Objective-C/Cocoa should be performed synchronously, not asynchronously via dispatch_async, as they don't require main thread coordination and should complete immediately to prevent memory leaks.
Applied to files:
v3/pkg/application/webview_panel_darwin.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/pkg/application/webview_window.go
📚 Learning: 2024-10-08T22:11:37.054Z
Learnt from: leaanthony
Repo: wailsapp/wails PR: 3763
File: v3/internal/commands/appimage_testfiles/main.go:295-299
Timestamp: 2024-10-08T22:11:37.054Z
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/pkg/application/webview_window.gov3/pkg/application/webview_panel.go
🧬 Code graph analysis (7)
v3/pkg/application/webview_panel_android.go (1)
v3/pkg/application/webview_panel.go (1)
WebviewPanel(54-69)
v3/pkg/application/webview_panel_options.go (2)
v3/internal/runtime/desktop/@wailsio/runtime/src/panel.ts (1)
Name(277-281)v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts (2)
Width(597-599)Height(309-311)
v3/pkg/application/webview_panel_ios.go (1)
v3/pkg/application/webview_panel.go (1)
WebviewPanel(54-69)
v3/pkg/application/webview_panel_windows.go (6)
v3/pkg/w32/typedef.go (2)
HWND(218-218)POINT(231-233)v3/pkg/w32/constants.go (9)
WS_CHILD(343-343)WS_CLIPSIBLINGS(347-347)WS_VISIBLE(345-345)SWP_NOZORDER(2218-2218)SWP_NOACTIVATE(2220-2220)HWND_TOP(2238-2238)HWND_BOTTOM(2236-2236)SWP_NOMOVE(2217-2217)SWP_NOSIZE(2216-2216)v3/pkg/application/screenmanager.go (2)
DipToPhysicalRect(853-855)PhysicalToDipRect(857-859)v3/pkg/w32/window.go (1)
MustStringToUTF16Ptr(173-180)v3/pkg/w32/kernel32.go (1)
GetModuleHandle(52-61)v3/internal/assetserver/assetserver.go (1)
GetStartURL(149-175)
v3/pkg/application/webview_panel_darwin.go (2)
v3/pkg/application/webview_panel.go (1)
WebviewPanel(54-69)v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts (2)
Width(597-599)Height(309-311)
v3/pkg/application/webview_panel_linux.go (1)
v3/pkg/application/webview_panel.go (1)
WebviewPanel(54-69)
v3/pkg/application/webview_panel.go (4)
v3/pkg/application/webview_panel_options.go (1)
WebviewPanelOptions(5-76)v3/pkg/application/webview_window.go (1)
WebviewWindow(142-180)v3/internal/runtime/desktop/@wailsio/runtime/src/panel.ts (7)
Name(277-281)SetBounds(95-103)Bounds(36-45)SetZIndex(121-126)SetURL(133-138)SetHTML(145-150)ExecJS(157-162)v3/pkg/application/mainthread.go (2)
InvokeSync(23-32)InvokeSyncWithResult(34-44)
🔇 Additional comments (24)
v3/pkg/application/webview_panel_android.go (1)
1-87: LGTM!The Android stub implementation is well-structured with appropriate build tags, sensible default return values, and clear documentation indicating the platform is not yet supported.
v3/pkg/application/webview_panel_windows.go (3)
403-408: Verify focus detection accuracy for WebView2.The
isFocused()method checks if the container window (p.hwnd) has focus, but WebView2 manages its own focus internally. When the WebView2 content has keyboard focus,GetFocus()may return the WebView2's internal HWND rather than the container, causing this method to returnfalseincorrectly.Consider checking the chromium controller's focus state for more accurate detection, or verify this behavior works as expected in practice.
162-182: CSS escaping has been improved.The CSS escaping now properly handles backslashes, double quotes, newlines, and carriage returns, addressing the previous review concerns.
214-223: Chromium cleanup sequence is now correct.The
destroy()method properly callsp.chromium.ShuttingDown()before destroying the window, consistent with the cleanup pattern in the main window implementation.v3/pkg/application/webview_panel_darwin.go (1)
45-77: Memory management for Cocoa objects is now correct.The
autoreleasecalls forconfig,userContentController, anddelegateproperly balance the reference counts and prevent memory leaks. This addresses the previous review concerns.v3/pkg/application/webview_panel_ios.go (1)
1-87: LGTM!The iOS stub implementation follows the same clean pattern as the Android stub, with appropriate build tags, sensible defaults, and clear documentation indicating the platform is not yet supported.
v3/pkg/application/webview_panel.go (6)
44-48: LGTM - Simplified ID generation.The previous redundant mutex around atomic operation has been correctly removed. Using only
atomic.AddUint32is sufficient and idiomatic for generating unique IDs.
54-103: LGTM - Well-structured panel definition and constructor.The
WebviewPanelstruct properly separates concerns with dedicated locks for destruction state and runtime state. The constructor applies sensible defaults and follows the pattern established byWebviewWindow.
219-235: LGTM - Race condition addressed.The
ExecJSmethod now properly usesruntimeLockto synchronize access toruntimeLoadedandpendingJS. The lock is correctly released before callingInvokeSyncto avoid potential deadlocks.
361-375: LGTM - Proper synchronization for runtime loading.The
markRuntimeLoadedmethod correctly snapshotspendingJSunder lock and iterates outside the lock. This prevents race conditions withExecJSand avoids deadlock sinceInvokeSyncis called after releasing the lock.
347-359: LGTM - Synchronized panel initialization.The
run()method now usesdestroyedLockto protect against concurrent initialization, addressing the previous race condition concern. The pattern correctly releases the lock before callingInvokeSync.
320-338: Minor TOCTOU window, but follows existing patterns.There's a small window between
isDestroyed()check and acquiring the write lock where another goroutine could setdestroyed=true. However, this is benign since double-destruction is idempotent (impl.destroy() should handle this gracefully, and settingdestroyed=truetwice is harmless). This follows the same pattern asWebviewWindow.v3/pkg/application/webview_panel_linux.go (3)
249-255: LGTM - Proper cleanup.Destroying the GtkFixed container will also destroy its child webview. Setting both pointers to nil prevents use-after-free.
312-324: LGTM - Consistent null checks.All methods properly check for nil pointers before CGO calls, preventing crashes on destroyed panels.
238-239: This concern is based on a misunderstanding of the intentional design difference between WebviewWindow and WebviewPanel. WebviewPanels do not inject the Wails runtime by default (as noted in the Darwin implementation), so they do not wait for the "wails:runtime:ready" message. The immediatemarkRuntimeLoaded()call at the end ofcreate()is intentional and consistent across all platforms (Linux, Darwin, Windows). WebviewPanel still maintains the samependingJSqueue mechanism for thread-safe handling of any JavaScript sent before the panel is fully initialized.Likely an incorrect or invalid review comment.
v3/pkg/application/messageprocessor_panel.go (2)
10-29: LGTM - Clean constant definitions.The method constants are well-organized with sequential values. Using constants instead of strings is efficient for the message processor.
115-231: LGTM - Consistent handler implementations.All handlers follow a clean pattern: validate required arguments, call the appropriate panel method, and return
unitfor void operations or the actual value for getters. Error messages are descriptive.v3/pkg/application/webview_window.go (7)
176-179: LGTM - Proper initialization of panel management.The
panelsmap andpanelsLockare correctly added to the struct and initialized inNewWindow. This follows the same pattern as other map fields inWebviewWindow.Also applies to: 296-296
301-308: LGTM - Panel cleanup on window close.The
destroyAllPanelsis now called during window closing, addressing the previous concern about resource leaks. The order (destroy panels → mark destroyed → close impl → remove) is correct.
1637-1651: LGTM - Race condition addressed.The
runPanelsmethod now correctly snapshots panels under read lock and releases the lock before callingpanel.run(). This prevents deadlocks sincepanel.run()internally callsInvokeSync.
1653-1666: LGTM - Proper panel cleanup implementation.The method correctly snapshots panels under lock and destroys them outside the lock. This prevents deadlocks since
panel.Destroy()will callremovePanel()which needs to acquire the write lock.
1558-1572: LGTM - Well-implemented panel creation.The method handles both cases: adding panels before the window runs (they'll be started by
runPanels()) and after (started immediately). The lock usage is correct.
1574-1604: LGTM - Thread-safe panel lookups.All lookup methods properly use read locks.
GetPaneliterates to find by name (O(n) but acceptable for typical panel counts), whileGetPanelByIDuses direct map lookup.GetPanelsreturns a defensive copy to prevent external modification.
1606-1633: LGTM - Clean removal implementation.The public
RemovePanelandRemovePanelByIDmethods correctly delegate topanel.Destroy(), which handles cleanup and calls the internalremovePanel()to remove from the map. This avoids duplicate cleanup logic.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| // Set panel z-index (bring to front or send to back) | ||
| void panelSetZIndex(void* panel, void* parentWindow, int zIndex) { | ||
| WKWebView* webView = (WKWebView*)panel; | ||
| WebviewWindow* window = (WebviewWindow*)parentWindow; | ||
| NSView* contentView = [window contentView]; | ||
| if (zIndex > 0) { | ||
| // Bring to front | ||
| [webView removeFromSuperview]; | ||
| [contentView addSubview:webView positioned:NSWindowAbove relativeTo:nil]; | ||
| } else { | ||
| // Send to back (but above main webview which is at index 0) | ||
| [webView removeFromSuperview]; | ||
| [contentView addSubview:webView positioned:NSWindowBelow relativeTo:nil]; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Z-index implementation uses same binary approach as Windows.
The panelSetZIndex function treats z-index as binary (above vs. below), which has the same limitation noted in the Windows implementation regarding granular stacking of multiple panels.
🤖 Prompt for AI Agents
In `@v3/pkg/application/webview_panel_darwin.go` around lines 115 - 130,
panelSetZIndex currently treats zIndex as binary (above/below) which prevents
granular stacking; change it to place the WKWebView at a specific stack position
based on the integer zIndex by inspecting and manipulating the parent
contentView's subviews. Locate panelSetZIndex (WKWebView* webView,
WebviewWindow* window, NSView* contentView) and replace the binary
removeFromSuperview/addSubview logic with code that: 1) clamp/normalize the
zIndex to a valid range (e.g., 0..(subviews.count)), 2) compute the target
sibling index (or target sibling view) corresponding to that zIndex, and 3)
insert the webView using addSubview:positioned:relativeTo: with the appropriate
relativeTo sibling (or reorder the subviews array) so multiple panels are
ordered according to their integer zIndex rather than only above/below.
| //export panelNavigationCompleted | ||
| func panelNavigationCompleted(_ C.uint) { | ||
| // Find the panel and mark runtime as loaded | ||
| // This is called from Objective-C when navigation completes | ||
| // For now, we'll handle this through the panel's own tracking | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Navigation completion callback is a no-op stub.
The panelNavigationCompleted export receives the panel ID from Objective-C but doesn't actually locate the panel or update its state. The comment says "For now, we'll handle this through the panel's own tracking," but this means the navigation completion event from webView:didFinishNavigation: is effectively discarded.
This is inconsistent with the Windows implementation where navigationCompletedCallback calls p.panel.markRuntimeLoaded().
Suggested implementation
//export panelNavigationCompleted
-func panelNavigationCompleted(_ C.uint) {
- // Find the panel and mark runtime as loaded
- // This is called from Objective-C when navigation completes
- // For now, we'll handle this through the panel's own tracking
+func panelNavigationCompleted(panelId C.uint) {
+ // Find the panel by ID and mark runtime as loaded
+ // This requires access to a panel registry or the parent window's panel map
+ // TODO: Implement panel lookup and call panel.markRuntimeLoaded()
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| //export panelNavigationCompleted | |
| func panelNavigationCompleted(_ C.uint) { | |
| // Find the panel and mark runtime as loaded | |
| // This is called from Objective-C when navigation completes | |
| // For now, we'll handle this through the panel's own tracking | |
| } | |
| //export panelNavigationCompleted | |
| func panelNavigationCompleted(panelId C.uint) { | |
| // Find the panel by ID and mark runtime as loaded | |
| // This requires access to a panel registry or the parent window's panel map | |
| // TODO: Implement panel lookup and call panel.markRuntimeLoaded() | |
| } |
🤖 Prompt for AI Agents
In `@v3/pkg/application/webview_panel_darwin.go` around lines 253 - 258,
panelNavigationCompleted is a no-op and should locate the panel by the exported
ID and mark its runtime loaded like the Windows path does; update the function
to convert the C.uint ID to the Go panel ID type, look up the panel via the
shared registry/lookup (e.g., the same mechanism used elsewhere to find panels),
check the panel is non-nil, and call p.panel.markRuntimeLoaded() (or the
equivalent method on the panel object), handling missing panels gracefully (log
or ignore) and preserving any required main-thread dispatch semantics for UI
callbacks.
| // Load initial content | ||
| if options.HTML != "" { | ||
| html := C.CString(options.HTML) | ||
| C.panelLoadHTML(p.webview, html) | ||
| C.free(unsafe.Pointer(html)) | ||
| } else if options.URL != "" { | ||
| url := C.CString(options.URL) | ||
| C.panelLoadURL(p.webview, url) | ||
| C.free(unsafe.Pointer(url)) | ||
| } | ||
|
|
||
| // Mark runtime as loaded (panels don't inject the wails runtime by default) | ||
| p.panel.markRuntimeLoaded() | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Runtime marked as loaded before URL navigation completes.
For URL-based content, markRuntimeLoaded() is called immediately after initiating navigation (line 309), not after navigation completes. This differs from the Windows implementation which waits for navigationCompletedCallback.
Any JavaScript queued via ExecJS before the page finishes loading may fail or execute on an incomplete page. Consider moving markRuntimeLoaded() to the panelNavigationCompleted callback for URL-based loading.
🤖 Prompt for AI Agents
In `@v3/pkg/application/webview_panel_darwin.go` around lines 297 - 310, The code
currently calls p.panel.markRuntimeLoaded() immediately after starting URL
navigation (when options.URL != ""), causing runtime to be marked loaded before
navigation finishes; move the markRuntimeLoaded() call out of this init path and
instead invoke p.panel.markRuntimeLoaded() from the panelNavigationCompleted
callback that runs when navigation finishes (ensure panelNavigationCompleted
handles both HTML and URL cases or only call markRuntimeLoaded for URL loads),
so any ExecJS queued before page load will run after navigationCompleted; update
or add logic in panelNavigationCompleted to detect the relevant p.panel and call
markRuntimeLoaded() for URL-based loads.
| var clientPoint w32.POINT | ||
| clientPoint.X = 0 | ||
| clientPoint.Y = 0 | ||
| parentClientX, parentClientY := w32.ClientToScreen(p.parent.hwnd, 0, 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unused variable declaration.
The clientPoint variable is declared and initialized but never used. This is dead code.
Suggested fix
// Calculate position relative to parent's client area
- var clientPoint w32.POINT
- clientPoint.X = 0
- clientPoint.Y = 0
parentClientX, parentClientY := w32.ClientToScreen(p.parent.hwnd, 0, 0)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| var clientPoint w32.POINT | |
| clientPoint.X = 0 | |
| clientPoint.Y = 0 | |
| parentClientX, parentClientY := w32.ClientToScreen(p.parent.hwnd, 0, 0) | |
| // Calculate position relative to parent's client area | |
| parentClientX, parentClientY := w32.ClientToScreen(p.parent.hwnd, 0, 0) |
🤖 Prompt for AI Agents
In `@v3/pkg/application/webview_panel_windows.go` around lines 267 - 270, The
variable clientPoint and its assignments (clientPoint w32.POINT; clientPoint.X =
0; clientPoint.Y = 0) are unused dead code; remove the clientPoint declaration
and the two assignments and leave the existing call that assigns parentClientX,
parentClientY via w32.ClientToScreen(p.parent.hwnd, 0, 0) unchanged so only the
needed variables remain.
| func (p *windowsPanelImpl) setZIndex(zIndex int) { | ||
| if p.hwnd == 0 { | ||
| return | ||
| } | ||
|
|
||
| // Use SetWindowPos to change z-order | ||
| // Higher zIndex = on top | ||
| var insertAfter uintptr | ||
| if zIndex > 0 { | ||
| insertAfter = w32.HWND_TOP | ||
| } else { | ||
| insertAfter = w32.HWND_BOTTOM | ||
| } | ||
|
|
||
| w32.SetWindowPos( | ||
| p.hwnd, | ||
| insertAfter, | ||
| 0, 0, 0, 0, | ||
| w32.SWP_NOMOVE|w32.SWP_NOSIZE|w32.SWP_NOACTIVATE, | ||
| ) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Z-index implementation doesn't support granular stacking.
The current implementation treats z-index as binary (top vs. bottom), but the WebviewPanelOptions.ZIndex documentation states "Higher values appear on top of lower values." With this implementation, multiple panels with z-index values 1, 2, and 3 would all be placed at HWND_TOP without relative ordering between them.
Consider iterating sibling panels and using SetWindowPos with a reference HWND to achieve proper stacking order, or document this limitation.



Pull Request: WebviewPanel - Multiple Webviews in a Single Window
Summary
This PR implements the WebviewPanel feature, allowing multiple independent webview containers to be embedded within a single window. This addresses #1997 and provides functionality similar to Electron's BrowserView/WebContentsView.
Motivation
Many desktop applications require displaying multiple web views in a single window:
Previously, this was only achievable using iframes, which have significant limitations:
WebviewPanel solves these issues by providing true native webview embedding.
Design Decisions
API Design Philosophy
The API follows the design proposed in #1997 by @SamHennessy:
Why This Design?
Rather than complex flex/grid layouts, we chose simple absolute positioning because:
Naming: "Panel" vs "Container" vs "Webview"
We chose "WebviewPanel" because:
WebviewWindow(top-level window)Implementation
Core Files
webview_panel.gowebview_panel_options.gowebview_panel_windows.gowebview_panel_darwin.gowebview_panel_linux.gomessageprocessor_panel.gopanel.tsPlatform Implementations
Windows: Creates a child HWND window with WebView2/Chromium embedded
macOS: Adds WKWebView as a subview of the window's content view
Linux: Uses GtkFixed container with WebKitWebView child widgets
API Overview
Testing
Breaking Changes
None. This is a new feature that doesn't affect existing APIs.
Checklist
examples/webview-panel/)Related Issues
Future Enhancements
Potential improvements for future PRs:
Summary by CodeRabbit
New Features
Platform Support
Documentation & Examples
✏️ Tip: You can customize this high-level summary in your review settings.