gui/docs/ref/wails-v3/contributing/extending-wails.mdx
Snider 4bdbb68f46
Some checks failed
Security Scan / security (push) Failing after 9s
Test / test (push) Failing after 1m21s
refactor: update import path from go-config to core/config
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-14 10:26:36 +00:00

246 lines
5.7 KiB
Text
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Extending Wails
description: Practical guide to adding new features and platforms to Wails v3
sidebar:
order: 8
---
> Wails is designed to be **hackable**.
> Every major subsystem lives in Go code you can read, modify, and ship.
> This page shows _where_ to start and _how_ to stay cross-platform when you:
* Add a **Service** (file server, KV store, custom IPC…)
* Create a **new CLI command** (`wails3 <foo>`)
* Extend the **Runtime** (window API, dialogs, events)
* Introduce a **Platform capability** (Wayland, Apple Vision OS…)
* Keep **cross-platform compatibility** without drowning in `//go:build` tags
---
## 1. Adding a Service
Services are background Go components exposed to apps via the `application.Context`.
```
internal/service/
├── template/ # Boilerplate for new services
│ ├── template.go
│ └── README.md
└── service.go # Registration + lifecycle interfaces
```
### 1.1 Define the Service
```go
package chat
type Service struct {
messages []string
}
func New() *Service { return &Service{} }
func (s *Service) Send(msg string) string {
s.messages = append(s.messages, msg)
return "ok"
}
```
### 1.2 Implement `service.Service`
```go
func (s *Service) Init(ctx *application.Context) error { return nil }
func (s *Service) Shutdown() error { return nil }
```
### 1.3 Register Generator
*Add to* `internal/generator/collect/services.go`
```go
services.Register("chat", chat.New)
```
> Now `wails3 generate` emits bindings so JS can call
> `window.backend.chat.Send("hi")`.
### 1.4 Ship an Example
Copy `v3/examples/services/` and adjust.
---
## 2. Writing a New CLI Command
CLI logic lives under `internal/commands/`.
Each command is **one file** that mounts itself in `init()`.
### 2.1 Create the File
`v3/internal/commands/hello.go`
```go
package commands
import (
"github.com/spf13/cobra"
)
func init() {
Add(&cobra.Command{
Use: "hello",
Short: "Prints Hello World",
RunE: func(cmd *cobra.Command, args []string) error {
cmd.Println("Hello Wails!")
return nil
},
})
}
```
`Add()` registers with the root in `cmd/wails3/main.go`.
### 2.2 Re-compile CLI
```
go install ./v3/cmd/wails3
wails3 hello
```
If your command needs **Taskfile** integration, reuse helpers in
`internal/commands/task_wrapper.go`.
---
## 3. Modifying the Runtime
Common reasons:
* New window feature (`SetOpacity`, `Shake`, …)
* Extra dialog (`ColorPicker`)
* System-level API (screen brightness)
### 3.1 Public API
Add to `pkg/application/window.go`:
```go
func (w *WebviewWindow) SetOpacity(o float32) {
w.ctx.Call("window:setOpacity", o)
}
```
### 3.2 Message Processor
Create `messageprocessor_window_opacity.go`:
```go
const MsgSetOpacity = "window:setOpacity"
func init() { register(MsgSetOpacity, handleSetOpacity) }
func handleSetOpacity(ctx *Context, params json.RawMessage) ([]byte, error) {
var req struct {
ID uint64 `json:"id"`
Opacity float32 `json:"o"`
}
json.Unmarshal(params, &req)
return nil, runtime.SetOpacity(req.ID, req.Opacity)
}
```
### 3.3 Platform Implementation
```
internal/runtime/
webview_window_darwin.go
webview_window_linux.go
webview_window_windows.go
```
Add `SetOpacity()` to each, guarded by build tags.
If a platform cant support it, return `ErrCapability`.
### 3.4 Capability Flag
Expose via `internal/capabilities`.
```go
const CapOpacity = "window:opacity"
```
Runtime files `*_darwin.go` etc. should `registerCapability(CapOpacity)` when
successfully initialised.
Apps can query:
```go
if application.HasCapability(application.CapOpacity) { ... }
```
---
## 4. Adding New Platform Capabilities
Example: **Wayland** clipboard on Linux.
1. Fork `internal/runtime/clipboard_linux.go`.
2. Split file:
* `clipboard_linux_x11.go` → `//go:build linux && !wayland`
* `clipboard_linux_wayland.go` → `//go:build linux && wayland`
3. Add CLI flag `--tags wayland` in `internal/commands/dev.go`
(`goEnv.BuildTags += ",wayland"`).
4. Document in **Asset Server** & README.
> Keep default build tags minimal; reserve opt-in tags for niche features.
---
## 5. Cross-Platform Compatibility Checklist
| ✅ Step | Why |
|---------|-----|
| Provide **every** public method in all platform files (even if stub) | Keeps build green on all OS/arch |
| Gate platform specifics behind **capabilities** | Let apps degrade gracefully |
| Use **pure Go** first, CGO only when needed | Simplifies cross-compiles |
| Test with `task matrix:test` | Runs `go test ./...` on linux/mac/windows in Docker |
| Document new build tags in `docs/getting-started/installation.mdx` | Users must know flags |
---
## 6. Debug Builds & Iteration Speed
* `wails3 dev -tags debug` adds extra log hooks (`logger_dev*.go`).
* Use `task reload` to rebuild runtime without restarting the whole app.
* Race detector: `wails3 dev -race` (see `pkg/application/RACE.md`).
---
## 7. Upstream Contributions
1. Open an **issue** to discuss the idea & design.
2. Follow the techniques above to implement.
3. Add:
* Unit tests (`*_test.go`)
* Example (`v3/examples/<feat>/`)
* Docs (this file or API reference)
4. Ensure `go vet`, `golangci-lint`, and CI matrix pass.
---
### Quick Links
| Area | Location |
|------|----------|
| Services framework | `internal/service/` |
| CLI core | `internal/commands/` |
| Runtime per-OS | `internal/runtime/` |
| Capability helpers| `internal/capabilities/` |
| Taskfile DSL | `tasks/Taskfile.yml` |
| Dev matrix tests | `tasks/events/generate.go` |
---
You now have a **roadmap** for bending Wails to your will—add services, sprinkle
CLI magic, hack the runtime, or bring entirely new OS features.
Happy extending!