Skip to content

fix(v3): App Exposé shows “ghost” window after calling App.Window.Current() - #4902

Closed
wimaha wants to merge 372 commits into
wailsapp:v3-alphafrom
wimaha:v3-alpha
Closed

wimaha wants to merge 372 commits into
wailsapp:v3-alphafrom
wimaha:v3-alpha

Conversation

@wimaha

@wimaha wimaha commented Jan 24, 2026 •

Copy link
Copy Markdown
Contributor

Description

On macOS, I’m seeing an issue where a window appears closed, but it still shows up in App Exposé after calling App.Window.Current() at some time.

Type of change

Please select the option that is relevant.

  • Just an example to show the potential bug

How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration using wails doctor.

  • Windows
  • macOS
  • Linux

If you checked Linux, please specify the distro and version.

Test Configuration

Any additional information here: https://discord.com/channels/1042734330029547630/1464733023864881354

wails3 doctor output:


# System 
┌──────────────────────────────────────────────────┐
| Name          | MacOS                            |
| Version       | 26.2                             |
| ID            | 25C56                            |
| Branding      | MacOS 26.2                       |
| 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 4 |
| Memory        | 16 GB                            |
└──────────────────────────────────────────────────┘

# Build Environment 
┌────────────────────────────────┐
| Wails CLI    | v3.0.0-alpha.62 |
| Go Version   | go1.25.5        |
| -buildmode   | exe             |
| -compiler    | gc              |
| CGO_ENABLED  | 1               |
| GOARCH       | arm64           |
| GOOS         | darwin          |
└────────────────────────────────┘

# Dependencies 
┌──────────────────────────────────────────────────────────────────────────────┐
| Xcode cli tools | 2416                                                       |
| npm             | 10.9.2                                                     |
| *NSIS           | v3.11                                                      |
| docker          | *Docker version 27.5.1, build 9f9e405 (daemon not running) |
└────────────────────────── * - Optional Dependency ───────────────────────────┘

