Skip to content

V3 Deep-link (Port of v2 feature) - #4289

Merged
atterpac merged 20 commits into
wailsapp:v3-alphafrom
atterpac:v3/deeplink
Jul 23, 2025
Merged

atterpac merged 20 commits into
wailsapp:v3-alphafrom
atterpac:v3/deeplink

Conversation

@atterpac

@atterpac atterpac commented May 18, 2025 •

Copy link
Copy Markdown
Member

This is a loose port of the v2 Custom Protocol Association feature initially introduced in #3000

There are some modification to how a user will use the API but the logic of gathering it is fairly similar

A unified Golang API for it is provided to the user regardless of OS as opposed to the v2 solution

app.OnApplicationEvent(events.Common.ApplicationLaunchedWithUrl, func(e *application.ApplicationEvent) {
   launchedURL := e.Context().URL() // Retrieve the URL from the event context
   log.Printf("Application launched with URL: %s", launchedURL)
})

I have tested on macOS but will need assistance to confirm/troubleshoot other OS, will include my hardware details below

Setting up custom protocol

Configuring the custom protocol is set inside the wails config file

{
 "name": "My App",
 "description": "An amazing Wails app!",
 "info": {
   "companyName": "My Company",
   "productName": "My Product",
   "protocols": [
     {
       "scheme": "myapp",
       "description": "My Application Custom Protocol"
     },
     {
       "scheme": "anotherprotocol",
       "description": "Another protocol for specific actions"
     }
   ]
 }
}

How it works

The config file will take care of most of the setup for you you will need to run task common:update:build-assets once updating your config file for the changes to take place

In all OS you will need a task package build in order for the custom protocol to work.

MacOS

Values are taken from the config file to populate the correct fields inside the plist

    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLName</key>
            <string>My Application Custom Protocol</string> <!-- From Protocol.Description in wails.json -->
            <key>CFBundleURLSchemes</key>
            <array>
                <string>myapp</string> <!-- From Protocol.Scheme in wails.json -->
            </array>
        </dict>
    </array>

Windows

**Requires an NSIS install step and makensis **
Windows will add the URL as an argument to the launch args, wails checks this on start up any launch args match your deep link protocol if so the application event is emitted saving you from having to parse and determine yourself.

Linux

Requires an install step

Linux is setup to configure your deep-link via nfpm and a .desktop file, on install the post-install.sh script will update the device database and MIME types to allow for deep linking.

Linux works similar to windows as it passes the url as an argument to the launch. Wails handles is the same way to populate the application event

Hardware details

 Wails (v3.0.0-dev)  Wails Doctor

# System

┌──────────────────────────────────────────────────┐
| Name          | MacOS                            |
| Version       | 15.4.1                           |
| ID            | 24E263                           |
| Branding      | Sequoia                          |
| Platform      | darwin                           |
| Architecture  | arm64                            |
| Apple Silicon | true                             |
| CPU           | Apple M4 Pro                     |
| CPU 1         | Apple M4 Pro                     |
| CPU 2         | Apple M4 Pro                     |
| GPU           | 16 cores, Metal Support: Metal 3 |
| Memory        | 24 GB                            |
└──────────────────────────────────────────────────┘

# Build Environment

┌─────────────────────────────────────────────────────────┐
| Wails CLI    | v3.0.0-dev                               |
| Go Version   | go1.24.1                                 |
| Revision     | 3716acaae4dc0bb6407a7d0a772b32d8509be6d0 |
| Modified     | true                                     |
| -buildmode   | exe                                      |
| -compiler    | gc                                       |
| CGO_CFLAGS   |                                          |
| CGO_CPPFLAGS |                                          |
| CGO_CXXFLAGS |                                          |
| CGO_ENABLED  | 1                                        |
| CGO_LDFLAGS  |                                          |
| GOARCH       | arm64                                    |
| GOARM64      | v8.0                                     |
| GOOS         | darwin                                   |
| vcs          | git                                      |
| vcs.modified | true                                     |
| vcs.revision | 3716acaae4dc0bb6407a7d0a772b32d8509be6d0 |
| vcs.time     | 2025-05-14T20:35:41Z                     |
└─────────────────────────────────────────────────────────┘

Summary by CodeRabbit

  • New Features

    • Added comprehensive support for custom protocol schemes (deep linking) across macOS, Windows, and Linux, enabling apps to handle custom URLs.
    • Introduced new event handling for application launches via custom URLs.
    • Provided detailed documentation and example projects demonstrating custom protocol integration and configuration.
  • Documentation

    • Added a new guide explaining how to implement and test custom protocol schemes in Wails applications.
  • Chores

    • Added configuration, packaging, and build automation files for cross-platform example projects.
    • Updated scripts and templates to support protocol registration and event emission for custom URL handling.

@coderabbitai

coderabbitai Bot commented May 18, 2025 •

Copy link
Copy Markdown
Contributor

Walkthrough

This change introduces comprehensive support for custom protocol schemes (deep linking) in Wails applications across macOS, Windows, and Linux. It includes new documentation, example projects, configuration files, build scripts, and runtime logic. The update adds event types, context handling, and platform-specific implementations to enable applications to register and respond to custom URL schemes, emitting events to the frontend when launched via a registered protocol.

Changes

