Skip to content

Conversation

@leaanthony
Copy link
Member

@leaanthony leaanthony commented Dec 17, 2025

Summary

  • BREAKING: MessageDialog.Show() now returns the clicked button's label as a string
  • Enables synchronous dialog workflows matching Wails v2 behavior
  • All platforms now consistently block until the user clicks a button

Motivation

Closes #4792

The current callback-based approach doesn't allow returning values synchronously for critical application decisions. This change enables clean switch-based code:

result := app.Dialogs.Question().
    SetTitle("Confirm Exit").
    SetMessage("Save changes?").
    AddButton("Save").
    AddButton("Don't Save").
    AddButton("Cancel").
    Show()

switch result {
case "Save":
    saveChanges()
    app.Quit()
case "Don't Save":
    app.Quit()
case "Cancel":
    // Do nothing
}

Breaking Change

MessageDialog.Show() signature changed from:

func (d *MessageDialog) Show()

to:

func (d *MessageDialog) Show() string

Existing code that calls Show() without using the return value will continue to work. Code that was relying on Show() returning nothing will need to be updated.

Changes

  • Updated messageDialogImpl interface: show() now returns string
  • Updated MessageDialog.Show() to return the button label using InvokeSyncWithResult
  • Windows: Already synchronous, now returns the result string
  • macOS: Uses channel to block and return result from callback
  • Linux: Uses channel to block and return result from callback
  • Android/iOS: Updated stubs to match interface (return empty string)

Button callbacks are still called for backwards compatibility.

Test plan

  • Test on Windows with Info/Question/Warning/Error dialogs
  • Test on macOS with Info/Question/Warning/Error dialogs
  • Test on Linux with Info/Question/Warning/Error dialogs
  • Verify button callbacks still work when defined
  • Verify return value matches clicked button label

🤖 Generated with Claude Code

Summary by CodeRabbit

  • BREAKING CHANGES

    • MessageDialog.Show() now returns the clicked button label and an error; callers must handle the result.
  • New Features

    • Fluent button builder and chaining (WithButton, WithDefaultButton, WithCancelButton).
    • Buttons/Result pattern for multi-button dialogs and consolidated result handling.
  • Bug Fixes

    • Escape/window-close now activates dialog cancel on Linux.
  • Documentation

    • Examples and guides updated for the new dialog Result/Show usage.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 17, 2025

Walkthrough

MessageDialog.Show() became synchronous: it now returns the clicked button label and an error ((string, error)). New builder-style button helpers (WithButton, WithDefaultButton, WithCancelButton, AddButton) were added. Platform implementations, docs, examples, and GTK escape/close handling were updated to propagate and return the selected label and errors.

Changes

