Skip to content

feat: Complete App API restructuring with organized manager pattern #4359

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

Open
wants to merge 4 commits into
base: v3-alpha
Choose a base branch
from

Conversation

leaanthony
Copy link
Member

@leaanthony leaanthony commented Jun 15, 2025

🚀 Major API Improvement: Complete App Restructuring

This PR completes the comprehensive restructuring of the Wails v3 App API, transforming it from a monolithic interface into a clean, organized manager pattern that dramatically improves developer experience and maintainability.

✨ High-Level Benefits

🎯 Improved Developer Experience

  • Enhanced Discoverability: Developers intuitively know where to find functionality (e.g., app.Windows.* for window operations, app.Events.* for events)
  • Better IDE Support: Grouped functionality provides cleaner autocomplete and better code navigation
  • Reduced Cognitive Load: No more guessing between duplicate methods or hunting through a massive App interface

🏗️ Better Architecture

  • Separation of Concerns: Each manager handles a specific domain (Windows, Events, Dialogs, etc.)
  • Consistent API Patterns: Unified naming conventions and return patterns across all managers
  • Elimination of Duplication: Removed confusing duplicate methods and circular dependencies

🔧 Enhanced Maintainability

  • Modular Design: Easy to extend and modify individual managers without affecting others
  • Private API Surface: Internal implementation details properly encapsulated
  • Clean Interfaces: Focused, purpose-built APIs for each functional area

🔄 Migration Guide

This is a breaking change that requires updating your application code. However, the migration is straightforward and provides immediate benefits.

Window Management

// ❌ Before (v3-alpha)
window := app.NewWebviewWindow()
window := app.NewWebviewWindowWithOptions(options)
current := app.CurrentWindow()

// ✅ After (this PR)
window := app.Windows.New()
window := app.Windows.NewWithOptions(options) 
current := app.Windows.Current()

Event Management

// ❌ Before (v3-alpha)
app.OnApplicationEvent(eventType, callback)
app.RegisterApplicationEventHook(eventType, callback)

// ✅ After (this PR)
app.Events.OnApplicationEvent(eventType, callback)
app.Events.RegisterApplicationEventHook(eventType, callback)

System Tray Management

// ❌ Before (v3-alpha)
tray := app.NewSystemTray()

// ✅ After (this PR)
tray := app.SystemTray.New()

Application Menu Management

// ❌ Before (v3-alpha)
app.ApplicationMenu = menu  // Direct field access

// ✅ After (this PR)
app.Menus.SetApplicationMenu(menu)
menu := app.Menus.GetApplicationMenu()

Quick Migration Script

For most cases, you can use these find-and-replace patterns:

# Window methods
sed -i 's/app\.NewWebviewWindow()/app.Windows.New()/g' *.go
sed -i 's/app\.NewWebviewWindowWithOptions(/app.Windows.NewWithOptions(/g' *.go
sed -i 's/app\.CurrentWindow()/app.Windows.Current()/g' *.go

# Event methods  
sed -i 's/app\.OnApplicationEvent(/app.Events.OnApplicationEvent(/g' *.go
sed -i 's/app\.RegisterApplicationEventHook(/app.Events.RegisterApplicationEventHook(/g' *.go

# System tray
sed -i 's/app\.NewSystemTray()/app.SystemTray.New()/g' *.go

# Global application usage
sed -i 's/application\.Get()\.CurrentWindow()/application.Get().Windows.Current()/g' *.go
sed -i 's/application\.Get()\.OnApplicationEvent(/application.Get().Events.OnApplicationEvent(/g' *.go

📋 Detailed Changes

Manager Organization

The App struct now provides access to 11 focused managers:

  • Windows - Window creation, management, and current window access
  • Events - Custom events and application event handling
  • Menus - Application menu management and menu creation
  • Dialogs - File dialogs and message dialogs
  • ContextMenus - Context menu registration and management
  • KeyBindings - Keyboard shortcut registration and handling
  • SystemTray - System tray creation and management
  • Screens - Multi-monitor support and screen information
  • Browser - Browser-related functionality and controls
  • Env - Environment information and system integration
  • Clipboard - Clipboard text operations

API Consistency Improvements

Unified Naming Conventions

  • Get() - Retrieve single items by name/identifier
  • GetAll() - Retrieve collections of items
  • GetByID() - Retrieve items by ID
  • Add() / Remove() - Manage collections
  • New() / NewWithOptions() - Create new instances

Consistent Return Patterns

  • Methods that may fail return (item, bool) tuples
  • Methods with guaranteed results return single values
  • Error-prone operations properly handle edge cases

Enhanced Manager APIs

  • WindowManager: Added GetByID(), improved Current() method
  • DialogManager: Added Show* prefixes for all dialog methods for clarity
  • KeyBindingManager: Structured KeyBinding objects instead of raw maps
  • ContextMenuManager: Collection-based management with GetAll()
  • EventManager: Complete event lifecycle management

Removed Deprecated Methods

  • App.NewWebviewWindow()App.Windows.New()
  • App.NewWebviewWindowWithOptions()App.Windows.NewWithOptions()
  • App.CurrentWindow()App.Windows.Current()
  • App.OnApplicationEvent()App.Events.OnApplicationEvent()
  • App.RegisterApplicationEventHook()App.Events.RegisterApplicationEventHook()
  • App.NewSystemTray()App.SystemTray.New()
  • WindowManager.GetCurrent()WindowManager.Current() (removed duplicate)

Private Implementation Details

  • Made ApplicationMenu field private with proper manager access
  • Made all manager constructors private (New*Manager()new*Manager())
  • Encapsulated internal window/event/tray management logic

Updated Examples and Tests

  • ✅ All 60+ example applications updated and verified
  • ✅ Internal test files updated to new API
  • ✅ Documentation updated to reflect new patterns
  • ✅ Compilation verified across multiple platforms

🧪 Testing

This PR maintains 100% functional compatibility while improving the developer interface:

  • No Functional Changes: All existing functionality preserved
  • Verified Examples: Badge, window, systray, events, dialogs, contextmenus examples tested
  • Cross-Platform: Changes tested on Darwin platform, designed for cross-platform compatibility
  • Backwards Compatibility: While the API changes, all functionality remains available

🔍 Review Focus Areas

  1. Migration Guide Completeness: Ensure all breaking changes are documented
  2. API Consistency: Verify naming patterns are consistent across managers
  3. Documentation Updates: Check that all documentation reflects new patterns
  4. Example Coverage: Confirm examples demonstrate best practices

🎉 Future Benefits

This restructuring provides a solid foundation for:

  • Enhanced Manager APIs: Easy to add features like image clipboard support, window management utilities, etc.
  • Plugin Architecture: Clean interfaces for potential plugin system
  • Testing Infrastructure: Managers can be easily mocked and tested independently
  • Developer Tooling: Better IDE integration and development experience

This change represents a significant step forward in Wails v3's API maturity, providing a clean, discoverable, and maintainable interface that will serve developers well as the framework continues to evolve.

Summary by CodeRabbit

  • New Features

    • Introduced dedicated manager objects for windows, menus, dialogs, events, key bindings, context menus, clipboard, system tray, browser, and environment operations.
    • Added new documentation pages covering Manager API, browser integration, environment info, key bindings, screen management, and window management.
  • Refactor

    • Restructured application API to access features via manager properties, enhancing code organization and discoverability.
    • Updated all example projects and documentation to adopt the new Manager API patterns.
  • Bug Fixes

    • Enhanced robustness and safety in window retrieval and event handling to prevent potential errors.
  • Documentation

    • Revised all guides and examples to reflect updated API usage with managers.
    • Added comprehensive usage instructions, best practices, and platform-specific considerations for new managers.
  • Chores

    • Updated example project dependencies for improved stability and compatibility.

@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Jun 15, 2025
Copy link
Contributor

coderabbitai bot commented Jun 15, 2025

Walkthrough

This update refactors the core application API by introducing dedicated manager structs for handling windows, events, menus, dialogs, environment, clipboard, key bindings, context menus, browser, screens, and system tray functionalities. Direct methods and fields on the App struct are removed in favor of these managers, with all usage in documentation and examples updated to reflect the new API structure.

Changes

