+
Custom Protocol / Deep Link Test
+
+ This page demonstrates handling custom URL schemes (deep links).
+
+
+ Example Link:
+ Try opening this URL (e.g., by pasting it into your browser's address bar or using open your-app-scheme://... in terminal):
+
+ wailsexample://test/path?value=123&message=hello
+
+
+
+
Received URL:
+
Waiting for application to be opened via a custom URL...
+
+
+ `;
+ } else {
+ console.error('Element with ID "app" not found.');
+ }
+});
+
+// Listen for the event from Go
+Events.On('frontend:ShowURL', (e) => {
+ console.log('frontend:ShowURL event received, data:', e);
+ displayUrl(e.data);
+});
+
+// Make displayUrl available globally just in case, though direct call from event is better
+window.displayUrl = function(url) {
+ const urlElement = document.getElementById('received-url');
+ if (urlElement) {
+ urlElement.textContent = url || "No URL received or an error occurred.";
+ } else {
+ console.error("Element with ID 'received-url' not found in displayUrl.");
+ }
+}
diff --git a/v3/examples/custom-protocol-example/frontend/src/style.css b/v3/examples/custom-protocol-example/frontend/src/style.css
new file mode 100644
index 000000000..0fb047c33
--- /dev/null
+++ b/v3/examples/custom-protocol-example/frontend/src/style.css
@@ -0,0 +1,142 @@
+:root {
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color-scheme: light dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: #242424;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+a {
+ font-weight: 500;
+ color: #646cff;
+ text-decoration: inherit;
+}
+a:hover {
+ color: #535bf2;
+}
+
+body {
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ margin: 0; /* Reset default margin */
+ background-color: #f7f7f7;
+ color: #333;
+ display: flex; /* Use flexbox to center content */
+ justify-content: center; /* Center horizontally */
+ align-items: center; /* Center vertically */
+ min-height: 100vh; /* Full viewport height */
+ padding: 20px; /* Add some padding around the content */
+ box-sizing: border-box; /* Ensure padding doesn't expand body beyond viewport */
+}
+
+h1 {
+ color: #0056b3;
+ font-size: 1.8em;
+ margin-bottom: 0.8em;
+}
+
+#app {
+ width: 100%;
+ max-width: 600px;
+ margin: auto; /* This also helps in centering if body flex isn't enough or overridden */
+}
+
+.container {
+ background-color: #fff;
+ padding: 25px 30px;
+ border-radius: 8px;
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
+ text-align: left;
+}
+
+p {
+ line-height: 1.6;
+ font-size: 1em;
+ margin-bottom: 1em;
+}
+
+a {
+ color: #007bff;
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+.url-display {
+ margin-top: 25px;
+ padding: 15px;
+ background-color: #e9ecef;
+ border: 1px solid #ced4da;
+ border-radius: 4px;
+ font-family: 'Courier New', Courier, monospace;
+ font-size: 0.95em;
+ word-break: break-all;
+}
+
+.label {
+ font-weight: bold;
+ display: block;
+ margin-bottom: 8px;
+ color: #495057;
+}
+
+.logo {
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+ transition: filter 300ms;
+}
+.logo:hover {
+ filter: drop-shadow(0 0 2em #646cffaa);
+}
+.logo.vanilla:hover {
+ filter: drop-shadow(0 0 2em #f7df1eaa);
+}
+
+.card {
+ padding: 2em;
+}
+
+.read-the-docs {
+ color: #888;
+}
+
+button {
+ border-radius: 8px;
+ border: 1px solid transparent;
+ padding: 0.6em 1.2em;
+ font-size: 1em;
+ font-weight: 500;
+ font-family: inherit;
+ background-color: #1a1a1a;
+ cursor: pointer;
+ transition: border-color 0.25s;
+}
+button:hover {
+ border-color: #646cff;
+}
+button:focus,
+button:focus-visible {
+ outline: 4px auto -webkit-focus-ring-color;
+}
+
+@media (prefers-color-scheme: light) {
+ :root {
+ color: #213547;
+ background-color: #ffffff;
+ }
+ a:hover {
+ color: #747bff;
+ }
+ button {
+ background-color: #f9f9f9;
+ }
+}
diff --git a/v3/examples/custom-protocol-example/greetservice.go b/v3/examples/custom-protocol-example/greetservice.go
new file mode 100644
index 000000000..8972c39cd
--- /dev/null
+++ b/v3/examples/custom-protocol-example/greetservice.go
@@ -0,0 +1,7 @@
+package main
+
+type GreetService struct{}
+
+func (g *GreetService) Greet(name string) string {
+ return "Hello " + name + "!"
+}
diff --git a/v3/examples/custom-protocol-example/main.go b/v3/examples/custom-protocol-example/main.go
new file mode 100644
index 000000000..184c93371
--- /dev/null
+++ b/v3/examples/custom-protocol-example/main.go
@@ -0,0 +1,90 @@
+package main
+
+import (
+ "embed"
+ _ "embed"
+ "log"
+ "time"
+
+ "github.com/wailsapp/wails/v3/pkg/application"
+ "github.com/wailsapp/wails/v3/pkg/events"
+)
+
+// Wails uses Go's `embed` package to embed the frontend files into the binary.
+// Any files in the frontend/dist folder will be embedded into the binary and
+// made available to the frontend.
+// See https://pkg.go.dev/embed for more information.
+
+//go:embed frontend/dist/*
+var assets embed.FS
+
+// main function serves as the application's entry point. It initializes the application, creates a window,
+// and starts a goroutine that emits a time-based event every second. It subsequently runs the application and
+// logs any error that might occur.
+func main() {
+ // Create a new Wails application by providing the necessary options.
+ // Variables 'Name' and 'Description' are for application metadata.
+ // 'Assets' configures the asset server with the 'FS' variable pointing to the frontend files.
+ // 'Bind' is a list of Go struct instances. The frontend has access to the methods of these instances.
+ // 'Mac' options tailor the application when running an macOS.
+ app := application.New(application.Options{
+ Name: "custom-protocol-example",
+ Description: "A demo of using raw HTML & CSS",
+ Services: []application.Service{
+ application.NewService(&GreetService{}),
+ },
+ Assets: application.AssetOptions{
+ Handler: application.AssetFileServerFS(assets),
+ },
+ Mac: application.MacOptions{
+ ApplicationShouldTerminateAfterLastWindowClosed: true,
+ },
+ // Add a custom protocol scheme
+ Protocols: []application.Protocol{
+ {
+ Scheme: "myexampleapp",
+ Description: "My Example Application Custom Protocol",
+ },
+ },
+ })
+
+ // Listen for the system event indicating the app was launched with a URL
+ app.OnApplicationEvent(events.Common.ApplicationLaunchedWithUrl, func(e *application.ApplicationEvent) {
+ log.Println("ApplicationLaunchedWithUrl", e.Context().URL())
+ app.EmitEvent("frontend:ShowURL", e.Context().URL())
+ })
+
+ // Create a new window with the necessary options.
+ // 'Title' is the title of the window.
+ // 'Mac' options tailor the window when running on macOS.
+ // 'BackgroundColour' is the background colour of the window.
+ // 'URL' is the URL that will be loaded into the webview.
+ _ = app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
+ Title: "Window 1",
+ Mac: application.MacWindow{
+ InvisibleTitleBarHeight: 50,
+ Backdrop: application.MacBackdropTranslucent,
+ TitleBar: application.MacTitleBarHiddenInset,
+ },
+ BackgroundColour: application.NewRGB(27, 38, 54),
+ URL: "/",
+ })
+
+ // Create a goroutine that emits an event containing the current time every second.
+ // The frontend can listen to this event and update the UI accordingly.
+ go func() {
+ for {
+ now := time.Now().Format(time.RFC1123)
+ app.EmitEvent("time", now)
+ time.Sleep(time.Second)
+ }
+ }()
+
+ // Run the application. This blocks until the application has been exited.
+ err := app.Run()
+
+ // If an error occurred while running the application, log it and exit.
+ if err != nil {
+ log.Fatal(err)
+ }
+}