Cohort / File(s) Summary
Core API & Changelog
v3/pkg/application/dialogs.go, v3/UNRELEASED_CHANGELOG.md
MessageDialog.Show() / internal messageDialogImpl.show() changed to return (string, error); added AddButton, WithButton, WithDefaultButton, WithCancelButton and inline doc comments; changelog updated.
Platform implementations
v3/pkg/application/dialogs_darwin.go, v3/pkg/application/dialogs_windows.go, v3/pkg/application/dialogs_linux.go, v3/pkg/application/dialogs_android.go, v3/pkg/application/dialogs_ios.go
Each platform show() now returns (string, error) and captures the clicked button label; Windows returns errors instead of calling fatal handlers; macOS, Linux, Windows wire callbacks to send label result; Android/iOS stubs updated to (string, error).
Linux GTK mapping
v3/pkg/application/linux_cgo.go, v3/pkg/application/linux_purego.go
Track cancel button index; map Escape/window-close responses to configured cancel button index when present to ensure Escape triggers cancel semantics.
Docs & Examples
docs/src/content/docs/**, v3/examples/dialogs/main.go
All docs and examples converted from per-button OnClick callbacks to Buttons/Result or Show-return patterns; examples updated to use fluent builder methods and to switch on returned label; many Show() calls adjusted to handle or ignore returned values.
Reference / Concept pages
docs/src/content/docs/reference/*.mdx, docs/src/content/docs/concepts/*.mdx
API references and lifecycle docs updated to reflect Buttons(...), Result()/Show() signatures and button-level IsDefault/IsCancel usage; examples updated accordingly.

Sequence Diagram(s)

sequenceDiagram
    participant App as Application Code
    participant API as MessageDialog (Show)
    participant Impl as Platform Impl (darwin/windows/linux/...)
    participant UI as Native UI

    App->>API: call Show()
    API->>Impl: InvokeSyncWithResultAndError / call impl.show()
    Impl->>UI: present dialog
    UI-->>UI: user selects button or presses Escape/Close
    UI->>Impl: deliver selection event
    Impl->>Impl: run button callback (if any) and capture Label
    Impl-->>API: return (Label, nil) or ("", err)
    API-->>App: return (Label, error)
    
    rect rgb(230,248,230)
      Note over Impl,UI: Linux maps Escape/Close to configured Cancel button when present
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

Enhancement, v3-alpha, Documentation, go, Windows, MacOS, Linux, size:L

Suggested reviewers

  • ndianabasi

Poem

🐰 I hopped into code where the buttons all dwell,

Now each little click has a clear tale to tell.
I chained WithButton, set Default and Cancel too,
Show returns the answer — no callbacks askew.
🥕 A synchronous nibble for every GUI view.

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: MessageDialog.Show() now returns the clicked button label. It accurately reflects the primary objective of the PR.
Description check ✅ Passed The description provides a clear summary of changes, motivation, breaking change notice, implementation details across platforms, and a test plan. All major sections from the template are addressed.
Linked Issues check ✅ Passed The PR addresses issue #4792 by implementing synchronous dialog behavior where Show() returns the clicked button label as a string, enabling switch-based control flow as requested.
Out of Scope Changes check ✅ Passed While the PR includes broader API changes (Buttons(), WithButton(), WithDefaultButton(), WithCancelButton(), Result(), and documentation updates), these extend the scope beyond the core requirement but appear justified by the need to provide fluent chaining alternatives and comprehensive platform support.

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

❤️ Share

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

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Dec 17, 2025

Deploying wails with  Cloudflare Pages  Cloudflare Pages

Latest commit: cfd7cd0
Status: ✅  Deploy successful!
Preview URL: https://baec7345.wails.pages.dev
Branch Preview URL: https://vk-4792-sync-dialog-show.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: 0

🧹 Nitpick comments (2)
v3/pkg/application/dialogs_windows.go (2)

68-74: Consider case-insensitive button label matching.

The button callback matching uses exact string comparison, which could fail if button labels differ in casing (e.g., "OK" vs "Ok"). Since Windows MessageBox only supports standard button combinations and the responses array uses specific casing ("Ok", "Cancel", "Yes", "No", etc.), developers might define buttons with different casing expecting them to match.

Apply this diff for more robust matching:

 // Check if there's a callback for the button pressed
 for _, buttonInDialog := range m.dialog.Buttons {
-	if buttonInDialog.Label == result {
+	if strings.EqualFold(buttonInDialog.Label, result) {
 		if buttonInDialog.Callback != nil {
 			buttonInDialog.Callback()
 		}
 	}
 }

62-66: Consider a clearer default value for unexpected button codes.

The default value "Error" (line 63) might be misleading since it doesn't represent an actual error condition, but rather an unexpected button return value from the Windows API. Consider using a more descriptive value like "Unknown" or an empty string to avoid confusion.

Apply this diff to clarify the default:

 // This maps MessageBox return values to strings
 responses := []string{"", "Ok", "Cancel", "Abort", "Retry", "Ignore", "Yes", "No", "", "", "Try Again", "Continue"}
-result := "Error"
+result := ""  // Default for unexpected button codes
 if int(button) < len(responses) {
 	result = responses[button]
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a13a426 and 5e39aed.

📒 Files selected for processing (7)
  • v3/UNRELEASED_CHANGELOG.md (1 hunks)
  • v3/pkg/application/dialogs.go (2 hunks)
  • v3/pkg/application/dialogs_android.go (1 hunks)
  • v3/pkg/application/dialogs_darwin.go (2 hunks)
  • v3/pkg/application/dialogs_ios.go (2 hunks)
  • v3/pkg/application/dialogs_linux.go (2 hunks)
  • v3/pkg/application/dialogs_windows.go (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
v3/pkg/application/dialogs_linux.go (3)
v3/pkg/application/mainthread.go (1)
  • InvokeAsync (82-87)
v2/pkg/menu/menuitem.go (1)
  • Label (272-277)
v2/pkg/menu/callback.go (1)
  • Callback (8-8)
v3/pkg/application/dialogs.go (1)
v3/pkg/application/mainthread.go (1)
  • InvokeSyncWithResult (34-44)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (macos-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
  • GitHub Check: semgrep/ci
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (8)
v3/UNRELEASED_CHANGELOG.md (1)

23-23: LGTM!

Clear changelog entry documenting the API change with proper issue reference.

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

68-71: LGTM!

Stub implementation correctly matches the updated interface signature. The empty string return is appropriate for an unimplemented platform.

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

68-71: LGTM!

Consistent stub implementation matching the updated interface.

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

68-70: LGTM!

Interface signature correctly updated to return string, enabling synchronous result capture across all platform implementations.


109-117: LGTM!

Well-documented API change. The use of InvokeSyncWithResult properly ensures thread-safe synchronous execution and result propagation from the platform-specific implementation.

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

374-453: Pattern is correct and deadlock-free—remove verification concern.

The implementation safely uses InvokeAsync to schedule modal dialog setup on the main thread. Unlike a nested InvokeSyncWithResult that would block, InvokeAsync is fire-and-forget, allowing C.dialogRunModal() to run the Cocoa event loop and process the button callback. The buffered channel prevents leaks, and the callback fires normally when the user interacts with the dialog. No issues.

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

30-62: Verify thread safety of nested InvokeAsync/InvokeSyncWithResult pattern on Linux.

show() is invoked via InvokeSyncWithResult (dialogs.go:116), which dispatches to the main thread and blocks. Inside show(), InvokeAsync (line 44) dispatches another task and then blocks on <-resultChan (line 61). If the calling thread is the main thread, the outer dispatch blocks the event loop while the inner dispatch awaits processing—this creates a deadlock scenario. GTK documentation explicitly warns against this reentrancy pattern and suggests avoiding main loop recursion.

Windows uses a synchronous approach (no nested dispatch), which is safer. Consider either:

  1. Documenting that Show() must only be called from worker threads, or
  2. Adopting the synchronous execution pattern used on Windows for consistency and safety.
v3/pkg/application/dialogs_windows.go (1)

33-33: LGTM! Method signature updated correctly.

The signature change from void to string return type is clean and properly implemented with the return statement on line 75.

Also applies to: 75-75

BREAKING CHANGE: MessageDialog.Show() now returns a string instead of void.

Make MessageDialog.Show() return the clicked button's label as a string,
enabling synchronous dialog workflows. This allows developers to use
switch statements on the result instead of callbacks for decision-making
flows.

All platforms now consistently block until the user clicks a button and
return the label. Button callbacks are still called for backwards
compatibility.

Closes #4792

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@leaanthony leaanthony force-pushed the vk/4792-sync-dialog-show branch from 5e39aed to 3d1c650 Compare December 17, 2025 10:16
Update documentation and examples to demonstrate the new synchronous
Show() return value pattern instead of callbacks.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Copy link

@ndianabasi ndianabasi left a comment

Choose a reason for hiding this comment

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

I was expecting the implementation to use a method like ShowSync to avoid breaking changes and to still give users the asynchronous, non-blocking behaviour.

I will test this when I'm settled in.

@ndianabasi
Copy link

@leaanthony Are there errors which could arise while Show is being called and which should be returned to the user? So that the return type of Show becomes (string, error)?

@ndianabasi
Copy link

This is my first use case:

		 result := app.Dialog.Question().
			SetTitle("Migration Warning").
			SetMessage(fmt.Sprintf("%d migrations are available. Do you want to run them now?", len(pending))).
			SetDefaultButton(&application.Button{
				Label: "Run",
				IsCancel: false,
				IsDefault: true,
			}).
			SetCancelButton(&application.Button{
				Label: "Cancel",
				IsCancel: true,
				IsDefault: false,
			}).
			Show()

		switch result {
		case "Run":
			doMigrations()
		case "Cancel":
			log.Println("Migration cancelled by user.")
			app.Quit()
		}

On darwin, the buttons did not show up as configured. Instead a generic button with an OK label was displayed.

@ndianabasi
Copy link

ndianabasi commented Dec 18, 2025

func (d *application.MessageDialog) AddButton(s string) *application.Button

Above does not allow chaining of the AddButton calls. So, below is not possible:

result := app.Dialog.Question().
   SetTitle("Migration Warning").
   SetMessage(fmt.Sprintf("%d migrations are available. Do you want to run them now?", len(pending))).
   AddButton("Run").
   AddButton("Cancel").
   Show()

@ndianabasi
Copy link

I refactored to:

		dialog := app.Dialog.Question().
			SetTitle("Migration Warning").
			SetMessage(fmt.Sprintf("%d migrations are available. Do you want to run them now?", len(pending)))

		dialog.AddButton("Run")
		dialog.AddButton("Cancel")

		switch dialog.Show() {
		case "Run":
			doMigrations()
		case "Cancel":
			log.Println("Migration cancelled by user.")
			app.Quit()
		}

And the dialog appeared with the configured buttons:

image

However, they look generic since the cancel or default buttons are not set.

@leaanthony
Copy link
Member Author

Fixing the signature and AddButton 👍

leaanthony and others added 3 commits December 20, 2025 15:58
Change the signature of MessageDialog.Show() from returning just a
string to returning (string, error) for consistency with other dialog
methods and proper error handling.

- Windows: Return error instead of calling handleFatalError()
- macOS/Linux: Return nil error (no error paths currently)
- Android/iOS: Return nil error (stub implementations)
- Update documentation with new signature examples

This is a breaking change for v3-alpha.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Add MessageDialog.WithButton() method that returns *MessageDialog
for builder pattern chaining. This complements AddButton() which
returns *Button for when you need to configure the button.

Example usage:
  result, _ := app.Dialog.Question().
      SetMessage("Choose").
      WithButton("Option 1").
      WithButton("Option 2").
      Show()

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
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: 3

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e39aed and 8e959ba.

📒 Files selected for processing (9)
  • docs/src/content/docs/features/dialogs/message.mdx (9 hunks)
  • v3/UNRELEASED_CHANGELOG.md (1 hunks)
  • v3/examples/dialogs/main.go (1 hunks)
  • v3/pkg/application/dialogs.go (3 hunks)
  • v3/pkg/application/dialogs_android.go (1 hunks)
  • v3/pkg/application/dialogs_darwin.go (2 hunks)
  • v3/pkg/application/dialogs_ios.go (2 hunks)
  • v3/pkg/application/dialogs_linux.go (2 hunks)
  • v3/pkg/application/dialogs_windows.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • v3/pkg/application/dialogs_windows.go
🧰 Additional context used
🧬 Code graph analysis (3)
v3/pkg/application/dialogs.go (1)
v3/pkg/application/mainthread.go (1)
  • InvokeSyncWithResultAndError (58-68)
v3/examples/dialogs/main.go (2)
v3/internal/go-common-file-dialog/cfd/CommonFileDialog.go (1)
  • Dialog (6-38)
v3/internal/runtime/desktop/@wailsio/runtime/src/dialogs.ts (1)
  • Question (164-164)
v3/pkg/application/dialogs_linux.go (3)
v3/pkg/application/mainthread.go (1)
  • InvokeAsync (82-87)
v2/pkg/menu/menuitem.go (1)
  • Label (272-277)
v2/pkg/menu/callback.go (1)
  • Callback (8-8)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
  • GitHub Check: Analyze (go)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (10)
v3/pkg/application/dialogs_ios.go (1)

68-71: LGTM - Stub implementation aligns with the new interface.

The placeholder correctly implements the updated show() (string, error) signature. The empty string return is appropriate for an unimplemented platform.

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

68-71: LGTM - Consistent with the interface update.

The Android stub correctly implements the updated signature, matching the iOS implementation pattern.

v3/UNRELEASED_CHANGELOG.md (1)

20-25: LGTM - Changelog entries are clear and complete.

The breaking change is properly documented with the new signature (string, error), and the WithButton() addition is noted. References to issue #4792 are correctly included.

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

30-61: LGTM - Channel-based blocking pattern is correct.

The implementation properly:

  • Uses a buffered channel to prevent goroutine leaks
  • Extracts the button label and sends it through the channel
  • Maintains backwards compatibility by still invoking button callbacks
  • Blocks until the user responds

The pattern is consistent with the Darwin implementation.

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

374-453: LGTM - Darwin implementation is well-structured.

The implementation:

  • Correctly integrates with the existing callback infrastructure
  • Uses a buffered channel for safe result passing
  • Maintains backwards compatibility by invoking button callbacks
  • Properly handles the reversed button order for macOS

The pattern is consistent with the Linux implementation.

v3/pkg/application/dialogs.go (3)

68-70: LGTM - Interface correctly updated.

The interface change to show() (string, error) properly propagates the return type to all platform implementations.


109-118: LGTM - Well-documented synchronous API.

The documentation clearly explains:

  • The method blocks until button click
  • Button callbacks are still invoked for backwards compatibility
  • Error is returned if the dialog could not be displayed

Using InvokeSyncWithResultAndError (from mainthread.go) correctly handles the synchronization pattern.


125-140: LGTM - Clear distinction between AddButton and WithButton.

Good API design:

  • AddButton() returns *Button for configuration (default, cancel, callback)
  • WithButton() returns *MessageDialog for fluent chaining when no configuration is needed

This addresses the issue mentioned in the PR comments about AddButton chaining not being supported.

docs/src/content/docs/features/dialogs/message.mdx (2)

130-160: LGTM - Documentation clearly demonstrates the new API.

The examples properly show:

  • Error handling with result, err := dialog.Show()
  • Switch-based result handling
  • The Buttons slice access for SetDefaultButton(dialog.Buttons[0])

Clear and comprehensive.


247-285: LGTM - Clear distinction between WithButton and AddButton usage.

The documentation effectively explains when to use each:

  • WithButton() for simple chaining without configuration
  • AddButton() when button configuration is needed (e.g., SetAsDefault(), SetAsCancel())

Comment on lines 89 to 93
// Show() returns the clicked button's label
result := dialog.Show()
if result == "Yes" {
app.Quit()
}
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 | 🔴 Critical

Fix: Handle the error return from Show().

Show() now returns (string, error), but this code only captures a single return value. This will cause a compilation error.

🔎 Proposed fix
-		// Show() returns the clicked button's label
-		result := dialog.Show()
-		if result == "Yes" {
+		// Show() returns the clicked button's label
+		result, _ := dialog.Show()
+		if result == "Yes" {
 			app.Quit()
 		}
📝 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
// Show() returns the clicked button's label
result := dialog.Show()
if result == "Yes" {
app.Quit()
}
// Show() returns the clicked button's label
result, _ := dialog.Show()
if result == "Yes" {
app.Quit()
}
🤖 Prompt for AI Agents
In v3/examples/dialogs/main.go around lines 89 to 93, Show() now returns
(string, error) but the code only captures one value; update the call to receive
both result and err, check err immediately (handle by logging, showing an error
dialog, or returning) and only compare result == "Yes" if err == nil; ensure
appropriate control flow after an error (e.g., skip quitting or propagate the
error).

Comment on lines 103 to 109
// Show() returns the clicked button's label - use switch for multiple options
switch dialog.Show() {
case "📥 Download":
app.Dialog.Info().SetMessage("Downloading...").Show()
case "Cancel":
// User cancelled
}
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 | 🔴 Critical

Fix: Handle the error return from Show().

Same issue - switch dialog.Show() won't compile when Show() returns (string, error).

🔎 Proposed fix
 		// Show() returns the clicked button's label - use switch for multiple options
-		switch dialog.Show() {
+		result, _ := dialog.Show()
+		switch result {
 		case "📥 Download":
 			app.Dialog.Info().SetMessage("Downloading...").Show()
 		case "Cancel":
 			// User cancelled
 		}
📝 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
// Show() returns the clicked button's label - use switch for multiple options
switch dialog.Show() {
case "📥 Download":
app.Dialog.Info().SetMessage("Downloading...").Show()
case "Cancel":
// User cancelled
}
// Show() returns the clicked button's label - use switch for multiple options
result, _ := dialog.Show()
switch result {
case "📥 Download":
app.Dialog.Info().SetMessage("Downloading...").Show()
case "Cancel":
// User cancelled
}
🤖 Prompt for AI Agents
In v3/examples/dialogs/main.go around lines 103 to 109, change the call to
dialog.Show() to receive both return values (label, err), check the error before
using the label (e.g., if err != nil { handle or log the error and
return/continue }), and then switch on the label variable instead of calling
dialog.Show() directly so compilation succeeds and errors are handled.

leaanthony and others added 2 commits December 20, 2025 16:22
… key

- Add WithDefaultButton() method for fluent API to add a button marked as
  default (Enter key triggers it)
- Add WithCancelButton() method for fluent API to add a button marked as
  cancel (Escape key triggers it)
- Fix IsCancel button not responding to Escape key on Linux (GTK)
  - GTK returns GTK_RESPONSE_DELETE_EVENT (-4) on Escape/window close
  - Now correctly maps this to the cancel button's index
- Update documentation with new API methods and examples
- Add example demonstrating the fluent Cancel/Default API

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Address CodeRabbit review comment - switch dialog.Show() doesn't
compile since Show() now returns (string, error).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@ndianabasi
Copy link

ndianabasi commented Dec 22, 2025

The app seems to be quitted whenever any button within the dialog is clicked. I disabled the app.Quit() line in the "Cancel" case and the result was still the same. If I disable the entire dialog.Show() call including the error and result handling, the app runs as expected. But, of course, the dialog won't appear.

This is observed within the Wails3 starter application.

	app.Event.OnApplicationEvent(events.Common.ApplicationStarted, func(event *application.ApplicationEvent) {
		dialog := app.Dialog.Question().
			SetTitle("Migration Warning").
			SetMessage(fmt.Sprintf("%d migrations are available. Do you want to run them now?", 2))

		dialog.AddButton("Run").SetAsDefault()
		dialog.AddButton("Cancel").SetAsCancel()

		if result, err := dialog.Show(); err != nil {
			log.Printf("error while show migration-warning dialog: %+v\n", err)
			return
		} else {
			switch result {
			case "Run":
				log.Println("Migration is running")
			case "Cancel":
				log.Println("Migration cancelled by user.")
				app.Quit()
			}
		}
	})

As you can see from the screen recording below, immediately after Migration is running is logged, the line Need documentation? Run: wails3 docs appears which indicates that the app was quitted.

Screen.Recording.2025-12-22.at.09.24.12.mov
 Wails (v3.0.0-alpha.47)  Wails Doctor 
                                                                                                                                                                                                                                
# System 

┌──────────────────────────────────────────────────┐
| Name          | MacOS                            |
| Version       | 14.6.1                           |
| ID            | 23G93                            |
| Branding      | Sonoma                           |
| Platform      | darwin                           |
| Architecture  | arm64                            |
| Apple Silicon | true                             |
| CPU           | Apple M1 Pro                     |
| CPU 1         | Apple M1 Pro                     |
| CPU 2         | Apple M1 Pro                     |
| GPU           | 16 cores, Metal Support: Metal 3 |
| Memory        | 16 GB                            |
└──────────────────────────────────────────────────┘

# Build Environment 

┌─────────────────────────────────────────────────────────────────────────────────────────────────────┐
| Wails CLI      | v3.0.0-alpha.47                                                                    |
| Go Version     | go1.25.4                                                                           |
| -buildmode     | exe                                                                                |
| -compiler      | gc                                                                                 |
| CGO_CFLAGS     |                                                                                    |
| CGO_CPPFLAGS   |                                                                                    |
| CGO_CXXFLAGS   |                                                                                    |
| CGO_ENABLED    | 1                                                                                  |
| CGO_LDFLAGS    |                                                                                    |
| DefaultGODEBUG | containermaxprocs=0,decoratemappings=0,tlssha1=1,updatemaxprocs=0,x509sha256skid=0 |
| GOARCH         | arm64                                                                              |
| GOARM64        | v8.0                                                                               |
| GOOS           | darwin                                                                             |
└─────────────────────────────────────────────────────────────────────────────────────────────────────┘

# Dependencies 

┌──────────────────────────────────────────────────────────────────────────────┐
| Xcode cli tools | 2408                                                       |
| npm             | 11.3.0                                                     |
| *NSIS           | v3.11                                                      |
| docker          | *Docker version 28.0.4, build b8034c0 (daemon not running) |
|                                                                              |
└────────────────────────── * - Optional Dependency ───────────────────────────┘

# Checking for issues 

 SUCCESS  No issues found

# Diagnosis 

 SUCCESS  Your system is ready for Wails development!

@leaanthony
Copy link
Member Author

@ndianabasi Are you able to reproduce with a single file? Do the examples work for you?

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: 4

🧹 Nitpick comments (4)
v3/pkg/application/linux_cgo.go (2)

1900-1912: Consider handling multiple cancel buttons explicitly.

If multiple buttons are marked with IsCancel, the current implementation will use the last one encountered. While this is likely an edge case, you might want to either:

  • Document this behavior, or
  • Validate that only one cancel button exists and log a warning if multiple are found.

1916-1923: LGTM! Consider defining a constant for the magic number.

The logic correctly maps the GTK delete event (Escape key or window close button) to the cancel button when defined. The comment is helpful.

For improved readability and maintainability, consider defining a constant at the file or package level:

🔎 Suggested refactor
+const (
+	GTK_RESPONSE_DELETE_EVENT = -4
+)
+
 func runQuestionDialog(parent pointer, options *MessageDialog) int {
 	// ... existing code ...
 	response := int(C.gtk_dialog_run((*C.GtkDialog)(unsafe.Pointer(dialog))))
 
-	// GTK_RESPONSE_DELETE_EVENT (-4) is triggered by Escape key or window close button.
+	// GTK_RESPONSE_DELETE_EVENT is triggered by Escape key or window close button.
 	// If a cancel button is defined, return its index instead.
-	if response == -4 && cancelButtonIndex >= 0 {
+	if response == GTK_RESPONSE_DELETE_EVENT && cancelButtonIndex >= 0 {
 		return cancelButtonIndex
 	}
 	return response
 }
v3/pkg/application/linux_purego.go (2)

1178-1190: Consider handling multiple cancel buttons explicitly.

Similar to the CGO implementation, if multiple buttons are marked with IsCancel, the last one will be used. Consider documenting this behavior or validating that only one cancel button exists.


1193-1200: LGTM! Consider defining a constant for consistency.

The implementation correctly mirrors the CGO version. For consistency and maintainability across both Linux implementations, consider defining the GTK response constant:

🔎 Suggested refactor

Add to the constants section (around line 28-63):

 const (
 	nilPointer pointer = 0
 )
 
 const (
 	GSourceRemove int = 0
 
+	// GTK dialog response codes
+	GTK_RESPONSE_DELETE_EVENT = -4
+
 	// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/gdkwindow.h#L121
 	GdkHintMinSize = 1 << 1
 	// ... rest of constants

Then update the condition:

 	response := gtkDialogRun(dialog)
 
-	// GTK_RESPONSE_DELETE_EVENT (-4) is triggered by Escape key or window close button.
+	// GTK_RESPONSE_DELETE_EVENT is triggered by Escape key or window close button.
 	// If a cancel button is defined, return its index instead.
-	if response == -4 && cancelButtonIndex >= 0 {
+	if response == GTK_RESPONSE_DELETE_EVENT && cancelButtonIndex >= 0 {
 		return cancelButtonIndex
 	}
 	return response
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e959ba and 0f3ba54.

📒 Files selected for processing (13)
  • docs/src/content/docs/changelog.mdx
  • docs/src/content/docs/concepts/lifecycle.mdx
  • docs/src/content/docs/features/browser/integration.mdx
  • docs/src/content/docs/features/dialogs/file.mdx
  • docs/src/content/docs/features/dialogs/message.mdx
  • docs/src/content/docs/features/dialogs/overview.mdx
  • docs/src/content/docs/reference/application.mdx
  • docs/src/content/docs/reference/dialogs.mdx
  • v3/UNRELEASED_CHANGELOG.md
  • v3/examples/dialogs/main.go
  • v3/pkg/application/dialogs.go
  • v3/pkg/application/linux_cgo.go
  • v3/pkg/application/linux_purego.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • v3/examples/dialogs/main.go
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-02-25T17:13:05.652Z
Learnt from: fbbdev
Repo: wailsapp/wails PR: 4100
File: v3/internal/runtime/desktop/@wailsio/runtime/src/dialogs.ts:222-228
Timestamp: 2025-02-25T17:13:05.652Z
Learning: The user has chosen to maintain the function name `Error` in the dialogs module despite it shadowing the global Error constructor. This is a deliberate choice due to documentation generator compatibility concerns, and the risk is mitigated by using `window.Error` when referencing the global constructor and by the strictly type-checked modular nature of the code.

Applied to files:

  • docs/src/content/docs/features/browser/integration.mdx
📚 Learning: 2025-12-29T08:02:06.122Z
Learnt from: popaprozac
Repo: wailsapp/wails PR: 4839
File: docs/src/content/docs/reference/window.mdx:616-620
Timestamp: 2025-12-29T08:02:06.122Z
Learning: In Wails v3, document window creation using app.Window.New() and app.Window.NewWithOptions(...). Do not show or reference app.NewWebviewWindow() or app.NewWebviewWindowWithOptions(...). The Application struct exposes a Window field of type *WindowManager that provides these methods. When updating docs, replace examples accordingly and mention that WindowManager methods create and configure new windows.

Applied to files:

  • docs/src/content/docs/features/browser/integration.mdx
  • docs/src/content/docs/features/dialogs/file.mdx
  • docs/src/content/docs/features/dialogs/message.mdx
  • docs/src/content/docs/reference/dialogs.mdx
  • docs/src/content/docs/concepts/lifecycle.mdx
  • docs/src/content/docs/reference/application.mdx
  • docs/src/content/docs/features/dialogs/overview.mdx
  • docs/src/content/docs/changelog.mdx
📚 Learning: 2025-12-13T19:52:13.812Z
Learnt from: leaanthony
Repo: wailsapp/wails PR: 4783
File: v3/pkg/events/events.go:72-100
Timestamp: 2025-12-13T19:52:13.812Z
Learning: In Wails v3, the linux:WindowLoadChanged event was intentionally removed as a breaking change and replaced with four granular WebKit2 load events: linux:WindowLoadStarted, linux:WindowLoadRedirected, linux:WindowLoadCommitted, and linux:WindowLoadFinished. Users should migrate to linux:WindowLoadFinished for detecting when the WebView has finished loading.

Applied to files:

  • docs/src/content/docs/changelog.mdx
🧬 Code graph analysis (1)
v3/pkg/application/dialogs.go (1)
v3/pkg/application/mainthread.go (1)
  • InvokeSyncWithResultAndError (58-68)
🪛 LanguageTool
docs/src/content/docs/changelog.mdx

[uncategorized] ~244-~244: The operating system from Apple is written “macOS”.
Context: ...ed issue where AssetServer can crash on MacOS in [#4576](https://github.com/wailsapp/...

(MAC_OS)


[uncategorized] ~258-~258: The operating system from Apple is written “macOS”.
Context: ...ed issue where AssetServer can crash on MacOS in [#4576](https://github.com/wailsapp/...

(MAC_OS)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Run Go Tests v3 (macos-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
  • GitHub Check: Analyze (go)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (22)
docs/src/content/docs/changelog.mdx (1)

82-82: LGTM! Clear and accurate API documentation.

The changelog entry accurately describes the new dialog API surface, including the shift from per-button callbacks to Buttons(...) with Show() error and Result() (string, error), and correctly references the new methods for folder selection and configuration.

v3/pkg/application/dialogs.go (3)

69-69: LGTM! Interface signature correctly updated.

The interface change from show() to show() (string, error) aligns with the PR objectives to enable synchronous dialog workflows and proper error handling.


109-118: LGTM! Well-documented synchronous behavior.

The updated Show() method correctly:

  • Returns the clicked button label and error
  • Documents the blocking behavior clearly
  • Notes backward compatibility with callbacks
  • Uses InvokeSyncWithResultAndError to ensure execution on the main thread

125-164: Excellent builder pattern implementation.

The new builder methods are well-designed:

  • Clear documentation for each method
  • WithButton() for simple chaining
  • WithDefaultButton() and WithCancelButton() properly clear existing flags to avoid conflicts
  • Consistent fluent API pattern
docs/src/content/docs/features/browser/integration.mdx (3)

157-169: LGTM! Example correctly demonstrates new dialog API.

The updated example properly shows:

  • Configuring buttons via Buttons(application.Button{...})
  • Using IsDefault and IsCancel flags
  • Checking the result with Result() instead of per-button callbacks
  • Handling the "Open" action based on the returned label

450-461: LGTM! Consistent with the new dialog pattern.

This example correctly mirrors the updated API usage pattern shown elsewhere in the documentation.


504-507: LGTM! Proper handling of unused return value.

Using _ = to suppress the unused return value warning is the correct approach when the error doesn't need to be handled (dialog display errors are typically logged internally).

v3/UNRELEASED_CHANGELOG.md (2)

20-26: LGTM! Clear and accurate changelog entries.

The changelog correctly documents:

  • New builder methods with proper categorization under "Added"
  • Breaking change clearly marked under "Changed"
  • Proper issue/PR references and attribution

39-39: LGTM! Platform-specific fix documented.

The Linux GTK fix for Escape key handling on cancel buttons is properly documented.

docs/src/content/docs/features/dialogs/file.mdx (3)

151-159: LGTM! Overwrite confirmation correctly updated.

The example properly demonstrates:

  • Configuring buttons with Buttons()
  • Setting default and cancel buttons via flags
  • Using Result() to check user choice
  • Early return on error or non-overwrite choice

265-269: LGTM! Consistent error handling pattern.

Using _ = to suppress unused return values from error dialogs is appropriate when dialog display errors don't need explicit handling.

Also applies to: 314-317, 321-324


298-306: LGTM! Comprehensive examples demonstrate the new API well.

All examples consistently show:

  • Button configuration via Buttons(application.Button{...})
  • Proper use of IsDefault and IsCancel flags
  • Result-based decision making with Result()
  • Error handling throughout

The examples cover real-world use cases (validation, batch processing, folder selection, import) and provide clear guidance for developers.

Also applies to: 346-384, 422-430, 473-495

docs/src/content/docs/reference/application.mdx (2)

272-290: LGTM - Message dialog examples updated correctly.

The examples properly demonstrate the new Show() return value pattern using _ = dialog.Show() to acknowledge the return value. The builder pattern with SetTitle(), SetMessage(), and Show() is clear and consistent.


295-313: LGTM - Question dialog example demonstrates the new API well.

The example correctly shows:

  • Using Buttons(...) with application.Button structs
  • Setting IsDefault and IsCancel properties inline
  • Using Result() to get the clicked button label synchronously
docs/src/content/docs/concepts/lifecycle.mdx (2)

280-291: LGTM - ShouldQuit example updated correctly.

The example demonstrates the synchronous dialog workflow for quit confirmation using the new Buttons() and Result() API. The pattern of comparing result == "Quit" is clear and intuitive.


508-527: LGTM - Window closing hook example is well-structured.

The example clearly shows:

  • Three-button dialog with Save/Discard/Cancel options
  • Using IsDefault and IsCancel flags appropriately
  • Switch statement on result for control flow
  • Calling e.Cancel() when user chooses Cancel
docs/src/content/docs/features/dialogs/overview.mdx (3)

18-42: LGTM - Quick start examples are clear and functional.

The examples demonstrate the key patterns:

  • Simple info dialog with _ = ...Show()
  • Question dialog with Buttons() and inline Result() check
  • File dialog unchanged

223-261: LGTM - Custom buttons documentation is thorough.

Good coverage of:

  • Buttons(...) API with multiple buttons
  • IsDefault and IsCancel properties
  • Result() usage with switch statement
  • Example showing safe default (Cancel as default for destructive action)

350-372: LGTM - Destructive action confirmation pattern is well-documented.

The example properly shows error handling with dialog.Result() and the pattern of returning early if the user didn't confirm or if an error occurred.

docs/src/content/docs/reference/dialogs.mdx (3)

420-450: LGTM - Method documentation is clear and complete.

The documentation for Buttons(), Default(), and Cancel() methods is well-structured with proper signatures and examples.


460-474: Verify consistency with PR objectives regarding Show() behavior.

The documentation states:

  • Line 462: Show() is "non-blocking"
  • Line 470: Result() is "blocking"

However, the PR objectives state: "all platforms now block until the user clicks a button" for Show(). This appears to contradict the "non-blocking" description. Please verify and align the documentation with the actual implementation.

If Show() is indeed non-blocking (just displays the dialog and returns), while Result() is blocking (waits for user response), this should be clarified more explicitly to avoid confusion.


537-577: LGTM - Complete examples demonstrate the new API patterns well.

The confirmation dialog and save changes examples properly show:

  • Buttons() configuration with semantic flags
  • Result() with error handling
  • Control flow based on button label comparison

- Fixed issue where AssetServer can crash on MacOS in [#4576](https://github.com/wailsapp/wails/pull/4576) by @jghiloni
- Fixed compilation issue when building with NextJs. Fixed in [#4585](https://github.com/wailsapp/wails/pull/4585) by @rev42
- Fixed redefinition error for liquid glass demo in [#4542](https://github.com/wailsapp/wails/pull/4542) by @Etesam913
- Fixed issue where AssetServer can crash on MacOS in [#4576](https://github.com/wailsapp/wails/pull/4576) by @jghiloni
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 | 🟡 Minor

Fix operating system name capitalization.

The operating system from Apple should be written as "macOS" (not "MacOS") for consistency with Apple's official branding.

🔎 Proposed fix
-Fixed issue where AssetServer can crash on MacOS in [#4576](https://github.com/wailsapp/...
+Fixed issue where AssetServer can crash on macOS in [#4576](https://github.com/wailsapp/...

Apply the same fix to both occurrences at lines 244 and 258.

Also applies to: 258-258

🧰 Tools
🪛 LanguageTool

[uncategorized] ~244-~244: The operating system from Apple is written “macOS”.
Context: ...ed issue where AssetServer can crash on MacOS in [#4576](https://github.com/wailsapp/...

(MAC_OS)

🤖 Prompt for AI Agents
In docs/src/content/docs/changelog.mdx around lines 244 and 258, the Apple
operating system name is capitalized as "MacOS"; change both occurrences to the
correct "macOS" to match Apple’s official branding and maintain consistency in
the changelog.

## Question dialog

Ask users questions and handle responses via button callbacks:
Ask users questions and handle responses. `Show()` is non-blocking and returns an error if the dialog could not be displayed. Use `Result()` to block and retrieve the clicked button's label:
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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "*.go" -o -name "*.ts" -o -name "*.js" | xargs grep -l "MessageDialog" | head -20

Repository: wailsapp/wails

Length of output: 846


🏁 Script executed:

fd -e go -e ts -e js | xargs grep -l "Show()" | grep -i dialog | head -20

Repository: wailsapp/wails

Length of output: 591


🏁 Script executed:

git log --oneline -20 | grep -i "dialog\|show"

Repository: wailsapp/wails

Length of output: 120


🏁 Script executed:

cat -n v3/pkg/application/dialogs.go | head -100

Repository: wailsapp/wails

Length of output: 2508


🏁 Script executed:

git show 0f3ba542 --stat

Repository: wailsapp/wails

Length of output: 50371


🏁 Script executed:

git show 0f3ba542 | grep -A 20 -B 5 "Show()"

Repository: wailsapp/wails

Length of output: 50373


🏁 Script executed:

cat -n v3/pkg/application/dialogs.go | head -200

Repository: wailsapp/wails

Length of output: 6135


🏁 Script executed:

grep -A 30 "func (m \*MessageDialog) Show" v3/pkg/application/dialogs.go

Repository: wailsapp/wails

Length of output: 40


🏁 Script executed:

grep -n "func.*Result" v3/pkg/application/dialogs.go

Repository: wailsapp/wails

Length of output: 40


🏁 Script executed:

rg "Result\(\)" docs/src/content/docs/features/dialogs/message.mdx -A 2 -B 2

Repository: wailsapp/wails

Length of output: 1604


🏁 Script executed:

grep -rn "func.*Result" v3/pkg/application/ --include="*.go"

Repository: wailsapp/wails

Length of output: 726


🏁 Script executed:

rg "Result\(" v3/pkg/application/dialogs.go

Repository: wailsapp/wails

Length of output: 40


🏁 Script executed:

sed -n '100,200p' docs/src/content/docs/features/dialogs/message.mdx

Repository: wailsapp/wails

Length of output: 2263


🏁 Script executed:

rg "func.*\(.*MessageDialog.*Result" v3/pkg/application/dialogs.go

Repository: wailsapp/wails

Length of output: 40


🏁 Script executed:

grep -A 5 "type MessageDialog" v3/pkg/application/dialogs.go | head -50

Repository: wailsapp/wails

Length of output: 299


🏁 Script executed:

grep -n "Result" v3/pkg/application/dialogs.go

Repository: wailsapp/wails

Length of output: 309


🏁 Script executed:

sed -n '100,150p' v3/pkg/application/dialogs.go

Repository: wailsapp/wails

Length of output: 1791


Update documentation: Show() is blocking, not non-blocking, and there is no Result() method.

The documentation states "Show() is non-blocking and returns an error if the dialog could not be displayed. Use Result() to block and retrieve the clicked button's label." However, the actual implementation shows:

  • Show() has signature func (d *MessageDialog) Show() (string, error)
  • Comments explicitly state: "This method blocks until the user clicks a button"
  • Show() returns the button label (string) and an error—not just an error
  • There is no separate Result() method

All examples in the documentation using Result() should instead call Show() directly, as Show() is the blocking call that returns the clicked button's label. The non-blocking/blocking distinction and the reference to a non-existent Result() method are incorrect throughout this file.

🤖 Prompt for AI Agents
In docs/src/content/docs/features/dialogs/message.mdx around line 133, the text
incorrectly claims Show() is non-blocking and references a non-existent Result()
method; update the wording to state that Show() is a blocking call that returns
the clicked button label and an error (signature: func (d *MessageDialog) Show()
(string, error)), remove any mention of Result(), and replace all example calls
to Result() with Show() so examples reflect the actual API and behavior.

Comment on lines +318 to 321
**2. Using `Default()` and `Cancel()` on the dialog:**

Use these when you want to set default/cancel behavior by button label:

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 | 🟡 Minor

Missing section numbering - "2." without a preceding "1."

Line 318 has the heading "2. Using Default() and Cancel() on the dialog:" but there's no corresponding "1." heading for the first approach (using IsDefault/IsCancel inline). Consider adding "1. Using inline flags:" before line 293 or removing the "2." prefix.

🔎 Suggested fix

Either add a "1." heading before the first approach:

+**1. Using `IsDefault` and `IsCancel` inline:**
+
 ```go
 result, err := app.Dialog.Question().
     SetTitle("Save Changes?").

Or remove the "2." prefix:

-**2. Using `Default()` and `Cancel()` on the dialog:**
+**Using `Default()` and `Cancel()` on the dialog:**
📝 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
**2. Using `Default()` and `Cancel()` on the dialog:**
Use these when you want to set default/cancel behavior by button label:
**Using `Default()` and `Cancel()` on the dialog:**
Use these when you want to set default/cancel behavior by button label:
🤖 Prompt for AI Agents
In docs/src/content/docs/features/dialogs/message.mdx around lines 318 to 321,
the heading is labeled "2. Using `Default()` and `Cancel()` on the dialog:" but
there is no preceding "1." section; either add a "1. Using inline flags:"
heading before the first approach near line ~293 so numbering is sequential, or
remove the "2." prefix from this heading so it no longer suggests prior
numbering—update the heading text accordingly to keep sections consistent.

Comment on lines +519 to 521
}
}

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 | 🟡 Minor

Missing closing code fence for the example.

The code block starting at line 502 for showCustomIconQuestion is missing its closing triple backticks. The ## Best Practices section on line 522 appears immediately after the function, indicating the code block was not properly closed.

🔎 Suggested fix
     case "Not so keen...":
 		_ = app.Dialog.Info().SetMessage("Too bad!").Show()
     }
 }
+```

 ## Best Practices
📝 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
}
}
}
}
🤖 Prompt for AI Agents
In docs/src/content/docs/features/dialogs/message.mdx around lines 519 to 521,
the example code block for showCustomIconQuestion is missing its closing triple
backticks; add a closing ``` fence immediately after the function (right after
the existing closing brace on line 521) so the code block ends before the "##
Best Practices" heading on line 522.

@leaanthony leaanthony requested a review from ndianabasi January 2, 2026 10:21
@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 2, 2026

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: 1

♻️ Duplicate comments (1)
docs/src/content/docs/changelog.mdx (1)

265-265: Fix operating system name capitalization.

The operating system from Apple should be written as "macOS" (not "MacOS") for consistency with Apple's official branding.

🔎 Proposed fix
-Fixed issue where AssetServer can crash on MacOS in [#4576](https://github.com/wailsapp/...
+Fixed issue where AssetServer can crash on macOS in [#4576](https://github.com/wailsapp/...

Apply this fix to both occurrences at lines 265 and 279.

Also applies to: 279-279

🧹 Nitpick comments (2)
v3/UNRELEASED_CHANGELOG.md (2)

20-22: Builder-style methods well-documented.

The three new builder methods are clearly described with appropriate issue tracking. However, it would be helpful to explicitly note that these methods enable fluent chaining syntax (e.g., dialog.WithButton(...).WithDefaultButton(...)), as that is the key benefit over the existing AddButton() approach mentioned in the PR discussion.

🔎 Suggested enhancement to entry text
- Add `MessageDialog.WithButton()` method for builder pattern chaining when adding buttons without configuration (#4792)
- Add `MessageDialog.WithDefaultButton()` method for adding a button marked as default (Enter key) with builder pattern chaining (#4810)
- Add `MessageDialog.WithCancelButton()` method for adding a button marked as cancel (Escape key) with builder pattern chaining (#4810)
+ Add `MessageDialog.WithButton()`, `WithDefaultButton()`, and `WithCancelButton()` methods for fluent builder-pattern chaining to configure buttons without repetitive calls (#4792, #4810)

This consolidates the three related entries and emphasizes the fluent chaining benefit that addresses the issue where AddButton() chaining was not possible.


30-30: Bug fix entry lacks implementation detail.

The entry correctly identifies the fix (Escape key handling for cancel buttons on Linux), but could benefit from a brief mention of what changed (e.g., "now maps Escape/close events to cancel button") to help users understand the scope of the fix.

🔎 Suggested enhancement
- Fix `IsCancel` button not responding to Escape key on Linux (GTK) (#4810)
+ Fix `IsCancel` button not responding to Escape key on Linux (GTK); Escape and window close events now properly trigger the designated cancel button (#4810)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0f3ba54 and cfd7cd0.

📒 Files selected for processing (2)
  • docs/src/content/docs/changelog.mdx
  • v3/UNRELEASED_CHANGELOG.md
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-29T08:02:06.122Z
Learnt from: popaprozac
Repo: wailsapp/wails PR: 4839
File: docs/src/content/docs/reference/window.mdx:616-620
Timestamp: 2025-12-29T08:02:06.122Z
Learning: In Wails v3, document window creation using app.Window.New() and app.Window.NewWithOptions(...). Do not show or reference app.NewWebviewWindow() or app.NewWebviewWindowWithOptions(...). The Application struct exposes a Window field of type *WindowManager that provides these methods. When updating docs, replace examples accordingly and mention that WindowManager methods create and configure new windows.

Applied to files:

  • docs/src/content/docs/changelog.mdx
📚 Learning: 2025-12-13T19:52:13.812Z
Learnt from: leaanthony
Repo: wailsapp/wails PR: 4783
File: v3/pkg/events/events.go:72-100
Timestamp: 2025-12-13T19:52:13.812Z
Learning: In Wails v3, the linux:WindowLoadChanged event was intentionally removed as a breaking change and replaced with four granular WebKit2 load events: linux:WindowLoadStarted, linux:WindowLoadRedirected, linux:WindowLoadCommitted, and linux:WindowLoadFinished. Users should migrate to linux:WindowLoadFinished for detecting when the WebView has finished loading.

Applied to files:

  • docs/src/content/docs/changelog.mdx
🪛 LanguageTool
docs/src/content/docs/changelog.mdx

[uncategorized] ~265-~265: The operating system from Apple is written “macOS”.
Context: ...ed issue where AssetServer can crash on MacOS in [#4576](https://github.com/wailsapp/...

(MAC_OS)


[uncategorized] ~279-~279: The operating system from Apple is written “macOS”.
Context: ...ed issue where AssetServer can crash on MacOS in [#4576](https://github.com/wailsapp/...

(MAC_OS)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (macos-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
  • GitHub Check: semgrep/ci
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: Analyze (go)
🔇 Additional comments (1)
v3/UNRELEASED_CHANGELOG.md (1)

26-26: Changelog entry accurately documents the breaking change.

The MessageDialog.Show() method in v3/pkg/application/dialogs.go returns (string, error) as stated, confirming the changelog entry is correct. The breaking change is clearly documented with proper references to the issue/PR and explanation of the rationale (synchronous workflows and error handling).

- Update the documentation page for Wails v3 Asset Server by @ndianabasi
- **BREAKING**: Remove package-level dialog functions (`application.InfoDialog()`, `application.QuestionDialog()`, etc.). Use the `app.Dialog` manager instead: `app.Dialog.Info()`, `app.Dialog.Question()`, `app.Dialog.Warning()`, `app.Dialog.Error()`, `app.Dialog.OpenFile()`, `app.Dialog.SaveFile()`
- Update dialogs documentation to match actual API: use `app.Dialog.*`, `AddButton()` with callbacks (not `SetButtons()`), `SetDefaultButton(*Button)` (not string), `AddFilter()` (not `SetFilters()`), `SetFilename()` (not `SetDefaultFilename()`), and `app.Dialog.OpenFile().CanChooseDirectories(true)` for folder selection
- Update dialogs documentation to match actual API: use `app.Dialog.*`, `Buttons(...)`, `Show() error` (non-blocking), `Result() (string, error)` (blocking), `AddFilter()`, `SetFilename()`, and `app.Dialog.OpenFile().CanChooseDirectories(true)` for folder selection
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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Find files that might contain MessageDialog definition
fd -e go -x grep -l "MessageDialog" {} \;

Repository: wailsapp/wails

Length of output: 784


🏁 Script executed:

# Search for MessageDialog Show method definition with context
rg -n -B2 -A5 'func.*MessageDialog.*Show' --type=go

Repository: wailsapp/wails

Length of output: 613


🏁 Script executed:

# Search for MessageDialog Result method definition with context
rg -n -B2 -A5 'func.*MessageDialog.*Result' --type=go

Repository: wailsapp/wails

Length of output: 429


🏁 Script executed:

# Alternative: Search for type MessageDialog definition
rg -n -B2 -A10 'type MessageDialog' --type=go

Repository: wailsapp/wails

Length of output: 2979


🏁 Script executed:

# Search for Result method in MessageDialog
rg -n -B2 -A5 'func.*MessageDialog.*Result' --type=go

Repository: wailsapp/wails

Length of output: 429


🏁 Script executed:

# Look at the full Show method and context in dialogs.go
sed -n '110,130p' ./v3/pkg/application/dialogs.go

Repository: wailsapp/wails

Length of output: 767


🏁 Script executed:

# Check what InvokeSyncWithResultAndError does to understand if Show() is blocking
rg -n -B2 -A10 'InvokeSyncWithResultAndError' --type=go | head -50

Repository: wailsapp/wails

Length of output: 2930


Fix dialog API documentation — multiple inaccuracies found.

The changelog documents an incorrect API. The actual MessageDialog.Show() implementation:

  • Returns (string, error) — not just error
  • Is blocking — the code comment explicitly states "This method blocks until the user clicks a button" and uses InvokeSyncWithResultAndError(...wg.Wait()...) to enforce synchronous execution
  • Is a single method — there is no separate Result() method on MessageDialog in v3

Update the changelog to accurately reflect: Show() (string, error) (blocking) and remove the non-existent Result() method from the documentation.

🤖 Prompt for AI Agents
In docs/src/content/docs/changelog.mdx around line 103, the changelog entry
incorrectly describes the MessageDialog API; change the documentation to state
that MessageDialog.Show() is a blocking call that returns (string, error) and
remove any mention of a non-existent Result() method — update the line to
reflect Show() (string, error) (blocking) and remove references to Result() or a
non-blocking Show() signature.

Copy link

@ndianabasi ndianabasi left a comment

Choose a reason for hiding this comment

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

  • The recent changes to the docs mentioned an unimplemented dialog.Result method.

I am still experiencing the app-quitting behaviour when I click on any dialog buttons. Below is main.go. I added a log a time log to the go routine to make the quitting more obvious.

This is the current behaviour.

  1. If I completely disable all dialog calls, app starts, UI is shown, go routine keeps logging.
  2. However, if I enable the dialog interactions, the app quits irrespective of which button is clicked.
  3. Lastly, I introduced an event listener and triggered the event from the frontend, this time, the dialog was shown and the buttons behaved as expected.
## Attempt 1: Clicking the `Run` button
## Go routine stops logging immediate the button is clicked. App is not longer running
## The app still quitted after this log: `2026/01/05 12:57:07 Migration ran. App did not quit!`

task: [darwin:run] mkdir -p bin/Wails3Demo.dev.app/Contents/{MacOS,Resources}
....
2026-01-05 12:57:01.155 Wails3Demo[77857:64104751] WebviewWindow: setDelegate - Registering window for dragged types (NSFilenamesPboardType) because WebviewWindowDelegate is being set.
2026/01/05 12:57:02 Current time is: Mon, 05 Jan 2026 11:57:02 UTC
2026/01/05 12:57:03 Current time is: Mon, 05 Jan 2026 11:57:03 UTC
2026/01/05 12:57:04 Current time is: Mon, 05 Jan 2026 11:57:04 UTC
2026/01/05 12:57:05 Current time is: Mon, 05 Jan 2026 11:57:05 UTC
2026/01/05 12:57:06 Current time is: Mon, 05 Jan 2026 11:57:06 UTC
2026/01/05 12:57:07 Current time is: Mon, 05 Jan 2026 11:57:07 UTC
2026/01/05 12:57:07 Run buttion cicked
2026/01/05 12:57:07 Migration is running
2026/01/05 12:57:07 Migration ran. App did not quit!

## Attempt two: Cancel button is clicked
## Go routine stops logging as well. The button is meant to quite the app.

2026/01/05 12:57:08 INFO File modified...Refreshing file=main.go
...
2026-01-05 12:57:11.265 Wails3Demo[78170:64106041] WebviewWindow: setDelegate - Registering window for dragged types (NSFilenamesPboardType) because WebviewWindowDelegate is being set.
2026/01/05 12:57:12 Current time is: Mon, 05 Jan 2026 11:57:12 UTC
2026/01/05 12:57:13 Current time is: Mon, 05 Jan 2026 11:57:13 UTC
2026/01/05 12:57:14 Current time is: Mon, 05 Jan 2026 11:57:14 UTC
2026/01/05 12:57:15 Current time is: Mon, 05 Jan 2026 11:57:15 UTC
2026/01/05 12:57:16 Current time is: Mon, 05 Jan 2026 11:57:16 UTC
2026/01/05 12:57:17 Current time is: Mon, 05 Jan 2026 11:57:17 UTC
2026/01/05 12:57:17 Cancel button clicked
2026/01/05 12:57:17 Migration cancelled by user.
2026/01/05 12:57:17 App is quitting.

Need documentation? Run: wails3 docs
 ♥   If Wails is useful to you or your company, please consider sponsoring the project: wails3 sponsor
package main

import (
	"embed"
	_ "embed"
	"fmt"
	"log"
	"time"

	"github.com/wailsapp/wails/v3/pkg/application"
	"github.com/wailsapp/wails/v3/pkg/events"
)

// Wails uses Go's `embed` package to embed the frontend files into the binary.
// Any files in the frontend/dist folder will be embedded into the binary and
// made available to the frontend.
// See https://pkg.go.dev/embed for more information.

//go:embed all:frontend/dist
var assets embed.FS

func init() {
	// Register a custom event whose associated data type is string.
	// This is not required, but the binding generator will pick up registered events
	// and provide a strongly typed JS/TS API for them.
	application.RegisterEvent[string]("time")
}

// main function serves as the application's entry point. It initializes the application, creates a window,
// and starts a goroutine that emits a time-based event every second. It subsequently runs the application and
// logs any error that might occur.
func main() {

	// Create a new Wails application by providing the necessary options.
	// Variables 'Name' and 'Description' are for application metadata.
	// 'Assets' configures the asset server with the 'FS' variable pointing to the frontend files.
	// 'Bind' is a list of Go struct instances. The frontend has access to the methods of these instances.
	// 'Mac' options tailor the application when running an macOS.
	app := application.New(application.Options{
		Name:        "GotedoImpress",
		Description: "A demo of using raw HTML & CSS",
		Services:    []application.Service{
			// application.NewService(&GreetService{}),
		},
		Assets: application.AssetOptions{
			Handler: application.AssetFileServerFS(assets),
		},
		Mac: application.MacOptions{
			ApplicationShouldTerminateAfterLastWindowClosed: true,
		},
	})

	// Create a new window with the necessary options.
	// 'Title' is the title of the window.
	// 'Mac' options tailor the window when running on macOS.
	// 'BackgroundColour' is the background colour of the window.
	// 'URL' is the URL that will be loaded into the webview.
	app.Window.NewWithOptions(application.WebviewWindowOptions{
		Title: "Window 1",
		Mac: application.MacWindow{
			InvisibleTitleBarHeight: 50,
			Backdrop:                application.MacBackdropTranslucent,
			TitleBar:                application.MacTitleBarHiddenInset,
		},
		BackgroundColour: application.NewRGB(27, 38, 54),
		URL:              "/",
		DevToolsEnabled:  true,
	})

	isDebug := app.Env.Info().Debug
	fmt.Printf("isDebug: %v\n", isDebug)

	// Application lifecycle events
	app.Event.OnApplicationEvent(events.Common.ApplicationStarted, func(event *application.ApplicationEvent) {
		dialog := app.Dialog.Question().
			SetTitle("Migration Warning").
			SetMessage(fmt.Sprintf("%d migrations are available. Do you want to run them now?", 2))

		dialog.AddButton("Run").SetAsDefault()
		dialog.AddButton("Cancel").SetAsCancel()

		if result, err := dialog.Show(); err != nil {
			log.Printf("error while show migration-warning dialog: %+v\n", err)
			return
		} else {
			switch result {
			case "Run":
				log.Println("Run buttion cicked")
				log.Println("Migration is running")
			case "Cancel":
				log.Println("Cancel button clicked")
				log.Println("Migration cancelled by user.")
				log.Println("App is quitting.")
				app.Quit()
			}
		}

		log.Println("Migration ran. App did not quit!")
	})

	// Create a goroutine that emits an event containing the current time every second.
	// The frontend can listen to this event and update the UI accordingly.
	go func() {
		for {
			now := time.Now().Format(time.RFC1123)
			app.Event.Emit("time", now)
			time.Sleep(time.Second)
			log.Printf("Current time is: %s", time.Now().UTC().Format(time.RFC1123))
		}
	}()

	// Run the application. This blocks until the application has been exited.
	err := app.Run()

	// If an error occurred while running the application, log it and exit.
	if err != nil {
		log.Fatal(err)
	}
}
	app.Event.On("show-dialog", func(event *application.CustomEvent) {
		dialog := app.Dialog.Question().
			SetTitle("Migration Warning").
			SetMessage(fmt.Sprintf("%d migrations are available. Do you want to run them now?", 2))

		dialog.AddButton("Run").SetAsDefault()
		dialog.AddButton("Cancel").SetAsCancel()

		if result, err := dialog.Show(); err != nil {
			log.Printf("error while show migration-warning dialog: %+v\n", err)
			return
		} else {
			switch result {
			case "Run":
				log.Println("Run buttion cicked")
				log.Println("Migration is running")
			case "Cancel":
				log.Println("Cancel button clicked")
				log.Println("Migration cancelled by user.")
				log.Println("App is quitting.")
				app.Quit()
			}
		}
	})

Summary: we need to investigate why triggering the dialog within the ApplicationStarted event quits the app when the button is clicked.

@leaanthony
Copy link
Member Author

@ndianabasi I am going to do some rework of this PR - moving to draft for now

@leaanthony leaanthony marked this pull request as draft January 7, 2026 05:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants