Files
Warp/apps/desktop/cmd/warp-desktop/main.go
T
tajniak81andClaude Opus 5.5 b6bf3ce741
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Test (windows-latest) (push) Canceled after 0s
CI / Storage tests on PostgreSQL (push) Canceled after 0s
CI / Complement (federation off) (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
CI / Smoke test the binary (push) Canceled after 0s
CI / Admin panel (push) Canceled after 0s
CI / Web client (push) Canceled after 0s
Desktop client (warp-desktop) with Wails v3
apps/desktop is the web client in a native window. It embeds the same
build warp-web does and serves it through the same Go package, so every
screen is shared. Unlike warp-web it proxies nothing: the client calls the
homeserver directly, and the CSP allows the homeserver plus any https:
and wss: origin for the call backend.

- First run asks for the homeserver: a server name or address, with
  .well-known discovery and a /versions check; File > Change homeserver
  asks again. Signed-in sessions keep their own base URL, so a change
  never sends one server's token to another.
- Links to other sites open in the system browser (/_desktop/links.js,
  added to index.html); only http, https and mailto are opened.
- One instance at a time, since sessions and keys live in the webview's
  storage.
- Dev builds open the DevTools Protocol on Windows when
  WARP_DESKTOP_REMOTE_DEBUGGING_PORT is set, for scripted testing.
- Packaging: icon, version info and manifest in the Windows exe, a macOS
  .app with camera and microphone descriptions, Linux packages via nfpm.
  Make targets: desktop, desktop-run, desktop-test, desktop-resources,
  desktop-macos, desktop-linux.

Web client: host and client move out of internal/ so the desktop app can
import them, and host gains AnyCallOrigin. The Dockerfile, .dockerignore
and the vite output path follow the move.

Pinned to Wails v3.0.0-beta.27.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 21:43:54 +02:00

164 lines
4.1 KiB
Go

// Command warp-desktop is the Warp desktop client: the web client in a
// native window (Wails v3), talking to the homeserver directly.
//
// warp-desktop [-homeserver https://matrix.example.org]
package main
import (
_ "embed"
"flag"
"fmt"
"log/slog"
"os"
"path/filepath"
"runtime"
"github.com/wailsapp/wails/v3/pkg/application"
"github.com/dariuszjaniak/warp/apps/desktop/internal/shell"
"github.com/dariuszjaniak/warp/apps/web/client"
)
// Set with -ldflags "-X main.version=...".
var version = "0.0.0-dev"
//go:embed appicon.png
var icon []byte
// appID names the app to the OS: the single-instance lock, and the
// directory the settings live in on Linux and macOS.
const appID = "io.github.dariuszjaniak.warp"
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, "warp-desktop:", err)
os.Exit(1)
}
}
func run() error {
homeserver := flag.String("homeserver", os.Getenv("WARP_DESKTOP_HOMESERVER"),
"homeserver base URL for this run, instead of the one chosen in the app (WARP_DESKTOP_HOMESERVER)")
showVersion := flag.Bool("version", false, "print the version and exit")
flag.Parse()
if *showVersion {
fmt.Println("warp-desktop", version)
return nil
}
logger := slog.New(slog.NewTextHandler(os.Stderr, nil))
slog.SetDefault(logger)
dirs, err := dataDirs()
if err != nil {
return err
}
files, built := client.FS()
if !built {
logger.Warn("the web client was not built into this binary; serving a placeholder page")
files = nil
}
var app *application.App
sh, err := shell.New(shell.Options{
Store: shell.NewStore(filepath.Join(dirs.config, "desktop.json")),
Homeserver: *homeserver,
Client: files,
Version: version,
Logger: logger,
OpenURL: func(u string) error { return app.Browser.OpenURL(u) },
})
if err != nil {
return err
}
var window *application.WebviewWindow
app = application.New(application.Options{
Name: "Warp",
Description: "A Matrix client",
Icon: icon,
Logger: logger,
Assets: application.AssetOptions{Handler: sh},
// The client's sessions and encryption keys are in the webview's
// storage; two instances on one store would both sync and both
// use the same one-time keys.
SingleInstance: &application.SingleInstanceOptions{
UniqueID: appID,
OnSecondInstanceLaunch: func(application.SecondInstanceData) {
if window != nil {
window.Restore()
window.Show()
window.Focus()
}
},
},
Mac: application.MacOptions{
ApplicationShouldTerminateAfterLastWindowClosed: true,
},
Windows: application.WindowsOptions{
WebviewUserDataPath: dirs.webview,
AdditionalBrowserArgs: devBrowserArgs(),
},
})
window = app.Window.NewWithOptions(application.WebviewWindowOptions{
Name: "main",
Title: "Warp",
Width: 1200,
Height: 800,
MinWidth: 360,
MinHeight: 480,
URL: "/",
})
app.Menu.SetApplicationMenu(appMenu(app, window))
return app.Run()
}
func appMenu(app *application.App, window *application.WebviewWindow) *application.Menu {
menu := app.NewMenu()
if runtime.GOOS == "darwin" {
menu.AddRole(application.AppMenu)
}
file := menu.AddSubmenu("File")
file.Add("Change homeserver…").OnClick(func(*application.Context) {
window.SetURL(shell.SetupPath)
})
file.AddSeparator()
if runtime.GOOS == "darwin" {
file.AddRole(application.CloseWindow)
} else {
file.AddRole(application.Quit)
}
menu.AddRole(application.EditMenu)
menu.AddRole(application.ViewMenu)
menu.AddRole(application.WindowMenu)
return menu
}
type dirs struct {
config string // settings
webview string // the webview's storage, on Windows
}
func dataDirs() (dirs, error) {
cfg, err := os.UserConfigDir()
if err != nil {
return dirs{}, err
}
d := dirs{config: filepath.Join(cfg, "Warp")}
if runtime.GOOS == "windows" {
// %LocalAppData%: the webview's storage is large and machine-local,
// so it doesn't belong in the roaming profile with the settings.
local, err := os.UserCacheDir()
if err != nil {
return dirs{}, err
}
d.webview = filepath.Join(local, "Warp", "WebView2")
}
return d, nil
}