# Checking for issues 
 SUCCESS  No issues found```

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **New Features**
  * Added a new multi-window example application featuring window management capabilities: open and close child windows, list active windows, and display window information. Includes a user interface with controls for window operations and supports building on macOS, Linux, and Windows.

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

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

leaanthony and others added 30 commits February 28, 2025 07:19
* docs: update Svelte recommendation

* Update routing.mdx

* Update changelog.mdx

* Updated changelog

---------

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
Fix Windows 11 identification in `wails doctor`
- Replace SW_RESTORE with SW_SHOW flag
- Resolves #4109
(cherry picked from commit fe6635c)
github-actions Bot and others added 7 commits January 13, 2026 21:48
Co-authored-by: leaanthony <1943904+leaanthony@users.noreply.github.com>
…4806)

Disable suppressesIncrementalRendering on macOS 26+ (Tahoe) to prevent
WebView crashes when the frontend performs frequent UI updates.

Fixes #4592

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

Co-authored-by: Claude <noreply@anthropic.com>
Updated sponsorship section with JetBrains logo.
Co-authored-by: leaanthony <1943904+leaanthony@users.noreply.github.com>
Co-authored-by: leaanthony <1943904+leaanthony@users.noreply.github.com>
* fix(v2): Replace ioutl.Discard with io.Discard

* docs: Update website changelog

---------

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
@coderabbitai

coderabbitai Bot commented Jan 24, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • 🔍 Trigger a full review

Walkthrough

This PR adds a complete multiwindow-close example application for Wails v3, featuring cross-platform build pipelines (macOS, Linux, Windows), a frontend UI for window management, and a Go backend service implementing window lifecycle operations including open, list, and close-by-name strategies.

Changes

Cohort / File(s) Summary
Example Root Configuration
.gitignore, README.md, Taskfile.yml, go.mod
Project initialization, dependency management, and top-level task orchestration.
Frontend Application
frontend/index.html, frontend/src/main.js, frontend/public/style.css, frontend/package.json, frontend/tsconfig.json, frontend/vite.config.js, frontend/Inter Font License.txt
Frontend UI, styling, build configuration, and licensing. Implements window control panel with open/close/list operations.
Frontend Generated Bindings
frontend/bindings/changeme/*, frontend/bindings/github.com/wailsapp/wails/v3/internal/*
Auto-generated JavaScript bindings for backend service calls (GreetService methods and WindowSummary model).
Backend Application
greetservice.go, main.go
Go backend service implementing window management (Greet, OpenChildWindow, ListWindows, CloseByName, CloseAfterCurrentByName, CloseUsingCurrent). Main app entry point with Wails configuration.
Build Configuration - Core
build/Taskfile.yml, build/config.yml
Cross-platform build task definitions and dev/production build configuration (dev_mode, fileAssociations, build metadata).
macOS Build Configuration
build/darwin/Taskfile.yml, build/darwin/Info.plist, build/darwin/Info.dev.plist
Native and universal macOS builds, app bundling, codesigning, notarization, and plist metadata. High-complexity task orchestration for native/Docker/universal binaries.
Linux Build Configuration
build/linux/Taskfile.yml, build/linux/appimage/build.sh, build/linux/desktop, build/linux/nfpm/nfpm.yaml, build/linux/nfpm/scripts/{postinstall,preinstall,postremove,preremove}.sh
AppImage, Debian, RPM, and Arch packaging workflows with desktop integration and post-install hooks.
Windows Build Configuration
build/windows/Taskfile.yml, build/windows/info.json, build/windows/wails.exe.manifest, build/windows/msix/{app_manifest.xml,template.xml}, build/windows/nsis/{project.nsi,wails_tools.nsh}
NSIS installer and MSIX package creation with architecture-aware builds, code signing, WebView2 runtime handling, and file/protocol associations.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

Documentation, MacOS, Windows, Linux, v3-alpha, bindings, size:XXL, lgtm

Suggested reviewers

  • leaanthony
  • atterpac

Poem

🐰 A multi-windowed garden we grow,
Cross-platform builds put on a show,
Close, list, and open with glee,
macOS, Windows, Linux—all three!
Task by task, the example stands tall,
Wails v3 magic for one and all! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description is incomplete against the template. Missing: linked issue number (no 'Fixes #' statement), specific type of change checkbox selection lacks clarity, no wails doctor output in proper format, most checklist items unchecked, and no changelog update mentioned. Add the GitHub issue number with 'Fixes #' syntax, clearly mark the change type, include complete wails doctor output, and check relevant checklist items including changelog updates.
✅ Passed checks (2 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The PR title accurately describes the main issue being addressed: a bug where closed windows still appear in macOS App Exposé after calling App.Window.Current().

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 12

🤖 Fix all issues with AI agents
In `@v3/examples/multiwindow-close/build/darwin/Taskfile.yml`:
- Around line 28-53: The build:native task runs from the Taskfile directory
which causes relative paths to break; update the build:native task to set dir to
the repository root so its deps (e.g. common:go:mod:tidy) and the go build
command execute from the repo root and produce outputs in the correct bin path;
modify the build:native task (where BUILD_FLAGS, OUTPUT, DEFAULT_OUTPUT and the
go build line are defined) to include a dir: entry that points to the repository
root.

In `@v3/examples/multiwindow-close/build/linux/appimage/build.sh`:
- Around line 33-34: The mv command uses a glob inside double quotes so the * is
literal and won't match; update the rename step to allow shell glob expansion by
removing quotes around the source glob (keep quotes around the target) or
explicitly resolve the generated filename (e.g., via a shell expansion or find)
before calling mv; target the mv invocation that references APP_NAME (the line
using mv "${APP_NAME}*.AppImage" to "${APP_NAME}.AppImage") so the source is
expanded as APP_NAME*.AppImage and the destination remains quoted.

In `@v3/examples/multiwindow-close/build/linux/desktop`:
- Around line 5-6: The desktop entry's Exec line is hardcoded to
/usr/local/bin/multiwindow which conflicts with the AppImage that installs the
binary to /usr/bin; either make the Exec path configurable during packaging or
remove the absolute path and use the binary name "multiwindow" (so the system
PATH is used) in the desktop file, or change the AppImage packaging to install
to /usr/local/bin to match nfpm; update the Exec line in the desktop file or
adjust the AppImage install location accordingly and ensure packaging templates
(the desktop file generator or AppImage build script) consistently produce the
same install path.

In `@v3/examples/multiwindow-close/build/linux/Taskfile.yml`:
- Around line 72-75: The docker run line that invokes alpine to chown using $(id
-u):$(id -g) is POSIX-only and will fail on Windows; update Taskfile.yml so that
the chown step is executed only on Unix hosts by moving that command into its
own task or step and adding a platform restriction (e.g., platforms:
["linux","darwin"]) or a shell guard that detects POSIX (so the line containing
'docker run --rm -v "{{.ROOT_DIR}}:/app" alpine chown -R $(id -u):$(id -g)
/app/bin' runs only on Unix), leaving the other docker/mv/mkdir steps unchanged.

In `@v3/examples/multiwindow-close/build/windows/info.json`:
- Around line 2-8: The Windows resource versions in info.json are using 3-part
strings; update them to 4-part format to match the MSIX manifests: change the
value of "fixed"."file_version" from "0.1.0" to "0.1.0.0" and update
"info"."0000"."ProductVersion" (and any other ProductVersion/CompanyName entries
inside the same object) to use the 4-part version "0.1.0.0" so all Windows
metadata is explicitly consistent with the MSIX manifest format.

In `@v3/examples/multiwindow-close/build/windows/msix/app_manifest.xml`:
- Around line 31-45: The manifest references an executable name that may not
match the packaged binary; check and reconcile the Executable attributes on the
Application element (Id="com.example.multiwindow" Executable="multiwindow") and
the desktop:Extension element (Category="windows.fullTrustProcess"
Executable="multiwindow") with the actual MSIX payload (the produced binary
multiWindow.exe); update the manifest Executable values to exactly match the
final packaged filename (including proper casing and .exe extension if present)
or adjust the build/msix packaging step to emit the executable name used in the
manifest so both manifest entries and the packaged binary are identical.

In `@v3/examples/multiwindow-close/build/windows/nsis/wails_tools.nsh`:
- Around line 145-179: The wails.webview2runtime macro currently runs the
WebView2 bootstrapper with ExecWait but doesn't check its exit code; modify the
ExecWait call to capture the installer exit code into a variable (e.g. ExecWait
'"$pluginsdir\webview2bootstrapper\MicrosoftEdgeWebview2Setup.exe" /silent
/install' $R0), then test $R0 and if non-zero emit a clear failure message
(DetailPrint/MessageBox using WAILS_INSTALL_WEBVIEW_DETAILPRINT or a descriptive
string) and abort the installer (Abort) so the install stops on WebView2
failure; keep all other logic in the macro (ReadRegStr checks, CreateDirectory,
File) intact.
- Around line 114-135: The uninstall macros wails.writeUninstaller and
wails.deleteUninstaller currently always write/delete under HKLM, which fails
for user-level installs; update both macros to choose the registry root
conditionally (use HKCU for REQUEST_EXECUTION_LEVEL == "user", otherwise HKLM)
following the same pattern used in wails.webview2runtime/wails.setShellContext:
introduce a variable (e.g., UNINST_ROOT) or inline If/StrCmp checks on
${REQUEST_EXECUTION_LEVEL} and then call WriteRegStr/WriteRegDWORD/DeleteRegKey
against that chosen root (and keep SetRegView behavior unchanged); ensure
UninstallString/QuietUninstallString,
Publisher/DisplayName/DisplayVersion/DisplayIcon, and EstimatedSize are written
to the conditional root and Delete uses the same root when removing the key.

In `@v3/examples/multiwindow-close/frontend/public/style.css`:
- Around line 188-192: There's an extra closing brace after the .note rule (the
stray "}" following the .note block) causing a CSS parse error; locate the .note
selector and remove the redundant closing brace so the rule ends with a single
"}" and the file's brace pairs remain balanced, then run a quick CSS lint or
open in a browser to confirm no parse errors.

In `@v3/examples/multiwindow-close/go.mod`:
- Around line 3-5: Update the module's Go version directive in go.mod from "go
1.25" to "go 1.24.0" to match other v3 examples and CI testing; edit the go
directive line in the go.mod file so it reads the 1.24.0 version string to
ensure consistent builds with the v3 examples and CI.

In `@v3/examples/multiwindow-close/greetservice.go`:
- Around line 87-96: The CloseUsingCurrent method dereferences the result of
g.app.Window.Current() without checking for nil; add a nil guard after calling w
:= g.app.Window.Current() (e.g., if w == nil { return WindowSummary{} }) before
calling w.ID(), w.Name(), or w.Close() so the function safely returns an empty
WindowSummary when Current() is nil; update GreetService.CloseUsingCurrent to
check w for nil and only access ID/Name/Close when non-nil.

In `@v3/examples/multiwindow-close/README.md`:
- Line 49: Update the README project structure to accurately list the actual
source files: replace the incorrect `app.go` entry with `main.go` and
`greetservice.go`, and give short descriptions (e.g., `main.go`: application
entrypoint and window management; `greetservice.go`: greeting service/handler
implementation) so the "project structure" section matches the repository
contents.
♻️ Duplicate comments (1)
v3/examples/multiwindow-close/build/darwin/Taskfile.yml (1)

138-147: Covered by working-directory note.

Relative paths here rely on the same repo-root assumption mentioned in the comment on Line 41.

🧹 Nitpick comments (12)
v3/examples/multiwindow-close/build/linux/nfpm/scripts/postinstall.sh (1)

3-19: Add icon cache refresh for installed hicolor icons.

The package installs icons to /usr/share/icons/hicolor/128x128/apps/ as confirmed in nfpm.yaml. Refreshing the icon cache in postinstall ensures icons appear immediately without restart delays.

♻️ Optional addition (guarded)
+if command -v gtk-update-icon-cache >/dev/null 2>&1; then
+  echo "Updating icon cache..."
+  gtk-update-icon-cache -q /usr/share/icons/hicolor
+else
+  echo "Warning: gtk-update-icon-cache command not found. Icons may not update immediately." >&2
+fi
v3/examples/multiwindow-close/README.md (1)

11-13: Add language specifiers to fenced code blocks.

The fenced code blocks are missing language specifiers, which affects syntax highlighting and fails markdown lint checks.

Suggested fix
-   ```
+   ```bash
    wails3 dev
    ```
-   ```
+   ```bash
    wails3 build
    ```
-   ```
+   ```bash
    go run .
    ```

Also applies to: 19-21, 33-35

v3/examples/multiwindow-close/build/linux/appimage/build.sh (1)

8-15: Consider validating required environment variables.

The script uses APP_NAME, APP_BINARY, ICON_PATH, and DESKTOP_FILE without validation. While set -u will catch unset variables, explicit validation with descriptive error messages would improve debuggability.

Optional: Add validation at script start
+# Validate required environment variables
+: "${APP_NAME:?APP_NAME is required}"
+: "${APP_BINARY:?APP_BINARY is required}"
+: "${ICON_PATH:?ICON_PATH is required}"
+: "${DESKTOP_FILE:?DESKTOP_FILE is required}"
+
 # Define variables
 APP_DIR="${APP_NAME}.AppDir"