Files/Paths Change Summary
docs/src/content/docs/guides/custom-protocol-association.mdx Added a detailed guide for implementing custom protocol schemes (deep linking) in Wails apps across all major platforms.
v3/examples/custom-protocol-example/* (README.md, .gitignore, Taskfile.yml, greetservice.go, main.go, etc.) Introduced a new example project demonstrating custom protocol usage, including Go backend, frontend assets, build scripts, configuration, and documentation.
v3/examples/custom-protocol-example/build/* (darwin, linux, windows, config.yml, etc.) Added cross-platform build and packaging scripts, configuration files, Info.plist templates, NSIS installer scripts, and Linux packaging support for the example.
v3/examples/custom-protocol-example/frontend/* (index.html, src/*, package.json, .gitignore, etc.) Added frontend implementation for the example, including event handling for URLs, UI, CSS, and generated JS bindings.
v3/internal/commands/build-assets.go Added ProtocolConfig struct and integrated protocol support into build/update config structs and asset update logic.
v3/internal/commands/build_assets/linux/nfpm/scripts/postinstall.sh Replaced empty script with logic to update desktop and MIME databases post-installation, with error handling.
v3/internal/commands/updatable_build_assets/darwin/Info.dev.plist.tmpl, Info.plist.tmpl Updated Info.plist templates to conditionally include custom URL scheme declarations based on provided protocols.
v3/internal/commands/updatable_build_assets/linux/desktop.tmpl Added a Linux desktop entry template supporting protocol MIME type handlers.
v3/internal/commands/updatable_build_assets/linux/nfpm/nfpm.yaml.tmpl Activated postinstall script in nfpm YAML template for Linux packaging.
v3/internal/runtime/desktop/@wailsio/runtime/src/event_types.ts Added new event type constant ApplicationLaunchedWithUrl to the Common event category.
v3/pkg/application/application_darwin.go, application_darwin_delegate.h, application_darwin_delegate.m Added macOS support for handling custom protocol Apple Events, including new handler class, initialization, and event emission.
v3/pkg/application/application_linux.go Extended Linux app to detect and handle custom protocol URLs from command-line arguments, emitting launch events.
v3/pkg/application/application_windows.go Enhanced Windows app to detect and handle custom protocol URLs from command-line arguments, emitting launch events.
v3/pkg/application/context_application_event.go Added/renamed constants and methods to support URL context in application events, including getter/setter for URL.
v3/pkg/events/events.txt, events.go Added new event identifier and struct field for ApplicationLaunchedWithUrl; incremented event IDs accordingly.

Sequence Diagram(s)

sequenceDiagram
    participant OS as Operating System
    participant App as Wails Application
    participant Backend as Go Backend
    participant Frontend as JS Frontend

    OS->>App: Launch via custom URL (e.g., myapp://...)
    App->>Backend: Emit ApplicationLaunchedWithUrl event with URL context
    Backend->>Frontend: Emit frontend event (e.g., "ShowURL") with URL data
    Frontend->>Frontend: Display or process received URL
Loading

Poem

🐇
A protocol hops through the door,
With URLs it brings to explore.
Mac, Windows, and Linux in sync,
Now apps can respond in a blink!
Events and handlers, all in a row—
Deep linking magic, ready to go.
Let’s celebrate with a joyful wink!


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 810dfbb and 101b096.

📒 Files selected for processing (2)
  • v3/internal/runtime/desktop/@wailsio/runtime/src/event_types.ts (1 hunks)
  • v3/pkg/events/events.txt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • v3/pkg/events/events.txt
  • v3/internal/runtime/desktop/@wailsio/runtime/src/event_types.ts
⏰ 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). (4)
  • GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (macos-latest, 1.24)
  • GitHub Check: semgrep-cloud-platform/scan
✨ Finishing Touches
  • 📝 Generate Docstrings

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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

Support

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

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

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

Documentation and Community

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

@sonarqubecloud

Copy link
Copy Markdown

@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: 13

🔭 Outside diff range comments (4)
v3/examples/custom-protocol-example/build/linux/nfpm/scripts/preremove.sh (1)

1-2: 🛠️ Refactor suggestion

Script is empty but should include cleanup logic for protocol handlers

This preremove script is executed before package removal on Linux systems. While the shebang is present, the script lacks any actual commands to clean up the custom protocol associations that were set up during installation.

Consider adding commands to unregister the protocol handlers from the system. Here's a suggested implementation:

 #!/bin/bash
+
+# Remove desktop file associations
+update-desktop-database -q
+
+# Clean up MIME type associations if necessary
+update-mime-database -q /usr/share/mime
v3/examples/custom-protocol-example/build/linux/nfpm/scripts/postinstall.sh (1)

1-2: 🛠️ Refactor suggestion

Script is empty but should update desktop and MIME databases

This postinstall script is intended to run after package installation to register the application and its custom protocol handlers on Linux systems. However, it's missing the necessary commands to update the system databases.

Add commands to update desktop and MIME databases to ensure proper protocol registration:

 #!/bin/bash
+
+# Update desktop file associations
+update-desktop-database -q
+
+# Update MIME type database
+update-mime-database -q /usr/share/mime
v3/examples/custom-protocol-example/README.md (1)

1-60: 🛠️ Refactor suggestion

Add information about the custom protocol feature

While the README provides good general guidance for Wails3 projects, it doesn't mention the custom protocol handling feature that this example specifically demonstrates. Consider adding a section explaining:

  • What custom protocols are
  • How they're implemented in this example
  • How to test the custom protocol functionality

Add a new section after "Getting Started" or "Project Structure":

## Custom Protocol Feature

This example demonstrates how to implement and use custom URL protocols (deep linking) in a Wails3 application. With this feature, your application can be launched from custom URLs like `wailsexample://some/path`.

### How It Works

1. The custom protocol is defined in `wails.json` under the `protocols` section:
   ```json
   "protocols": [
     {
       "scheme": "wailsexample",
       "description": "Wails Example Protocol"
     }
   ]
  1. The application listens for launch events via the custom protocol:

    app.OnApplicationEvent(events.Common.ApplicationLaunchedWithUrl, func(e *application.ApplicationEvent) {
      app.EmitEvent("frontend:ShowURL", e.Context().URL())
    })
  2. The frontend receives and displays the URL:

    window.runtime.EventsOn("frontend:ShowURL", (url) => {
      document.getElementById("launch-url").textContent = url;
    });

Testing the Custom Protocol

After building and installing the application:

  1. On macOS: Open Terminal and type: open "wailsexample://test/url"
  2. On Windows: Type wailsexample://test/url in the Run dialog (Win+R)
  3. On Linux: Run xdg-open "wailsexample://test/url" in a terminal

The application should launch and display the URL.


<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[uncategorized] ~47-~47: Loose punctuation mark.
Context: ...h your project structure:  - `frontend/`: Contains your frontend code (HTML, CSS,...

(UNLIKELY_OPENING_PUNCTUATION)

---

[style] ~59-~59: Using many exclamation marks might seem excessive (in this case: 3 exclamation marks for a text that’s 2020 characters long)
Context: ...wails3 build`.  Happy coding with Wails3! If you encounter any issues or have que...

(EN_EXCESSIVE_EXCLAMATION)

</details>
<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

11-11: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

---

19-19: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

---

33-33: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

</details>

</details>

</blockquote></details>
<details>
<summary>v3/pkg/application/context_application_event.go (1)</summary><blockquote>

`70-100`: _🛠️ Refactor suggestion_

**Identifier shadowing hides the package-level `url` constant**  

Inside `URL()` you redeclare `url`:

```go
url, ok := c.data[url]

After this line the variable url shadows the constant url, reducing clarity and increasing the chance of accidental misuse.
Rename the local variable (e.g. u or rawURL) to avoid shadowing:

-func (c ApplicationEventContext) URL() string {
-    url, ok := c.data[url]
+func (c ApplicationEventContext) URL() string {
+    rawURL, ok := c.data[url]
🧹 Nitpick comments (25)
v3/examples/custom-protocol-example/build/linux/nfpm/scripts/preinstall.sh (1)

1-2: Enhance script robustness & portability.

Currently this stub only includes a bash shebang. Consider switching to a #!/usr/bin/env bash shebang for portability, and add strict mode (set -euo pipefail) plus a comment describing why no preinstall actions are needed.

Apply this diff:

-#!/bin/bash
+#!/usr/bin/env bash
+set -euo pipefail
+# No preinstall steps required for the custom protocol example
v3/examples/custom-protocol-example/build/linux/nfpm/scripts/postremove.sh (1)

1-2: Make stub script portable & add strict mode.

As with preinstall.sh, consider using #!/usr/bin/env bash, enabling strict mode, and adding a placeholder comment:

-#!/bin/bash
+#!/usr/bin/env bash
+set -euo pipefail
+# No post-remove cleanup required for the custom protocol example
v3/examples/custom-protocol-example/build/windows/info.json (1)

1-15: Metadata contains placeholder values

The Windows build metadata is structured correctly, but contains generic placeholder values that should be customized for the actual application.

Consider updating the placeholder values to match the actual custom-protocol-example application:

 {
 	"fixed": {
 		"file_version": "0.0.1"
 	},
 	"info": {
 		"0000": {
 			"ProductVersion": "0.0.1",
-			"CompanyName": "My Company",
-			"FileDescription": "A program that does X",
-			"LegalCopyright": "(c) 2025, My Company",
-			"ProductName": "My Product",
-			"Comments": "Some Product Comments"
+			"CompanyName": "Wails",
+			"FileDescription": "Custom Protocol Example Application",
+			"LegalCopyright": "(c) 2025, Wails Contributors",
+			"ProductName": "Custom Protocol Example",
+			"Comments": "Demonstrates custom protocol handling in Wails applications"
 		}
 	}
 }
v3/examples/custom-protocol-example/frontend/src/counter.js (1)

1-9: Counter implementation looks good but could benefit from enhancements

The counter implementation is clean and functional for demonstration purposes. It properly encapsulates the counter state, sets up the event listener, and updates the UI.

For a more robust implementation in a production environment, consider adding:

 export function setupCounter(element) {
+  if (!element) return;
   let counter = 0
   const setCounter = (count) => {
     counter = count
     element.innerHTML = `count is ${counter}`
   }
-  element.addEventListener('click', () => setCounter(counter + 1))
+  const clickHandler = () => setCounter(counter + 1);
+  element.addEventListener('click', clickHandler)
   setCounter(0)
+  
+  // Return cleanup function to remove event listener if needed
+  return () => {
+    element.removeEventListener('click', clickHandler);
+  }
 }
v3/examples/custom-protocol-example/frontend/index.html (1)

1-13: Basic HTML structure is appropriate for the example

The HTML file provides a clean, minimal structure for mounting the frontend application. It properly includes the necessary meta tags and script reference.

Consider updating the title to better reflect the purpose of this example:

   <meta charset="UTF-8" />
   <link rel="icon" type="image/svg+xml" href="/vite.svg" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-  <title>Vite App</title>
+  <title>Wails Custom Protocol Example</title>
v3/examples/custom-protocol-example/greetservice.go (1)

1-7: Simple service implementation looks good

The GreetService implementation is clean, minimal, and appropriate for demonstration purposes.

Consider adding GoDoc comments to document the service and method:

 package main

+// GreetService provides greeting functionality for the application
 type GreetService struct{}

+// Greet returns a personalized greeting message for the given name
 func (g *GreetService) Greet(name string) string {
 	return "Hello " + name + "!"
 }
v3/internal/commands/updatable_build_assets/darwin/Info.dev.plist.tmpl (1)

54-68: Well-implemented macOS custom protocol configuration

The addition of the CFBundleURLTypes section correctly implements macOS custom URL scheme handling according to Apple's guidelines. The conditional template logic ensures this section only appears when protocols are defined in the application configuration.

Consider making the URL name format (wails.com.{{.Scheme}}) configurable in the future to allow developers to use their own domain formats, but this implementation is solid for the initial release of the feature.

For future enhancement:

-                <string>wails.com.{{.Scheme}}</string>
+                <string>{{.URLName | default (printf "wails.com.%s" .Scheme)}}</string>

This would allow developers to specify a custom URL name if desired, while maintaining the current pattern as a default.

v3/examples/custom-protocol-example/build/linux/appimage/build.sh (1)

17-31: DRY up architecture-specific download logic and pin linuxdeploy version.

The if/else blocks are almost identical except for the URL and binary name. Consider extracting into a helper function and pinning to a known release tag instead of continuous to avoid unpredictable breakage:

+LINUXDEPLOY_VERSION="1.6.2"
+download_linuxdeploy() {
+  local arch_suffix=$1  # "x86_64" or "aarch64"
+  local img="linuxdeploy-${arch_suffix}.AppImage"
+  local url="https://github.com/linuxdeploy/linuxdeploy/releases/download/${LINUXDEPLOY_VERSION}/${img}"
+  wget -q -4 -N "$url" && chmod +x "$img"
+  ./"$img" --appdir "${APP_DIR}" --output appimage
+}
 if [[ $(uname -m) == *x86_64* ]]; then
-   wget -q -4 -N https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
-   chmod +x linuxdeploy-x86_64.AppImage
-   ./linuxdeploy-x86_64.AppImage --appdir "${APP_DIR}" --output appimage
+  download_linuxdeploy "x86_64"
 else
-   wget -q -4 -N https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-aarch64.AppImage
-   chmod +x linuxdeploy-aarch64.AppImage
-   ./linuxdeploy-aarch64.AppImage --appdir "${APP_DIR}" --output appimage
+  download_linuxdeploy "aarch64"
 fi
v3/examples/custom-protocol-example/build/linux/nfpm/nfpm.yaml (1)

12-14: Update placeholder description to be meaningful.

The description "A program that does X" is a placeholder. Consider something like:

description: "Custom Protocol Example: registers and handles deep links on Linux"

This improves clarity for end users browsing package metadata.

v3/internal/commands/updatable_build_assets/linux/desktop.tmpl (1)

1-15: Consider adding StartupNotify and richer categories.

For a smoother UX, you could include:

StartupNotify=true
Categories=Utility;Network;

This ensures launch feedback is provided and the app appears under network-related menus.

v3/examples/custom-protocol-example/build/windows/wails.exe.manifest (1)

1-15: Assembly identity contains placeholder values

The manifest uses generic placeholder values for assembly identity (com.mycompany.myproduct). While this works for an example, in real applications it should be replaced with the actual application identifier.

The manifest correctly implements proper DPI awareness settings with appropriate fallbacks for different Windows versions, which is important for modern Windows applications.

-    <assemblyIdentity type="win32" name="com.mycompany.myproduct" version="0.0.1" processorArchitecture="*"/>
+    <assemblyIdentity type="win32" name="com.example.wailsexample" version="0.0.1" processorArchitecture="*"/>
v3/examples/custom-protocol-example/frontend/src/main.js (1)

37-45: Consider adding error handling for malformed URLs

The displayUrl function handles missing DOM elements but doesn't include parsing or validation for malformed URLs. In a production application, you might want to add validation and error handling for invalid URL formats.

window.displayUrl = function(url) {
    const urlElement = document.getElementById('received-url');
    if (urlElement) {
-        urlElement.textContent = url || "No URL received or an error occurred.";
+        try {
+            // Optional: validate or parse the URL if needed
+            if (!url) {
+                urlElement.textContent = "No URL received or an error occurred.";
+            } else {
+                // Display the URL, possibly with formatting or parsing
+                urlElement.textContent = url;
+            }
+        } catch (error) {
+            console.error("Error processing URL:", error);
+            urlElement.textContent = "Error processing URL: " + error.message;
+        }
    } else {
        console.error("Element with ID 'received-url' not found in displayUrl.");
    }
}
v3/pkg/application/application_darwin.go (1)

404-407: Consider adding URL validation

For robustness, consider validating the URL string before emitting the event. This could help prevent issues with malformed URLs.

func HandleCustomProtocol(urlCString *C.char) {
	urlString := C.GoString(urlCString)
+	// Optional: validate the URL
+	if urlString == "" {
+		// Log an error or warning for empty URL
+		return
+	}
	eventContext := newApplicationEventContext()
	eventContext.setURL(urlString)

	// Emit the standard event with the URL string as data
	applicationEvents <- &ApplicationEvent{
		Id:  uint(events.Common.ApplicationLaunchedWithUrl),
		ctx: eventContext,
	}
}
v3/examples/custom-protocol-example/frontend/src/style.css (2)

26-26: Simplify the font-family declaration

The font-family declaration is unnecessarily verbose since you've already defined a system font stack in the :root selector.

-  font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+  font-family: inherit;

1-142: Consider organizing CSS with comments for better maintainability

The stylesheet lacks organizational comments to separate different sections (variables, layout, components, etc.), which could make maintenance harder as the application grows.

Consider adding section comments like:

/* -----------------------------
   Variables & Root Styles
   ----------------------------- */