Files/Groups Change Summary
v3/pkg/application/application.go Major refactor: Removes direct event, window, menu, context menu, key binding, clipboard, environment, and system tray handling from App. Introduces manager fields (e.g., Windows, Events, Menus, etc.) and delegates all related functionality to these managers. Removes many exported methods and fields from App.
v3/pkg/application/browser_manager.go, clipboard_manager.go, context_menu_manager.go, dialog_manager.go, environment_manager.go, event_manager.go, key_binding_manager.go Adds new manager types: BrowserManager, ClipboardManager, ContextMenuManager, DialogManager, EnvironmentManager, EventManager, KeyBindingManager. Each encapsulates a domain of application functionality with methods for creation, access, and management.
v3/pkg/application/application_darwin.go, application_linux.go, application_windows.go Updates platform-specific code to use new manager fields (e.g., Events, Env, Windows, Menus) instead of direct method calls or fields on App. Fixes field casing and access as needed.
v3/pkg/application/dialogs_linux.go, linux_cgo.go, events_common_darwin.go Replaces calls to removed methods/fields (e.g., getWindowForID, IsDarkMode, event registration) with new manager methods (e.g., Windows.GetByID, Env.IsDarkMode, Events.OnApplicationEvent).
docs/src/content/docs/guides/menus.mdx, learn/application-menu.mdx, learn/clipboard.mdx, learn/context-menu.mdx, learn/dialogs.mdx, learn/environment.mdx, learn/events.mdx, learn/keybindings.mdx, learn/manager-api.mdx, learn/notifications.mdx, learn/screens.mdx, learn/systray.mdx, learn/windows.mdx Updates all documentation and code examples to use the new manager-based API (e.g., app.Menus.New(), app.Windows.Current(), app.Events.On(), app.ContextMenus.Add(), etc.) instead of previous direct method calls on app. Adds new docs for Browser, Environment, Key Bindings, Screens, Windows, and Manager API.
v3/examples/*/main.go, v3/examples/badge/frontend/bindings/* Updates all example code to use the new manager-based API for windows, events, menus, context menus, system tray, clipboard, and screens. TypeScript bindings for the badge service are updated with new exports and function signatures.
v3/examples/dev/go.mod Updates and cleans up indirect dependencies; no functional code changes.

Sequence Diagram(s)

sequenceDiagram
    participant App
    participant Windows
    participant Events
    participant Menus
    participant Dialogs
    participant Clipboard
    participant KeyBindings
    participant ContextMenus
    participant Browser
    participant Env
    participant Screens
    participant SystemTray

    App->>Windows: Create/Retrieve/Manage windows
    App->>Events: Emit/Subscribe to custom/app events
    App->>Menus: Create/Set application menus
    App->>Dialogs: Show dialogs (info, question, etc.)
    App->>Clipboard: Set/Get clipboard text
    App->>KeyBindings: Register/Remove key bindings
    App->>ContextMenus: Create/Add context menus
    App->>Browser: Open URL or file in browser
    App->>Env: Query environment info, dark mode, open file manager
    App->>Screens: Get screen info
    App->>SystemTray: Create/manage system tray icons
Loading

Possibly related PRs

  • wailsapp/wails#3731: Refactors event handling APIs to align with previous version's style, directly related as both PRs modify the event handling architecture and APIs.
  • wailsapp/wails#4031: Adds new methods to the Menu struct for menu item management, related to this PR's changes to menu management via a MenuManager.
  • wailsapp/wails#4013: Introduces a new ContextMenu API and example, aligning with this PR's manager-based approach for context menu handling.

Poem

In the warren of code, where managers dwell,
Each task has a home, each feature a shell.
No more direct calls—let the managers steer,
For windows and menus, the pathway is clear!
With events and dialogs, clipboard and tray,
The rabbit hops forward—modular all the way!
🐇✨


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f16b748 and 609a331.

📒 Files selected for processing (1)
  • .gitignore (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .gitignore
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (macos-latest, 1.24)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep/ci
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
  • GitHub Check: Cloudflare Pages

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@dosubot dosubot bot added Enhancement New feature or request Implemented in v3 labels Jun 15, 2025
@@ -65,7 +66,7 @@
m.Error("Window ID not parsable:", "id", windowID, "error", err)
return nil, windowID
}
targetWindow := globalApplication.getWindowForID(uint(wID))
targetWindow, _ := globalApplication.Windows.GetByID(uint(wID))

Check failure

Code scanning / CodeQL

Incorrect conversion between integer types High

Incorrect conversion of an unsigned 64-bit integer from
strconv.ParseUint
to a lower bit size type uint without an upper bound check.

Copilot Autofix

AI 3 days ago

To fix the issue, we need to ensure that the value parsed from windowID is within the valid range for the uint type before performing the conversion. This can be achieved by adding an upper bound check against math.MaxUint32 (the maximum value for uint on most platforms). If the value exceeds this limit, the function should handle the error gracefully, such as by logging the issue and returning nil.

Changes required:

  1. Add an upper bound check for wID after parsing it with strconv.ParseUint.
  2. Use the math package to access math.MaxUint32 for the comparison.
  3. Handle cases where the value is out of range by logging an error and returning nil.
Suggested changeset 1
v3/pkg/application/messageprocessor.go

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/v3/pkg/application/messageprocessor.go b/v3/pkg/application/messageprocessor.go
--- a/v3/pkg/application/messageprocessor.go
+++ b/v3/pkg/application/messageprocessor.go
@@ -11,2 +11,3 @@
 	"sync"
+	"math"
 )
@@ -67,2 +68,7 @@
 		return nil, windowID
+	}
+	// Check if wID is within the valid range for uint
+	if wID > math.MaxUint32 {
+		m.Error("Window ID out of range for uint:", "id", wID)
+		return nil, windowID
 	}
EOF
@@ -11,2 +11,3 @@
"sync"
"math"
)
@@ -67,2 +68,7 @@
return nil, windowID
}
// Check if wID is within the valid range for uint
if wID > math.MaxUint32 {
m.Error("Window ID out of range for uint:", "id", wID)
return nil, windowID
}
Copilot is powered by AI and may make mistakes. Always verify output.
Copy link

cloudflare-workers-and-pages bot commented Jun 15, 2025

Deploying wails with  Cloudflare Pages  Cloudflare Pages

Latest commit: 609a331
Status: ✅  Deploy successful!
Preview URL: https://f0b6a703.wails.pages.dev
Branch Preview URL: https://v3-alpha-chore-refactor-appl.wails.pages.dev

View logs

Copy link
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.

Actionable comments posted: 17

🔭 Outside diff range comments (9)
v3/internal/commands/appimage_testfiles/main.go (1)

53-59: 🛠️ Refactor suggestion

Mixed window-creation APIs – finish the migration

app.Windows.New() is correctly used here, but the same file still contains multiple calls to the old helpers (app.NewWebviewWindowWithOptions) further down (e.g. lines 86-110, 115-198). Leaving a mixture of both APIs undermines the goal of a clean break and will mislead developers studying the example.

-			app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ ... })
+			app.Windows.NewWithOptions(application.WebviewWindowOptions{ ... })

Please update every remaining occurrence in this file.

v3/examples/plain/main.go (1)

27-36: ⚠️ Potential issue

Return value from NewWithOptions is silently ignored

app.Windows.NewWithOptions(...) returns the newly-created *application.WebviewWindow (and in many helpers also an error). Swallowing these values loses an opportunity to:

  1. handle window-creation errors early, and
  2. keep a handle to the window for subsequent API calls.
-window := app.Windows.NewWithOptions(application.WebviewWindowOptions{ ... })
+window, err := app.Windows.NewWithOptions(application.WebviewWindowOptions{ ... })
+if err != nil {
+    log.Fatal(err)
+}
+_ = window   // or store it if you need it later
v3/examples/clipboard/main.go (1)

23-30: ⚠️ Potential issue

app.NewMenu() was removed – use the new manager API

The PR deprecates direct constructors; menu creation should go through the Menus manager:

-menu := app.NewMenu()
+menu := app.Menus.New()

Compilation will fail otherwise.

docs/src/content/docs/learn/systray.mdx (1)

110-118: 🛠️ Refactor suggestion

Update window-creation example to the new Manager API

The snippet still calls the removed app.NewWebviewWindow() helper.
To compile after the API break, replace it with the Windows manager variant:

-// Create a window
-window := app.NewWebviewWindow()
+// Create a window
+window := app.Windows.New()
v3/examples/systray-menu/main.go (1)

55-60: ⚠️ Potential issue

Switch to app.Menus.New()app.NewMenu() no longer exists after the refactor

The new API groups menu functionality under the Menus manager. Using the old constructor will not compile.

-myMenu := app.NewMenu()
+myMenu := app.Menus.New()
v3/examples/dialogs/main.go (1)

28-34: 🛠️ Refactor suggestion

Menu construction uses deprecated helper

app.NewMenu() was removed; use the manager instead to prevent a build break:

-// Create a custom menu
-menu := app.NewMenu()
+// Create a custom menu
+menu := app.Menus.New()
v3/examples/build/main.go (1)

49-56: 🛠️ Refactor suggestion

Still using deprecated app.NewMenu() helper

All other changes moved to manager accessors (app.Menus.*). For consistency and to avoid removal-time breakage, replace with app.Menus.New().

-	menu := app.NewMenu()
+	menu := app.Menus.New()
docs/src/content/docs/guides/menus.mdx (1)

291-299: ⚠️ Potential issue

Out-of-date window API in example

app.NewWebviewWindowWithOptions predates the manager refactor.
Replace with:

window, _ := app.Windows.NewWithOptions(application.WebviewWindowOptions{
    Title: "Custom Menu Window",
    Windows: application.WindowsWindow{
        Menu: customMenu,
    },
})

Ensures all examples consistently use the new Windows manager.

v3/examples/window/main.go (1)

30-38: ⚠️ Potential issue

Possible nil-pointer dereference

application.Get().Windows.Current() can return nil if no window is focused; the subsequent Position() call would panic.
Either reuse the currentWindow helper or add a guard:

win := application.Get().Windows.Current()
if win == nil {
    return
}
♻️ Duplicate comments (8)
v3/internal/generator/testcases/out_of_tree/main.go (1)

39-39: Same unused return value issue as flagged in variable_single_from_function/main.go

This line has the identical problem – see earlier comment for details and recommended fix.

v3/internal/generator/testcases/function_from_imported_package/main.go (1)

43-43: Unused return from Windows.New()

Replicates the compilation issue already pointed out; apply the _ = … or variable assignment fix here as well.

v3/internal/generator/testcases/struct_literal_multiple_files/main.go (1)

18-18: Unused return from Windows.New()

Same concern as in the previous files – update accordingly.

v3/internal/generator/testcases/struct_literal_single/main.go (1)

197-197: Unused return from Windows.New()

Duplicate of the issue already raised; please correct here as well to keep the test-cases compiling.

v3/examples/badge-custom/main.go (1)

77-83: Ditto for "set:badge" listener

Apply the same pattern as above.

v3/examples/badge/main.go (2)

63-69: Store unsubscribe for "remove:badge" listener

Same recommendation as in the custom-badge example.


70-76: Store unsubscribe for "set:badge" listener

Same recommendation as above.

docs/src/content/docs/learn/screens.mdx (1)

365-368: Same shadowing issue in isValidHTTPURL helper

Rename the parameter or alias the import to avoid url.Parse ambiguity, mirroring the correction proposed for the browser guide.

🧹 Nitpick comments (65)
v3/internal/generator/testcases/variable_single/main.go (1)

29-29: *Capture or explicitly discard the returned Window to avoid linter noise

Windows.New() returns a *application.Window. Most static-analysis tools (e.g. staticcheck, errcheck) will flag the ignored result.
Either capture it for later use or assign it to _ with an explanatory comment.

-	app.Windows.New()
+	// Initialise the main window – discard handle if not needed.
+	_ = app.Windows.New() // nolint:errcheck,unused
v3/internal/generator/testcases/function_from_nested_imported_package/main.go (1)

42-42: Same note: don’t silently drop the *Window returned by Windows.New()

Consider:

-	app.Windows.New()
+	_ = app.Windows.New() // keep for future use or document intentional discard
v3/internal/generator/testcases/embedded_interface/main.go (1)

43-43: Window handle is ignored

If the window instance isn’t required, discard it explicitly to keep linters quiet; otherwise store it for later interaction.

-	app.Windows.New()
+	_ = app.Windows.New()
v3/internal/generator/testcases/function_single_internal/main.go (1)

53-53: Explicitly handle the value returned from Windows.New()

-	app.Windows.New()
+	_ = app.Windows.New() // or `mainWin := …` if needed later
v3/internal/generator/testcases/struct_literal_multiple_other/main.go (1)

41-41: Avoid dropping the returned window without comment

-	app.Windows.New()
+	_ = app.Windows.New() // intentional ignore
v3/internal/generator/testcases/complex_json/main.go (1)

116-116: Unused window instance

If the created window instance is needed later (e.g., to manipulate size, title, etc.), keep a reference:

-	app.Windows.New()
+	win := app.Windows.New()
+	_ = win // remove or use later

If truly unused (e.g., tests that only need a default window), ignore this suggestion.

v3/internal/generator/testcases/enum/main.go (1)

66-66: Ensure method chaining isn’t required

Previous API allowed chaining (app.NewWebviewWindow().On...).
If any following code expected the returned window for further configuration, capture it after switching to the manager API.

v3/examples/events-bug/main.go (1)

31-40: API migration correct; consider naming returned windows

Migration to app.Windows.NewWithOptions() is accurate.
Optional: assign the returned *WebviewWindow to a variable (e.g., win1 := …) if you later need to reference it (events, state, etc.).
Purely a readability nit – ignore if not required in this demo.

v3/examples/html-dnd-api/main.go (1)

26-33: Consider handling the returned window / error
app.Windows.NewWithOptions() returns a Window and possibly an error. In an example it’s fine to ignore them, but in production it’s safer to:

  1. capture the window if you later need to manipulate it;
  2. check the error to avoid a silent failure.
-window, _ := app.Windows.NewWithOptions(application.WebviewWindowOptions{ … })
+if wnd, err := app.Windows.NewWithOptions(application.WebviewWindowOptions{ … }); err != nil {
+    log.Fatal(err)
+} else {
+    _ = wnd // use if needed
+}
v3/examples/raw-message/main.go (1)

29-37: Same note on ignored return values
For consistency with the new manager API, capture and check the error returned by NewWithOptions. Skipping it risks masking runtime issues (e.g. invalid options on some platforms).

v3/pkg/application/dialogs_linux.go (2)

4-8: Minor redundancy after GetByID change
Windows.GetByID() already tells you whether a window was found via the second bool result. Immediately checking window != nil duplicates that information. If you keep the nil-check, drop the unused bool; otherwise:

-window, _ := globalApplication.Windows.GetByID(a.getCurrentWindowID())
-if window != nil {
+if window, ok := globalApplication.Windows.GetByID(a.getCurrentWindowID()); ok {
     parent, _ = window.(*WebviewWindow).NativeWindowHandle()
 }

26-31: Apply the same simplification here
Replicate the ok pattern in linuxDialog.show() to keep the codebase consistent.

v3/examples/window-api/main.go (1)

26-35: Handle NewWithOptions result
Same remark as in the other examples: capture the returned (Window, error) to surface construction failures.

v3/internal/generator/testcases/map_keys/main.go (1)

299-299: Consider capturing the returned window for clarity

app.Windows.New() returns a *application.WebviewWindow.
Even if you don’t need it immediately, assigning it to a variable (or the blank identifier) documents intent and avoids the impression of an ignored result.

-	app.Windows.New()
+	_ = app.Windows.New() // returned window currently unused
v3/internal/generator/testcases/function_single/main.go (1)

32-32: Capture or intentionally discard the returned window

Similar to the earlier test case, app.Windows.New() returns a window pointer. Assigning it (or using _) makes the ignored value explicit:

-	app.Windows.New()
+	_ = app.Windows.New()
v3/internal/commands/appimage_testfiles/main.go (1)

63-71: Minor: avoid hidden slice growth

Appending to hiddenWindows inside the WindowClosing hook without bounds or deduplication may leak memory if users repeatedly open/close windows. Consider using a map[*application.WebviewWindow]struct{} or checking contains before appending.

v3/examples/services/main.go (1)

58-61: Window pointer is discarded – consider retaining it for later use

app.Windows.NewWithOptions(...) returns a *application.WebviewWindow.
Although not strictly required for this example, keeping the pointer enables future window customisation (title updates, event hooks, etc.) and avoids creating an unreachable object.

-app.Windows.NewWithOptions(application.WebviewWindowOptions{
+mainWindow := app.Windows.NewWithOptions(application.WebviewWindowOptions{
     Width:  1024,
     Height: 768,
 })
+_ = mainWindow       // remove or use as needed
v3/examples/dialogs-basic/main.go (1)

26-33: Mixed old/new menu creation style – consider uniform Manager API

You’ve migrated window creation (app.Windows.NewWithOptions) but still rely on the convenience method app.NewMenu().
For full consistency and discoverability, use the Menus manager:

-// Create main menu
-menu := app.NewMenu()
+// Create main menu
+menu := app.Menus.New()

(Assumes Menus.New() is exposed; if not, keep as-is.)

v3/examples/gin-example/main.go (1)

96-100: Leaking event handler – store and defer the unsubscribe function

Events.On returns an unsubscribe callback.
Ignoring it means the handler is never removed and will stay alive for the whole app lifetime, which is fine for this tiny sample but is a bad pattern for real-world code or long-running apps.

-app.Events.On("gin-button-clicked", func(event *application.CustomEvent) {
+off := app.Events.On("gin-button-clicked", func(event *application.CustomEvent) {
     log.Printf("Received event from frontend: %v", event.Data)
 })
+defer off() // unregister on shutdown
v3/examples/window-menu/main.go (1)

40-58: Propagate window-creation error

NewWithOptions may fail; make the sample robust:

- app.Windows.NewWithOptions(application.WebviewWindowOptions{ … })
+ if _, err := app.Windows.NewWithOptions(application.WebviewWindowOptions{ … }); err != nil {
+     log.Fatal(err)
+ }
v3/examples/badge-custom/main.go (3)

59-68: Check result of NewWithOptions

Same concern as other examples—handle possible errors.


70-76: Unsubscribe handle ignored for "remove:badge"

Store the off function so the listener can be removed if the window is closed/recreated.


88-93: Potential goroutine leak – stop the ticker on app quit

The infinite loop keeps running after all windows are closed.
Listen for app.OnQuit (or similar) or use a context.Context to cancel.

v3/examples/badge/main.go (2)

52-61: Handle window creation error

Propagate or log the error returned by NewWithOptions.


81-85: Gracefully stop the broadcaster goroutine

Consider cancelling the loop on application shutdown to avoid stray goroutines.

v3/examples/systray-basic/main.go (1)

36-40: Prefer a symbolic key-constant instead of the raw "F12" string

Hard-coding "F12" makes the binding less self-documenting and more error-prone if the key name ever changes. If the framework already exposes key constants (e.g. application.KeyF12 or similar), swap the literal for the constant to improve discoverability and avoid typos.

v3/examples/contextmenus/main.go (1)

38-45: Consistent embed patterns increase readability

Earlier examples embed all assets via //go:embed assets/*, whereas this file uses //go:embed assets. Both are valid, but adopting one style across examples avoids confusion for newcomers skimming the repo.

v3/examples/menu/main.go (3)

59-65: Cache the current window instead of calling app.Windows.Current() twice

Calling app.Windows.Current() twice inside the same callback costs an extra bridge hop and risks returning nil if the active window changes in-between (highly unlikely but possible when menus trigger window focus changes). Grab the pointer once for clarity and safety.

-    if app.Windows.Current().Resizable() {
-        app.Windows.Current().SetResizable(false)
+    w := app.Windows.Current()
+    if w == nil {
+        return // no window – nothing to resize
+    }
+    if w.Resizable() {
+        w.SetResizable(false)
...
-        app.Windows.Current().SetResizable(true)
+        w.SetResizable(true)

147-147: Prefer app.Menus.Apply() over Set() for clarity

Menus.Set() mutates global state; a more descriptive name such as Apply (used in internal API docs) avoids confusion with assigning a menu instance. If both names exist, consider switching for consistency or deprecating one of them.


149-149: Naming nit – unused variable warning in linters

window := app.Windows.New() is only used for the fluent call; the local variable isn’t referenced afterwards except to call SetMenu. Either inline the expression or use _ to silence “unused variable” warnings in stricter linters.

-window := app.Windows.New().SetBackgroundColour(application.NewRGB(33, 37, 41))
-window.SetMenu(menu)
+app.Windows.
+    New().
+    SetBackgroundColour(application.NewRGB(33, 37, 41)).
+    SetMenu(menu)
v3/examples/notifications/main.go (1)

71-72: Handle possible emit failure

Same concern as above: ignore result only if you deliberately don’t care. Otherwise:

-    app.Events.Emit("notification:action", result.Response)
+    if _, err := app.Events.Emit("notification:action", result.Response); err != nil {
+        app.Logger.Error("emit failed", "err", err)
+    }
v3/examples/systray-custom/main.go (2)

19-30: Double-check Hidden:true during window creation

Creating the systray window with Hidden: true then immediately calling window.Show() a few lines below is fine, but setting Hidden to false and dropping the extra Show() call removes one IPC round-trip.

No change required if you want the explicit call – mentioning for potential micro-optimisation.


53-53: Consider naming the tray variable tray instead of systemTray

Minor readability suggestion – the type already conveys it’s a system tray; shorter names make subsequent chained calls less verbose.

-systemTray := app.SystemTray.New()
+tray := app.SystemTray.New()
v3/examples/video/main.go (1)

30-39: Capture or intentionally discard the returned window

app.Windows.NewWithOptions(...) returns a *WebviewWindow.
If the example intends to manipulate the window later (show/hide, emit events, etc.), keep the handle:

win := app.Windows.NewWithOptions(application.WebviewWindowOptions{ ... })
_ = win // remove if used

If no interaction is needed, explicitly ignore the value (_) to document the intent.

v3/examples/events/main.go (1)

36-41: Busy-loop goroutine may block shutdown

Inside the ApplicationStarted handler an infinite for { ... Sleep } loop is spawned.
Because the goroutine is untracked and never terminates, app.Quit() will hang until the program exits forcibly on some platforms. Consider replacing it with a time.Ticker and stopping the ticker in a shutdown hook.

-	for {
-		app.Events.Emit("myevent", "hello")
-		time.Sleep(10 * time.Second)
-	}
+	ticker := time.NewTicker(10 * time.Second)
+	go func() {
+		for range ticker.C {
+			app.Events.Emit("myevent", "hello")
+		}
+	}()
+	app.OnShutdown(func() { ticker.Stop() })
v3/pkg/application/linux_cgo.go (4)

1282-1288: Prefer checking the ok flag returned by GetByID

window, ok := globalApplication.Windows.GetByID(uint(we.id))
if !ok {
    return
}

Explicitly using the boolean avoids ambiguity if GetByID ever returns (nil, true) or a non-nil stub.


1293-1315: Same ok-flag handling applies here

Replicate the ok check to avoid unnecessary type assertions when the window isn’t found.


1474-1481: Minor: early-exit pattern simplifies control flow

-window, _ := globalApplication.Windows.GetByID(windowId)
-if window == nil {
-    return C.gboolean(0)
-}
+window, ok := globalApplication.Windows.GetByID(windowId)
+if !ok {
+    return C.gboolean(0)
+}

1511-1518: Repeat of the ok-flag suggestion

Consistency across handlers makes the code easier to reason about and marginally faster.

v3/examples/gin-service/services/gin_service.go (2)

32-35: EventData struct appears unused

The new EventData type isn’t referenced anywhere in the service. If it’s only here for future use, consider documenting that intent; otherwise remove it to avoid dead-code noise.


150-161: Email validation is extremely naive

strings.Contains(email, "@") will accept many invalid addresses (e.g. "@", "foo@"). If correctness matters, switch to net/mail.ParseAddress or a dedicated validator package.

-import "strings"
+import (
+	"strings"
+	"net/mail"
+)
...
-	if !strings.Contains(newUser.Email, "@") {
+	if _, err := mail.ParseAddress(newUser.Email); err != nil {
 		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid email format"})
 		return
 	}
v3/pkg/application/browser_manager.go (1)

19-27: Add basic argument validation & context to errors

browser.OpenURL("")/OpenFile("") silently fails on some platforms. Guard against empty strings and provide clearer error context.

-import (
-	"github.com/pkg/browser"
-)
+import (
+	"fmt"
+	"github.com/pkg/browser"
+)
...
-func (bm *BrowserManager) OpenURL(url string) error {
-	return browser.OpenURL(url)
+func (bm *BrowserManager) OpenURL(url string) error {
+	if url == "" {
+		return fmt.Errorf("OpenURL: empty url")
+	}
+	return browser.OpenURL(url)
}
...
-func (bm *BrowserManager) OpenFile(path string) error {
-	return browser.OpenFile(path)
+func (bm *BrowserManager) OpenFile(path string) error {
+	if path == "" {
+		return fmt.Errorf("OpenFile: empty path")
+	}
+	return browser.OpenFile(path)
}
v3/examples/badge/frontend/bindings/image/color/models.ts (1)

42-45: Consider guarding JSON parsing errors

JSON.parse will throw on invalid input, crashing the caller. Wrapping it in try/catch and returning a typed error (or undefined) would be safer for consumer code.

docs/src/content/docs/learn/dialogs.mdx (2)

17-22: Nitpick – prefixing with Info() already returns a fully-configured dialog

Because app.Dialogs.Info() returns a builder that already carries the correct dialog type, naming the local variable dialog is fine; however, some examples inside the docs use more explicit names (infoDialog, questionDialog, …). Sticking to one convention improves readability for people copy-pasting snippets.


114-117: Minor API consistency – AttachToWindow accepts a window, not a manager

The example calls

dialog.AttachToWindow(app.Windows.Current())

which is correct, but a short sentence clarifying that app.Windows.Current() returns a Window (not another manager) would prevent confusion for newcomers reading the snippet in isolation.

v3/pkg/application/environment_manager.go (2)

22-33: EnvironmentManager.Info() duplicates expensive OS queries

operatingsystem.Info() and em.app.platformEnvironment() might each probe the system (file I/O, shell exec). If Info() is called frequently you can cache the result:

 type EnvironmentManager struct {
     app *App
+    cachedInfo *EnvironmentInfo
 }
 ...
 func (em *EnvironmentManager) Info() EnvironmentInfo {
-    info, _ := operatingsystem.Info()
-    ...
-    return result
+    if em.cachedInfo != nil {
+        return *em.cachedInfo
+    }
+    info, _ := operatingsystem.Info()
+    ...
+    em.cachedInfo = &result
+    return result
 }

This keeps the API unchanged while avoiding redundant work.


36-41: Nil-impl guard good – but document behaviour

IsDarkMode() silently returns false when app.impl is nil. Add a short comment or godoc sentence so users know a false result can also mean “not initialised yet”.

v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/models.ts (1)

40-54: Generated helper loses type-safety for unknown keys

Object.assign(this, $$source) blindly copies all properties, so typos slip through unnoticed. For generated models you can enforce strictness:

for (const key of Object.keys($$source)) {
  if (!(key in this)) {
    throw new Error(`Unknown property '${key}' supplied to Options`);
  }
}
Object.assign(this, $$source);

Not critical for generated code but improves DX when developers hand-craft JSON.

docs/src/content/docs/guides/menus.mdx (1)

417-420: Context-menu creation deviates from new pattern

After the refactor, creating a context menu without immediately registering it can confuse readers.
Consider showing:

contextMenu := app.ContextMenus.New()     // or .New("imageMenu")
app.ContextMenus.Add("imageMenu", contextMenu)

Aligns with the “manager owns creation” philosophy introduced elsewhere.

docs/src/content/docs/learn/context-menu.mdx (3)

13-17: Mismatch between recommendation and code sample

The text states “use the Add method from the ContextMenus manager”, yet the snippet still instantiates the menu with application.NewContextMenu() instead of the new, shorter helper app.ContextMenus.New().
Using app.ContextMenus.New() keeps the sample consistent with the manager-based guidance and avoids pulling in the (still valid but now secondary) application package function.

-contextMenu := app.ContextMenus.Add("menu-id", application.NewContextMenu())
+contextMenu := app.ContextMenus.Add("menu-id", app.ContextMenus.New())

26-39: Prefer the manager helper to emphasise the new pattern

Throughout the doc you alternate between application.NewContextMenu() and app.ContextMenus.New(). Sticking to one style (the manager helper) reduces cognitive load and shows the intended migration path.

Same applies to the registration comment – the two-step flow (New then Add) is clearer when both calls originate from app.ContextMenus.


112-117: Mixed creation styles within one document

application.NewContextMenu() resurfaces here; consider swapping to the manager helper for consistency and to reinforce the new API.

-contextMenu := application.NewContextMenu()
+contextMenu := app.ContextMenus.New()
v3/pkg/application/application_darwin.go (2)

238-242: Unused cancel-function from OnApplicationEvent

m.parent.Events.OnApplicationEvent returns a cancel function; ignoring it makes it impossible to detach the listener in long-running apps and may leak closures.
Store it (even if only to call in destroy() later) to avoid silent leaks.

- m.parent.Events.OnApplicationEvent(events.Mac.ApplicationDidFinishLaunching, func(*ApplicationEvent) {
+ cancel := m.parent.Events.OnApplicationEvent(events.Mac.ApplicationDidFinishLaunching, func(*ApplicationEvent) {
     // …
 })
 // TODO: call `cancel()` during shutdown if required

293-295: Repeated dark-mode lookup could be expensive

globalApplication.Env.IsDarkMode() may hit Objective-C each time the event fires.
If this event is frequent, cache the bool inside the event context (or Env) before broadcasting.

v3/examples/window/main.go (1)

670-676: Screen API now returns zero value on failure

app.Screens.GetPrimary() and GetAll() no longer surface an error. Consider checking for a zero‐initialised Screen (empty ID) before using the struct to prevent empty-dialog spam on headless setups.

docs/src/content/docs/learn/events.mdx (1)

431-439: Duplicated word & small grammar nit

The comment // Emit from a specific window sits immediately above window.EmitEvent – fine – but static analysis flags a repeated word (“window window” if rendered after line wrap).
Also consider mirroring the Manager API by using window.Events.Emit once the window manager exposes it, to keep messaging symmetrical.

-// Emit from a specific window
+// Emit from a specific window
🧰 Tools
🪛 LanguageTool

[duplication] ~437-~437: Possible typo: you repeated a word.
Context: ...ent", "hello") // Emit from a specific window window.EmitEvent("windowevent", "window specif...

(ENGLISH_WORD_REPEAT_RULE)

v3/examples/dev/go.mod (1)

8-15: New indirects: double-check necessity

Several new indirect dependencies (mergo, go-crypto, xdg, circl, filepath-securejoin) balloon the example’s module graph.
If they’re brought in only via the root wails upgrade you can prune them with go mod tidy -compat=1.22 once the upstream settles.

v3/pkg/application/key_binding_manager.go (1)

48-61: Expose immutable data from GetAll

GetAll returns a slice of pointers to the internally stored callbacks.
External packages can mutate the pointed-to struct fields, bypassing the lock and corrupting state.

-func (kbm *KeyBindingManager) GetAll() []*KeyBinding {
+func (kbm *KeyBindingManager) GetAll() []KeyBinding {-    result = append(result, &KeyBinding{ … })
+    result = append(result, KeyBinding{ … }) // value copy
docs/src/content/docs/learn/keybindings.mdx (1)

116-128: Normalise accelerator strings or document case-sensitivity

Accelerators are shown with mixed-case (“Cmd+S”, “Ctrl+S”).
KeyBindingManager.Add does not enforce normalisation, so "ctrl+s" and "Ctrl+S" register two distinct bindings. Either:

  1. Internally canonicalise to a lower-case format, or
  2. Explicitly state in the docs that accelerators are case-sensitive.

This avoids subtle duplicate or missed-binding bugs.

v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/service.ts (2)

11-15: Remove unused import to avoid dead-code noise

$Create is imported but never referenced.
Even though the // @ts-ignore comment suppresses the TypeScript error, shipping unused symbols clutters the bundle and defeats tree-shaking.

-import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime";
+import { Call as $Call, CancellablePromise as $CancellablePromise } from "@wailsio/runtime";

31-33: Missing JSDoc for newly added API

SetCustomBadge lacks the descriptive block that the other exported functions have. Generated bindings are part of the public surface; keep them self-documented for IDE tooltips and website generation.

+/**
+ * SetCustomBadge sets a badge with extended styling options.
+ * @param label   The text to display inside the badge.
+ * @param options Additional styling options (colour, font, etc.).
+ */
 export function SetCustomBadge(label: string, options: $models.Options): $CancellablePromise<void> {
docs/src/content/docs/learn/browser.mdx (2)

167-170: Compile-time name collision in example code

In isValidURL, the parameter url shadows the imported net/url package; url.Parse therefore fails to compile.

-func isValidURL(url string) bool {
-    parsed, err := url.Parse(url)
+func isValidURL(rawURL string) bool {
+    parsed, err := neturl.Parse(rawURL) // import net/url as neturl

Small detail, but examples copied verbatim should build.


505-505: Minor grammar – missing determiner

“showing them in a dialog” – add “the”/“a” as appropriate to improve readability.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~505-~505: You might be missing the article “the” here.
Context: ...perations fail, such as copying URLs to clipboard or showing them in a dialog for manual ...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

v3/pkg/application/event_manager.go (1)

33-41: Method names are fine – consider documenting cancellation semantics

On, Off, Reset work well but callers might miss that callbacks can call event.Cancel(). A short comment or example would aid discoverability.

v3/pkg/application/application.go (1)

311-313: Minor naming confusion

EventListener.callback func(app *ApplicationEvent) uses parameter name app, which shadows the App concept and can mislead readers. Recommend renaming to evt or event in a follow-up patch.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a8c8417 and 33fb417.

⛔ Files ignored due to path filters (5)
  • v3/examples/badge-custom/frontend/package-lock.json is excluded by !**/package-lock.json
  • v3/examples/badge/frontend/dist/assets/index-edhLCYCH.js is excluded by !**/dist/**
  • v3/examples/badge/frontend/dist/index.html is excluded by !**/dist/**
  • v3/examples/badge/frontend/package-lock.json is excluded by !**/package-lock.json
  • v3/examples/dev/go.sum is excluded by !**/*.sum
📒 Files selected for processing (107)
  • docs/src/content/docs/guides/menus.mdx (9 hunks)
  • docs/src/content/docs/learn/application-menu.mdx (8 hunks)
  • docs/src/content/docs/learn/browser.mdx (1 hunks)
  • docs/src/content/docs/learn/clipboard.mdx (3 hunks)
  • docs/src/content/docs/learn/context-menu.mdx (7 hunks)
  • docs/src/content/docs/learn/dialogs.mdx (10 hunks)
  • docs/src/content/docs/learn/environment.mdx (1 hunks)
  • docs/src/content/docs/learn/events.mdx (2 hunks)
  • docs/src/content/docs/learn/keybindings.mdx (1 hunks)
  • docs/src/content/docs/learn/manager-api.mdx (1 hunks)
  • docs/src/content/docs/learn/notifications.mdx (1 hunks)
  • docs/src/content/docs/learn/screens.mdx (1 hunks)
  • docs/src/content/docs/learn/systray.mdx (3 hunks)
  • docs/src/content/docs/learn/windows.mdx (1 hunks)
  • v3/examples/badge-custom/main.go (3 hunks)
  • v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/index.ts (1 hunks)
  • v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/models.ts (1 hunks)
  • v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/service.ts (1 hunks)
  • v3/examples/badge/frontend/bindings/image/color/index.ts (1 hunks)
  • v3/examples/badge/frontend/bindings/image/color/models.ts (1 hunks)
  • v3/examples/badge/main.go (3 hunks)
  • v3/examples/binding/main.go (1 hunks)
  • v3/examples/build/main.go (8 hunks)
  • v3/examples/cancel-async/main.go (1 hunks)
  • v3/examples/cancel-chaining/main.go (1 hunks)
  • v3/examples/clipboard/main.go (2 hunks)
  • v3/examples/contextmenus/main.go (3 hunks)
  • v3/examples/dev/go.mod (1 hunks)
  • v3/examples/dev/main.go (1 hunks)
  • v3/examples/dialogs-basic/main.go (2 hunks)
  • v3/examples/dialogs/main.go (5 hunks)
  • v3/examples/drag-n-drop/main.go (2 hunks)
  • v3/examples/environment/main.go (1 hunks)
  • v3/examples/events-bug/main.go (2 hunks)
  • v3/examples/events/main.go (3 hunks)
  • v3/examples/file-association/main.go (3 hunks)
  • v3/examples/frameless/main.go (1 hunks)
  • v3/examples/gin-example/main.go (1 hunks)
  • v3/examples/gin-routing/main.go (1 hunks)
  • v3/examples/gin-service/main.go (1 hunks)
  • v3/examples/gin-service/services/gin_service.go (3 hunks)
  • v3/examples/hide-window/main.go (2 hunks)
  • v3/examples/html-dnd-api/main.go (1 hunks)
  • v3/examples/ignore-mouse/main.go (1 hunks)
  • v3/examples/keybindings/main.go (2 hunks)
  • v3/examples/menu/main.go (2 hunks)
  • v3/examples/notifications/main.go (2 hunks)
  • v3/examples/panic-handling/main.go (1 hunks)
  • v3/examples/plain/main.go (2 hunks)
  • v3/examples/raw-message/main.go (1 hunks)
  • v3/examples/screen/main.go (1 hunks)
  • v3/examples/screen/screens.go (2 hunks)
  • v3/examples/services/main.go (1 hunks)
  • v3/examples/show-macos-toolbar/main.go (2 hunks)
  • v3/examples/single-instance/main.go (1 hunks)
  • v3/examples/systray-basic/main.go (1 hunks)
  • v3/examples/systray-custom/main.go (2 hunks)
  • v3/examples/systray-menu/main.go (1 hunks)
  • v3/examples/video/main.go (1 hunks)
  • v3/examples/window-api/main.go (1 hunks)
  • v3/examples/window-call/main.go (2 hunks)
  • v3/examples/window-menu/main.go (1 hunks)
  • v3/examples/window/main.go (30 hunks)
  • v3/examples/wml/main.go (2 hunks)
  • v3/internal/commands/appimage_testfiles/main.go (3 hunks)
  • v3/internal/generator/testcases/aliases/main.go (1 hunks)
  • v3/internal/generator/testcases/complex_expressions/main.go (1 hunks)
  • v3/internal/generator/testcases/complex_instantiations/main.go (1 hunks)
  • v3/internal/generator/testcases/complex_json/main.go (1 hunks)
  • v3/internal/generator/testcases/complex_method/main.go (1 hunks)
  • v3/internal/generator/testcases/cyclic_imports/main.go (1 hunks)
  • v3/internal/generator/testcases/cyclic_types/main.go (1 hunks)
  • v3/internal/generator/testcases/directives/main.go (1 hunks)
  • v3/internal/generator/testcases/embedded_interface/main.go (1 hunks)
  • v3/internal/generator/testcases/enum/main.go (1 hunks)
  • v3/internal/generator/testcases/enum_from_imported_package/main.go (1 hunks)
  • v3/internal/generator/testcases/function_from_imported_package/main.go (1 hunks)
  • v3/internal/generator/testcases/function_from_nested_imported_package/main.go (1 hunks)
  • v3/internal/generator/testcases/function_multiple_files/main.go (1 hunks)
  • v3/internal/generator/testcases/function_single/main.go (1 hunks)
  • v3/internal/generator/testcases/function_single_context/main.go (1 hunks)
  • v3/internal/generator/testcases/function_single_internal/main.go (1 hunks)
  • v3/internal/generator/testcases/map_keys/main.go (1 hunks)
  • v3/internal/generator/testcases/marshalers/main.go (1 hunks)
  • v3/internal/generator/testcases/out_of_tree/main.go (1 hunks)
  • v3/internal/generator/testcases/struct_literal_multiple/main.go (1 hunks)
  • v3/internal/generator/testcases/struct_literal_multiple_files/main.go (1 hunks)
  • v3/internal/generator/testcases/struct_literal_multiple_other/main.go (1 hunks)
  • v3/internal/generator/testcases/struct_literal_non_pointer_single/main.go (1 hunks)
  • v3/internal/generator/testcases/struct_literal_single/main.go (1 hunks)
  • v3/internal/generator/testcases/variable_single/main.go (1 hunks)
  • v3/internal/generator/testcases/variable_single_from_function/main.go (1 hunks)
  • v3/internal/generator/testcases/variable_single_from_other_function/main.go (1 hunks)
  • v3/pkg/application/application.go (7 hunks)
  • v3/pkg/application/application_darwin.go (3 hunks)
  • v3/pkg/application/application_linux.go (1 hunks)
  • v3/pkg/application/application_windows.go (1 hunks)
  • v3/pkg/application/browser_manager.go (1 hunks)
  • v3/pkg/application/clipboard_manager.go (1 hunks)
  • v3/pkg/application/context_menu_manager.go (1 hunks)
  • v3/pkg/application/dialog_manager.go (1 hunks)
  • v3/pkg/application/dialogs_linux.go (2 hunks)
  • v3/pkg/application/environment_manager.go (1 hunks)
  • v3/pkg/application/event_manager.go (1 hunks)
  • v3/pkg/application/events_common_darwin.go (1 hunks)
  • v3/pkg/application/key_binding_manager.go (1 hunks)
  • v3/pkg/application/linux_cgo.go (5 hunks)
⛔ Files not processed due to max files limit (17)
  • v3/pkg/application/linux_purego.go
  • v3/pkg/application/menu.go
  • v3/pkg/application/menu_manager.go
  • v3/pkg/application/menuitem_dev.go
  • v3/pkg/application/menuitem_roles.go
  • v3/pkg/application/messageprocessor.go
  • v3/pkg/application/messageprocessor_clipboard.go
  • v3/pkg/application/messageprocessor_dialog.go
  • v3/pkg/application/messageprocessor_screens.go
  • v3/pkg/application/messageprocessor_system.go
  • v3/pkg/application/roles.go
  • v3/pkg/application/screen_windows.go
  • v3/pkg/application/screenmanager.go
  • v3/pkg/application/system_tray_manager.go
  • v3/pkg/application/systemtray.go
  • v3/pkg/application/webview_window.go
  • v3/pkg/application/window_manager.go
🧰 Additional context used
🧠 Learnings (1)
v3/examples/keybindings/main.go (1)
Learnt from: nixpare
PR: wailsapp/wails#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.
🧬 Code Graph Analysis (19)
v3/internal/generator/testcases/complex_json/main.go (1)
v3/pkg/events/events.go (1)
  • Windows (364-364)
v3/internal/generator/testcases/complex_method/main.go (1)
v3/pkg/events/events.go (1)
  • Windows (364-364)
v3/examples/panic-handling/main.go (2)
v3/pkg/events/events.go (1)
  • Windows (364-364)
v3/pkg/application/application.go (1)
  • New (49-168)
v3/internal/generator/testcases/function_single_internal/main.go (1)
v3/pkg/events/events.go (1)
  • Windows (364-364)
v3/examples/cancel-chaining/main.go (1)
v3/pkg/application/webview_window_options.go (1)
  • WebviewWindowOptions (32-142)
v3/internal/generator/testcases/variable_single/main.go (1)
v3/pkg/events/events.go (1)
  • Windows (364-364)
v3/examples/environment/main.go (1)
v3/pkg/application/webview_window_options.go (1)
  • WebviewWindowOptions (32-142)
v3/examples/screen/main.go (1)
v3/pkg/application/webview_window_options.go (1)
  • WebviewWindowOptions (32-142)
v3/examples/binding/main.go (1)
v3/pkg/application/webview_window_options.go (1)
  • WebviewWindowOptions (32-142)
v3/examples/gin-example/main.go (3)
v3/internal/runtime/desktop/@wailsio/runtime/src/events.ts (1)
  • On (96-98)
v3/pkg/application/events.go (1)
  • CustomEvent (52-58)
v3/pkg/application/webview_window_options.go (1)
  • WebviewWindowOptions (32-142)
v3/examples/dialogs/main.go (2)
v3/pkg/events/events.go (1)
  • Windows (364-364)
v3/pkg/application/application.go (1)
  • New (49-168)
v3/internal/generator/testcases/function_single_context/main.go (1)
v3/pkg/events/events.go (1)
  • Windows (364-364)
v3/examples/contextmenus/main.go (3)
v3/pkg/events/events.go (1)
  • Windows (364-364)
v3/pkg/application/webview_window_options.go (1)
  • WebviewWindowOptions (32-142)
v3/pkg/application/application.go (1)
  • New (49-168)
v3/examples/systray-custom/main.go (3)
v3/pkg/application/webview_window_options.go (1)
  • WebviewWindowOptions (32-142)
v3/pkg/application/systemtray.go (1)
  • SystemTray (44-66)
v3/pkg/application/application.go (1)
  • New (49-168)
v3/examples/video/main.go (3)
v3/pkg/events/events.go (2)
  • Mac (90-90)
  • Windows (364-364)
v3/pkg/application/events.go (1)
  • ApplicationEvent (11-16)
v3/pkg/application/webview_window_options.go (1)
  • WebviewWindowOptions (32-142)
v3/examples/keybindings/main.go (2)
v3/pkg/events/events.go (1)
  • Windows (364-364)
v3/pkg/application/webview_window_options.go (1)
  • WebviewWindowOptions (32-142)
v3/examples/gin-routing/main.go (2)
v3/pkg/application/events.go (1)
  • CustomEvent (52-58)
v3/pkg/application/webview_window_options.go (1)
  • WebviewWindowOptions (32-142)
v3/examples/show-macos-toolbar/main.go (1)
v3/pkg/application/webview_window_options.go (1)
  • WebviewWindowOptions (32-142)
v3/examples/single-instance/main.go (2)
v3/pkg/events/events.go (1)
  • Windows (364-364)
v3/pkg/application/webview_window_options.go (1)
  • WebviewWindowOptions (32-142)
🪛 LanguageTool
docs/src/content/docs/learn/windows.mdx

[style] ~266-~266: Consider an alternative adverb to strengthen your wording.
Context: ... }) ``` 2. Resource Management: Properly clean up windows when they're no longer...

(PROPERLY_THOROUGHLY)

docs/src/content/docs/learn/browser.mdx

[uncategorized] ~505-~505: You might be missing the article “the” here.
Context: ...perations fail, such as copying URLs to clipboard or showing them in a dialog for manual ...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

docs/src/content/docs/learn/events.mdx

[duplication] ~437-~437: Possible typo: you repeated a word.
Context: ...ent", "hello") // Emit from a specific window window.EmitEvent("windowevent", "window specif...

(ENGLISH_WORD_REPEAT_RULE)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (71)
v3/internal/generator/testcases/complex_expressions/main.go (1)

46-46: API refactor applied correctly

app.Windows.New() is the correct replacement for the old app.NewWebviewWindow() call under the new Manager API. No other changes needed here.

v3/internal/generator/testcases/enum_from_imported_package/main.go (1)

30-30: Window creation migrated to the Windows manager

Call updated to the new pattern – looks good and keeps the example aligned with the refactor.

v3/internal/generator/testcases/directives/main.go (1)

52-52: Consistent use of the new Manager API

The change is correct and consistent with the project-wide shift to app.Windows.New().

v3/internal/generator/testcases/complex_method/main.go (1)

35-35: Refactor compliance verified

app.Windows.New() correctly replaces the deprecated call.

v3/internal/generator/testcases/function_single_context/main.go (1)

38-38: Updated to new window-manager call

Matches the new API; no further action required.

v3/internal/generator/testcases/complex_instantiations/main.go (1)

52-52: Confirm returned value from Windows.New() is intentionally ignored

app.Windows.New() likely returns a *application.Window (or similar).
If the caller does not need the handle, ignoring it is fine, but double-check that the function does not return an additional boolean/error; otherwise compilation will fail.

v3/internal/generator/testcases/cyclic_imports/main.go (1)

47-47: Validate signature change for Windows.New()

Same note as other files: ensure the new manager API’s New() method returns a single value; if it returns two, the current call will not compile.

v3/internal/generator/testcases/variable_single_from_other_function/main.go (1)

45-45: Aligned with new manager-based API – looks good

The switch to app.Windows.New() matches the new modular architecture. No additional adjustments needed here.

v3/internal/generator/testcases/marshalers/main.go (1)

201-201: Change correctly adopts Windows manager

The update keeps this test-case in sync with the new API surface. ✔️

v3/internal/generator/testcases/aliases/main.go (1)

129-129: Consistent with the refactor

Using app.Windows.New() here maintains consistency across all generator test-cases.

v3/examples/gin-service/main.go (1)

33-37: Correct migration to NewWithOptions

app.Windows.NewWithOptions(...) is the proper replacement for the old NewWebviewWindowWithOptions call; the supplied application.WebviewWindowOptions struct remains valid. Looks good.

v3/internal/generator/testcases/cyclic_types/main.go (1)

38-38: Window creation updated as expected

The call now routes through the Windows manager, matching the new API contract.

v3/examples/panic-handling/main.go (1)

51-53: Adopts new Windows manager cleanly – looks good

The call chain now uses app.Windows.New() and method chaining still reads clearly. No functional or stylistic issues spotted.

v3/examples/events-bug/main.go (1)

42-51: Second window call mirrors first – consistent

Same comment as above; change is correct and consistent with the new API.

v3/internal/generator/testcases/struct_literal_multiple/main.go (1)

33-34: Update aligns with manager pattern

app.Windows.New() replaces the deprecated constructor as expected. No further action needed.

v3/internal/generator/testcases/struct_literal_non_pointer_single/main.go (1)

197-198: Correct switch to Windows manager

The example now follows the new API. All good.

v3/examples/badge/frontend/bindings/image/color/index.ts (1)

1-6: Verify module-resolution for ./models.js

The re-export targets ./models.js, while the source file in the repo appears to be models.ts.
With TypeScript’s Node16/Next resolution this can work only if:

  1. A compiled models.js will exist at runtime, and
  2. tsc is configured to allow imports with .js extension pointing to .ts sources (moduleResolution = node16 or bundler, or allowImportingTsExtensions).

If neither is true the build will fail to resolve the module. Please double-check the tsconfig or emit pipeline.

v3/examples/window-call/main.go (2)

66-66: Inconsistent menu API usage
You’ve migrated Set to app.Menus, but the menu is still created with app.NewMenu().
If the old helper is deprecated, switch to the manager for symmetry:

-menu := app.NewMenu()
+menu := app.Menus.New()

Confirm that Menus.New() exists in the refactor; otherwise keep as-is.


59-63: 🛠️ Refactor suggestion

Ditto for windows created from the menu action
Failing fast inside the click handler avoids leaving the app in an inconsistent state.

- app.Windows.New().
+ if wnd, err := app.Windows.New(); err == nil {
     wnd.SetTitle("WebviewWindow …").SetRelativePosition(…).Show()
   } else {
     log.Println("failed to open window:", err)
   }

Likely an incorrect or invalid review comment.

v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/index.ts (1)

8-11: Verify .js import path works under the TypeScript compiler

The file is a TypeScript source but imports Options from "./models.js".
Depending on tsconfig (allowJs, resolveJsonModule, esModuleInterop), this path can cause “cannot find module” errors when emitting declaration files.

If models.ts exists, prefer the TypeScript extension:

-} from "./models.js";
+} from "./models.js"; // ✅ runtime
+// If you emit declarations, consider "./models.js" ➜ "./models"

Double-check the build passes on strict CI.

v3/examples/screen/main.go (1)

62-71: LGTM – call updated to new Window manager

The switch to app.Windows.NewWithOptions aligns with the new manager-based API and preserves the original options.
No additional changes required.

v3/examples/environment/main.go (1)

27-36: LGTM – window creation updated correctly

The code now uses app.Windows.NewWithOptions, matching the refactored API. Everything else remains intact.

v3/examples/single-instance/main.go (1)

66-71: API-migration looks correct – no further action

Swapping app.NewWebviewWindowWithOptions for app.Windows.NewWithOptions matches the new manager API. Signature and option usage remain intact, so this hunk needs no additional changes.

v3/examples/keybindings/main.go (2)

23-33: Correct use of the Windows manager

The example now instantiates the first window via app.Windows.NewWithOptions, fully aligning with the new API.


34-43: Ditto for second window – LGTM

v3/pkg/application/application_windows.go (1)

133-134: Encapsulation approved

Assigning the menu to the unexported applicationMenu field reinforces the new Menus manager encapsulation. No issues spotted.

docs/src/content/docs/learn/notifications.mdx (1)

123-124: Documentation correctly reflects Events manager

The snippet now uses app.Events.Emit, matching the refactor. Good catch.

v3/examples/cancel-chaining/main.go (1)

26-29: Switched to manager API correctly – looks good

The example now creates its window via app.Windows.NewWithOptions, matching the new modular API. No further changes needed here.

v3/examples/dev/main.go (1)

26-36: Correct adoption of Windows manager

The call to app.Windows.NewWithOptions (plus the unchanged options struct) is consistent with the new design. Nice straightforward update.

v3/examples/frameless/main.go (1)

27-30: Manager-based window creation acknowledged

The frameless-window example is now in line with the new API. Implementation remains functionally identical – all good.

v3/examples/cancel-async/main.go (1)

26-29: Conformant change to app.Windows

The migration to app.Windows.NewWithOptions is correct and compiles cleanly with the updated package. Nothing further.

v3/examples/ignore-mouse/main.go (1)

19-25: Window variable retained, API call updated – good

The example keeps the returned *WebviewWindow in window and moves to app.Windows.NewWithOptions. Alignment with the new API is accurate.

v3/pkg/application/events_common_darwin.go (1)

16-19: Good adaptation to the new Events manager

Switching to m.parent.Events.OnApplicationEvent aligns with the manager-based API and keeps event registration encapsulated.
No further action required.

v3/pkg/application/application_linux.go (1)

90-92: Assignment now targets the private field – looks correct

Updating to globalApplication.applicationMenu matches the recent visibility change and prevents accidental external mutation.
Implementation is consistent with the new Menus manager. Good catch.

v3/examples/binding/main.go (1)

26-29: API call updated correctly

The example now uses app.Windows.NewWithOptions, matching the new manager pattern. No issues spotted.

v3/examples/dialogs-basic/main.go (1)

37-38: app.Menus.Set call looks good

The menu is now set via the manager, matching the refactor. 👍

v3/examples/screen/screens.go (1)

16-17: Loss of error-path visibility – GetAll() does not expose failures

The old GetScreens() returned (screens []*Screen, err error), letting callers react to runtime failures.
After the refactor the call is replaced by Screens.GetAll() which returns only the slice, silently swallowing any potential backend error.

If the new contract genuinely guarantees a non-nil/always-valid slice, please annotate that fact in code comments or docs.
Otherwise propagate an error (or bool ok) so callers can fail fast instead of continuing with an invalid slice.

v3/examples/gin-example/main.go (1)

101-108: Missing error handling for window creation

Windows.NewWithOptions can return an error (e.g. invalid options, platform failure).
Bubble it up instead of discarding:

-win := app.Windows.NewWithOptions(application.WebviewWindowOptions{ … })
+win, err := app.Windows.NewWithOptions(application.WebviewWindowOptions{ … })
+if err != nil {
+    log.Fatal(err)
+}
+_ = win
v3/examples/plain/main.go (1)

49-56: Verify that Windows.NewWithOptions is goroutine-safe

The goroutine executes after app.Run() starts, meaning window creation happens concurrently with the main UI thread. Double-check that the window manager is designed for concurrent use; otherwise, marshal the call back onto the main thread (e.g., via app.Events.Dispatch(...) or a dedicated UI scheduler).

v3/examples/clipboard/main.go (1)

68-70: Nice usage of app.Menus.Set, but update the matching constructor

Once the constructor above is fixed, this call remains valid.

v3/examples/wml/main.go (1)

38-43: LGTM – event wiring matches the new Events manager

The switch to app.Events.On(...) is correct and consistent with the new API.

v3/examples/drag-n-drop/main.go (1)

40-41:

❌ Incorrect review comment

Check the error/ok result of app.Events.Emit

Events.Emit returns (bool, error) (per manager API) so silently discarding it loses delivery failures and makes debugging harder.

-    app.Events.Emit("files", files)
+    if ok, err := app.Events.Emit("files", files); err != nil {
+        app.Logger.Error("failed emitting 'files' event", "error", err)
+    } else if !ok {
+        app.Logger.Warn("'files' event had no listeners")
+    }

🏁 Script executed:

#!/bin/bash
# Find the definition of the Emit method to confirm its return signature.
rg -R "^func.*Emit" -n .

Length of output: 431


🏁 Script executed:

#!/bin/bash
# Locate all Emit method definitions and the Events type to verify return signatures.
grep -rn --include="*.go" "func .*Emit" .  
grep -rn --include="*.go" "type Events" .

Length of output: 1010


🏁 Script executed:

#!/bin/bash
# Show the signature and any return statements for EventManager.Emit
sed -n '1,200p' v3/pkg/application/event_manager.go | sed -n '1,50p'

Length of output: 1406


Ignore error/ok checks on Emit – it has no return values
The app.Events.Emit method in v3’s EventManager is defined as returning void, so there is no (bool, error) to inspect. The proposed diff isn’t applicable and can be safely disregarded.

Likely an incorrect or invalid review comment.

v3/examples/show-macos-toolbar/main.go (1)

20-30: Store window pointers if future interaction is required

Both windows are created and the pointers discarded. Fine for a static demo, but if you later need to modify them (title, size, etc.) you’ll need the references. Keeping them in variables now avoids refactors later.

No action needed if the demo deliberately keeps them fire-and-forget.

v3/examples/dialogs/main.go (1)

60-66: ShowAbout already shows a dialog – no need for a submenu click handler?

app.Menus.ShowAbout() immediately displays the standard About dialog.
Invoking it inside a menu item click is correct but consider whether the separate submenu is still required or if the item should directly use the built-in About role to keep platform behaviour consistent.

docs/src/content/docs/learn/application-menu.mdx (4)

13-17: Documentation correctly reflects the Manager API

The example correctly demonstrates menu creation via app.Menus.New() which matches the new API.
No issues spotted.


26-30: macOS example looks good

app.Menus.Set(menu) is the correct replacement for the old direct method.


39-50: Windows/Linux snippets are consistent

Using app.Windows.Current().SetMenu(menu) aligns with the new Window manager.


192-194: Nice clarification

Calling app.Windows.Current() inside handlers is a helpful hint.

v3/examples/hide-window/main.go (1)

46-49: Good migration to Events manager

app.Events.OnApplicationEvent is the correct replacement for the old app.OnApplicationEvent.

v3/examples/file-association/main.go (3)

51-60: Window creation migration LGTM

Using app.Windows.NewWithOptions is correct and the options struct is unchanged.


63-66: Application-level event subscription looks correct

app.Events.OnApplicationEvent(events.Common.ApplicationOpenedWithFile, …) matches the new API.


79-80: Custom event emission migrated

app.Events.Emit("time", now) is the right call.

v3/examples/gin-routing/main.go (2)

97-99: Event handler migration LGTM

app.Events.On("gin-button-clicked", …) matches the new Events manager.


102-107: Window creation migration LGTM

app.Windows.NewWithOptions is used consistently.

v3/pkg/application/linux_cgo.go (1)

1603-1610: Good defensive check on window lookup

The inline ok guard avoids a potential nil-dereference – nice.

docs/src/content/docs/learn/clipboard.mdx (1)

14-15: Docs align with new manager API – looks good

v3/examples/build/main.go (1)

263-266: Initial window not shown

app.Windows.New() returns a builder that requires .Show() (unless New() side-effects changed). Without it the demo starts with no visible window.

-	app.Windows.New()
+	app.Windows.New().Show()
docs/src/content/docs/guides/menus.mdx (1)

268-281: Macro roles example ok – but remind users to call menu.Update()

After mutating the menu (adding roles/submenus) it must be Update()-ed on Windows/Linux. Adding a one-liner avoids subtle bugs where menu appears unchanged.

docs/src/content/docs/learn/context-menu.mdx (2)

50-59: Out-of-date wording on menu-id reference

The surrounding paragraph later says the CSS id must match that used in NewContextMenu, but in the new flow the id comes from Add(...).
Update the prose accordingly, otherwise beginners may hunt for a non-existent parameter.


198-212: Window creation code not yet migrated

The example switches to app.ContextMenus.* but still calls app.NewWebviewWindow().
For consistency with the rest of the PR, use app.Windows.New() (or .NewWithOptions()).

v3/pkg/application/application_darwin.go (1)

318-325: Potential race on globalApplication.Windows.GetByID

processURLRequest is executed from C callbacks that may run on non-Go threads; ensure GetByID is safe for concurrent access.
If the manager already guards with a mutex you’re fine; otherwise wrap this lookup with the manager’s lock.

v3/examples/dev/go.mod (1)

22-22: Suspicious pseudo-version timestamp

github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 references a commit from November 2024, which is in the future relative to Go’s module proxy mirrors.
Resolve to an existing commit or you risk go mod download failures.

docs/src/content/docs/learn/manager-api.mdx (1)

9-11: “Full backward-compatibility” claim conflicts with the PR description

The introduction states that the Manager API is “maintaining full backward compatibility”, yet the PR summary explicitly calls this a breaking change that requires migration. Please revisit the wording to avoid misleading readers and to set correct expectations for developers upgrading to v3.

docs/src/content/docs/learn/windows.mdx (2)

116-124: Verify OnCreate callback signature

The snippet shows

app.Windows.OnCreate(func(window application.Window) { … })

In the new manager code the concrete callback type is likely func(window Window) or func(window *application.WebviewWindow). Double-check the exported signature to keep examples compile-ready.


126-138: Confirm Remove / RemoveByName API availability

app.Windows.Remove(id) and RemoveByName(name) are demonstrated here, but they are not mentioned in the Manager API overview. If they are truly exported, add them to the reference section; otherwise, update the docs to the correct call (e.g. Close() on the window, or an internal manager helper).

v3/pkg/application/key_binding_manager.go (1)

30-38: Race window between lookup and callback invocation

The read lock is released before invoking callback.
A concurrent Remove() could nil-out the map entry, leading to a callback that is no longer registered being executed.
Consider holding the read lock for the duration of the call or documenting this behaviour clearly. A safer pattern is to copy the function pointer under lock and execute it afterwards only if it is still equal to the looked-up value.

v3/pkg/application/dialog_manager.go (1)

21-25: Nil-options guard

OpenFileWithOptions and SaveFileWithOptions dereference options via SetOptions without a nil check.
If callers pass nil (easy to do given the pointer type), you risk a panic unless SetOptions handles it internally. Clarify the contract or add a guard.

if options != nil {
    result.SetOptions(options)
}
v3/pkg/application/application.go (4)

79-80: Potential nil-pointer – verify manager initialisation order

result.customEventProcessor = NewWailsEventProcessor(result.Events.dispatch) is executed inside New().
If result.Events is set after this line (i.e. init() hasn’t run yet), dispatch will panic at runtime.

Please double-check that newApplication() invokes (*App).init() (or otherwise sets Events) before reaching this assignment. If not, swap the lines or call result.init() first.


273-285: Nice encapsulation with managers

The embedded managers dramatically improve discoverability and separation of concerns. 👍


415-427: Concurrent safety of manager pointers

All managers are created without guarding a.runLock. That’s fine during initialisation, but if any manager stores references to shared maps (like Events does) ensure they always respect the parent App locks when accessing those maps; otherwise the race detector will still complain even after fixing dispatch().


557-558: Menus channel consumer renamed – looks good

Routing menuItemClicked to a.Menus.handleMenuItemClicked keeps responsibilities local to the manager. No issues spotted.

@@ -30,7 +30,7 @@ func main() {
},
})

app.NewWebviewWindow()
app.Windows.New()
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Return value from app.Windows.New() is ignored – compilation will fail

Windows.New() returns at least one value (*application.Window, possibly a (win *application.Window, ok bool) tuple based on the new manager-API rules).
Calling it as a stand-alone statement causes “value not used” compilation errors in Go.

-	app.Windows.New()
+	_ = app.Windows.New()          // discard
+	// or: win := app.Windows.New() // keep for later

Adjust all occurrences (examples below) or change the API to return void if that is the intent.

📝 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.

Suggested change
app.Windows.New()
_ = app.Windows.New() // discard
// or: win := app.Windows.New() // keep for later
🤖 Prompt for AI Agents
In v3/internal/generator/testcases/variable_single_from_function/main.go at line
33, the call to app.Windows.New() returns a value that is currently ignored,
causing a compilation error. Modify the code to capture the returned value(s)
from app.Windows.New() by assigning them to appropriate variables or use the
blank identifier (_) if the values are not needed. This will resolve the "value
not used" error without changing the API.

@@ -14,7 +14,7 @@ func main() {
},
})

app.NewWebviewWindow()
app.Windows.New()
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Compile-time check for new API

Quick search script to surface any remaining NewWebviewWindow( calls:

Run it to ensure migration is complete.


🏁 Script executed:

#!/bin/bash
rg -n "NewWebviewWindow\\(" || echo "All occurrences migrated."

Length of output: 413


Update remaining NewWebviewWindow references in documentation

The migration isn’t complete—these MDX files still reference the old API and need to be updated to the new app.Windows.New() call:

• docs/src/content/docs/learn/application-menu.mdx:251
• docs/src/content/docs/learn/bindings.mdx:186
• docs/src/content/docs/learn/clipboard.mdx:113
• docs/src/content/docs/learn/context-menu.mdx:213
• docs/src/content/docs/learn/systray.mdx:115

Please replace each app.NewWebviewWindow() occurrence with the new API and re-run:

rg -n "NewWebviewWindow\(" || echo "All occurrences migrated."

to confirm that no references remain.

🤖 Prompt for AI Agents
In v3/internal/generator/testcases/function_multiple_files/main.go at line 17,
the code correctly uses the new API call app.Windows.New(). However, the review
points out that there are still references to the old API app.NewWebviewWindow()
in several documentation MDX files. To fix this, search for all occurrences of
app.NewWebviewWindow() in the listed MDX files and replace them with
app.Windows.New(). After updating, run the provided rg command to confirm no old
API references remain.

Comment on lines +41 to 43
app.Windows.New().
SetTitle("WebviewWindow 1").
Show()
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Error ignored on initial window creation
app.Windows.New() also returns (Window, error). Consider at least logging the error:

-wnd := app.Windows.New()
-if err != nil { … }
-
+if wnd, err := app.Windows.New(); err != nil {
+    log.Fatal(err)
+} else {
+    wnd.SetTitle("WebviewWindow 1").Show()
+}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In v3/examples/window-call/main.go around lines 41 to 43, the call to
app.Windows.New() returns a Window and an error, but the error is currently
ignored. Modify the code to capture both the Window and error return values,
check if the error is non-nil, and log the error appropriately before proceeding
to set the title and show the window.

Comment on lines +343 to 349
screen := app.GetPrimaryScreen()
msg := fmt.Sprintf("Screen: %+v", screen)
application.InfoDialog().SetTitle("Primary Screen").SetMessage(msg).Show()
})
stateMenu.Add("Get Screens").OnClick(func(ctx *application.Context) {
screens, err := app.GetScreens()
if err != nil {
application.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show()
return
}
screens := app.GetScreens()
for _, screen := range screens {
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Screen API not yet migrated

app.GetPrimaryScreen() / app.GetScreens() should be routed through the new Screens manager for consistency:

-		screen := app.GetPrimaryScreen()
+		screen := app.Screens.GetPrimary()
-		screens := app.GetScreens()
+		screens := app.Screens.GetAll()

(Adjust return-type handling if the new methods differ.)

📝 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.

Suggested change
screen := app.GetPrimaryScreen()
msg := fmt.Sprintf("Screen: %+v", screen)
application.InfoDialog().SetTitle("Primary Screen").SetMessage(msg).Show()
})
stateMenu.Add("Get Screens").OnClick(func(ctx *application.Context) {
screens, err := app.GetScreens()
if err != nil {
application.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show()
return
}
screens := app.GetScreens()
for _, screen := range screens {
screen := app.Screens.GetPrimary()
msg := fmt.Sprintf("Screen: %+v", screen)
application.InfoDialog().SetTitle("Primary Screen").SetMessage(msg).Show()
})
stateMenu.Add("Get Screens").OnClick(func(ctx *application.Context) {
screens := app.Screens.GetAll()
for _, screen := range screens {
🤖 Prompt for AI Agents
In v3/internal/commands/appimage_testfiles/main.go around lines 343 to 349,
replace calls to app.GetPrimaryScreen() and app.GetScreens() with the
corresponding methods from the new Screens manager to maintain consistency.
Review the return types of the new Screens manager methods and adjust the code
accordingly to handle any differences in return values or data structures.

Comment on lines 54 to 56
s.screenManager.LayoutScreens(screens)
return s.screenManager.Screens()
return s.screenManager.GetAll()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Return value now depends on internal state – guard against a nil screenManager

LayoutScreens() and GetAll() both dereference s.screenManager.
If the service is instantiated without an injected ScreenManager this will panic.

Consider a defensive check:

 if s.screenManager == nil {
-    return s.screenManager.GetAll()
+    return nil // or log/return an error
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In v3/examples/screen/screens.go around lines 54 to 56, the code calls
LayoutScreens and GetAll on s.screenManager without checking if s.screenManager
is nil, which can cause a panic if it is not initialized. Add a defensive check
to verify s.screenManager is not nil before calling these methods, and handle
the nil case appropriately to prevent runtime panics.

Comment on lines +17 to +20
kbm.app.keyBindingsLock.Lock()
defer kbm.app.keyBindingsLock.Unlock()
kbm.app.keyBindings[accelerator] = callback
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Nil-map safety & duplicate detection

Add assumes app.keyBindings is already initialised.
If an external caller instantiates App without the internal initialiser, this will panic.

func (kbm *KeyBindingManager) Add(accelerator string, callback func(window *WebviewWindow)) {
-    kbm.app.keyBindingsLock.Lock()
-    defer kbm.app.keyBindingsLock.Unlock()
-    kbm.app.keyBindings[accelerator] = callback
+    if accelerator == "" || callback == nil {
+        return // silently ignore invalid input
+    }
+    kbm.app.keyBindingsLock.Lock()
+    defer kbm.app.keyBindingsLock.Unlock()
+    if kbm.app.keyBindings == nil {
+        kbm.app.keyBindings = make(map[string]func(*WebviewWindow))
+    }
+    kbm.app.keyBindings[accelerator] = callback
 }
🤖 Prompt for AI Agents
In v3/pkg/application/key_binding_manager.go around lines 17 to 20, the Add
method assumes that app.keyBindings map is already initialized, which can cause
a panic if it is nil. To fix this, add a nil check before using the map and
initialize it if nil. This ensures nil-map safety and prevents runtime panics
when adding key bindings.

Comment on lines +37 to +42
func (cmm *ContextMenuManager) Get(name string) (*ContextMenu, bool) {
cmm.app.contextMenusLock.Lock()
defer cmm.app.contextMenusLock.Unlock()
menu, exists := cmm.app.contextMenus[name]
return menu, exists
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use RLock for read-only access to maximise parallelism

Get only reads the map; using Lock blocks every other goroutine—even readers—while the map is consulted.

-   cmm.app.contextMenusLock.Lock()
-   defer cmm.app.contextMenusLock.Unlock()
+   cmm.app.contextMenusLock.RLock()
+   defer cmm.app.contextMenusLock.RUnlock()

Apply the same pattern to GetAll.

📝 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.

Suggested change
func (cmm *ContextMenuManager) Get(name string) (*ContextMenu, bool) {
cmm.app.contextMenusLock.Lock()
defer cmm.app.contextMenusLock.Unlock()
menu, exists := cmm.app.contextMenus[name]
return menu, exists
}
func (cmm *ContextMenuManager) Get(name string) (*ContextMenu, bool) {
cmm.app.contextMenusLock.RLock()
defer cmm.app.contextMenusLock.RUnlock()
menu, exists := cmm.app.contextMenus[name]
return menu, exists
}
🤖 Prompt for AI Agents
In v3/pkg/application/context_menu_manager.go around lines 37 to 42, the Get
method uses a full Lock for reading the contextMenus map, which unnecessarily
blocks other readers. Change the Lock to RLock and the Unlock to RUnlock to
allow multiple concurrent readers. Apply the same change to the GetAll method to
maximize parallelism during read-only access.

Comment on lines +45 to +54
func (cmm *ContextMenuManager) GetAll() []*ContextMenu {
cmm.app.contextMenusLock.Lock()
defer cmm.app.contextMenusLock.Unlock()

result := make([]*ContextMenu, 0, len(cmm.app.contextMenus))
for _, menu := range cmm.app.contextMenus {
result = append(result, menu)
}
return result
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Inefficient slice pre-allocation with write lock

GetAll currently (a) grabs a full write lock and (b) iterates twice (length + range).
Switch to RLock and append without pre-allocating—the map size is already known but the micro-optimisation isn’t worth the exclusive lock.

-   cmm.app.contextMenusLock.Lock()
-   defer cmm.app.contextMenusLock.Unlock()
-
-   result := make([]*ContextMenu, 0, len(cmm.app.contextMenus))
-   for _, menu := range cmm.app.contextMenus {
-       result = append(result, menu)
-   }
+   cmm.app.contextMenusLock.RLock()
+   defer cmm.app.contextMenusLock.RUnlock()
+
+   result := make([]*ContextMenu, 0, len(cmm.app.contextMenus))
+   for _, menu := range cmm.app.contextMenus {
+       result = append(result, menu)
+   }
📝 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.

Suggested change
func (cmm *ContextMenuManager) GetAll() []*ContextMenu {
cmm.app.contextMenusLock.Lock()
defer cmm.app.contextMenusLock.Unlock()
result := make([]*ContextMenu, 0, len(cmm.app.contextMenus))
for _, menu := range cmm.app.contextMenus {
result = append(result, menu)
}
return result
}
func (cmm *ContextMenuManager) GetAll() []*ContextMenu {
cmm.app.contextMenusLock.RLock()
defer cmm.app.contextMenusLock.RUnlock()
result := make([]*ContextMenu, 0, len(cmm.app.contextMenus))
for _, menu := range cmm.app.contextMenus {
result = append(result, menu)
}
return result
}
🤖 Prompt for AI Agents
In v3/pkg/application/context_menu_manager.go lines 45 to 54, replace the full
write lock with a read lock by using RLock and RUnlock instead of Lock and
Unlock. Remove the pre-allocation of the result slice and simply append each
menu item during iteration. This avoids holding an exclusive lock and eliminates
the unnecessary micro-optimization of pre-allocating the slice capacity.

Comment on lines +54 to +76
func (em *EventManager) OnApplicationEvent(eventType events.ApplicationEventType, callback func(event *ApplicationEvent)) func() {
eventID := uint(eventType)
em.app.applicationEventListenersLock.Lock()
defer em.app.applicationEventListenersLock.Unlock()
listener := &EventListener{
callback: callback,
}
em.app.applicationEventListeners[eventID] = append(em.app.applicationEventListeners[eventID], listener)
if em.app.impl != nil {
go func() {
defer handlePanic()
em.app.impl.on(eventID)
}()
}

return func() {
// lock the map
em.app.applicationEventListenersLock.Lock()
defer em.app.applicationEventListenersLock.Unlock()
// Remove listener
em.app.applicationEventListeners[eventID] = lo.Without(em.app.applicationEventListeners[eventID], listener)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Copy listener slice while locked to avoid future races

handleApplicationEvent copies listeners after releasing the RW-lock. If another goroutine adds/removes listeners the underlying array can still be mutated, leading to races. Copy the slice while the lock is held:

em.app.applicationEventListenersLock.RLock()
listeners := append([]*EventListener(nil), em.app.applicationEventListeners[event.Id]...)
em.app.applicationEventListenersLock.RUnlock()

Same applies to hooks.

🤖 Prompt for AI Agents
In v3/pkg/application/event_manager.go around lines 54 to 76, the listener slice
is accessed without copying it while holding the lock, which can cause race
conditions if the underlying array is mutated concurrently. To fix this, modify
the code that reads the listeners to copy the slice inside a read lock by
acquiring the RLock, copying the slice with append to a new slice, then
releasing the RLock before using the copy. Apply the same pattern for hooks to
ensure safe concurrent access.

Comment on lines +96 to +112
func (em *EventManager) dispatch(event *CustomEvent) {
listeners := em.app.wailsEventListeners

for _, window := range em.app.windows {
if event.IsCancelled() {
return
}
window.DispatchWailsEvent(event)
}

for _, listener := range listeners {
if event.IsCancelled() {
return
}
listener.DispatchWailsEvent(event)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Unsynchronised map & slice access can trigger data races

dispatch() walks em.app.windows and em.app.wailsEventListeners without acquiring the matching read-locks (windowsLock, wailsEventListenerLock).
While other goroutines (window creation/close, listener registration) mutate those collections, this will race under -race.

func (em *EventManager) dispatch(event *CustomEvent) {
-    listeners := em.app.wailsEventListeners
-
-    for _, window := range em.app.windows {
+    em.app.windowsLock.RLock()
+    for _, window := range em.app.windows {
         if event.IsCancelled() {
-            return
+            em.app.windowsLock.RUnlock()
+            return
         }
         window.DispatchWailsEvent(event)
     }
-    for _, listener := range listeners {
+    em.app.windowsLock.RUnlock()
+
+    em.app.wailsEventListenerLock.Lock()
+    listeners := slices.Clone(em.app.wailsEventListeners)
+    em.app.wailsEventListenerLock.Unlock()
+
+    for _, listener := range listeners {
         ...
     }
}

Please wrap the reads in the appropriate locks or copy the collections while locked.
Failing tests under the race detector will surface otherwise.

📝 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.

Suggested change
func (em *EventManager) dispatch(event *CustomEvent) {
listeners := em.app.wailsEventListeners
for _, window := range em.app.windows {
if event.IsCancelled() {
return
}
window.DispatchWailsEvent(event)
}
for _, listener := range listeners {
if event.IsCancelled() {
return
}
listener.DispatchWailsEvent(event)
}
}
func (em *EventManager) dispatch(event *CustomEvent) {
// Snapshot windows under RLock
em.app.windowsLock.RLock()
for _, window := range em.app.windows {
if event.IsCancelled() {
em.app.windowsLock.RUnlock()
return
}
window.DispatchWailsEvent(event)
}
em.app.windowsLock.RUnlock()
// Snapshot listeners under Lock
em.app.wailsEventListenerLock.Lock()
listeners := slices.Clone(em.app.wailsEventListeners)
em.app.wailsEventListenerLock.Unlock()
for _, listener := range listeners {
if event.IsCancelled() {
return
}
listener.DispatchWailsEvent(event)
}
}
🤖 Prompt for AI Agents
In v3/pkg/application/event_manager.go around lines 96 to 112, the dispatch
method accesses em.app.windows and em.app.wailsEventListeners without acquiring
the corresponding read locks (windowsLock and wailsEventListenerLock), causing
potential data races. To fix this, acquire the appropriate read locks before
accessing these collections, either by locking windowsLock before reading
em.app.windows and wailsEventListenerLock before reading
em.app.wailsEventListeners, or by copying the slices/maps while holding the
locks and then iterating over the copies after releasing the locks.

@popaprozac
Copy link

Really love this. Would window management under app.Window singular help minimize any confusion with Windows the platform?

@leaanthony
Copy link
Member Author

Really love this. Would window management under app.Window singular help minimize any confusion with Windows the platform?

Yeah I did wonder this myself and the more I think about it, the more it makes sense..... e.g. Window.Get()

Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
3.9% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bindings cli Documentation Improvements or additions to documentation Enhancement New feature or request Implemented in v3 Linux MacOS size:XXL This PR changes 1000+ lines, ignoring generated files. v3-alpha Windows
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants