diff --git a/v3/examples/badge/frontend/src/main.ts b/v3/examples/badge/frontend/src/main.ts
index 934741aba..7f4ba9bfb 100644
--- a/v3/examples/badge/frontend/src/main.ts
+++ b/v3/examples/badge/frontend/src/main.ts
@@ -1,5 +1,5 @@
import {Events} from "@wailsio/runtime";
-import {SetBadge, RemoveBadge} from "../bindings/github.com/wailsapp/wails/v3/pkg/services/badge/badgeservice";
+import {SetBadge, RemoveBadge} from "../bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice";
const setButton = document.getElementById('set')! as HTMLButtonElement;
const removeButton = document.getElementById('remove')! as HTMLButtonElement;
diff --git a/v3/examples/badge/main.go b/v3/examples/badge/main.go
index 9de2944c0..f97f3924e 100644
--- a/v3/examples/badge/main.go
+++ b/v3/examples/badge/main.go
@@ -7,7 +7,7 @@ import (
"time"
"github.com/wailsapp/wails/v3/pkg/application"
- "github.com/wailsapp/wails/v3/pkg/services/badge"
+ "github.com/wailsapp/wails/v3/pkg/services/dock"
)
// Wails uses Go's `embed` package to embed the frontend files into the binary.
@@ -28,13 +28,13 @@ func main() {
// '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.
- badgeService := badge.New()
+ dockService := dock.New()
app := application.New(application.Options{
Name: "badge",
Description: "A demo of using raw HTML & CSS",
Services: []application.Service{
- application.NewService(badgeService),
+ application.NewService(dockService),
},
Assets: application.AssetOptions{
Handler: application.AssetFileServerFS(assets),
@@ -62,7 +62,7 @@ func main() {
// Store cleanup functions for proper resource management
removeBadgeHandler := app.Event.On("remove:badge", func(event *application.CustomEvent) {
- err := badgeService.RemoveBadge()
+ err := dockService.RemoveBadge()
if err != nil {
log.Fatal(err)
}
@@ -70,12 +70,12 @@ func main() {
setBadgeHandler := app.Event.On("set:badge", func(event *application.CustomEvent) {
text := event.Data.(string)
- err := badgeService.SetBadge(text)
+ err := dockService.SetBadge(text)
if err != nil {
log.Fatal(err)
}
})
-
+
// Note: In a production application, you would call these cleanup functions
// when the handlers are no longer needed, e.g., during shutdown:
// defer removeBadgeHandler()
diff --git a/v3/examples/dock/README.md b/v3/examples/dock/README.md
new file mode 100644
index 000000000..ad12c3f40
--- /dev/null
+++ b/v3/examples/dock/README.md
@@ -0,0 +1,59 @@
+# Welcome to Your New Wails3 Project!
+
+Congratulations on generating your Wails3 application! This README will guide you through the next steps to get your project up and running.
+
+## Getting Started
+
+1. Navigate to your project directory in the terminal.
+
+2. To run your application in development mode, use the following command:
+
+ ```
+ wails3 dev
+ ```
+
+ This will start your application and enable hot-reloading for both frontend and backend changes.
+
+3. To build your application for production, use:
+
+ ```
+ wails3 build
+ ```
+
+ This will create a production-ready executable in the `build` directory.
+
+## Exploring Wails3 Features
+
+Now that you have your project set up, it's time to explore the features that Wails3 offers:
+
+1. **Check out the examples**: The best way to learn is by example. Visit the `examples` directory in the `v3/examples` directory to see various sample applications.
+
+2. **Run an example**: To run any of the examples, navigate to the example's directory and use:
+
+ ```
+ go run .
+ ```
+
+ Note: Some examples may be under development during the alpha phase.
+
+3. **Explore the documentation**: Visit the [Wails3 documentation](https://v3.wails.io/) for in-depth guides and API references.
+
+4. **Join the community**: Have questions or want to share your progress? Join the [Wails Discord](https://discord.gg/JDdSxwjhGf) or visit the [Wails discussions on GitHub](https://github.com/wailsapp/wails/discussions).
+
+## Project Structure
+
+Take a moment to familiarize yourself with your project structure:
+
+- `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
+- `wails.json`: Configuration file for your Wails project
+
+## Next Steps
+
+1. Modify the frontend in the `frontend/` directory to create your desired UI.
+2. Add backend functionality in `main.go`.
+3. Use `wails3 dev` to see your changes in real-time.
+4. When ready, build your application with `wails3 build`.
+
+Happy coding with Wails3! If you encounter any issues or have questions, don't hesitate to consult the documentation or reach out to the Wails community.
diff --git a/v3/examples/dock/Taskfile.yml b/v3/examples/dock/Taskfile.yml
new file mode 100644
index 000000000..b80b6614a
--- /dev/null
+++ b/v3/examples/dock/Taskfile.yml
@@ -0,0 +1,34 @@
+version: '3'
+
+includes:
+ common: ./build/Taskfile.yml
+ windows: ./build/windows/Taskfile.yml
+ darwin: ./build/darwin/Taskfile.yml
+ linux: ./build/linux/Taskfile.yml
+
+vars:
+ APP_NAME: "dock"
+ BIN_DIR: "bin"
+ VITE_PORT: '{{.WAILS_VITE_PORT | default 9245}}'
+
+tasks:
+ build:
+ summary: Builds the application
+ cmds:
+ - task: "{{OS}}:build"
+
+ package:
+ summary: Packages a production build of the application
+ cmds:
+ - task: "{{OS}}:package"
+
+ run:
+ summary: Runs the application
+ cmds:
+ - task: "{{OS}}:run"
+
+ dev:
+ summary: Runs the application in development mode
+ cmds:
+ - wails3 dev -config ./build/config.yml -port {{.VITE_PORT}}
+
diff --git a/v3/examples/dock/build/Taskfile.yml b/v3/examples/dock/build/Taskfile.yml
new file mode 100644
index 000000000..5f3517efc
--- /dev/null
+++ b/v3/examples/dock/build/Taskfile.yml
@@ -0,0 +1,86 @@
+version: '3'
+
+tasks:
+ go:mod:tidy:
+ summary: Runs `go mod tidy`
+ internal: true
+ cmds:
+ - go mod tidy
+
+ install:frontend:deps:
+ summary: Install frontend dependencies
+ dir: frontend
+ sources:
+ - package.json
+ - package-lock.json
+ generates:
+ - node_modules/*
+ preconditions:
+ - sh: npm version
+ msg: "Looks like npm isn't installed. Npm is part of the Node installer: https://nodejs.org/en/download/"
+ cmds:
+ - npm install
+
+ build:frontend:
+ label: build:frontend (PRODUCTION={{.PRODUCTION}})
+ summary: Build the frontend project
+ dir: frontend
+ sources:
+ - "**/*"
+ generates:
+ - dist/**/*
+ deps:
+ - task: install:frontend:deps
+ - task: generate:bindings
+ vars:
+ BUILD_FLAGS:
+ ref: .BUILD_FLAGS
+ cmds:
+ - npm run {{.BUILD_COMMAND}} -q
+ env:
+ PRODUCTION: '{{.PRODUCTION | default "false"}}'
+ vars:
+ BUILD_COMMAND: '{{if eq .PRODUCTION "true"}}build{{else}}build:dev{{end}}'
+
+
+ generate:bindings:
+ label: generate:bindings (BUILD_FLAGS={{.BUILD_FLAGS}})
+ summary: Generates bindings for the frontend
+ deps:
+ - task: go:mod:tidy
+ sources:
+ - "**/*.[jt]s"
+ - exclude: frontend/**/*
+ - frontend/bindings/**/* # Rerun when switching between dev/production mode causes changes in output
+ - "**/*.go"
+ - go.mod
+ - go.sum
+ generates:
+ - frontend/bindings/**/*
+ cmds:
+ - wails3 generate bindings -f '{{.BUILD_FLAGS}}' -clean=true -ts
+
+ generate:icons:
+ summary: Generates Windows `.ico` and Mac `.icns` files from an image
+ dir: build
+ sources:
+ - "appicon.png"
+ generates:
+ - "darwin/icons.icns"
+ - "windows/icon.ico"
+ cmds:
+ - wails3 generate icons -input appicon.png -macfilename darwin/icons.icns -windowsfilename windows/icon.ico
+
+ dev:frontend:
+ summary: Runs the frontend in development mode
+ dir: frontend
+ deps:
+ - task: install:frontend:deps
+ cmds:
+ - npm run dev -- --port {{.VITE_PORT}} --strictPort
+
+ update:build-assets:
+ summary: Updates the build assets
+ dir: build
+ cmds:
+ - wails3 update build-assets -name "{{.APP_NAME}}" -binaryname "{{.APP_NAME}}" -config config.yml -dir .
diff --git a/v3/examples/dock/build/appicon.png b/v3/examples/dock/build/appicon.png
new file mode 100644
index 000000000..63617fe4f
Binary files /dev/null and b/v3/examples/dock/build/appicon.png differ
diff --git a/v3/examples/dock/build/config.yml b/v3/examples/dock/build/config.yml
new file mode 100644
index 000000000..aaa3240fb
--- /dev/null
+++ b/v3/examples/dock/build/config.yml
@@ -0,0 +1,63 @@
+# This file contains the configuration for this project.
+# When you update `info` or `fileAssociations`, run `wails3 task common:update:build-assets` to update the assets.
+# Note that this will overwrite any changes you have made to the assets.
+version: '3'
+
+# This information is used to generate the build assets.
+info:
+ companyName: "My Company" # The name of the company
+ productName: "My Product" # The name of the application
+ productIdentifier: "com.mycompany.myproduct" # The unique product identifier
+ description: "A program that does X" # The application description
+ copyright: "(c) 2025, My Company" # Copyright text
+ comments: "Some Product Comments" # Comments
+ version: "0.0.1" # The application version
+
+# Dev mode configuration
+dev_mode:
+ root_path: .
+ log_level: warn
+ debounce: 1000
+ ignore:
+ dir:
+ - .git
+ - node_modules
+ - frontend
+ - bin
+ file:
+ - .DS_Store
+ - .gitignore
+ - .gitkeep
+ watched_extension:
+ - "*.go"
+ git_ignore: true
+ executes:
+ - cmd: wails3 task common:install:frontend:deps
+ type: once
+ - cmd: wails3 task common:dev:frontend
+ type: background
+ - cmd: go mod tidy
+ type: blocking
+ - cmd: wails3 task build
+ type: blocking
+ - cmd: wails3 task run
+ type: primary
+
+# File Associations
+# More information at: https://v3.wails.io/noit/done/yet
+fileAssociations:
+# - ext: wails
+# name: Wails
+# description: Wails Application File
+# iconName: wailsFileIcon
+# role: Editor
+# - ext: jpg
+# name: JPEG
+# description: Image File
+# iconName: jpegFileIcon
+# role: Editor
+# mimeType: image/jpeg # (optional)
+
+# Other data
+other:
+ - name: My Other Data
\ No newline at end of file
diff --git a/v3/examples/dock/build/darwin/Info.dev.plist b/v3/examples/dock/build/darwin/Info.dev.plist
new file mode 100644
index 000000000..58d7f65e7
--- /dev/null
+++ b/v3/examples/dock/build/darwin/Info.dev.plist
@@ -0,0 +1,32 @@
+
+
+
+ CFBundlePackageType
+ APPL
+ CFBundleName
+ My Product
+ CFBundleExecutable
+ dock
+ CFBundleIdentifier
+ com.wails.dock
+ CFBundleVersion
+ 0.1.0
+ CFBundleGetInfoString
+ This is a comment
+ CFBundleShortVersionString
+ 0.1.0
+ CFBundleIconFile
+ icons
+ LSMinimumSystemVersion
+ 10.15.0
+ NSHighResolutionCapable
+ true
+ NSHumanReadableCopyright
+ © now, My Company
+ NSAppTransportSecurity
+
+ NSAllowsLocalNetworking
+
+
+
+
\ No newline at end of file
diff --git a/v3/examples/dock/build/darwin/Info.plist b/v3/examples/dock/build/darwin/Info.plist
new file mode 100644
index 000000000..aff7a2812
--- /dev/null
+++ b/v3/examples/dock/build/darwin/Info.plist
@@ -0,0 +1,27 @@
+
+
+
+ CFBundlePackageType
+ APPL
+ CFBundleName
+ My Product
+ CFBundleExecutable
+ dock
+ CFBundleIdentifier
+ com.wails.dock
+ CFBundleVersion
+ 0.1.0
+ CFBundleGetInfoString
+ This is a comment
+ CFBundleShortVersionString
+ 0.1.0
+ CFBundleIconFile
+ icons
+ LSMinimumSystemVersion
+ 10.15.0
+ NSHighResolutionCapable
+ true
+ NSHumanReadableCopyright
+ © now, My Company
+
+
\ No newline at end of file
diff --git a/v3/examples/dock/build/darwin/Taskfile.yml b/v3/examples/dock/build/darwin/Taskfile.yml
new file mode 100644
index 000000000..f0791fea9
--- /dev/null
+++ b/v3/examples/dock/build/darwin/Taskfile.yml
@@ -0,0 +1,81 @@
+version: '3'
+
+includes:
+ common: ../Taskfile.yml
+
+tasks:
+ build:
+ summary: Creates a production build of the application
+ deps:
+ - task: common:go:mod:tidy
+ - task: common:build:frontend
+ vars:
+ BUILD_FLAGS:
+ ref: .BUILD_FLAGS
+ PRODUCTION:
+ ref: .PRODUCTION
+ - task: common:generate:icons
+ cmds:
+ - go build {{.BUILD_FLAGS}} -o {{.OUTPUT}}
+ vars:
+ BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-buildvcs=false -gcflags=all="-l"{{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"
+ PRODUCTION: '{{.PRODUCTION | default "false"}}'
+
+ build:universal:
+ summary: Builds darwin universal binary (arm64 + amd64)
+ deps:
+ - task: build
+ vars:
+ ARCH: amd64
+ OUTPUT: "{{.BIN_DIR}}/{{.APP_NAME}}-amd64"
+ - task: build
+ vars:
+ ARCH: arm64
+ OUTPUT: "{{.BIN_DIR}}/{{.APP_NAME}}-arm64"
+ cmds:
+ - lipo -create -output "{{.BIN_DIR}}/{{.APP_NAME}}" "{{.BIN_DIR}}/{{.APP_NAME}}-amd64" "{{.BIN_DIR}}/{{.APP_NAME}}-arm64"
+ - rm "{{.BIN_DIR}}/{{.APP_NAME}}-amd64" "{{.BIN_DIR}}/{{.APP_NAME}}-arm64"
+
+ package:
+ summary: Packages a production build of the application into a `.app` bundle
+ deps:
+ - task: build
+ vars:
+ PRODUCTION: "true"
+ cmds:
+ - task: create:app:bundle
+
+ package:universal:
+ summary: Packages darwin universal binary (arm64 + amd64)
+ deps:
+ - task: build:universal
+ cmds:
+ - task: create:app:bundle
+
+
+ create:app:bundle:
+ summary: Creates an `.app` bundle
+ cmds:
+ - mkdir -p {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/{MacOS,Resources}
+ - cp build/darwin/icons.icns {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/Resources
+ - cp {{.BIN_DIR}}/{{.APP_NAME}} {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/MacOS
+ - cp build/darwin/Info.plist {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents
+ - codesign --force --deep --sign - {{.BIN_DIR}}/{{.APP_NAME}}.app
+
+ run:
+ cmds:
+ - mkdir -p {{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/{MacOS,Resources}
+ - cp build/darwin/icons.icns {{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/Resources
+ - cp {{.BIN_DIR}}/{{.APP_NAME}} {{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/MacOS
+ - cp build/darwin/Info.dev.plist {{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/Info.plist
+ - codesign --force --deep --sign - {{.BIN_DIR}}/{{.APP_NAME}}.dev.app
+ - '{{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/MacOS/{{.APP_NAME}}'
diff --git a/v3/examples/dock/build/darwin/icons.icns b/v3/examples/dock/build/darwin/icons.icns
new file mode 100644
index 000000000..1b5bd4c86
Binary files /dev/null and b/v3/examples/dock/build/darwin/icons.icns differ
diff --git a/v3/examples/dock/build/linux/Taskfile.yml b/v3/examples/dock/build/linux/Taskfile.yml
new file mode 100644
index 000000000..560cc9c92
--- /dev/null
+++ b/v3/examples/dock/build/linux/Taskfile.yml
@@ -0,0 +1,119 @@
+version: '3'
+
+includes:
+ common: ../Taskfile.yml
+
+tasks:
+ build:
+ summary: Builds the application for Linux
+ deps:
+ - task: common:go:mod:tidy
+ - task: common:build:frontend
+ vars:
+ BUILD_FLAGS:
+ ref: .BUILD_FLAGS
+ PRODUCTION:
+ ref: .PRODUCTION
+ - task: common:generate:icons
+ cmds:
+ - go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}}
+ 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"}}'
+
+ package:
+ summary: Packages a production build of the application for Linux
+ deps:
+ - task: build
+ vars:
+ PRODUCTION: "true"
+ cmds:
+ - task: create:appimage
+ - task: create:deb
+ - task: create:rpm
+ - task: create:aur
+
+ create:appimage:
+ summary: Creates an AppImage
+ dir: build/linux/appimage
+ deps:
+ - task: build
+ vars:
+ PRODUCTION: "true"
+ - task: generate:dotdesktop
+ cmds:
+ - cp {{.APP_BINARY}} {{.APP_NAME}}
+ - cp ../../appicon.png appicon.png
+ - wails3 generate appimage -binary {{.APP_NAME}} -icon {{.ICON}} -desktopfile {{.DESKTOP_FILE}} -outputdir {{.OUTPUT_DIR}} -builddir {{.ROOT_DIR}}/build/linux/appimage/build
+ vars:
+ APP_NAME: '{{.APP_NAME}}'
+ APP_BINARY: '../../../bin/{{.APP_NAME}}'
+ ICON: '../../appicon.png'
+ DESKTOP_FILE: '../{{.APP_NAME}}.desktop'
+ OUTPUT_DIR: '../../../bin'
+
+ create:deb:
+ summary: Creates a deb package
+ deps:
+ - task: build
+ vars:
+ PRODUCTION: "true"
+ cmds:
+ - task: generate:dotdesktop
+ - task: generate:deb
+
+ create:rpm:
+ summary: Creates a rpm package
+ deps:
+ - task: build
+ vars:
+ PRODUCTION: "true"
+ cmds:
+ - task: generate:dotdesktop
+ - task: generate:rpm
+
+ create:aur:
+ summary: Creates a arch linux packager package
+ deps:
+ - task: build
+ vars:
+ PRODUCTION: "true"
+ cmds:
+ - task: generate:dotdesktop
+ - task: generate:aur
+
+ generate:deb:
+ summary: Creates a deb package
+ cmds:
+ - wails3 tool package -name {{.APP_NAME}} -format deb -config ./build/linux/nfpm/nfpm.yaml -out {{.ROOT_DIR}}/bin
+
+ generate:rpm:
+ summary: Creates a rpm package
+ cmds:
+ - wails3 tool package -name {{.APP_NAME}} -format rpm -config ./build/linux/nfpm/nfpm.yaml -out {{.ROOT_DIR}}/bin
+
+ generate:aur:
+ summary: Creates a arch linux packager package
+ cmds:
+ - wails3 tool package -name {{.APP_NAME}} -format archlinux -config ./build/linux/nfpm/nfpm.yaml -out {{.ROOT_DIR}}/bin
+
+ generate:dotdesktop:
+ summary: Generates a `.desktop` file
+ dir: build
+ cmds:
+ - mkdir -p {{.ROOT_DIR}}/build/linux/appimage
+ - wails3 generate .desktop -name "{{.APP_NAME}}" -exec "{{.EXEC}}" -icon "{{.ICON}}" -outputfile {{.ROOT_DIR}}/build/linux/{{.APP_NAME}}.desktop -categories "{{.CATEGORIES}}"
+ vars:
+ APP_NAME: '{{.APP_NAME}}'
+ EXEC: '{{.APP_NAME}}'
+ ICON: 'appicon'
+ CATEGORIES: 'Development;'
+ OUTPUTFILE: '{{.ROOT_DIR}}/build/linux/{{.APP_NAME}}.desktop'
+
+ run:
+ cmds:
+ - '{{.BIN_DIR}}/{{.APP_NAME}}'
diff --git a/v3/examples/dock/build/linux/appimage/build.sh b/v3/examples/dock/build/linux/appimage/build.sh
new file mode 100644
index 000000000..85901c34e
--- /dev/null
+++ b/v3/examples/dock/build/linux/appimage/build.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+# Copyright (c) 2018-Present Lea Anthony
+# SPDX-License-Identifier: MIT
+
+# Fail script on any error
+set -euxo pipefail
+
+# Define variables
+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}/"
+
+if [[ $(uname -m) == *x86_64* ]]; then
+ # Download linuxdeploy and make it executable
+ wget -q -4 -N https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
+ chmod +x linuxdeploy-x86_64.AppImage
+
+ # Run linuxdeploy to bundle the application
+ ./linuxdeploy-x86_64.AppImage --appdir "${APP_DIR}" --output appimage
+else
+ # Download linuxdeploy and make it executable (arm64)
+ wget -q -4 -N https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-aarch64.AppImage
+ chmod +x linuxdeploy-aarch64.AppImage
+
+ # Run linuxdeploy to bundle the application (arm64)
+ ./linuxdeploy-aarch64.AppImage --appdir "${APP_DIR}" --output appimage
+fi
+
+# Rename the generated AppImage
+mv "${APP_NAME}*.AppImage" "${APP_NAME}.AppImage"
+
diff --git a/v3/examples/dock/build/linux/desktop b/v3/examples/dock/build/linux/desktop
new file mode 100644
index 000000000..ddb025903
--- /dev/null
+++ b/v3/examples/dock/build/linux/desktop
@@ -0,0 +1,13 @@
+[Desktop Entry]
+Version=1.0
+Name=My Product
+Comment=My Product Description
+# The Exec line includes %u to pass the URL to the application
+Exec=/usr/local/bin/dock %u
+Terminal=false
+Type=Application
+Icon=dock
+Categories=Utility;
+StartupWMClass=dock
+
+
diff --git a/v3/examples/dock/build/linux/nfpm/nfpm.yaml b/v3/examples/dock/build/linux/nfpm/nfpm.yaml
new file mode 100644
index 000000000..6e9be6550
--- /dev/null
+++ b/v3/examples/dock/build/linux/nfpm/nfpm.yaml
@@ -0,0 +1,73 @@
+# Feel free to remove those if you don't want/need to use them.
+# Make sure to check the documentation at https://nfpm.goreleaser.com
+#
+# The lines below are called `modelines`. See `:help modeline`
+
+name: "dock"
+arch: ${GOARCH}
+platform: "linux"
+version: "0.1.0"
+section: "default"
+priority: "extra"
+maintainer: ${GIT_COMMITTER_NAME} <${GIT_COMMITTER_EMAIL}>
+description: "My Product Description"
+vendor: "My Company"
+homepage: "https://wails.io"
+license: "MIT"
+release: "1"
+
+contents:
+ - src: "./bin/dock"
+ dst: "/usr/local/bin/dock"
+ - src: "./build/appicon.png"
+ dst: "/usr/share/icons/hicolor/128x128/apps/dock.png"
+ - src: "./build/linux/dock.desktop"
+ dst: "/usr/share/applications/dock.desktop"
+
+# Default dependencies for Debian 12/Ubuntu 22.04+ with WebKit 4.1
+depends:
+ - libgtk-3-dev
+ - libwebkit2gtk-4.1-dev
+ - build-essential
+ - pkg-config
+
+# Distribution-specific overrides for different package formats and WebKit versions
+overrides:
+ # RPM packages for RHEL/CentOS/AlmaLinux/Rocky Linux (WebKit 4.0)
+ rpm:
+ depends:
+ - gtk3-devel
+ - webkit2gtk3-devel
+ - gcc-c++
+ - pkg-config
+
+ # Arch Linux packages (WebKit 4.1)
+ archlinux:
+ depends:
+ - gtk3
+ - webkit2gtk-4.1
+ - base-devel
+ - pkgconf
+
+# scripts section to ensure desktop database is updated after install
+scripts:
+ postinstall: "./build/linux/nfpm/scripts/postinstall.sh"
+ # You can also add preremove, postremove if needed
+ # preremove: "./build/linux/nfpm/scripts/preremove.sh"
+ # postremove: "./build/linux/nfpm/scripts/postremove.sh"
+
+# replaces:
+# - foobar
+# provides:
+# - bar
+# depends:
+# - gtk3
+# - libwebkit2gtk
+# recommends:
+# - whatever
+# suggests:
+# - something-else
+# conflicts:
+# - not-foo
+# - not-bar
+# changelog: "changelog.yaml"
\ No newline at end of file
diff --git a/v3/examples/dock/build/linux/nfpm/scripts/postinstall.sh b/v3/examples/dock/build/linux/nfpm/scripts/postinstall.sh
new file mode 100644
index 000000000..4bbb815a3
--- /dev/null
+++ b/v3/examples/dock/build/linux/nfpm/scripts/postinstall.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+# Update desktop database for .desktop file changes
+# This makes the application appear in application menus and registers its capabilities.
+if command -v update-desktop-database >/dev/null 2>&1; then
+ echo "Updating desktop database..."
+ update-desktop-database -q /usr/share/applications
+else
+ echo "Warning: update-desktop-database command not found. Desktop file may not be immediately recognized." >&2
+fi
+
+# Update MIME database for custom URL schemes (x-scheme-handler)
+# This ensures the system knows how to handle your custom protocols.
+if command -v update-mime-database >/dev/null 2>&1; then
+ echo "Updating MIME database..."
+ update-mime-database -n /usr/share/mime
+else
+ echo "Warning: update-mime-database command not found. Custom URL schemes may not be immediately recognized." >&2
+fi
+
+exit 0
diff --git a/v3/examples/dock/build/linux/nfpm/scripts/postremove.sh b/v3/examples/dock/build/linux/nfpm/scripts/postremove.sh
new file mode 100644
index 000000000..a9bf588e2
--- /dev/null
+++ b/v3/examples/dock/build/linux/nfpm/scripts/postremove.sh
@@ -0,0 +1 @@
+#!/bin/bash
diff --git a/v3/examples/dock/build/linux/nfpm/scripts/preinstall.sh b/v3/examples/dock/build/linux/nfpm/scripts/preinstall.sh
new file mode 100644
index 000000000..a9bf588e2
--- /dev/null
+++ b/v3/examples/dock/build/linux/nfpm/scripts/preinstall.sh
@@ -0,0 +1 @@
+#!/bin/bash
diff --git a/v3/examples/dock/build/linux/nfpm/scripts/preremove.sh b/v3/examples/dock/build/linux/nfpm/scripts/preremove.sh
new file mode 100644
index 000000000..a9bf588e2
--- /dev/null
+++ b/v3/examples/dock/build/linux/nfpm/scripts/preremove.sh
@@ -0,0 +1 @@
+#!/bin/bash
diff --git a/v3/examples/dock/build/windows/Taskfile.yml b/v3/examples/dock/build/windows/Taskfile.yml
new file mode 100644
index 000000000..19f137616
--- /dev/null
+++ b/v3/examples/dock/build/windows/Taskfile.yml
@@ -0,0 +1,98 @@
+version: '3'
+
+includes:
+ common: ../Taskfile.yml
+
+tasks:
+ build:
+ summary: Builds the application for Windows
+ deps:
+ - task: common:go:mod:tidy
+ - task: common:build:frontend
+ vars:
+ BUILD_FLAGS:
+ ref: .BUILD_FLAGS
+ PRODUCTION:
+ ref: .PRODUCTION
+ - task: common:generate:icons
+ cmds:
+ - task: generate:syso
+ - go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}}.exe
+ - cmd: powershell Remove-item *.syso
+ platforms: [windows]
+ - cmd: rm -f *.syso
+ platforms: [linux, darwin]
+ vars:
+ BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s -H windowsgui"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}'
+ env:
+ GOOS: windows
+ CGO_ENABLED: 0
+ GOARCH: '{{.ARCH | default ARCH}}'
+ PRODUCTION: '{{.PRODUCTION | default "false"}}'
+
+ package:
+ summary: Packages a production build of the application
+ cmds:
+ - |-
+ if [ "{{.FORMAT | default "nsis"}}" = "msix" ]; then
+ task: create:msix:package
+ else
+ task: create:nsis:installer
+ fi
+ vars:
+ FORMAT: '{{.FORMAT | default "nsis"}}'
+
+ generate:syso:
+ summary: Generates Windows `.syso` file
+ dir: build
+ cmds:
+ - wails3 generate syso -arch {{.ARCH}} -icon windows/icon.ico -manifest windows/wails.exe.manifest -info windows/info.json -out ../wails_windows_{{.ARCH}}.syso
+ vars:
+ ARCH: '{{.ARCH | default ARCH}}'
+
+ create:nsis:installer:
+ summary: Creates an NSIS installer
+ dir: build/windows/nsis
+ deps:
+ - task: build
+ vars:
+ PRODUCTION: "true"
+ cmds:
+ # Create the Microsoft WebView2 bootstrapper if it doesn't exist
+ - wails3 generate webview2bootstrapper -dir "{{.ROOT_DIR}}/build/windows/nsis"
+ - makensis -DARG_WAILS_{{.ARG_FLAG}}_BINARY="{{.ROOT_DIR}}/{{.BIN_DIR}}/{{.APP_NAME}}.exe" project.nsi
+ vars:
+ ARCH: '{{.ARCH | default ARCH}}'
+ ARG_FLAG: '{{if eq .ARCH "amd64"}}AMD64{{else}}ARM64{{end}}'
+
+ create:msix:package:
+ summary: Creates an MSIX package
+ deps:
+ - task: build
+ vars:
+ PRODUCTION: "true"
+ cmds:
+ - |-
+ wails3 tool msix \
+ --config "{{.ROOT_DIR}}/wails.json" \
+ --name "{{.APP_NAME}}" \
+ --executable "{{.ROOT_DIR}}/{{.BIN_DIR}}/{{.APP_NAME}}.exe" \
+ --arch "{{.ARCH}}" \
+ --out "{{.ROOT_DIR}}/{{.BIN_DIR}}/{{.APP_NAME}}-{{.ARCH}}.msix" \
+ {{if .CERT_PATH}}--cert "{{.CERT_PATH}}"{{end}} \
+ {{if .PUBLISHER}}--publisher "{{.PUBLISHER}}"{{end}} \
+ {{if .USE_MSIX_TOOL}}--use-msix-tool{{else}}--use-makeappx{{end}}
+ vars:
+ ARCH: '{{.ARCH | default ARCH}}'
+ CERT_PATH: '{{.CERT_PATH | default ""}}'
+ PUBLISHER: '{{.PUBLISHER | default ""}}'
+ USE_MSIX_TOOL: '{{.USE_MSIX_TOOL | default "false"}}'
+
+ install:msix:tools:
+ summary: Installs tools required for MSIX packaging
+ cmds:
+ - wails3 tool msix-install-tools
+
+ run:
+ cmds:
+ - '{{.BIN_DIR}}/{{.APP_NAME}}.exe'
diff --git a/v3/examples/dock/build/windows/icon.ico b/v3/examples/dock/build/windows/icon.ico
new file mode 100644
index 000000000..bfa0690b7
Binary files /dev/null and b/v3/examples/dock/build/windows/icon.ico differ
diff --git a/v3/examples/dock/build/windows/info.json b/v3/examples/dock/build/windows/info.json
new file mode 100644
index 000000000..850b2b5b0
--- /dev/null
+++ b/v3/examples/dock/build/windows/info.json
@@ -0,0 +1,15 @@
+{
+ "fixed": {
+ "file_version": "0.1.0"
+ },
+ "info": {
+ "0000": {
+ "ProductVersion": "0.1.0",
+ "CompanyName": "My Company",
+ "FileDescription": "My Product Description",
+ "LegalCopyright": "© now, My Company",
+ "ProductName": "My Product",
+ "Comments": "This is a comment"
+ }
+ }
+}
\ No newline at end of file
diff --git a/v3/examples/dock/build/windows/msix/app_manifest.xml b/v3/examples/dock/build/windows/msix/app_manifest.xml
new file mode 100644
index 000000000..8eabe1a03
--- /dev/null
+++ b/v3/examples/dock/build/windows/msix/app_manifest.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+ My Product
+ My Company
+ My Product Description
+ Assets\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/v3/examples/dock/build/windows/msix/template.xml b/v3/examples/dock/build/windows/msix/template.xml
new file mode 100644
index 000000000..2824a2932
--- /dev/null
+++ b/v3/examples/dock/build/windows/msix/template.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ My Product
+ My Company
+ My Product Description
+ Assets\AppIcon.png
+
+
+
+
+
+
+
diff --git a/v3/examples/dock/build/windows/nsis/project.nsi b/v3/examples/dock/build/windows/nsis/project.nsi
new file mode 100644
index 000000000..b992809bc
--- /dev/null
+++ b/v3/examples/dock/build/windows/nsis/project.nsi
@@ -0,0 +1,112 @@
+Unicode true
+
+####
+## Please note: Template replacements don't work in this file. They are provided with default defines like
+## mentioned underneath.
+## If the keyword is not defined, "wails_tools.nsh" will populate them.
+## If they are defined here, "wails_tools.nsh" will not touch them. This allows you to use this project.nsi manually
+## from outside of Wails for debugging and development of the installer.
+##
+## For development first make a wails nsis build to populate the "wails_tools.nsh":
+## > wails build --target windows/amd64 --nsis
+## Then you can call makensis on this file with specifying the path to your binary:
+## For a AMD64 only installer:
+## > makensis -DARG_WAILS_AMD64_BINARY=..\..\bin\app.exe
+## For a ARM64 only installer:
+## > makensis -DARG_WAILS_ARM64_BINARY=..\..\bin\app.exe
+## For a installer with both architectures:
+## > makensis -DARG_WAILS_AMD64_BINARY=..\..\bin\app-amd64.exe -DARG_WAILS_ARM64_BINARY=..\..\bin\app-arm64.exe
+####
+## The following information is taken from the wails_tools.nsh file, but they can be overwritten here.
+####
+## !define INFO_PROJECTNAME "my-project" # Default "dock"
+## !define INFO_COMPANYNAME "My Company" # Default "My Company"
+## !define INFO_PRODUCTNAME "My Product Name" # Default "My Product"
+## !define INFO_PRODUCTVERSION "1.0.0" # Default "0.1.0"
+## !define INFO_COPYRIGHT "(c) Now, My Company" # Default "© now, My Company"
+###
+## !define PRODUCT_EXECUTABLE "Application.exe" # Default "${INFO_PROJECTNAME}.exe"
+## !define UNINST_KEY_NAME "UninstKeyInRegistry" # Default "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}"
+####
+## !define REQUEST_EXECUTION_LEVEL "admin" # Default "admin" see also https://nsis.sourceforge.io/Docs/Chapter4.html
+####
+## Include the wails tools
+####
+!include "wails_tools.nsh"
+
+# The version information for this two must consist of 4 parts
+VIProductVersion "${INFO_PRODUCTVERSION}.0"
+VIFileVersion "${INFO_PRODUCTVERSION}.0"
+
+VIAddVersionKey "CompanyName" "${INFO_COMPANYNAME}"
+VIAddVersionKey "FileDescription" "${INFO_PRODUCTNAME} Installer"
+VIAddVersionKey "ProductVersion" "${INFO_PRODUCTVERSION}"
+VIAddVersionKey "FileVersion" "${INFO_PRODUCTVERSION}"
+VIAddVersionKey "LegalCopyright" "${INFO_COPYRIGHT}"
+VIAddVersionKey "ProductName" "${INFO_PRODUCTNAME}"
+
+# Enable HiDPI support. https://nsis.sourceforge.io/Reference/ManifestDPIAware
+ManifestDPIAware true
+
+!include "MUI.nsh"
+
+!define MUI_ICON "..\icon.ico"
+!define MUI_UNICON "..\icon.ico"
+# !define MUI_WELCOMEFINISHPAGE_BITMAP "resources\leftimage.bmp" #Include this to add a bitmap on the left side of the Welcome Page. Must be a size of 164x314
+!define MUI_FINISHPAGE_NOAUTOCLOSE # Wait on the INSTFILES page so the user can take a look into the details of the installation steps
+!define MUI_ABORTWARNING # This will warn the user if they exit from the installer.
+
+!insertmacro MUI_PAGE_WELCOME # Welcome to the installer page.
+# !insertmacro MUI_PAGE_LICENSE "resources\eula.txt" # Adds a EULA page to the installer
+!insertmacro MUI_PAGE_DIRECTORY # In which folder install page.
+!insertmacro MUI_PAGE_INSTFILES # Installing page.
+!insertmacro MUI_PAGE_FINISH # Finished installation page.
+
+!insertmacro MUI_UNPAGE_INSTFILES # Uninstalling page
+
+!insertmacro MUI_LANGUAGE "English" # Set the Language of the installer
+
+## The following two statements can be used to sign the installer and the uninstaller. The path to the binaries are provided in %1
+#!uninstfinalize 'signtool --file "%1"'
+#!finalize 'signtool --file "%1"'
+
+Name "${INFO_PRODUCTNAME}"
+OutFile "..\..\..\bin\${INFO_PROJECTNAME}-${ARCH}-installer.exe" # Name of the installer's file.
+InstallDir "$PROGRAMFILES64\${INFO_COMPANYNAME}\${INFO_PRODUCTNAME}" # Default installing folder ($PROGRAMFILES is Program Files folder).
+ShowInstDetails show # This will always show the installation details.
+
+Function .onInit
+ !insertmacro wails.checkArchitecture
+FunctionEnd
+
+Section
+ !insertmacro wails.setShellContext
+
+ !insertmacro wails.webview2runtime
+
+ SetOutPath $INSTDIR
+
+ !insertmacro wails.files
+
+ CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
+ CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
+
+ !insertmacro wails.associateFiles
+
+ !insertmacro wails.writeUninstaller
+SectionEnd
+
+Section "uninstall"
+ !insertmacro wails.setShellContext
+
+ RMDir /r "$AppData\${PRODUCT_EXECUTABLE}" # Remove the WebView2 DataPath
+
+ RMDir /r $INSTDIR
+
+ Delete "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk"
+ Delete "$DESKTOP\${INFO_PRODUCTNAME}.lnk"
+
+ !insertmacro wails.unassociateFiles
+
+ !insertmacro wails.deleteUninstaller
+SectionEnd
diff --git a/v3/examples/dock/build/windows/nsis/wails_tools.nsh b/v3/examples/dock/build/windows/nsis/wails_tools.nsh
new file mode 100644
index 000000000..b5ff5b23c
--- /dev/null
+++ b/v3/examples/dock/build/windows/nsis/wails_tools.nsh
@@ -0,0 +1,212 @@
+# DO NOT EDIT - Generated automatically by `wails build`
+
+!include "x64.nsh"
+!include "WinVer.nsh"
+!include "FileFunc.nsh"
+
+!ifndef INFO_PROJECTNAME
+ !define INFO_PROJECTNAME "dock"
+!endif
+!ifndef INFO_COMPANYNAME
+ !define INFO_COMPANYNAME "My Company"
+!endif
+!ifndef INFO_PRODUCTNAME
+ !define INFO_PRODUCTNAME "My Product"
+!endif
+!ifndef INFO_PRODUCTVERSION
+ !define INFO_PRODUCTVERSION "0.1.0"
+!endif
+!ifndef INFO_COPYRIGHT
+ !define INFO_COPYRIGHT "© now, My Company"
+!endif
+!ifndef PRODUCT_EXECUTABLE
+ !define PRODUCT_EXECUTABLE "${INFO_PROJECTNAME}.exe"
+!endif
+!ifndef UNINST_KEY_NAME
+ !define UNINST_KEY_NAME "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}"
+!endif
+!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINST_KEY_NAME}"
+
+!ifndef REQUEST_EXECUTION_LEVEL
+ !define REQUEST_EXECUTION_LEVEL "admin"
+!endif
+
+RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}"
+
+!ifdef ARG_WAILS_AMD64_BINARY
+ !define SUPPORTS_AMD64
+!endif
+
+!ifdef ARG_WAILS_ARM64_BINARY
+ !define SUPPORTS_ARM64
+!endif
+
+!ifdef SUPPORTS_AMD64
+ !ifdef SUPPORTS_ARM64
+ !define ARCH "amd64_arm64"
+ !else
+ !define ARCH "amd64"
+ !endif
+!else
+ !ifdef SUPPORTS_ARM64
+ !define ARCH "arm64"
+ !else
+ !error "Wails: Undefined ARCH, please provide at least one of ARG_WAILS_AMD64_BINARY or ARG_WAILS_ARM64_BINARY"
+ !endif
+!endif
+
+!macro wails.checkArchitecture
+ !ifndef WAILS_WIN10_REQUIRED
+ !define WAILS_WIN10_REQUIRED "This product is only supported on Windows 10 (Server 2016) and later."
+ !endif
+
+ !ifndef WAILS_ARCHITECTURE_NOT_SUPPORTED
+ !define WAILS_ARCHITECTURE_NOT_SUPPORTED "This product can't be installed on the current Windows architecture. Supports: ${ARCH}"
+ !endif
+
+ ${If} ${AtLeastWin10}
+ !ifdef SUPPORTS_AMD64
+ ${if} ${IsNativeAMD64}
+ Goto ok
+ ${EndIf}
+ !endif
+
+ !ifdef SUPPORTS_ARM64
+ ${if} ${IsNativeARM64}
+ Goto ok
+ ${EndIf}
+ !endif
+
+ IfSilent silentArch notSilentArch
+ silentArch:
+ SetErrorLevel 65
+ Abort
+ notSilentArch:
+ MessageBox MB_OK "${WAILS_ARCHITECTURE_NOT_SUPPORTED}"
+ Quit
+ ${else}
+ IfSilent silentWin notSilentWin
+ silentWin:
+ SetErrorLevel 64
+ Abort
+ notSilentWin:
+ MessageBox MB_OK "${WAILS_WIN10_REQUIRED}"
+ Quit
+ ${EndIf}
+
+ ok:
+!macroend
+
+!macro wails.files
+ !ifdef SUPPORTS_AMD64
+ ${if} ${IsNativeAMD64}
+ File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_AMD64_BINARY}"
+ ${EndIf}
+ !endif
+
+ !ifdef SUPPORTS_ARM64
+ ${if} ${IsNativeARM64}
+ File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_ARM64_BINARY}"
+ ${EndIf}
+ !endif
+!macroend
+
+!macro wails.writeUninstaller
+ WriteUninstaller "$INSTDIR\uninstall.exe"
+
+ SetRegView 64
+ WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}"
+ WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}"
+ WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${INFO_PRODUCTVERSION}"
+ WriteRegStr HKLM "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_EXECUTABLE}"
+ WriteRegStr HKLM "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
+ WriteRegStr HKLM "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
+
+ ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
+ IntFmt $0 "0x%08X" $0
+ WriteRegDWORD HKLM "${UNINST_KEY}" "EstimatedSize" "$0"
+!macroend
+
+!macro wails.deleteUninstaller
+ Delete "$INSTDIR\uninstall.exe"
+
+ SetRegView 64
+ DeleteRegKey HKLM "${UNINST_KEY}"
+!macroend
+
+!macro wails.setShellContext
+ ${If} ${REQUEST_EXECUTION_LEVEL} == "admin"
+ SetShellVarContext all
+ ${else}
+ SetShellVarContext current
+ ${EndIf}
+!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'
+
+ SetDetailsPrint both
+ ok:
+!macroend
+
+# 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
+
+!macro wails.associateFiles
+ ; Create file associations
+
+!macroend
+
+!macro wails.unassociateFiles
+ ; Delete app associations
+
+!macroend
\ No newline at end of file
diff --git a/v3/examples/dock/build/windows/wails.exe.manifest b/v3/examples/dock/build/windows/wails.exe.manifest
new file mode 100644
index 000000000..48052e7e6
--- /dev/null
+++ b/v3/examples/dock/build/windows/wails.exe.manifest
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+ true/pm
+ permonitorv2,permonitor
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/v3/examples/dock/frontend/Inter Font License.txt b/v3/examples/dock/frontend/Inter Font License.txt
new file mode 100644
index 000000000..b525cbf3a
--- /dev/null
+++ b/v3/examples/dock/frontend/Inter Font License.txt
@@ -0,0 +1,93 @@
+Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice.ts b/v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice.ts
new file mode 100644
index 000000000..abf1f22e4
--- /dev/null
+++ b/v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice.ts
@@ -0,0 +1,53 @@
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+
+/**
+ * Service represents the dock service
+ * @module
+ */
+
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-ignore: Unused imports
+import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime";
+
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-ignore: Unused imports
+import * as $models from "./models.js";
+
+/**
+ * HideAppIcon hides the app icon in the dock/taskbar.
+ */
+export function HideAppIcon(): $CancellablePromise
{
+ return $Call.ByID(3413658144);
+}
+
+/**
+ * RemoveBadge removes the badge label from the application icon.
+ * This method ensures the badge call is made on the main thread to avoid crashes.
+ */
+export function RemoveBadge(): $CancellablePromise {
+ return $Call.ByID(2752757297);
+}
+
+/**
+ * SetBadge sets the badge label on the application icon.
+ * This method ensures the badge call is made on the main thread to avoid crashes.
+ */
+export function SetBadge(label: string): $CancellablePromise {
+ return $Call.ByID(1717705661, label);
+}
+
+/**
+ * SetCustomBadge sets the badge label on the application icon with custom options.
+ * This method ensures the badge call is made on the main thread to avoid crashes.
+ */
+export function SetCustomBadge(label: string, options: $models.BadgeOptions): $CancellablePromise {
+ return $Call.ByID(2730169760, label, options);
+}
+
+/**
+ * ShowAppIcon shows the app icon in the dock/taskbar.
+ */
+export function ShowAppIcon(): $CancellablePromise {
+ return $Call.ByID(3409697379);
+}
diff --git a/v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/index.ts b/v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/index.ts
new file mode 100644
index 000000000..fbdaf19f3
--- /dev/null
+++ b/v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/index.ts
@@ -0,0 +1,11 @@
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+
+import * as DockService from "./dockservice.js";
+export {
+ DockService
+};
+
+export {
+ BadgeOptions
+} from "./models.js";
diff --git a/v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/models.ts b/v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/models.ts
new file mode 100644
index 000000000..f97c8a4c6
--- /dev/null
+++ b/v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/models.ts
@@ -0,0 +1,61 @@
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-ignore: Unused imports
+import { Create as $Create } from "@wailsio/runtime";
+
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-ignore: Unused imports
+import * as color$0 from "../../../../../../../image/color/models.js";
+
+/**
+ * BadgeOptions represents options for customizing badge appearance
+ */
+export class BadgeOptions {
+ "TextColour": color$0.RGBA;
+ "BackgroundColour": color$0.RGBA;
+ "FontName": string;
+ "FontSize": number;
+ "SmallFontSize": number;
+
+ /** Creates a new BadgeOptions instance. */
+ constructor($$source: Partial = {}) {
+ if (!("TextColour" in $$source)) {
+ this["TextColour"] = (new color$0.RGBA());
+ }
+ if (!("BackgroundColour" in $$source)) {
+ this["BackgroundColour"] = (new color$0.RGBA());
+ }
+ if (!("FontName" in $$source)) {
+ this["FontName"] = "";
+ }
+ if (!("FontSize" in $$source)) {
+ this["FontSize"] = 0;
+ }
+ if (!("SmallFontSize" in $$source)) {
+ this["SmallFontSize"] = 0;
+ }
+
+ Object.assign(this, $$source);
+ }
+
+ /**
+ * Creates a new BadgeOptions instance from a string or object.
+ */
+ static createFrom($$source: any = {}): BadgeOptions {
+ const $$createField0_0 = $$createType0;
+ const $$createField1_0 = $$createType0;
+ let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
+ if ("TextColour" in $$parsedSource) {
+ $$parsedSource["TextColour"] = $$createField0_0($$parsedSource["TextColour"]);
+ }
+ if ("BackgroundColour" in $$parsedSource) {
+ $$parsedSource["BackgroundColour"] = $$createField1_0($$parsedSource["BackgroundColour"]);
+ }
+ return new BadgeOptions($$parsedSource as Partial);
+ }
+}
+
+// Private type creation functions
+const $$createType0 = color$0.RGBA.createFrom;
diff --git a/v3/examples/dock/frontend/bindings/image/color/index.ts b/v3/examples/dock/frontend/bindings/image/color/index.ts
new file mode 100644
index 000000000..97b507b08
--- /dev/null
+++ b/v3/examples/dock/frontend/bindings/image/color/index.ts
@@ -0,0 +1,6 @@
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+
+export {
+ RGBA
+} from "./models.js";
diff --git a/v3/examples/dock/frontend/bindings/image/color/models.ts b/v3/examples/dock/frontend/bindings/image/color/models.ts
new file mode 100644
index 000000000..0d4eab56d
--- /dev/null
+++ b/v3/examples/dock/frontend/bindings/image/color/models.ts
@@ -0,0 +1,46 @@
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-ignore: Unused imports
+import { Create as $Create } from "@wailsio/runtime";
+
+/**
+ * RGBA represents a traditional 32-bit alpha-premultiplied color, having 8
+ * bits for each of red, green, blue and alpha.
+ *
+ * An alpha-premultiplied color component C has been scaled by alpha (A), so
+ * has valid values 0 <= C <= A.
+ */
+export class RGBA {
+ "R": number;
+ "G": number;
+ "B": number;
+ "A": number;
+
+ /** Creates a new RGBA instance. */
+ constructor($$source: Partial = {}) {
+ if (!("R" in $$source)) {
+ this["R"] = 0;
+ }
+ if (!("G" in $$source)) {
+ this["G"] = 0;
+ }
+ if (!("B" in $$source)) {
+ this["B"] = 0;
+ }
+ if (!("A" in $$source)) {
+ this["A"] = 0;
+ }
+
+ Object.assign(this, $$source);
+ }
+
+ /**
+ * Creates a new RGBA instance from a string or object.
+ */
+ static createFrom($$source: any = {}): RGBA {
+ let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
+ return new RGBA($$parsedSource as Partial);
+ }
+}
diff --git a/v3/examples/dock/frontend/dist/Inter-Medium.ttf b/v3/examples/dock/frontend/dist/Inter-Medium.ttf
new file mode 100644
index 000000000..a01f3777a
Binary files /dev/null and b/v3/examples/dock/frontend/dist/Inter-Medium.ttf differ
diff --git a/v3/examples/dock/frontend/dist/assets/index-BJIh-HIc.js b/v3/examples/dock/frontend/dist/assets/index-BJIh-HIc.js
new file mode 100644
index 000000000..5217ab9a7
--- /dev/null
+++ b/v3/examples/dock/frontend/dist/assets/index-BJIh-HIc.js
@@ -0,0 +1,6 @@
+(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))n(o);new MutationObserver(o=>{for(const i of o)if(i.type==="childList")for(const s of i.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&n(s)}).observe(document,{childList:!0,subtree:!0});function r(o){const i={};return o.integrity&&(i.integrity=o.integrity),o.referrerPolicy&&(i.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?i.credentials="include":o.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function n(o){if(o.ep)return;o.ep=!0;const i=r(o);fetch(o.href,i)}})();const ie="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";function K(t=21){let e="",r=t|0;for(;r--;)e+=ie[Math.random()*64|0];return e}const se=window.location.origin+"/wails/runtime",O=Object.freeze({Call:0,Clipboard:1,Application:2,Events:3,ContextMenu:4,Dialog:5,Window:6,Screens:7,System:8,Browser:9,CancelCall:10});let ce=K();function R(t,e=""){return function(r,n=null){return le(t,r,e,n)}}async function le(t,e,r,n){var o,i;let s=new URL(se);s.searchParams.append("object",t.toString()),s.searchParams.append("method",e.toString()),n&&s.searchParams.append("args",JSON.stringify(n));let l={"x-wails-client-id":ce};r&&(l["x-wails-window-name"]=r);let c=await fetch(s,{headers:l});if(!c.ok)throw new Error(await c.text());return((i=(o=c.headers.get("Content-Type"))===null||o===void 0?void 0:o.indexOf("application/json"))!==null&&i!==void 0?i:-1)!==-1?c.json():c.text()}R(O.System);const P=function(){var t,e,r,n,o;try{if(!((e=(t=window.chrome)===null||t===void 0?void 0:t.webview)===null||e===void 0)&&e.postMessage)return window.chrome.webview.postMessage.bind(window.chrome.webview);if(!((o=(n=(r=window.webkit)===null||r===void 0?void 0:r.messageHandlers)===null||n===void 0?void 0:n.external)===null||o===void 0)&&o.postMessage)return window.webkit.messageHandlers.external.postMessage.bind(window.webkit.messageHandlers.external)}catch{}return console.warn(`
+%c⚠️ Browser Environment Detected %c
+
+%cOnly UI previews are available in the browser. For full functionality, please run the application in desktop mode.
+More information at: https://v3.wails.io/learn/build/#using-a-browser-for-development
+`,"background: #ffffff; color: #000000; font-weight: bold; padding: 4px 8px; border-radius: 4px; border: 2px solid #000000;","background: transparent;","color: #ffffff; font-style: italic; font-weight: bold;"),null}();function M(t){P==null||P(t)}function Q(){return window._wails.environment.OS==="windows"}function ae(){return!!window._wails.environment.Debug}function ue(){return new MouseEvent("mousedown").buttons===0}function Z(t){var e;return t.target instanceof HTMLElement?t.target:!(t.target instanceof HTMLElement)&&t.target instanceof Node&&(e=t.target.parentElement)!==null&&e!==void 0?e:document.body}document.addEventListener("DOMContentLoaded",()=>{});window.addEventListener("contextmenu",pe);const de=R(O.ContextMenu),fe=0;function we(t,e,r,n){de(fe,{id:t,x:e,y:r,data:n})}function pe(t){const e=Z(t),r=window.getComputedStyle(e).getPropertyValue("--custom-contextmenu").trim();if(r){t.preventDefault();const n=window.getComputedStyle(e).getPropertyValue("--custom-contextmenu-data");we(r,t.clientX,t.clientY,n)}else he(t,e)}function he(t,e){if(ae())return;switch(window.getComputedStyle(e).getPropertyValue("--default-contextmenu").trim()){case"show":return;case"hide":t.preventDefault();return}if(e.isContentEditable)return;const r=window.getSelection(),n=r&&r.toString().length>0;if(n)for(let o=0;o{F=t,F||(b=v=!1,u())};window.addEventListener("mousedown",N,{capture:!0});window.addEventListener("mousemove",N,{capture:!0});window.addEventListener("mouseup",N,{capture:!0});for(const t of["click","contextmenu","dblclick"])window.addEventListener(t,me,{capture:!0});function me(t){(E||v)&&(t.stopImmediatePropagation(),t.stopPropagation(),t.preventDefault())}const D=0,ye=1,k=2;function N(t){let e,r=t.buttons;switch(t.type){case"mousedown":e=D,H||(r=h|1<"u"||typeof e=="object"))try{var r=C.call(e);return(r===Ce||r===Me||r===Oe||r===Se)&&e("")==null}catch{}return!1})}function Pe(t){if(X(t))return!0;if(!t||typeof t!="function"&&typeof t!="object")return!1;try{y(t,null,B)}catch(e){if(e!==x)return!1}return!W(t)&&_(t)}function Ae(t){if(X(t))return!0;if(!t||typeof t!="function"&&typeof t!="object")return!1;if(Re)return _(t);if(W(t))return!1;var e=C.call(t);return e!==ze&&e!==xe&&!/^\[object HTML/.test(e)?!1:_(t)}const p=y?Pe:Ae;var I;class U extends Error{constructor(e,r){super(e,r),this.name="CancelError"}}class S extends Error{constructor(e,r,n){super((n??"Unhandled rejection in cancelled promise.")+" Reason: "+He(r),{cause:r}),this.promise=e,this.name="CancelledRejectionError"}}const f=Symbol("barrier"),J=Symbol("cancelImpl"),V=(I=Symbol.species)!==null&&I!==void 0?I:Symbol("speciesPolyfill");class a extends Promise{constructor(e,r){let n,o;if(super((c,d)=>{n=c,o=d}),this.constructor[V]!==Promise)throw new TypeError("CancellablePromise does not support transparent subclassing. Please refrain from overriding the [Symbol.species] static property.");let i={promise:this,resolve:n,reject:o,get oncancelled(){return r??null},set oncancelled(c){r=c??void 0}};const s={get root(){return s},resolving:!1,settled:!1};Object.defineProperties(this,{[f]:{configurable:!1,enumerable:!1,writable:!0,value:null},[J]:{configurable:!1,enumerable:!1,writable:!1,value:ee(i,s)}});const l=re(i,s);try{e(te(i,s),l)}catch(c){s.resolving?console.log("Unhandled exception in CancellablePromise executor.",c):l(c)}}cancel(e){return new a(r=>{Promise.all([this[J](new U("Promise cancelled.",{cause:e})),De(this)]).then(()=>r(),()=>r())})}cancelOn(e){return e.aborted?this.cancel(e.reason):e.addEventListener("abort",()=>void this.cancel(e.reason),{capture:!0}),this}then(e,r,n){if(!(this instanceof a))throw new TypeError("CancellablePromise.prototype.then called on an invalid object.");if(p(e)||(e=q),p(r)||(r=G),e===q&&r==G)return new a(i=>i(this));const o={};return this[f]=o,new a((i,s)=>{super.then(l=>{var c;this[f]===o&&(this[f]=null),(c=o.resolve)===null||c===void 0||c.call(o);try{i(e(l))}catch(d){s(d)}},l=>{var c;this[f]===o&&(this[f]=null),(c=o.resolve)===null||c===void 0||c.call(o);try{i(r(l))}catch(d){s(d)}})},async i=>{try{return n==null?void 0:n(i)}finally{await this.cancel(i)}})}catch(e,r){return this.then(void 0,e,r)}finally(e,r){if(!(this instanceof a))throw new TypeError("CancellablePromise.prototype.finally called on an invalid object.");return p(e)?this.then(n=>a.resolve(e()).then(()=>n),n=>a.resolve(e()).then(()=>{throw n}),r):this.then(e,e,r)}static get[V](){return Promise}static all(e){let r=Array.from(e);const n=r.length===0?a.resolve(r):new a((o,i)=>{Promise.all(r).then(o,i)},o=>z(n,r,o));return n}static allSettled(e){let r=Array.from(e);const n=r.length===0?a.resolve(r):new a((o,i)=>{Promise.allSettled(r).then(o,i)},o=>z(n,r,o));return n}static any(e){let r=Array.from(e);const n=r.length===0?a.resolve(r):new a((o,i)=>{Promise.any(r).then(o,i)},o=>z(n,r,o));return n}static race(e){let r=Array.from(e);const n=new a((o,i)=>{Promise.race(r).then(o,i)},o=>z(n,r,o));return n}static cancel(e){const r=new a(()=>{});return r.cancel(e),r}static timeout(e,r){const n=new a(()=>{});return AbortSignal&&typeof AbortSignal=="function"&&AbortSignal.timeout&&typeof AbortSignal.timeout=="function"?AbortSignal.timeout(e).addEventListener("abort",()=>void n.cancel(r)):setTimeout(()=>void n.cancel(r),e),n}static sleep(e,r){return new a(n=>{setTimeout(()=>n(r),e)})}static reject(e){return new a((r,n)=>n(e))}static resolve(e){return e instanceof a?e:new a(r=>r(e))}static withResolvers(){let e={oncancelled:null};return e.promise=new a((r,n)=>{e.resolve=r,e.reject=n},r=>{var n;(n=e.oncancelled)===null||n===void 0||n.call(e,r)}),e}}function ee(t,e){let r;return n=>{if(e.settled||(e.settled=!0,e.reason=n,t.reject(n),Promise.prototype.then.call(t.promise,void 0,o=>{if(o!==n)throw o})),!(!e.reason||!t.oncancelled))return r=new Promise(o=>{try{o(t.oncancelled(e.reason.cause))}catch(i){Promise.reject(new S(t.promise,i,"Unhandled exception in oncancelled callback."))}}).catch(o=>{Promise.reject(new S(t.promise,o,"Unhandled rejection in oncancelled callback."))}),t.oncancelled=null,r}}function te(t,e){return r=>{if(!e.resolving){if(e.resolving=!0,r===t.promise){if(e.settled)return;e.settled=!0,t.reject(new TypeError("A promise cannot be resolved with itself."));return}if(r!=null&&(typeof r=="object"||typeof r=="function")){let n;try{n=r.then}catch(o){e.settled=!0,t.reject(o);return}if(p(n)){try{let s=r.cancel;if(p(s)){const l=c=>{Reflect.apply(s,r,[c])};e.reason?ee(Object.assign(Object.assign({},t),{oncancelled:l}),e)(e.reason):t.oncancelled=l}}catch{}const o={root:e.root,resolving:!1,get settled(){return this.root.settled},set settled(s){this.root.settled=s},get reason(){return this.root.reason}},i=re(t,o);try{Reflect.apply(n,r,[te(t,o),i])}catch(s){i(s)}return}}e.settled||(e.settled=!0,t.resolve(r))}}}function re(t,e){return r=>{if(!e.resolving)if(e.resolving=!0,e.settled){try{if(r instanceof U&&e.reason instanceof U&&Object.is(r.cause,e.reason.cause))return}catch{}Promise.reject(new S(t.promise,r))}else e.settled=!0,t.reject(r)}}function z(t,e,r){const n=[];for(const o of e){let i;try{if(!p(o.then)||(i=o.cancel,!p(i)))continue}catch{continue}let s;try{s=Reflect.apply(i,o,[r])}catch(l){Promise.reject(new S(t,l,"Unhandled exception in cancel method."));continue}s&&n.push((s instanceof Promise?s:Promise.resolve(s)).catch(l=>{Promise.reject(new S(t,l,"Unhandled rejection in cancel method."))}))}return Promise.all(n)}function q(t){return t}function G(t){throw t}function He(t){try{if(t instanceof Error||typeof t!="object"||t.toString!==Object.prototype.toString)return""+t}catch{}try{return JSON.stringify(t)}catch{}try{return Object.prototype.toString.call(t)}catch{}return""}function De(t){var e;let r=(e=t[f])!==null&&e!==void 0?e:{};return"promise"in r||Object.assign(r,m()),t[f]==null&&(r.resolve(),t[f]=r),r.promise}let m=Promise.withResolvers;m&&typeof m=="function"?m=m.bind(Promise):m=function(){let t,e;return{promise:new Promise((n,o)=>{t=n,e=o}),resolve:t,reject:e}};window._wails=window._wails||{};window._wails.callResultHandler=Ue;window._wails.callErrorHandler=Ne;const ke=R(O.Call),Ie=R(O.CancelCall),g=new Map,Fe=0,Be=0;class _e extends Error{constructor(e,r){super(e,r),this.name="RuntimeError"}}function Ue(t,e,r){const n=ne(t);if(n)if(!e)n.resolve(void 0);else if(!r)n.resolve(e);else try{n.resolve(JSON.parse(e))}catch(o){n.reject(new TypeError("could not parse result: "+o.message,{cause:o}))}}function Ne(t,e,r){const n=ne(t);if(n)if(!r)n.reject(new Error(e));else{let o;try{o=JSON.parse(e)}catch(l){n.reject(new TypeError("could not parse error: "+l.message,{cause:l}));return}let i={};o.cause&&(i.cause=o.cause);let s;switch(o.kind){case"ReferenceError":s=new ReferenceError(o.message,i);break;case"TypeError":s=new TypeError(o.message,i);break;case"RuntimeError":s=new _e(o.message,i);break;default:s=new Error(o.message,i);break}n.reject(s)}}function ne(t){const e=g.get(t);return g.delete(t),e}function We(){let t;do t=K();while(g.has(t));return t}function Xe(t){const e=We(),r=a.withResolvers();g.set(e,{resolve:r.resolve,reject:r.reject});const n=ke(Fe,Object.assign({"call-id":e},t));let o=!1;n.then(()=>{o=!0},s=>{g.delete(e),r.reject(s)});const i=()=>(g.delete(e),Ie(Be,{"call-id":e}).catch(s=>{console.error("Error while requesting binding call cancellation:",s)}));return r.oncancelled=()=>o?i():n.then(i),r.promise}function oe(t,...e){return Xe({methodID:t,args:e})}window._wails=window._wails||{};window._wails.invoke=M;M("wails:runtime:ready");function Ye(){return oe(3413658144)}function Je(){return oe(3409697379)}const Ve=document.getElementById("show"),qe=document.getElementById("hide");Ve.addEventListener("click",()=>{Je()});qe.addEventListener("click",()=>{Ye()});
diff --git a/v3/examples/dock/frontend/dist/index.html b/v3/examples/dock/frontend/dist/index.html
new file mode 100644
index 000000000..1836ad6e7
--- /dev/null
+++ b/v3/examples/dock/frontend/dist/index.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+ Wails App
+
+
+
+
+
+
Wails + Typescript
+
+
+
+
+
diff --git a/v3/examples/dock/frontend/dist/style.css b/v3/examples/dock/frontend/dist/style.css
new file mode 100644
index 000000000..0b9c58279
--- /dev/null
+++ b/v3/examples/dock/frontend/dist/style.css
@@ -0,0 +1,157 @@
+:root {
+ font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
+ "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
+ sans-serif;
+ font-size: 16px;
+ line-height: 24px;
+ font-weight: 400;
+ color-scheme: light dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: rgba(27, 38, 54, 1);
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-text-size-adjust: 100%;
+ user-select: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+}
+
+@font-face {
+ font-family: "Inter";
+ font-style: normal;
+ font-weight: 400;
+ src: local(""),
+ url("./Inter-Medium.ttf") format("truetype");
+}
+
+h3 {
+ font-size: 3em;
+ line-height: 1.1;
+}
+
+a {
+ font-weight: 500;
+ color: #646cff;
+ text-decoration: inherit;
+}
+
+a:hover {
+ color: #535bf2;
+}
+
+button {
+ width: 60px;
+ height: 30px;
+ line-height: 30px;
+ border-radius: 3px;
+ border: none;
+ margin: 0 0 0 20px;
+ padding: 0 8px;
+ cursor: pointer;
+}
+
+.result {
+ height: 20px;
+ line-height: 20px;
+}
+
+body {
+ margin: 0;
+ display: flex;
+ place-items: center;
+ place-content: center;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+.container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+h1 {
+ font-size: 3.2em;
+ line-height: 1.1;
+}
+
+#app {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
+}
+
+.logo {
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+}
+
+.logo:hover {
+ filter: drop-shadow(0 0 2em #e80000aa);
+}
+
+.logo.vanilla:hover {
+ filter: drop-shadow(0 0 2em #f7df1eaa);
+}
+
+.result {
+ height: 20px;
+ line-height: 20px;
+ margin: 1.5rem auto;
+ text-align: center;
+}
+
+.footer {
+ margin-top: 1rem;
+ align-content: center;
+ text-align: center;
+}
+
+@media (prefers-color-scheme: light) {
+ :root {
+ color: #213547;
+ background-color: #ffffff;
+ }
+
+ a:hover {
+ color: #747bff;
+ }
+
+ button {
+ background-color: #f9f9f9;
+ }
+}
+
+
+.input-box .btn:hover {
+ background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%);
+ color: #333333;
+}
+
+.input-box .input {
+ border: none;
+ border-radius: 3px;
+ outline: none;
+ height: 30px;
+ line-height: 30px;
+ padding: 0 10px;
+ color: black;
+ background-color: rgba(240, 240, 240, 1);
+ -webkit-font-smoothing: antialiased;
+}
+
+.input-box .input:hover {
+ border: none;
+ background-color: rgba(255, 255, 255, 1);
+}
+
+.input-box .input:focus {
+ border: none;
+ background-color: rgba(255, 255, 255, 1);
+}
\ No newline at end of file
diff --git a/v3/examples/dock/frontend/dist/typescript.svg b/v3/examples/dock/frontend/dist/typescript.svg
new file mode 100644
index 000000000..d91c910cc
--- /dev/null
+++ b/v3/examples/dock/frontend/dist/typescript.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/v3/examples/dock/frontend/dist/wails.png b/v3/examples/dock/frontend/dist/wails.png
new file mode 100644
index 000000000..8bdf42483
Binary files /dev/null and b/v3/examples/dock/frontend/dist/wails.png differ
diff --git a/v3/examples/dock/frontend/index.html b/v3/examples/dock/frontend/index.html
new file mode 100644
index 000000000..1a310f0e9
--- /dev/null
+++ b/v3/examples/dock/frontend/index.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+ Wails App
+
+
+
+
+
Wails + Typescript
+
+
+
+
+
+
diff --git a/v3/examples/dock/frontend/package-lock.json b/v3/examples/dock/frontend/package-lock.json
new file mode 100644
index 000000000..0c0df727a
--- /dev/null
+++ b/v3/examples/dock/frontend/package-lock.json
@@ -0,0 +1,936 @@
+{
+ "name": "frontend",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "frontend",
+ "version": "0.0.0",
+ "dependencies": {
+ "@wailsio/runtime": "latest"
+ },
+ "devDependencies": {
+ "typescript": "^4.9.3",
+ "vite": "^5.0.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz",
+ "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz",
+ "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz",
+ "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz",
+ "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz",
+ "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz",
+ "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz",
+ "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz",
+ "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz",
+ "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz",
+ "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz",
+ "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz",
+ "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz",
+ "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz",
+ "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz",
+ "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz",
+ "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz",
+ "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz",
+ "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz",
+ "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz",
+ "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@wailsio/runtime": {
+ "version": "3.0.0-alpha.66",
+ "resolved": "https://registry.npmjs.org/@wailsio/runtime/-/runtime-3.0.0-alpha.66.tgz",
+ "integrity": "sha512-ENLu8rn1griL1gFHJqkq1i+BVxrrA0JPJHYneUJYuf/s54kjuQViW0RKDEe/WTDo56ABpfykrd/T8OYpPUyXUw==",
+ "license": "MIT"
+ },
+ "node_modules/esbuild": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.21.5",
+ "@esbuild/android-arm": "0.21.5",
+ "@esbuild/android-arm64": "0.21.5",
+ "@esbuild/android-x64": "0.21.5",
+ "@esbuild/darwin-arm64": "0.21.5",
+ "@esbuild/darwin-x64": "0.21.5",
+ "@esbuild/freebsd-arm64": "0.21.5",
+ "@esbuild/freebsd-x64": "0.21.5",
+ "@esbuild/linux-arm": "0.21.5",
+ "@esbuild/linux-arm64": "0.21.5",
+ "@esbuild/linux-ia32": "0.21.5",
+ "@esbuild/linux-loong64": "0.21.5",
+ "@esbuild/linux-mips64el": "0.21.5",
+ "@esbuild/linux-ppc64": "0.21.5",
+ "@esbuild/linux-riscv64": "0.21.5",
+ "@esbuild/linux-s390x": "0.21.5",
+ "@esbuild/linux-x64": "0.21.5",
+ "@esbuild/netbsd-x64": "0.21.5",
+ "@esbuild/openbsd-x64": "0.21.5",
+ "@esbuild/sunos-x64": "0.21.5",
+ "@esbuild/win32-arm64": "0.21.5",
+ "@esbuild/win32-ia32": "0.21.5",
+ "@esbuild/win32-x64": "0.21.5"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz",
+ "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.46.2",
+ "@rollup/rollup-android-arm64": "4.46.2",
+ "@rollup/rollup-darwin-arm64": "4.46.2",
+ "@rollup/rollup-darwin-x64": "4.46.2",
+ "@rollup/rollup-freebsd-arm64": "4.46.2",
+ "@rollup/rollup-freebsd-x64": "4.46.2",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.46.2",
+ "@rollup/rollup-linux-arm-musleabihf": "4.46.2",
+ "@rollup/rollup-linux-arm64-gnu": "4.46.2",
+ "@rollup/rollup-linux-arm64-musl": "4.46.2",
+ "@rollup/rollup-linux-loongarch64-gnu": "4.46.2",
+ "@rollup/rollup-linux-ppc64-gnu": "4.46.2",
+ "@rollup/rollup-linux-riscv64-gnu": "4.46.2",
+ "@rollup/rollup-linux-riscv64-musl": "4.46.2",
+ "@rollup/rollup-linux-s390x-gnu": "4.46.2",
+ "@rollup/rollup-linux-x64-gnu": "4.46.2",
+ "@rollup/rollup-linux-x64-musl": "4.46.2",
+ "@rollup/rollup-win32-arm64-msvc": "4.46.2",
+ "@rollup/rollup-win32-ia32-msvc": "4.46.2",
+ "@rollup/rollup-win32-x64-msvc": "4.46.2",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "4.9.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
+ "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "5.4.19",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz",
+ "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.21.3",
+ "postcss": "^8.4.43",
+ "rollup": "^4.20.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ }
+ }
+}
diff --git a/v3/examples/dock/frontend/package.json b/v3/examples/dock/frontend/package.json
new file mode 100644
index 000000000..b39da7ece
--- /dev/null
+++ b/v3/examples/dock/frontend/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "frontend",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build:dev": "tsc && vite build --minify false --mode development",
+ "build": "tsc && vite build --mode production",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@wailsio/runtime": "latest"
+ },
+ "devDependencies": {
+ "typescript": "^4.9.3",
+ "vite": "^5.0.0"
+ }
+}
diff --git a/v3/examples/dock/frontend/public/Inter-Medium.ttf b/v3/examples/dock/frontend/public/Inter-Medium.ttf
new file mode 100644
index 000000000..a01f3777a
Binary files /dev/null and b/v3/examples/dock/frontend/public/Inter-Medium.ttf differ
diff --git a/v3/examples/dock/frontend/public/style.css b/v3/examples/dock/frontend/public/style.css
new file mode 100644
index 000000000..0b9c58279
--- /dev/null
+++ b/v3/examples/dock/frontend/public/style.css
@@ -0,0 +1,157 @@
+:root {
+ font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
+ "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
+ sans-serif;
+ font-size: 16px;
+ line-height: 24px;
+ font-weight: 400;
+ color-scheme: light dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: rgba(27, 38, 54, 1);
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-text-size-adjust: 100%;
+ user-select: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+}
+
+@font-face {
+ font-family: "Inter";
+ font-style: normal;
+ font-weight: 400;
+ src: local(""),
+ url("./Inter-Medium.ttf") format("truetype");
+}
+
+h3 {
+ font-size: 3em;
+ line-height: 1.1;
+}
+
+a {
+ font-weight: 500;
+ color: #646cff;
+ text-decoration: inherit;
+}
+
+a:hover {
+ color: #535bf2;
+}
+
+button {
+ width: 60px;
+ height: 30px;
+ line-height: 30px;
+ border-radius: 3px;
+ border: none;
+ margin: 0 0 0 20px;
+ padding: 0 8px;
+ cursor: pointer;
+}
+
+.result {
+ height: 20px;
+ line-height: 20px;
+}
+
+body {
+ margin: 0;
+ display: flex;
+ place-items: center;
+ place-content: center;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+.container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+h1 {
+ font-size: 3.2em;
+ line-height: 1.1;
+}
+
+#app {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
+}
+
+.logo {
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+}
+
+.logo:hover {
+ filter: drop-shadow(0 0 2em #e80000aa);
+}
+
+.logo.vanilla:hover {
+ filter: drop-shadow(0 0 2em #f7df1eaa);
+}
+
+.result {
+ height: 20px;
+ line-height: 20px;
+ margin: 1.5rem auto;
+ text-align: center;
+}
+
+.footer {
+ margin-top: 1rem;
+ align-content: center;
+ text-align: center;
+}
+
+@media (prefers-color-scheme: light) {
+ :root {
+ color: #213547;
+ background-color: #ffffff;
+ }
+
+ a:hover {
+ color: #747bff;
+ }
+
+ button {
+ background-color: #f9f9f9;
+ }
+}
+
+
+.input-box .btn:hover {
+ background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%);
+ color: #333333;
+}
+
+.input-box .input {
+ border: none;
+ border-radius: 3px;
+ outline: none;
+ height: 30px;
+ line-height: 30px;
+ padding: 0 10px;
+ color: black;
+ background-color: rgba(240, 240, 240, 1);
+ -webkit-font-smoothing: antialiased;
+}
+
+.input-box .input:hover {
+ border: none;
+ background-color: rgba(255, 255, 255, 1);
+}
+
+.input-box .input:focus {
+ border: none;
+ background-color: rgba(255, 255, 255, 1);
+}
\ No newline at end of file
diff --git a/v3/examples/dock/frontend/public/typescript.svg b/v3/examples/dock/frontend/public/typescript.svg
new file mode 100644
index 000000000..d91c910cc
--- /dev/null
+++ b/v3/examples/dock/frontend/public/typescript.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/v3/examples/dock/frontend/public/wails.png b/v3/examples/dock/frontend/public/wails.png
new file mode 100644
index 000000000..8bdf42483
Binary files /dev/null and b/v3/examples/dock/frontend/public/wails.png differ
diff --git a/v3/examples/dock/frontend/src/main.ts b/v3/examples/dock/frontend/src/main.ts
new file mode 100644
index 000000000..6ef5af25f
--- /dev/null
+++ b/v3/examples/dock/frontend/src/main.ts
@@ -0,0 +1,12 @@
+import { DockService } from "../bindings/github.com/wailsapp/wails/v3/pkg/services/dock"
+
+const showButton = document.getElementById('show')! as HTMLButtonElement;
+const hideButton = document.getElementById('hide')! as HTMLButtonElement;
+
+showButton.addEventListener('click', () => {
+ DockService.ShowAppIcon();
+});
+
+hideButton.addEventListener('click', () => {
+ DockService.HideAppIcon();
+});
\ No newline at end of file
diff --git a/v3/examples/dock/frontend/src/vite-env.d.ts b/v3/examples/dock/frontend/src/vite-env.d.ts
new file mode 100644
index 000000000..11f02fe2a
--- /dev/null
+++ b/v3/examples/dock/frontend/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/v3/examples/dock/frontend/tsconfig.json b/v3/examples/dock/frontend/tsconfig.json
new file mode 100644
index 000000000..c267ecf24
--- /dev/null
+++ b/v3/examples/dock/frontend/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "useDefineForClassFields": true,
+ "module": "ESNext",
+ "lib": ["ESNext", "DOM"],
+ "moduleResolution": "Node",
+ "strict": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "esModuleInterop": true,
+ "noEmit": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": false,
+ "noImplicitAny": false,
+ "noImplicitReturns": true,
+ "skipLibCheck": true
+ },
+ "include": ["src"]
+}
diff --git a/v3/examples/dock/main.go b/v3/examples/dock/main.go
new file mode 100644
index 000000000..446645448
--- /dev/null
+++ b/v3/examples/dock/main.go
@@ -0,0 +1,70 @@
+package main
+
+import (
+ "embed"
+ _ "embed"
+ "log"
+
+ "github.com/wailsapp/wails/v3/pkg/application"
+ "github.com/wailsapp/wails/v3/pkg/services/dock"
+)
+
+// Wails uses Go's `embed` package to embed the frontend files into the binary.
+// Any files in the frontend/dist folder will be embedded into the binary and
+// made available to the frontend.
+// See https://pkg.go.dev/embed for more information.
+
+//go:embed all:frontend/dist
+var assets embed.FS
+
+// 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.
+
+ dockService := dock.New()
+
+ app := application.New(application.Options{
+ Name: "dock",
+ Description: "A demo of using raw HTML & CSS",
+ Services: []application.Service{
+ application.NewService(dockService),
+ },
+ Assets: application.AssetOptions{
+ Handler: application.AssetFileServerFS(assets),
+ },
+ Mac: application.MacOptions{
+ ApplicationShouldTerminateAfterLastWindowClosed: true,
+ },
+ })
+
+ // Create a new window with the necessary options.
+ // 'Title' is the title of the window.
+ // 'Mac' options tailor the window when running on macOS.
+ // 'BackgroundColour' is the background colour of the window.
+ // 'URL' is the URL that will be loaded into the webview.
+ app.Window.NewWithOptions(application.WebviewWindowOptions{
+ Title: "Window 1",
+ Mac: application.MacWindow{
+ InvisibleTitleBarHeight: 50,
+ Backdrop: application.MacBackdropTranslucent,
+ TitleBar: application.MacTitleBarHiddenInset,
+ },
+ BackgroundColour: application.NewRGB(27, 38, 54),
+ URL: "/",
+ })
+
+ // 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)
+ }
+}
diff --git a/v3/pkg/services/badge/badge.go b/v3/pkg/services/badge/badge.go
deleted file mode 100644
index 29b363082..000000000
--- a/v3/pkg/services/badge/badge.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package badge
-
-import (
- "context"
- "image/color"
-
- "github.com/wailsapp/wails/v3/pkg/application"
-)
-
-type platformBadge interface {
- // Lifecycle methods
- Startup(ctx context.Context, options application.ServiceOptions) error
- Shutdown() error
-
- SetBadge(label string) error
- SetCustomBadge(label string, options Options) error
- RemoveBadge() error
-}
-
-// Service represents the notifications service
-type BadgeService struct {
- impl platformBadge
-}
-
-type Options struct {
- TextColour color.RGBA
- BackgroundColour color.RGBA
- FontName string
- FontSize int
- SmallFontSize int
-}
-
-// ServiceName returns the name of the service.
-func (b *BadgeService) ServiceName() string {
- return "github.com/wailsapp/wails/v3/services/badge"
-}
-
-// ServiceStartup is called when the service is loaded.
-func (b *BadgeService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
- return b.impl.Startup(ctx, options)
-}
-
-// ServiceShutdown is called when the service is unloaded.
-func (b *BadgeService) ServiceShutdown() error {
- return b.impl.Shutdown()
-}
-
-// SetBadge sets the badge label on the application icon.
-func (b *BadgeService) SetBadge(label string) error {
- return b.impl.SetBadge(label)
-}
-
-func (b *BadgeService) SetCustomBadge(label string, options Options) error {
- return b.impl.SetCustomBadge(label, options)
-}
-
-// RemoveBadge removes the badge label from the application icon.
-func (b *BadgeService) RemoveBadge() error {
- return b.impl.RemoveBadge()
-}
diff --git a/v3/pkg/services/badge/badge_darwin.go b/v3/pkg/services/badge/badge_darwin.go
deleted file mode 100644
index cf61c71ca..000000000
--- a/v3/pkg/services/badge/badge_darwin.go
+++ /dev/null
@@ -1,74 +0,0 @@
-//go:build darwin
-
-package badge
-
-/*
-#cgo CFLAGS: -x objective-c
-#cgo LDFLAGS: -framework Cocoa
-#import
-
-static void setBadge(const char *label) {
- NSString *nsLabel = nil;
- if (label != NULL) {
- nsLabel = [NSString stringWithUTF8String:label];
- }
- [[NSApp dockTile] setBadgeLabel:nsLabel];
- [[NSApp dockTile] display];
-}
-*/
-import "C"
-import (
- "context"
- "unsafe"
-
- "github.com/wailsapp/wails/v3/pkg/application"
-)
-
-type darwinBadge struct{}
-
-// Creates a new Badge Service.
-func New() *BadgeService {
- return &BadgeService{
- impl: &darwinBadge{},
- }
-}
-
-// NewWithOptions creates a new badge service with the given options.
-// Currently, options are not available on macOS and are ignored.
-// (Windows-specific)
-func NewWithOptions(options Options) *BadgeService {
- return New()
-}
-
-func (d *darwinBadge) Startup(ctx context.Context, options application.ServiceOptions) error {
- return nil
-}
-
-func (d *darwinBadge) Shutdown() error {
- return nil
-}
-
-// SetBadge sets the badge label on the application icon.
-func (d *darwinBadge) SetBadge(label string) error {
- var cLabel *C.char
- if label != "" {
- cLabel = C.CString(label)
- defer C.free(unsafe.Pointer(cLabel))
- } else {
- cLabel = C.CString("●") // Default badge character
- }
- C.setBadge(cLabel)
- return nil
-}
-
-// SetCustomBadge is not supported on macOS, SetBadge is called instead.
-// (Windows-specific)
-func (d *darwinBadge) SetCustomBadge(label string, options Options) error {
- return d.SetBadge(label)
-}
-
-// RemoveBadge removes the badge label from the application icon.
-func (d *darwinBadge) RemoveBadge() error {
- C.setBadge(nil)
- return nil
-}
diff --git a/v3/pkg/services/badge/badge_linux.go b/v3/pkg/services/badge/badge_linux.go
deleted file mode 100644
index aba02f47d..000000000
--- a/v3/pkg/services/badge/badge_linux.go
+++ /dev/null
@@ -1,58 +0,0 @@
-//go:build linux
-
-package badge
-
-import (
- "context"
-
- "github.com/wailsapp/wails/v3/pkg/application"
-)
-
-type linuxBadge struct{}
-
-// New creates a new Badge Service.
-// On Linux, this returns a no-op implementation since most desktop environments
-// don't have standardized dock badge functionality.
-func New() *BadgeService {
- return &BadgeService{
- impl: &linuxBadge{},
- }
-}
-
-// NewWithOptions creates a new badge service with the given options.
-// On Linux, this returns a no-op implementation since most desktop environments
-// don't have standardized dock badge functionality. Options are ignored.
-func NewWithOptions(options Options) *BadgeService {
- return New()
-}
-
-func (l *linuxBadge) Startup(ctx context.Context, options application.ServiceOptions) error {
- // No-op: Linux doesn't have standardized badge support
- return nil
-}
-
-func (l *linuxBadge) Shutdown() error {
- // No-op: Linux doesn't have standardized badge support
- return nil
-}
-
-// SetBadge is a no-op on Linux since most desktop environments don't support
-// application dock badges. This method exists for cross-platform compatibility.
-func (l *linuxBadge) SetBadge(label string) error {
- // No-op: Linux doesn't have standardized badge support
- return nil
-}
-
-// SetCustomBadge is a no-op on Linux since most desktop environments don't support
-// application dock badges. This method exists for cross-platform compatibility.
-func (l *linuxBadge) SetCustomBadge(label string, options Options) error {
- // No-op: Linux doesn't have standardized badge support
- return nil
-}
-
-// RemoveBadge is a no-op on Linux since most desktop environments don't support
-// application dock badges. This method exists for cross-platform compatibility.
-func (l *linuxBadge) RemoveBadge() error {
- // No-op: Linux doesn't have standardized badge support
- return nil
-}
diff --git a/v3/pkg/services/dock/dock.go b/v3/pkg/services/dock/dock.go
new file mode 100644
index 000000000..eb11f59ca
--- /dev/null
+++ b/v3/pkg/services/dock/dock.go
@@ -0,0 +1,77 @@
+package dock
+
+import (
+ "context"
+ "image/color"
+
+ "github.com/wailsapp/wails/v3/pkg/application"
+)
+
+type platformDock interface {
+ // Lifecycle methods
+ Startup(ctx context.Context, options application.ServiceOptions) error
+ Shutdown() error
+
+ // Dock icon visibility methods
+ HideAppIcon()
+ ShowAppIcon()
+
+ // Badge methods
+ SetBadge(label string) error
+ SetCustomBadge(label string, options BadgeOptions) error
+ RemoveBadge() error
+}
+
+// Service represents the dock service
+type DockService struct {
+ impl platformDock
+}
+
+// BadgeOptions represents options for customizing badge appearance
+type BadgeOptions struct {
+ TextColour color.RGBA
+ BackgroundColour color.RGBA
+ FontName string
+ FontSize int
+ SmallFontSize int
+}
+
+// ServiceName returns the name of the service.
+func (d *DockService) ServiceName() string {
+ return "github.com/wailsapp/wails/v3/pkg/services/dock"
+}
+
+// ServiceStartup is called when the service is loaded.
+func (d *DockService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
+ return d.impl.Startup(ctx, options)
+}
+
+// ServiceShutdown is called when the service is unloaded.
+func (d *DockService) ServiceShutdown() error {
+ return d.impl.Shutdown()
+}
+
+// HideAppIcon hides the app icon in the dock/taskbar.
+func (d *DockService) HideAppIcon() {
+ d.impl.HideAppIcon()
+}
+
+// ShowAppIcon shows the app icon in the dock/taskbar.
+func (d *DockService) ShowAppIcon() {
+ d.impl.ShowAppIcon()
+}
+
+// SetBadge sets the badge label on the application icon.
+func (d *DockService) SetBadge(label string) error {
+ return d.impl.SetBadge(label)
+}
+
+// SetCustomBadge sets the badge label on the application icon with custom options.
+func (d *DockService) SetCustomBadge(label string, options BadgeOptions) error {
+ return d.impl.SetCustomBadge(label, options)
+}
+
+// RemoveBadge removes the badge label from the application icon.
+func (d *DockService) RemoveBadge() error {
+ return d.impl.RemoveBadge()
+}
diff --git a/v3/pkg/services/dock/dock_darwin.go b/v3/pkg/services/dock/dock_darwin.go
new file mode 100644
index 000000000..c42ae6b0c
--- /dev/null
+++ b/v3/pkg/services/dock/dock_darwin.go
@@ -0,0 +1,97 @@
+//go:build darwin
+
+package dock
+
+/*
+#cgo CFLAGS: -x objective-c
+#cgo LDFLAGS: -framework Cocoa
+#import
+
+void hideDockIcon() {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
+ });
+}
+
+void showDockIcon() {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
+ });
+}
+
+static void setBadge(const char *label) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ NSString *nsLabel = nil;
+ if (label != NULL) {
+ nsLabel = [NSString stringWithUTF8String:label];
+ }
+ [[NSApp dockTile] setBadgeLabel:nsLabel];
+ [[NSApp dockTile] display];
+ });
+}
+*/
+import "C"
+import (
+ "context"
+ "unsafe"
+
+ "github.com/wailsapp/wails/v3/pkg/application"
+)
+
+type darwinDock struct{}
+
+// Creates a new Dock Service.
+func New() *DockService {
+ return &DockService{
+ impl: &darwinDock{},
+ }
+}
+
+// NewWithOptions creates a new dock service with badge options.
+// Currently, options are not available on macOS and are ignored.
+func NewWithOptions(options BadgeOptions) *DockService {
+ return New()
+}
+
+func (d *darwinDock) Startup(ctx context.Context, options application.ServiceOptions) error {
+ return nil
+}
+
+func (d *darwinDock) Shutdown() error {
+ return nil
+}
+
+// HideAppIcon hides the app icon in the macOS Dock.
+func (d *darwinDock) HideAppIcon() {
+ C.hideDockIcon()
+}
+
+// ShowAppIcon shows the app icon in the macOS Dock.
+func (d *darwinDock) ShowAppIcon() {
+ C.showDockIcon()
+}
+
+// SetBadge sets the badge label on the application icon.
+func (d *darwinDock) SetBadge(label string) error {
+ // Always pick a label (use “●” if empty), then allocate + free exactly once.
+ value := label
+ if value == "" {
+ value = "●" // Default badge character
+ }
+ cLabel := C.CString(value)
+ defer C.free(unsafe.Pointer(cLabel))
+
+ C.setBadge(cLabel)
+ return nil
+}
+
+// SetCustomBadge is not supported on macOS, SetBadge is called instead.
+func (d *darwinDock) SetCustomBadge(label string, options BadgeOptions) error {
+ return d.SetBadge(label)
+}
+
+// RemoveBadge removes the badge label from the application icon.
+func (d *darwinDock) RemoveBadge() error {
+ C.setBadge(nil)
+ return nil
+}
diff --git a/v3/pkg/services/dock/dock_linux.go b/v3/pkg/services/dock/dock_linux.go
new file mode 100644
index 000000000..af0311c67
--- /dev/null
+++ b/v3/pkg/services/dock/dock_linux.go
@@ -0,0 +1,70 @@
+//go:build linux
+
+package dock
+
+import (
+ "context"
+
+ "github.com/wailsapp/wails/v3/pkg/application"
+)
+
+type linuxDock struct{}
+
+// New creates a new Dock Service.
+// On Linux, this returns a stub implementation since dock icon visibility
+// and badge functionality are not standardized across desktop environments.
+func New() *DockService {
+ return &DockService{
+ impl: &linuxDock{},
+ }
+}
+
+// NewWithOptions creates a new dock service with badge options.
+// On Linux, this returns a stub implementation since badge functionality
+// is not standardized across desktop environments. Options are ignored.
+func NewWithOptions(options BadgeOptions) *DockService {
+ return New()
+}
+
+func (l *linuxDock) Startup(ctx context.Context, options application.ServiceOptions) error {
+ // No-op: Linux doesn't have standardized dock/badge support
+ return nil
+}
+
+func (l *linuxDock) Shutdown() error {
+ // No-op: Linux doesn't have standardized dock/badge support
+ return nil
+}
+
+// HideAppIcon is a stub on Linux since dock icon visibility is not
+// standardized across desktop environments.
+func (l *linuxDock) HideAppIcon() {
+ // No-op: Linux doesn't have standardized dock icon visibility support
+}
+
+// ShowAppIcon is a stub on Linux since dock icon visibility is not
+// standardized across desktop environments.
+func (l *linuxDock) ShowAppIcon() {
+ // No-op: Linux doesn't have standardized dock icon visibility support
+}
+
+// SetBadge is a stub on Linux since most desktop environments don't support
+// application dock badges. This method exists for cross-platform compatibility.
+func (l *linuxDock) SetBadge(label string) error {
+ // No-op: Linux doesn't have standardized badge support
+ return nil
+}
+
+// SetCustomBadge is a stub on Linux since most desktop environments don't support
+// application dock badges. This method exists for cross-platform compatibility.
+func (l *linuxDock) SetCustomBadge(label string, options BadgeOptions) error {
+ // No-op: Linux doesn't have standardized badge support
+ return nil
+}
+
+// RemoveBadge is a stub on Linux since most desktop environments don't support
+// application dock badges. This method exists for cross-platform compatibility.
+func (l *linuxDock) RemoveBadge() error {
+ // No-op: Linux doesn't have standardized badge support
+ return nil
+}
diff --git a/v3/pkg/services/badge/badge_windows.go b/v3/pkg/services/dock/dock_windows.go
similarity index 53%
rename from v3/pkg/services/badge/badge_windows.go
rename to v3/pkg/services/dock/dock_windows.go
index 3ef503e26..26f40d259 100644
--- a/v3/pkg/services/badge/badge_windows.go
+++ b/v3/pkg/services/dock/dock_windows.go
@@ -1,6 +1,6 @@
//go:build windows
-package badge
+package dock
import (
"bytes"
@@ -18,15 +18,15 @@ import (
"golang.org/x/image/math/fixed"
)
-type windowsBadge struct {
- taskbar *w32.ITaskbarList3
- badgeImg *image.RGBA
- badgeSize int
- fontManager *FontManager
- options Options
+type windowsDock struct {
+ taskbar *w32.ITaskbarList3
+ badgeImg *image.RGBA
+ badgeSize int
+ fontManager *FontManager
+ badgeOptions BadgeOptions
}
-var defaultOptions = Options{
+var defaultOptions = BadgeOptions{
TextColour: color.RGBA{255, 255, 255, 255},
BackgroundColour: color.RGBA{255, 0, 0, 255},
FontName: "segoeuib.ttf",
@@ -35,24 +35,24 @@ var defaultOptions = Options{
}
// Creates a new Badge Service.
-func New() *BadgeService {
- return &BadgeService{
- impl: &windowsBadge{
- options: defaultOptions,
+func New() *DockService {
+ return &DockService{
+ impl: &windowsDock{
+ badgeOptions: defaultOptions,
},
}
}
-// NewWithOptions creates a new badge service with the given options.
-func NewWithOptions(options Options) *BadgeService {
- return &BadgeService{
- impl: &windowsBadge{
- options: options,
+// NewWithOptions creates a new badge service with the given badge options.
+func NewWithOptions(options BadgeOptions) *DockService {
+ return &DockService{
+ impl: &windowsDock{
+ badgeOptions: options,
},
}
}
-func (w *windowsBadge) Startup(ctx context.Context, options application.ServiceOptions) error {
+func (w *windowsDock) Startup(ctx context.Context, options application.ServiceOptions) error {
taskbar, err := w32.NewTaskbarList3()
if err != nil {
return err
@@ -63,7 +63,7 @@ func (w *windowsBadge) Startup(ctx context.Context, options application.ServiceO
return nil
}
-func (w *windowsBadge) Shutdown() error {
+func (w *windowsDock) Shutdown() error {
if w.taskbar != nil {
w.taskbar.Release()
}
@@ -71,134 +71,150 @@ func (w *windowsBadge) Shutdown() error {
return nil
}
+// HideAppIcon hides the app icon in the macOS Dock.
+func (w *windowsDock) HideAppIcon() {
+ // No-op: researching Windows options
+}
+
+// ShowAppIcon shows the app icon in the macOS Dock.
+func (w *windowsDock) ShowAppIcon() {
+ // No-op: researching Windows options
+}
+
// SetBadge sets the badge label on the application icon.
-func (w *windowsBadge) SetBadge(label string) error {
- if w.taskbar == nil {
- return nil
- }
-
- app := application.Get()
- if app == nil {
- return nil
- }
-
- window := app.Window.Current()
- if window == nil {
- return nil
- }
-
- nativeWindow := window.NativeWindow()
- if nativeWindow == nil {
- return errors.New("window native handle unavailable")
- }
- hwnd := uintptr(nativeWindow)
-
- w.createBadge()
-
- var hicon w32.HICON
- var err error
- if label == "" {
- hicon, err = w.createBadgeIcon()
- if err != nil {
- return err
+func (w *windowsDock) SetBadge(label string) error {
+ return application.InvokeSyncWithError(func() error {
+ if w.taskbar == nil {
+ return nil
}
- } else {
- hicon, err = w.createBadgeIconWithText(label)
- if err != nil {
- return err
- }
- }
- defer w32.DestroyIcon(hicon)
- return w.taskbar.SetOverlayIcon(hwnd, hicon, nil)
+ app := application.Get()
+ if app == nil {
+ return nil
+ }
+
+ window := app.Window.Current()
+ if window == nil {
+ return nil
+ }
+
+ nativeWindow := window.NativeWindow()
+ if nativeWindow == nil {
+ return errors.New("window native handle unavailable")
+ }
+ hwnd := uintptr(nativeWindow)
+
+ w.createBadge()
+
+ var hicon w32.HICON
+ var err error
+ if label == "" {
+ hicon, err = w.createBadgeIcon()
+ if err != nil {
+ return err
+ }
+ } else {
+ hicon, err = w.createBadgeIconWithText(label)
+ if err != nil {
+ return err
+ }
+ }
+ defer w32.DestroyIcon(hicon)
+
+ return w.taskbar.SetOverlayIcon(hwnd, hicon, nil)
+ })
}
// SetCustomBadge sets the badge label on the application icon with one-off options.
-func (w *windowsBadge) SetCustomBadge(label string, options Options) error {
- if w.taskbar == nil {
- return nil
- }
+func (w *windowsDock) SetCustomBadge(label string, options BadgeOptions) error {
+ return application.InvokeSyncWithError(func() error {
+ if w.taskbar == nil {
+ return nil
+ }
- app := application.Get()
- if app == nil {
- return nil
- }
+ app := application.Get()
+ if app == nil {
+ return nil
+ }
- window := app.Window.Current()
- if window == nil {
- return nil
- }
+ window := app.Window.Current()
+ if window == nil {
+ return nil
+ }
- nativeWindow := window.NativeWindow()
- if nativeWindow == nil {
- return errors.New("window native handle unavailable")
- }
- hwnd := uintptr(nativeWindow)
+ nativeWindow := window.NativeWindow()
+ if nativeWindow == nil {
+ return errors.New("window native handle unavailable")
+ }
+ hwnd := uintptr(nativeWindow)
- const badgeSize = 32
+ const badgeSize = 32
- img := image.NewRGBA(image.Rect(0, 0, badgeSize, badgeSize))
+ img := image.NewRGBA(image.Rect(0, 0, badgeSize, badgeSize))
- backgroundColour := options.BackgroundColour
- radius := badgeSize / 2
- centerX, centerY := radius, radius
+ backgroundColour := options.BackgroundColour
+ radius := badgeSize / 2
+ centerX, centerY := radius, radius
- for y := 0; y < badgeSize; y++ {
- for x := 0; x < badgeSize; x++ {
- dx := float64(x - centerX)
- dy := float64(y - centerY)
+ for y := 0; y < badgeSize; y++ {
+ for x := 0; x < badgeSize; x++ {
+ dx := float64(x - centerX)
+ dy := float64(y - centerY)
- if dx*dx+dy*dy < float64(radius*radius) {
- img.Set(x, y, backgroundColour)
+ if dx*dx+dy*dy < float64(radius*radius) {
+ img.Set(x, y, backgroundColour)
+ }
}
}
- }
- var hicon w32.HICON
- var err error
- if label == "" {
- hicon, err = createBadgeIcon(badgeSize, img, options)
- if err != nil {
- return err
+ var hicon w32.HICON
+ var err error
+ if label == "" {
+ hicon, err = createBadgeIcon(badgeSize, img, options)
+ if err != nil {
+ return err
+ }
+ } else {
+ hicon, err = createBadgeIconWithText(w, label, badgeSize, img, options)
+ if err != nil {
+ return err
+ }
}
- } else {
- hicon, err = createBadgeIconWithText(w, label, badgeSize, img, options)
- if err != nil {
- return err
- }
- }
- defer w32.DestroyIcon(hicon)
+ defer w32.DestroyIcon(hicon)
- return w.taskbar.SetOverlayIcon(hwnd, hicon, nil)
+ return w.taskbar.SetOverlayIcon(hwnd, hicon, nil)
+ })
}
// RemoveBadge removes the badge label from the application icon.
-func (w *windowsBadge) RemoveBadge() error {
- if w.taskbar == nil {
- return nil
- }
+func (w *windowsDock) RemoveBadge() error {
+ return application.InvokeSyncWithError(func() error {
+ if w.taskbar == nil {
+ return nil
+ }
- app := application.Get()
- if app == nil {
- return nil
- }
+ app := application.Get()
+ if app == nil {
+ return nil
+ }
- window := app.Window.Current()
- if window == nil {
- return nil
- }
+ window := app.Window.Current()
+ if window == nil {
+ return nil
+ }
- nativeWindow := window.NativeWindow()
- if nativeWindow == nil {
- return errors.New("window native handle unavailable")
- }
- hwnd := uintptr(nativeWindow)
+ nativeWindow := window.NativeWindow()
+ if nativeWindow == nil {
+ return errors.New("window native handle unavailable")
+ }
+ hwnd := uintptr(nativeWindow)
- return w.taskbar.SetOverlayIcon(hwnd, 0, nil)
+ return w.taskbar.SetOverlayIcon(hwnd, 0, nil)
+ })
}
// createBadgeIcon creates a badge icon with the specified size and color.
-func (w *windowsBadge) createBadgeIcon() (w32.HICON, error) {
+func (w *windowsDock) createBadgeIcon() (w32.HICON, error) {
radius := w.badgeSize / 2
centerX, centerY := radius, radius
innerRadius := w.badgeSize / 5
@@ -209,7 +225,7 @@ func (w *windowsBadge) createBadgeIcon() (w32.HICON, error) {
dy := float64(y - centerY)
if dx*dx+dy*dy < float64(innerRadius*innerRadius) {
- w.badgeImg.Set(x, y, w.options.TextColour)
+ w.badgeImg.Set(x, y, w.badgeOptions.TextColour)
}
}
}
@@ -223,7 +239,7 @@ func (w *windowsBadge) createBadgeIcon() (w32.HICON, error) {
return hicon, err
}
-func createBadgeIcon(badgeSize int, img *image.RGBA, options Options) (w32.HICON, error) {
+func createBadgeIcon(badgeSize int, img *image.RGBA, options BadgeOptions) (w32.HICON, error) {
radius := badgeSize / 2
centerX, centerY := radius, radius
innerRadius := badgeSize / 5
@@ -249,8 +265,8 @@ func createBadgeIcon(badgeSize int, img *image.RGBA, options Options) (w32.HICON
}
// createBadgeIconWithText creates a badge icon with the specified text.
-func (w *windowsBadge) createBadgeIconWithText(label string) (w32.HICON, error) {
- fontPath := w.fontManager.FindFontOrDefault(w.options.FontName)
+func (w *windowsDock) createBadgeIconWithText(label string) (w32.HICON, error) {
+ fontPath := w.fontManager.FindFontOrDefault(w.badgeOptions.FontName)
if fontPath == "" {
return w.createBadgeIcon()
}
@@ -265,9 +281,9 @@ func (w *windowsBadge) createBadgeIconWithText(label string) (w32.HICON, error)
return w.createBadgeIcon()
}
- fontSize := float64(w.options.FontSize)
+ fontSize := float64(w.badgeOptions.FontSize)
if len(label) > 1 {
- fontSize = float64(w.options.SmallFontSize)
+ fontSize = float64(w.badgeOptions.SmallFontSize)
}
// Get DPI of the current screen
@@ -286,7 +302,7 @@ func (w *windowsBadge) createBadgeIconWithText(label string) (w32.HICON, error)
d := &font.Drawer{
Dst: w.badgeImg,
- Src: image.NewUniform(w.options.TextColour),
+ Src: image.NewUniform(w.badgeOptions.TextColour),
Face: face,
}
@@ -302,7 +318,7 @@ func (w *windowsBadge) createBadgeIconWithText(label string) (w32.HICON, error)
return w32.CreateSmallHIconFromImage(buf.Bytes())
}
-func createBadgeIconWithText(w *windowsBadge, label string, badgeSize int, img *image.RGBA, options Options) (w32.HICON, error) {
+func createBadgeIconWithText(w *windowsDock, label string, badgeSize int, img *image.RGBA, options BadgeOptions) (w32.HICON, error) {
fontPath := w.fontManager.FindFontOrDefault(options.FontName)
if fontPath == "" {
return createBadgeIcon(badgeSize, img, options)
@@ -356,12 +372,12 @@ func createBadgeIconWithText(w *windowsBadge, label string, badgeSize int, img *
}
// createBadge creates a circular badge with the specified background color.
-func (w *windowsBadge) createBadge() {
+func (w *windowsDock) createBadge() {
w.badgeSize = 32
img := image.NewRGBA(image.Rect(0, 0, w.badgeSize, w.badgeSize))
- backgroundColour := w.options.BackgroundColour
+ backgroundColour := w.badgeOptions.BackgroundColour
radius := w.badgeSize / 2
centerX, centerY := radius, radius
diff --git a/v3/pkg/services/badge/font.go b/v3/pkg/services/dock/font.go
similarity index 99%
rename from v3/pkg/services/badge/font.go
rename to v3/pkg/services/dock/font.go
index 430a9c14d..54dcc8cd6 100644
--- a/v3/pkg/services/badge/font.go
+++ b/v3/pkg/services/dock/font.go
@@ -1,6 +1,6 @@
//go:build windows
-package badge
+package dock
import (
"errors"