/* -----------------------------
   Typography 
   ----------------------------- */

/* -----------------------------
   Layout & Containers
   ----------------------------- */

/* -----------------------------
   Components
   ----------------------------- */

/* -----------------------------
   Media Queries
   ----------------------------- */
v3/examples/custom-protocol-example/README.md (1)

11-14: Add language identifiers to code blocks

Code blocks should have language identifiers for proper syntax highlighting.

-   ```
+   ```bash
    wails3 dev
    ```

-   ```
+   ```bash
    wails3 build
    ```

-   ```
+   ```bash
    go run .
    ```

Also applies to: 19-22, 33-36

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

11-11: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

v3/examples/custom-protocol-example/main.go (1)

54-54: Consider storing the window reference

The window creation result is assigned to _, discarding the reference. If you need to manipulate this window later, you should store the reference.

-	_ = app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
+	mainWindow := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
v3/examples/custom-protocol-example/build/config.yml (2)

47-47: Update the documentation URL placeholder

The URL placeholder "https://v3.wails.io/noit/done/yet" appears to be a temporary value that should be updated with the actual documentation URL before release.

-# More information at: https://v3.wails.io/noit/done/yet
+# More information at: https://wails.io/docs/guides/custom-protocol-association

67-67: Add newline at end of file

YAML files should end with a newline character to follow standard file formatting practices. This was flagged by static analysis.

 other:
   - name: My Other Data
+
🧰 Tools
🪛 YAMLlint (1.37.1)

[error] 67-67: no new line character at the end of file

(new-line-at-end-of-file)

v3/examples/custom-protocol-example/build/Taskfile.yml (2)

41-42: Redundant build command condition

The BUILD_COMMAND variable uses a conditional that returns the same value in both cases:

-      BUILD_COMMAND: '{{if eq .PRODUCTION "true"}}build{{else}}build{{end}}'
+      BUILD_COMMAND: 'build'

73-79: Skipping frontend development mode

Similar to the dependency installation task, the frontend development mode is also being skipped. This seems inconsistent with having a dedicated task for it.

If the example project doesn't need a frontend development mode, consider removing this task or adding a clarifying comment about why it's included but not implemented.

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

470-678: Event string mappings updated

The eventToJS map has been updated with the new event and all shifted IDs. This ensures that JavaScript event handlers will correctly receive the right event names.

One suggestion: Consider adding a test to verify that all events are properly registered in the map. This could prevent future issues when adding or removing events.

v3/examples/custom-protocol-example/build/linux/Taskfile.yml (2)

104-110: Dead variable – OUTPUTFILE is defined but never used

Keeping unused vars in a Taskfile is noise and increases maintenance overhead.
Unless another task imports this value, please remove it:

-      OUTPUTFILE: '{{.ROOT_DIR}}/build/linux/{{.APP_NAME}}.desktop'

73-75: Typo in task description

Creates a arch linux packager package → Creates an Arch Linux package.
Pure docs but visible in task --list.

docs/src/content/docs/guides/custom-protocol-association.mdx (1)

12-15: Minor wording improvement for readability

“…with a specific context or to perform a particular action.” → “…in a specific context or to perform a particular action.”
The preposition “in” matches the usual English construction.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~14-~14: The preposition “in” seems more likely in this position.
Context: ...ion communication. - Launching your app with a specific context or to perform a part...

(AI_EN_LECTOR_REPLACEMENT_PREPOSITION)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 3716aca and 2134bc6.

⛔ Files ignored due to path filters (5)
  • v3/examples/custom-protocol-example/build/appicon.png is excluded by !**/*.png
  • v3/examples/custom-protocol-example/build/windows/icon.ico is excluded by !**/*.ico
  • v3/examples/custom-protocol-example/frontend/package-lock.json is excluded by !**/package-lock.json
  • v3/examples/custom-protocol-example/frontend/public/vite.svg is excluded by !**/*.svg
  • v3/examples/custom-protocol-example/frontend/src/javascript.svg is excluded by !**/*.svg
📒 Files selected for processing (48)
  • docs/src/content/docs/guides/custom-protocol-association.mdx (1 hunks)
  • v3/examples/custom-protocol-example/.gitignore (1 hunks)
  • v3/examples/custom-protocol-example/README.md (1 hunks)
  • v3/examples/custom-protocol-example/Taskfile.yml (1 hunks)
  • v3/examples/custom-protocol-example/build/Taskfile.yml (1 hunks)
  • v3/examples/custom-protocol-example/build/config.yml (1 hunks)
  • v3/examples/custom-protocol-example/build/darwin/Info.dev.plist (1 hunks)
  • v3/examples/custom-protocol-example/build/darwin/Info.plist (1 hunks)
  • v3/examples/custom-protocol-example/build/darwin/Taskfile.yml (1 hunks)
  • v3/examples/custom-protocol-example/build/linux/Taskfile.yml (1 hunks)
  • v3/examples/custom-protocol-example/build/linux/appimage/build.sh (1 hunks)
  • v3/examples/custom-protocol-example/build/linux/nfpm/nfpm.yaml (1 hunks)
  • v3/examples/custom-protocol-example/build/linux/nfpm/scripts/postinstall.sh (1 hunks)
  • v3/examples/custom-protocol-example/build/linux/nfpm/scripts/postremove.sh (1 hunks)
  • v3/examples/custom-protocol-example/build/linux/nfpm/scripts/preinstall.sh (1 hunks)
  • v3/examples/custom-protocol-example/build/linux/nfpm/scripts/preremove.sh (1 hunks)
  • v3/examples/custom-protocol-example/build/windows/Taskfile.yml (1 hunks)
  • v3/examples/custom-protocol-example/build/windows/info.json (1 hunks)
  • v3/examples/custom-protocol-example/build/windows/nsis/project.nsi (1 hunks)
  • v3/examples/custom-protocol-example/build/windows/nsis/wails_tools.nsh (1 hunks)
  • v3/examples/custom-protocol-example/build/windows/wails.exe.manifest (1 hunks)
  • v3/examples/custom-protocol-example/frontend/.gitignore (1 hunks)
  • v3/examples/custom-protocol-example/frontend/bindings/github.com/wailsapp/wails/v3/examples/custom-protocol-example/greetservice.js (1 hunks)
  • v3/examples/custom-protocol-example/frontend/bindings/github.com/wailsapp/wails/v3/examples/custom-protocol-example/index.js (1 hunks)
  • v3/examples/custom-protocol-example/frontend/index.html (1 hunks)
  • v3/examples/custom-protocol-example/frontend/package.json (1 hunks)
  • v3/examples/custom-protocol-example/frontend/src/counter.js (1 hunks)
  • v3/examples/custom-protocol-example/frontend/src/main.js (1 hunks)
  • v3/examples/custom-protocol-example/frontend/src/style.css (1 hunks)
  • v3/examples/custom-protocol-example/greetservice.go (1 hunks)
  • v3/examples/custom-protocol-example/main.go (1 hunks)
  • v3/internal/commands/build-assets.go (5 hunks)
  • v3/internal/commands/build_assets/linux/nfpm/scripts/postinstall.sh (1 hunks)
  • v3/internal/commands/updatable_build_assets/darwin/Info.dev.plist.tmpl (1 hunks)
  • v3/internal/commands/updatable_build_assets/darwin/Info.plist.tmpl (1 hunks)
  • v3/internal/commands/updatable_build_assets/linux/desktop.tmpl (1 hunks)
  • v3/internal/commands/updatable_build_assets/linux/nfpm/nfpm.yaml.tmpl (2 hunks)
  • v3/internal/runtime/desktop/@wailsio/runtime/src/event_types.ts (1 hunks)
  • v3/pkg/application/application_darwin.go (3 hunks)
  • v3/pkg/application/application_darwin_delegate.h (1 hunks)
  • v3/pkg/application/application_darwin_delegate.m (3 hunks)
  • v3/pkg/application/application_linux.go (3 hunks)
  • v3/pkg/application/application_windows.go (4 hunks)
  • v3/pkg/application/context_application_event.go (3 hunks)
  • v3/pkg/events/events.go (6 hunks)
  • v3/pkg/events/events.txt (1 hunks)
  • v3/pkg/events/events_darwin.h (1 hunks)
  • v3/pkg/events/events_linux.h (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
v3/examples/custom-protocol-example/greetservice.go (1)
v3/examples/custom-protocol-example/frontend/bindings/github.com/wailsapp/wails/v3/examples/custom-protocol-example/greetservice.js (1)
  • Greet (13-15)
v3/pkg/application/application_darwin.go (3)
v3/pkg/events/events.go (2)
  • Mac (92-92)
  • Common (6-6)
v3/pkg/application/application_darwin_delegate.h (1)
  • HandleCustomProtocol (17-17)
v3/pkg/application/events.go (1)
  • ApplicationEvent (11-16)
v3/pkg/application/application_linux.go (2)
v3/pkg/application/events.go (1)
  • ApplicationEvent (11-16)
v3/pkg/events/events.go (1)
  • Common (6-6)
🪛 YAMLlint (1.37.1)
v3/examples/custom-protocol-example/build/config.yml

[error] 67-67: no new line character at the end of file

(new-line-at-end-of-file)

🪛 LanguageTool
v3/examples/custom-protocol-example/README.md

[uncategorized] ~47-~47: Loose punctuation mark.
Context: ...h your project structure: - frontend/: Contains your frontend code (HTML, CSS,...

(UNLIKELY_OPENING_PUNCTUATION)


[style] ~59-~59: Using many exclamation marks might seem excessive (in this case: 3 exclamation marks for a text that’s 2020 characters long)
Context: ...wails3 build`. Happy coding with Wails3! If you encounter any issues or have que...

