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
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>
164 lines
4.1 KiB
Go
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
|
|
}
|