v3/examples/multiwindow-close/main.go (2)

4-5: Remove duplicate embed import.

The embed package is imported twice — once as a regular import and once as a blank import. The blank import _ "embed" is unnecessary when embed.FS is already used.

Proposed fix
 import (
 	"embed"
-	_ "embed"
 	"log"

19-21: Comment is misleading — no time-based event goroutine exists.

The comment states the function "starts a goroutine that emits a time-based event every second," but no such goroutine is present in this code. Update the comment to reflect the actual behavior.

Proposed fix
-// 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.
+// main function serves as the application's entry point. It initializes the application, creates a window,
+// and runs the application, logging any error that might occur.
v3/examples/multiwindow-close/build/darwin/Info.dev.plist (1)

22-23: Use boolean type for NSHighResolutionCapable.

NSHighResolutionCapable should use a boolean value (<true/>) rather than a string. While macOS may coerce the string, the canonical plist format uses the boolean type.

Proposed fix
         <key>NSHighResolutionCapable</key>
-            <string>true</string>
+            <true/>
v3/examples/multiwindow-close/build/darwin/Info.plist (1)

22-23: Use boolean type for NSHighResolutionCapable.

Same issue as in Info.dev.plist — use <true/> instead of <string>true</string> for the canonical plist format.

Proposed fix
         <key>NSHighResolutionCapable</key>
-            <string>true</string>
+            <true/>
v3/examples/multiwindow-close/build/linux/nfpm/nfpm.yaml (1)

27-38: Consider broader Debian/Ubuntu compatibility and fix misleading comment.

Two issues:

  1. The default dependency libwebkit2gtk-4.1-0 works for Debian 12+ but not Ubuntu 22.04 (which uses libwebkit2gtk-4.0-37). Consider an OR dependency for broader compatibility:

    depends:
      - libgtk-3-0
      - libwebkit2gtk-4.1-0 | libwebkit2gtk-4.0-37
  2. The RPM comment says "WebKit 4.0" but the dependency is webkit2gtk4.1 (WebKit 4.1). The comment should say "WebKit 4.1" to match the actual dependency.

♻️ Proposed fix for the comment
-  # RPM packages for RHEL/CentOS/AlmaLinux/Rocky Linux (WebKit 4.0)
+  # RPM packages for RHEL/CentOS/AlmaLinux/Rocky Linux (WebKit 4.1)
   rpm:
     depends:
       - gtk3
       - webkit2gtk4.1

Based on learnings, Debian 12 uses libwebkit2gtk-4.1-0 while Ubuntu 22.04 uses libwebkit2gtk-4.0-37, and RPM packages for WebKit 4.1 should use webkit2gtk4.1 (which is correct here, but the comment is misleading).

v3/examples/multiwindow-close/build/windows/nsis/project.nsi (1)

91-92: Minor: Inconsistent casing in shortcut commands.

Line 91 uses CreateShortcut while line 92 uses CreateShortCut. NSIS is case-insensitive so this works, but consistent casing improves readability.

♻️ Proposed fix
     CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
-    CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
+    CreateShortcut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
v3/examples/multiwindow-close/greetservice.go (1)

46-59: Return an empty slice when the app is nil

Returning nil will marshal to null, which forces extra null checks on the frontend. Prefer an empty slice to keep the API shape consistent.

♻️ Suggested change
 func (g *GreetService) ListWindows() []WindowSummary {
 	if g.app == nil {
-		return nil
+		return []WindowSummary{}
 	}
v3/examples/multiwindow-close/frontend/src/main.js (1)

54-85: Guard CloseByName actions when ?name= is missing

If a child window opens without a name parameter, the close actions still execute with an empty string. Depending on backend behavior, that could target the wrong window or silently fail. Consider a small guard to short‑circuit and log a clear message.

🧩 Optional helper
 function setChildLog(obj) {
     if (!childLogEl) return;
     if (typeof obj === 'string') {
         childLogEl.textContent = obj;
         return;
     }
@@
     }
 }
+
+function requireChildName(action) {
+    if (childName) return true;
+    setChildLog({action, error: 'missing ?name=...'});
+    return false;
+}
 
 window.closeNoCurrent = async () => {
+    if (!requireChildName('CloseByName')) return;
     try {
         const ok = await Call.ByName('main.GreetService.CloseByName', childName);
         setChildLog({action: 'CloseByName', childName, ok});
@@
 };
 
 window.closeAfterCurrent = async () => {
+    if (!requireChildName('CloseAfterCurrentByName')) return;
     try {
         const ok = await Call.ByName('main.GreetService.CloseAfterCurrentByName', childName);
         setChildLog({action: 'CloseAfterCurrentByName (calls App.Window.Current first)', childName, ok});
v3/examples/multiwindow-close/build/Taskfile.yml (1)

46-80: Consider pinning or vendoring Puppertino CSS

The fetch step pulls from the upstream main branch, which can change over time and make builds non‑reproducible or brittle offline. Consider pinning to a tag/commit or vendoring the CSS in‑repo if stability matters.

Comment on lines +28 to +53
build:native:
summary: Builds the application natively on macOS
internal: true
deps:
- task: common:go:mod:tidy
- task: common:build:frontend
vars:
BUILD_FLAGS:
ref: .BUILD_FLAGS
DEV:
ref: .DEV
- task: common:generate:icons
cmds:
- go build {{.BUILD_FLAGS}} -o {{.OUTPUT}}
vars:
BUILD_FLAGS: '{{if eq .DEV "true"}}-buildvcs=false -gcflags=all="-l"{{else}}-tags production -trimpath -buildvcs=false -ldflags="-w -s"{{end}}'
DEFAULT_OUTPUT: '{{.BIN_DIR}}/{{.APP_NAME}}'
OUTPUT: '{{ .OUTPUT | default .DEFAULT_OUTPUT }}'
env:
GOOS: darwin
CGO_ENABLED: 1
GOARCH: '{{.ARCH | default ARCH}}'
CGO_CFLAGS: "-mmacosx-version-min=10.15"
CGO_LDFLAGS: "-mmacosx-version-min=10.15"
MACOSX_DEPLOYMENT_TARGET: "10.15"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether a working directory is set in the parent Taskfile.
echo "=== Checking for dir settings ==="
rg -n '^\s*dir:' v3/examples/multiwindow-close/build/Taskfile.yml v3/examples/multiwindow-close/build/darwin/Taskfile.yml

echo ""
echo "=== Checking for ROOT_DIR references ==="
rg -n 'ROOT_DIR' v3/examples/multiwindow-close/build/Taskfile.yml v3/examples/multiwindow-close/build/darwin/Taskfile.yml

echo ""
echo "=== Checking includes and task structure in parent ==="
head -50 v3/examples/multiwindow-close/build/Taskfile.yml

Repository: wailsapp/wails

Length of output: 2117


🏁 Script executed:

#!/bin/bash
# Check the structure of darwin/Taskfile.yml to understand variable definitions and includes
echo "=== Full darwin/Taskfile.yml content ==="
cat -n v3/examples/multiwindow-close/build/darwin/Taskfile.yml

echo ""
echo "=== Check if there's a common Taskfile being included ==="
rg -n 'includes:' v3/examples/multiwindow-close/build/darwin/Taskfile.yml

Repository: wailsapp/wails

Length of output: 9591


🏁 Script executed:

#!/bin/bash
# Check parent Taskfile for ROOT_DIR definition and top-level vars
echo "=== Checking parent Taskfile for ROOT_DIR and global vars ==="
head -150 v3/examples/multiwindow-close/build/Taskfile.yml | tail -100

Repository: wailsapp/wails

Length of output: 4306


🏁 Script executed:

#!/bin/bash
# Check the very beginning of parent Taskfile for ROOT_DIR and vars
echo "=== First 40 lines of parent Taskfile ==="
head -40 v3/examples/multiwindow-close/build/Taskfile.yml

echo ""
echo "=== Search for ROOT_DIR definition ==="
rg -A2 -B2 'ROOT_DIR|BIN_DIR|APP_NAME' v3/examples/multiwindow-close/build/Taskfile.yml | head -50

Repository: wailsapp/wails

Length of output: 1160


🏁 Script executed:

#!/bin/bash
# Check if ROOT_DIR is defined elsewhere or is a built-in variable
echo "=== Search for ROOT_DIR definition anywhere in repo ==="
rg 'ROOT_DIR' v3/examples/multiwindow-close/ --max-count=20

echo ""
echo "=== Check if there's a root Taskfile.yml ==="
find v3/examples/multiwindow-close -name "Taskfile.yml" -o -name "Taskfile.yaml" | head -10

echo ""
echo "=== Check the actual repo structure ==="
ls -la v3/examples/multiwindow-close/
</parameter>

Repository: wailsapp/wails

Length of output: 4412


🏁 Script executed:

#!/bin/bash
# Check root Taskfile.yml to find ROOT_DIR definition
echo "=== Root Taskfile.yml ==="
cat -n v3/examples/multiwindow-close/Taskfile.yml

Repository: wailsapp/wails

Length of output: 1176


🏁 Script executed:

#!/bin/bash
# Search for ROOT_DIR in wails documentation or check if it's a built-in task variable
echo "=== Search for ROOT_DIR in all Taskfiles to understand its usage ==="
rg -B3 -A1 'ROOT_DIR' v3/examples/multiwindow-close/build/darwin/Taskfile.yml | head -20

echo ""
echo "=== Check wails repo for ROOT_DIR template variable documentation ==="
rg 'ROOT_DIR' v3/internal/templates --max-count=10

echo ""
echo "=== Check if ROOT_DIR is dynamically set by task itself ==="
rg -n 'ROOT_DIR' v3/pkg --max-count=5

Repository: wailsapp/wails

Length of output: 650


🏁 Script executed:

#!/bin/bash
# Check how build:native is invoked and if parent sets working directory
echo "=== Check build:native invocation in darwin/Taskfile.yml ==="
rg -B10 'build:native' v3/examples/multiwindow-close/build/darwin/Taskfile.yml | head -30

echo ""
echo "=== Check if build:frontend sets dir and verify inheritance ==="
rg -A10 'build:frontend:' v3/examples/multiwindow-close/build/Taskfile.yml | head -20

echo ""
echo "=== Verify path resolution - check if paths work from build/darwin/ or need repo root ==="
cat v3/examples/multiwindow-close/build/darwin/Taskfile.yml | grep -A5 'build:native:' | head -15

Repository: wailsapp/wails

Length of output: 1269


🏁 Script executed:

#!/bin/bash
# Check if ROOT_DIR is supposed to be auto-injected by wails3 or is a common pattern
echo "=== Search entire wails repo for ROOT_DIR definition patterns ==="
rg 'vars:' v3/internal/templates -A10 | grep -A10 'ROOT_DIR' | head -30

echo ""
echo "=== Check if there's a common Taskfile template that defines ROOT_DIR ==="
find v3/internal/templates -name "*.tmpl*" -o -name "*Taskfile*" | head -20

echo ""
echo "=== Check actual template files for ROOT_DIR ==="
rg 'ROOT_DIR' v3/internal/templates

Repository: wailsapp/wails

Length of output: 1318


🏁 Script executed:

#!/bin/bash
# Check the common Taskfile template to see what variables are actually defined
echo "=== Common Taskfile template ==="
cat v3/internal/templates/_common/Taskfile.tmpl.yml

echo ""
echo "=== Search for how variables are inherited in task includes ==="
rg -A20 'includes:' v3/examples/multiwindow-close/build/darwin/Taskfile.yml

Repository: wailsapp/wails

Length of output: 1711


🏁 Script executed:

#!/bin/bash
# Check if there's a go.mod at different levels that would affect path resolution
echo "=== Check for go.mod files ==="
find v3/examples/multiwindow-close -name "go.mod" -type f

echo ""
echo "=== Check if go build would work from build/darwin/ ==="
ls -la v3/examples/multiwindow-close/build/darwin/

echo ""
echo "=== Verify the actual issue: where would bin/multiWindow be created if running from build/darwin/ ==="
echo "If task runs from build/darwin/ and outputs to 'bin/multiWindow', output goes to:"
echo "  build/darwin/bin/multiWindow (WRONG)"
echo "But it should go to:"
echo "  bin/multiWindow (from repo root)"

echo ""
echo "=== Check if ROOT_DIR might be auto-injected by wails3 ==="
rg 'ROOT_DIR' v3 --max-count=5 -B2 -A2 | head -40

Repository: wailsapp/wails

Length of output: 4767


🏁 Script executed:

#!/bin/bash
# Check wails3 source code to see if ROOT_DIR is auto-injected
echo "=== Check if wails3 injects ROOT_DIR ==="
find v3/cmd -name "*.go" | xargs grep -l "ROOT_DIR" 2>/dev/null | head -5

echo ""
echo "=== Check task runner integration in wails ==="
find v3 -name "*.go" | xargs grep -l "{{.ROOT_DIR}}" 2>/dev/null | head -5

echo ""
echo "=== Try to understand how ROOT_DIR should work by checking go-task documentation patterns ==="
rg 'ROOT_DIR.*:' v3 --max-count=3

echo ""
echo "=== Check if there's any initialization that sets ROOT_DIR ==="
rg 'ROOT_DIR' v3/cmd v3/pkg --max-count=10 -A1

Repository: wailsapp/wails

Length of output: 2457


🏁 Script executed:

#!/bin/bash
# Check go-task documentation - ROOT_DIR might be a built-in variable
echo "=== Check all variable usages in build/darwin/Taskfile.yml ==="
rg '{{\.([A-Z_]+)' v3/examples/multiwindow-close/build/darwin/Taskfile.yml -o | sort | uniq

echo ""
echo "=== Check what variables are actually defined ==="
rg -A50 'vars:' v3/examples/multiwindow-close/build/darwin/Taskfile.yml | head -40

echo ""
echo "=== Verify if build:docker references paths that would break without dir ==="
grep -A8 'build:docker:' v3/examples/multiwindow-close/build/darwin/Taskfile.yml | head -15

Repository: wailsapp/wails

Length of output: 2016


🏁 Script executed:

#!/bin/bash
# Let's verify the actual problem: check if {{.OUTPUT}} uses absolute or relative paths
echo "=== Trace OUTPUT variable definition ==="
grep -n "OUTPUT:" v3/examples/multiwindow-close/build/darwin/Taskfile.yml

echo ""
echo "=== Trace BIN_DIR and APP_NAME ==="
grep -n "BIN_DIR\|APP_NAME" v3/examples/multiwindow-close/build/Taskfile.yml v3/examples/multiwindow-close/Taskfile.yml

echo ""
echo "=== Check what go:mod:tidy does (first dependency) ==="
grep -A5 'go:mod:tidy:' v3/examples/multiwindow-close/build/Taskfile.yml

echo ""
echo "=== Simulate: if {{.BIN_DIR}} = 'bin' and task runs from build/darwin/, output would be at ==="
echo "  build/darwin/bin/multiWindow instead of bin/multiWindow (PROBLEM!)"

echo ""
echo "=== Check if other OS-specific Taskfiles have dir set ==="
rg -A5 'build:native:' v3/examples/multiwindow-close/build/linux/Taskfile.yml

Repository: wailsapp/wails

Length of output: 1466


Set working directory for build:native to ensure commands execute from repo root.

The go build command and dependency common:go:mod:tidy use relative paths that only resolve correctly from the repository root. Without an explicit dir: setting, go-task runs from the Taskfile's location (build/darwin/), causing go mod tidy to fail and output files to create at incorrect paths like build/darwin/bin/multiWindow instead of bin/multiWindow.

🛠️ Minimal fix
  build:native:
    summary: Builds the application natively on macOS
    internal: true
+   dir: '{{.ROOT_DIR}}'
    deps:
      - task: common:go:mod:tidy
🤖 Prompt for AI Agents
In `@v3/examples/multiwindow-close/build/darwin/Taskfile.yml` around lines 28 -
53, The build:native task runs from the Taskfile directory which causes relative
paths to break; update the build:native task to set dir to the repository root
so its deps (e.g. common:go:mod:tidy) and the go build command execute from the
repo root and produce outputs in the correct bin path; modify the build:native
task (where BUILD_FLAGS, OUTPUT, DEFAULT_OUTPUT and the go build line are
defined) to include a dir: entry that points to the repository root.

Comment on lines +33 to +34
# Rename the generated AppImage
mv "${APP_NAME}*.AppImage" "${APP_NAME}.AppImage"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Glob pattern won't expand inside double quotes.

The glob * inside double quotes is treated literally, so mv "${APP_NAME}*.AppImage" will fail to match the generated AppImage file.

Proposed fix
 # Rename the generated AppImage
-mv "${APP_NAME}*.AppImage" "${APP_NAME}.AppImage"
+mv ${APP_NAME}*.AppImage "${APP_NAME}.AppImage"
📝 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
# Rename the generated AppImage
mv "${APP_NAME}*.AppImage" "${APP_NAME}.AppImage"
# Rename the generated AppImage
mv ${APP_NAME}*.AppImage "${APP_NAME}.AppImage"
🤖 Prompt for AI Agents
In `@v3/examples/multiwindow-close/build/linux/appimage/build.sh` around lines 33
- 34, The mv command uses a glob inside double quotes so the * is literal and
won't match; update the rename step to allow shell glob expansion by removing
quotes around the source glob (keep quotes around the target) or explicitly
resolve the generated filename (e.g., via a shell expansion or find) before
calling mv; target the mv invocation that references APP_NAME (the line using mv
"${APP_NAME}*.AppImage" to "${APP_NAME}.AppImage") so the source is expanded as
APP_NAME*.AppImage and the destination remains quoted.

Comment on lines +5 to +6
# The Exec line includes %u to pass the URL to the application
Exec=/usr/local/bin/multiwindow %u

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find packaging configuration files
fd -a "nfpm.yaml" "v3/examples/multiwindow-close" 2>/dev/null || echo "No nfpm.yaml found"

# Also check for other packaging configs
fd -a "nfpm\|appimage\|deb\|rpm" "v3/examples/multiwindow-close/build/linux" 2>/dev/null | head -20

# Search for binary path references
rg -n "multiwindow|/usr/(local/)?bin" "v3/examples/multiwindow-close/build" 2>/dev/null | head -30

Repository: wailsapp/wails

Length of output: 2735


Desktop entry path conflicts with appimage binary location.

The desktop file hardcodes /usr/local/bin/multiwindow, which matches the nfpm (deb/rpm) installation target. However, the appimage build installs the binary to /usr/bin/, causing the launcher to fail for appimage distributions.

Either update the appimage build to install to /usr/local/bin or make the desktop file path configurable during packaging.

🔧 Example fix (adjust appimage to match nfpm path)
-mkdir -p "${APP_DIR}/usr/bin"
-cp -r "${APP_BINARY}" "${APP_DIR}/usr/bin/"
+mkdir -p "${APP_DIR}/usr/local/bin"
+cp -r "${APP_BINARY}" "${APP_DIR}/usr/local/bin/"
🤖 Prompt for AI Agents
In `@v3/examples/multiwindow-close/build/linux/desktop` around lines 5 - 6, The
desktop entry's Exec line is hardcoded to /usr/local/bin/multiwindow which
conflicts with the AppImage that installs the binary to /usr/bin; either make
the Exec path configurable during packaging or remove the absolute path and use
the binary name "multiwindow" (so the system PATH is used) in the desktop file,
or change the AppImage packaging to install to /usr/local/bin to match nfpm;
update the Exec line in the desktop file or adjust the AppImage install location
accordingly and ensure packaging templates (the desktop file generator or
AppImage build script) consistently produce the same install path.

Comment on lines +72 to +75
- docker run --rm -v "{{.ROOT_DIR}}:/app" {{.GO_CACHE_MOUNT}} {{.REPLACE_MOUNTS}} -e APP_NAME="{{.APP_NAME}}" "{{.CROSS_IMAGE}}" linux {{.DOCKER_ARCH}}
- docker run --rm -v "{{.ROOT_DIR}}:/app" alpine chown -R $(id -u):$(id -g) /app/bin
- mkdir -p {{.BIN_DIR}}
- mv "bin/{{.APP_NAME}}-linux-{{.DOCKER_ARCH}}" "{{.OUTPUT}}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, find the file and check its existence
find . -path "*v3/examples/multiwindow-close/build/linux/Taskfile.yml" -type f

Repository: wailsapp/wails

Length of output: 114


🏁 Script executed:

# List the directory structure to understand the context
git ls-files | grep -E "v3/examples/multiwindow-close" | head -20

Repository: wailsapp/wails

Length of output: 1155


🏁 Script executed:

# Get the actual content of the Taskfile.yml file
cat -n "v3/examples/multiwindow-close/build/linux/Taskfile.yml"

Repository: wailsapp/wails

Length of output: 9426


🏁 Script executed:

# Search for similar task patterns in the codebase to understand Taskfile usage
rg "platforms:" --type yaml -A 2 -B 2 | head -40

Repository: wailsapp/wails

Length of output: 3048


Restrict chown command to Unix platforms

The $(id -u):$(id -g) substitution uses POSIX shell syntax and will fail on Windows hosts. Since chown is not available on Windows anyway, restrict this command to Unix platforms.

🔧 Suggested fix
-      - docker run --rm -v "{{.ROOT_DIR}}:/app" alpine chown -R $(id -u):$(id -g) /app/bin
+      - cmd: docker run --rm -v "{{.ROOT_DIR}}:/app" alpine chown -R $(id -u):$(id -g) /app/bin
+        platforms: [linux, darwin]
📝 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
- docker run --rm -v "{{.ROOT_DIR}}:/app" {{.GO_CACHE_MOUNT}} {{.REPLACE_MOUNTS}} -e APP_NAME="{{.APP_NAME}}" "{{.CROSS_IMAGE}}" linux {{.DOCKER_ARCH}}
- docker run --rm -v "{{.ROOT_DIR}}:/app" alpine chown -R $(id -u):$(id -g) /app/bin
- mkdir -p {{.BIN_DIR}}
- mv "bin/{{.APP_NAME}}-linux-{{.DOCKER_ARCH}}" "{{.OUTPUT}}"
- docker run --rm -v "{{.ROOT_DIR}}:/app" {{.GO_CACHE_MOUNT}} {{.REPLACE_MOUNTS}} -e APP_NAME="{{.APP_NAME}}" "{{.CROSS_IMAGE}}" linux {{.DOCKER_ARCH}}
- cmd: docker run --rm -v "{{.ROOT_DIR}}:/app" alpine chown -R $(id -u):$(id -g) /app/bin
platforms: [linux, darwin]
- mkdir -p {{.BIN_DIR}}
- mv "bin/{{.APP_NAME}}-linux-{{.DOCKER_ARCH}}" "{{.OUTPUT}}"
🤖 Prompt for AI Agents
In `@v3/examples/multiwindow-close/build/linux/Taskfile.yml` around lines 72 - 75,
The docker run line that invokes alpine to chown using $(id -u):$(id -g) is
POSIX-only and will fail on Windows; update Taskfile.yml so that the chown step
is executed only on Unix hosts by moving that command into its own task or step
and adding a platform restriction (e.g., platforms: ["linux","darwin"]) or a
shell guard that detects POSIX (so the line containing 'docker run --rm -v
"{{.ROOT_DIR}}:/app" alpine chown -R $(id -u):$(id -g) /app/bin' runs only on
Unix), leaving the other docker/mv/mkdir steps unchanged.

Comment on lines +2 to +8
"fixed": {
"file_version": "0.1.0"
},
"info": {
"0000": {
"ProductVersion": "0.1.0",
"CompanyName": "My Company",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's explore the Windows build directory structure
find v3/examples/multiwindow-close/build/windows -type f -name "*.json" -o -name "*.xml" | head -20

Repository: wailsapp/wails

Length of output: 239


🏁 Script executed:

# Search for version patterns in the Windows examples
rg -n "file_version|ProductVersion|version" -i v3/examples/multiwindow-close/build/windows/

Repository: wailsapp/wails

Length of output: 3486


🏁 Script executed:

# Check for MSIX manifest or other Windows metadata files
fd -e appxmanifest -e xml -e json v3/examples/multiwindow-close/

Repository: wailsapp/wails

Length of output: 483


🏁 Script executed:

# Check if there are build scripts or CMakeLists that define version format
fd -e cmake -e bat -e ps1 v3/examples/multiwindow-close/build/

Repository: wailsapp/wails

Length of output: 507


🏁 Script executed:

# Look at similar examples to see how versions are handled
fd info.json v3/examples/ --exec cat -n {}

Repository: wailsapp/wails

Length of output: 4501


Align Windows resource versions to 4-part format for consistency with MSIX manifest.

The MSIX manifest files (app_manifest.xml, template.xml) use 4-part versions (Version="0.1.0.0"), while info.json stores 3-part versions ("0.1.0"). Although the NSIS build system appends ".0" to create 4-part versions at build time, aligning info.json directly to 4-part format ("0.1.0.0") improves consistency and explicitness across all Windows metadata.

Alignment to 4-part format
-  "file_version": "0.1.0"
+  "file_version": "0.1.0.0"
@@
-      "ProductVersion": "0.1.0",
+      "ProductVersion": "0.1.0.0",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"fixed": {
"file_version": "0.1.0"
},
"info": {
"0000": {
"ProductVersion": "0.1.0",
"CompanyName": "My Company",
"fixed": {
"file_version": "0.1.0.0"
},
"info": {
"0000": {
"ProductVersion": "0.1.0.0",
"CompanyName": "My Company",
🤖 Prompt for AI Agents
In `@v3/examples/multiwindow-close/build/windows/info.json` around lines 2 - 8,
The Windows resource versions in info.json are using 3-part strings; update them
to 4-part format to match the MSIX manifests: change the value of
"fixed"."file_version" from "0.1.0" to "0.1.0.0" and update
"info"."0000"."ProductVersion" (and any other ProductVersion/CompanyName entries
inside the same object) to use the 4-part version "0.1.0.0" so all Windows
metadata is explicitly consistent with the MSIX manifest format.

Comment on lines +145 to +179
# Install webview2 by launching the bootstrapper
# See https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution#online-only-deployment
!macro wails.webview2runtime
!ifndef WAILS_INSTALL_WEBVIEW_DETAILPRINT
!define WAILS_INSTALL_WEBVIEW_DETAILPRINT "Installing: WebView2 Runtime"
!endif

SetRegView 64
# If the admin key exists and is not empty then webview2 is already installed
ReadRegStr $0 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
${If} $0 != ""
Goto ok
${EndIf}

${If} ${REQUEST_EXECUTION_LEVEL} == "user"
# If the installer is run in user level, check the user specific key exists and is not empty then webview2 is already installed
ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
${If} $0 != ""
Goto ok
${EndIf}
${EndIf}

SetDetailsPrint both
DetailPrint "${WAILS_INSTALL_WEBVIEW_DETAILPRINT}"
SetDetailsPrint listonly

InitPluginsDir
CreateDirectory "$pluginsdir\webview2bootstrapper"
SetOutPath "$pluginsdir\webview2bootstrapper"
File "MicrosoftEdgeWebview2Setup.exe"
ExecWait '"$pluginsdir\webview2bootstrapper\MicrosoftEdgeWebview2Setup.exe" /silent /install'

SetDetailsPrint both
ok:
!macroend

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

NSIS ExecWait return code capture variable documentation

💡 Result:

In NSIS, ExecWait can capture the launched process’ exit code into a user variable by providing a 2nd parameter:

ExecWait '"$INSTDIR\someprogram.exe" parameters' $0
DetailPrint "Exit code: $0"
  • Syntax: ExecWait command [user_var(exit code)] [1]
  • If you do specify the output variable, NSIS writes the process exit code to that variable and sets the error flag only if ExecWait itself fails (e.g., couldn’t start the process). If an error occurs, the output variable is undefined. [1]
  • If you do not specify an output variable, ExecWait sets the error flag when the executed program returns a non-zero exit code or when an execution error occurs. [1]

Practical pattern:

ClearErrors
ExecWait '"$INSTDIR\application.exe"' $0
IfErrors 0 +2
  MessageBox MB_OK "Failed to execute (could not start process)"
StrCmp $0 0 done
MessageBox MB_OK "App returned exit code $0"
done:

Sources: [1] [2]


Capture WebView2 bootstrapper exit code and abort on failure.

The current code doesn't check the exit status of the installer, so a failed WebView2 installation will silently proceed and leave the application unusable. Capture the exit code and abort with a clear error message if installation fails.

🔒 Suggested fix
-    ExecWait '"$pluginsdir\webview2bootstrapper\MicrosoftEdgeWebview2Setup.exe" /silent /install'
+    ExecWait '"$pluginsdir\webview2bootstrapper\MicrosoftEdgeWebview2Setup.exe" /silent /install' $0
+    ${If} $0 != 0
+        MessageBox MB_OK "WebView2 Runtime installation failed (exit code $0)."
+        Quit
+    ${EndIf}
📝 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
# Install webview2 by launching the bootstrapper
# See https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution#online-only-deployment
!macro wails.webview2runtime
!ifndef WAILS_INSTALL_WEBVIEW_DETAILPRINT
!define WAILS_INSTALL_WEBVIEW_DETAILPRINT "Installing: WebView2 Runtime"
!endif
SetRegView 64
# If the admin key exists and is not empty then webview2 is already installed
ReadRegStr $0 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
${If} $0 != ""
Goto ok
${EndIf}
${If} ${REQUEST_EXECUTION_LEVEL} == "user"
# If the installer is run in user level, check the user specific key exists and is not empty then webview2 is already installed
ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
${If} $0 != ""
Goto ok
${EndIf}
${EndIf}
SetDetailsPrint both
DetailPrint "${WAILS_INSTALL_WEBVIEW_DETAILPRINT}"
SetDetailsPrint listonly
InitPluginsDir
CreateDirectory "$pluginsdir\webview2bootstrapper"
SetOutPath "$pluginsdir\webview2bootstrapper"
File "MicrosoftEdgeWebview2Setup.exe"
ExecWait '"$pluginsdir\webview2bootstrapper\MicrosoftEdgeWebview2Setup.exe" /silent /install'
SetDetailsPrint both
ok:
!macroend
# Install webview2 by launching the bootstrapper
# See https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution#online-only-deployment
!macro wails.webview2runtime
!ifndef WAILS_INSTALL_WEBVIEW_DETAILPRINT
!define WAILS_INSTALL_WEBVIEW_DETAILPRINT "Installing: WebView2 Runtime"
!endif
SetRegView 64
# If the admin key exists and is not empty then webview2 is already installed
ReadRegStr $0 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
${If} $0 != ""
Goto ok
${EndIf}
${If} ${REQUEST_EXECUTION_LEVEL} == "user"
# If the installer is run in user level, check the user specific key exists and is not empty then webview2 is already installed
ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
${If} $0 != ""
Goto ok
${EndIf}
${EndIf}
SetDetailsPrint both
DetailPrint "${WAILS_INSTALL_WEBVIEW_DETAILPRINT}"
SetDetailsPrint listonly
InitPluginsDir
CreateDirectory "$pluginsdir\webview2bootstrapper"
SetOutPath "$pluginsdir\webview2bootstrapper"
File "MicrosoftEdgeWebview2Setup.exe"
ExecWait '"$pluginsdir\webview2bootstrapper\MicrosoftEdgeWebview2Setup.exe" /silent /install' $0
${If} $0 != 0
MessageBox MB_OK "WebView2 Runtime installation failed (exit code $0)."
Quit
${EndIf}
SetDetailsPrint both
ok:
!macroend
🤖 Prompt for AI Agents
In `@v3/examples/multiwindow-close/build/windows/nsis/wails_tools.nsh` around
lines 145 - 179, The wails.webview2runtime macro currently runs the WebView2
bootstrapper with ExecWait but doesn't check its exit code; modify the ExecWait
call to capture the installer exit code into a variable (e.g. ExecWait
'"$pluginsdir\webview2bootstrapper\MicrosoftEdgeWebview2Setup.exe" /silent
/install' $R0), then test $R0 and if non-zero emit a clear failure message
(DetailPrint/MessageBox using WAILS_INSTALL_WEBVIEW_DETAILPRINT or a descriptive
string) and abort the installer (Abort) so the install stops on WebView2
failure; keep all other logic in the macro (ReadRegStr checks, CreateDirectory,
File) intact.

Comment on lines +188 to +192
.note {
margin-top: 10px;
opacity: 0.9;
}
} No newline at end of file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

CSS syntax error: extra closing brace.

There's a stray } on line 192 that will cause a CSS parse error. The .note rule is already properly closed on line 191.

🐛 Proposed fix
 .note {
     margin-top: 10px;
     opacity: 0.9;
 }
-}
📝 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
.note {
margin-top: 10px;
opacity: 0.9;
}
}
.note {
margin-top: 10px;
opacity: 0.9;
}
🧰 Tools
🪛 Biome (2.1.2)

[error] 191-192: Expected a qualified rule, or an at rule but instead found '}'.

Expected a qualified rule, or an at rule here.

(parse)

🤖 Prompt for AI Agents
In `@v3/examples/multiwindow-close/frontend/public/style.css` around lines 188 -
192, There's an extra closing brace after the .note rule (the stray "}"
following the .note block) causing a CSS parse error; locate the .note selector
and remove the redundant closing brace so the rule ends with a single "}" and
the file's brace pairs remain balanced, then run a quick CSS lint or open in a
browser to confirm no parse errors.

Comment on lines +3 to +5
go 1.25

require github.com/wailsapp/wails/v3 v3.0.0-alpha.62

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check repo Go version settings (go.work/go.mod/CI).
echo "=== Checking go.work files ==="
fd -a "go.work" 2>/dev/null || echo "No go.work found"

echo -e "\n=== Checking go directives in go.mod files ==="
fd -a "go.mod" 2>/dev/null | while read f; do
  echo "File: $f"
  rg -n "^go " "$f" 2>/dev/null || echo "  (no go directive)"
done

echo -e "\n=== Checking CI Go version settings ==="
fd "\.yml$|\.yaml$" .github 2>/dev/null | while read f; do
  echo "File: $f"
  rg -n "setup-go|go-version|GOVERSION" "$f" 2>/dev/null || echo "  (no Go version config)"
done

Repository: wailsapp/wails

Length of output: 5311


Downgrade Go version to match other v3 examples and CI testing.

The go 1.25 directive is inconsistent: all other v3 examples specify go 1.24.0, and CI tests v3 only against Go 1.24. Change this example to go 1.24.0 to ensure it builds in the current CI environment and maintains consistency across examples.

🤖 Prompt for AI Agents
In `@v3/examples/multiwindow-close/go.mod` around lines 3 - 5, Update the module's
Go version directive in go.mod from "go 1.25" to "go 1.24.0" to match other v3
examples and CI testing; edit the go directive line in the go.mod file so it
reads the 1.24.0 version string to ensure consistent builds with the v3 examples
and CI.

Comment on lines +87 to +96
// CloseUsingCurrent closes whichever window Wails considers "current".
func (g *GreetService) CloseUsingCurrent() WindowSummary {
if g.app == nil {
return WindowSummary{}
}

w := g.app.Window.Current()
info := WindowSummary{ID: w.ID(), Name: w.Name()}
w.Close()
return info

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find the Window struct and Current() method implementation
fd -t f "window" v3 | grep -E "\.(go)$" | head -20

Repository: wailsapp/wails

Length of output: 968


🏁 Script executed:

# Search for Window.Current() method definition
rg "func.*Current\(\)" v3 -A 5

Repository: wailsapp/wails

Length of output: 2179


🏁 Script executed:

# Search for Window manager implementation
rg "type Window" v3 -B 2 -A 10 | head -100

Repository: wailsapp/wails

Length of output: 5441


🏁 Script executed:

# Get the full Current() method implementation
sed -n '/func (wm \*WindowManager) Current() Window {/,/^}/p' v3/pkg/application/window_manager.go

Repository: wailsapp/wails

Length of output: 293


🏁 Script executed:

# Search for usages of .Current() to see how it's typically handled
rg "\.Current\(\)" v3 -B 2 -A 3 | head -80

Repository: wailsapp/wails

Length of output: 4854


🏁 Script executed:

# Check tests for Window.Current() to understand expected behavior
rg "Current\(\)" v3 -t go -B 3 -A 3 | grep -A 5 -B 5 "test\|Test"

Repository: wailsapp/wails

Length of output: 846


Add nil guard for Window.Current() result

The Window.Current() method can return nil (visible in the implementation: if wm.app.impl == nil { return nil }). The code directly dereferences w.ID() and w.Name() without checking, which will panic if Current returns nil. This pattern is consistently guarded throughout the codebase (menuitem_roles.go, menuitem_dev.go, examples/build/main.go, etc.).

Suggested fix
 	w := g.app.Window.Current()
+	if w == nil {
+		return WindowSummary{}
+	}
 	info := WindowSummary{ID: w.ID(), Name: w.Name()}
📝 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
// CloseUsingCurrent closes whichever window Wails considers "current".
func (g *GreetService) CloseUsingCurrent() WindowSummary {
if g.app == nil {
return WindowSummary{}
}
w := g.app.Window.Current()
info := WindowSummary{ID: w.ID(), Name: w.Name()}
w.Close()
return info
// CloseUsingCurrent closes whichever window Wails considers "current".
func (g *GreetService) CloseUsingCurrent() WindowSummary {
if g.app == nil {
return WindowSummary{}
}
w := g.app.Window.Current()
if w == nil {
return WindowSummary{}
}
info := WindowSummary{ID: w.ID(), Name: w.Name()}
w.Close()
return info
🤖 Prompt for AI Agents
In `@v3/examples/multiwindow-close/greetservice.go` around lines 87 - 96, The
CloseUsingCurrent method dereferences the result of g.app.Window.Current()
without checking for nil; add a nil guard after calling w :=
g.app.Window.Current() (e.g., if w == nil { return WindowSummary{} }) before
calling w.ID(), w.Name(), or w.Close() so the function safely returns an empty
WindowSummary when Current() is nil; update GreetService.CloseUsingCurrent to
check w for nil and only access ID/Name/Close when non-nil.


- `frontend/`: Contains your frontend code (HTML, CSS, JavaScript/TypeScript)
- `main.go`: The entry point of your Go backend
- `app.go`: Define your application structure and methods here

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

fd -e go . v3/examples/multiwindow-close --max-depth 1

Repository: wailsapp/wails

Length of output: 141


🏁 Script executed:

sed -n '45,55p' v3/examples/multiwindow-close/README.md

Repository: wailsapp/wails

Length of output: 510


Update README to reflect actual project files.

Line 49 references app.go, but this file does not exist in the example. The directory contains main.go and greetservice.go. Update the project structure section to accurately document these files.

🤖 Prompt for AI Agents
In `@v3/examples/multiwindow-close/README.md` at line 49, Update the README
project structure to accurately list the actual source files: replace the
incorrect `app.go` entry with `main.go` and `greetservice.go`, and give short
descriptions (e.g., `main.go`: application entrypoint and window management;
`greetservice.go`: greeting service/handler implementation) so the "project
structure" section matches the repository contents.

@leaanthony

Copy link
Copy Markdown
Member

Hi there 👋 This PR seems to be an example of the issue and not the fix so moving to draft for now.

@leaanthony
leaanthony marked this pull request as draft January 25, 2026 00:13
Xelus22 and others added 7 commits January 25, 2026 11:16
* Fix up templates to correct 1.23.0

* add changelog

---------

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
* Update code block range in howdoesitwork.mdx

* docs: add changelog entry for PR #4884

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* fix(v2/linux): fix crash on panic in JS-bound Go methods

WebKit2GTK installs signal handlers after gtk_main() starts, overriding
our SA_ONSTACK fix. This causes Go panics (e.g., nil pointer dereference)
in JS-bound methods to crash with 'non-Go code set up signal handler
without SA_ONSTACK flag'.

Fix by deferring signal handler installation via g_idle_add() to run
after GTK main loop starts, ensuring we fix handlers AFTER WebKit
has installed its own.

Fixes #3965

* docs: add changelog entry for Linux signal handler fix

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* fix: add missing EventsOffAll export to runtime templates

The EventsOffAll function was implemented in the internal runtime
(desktop/events.js) and exported in the wrapper runtime files, but was
missing from the public runtime templates that are generated in user
projects. This caused a type mismatch where EventsOffAll was declared
in the TypeScript definition but the implementation was missing.

This commit adds the EventsOffAll export and type declaration to all
frontend framework templates:
- Common templates (generate/assets/common)
- React / React-TS
- Vue / Vue-TS
- Svelte / Svelte-TS
- Preact / Preact-TS
- Lit / Lit-TS
- Vanilla / Vanilla-TS

Fixes #4703

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* docs: add EventsOffAll to runtime events documentation

Added EventsOffAll function documentation to both English and Chinese
versions of the runtime events reference.

The function was already implemented in Go (pkg/runtime/events.go) and
JavaScript (internal/frontend/runtime/desktop/events.js), but was missing
from the public documentation.

Related to #4703

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* chore: add EventsOffAll fix to changelog

Added entry for the EventsOffAll fix to the Unreleased section
of the changelog.

Related to #4703

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* docs: translate EventsOffAll description to Chinese

Improved consistency in Chinese documentation by translating the
EventsOffAll description from English to Chinese, matching the
style of other event method descriptions.

Related to #4703

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: cc <Zhuanz@MacBook-Pro.local>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
Updated getCurrentWindowID to safely access AppKit APIs by dispatching to the main thread when called from non-main threads. This prevents potential crashes or undefined behavior when accessing NSApp and window delegates from arbitrary Go goroutines.
@wimaha

wimaha commented Jan 25, 2026

Copy link
Copy Markdown
Contributor Author

Hi! I've found the problem. Window.Current() relies on the Darwin getCurrentWindowID(), which previously read [NSApp keyWindow] from whatever goroutine called it. Since AppKit isn’t thread-safe, it has lead to this strange behavior.

To resolve this, I've updated the getCurrentWindowID() function:

  • Always access AppKit on the main thread (uses dispatch_sync(dispatch_get_main_queue(), …) when needed)
  • handle nil windows/delegates
  • Fallback to [NSApp mainWindow] when keyWindow is nil
  • Return 0 when no suitable window exists

The actual fix can be found in this commit: 4c4a71c

If the change is positively reviewed, I will remove the new example since I don't think it needs to persist.

@wimaha wimaha changed the title [v3 alpha test] App Exposé shows “ghost” window after calling App.Window.Current() fix(v3): App Exposé shows “ghost” window after calling App.Window.Current() Jan 26, 2026
github-actions Bot and others added 7 commits January 26, 2026 22:16
Co-authored-by: leaanthony <1943904+leaanthony@users.noreply.github.com>
- Fix Dockerfile path (was pointing to non-existent setupwizard/docker)
- Add multi-platform build (linux/amd64 + linux/arm64)
- Add QEMU for ARM64 cross-compilation
- Add automatic trigger on Dockerfile.cross changes to v3-alpha
- Update SDK version default to 14.5

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add test-cross-compile job that tests CGO builds for all 6 platform/arch combos
- Add test-non-cgo job for pure Go cross-compilation verification
- Add test-summary job with GitHub Actions summary output
- Add skip_tests input for manual workflow dispatch
- Verify Linux binaries link to required GTK/WebKit libraries
- Verify binary format matches expected architecture

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: leaanthony <1943904+leaanthony@users.noreply.github.com>
Co-authored-by: leaanthony <1943904+leaanthony@users.noreply.github.com>
…4921)

* feat(v2): add runtime.ResetSignalHandlers() for Linux panic recovery

Add a new runtime function that allows users to reset signal handlers
before code that might panic from nil pointer dereferences.

On Linux, WebKit installs signal handlers without the SA_ONSTACK flag,
which prevents Go from properly recovering from panics caused by
SIGSEGV and other signals. This function adds SA_ONSTACK to the
relevant signal handlers (SIGSEGV, SIGBUS, SIGFPE, SIGABRT).

Usage:
```go
go func() {
    defer func() {
        if err := recover(); err != nil {
            log.Printf("Recovered: %v", err)
        }
    }()
    runtime.ResetSignalHandlers()
    // Code that might panic...
}()
```

The function is a no-op on macOS and Windows.

Fixes #3965

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test(v2): add panic-recovery-test example

Add an example that demonstrates the Linux signal handler issue (#3965)
and verifies the fix using runtime.ResetSignalHandlers().

The example includes:
- A Greet function that triggers a nil pointer dereference after a delay
- Auto-call from frontend after 5 seconds
- README with reproduction steps

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Feb 2, 2026

Copy link
Copy Markdown

@wimaha

wimaha commented Feb 2, 2026

Copy link
Copy Markdown
Contributor Author

Sorry. Just wanted to merge the upstream, but made a mistake. Will make a new clean PR.

@wimaha wimaha closed this Feb 2, 2026
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.