(EN_EXCESSIVE_EXCLAMATION)

docs/src/content/docs/guides/custom-protocol-association.mdx

[uncategorized] ~14-~14: The preposition “in” seems more likely in this position.
Context: ...ion communication. - Launching your app with a specific context or to perform a part...

(AI_EN_LECTOR_REPLACEMENT_PREPOSITION)

🪛 markdownlint-cli2 (0.17.2)
v3/examples/custom-protocol-example/README.md

11-11: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


19-19: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


33-33: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

🔇 Additional comments (59)
v3/examples/custom-protocol-example/.gitignore (1)

1-6: Patterns appear comprehensive – approving .gitignore entries.

The listed build artifacts and dependency directories align with the platforms and tooling used in this example. No obvious omissions.

v3/examples/custom-protocol-example/frontend/.gitignore (1)

1-24: .gitignore looks solid.

This frontend ignore covers common logs, node_modules, dist outputs, and IDE files. No missing patterns detected.

v3/pkg/events/events.txt (1)

2-4: New event placement is correct.

common:ApplicationLaunchedWithUrl is appropriately inserted after ApplicationStarted. The ordering maintains the common group and aligns with the documentation of the event ID shift.

v3/internal/runtime/desktop/@wailsio/runtime/src/event_types.ts (1)

208-208: Event type addition looks good

The added ApplicationLaunchedWithUrl event type follows the proper naming convention and is correctly placed in the Common category since this feature works across all supported platforms.

v3/internal/commands/updatable_build_assets/darwin/Info.plist.tmpl (1)

49-63: Well-implemented macOS URL scheme registration

The implementation for macOS custom protocol handling is correctly done by adding the CFBundleURLTypes entry to the Info.plist template. The conditional block and template variable usage is consistent with the rest of the file.

v3/examples/custom-protocol-example/frontend/bindings/github.com/wailsapp/wails/v3/examples/custom-protocol-example/index.js (1)

1-8: Auto-generated binding file looks good

This file correctly imports and re-exports the GreetService module, making it available for the frontend. As noted in the comments, this is an auto-generated file that shouldn't be manually edited.

v3/internal/commands/updatable_build_assets/linux/nfpm/nfpm.yaml.tmpl (1)

31-36: Well-structured Linux package post-installation handling

The addition of the scripts section with a postinstall script is a good approach to ensure the Linux desktop database gets updated after installing the package. This is essential for proper recognition of custom protocol handlers on Linux systems.

You've also thoughtfully included commented placeholders for other lifecycle hooks that might be needed in the future.

v3/pkg/events/events_darwin.h (2)

9-140: Systematic event ID incrementation to accommodate new event

All event constant definitions have been incremented by 1 to accommodate the new ApplicationLaunchedWithUrl event in the event sequence. The changes are consistent and systematic across all event constants.

This approach maintains compatibility with existing event handlers while integrating the new custom protocol functionality.


142-142: Correctly updated MAX_EVENTS macro

The MAX_EVENTS macro has been properly incremented from 1188 to 1189 to reflect the addition of the new event. This ensures that memory allocations and event handling logic will have the correct upper bounds.

v3/examples/custom-protocol-example/frontend/bindings/github.com/wailsapp/wails/v3/examples/custom-protocol-example/greetservice.js (1)

1-15: Auto-generated binding looks correct

The binding file provides a clean interface for calling the Greet method from JavaScript. The TypeScript annotations improve developer experience with type checking, and the function correctly returns a cancellable promise.

As an auto-generated file, this follows the expected Wails binding patterns.

v3/internal/commands/build_assets/linux/nfpm/scripts/postinstall.sh (1)

1-22: Approve script for updating desktop and MIME databases.

The POSIX-compliant postinstall.sh correctly checks for update-desktop-database and update-mime-database, emits warnings if missing, and exits successfully. This covers the critical step of registering .desktop entries and custom protocol MIME types after installation.

v3/examples/custom-protocol-example/frontend/package.json (1)

1-17: Approve frontend package configuration.

The package.json sets up Vite for dev, build, and preview, pins the Wails runtime, and marks the project as private. This aligns with best practices for an example project.

v3/internal/commands/updatable_build_assets/linux/desktop.tmpl (2)

1-11: Approve Linux desktop entry template.

The static fields (Name, Comment, Exec %u, Icon, etc.) follow the .desktop spec correctly and will pass the URL via %u.


13-15: Approve conditional MimeType block.

The {{if .Info.Protocols}}…{{end}} block properly generates x-scheme-handler/{scheme} entries per protocol, enabling deep-link registration.

v3/examples/custom-protocol-example/Taskfile.yml (2)

13-33: Well-structured task organization for cross-platform development

The task structure efficiently uses platform-specific delegation with {{OS}} variable, providing a clean abstraction for build, package, and run operations across different operating systems. This aligns well with the cross-platform nature of the deep-linking feature being implemented.


3-7: Good separation of platform-specific build configurations

Including separate taskfiles for each platform (common, Windows, Darwin, Linux) follows best practices for organizing cross-platform build systems. This separation will make it easier to maintain the platform-specific protocol handler implementations.

v3/examples/custom-protocol-example/frontend/src/main.js (2)

31-35: Event handling is properly implemented

Good implementation of the event listener for receiving URL data from the backend. The code correctly logs received data and updates the UI accordingly.


16-18: Protocol scheme matches configuration

The example URL wailsexample://test/path?value=123&message=hello correctly demonstrates the custom protocol format. This aligns with the PR objectives for handling custom URL protocols.

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

60-61: Good registration of custom protocol handler

Adding the custom protocol handler registration in the init method ensures it's set up early in the application lifecycle. This aligns with macOS expectations for registering URL handlers.


398-409: Well-implemented URL protocol handler for macOS

The HandleCustomProtocol function correctly:

  1. Converts the C string URL to a Go string
  2. Creates an event context and sets the URL information
  3. Emits the cross-platform ApplicationLaunchedWithUrl event

This implementation follows established patterns in the codebase and provides a consistent event across platforms as mentioned in the PR objectives.

v3/examples/custom-protocol-example/build/darwin/Info.dev.plist (1)

31-41: Custom URL scheme registration looks correct

The custom URL scheme registration for "wailsexample" looks correct and follows the macOS requirements for registering a URL handler.

v3/examples/custom-protocol-example/build/darwin/Info.plist (1)

26-36: Custom URL scheme registration looks correct

The custom URL scheme registration for "wailsexample" looks correct and follows the macOS requirements for registering a URL handler.

v3/examples/custom-protocol-example/main.go (2)

44-47: Custom protocol handling looks good

The implementation correctly listens for the ApplicationLaunchedWithUrl event and forwards the URL to the frontend via an event emission.


34-34:

Details

✅ Verification successful

Verify GreetService implementation

The code imports GreetService, but its implementation isn't shown in this file. Ensure it exists and is properly defined elsewhere (likely in greetservice.go).


🏁 Script executed:

#!/bin/bash
# Check if the GreetService is defined in another file
fd "GreetService" --type file --exec grep -A 10 "type GreetService" {} \;

Length of output: 284


GreetService implementation verified
The GreetService type and its Greet method are defined in greetservice.go. No further changes needed.

v3/examples/custom-protocol-example/build/config.yml (1)

61-64: The custom protocol configuration looks good

The protocol configuration correctly defines the "wailsexample" scheme with an appropriate description, which aligns with the PR's objective to support custom URL protocols.

v3/pkg/application/application_darwin_delegate.h (3)

16-18: Well-structured protocol handling declaration

Good implementation of the function declaration for handling custom protocol URLs on macOS. This complements the existing file open handling.


19-22: Good Objective-C interface design

The CustomProtocolSchemeHandler interface with the Apple Event handler method follows good Objective-C practices and provides the necessary functionality for handling URL scheme events.


23-23: Well-defined initialization method

The StartCustomProtocolHandler function declaration follows the same pattern as other initialization functions in this file, maintaining consistency.

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

10-10: Good import addition

Appropriate addition of the strings import to support the URL detection logic.


148-177: Well-implemented command line parsing logic

The implementation correctly detects and handles command line arguments that could be custom protocol URLs or file associations. The logic is clear and well-structured, with detailed logging to aid debugging.

The approach of using strings.Contains(arg1, "://") for URL detection is straightforward and appropriate for this use case.


396-398: Improved code formatting

The formatting changes to the function call improve readability by breaking the long line into multiple lines.


413-415: Consistent formatting improvement

Similar to the previous formatting change, this improves readability for long function calls.

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

179-182: Improved log formatting

Good improvement to the formatting of the DBus connection error log message, making it more readable.

v3/examples/custom-protocol-example/build/darwin/Taskfile.yml (1)

1-77: Well-structured Darwin build configuration for custom protocol example

This Taskfile.yml provides a comprehensive set of tasks for building, packaging, and running the custom protocol example application on macOS. The configuration properly handles:

  • Architecture-specific builds (arm64, amd64, and universal binary)
  • Proper macOS deployment target specification (10.15)
  • App bundle creation with appropriate structure
  • Development vs. production builds
  • Code signing with ad-hoc identity

The implementation follows best practices for macOS application packaging and aligns well with the custom protocol feature being added.

v3/pkg/events/events_linux.h (2)

9-16: Event ID increments aligned with new URL protocol event

The event IDs have been correctly incremented by 1 to accommodate the new ApplicationLaunchedWithUrl event mentioned in the PR description.


18-18: MAX_EVENTS properly updated

The MAX_EVENTS constant has been correctly updated to 1057 to reflect the new event count.

v3/internal/commands/build-assets.go (5)

24-30: Good design for protocol configuration structure

The ProtocolConfig struct is well-designed with:

  • Essential fields for scheme and description
  • Proper JSON/YAML tags with omitempty for optional fields
  • Forward-thinking comments about potential platform-specific extensions

This structure provides a solid foundation for the custom protocol feature.


52-52: Protocol configuration properly integrated into BuildConfig

The Protocols field has been correctly added to the BuildConfig struct.


146-146: Protocol configuration properly integrated into UpdateConfig

The Protocols field has been correctly added to the UpdateConfig struct.


161-161: Protocol configuration properly integrated into WailsConfig

The Protocols field has been correctly added to the WailsConfig struct.


194-194: Correctly populating protocols from Wails configuration

The code properly assigns the protocols from the parsed configuration to the UpdateConfig struct.

v3/pkg/application/application_darwin_delegate.m (3)

4-4: Properly importing CoreServices for Apple Event constants

The CoreServices header is correctly imported to access the Apple Event constants needed for URL scheme handling.


184-191: Well-implemented custom protocol handler for Apple Events

The CustomProtocolSchemeHandler class properly implements the Apple Event handling mechanism for URL schemes:

  • Correctly extracts the URL from the event descriptor
  • Safely passes the URL string to the native HandleCustomProtocol function

This implementation follows Apple's recommended approach for handling custom URL schemes.


192-198: Good initialization of the custom protocol handler

The StartCustomProtocolHandler function correctly:

  • Gets the shared Apple Event Manager
  • Registers the handler class and selector for Internet events
  • Uses proper event class (kInternetEventClass) and event ID (kAEGetURL)

This ensures the application will properly receive URL scheme launch events.

v3/examples/custom-protocol-example/build/Taskfile.yml (2)

10-21: Frontend dependency installation is skipped

I notice that the frontend dependency installation task is configured to simply output a message and skip the actual installation. While this may be intentional for this example project, it could be confusing for users trying to learn from this example.

Is this intentional for the example project or should actual dependency installation commands be included? If intentional, consider adding a comment explaining why (e.g., "Example has no frontend dependencies").


1-86: Overall Taskfile structure is well-defined

The Taskfile is well-structured with clear task definitions, dependencies, and outputs. It provides a comprehensive set of tasks for building and running the custom protocol example application.

v3/examples/custom-protocol-example/build/windows/Taskfile.yml (4)

13-18: Good cross-platform file cleanup

The task handles .syso file cleanup differently based on the platform, which is a good practice. This ensures the build process works correctly on Windows, Linux, and macOS development environments.


32-38: Architecture-aware syso generation

The syso generation task correctly passes the architecture parameter, ensuring that the right resources are embedded for different CPU architectures.


40-53: NSIS installer generation appears complete

The task for creating the NSIS installer includes all necessary steps:

  1. Generating the WebView2 bootstrapper
  2. Setting architecture-specific flags
  3. Running makensis with the appropriate parameters

This ensures proper packaging for Windows distribution with custom protocol support.


55-57: Simple run command

The run task is straightforward and simply executes the built binary. This is appropriate for an example application.

v3/examples/custom-protocol-example/build/windows/nsis/project.nsi (5)

3-19: Well-documented NSIS script

The header comments provide clear instructions on how to use the script both within the Wails ecosystem and for manual debugging/development. This is excellent documentation practice.


34-47: Proper version information setup

The script correctly sets up version information for the installer using VIProductVersion and VIAddVersionKey. This ensures that Windows properly displays version and product metadata for the installed application.


48-50: HiDPI support enabled

The ManifestDPIAware true setting ensures the installer will display correctly on high-DPI displays, which is increasingly important for modern Windows applications.


94-97: File association integration

The script calls the wails.associateFiles macro which will handle registering the custom URL protocol handlers in the Windows registry. This is a key part of implementing the deep linking functionality.


99-112: Complete uninstaller implementation

The uninstaller section properly cleans up everything installed by the installer, including:

  1. WebView2 data path
  2. Installed files
  3. Shortcuts
  4. File associations

This ensures a clean uninstallation experience.

v3/pkg/events/events.go (4)

10-12: New ApplicationLaunchedWithUrl event added

The new event is correctly positioned in the commonEvents struct, which will be used to signal when the application is launched via a custom URL protocol.


39-63: Event ID sequencing updated

All common event IDs have been correctly incremented after the insertion of the new ApplicationLaunchedWithUrl event. This maintains the requirement for unique event IDs throughout the framework.


79-461: Platform-specific event IDs adjusted

All platform-specific event IDs (Linux, Mac, Windows) have been incremented to account for the new common event. This is necessary to maintain the integrity of the event system.


10-678:

Details

❓ Verification inconclusive

Verify event handling implementation across platforms

The addition of the ApplicationLaunchedWithUrl event is a significant feature that needs to work across all supported platforms. Make sure there's proper implementation and testing for this event in:

  1. macOS: URL handling via Info.plist CFBundleURLTypes
  2. Windows: Command-line argument parsing for URL protocols
  3. Linux: Desktop file associations and command-line parsing

🏁 Script executed:

#!/bin/bash
# Verify implementation of URL handling across platforms

# Check for platform-specific URL handling code
echo "Checking for macOS implementation..."
rg -A 5 "ApplicationLaunchedWithUrl" "v3/pkg/application/application_darwin.go"

echo "Checking for Windows implementation..."
rg -A 5 "ApplicationLaunchedWithUrl" "v3/pkg/application/application_windows.go"

echo "Checking for Linux implementation..."
rg -A 5 "ApplicationLaunchedWithUrl" "v3/pkg/application/application_linux.go"

# Check for URL context implementation
echo "Checking for URL context implementation..."
rg -A 3 "URL()" "v3/pkg/application/"

Length of output: 89171


URL handling wired up on macOS, Windows and Linux – please verify packaging & tests

  • macOS
    • CustomProtocolSchemeHandler in application_darwin_delegate.{h,m} registers the kAEGetURL AppleEvent
    • StartCustomProtocolHandler() and processURLRequest() in application_darwin.go emit ApplicationLaunchedWithUrl
  • Windows
    • application_windows.go checks strings.Contains(arg, "://"), sets URL via ApplicationEventContext.setURL, and raises ApplicationLaunchedWithUrl
  • Linux
    • application_linux.go uses the same “://” check, populates ApplicationEventContext and fires ApplicationLaunchedWithUrl
  • Event context
    • context_application_event.go implements setURL() and URL() accessors

Next steps:

  • Add a CFBundleURLTypes entry to your macOS Info.plist for your custom URL scheme
  • Add or update unit/integration tests on each platform to cover the new ApplicationLaunchedWithUrl event

Comment on lines +9 to +16
APP_DIR="${APP_NAME}.AppDir"

# Create AppDir structure
mkdir -p "${APP_DIR}/usr/bin"
cp -r "${APP_BINARY}" "${APP_DIR}/usr/bin/"
cp "${ICON_PATH}" "${APP_DIR}/"
cp "${DESKTOP_FILE}" "${APP_DIR}/"

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.

🛠️ Refactor suggestion

Add validation for required environment variables and robust error handling.

The script assumes that APP_NAME, APP_BINARY, ICON_PATH, and DESKTOP_FILE are set externally. To avoid silent failures or copying the wrong files, add explicit checks at the top:

: "${APP_NAME:?Environment variable APP_NAME must be set}"
: "${APP_BINARY:?Environment variable APP_BINARY must be set}"
: "${ICON_PATH:?Environment variable ICON_PATH must be set}"
: "${DESKTOP_FILE:?Environment variable DESKTOP_FILE must be set}"

This ensures the script exits with a clear message if any are missing.

🤖 Prompt for AI Agents
In v3/examples/custom-protocol-example/build/linux/appimage/build.sh around
lines 9 to 16, the script uses environment variables APP_NAME, APP_BINARY,
ICON_PATH, and DESKTOP_FILE without validating their presence, which can cause
silent failures. Add explicit checks at the top of the script using parameter
expansion with error messages for each variable to ensure the script exits
immediately with a clear message if any required variable is not set.

Comment on lines +33 to +35
# 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

Fix wildcard expansion in the rename step.

Quoting the glob prevents shell expansion, so the rename will fail:

- mv "${APP_NAME}*.AppImage" "${APP_NAME}.AppImage"
+ mv ${APP_NAME}*.AppImage "${APP_NAME}.AppImage"

Also consider guarding against multiple matches or missing files.

🤖 Prompt for AI Agents
In v3/examples/custom-protocol-example/build/linux/appimage/build.sh around
lines 33 to 35, the rename command uses a quoted wildcard which prevents shell
expansion, causing the rename to fail. Remove the quotes around the wildcard to
allow proper glob expansion. Additionally, add checks to handle cases where
multiple files match the pattern or no files are found, to avoid unexpected
behavior or errors during the rename step.

Comment on lines +46 to +50
# scripts:
# preinstall: ./build/linux/nfpm/scripts/preinstall.sh
# postinstall: ./build/linux/nfpm/scripts/postinstall.sh
# preremove: ./build/linux/nfpm/scripts/preremove.sh
# postremove: ./build/linux/nfpm/scripts/postremove.sh

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

Enable the postinstall script to update desktop and MIME databases.

The scripts block is currently commented out, so postinstall.sh will never run. Uncomment and point to the script you added:

 scripts:
   postinstall: ./build/linux/nfpm/scripts/postinstall.sh

Without this, users won’t get their MIME database updated, breaking deep-link registration.

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

🤖 Prompt for AI Agents
In v3/examples/custom-protocol-example/build/linux/nfpm/nfpm.yaml around lines
46 to 50, the scripts block is commented out, preventing the postinstall.sh
script from running. Uncomment the scripts section and ensure the postinstall
entry points to ./build/linux/nfpm/scripts/postinstall.sh so that the desktop
and MIME databases are updated correctly after installation.

Comment on lines +16 to +23
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}

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.

🛠️ Refactor suggestion

Duplicate CSS selectors for links found

The file contains duplicate selectors for a and a:hover with different styling values. The second occurrence (lines 64-71) will override the first (lines 16-23), potentially causing confusion or rendering inconsistencies.

-a {
-  font-weight: 500;
-  color: #646cff;
-  text-decoration: inherit;
-}
-a:hover {
-  color: #535bf2;
-}

/* Later in the file */
a {
  color: #007bff;
  text-decoration: none;
+  font-weight: 500;
}

Also applies to: 64-71

🤖 Prompt for AI Agents
In v3/examples/custom-protocol-example/frontend/src/style.css between lines
16-23 and 64-71, there are duplicate CSS selectors for 'a' and 'a:hover' with
conflicting styles. To fix this, consolidate the styles for these selectors into
a single block each, merging or choosing the appropriate properties to avoid
overrides and confusion. Remove the redundant duplicate selectors so that each
selector appears only once with the intended styles.

<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleName</key>
<string>My Product</string>

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.

🛠️ Refactor suggestion

Replace placeholder values with actual product information

The file contains generic placeholder values ("My Product", "com.mycompany.myproduct", etc.) and a future copyright year (2025) that should be replaced with actual information for this example.

-        <key>CFBundleName</key>
-            <string>My Product</string>
+        <key>CFBundleName</key>
+            <string>Custom Protocol Example</string>

-        <key>CFBundleIdentifier</key>
-            <string>com.mycompany.myproduct</string>
+        <key>CFBundleIdentifier</key>
+            <string>com.wailsapp.custom-protocol-example</string>

-        <key>CFBundleGetInfoString</key>
-            <string>Some Product Comments</string>
+        <key>CFBundleGetInfoString</key>
+            <string>Example application demonstrating custom protocol handling</string>

-        <key>NSHumanReadableCopyright</key>
-            <string>(c) 2025, My Company</string>
+        <key>NSHumanReadableCopyright</key>
+            <string>(c) 2023, Wails</string>

Also applies to: 11-11, 15-15, 25-25

🤖 Prompt for AI Agents
In v3/examples/custom-protocol-example/build/darwin/Info.dev.plist at lines 7,
11, 15, and 25, replace all placeholder values such as "My Product",
"com.mycompany.myproduct", and the copyright year "2025" with the actual product
name, bundle identifier, and current copyright year relevant to this example
project to ensure accurate and meaningful metadata.

Comment on lines 44 to 46
- (BOOL)applicationShouldHandleReopen:(NSNotification *)notification
hasVisibleWindows:(BOOL)flag {
hasVisibleWindows:(BOOL)flag { // Changed from NSApplication to NSNotification
if( hasListeners(EventApplicationShouldHandleReopen) ) {

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

Fix incorrect parameter type in applicationShouldHandleReopen method

The parameter type for the applicationShouldHandleReopen method has been changed from NSApplication * to NSNotification *, which doesn't match Apple's NSApplicationDelegate protocol definition.

According to Apple's documentation, the correct signature should be:

-- (BOOL)applicationShouldHandleReopen:(NSNotification *)notification
-                    hasVisibleWindows:(BOOL)flag { // Changed from NSApplication to NSNotification
+- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender
+                    hasVisibleWindows:(BOOL)flag {

This could cause unexpected behavior when the application is reopened.

🤖 Prompt for AI Agents
In v3/pkg/application/application_darwin_delegate.m around lines 44 to 46, the
method applicationShouldHandleReopen has an incorrect parameter type
NSNotification * instead of NSApplication *. Change the first parameter type
back to NSApplication * to match Apple's NSApplicationDelegate protocol
definition and ensure correct behavior when the application is reopened.

Comment on lines +14 to +20
vars:
BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}'
env:
GOOS: linux
CGO_ENABLED: 1
GOARCH: '{{.ARCH | default ARCH}}'
PRODUCTION: '{{.PRODUCTION | default "false"}}'

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.

🛠️ Refactor suggestion

Incorrect defaulting of GOARCH may yield empty or invalid builds

'{{.ARCH | default ARCH}}' attempts to default to the string literal ARCH, not the env-var/content of .ARCH.
If callers don’t pass an ARCH variable, the resulting GOARCH will literally be “ARCH”, causing the build to fail.

-      GOARCH: '{{.ARCH | default ARCH}}'
+      # Use sprig's `default` to fall back to amd64 when .ARCH is not provided
+      GOARCH: '{{ default "amd64" .ARCH }}'
📝 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
vars:
BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}'
env:
GOOS: linux
CGO_ENABLED: 1
GOARCH: '{{.ARCH | default ARCH}}'
PRODUCTION: '{{.PRODUCTION | default "false"}}'
vars:
BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}'
env:
GOOS: linux
CGO_ENABLED: 1
# Use sprig's `default` to fall back to amd64 when .ARCH is not provided
GOARCH: '{{ default "amd64" .ARCH }}'
PRODUCTION: '{{.PRODUCTION | default "false"}}'
🤖 Prompt for AI Agents
In v3/examples/custom-protocol-example/build/linux/Taskfile.yml around lines 14
to 20, the GOARCH variable is incorrectly defaulted to the string literal "ARCH"
instead of the value of the ARCH environment variable. To fix this, change the
default expression to use an actual default value like "amd64" or another valid
architecture string instead of "ARCH" so that if .ARCH is not provided, GOARCH
gets a valid default and the build does not fail.

Comment on lines +62 to 68
func (c *ApplicationEventContext) setData(data map[string]any) {
c.data = data
}

func (c ApplicationEventContext) setOpenedWithFile(filepath string) {
func (c *ApplicationEventContext) setOpenedWithFile(filepath string) {
c.data[filename] = filepath
}

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

Possible nil-map panic when setters are used on an un-initialised context

blankApplicationEventContext is created with data == nil.
Calling any setter (setURL, setOpenedFiles, …) on such a context will panic because writing to a nil map is invalid.

Safest fix: lazily initialise in each setter:

 if c.data == nil {
     c.data = make(map[string]any)
 }

Add this guard to all setter methods.

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

🤖 Prompt for AI Agents
In v3/pkg/application/context_application_event.go around lines 62 to 68, the
setter methods assign values to the c.data map without checking if it is
initialized, which can cause a nil-map panic. To fix this, add a check at the
start of each setter method to initialize c.data as an empty map if it is nil
before assigning any values. This lazy initialization will prevent panics when
setters are called on an uninitialized context.

Comment on lines +67 to +78
${If} ${AtLeastWin10}
!ifdef SUPPORTS_AMD64
${if} ${IsNativeAMD64}
Goto ok
${EndIf}
!endif

!ifdef SUPPORTS_ARM64
${if} ${IsNativeARM64}
Goto ok
${EndIf}
!endif

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

LogicLib keyword uses lowercase ${if} – NSIS will fail to compile

LogicLib is case-sensitive; use ${If}/${EndIf}.
The two blocks showing the issue:

${if} ${IsNativeAMD64}   ; invalid
...
${if} ${IsNativeARM64}   ; invalid
-            ${if} ${IsNativeAMD64}
+            ${If} ${IsNativeAMD64}
...
-            ${if} ${IsNativeARM64}
+            ${If} ${IsNativeARM64}

Same problem occurs again in the wails.files macro; patch both locations.

🤖 Prompt for AI Agents
In v3/examples/custom-protocol-example/build/windows/nsis/wails_tools.nsh around
lines 67 to 78, the LogicLib conditional keywords `${if}` and `${EndIf}` are
incorrectly lowercase, causing NSIS compilation failure. Replace all instances
of `${if}` with `${If}` and `${EndIf}` with `${EndIf}` (proper casing) in these
blocks. Also, locate the same incorrect usage in the `wails.files` macro within
this file and apply the same casing corrections to ensure successful
compilation.

Comment on lines +181 to +202
# Copy of APP_ASSOCIATE and APP_UNASSOCIATE macros from here https://gist.github.com/nikku/281d0ef126dbc215dd58bfd5b3a5cd5b
!macro APP_ASSOCIATE EXT FILECLASS DESCRIPTION ICON COMMANDTEXT COMMAND
; Backup the previously associated file class
ReadRegStr $R0 SHELL_CONTEXT "Software\Classes\.${EXT}" ""
WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "${FILECLASS}_backup" "$R0"

WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "" "${FILECLASS}"

WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}" "" `${DESCRIPTION}`
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\DefaultIcon" "" `${ICON}`
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell" "" "open"
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\open" "" `${COMMANDTEXT}`
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\open\command" "" `${COMMAND}`
!macroend

!macro APP_UNASSOCIATE EXT FILECLASS
; Backup the previously associated file class
ReadRegStr $R0 SHELL_CONTEXT "Software\Classes\.${EXT}" `${FILECLASS}_backup`
WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "" "$R0"

DeleteRegKey SHELL_CONTEXT `Software\Classes\${FILECLASS}`
!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

Undefined constant SHELL_CONTEXT will break registry operations

WriteRegStr SHELL_CONTEXT ... relies on a compile-time constant that is never !defined.
Define it once depending on REQUEST_EXECUTION_LEVEL (admin ⇒ HKLM, user ⇒ HKCU) so file-association macros work:

+# Determine registry root based on installer elevation
+!ifndef SHELL_CONTEXT
+    !if "${REQUEST_EXECUTION_LEVEL}" == "admin"
+        !define SHELL_CONTEXT "HKLM"
+    !else
+        !define SHELL_CONTEXT "HKCU"
+    !endif
+!endif

Place this before the first use of SHELL_CONTEXT.

🤖 Prompt for AI Agents
In v3/examples/custom-protocol-example/build/windows/nsis/wails_tools.nsh around
lines 181 to 202, the constant SHELL_CONTEXT used in registry operations is not
defined, causing failures. Define SHELL_CONTEXT before its first use based on
REQUEST_EXECUTION_LEVEL: set it to HKLM if admin level is requested, otherwise
set it to HKCU. This ensures the file association macros correctly target the
appropriate registry hive.

@dosubot dosubot Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Jul 12, 2025
@atterpac
atterpac enabled auto-merge July 13, 2025 01:41
@sonarqubecloud

Copy link
Copy Markdown

@atterpac
atterpac merged commit b5ef00f into wailsapp:v3-alpha Jul 23, 2025
@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Jul 23, 2025
@leaanthony

Copy link
Copy Markdown
Member

Thanks @atterpac 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli Documentation Improvements or additions to documentation lgtm This PR has been approved by a maintainer Linux MacOS runtime size:XXL This PR changes 1000+ lines, ignoring generated files. v3 Windows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants