Files
netbird/client/ui
Zoltan Papp aab8274b1a clear connect-action latch when external disconnect cancels Connecting
The main-window toggle stayed visually stuck on "Connecting" when the
user clicked Connect in the UI and then clicked Disconnect in the
tray (or the daemon was otherwise cancelled mid-Connecting).

Repro: open the main window, click the toggle to Connect, then while
the daemon is still in Connecting click Disconnect in the tray menu.
The tray and daemon agree the session is Idle, but the React toggle
keeps painting "Connecting" until the next manual interaction.

Root cause is in ConnectionStatusSwitch.tsx. The component holds an
`action` latch ("connect" | "logging-in" | "disconnect" | null) so the
toggle can show an optimistic transitional state while the daemon
catches up. The connState memo treats `action === "connect"` plus any
non-Connected daemon state as Connecting:

    if ((action === "connect" || action === "logging-in") &&
        daemonState !== "Connected") {
        return ConnectionState.Connecting;
    }

The effect that releases the latch only cleared it on `Connected` or
`DaemonUnavailable`. There was no branch for "the connect flow was
cancelled externally and the daemon is back at Idle", so the latch
remained set forever and the optimistic Connecting state never
collapsed.

Fix: add a `sawConnectingRef` that flips to true the first time the
daemon reports Connecting during an active "connect" action, and
resets when `action` returns to null. When `action === "connect"` and
the daemon flips from a state we'd observed as Connecting back to
Idle, clear the latch so connState falls through to Disconnected.

Other paths are untouched:
- Successful connect still clears on Connected.
- NeedsLogin still hands off to driveLogin.
- DaemonUnavailable still clears via the `unreachable` branch.
- The `"logging-in"` action is intentionally not handled here; Login's
  internal Down flaps the daemon through Idle and driveLogin's
  .finally remains the sole clearer for that latch.
- The `"disconnect"` action's Idle/Disconnected/unreachable clear is
  unchanged.
2026-05-20 19:44:02 +02:00
..
2026-05-20 16:43:14 +02:00
2026-05-20 13:17:13 +02:00
2026-05-15 16:22:14 +02:00
2026-05-20 16:43:14 +02:00
2026-05-15 16:22:14 +02:00

NetBird desktop UI (Wails3 + React)

Replaces client/ui (Fyne). One binary on Windows / macOS / Linux, talks to the NetBird daemon over gRPC, renders a React frontend in a WebView.

Prerequisites

  • Go ≥ 1.25, Node ≥ 20, pnpm (corepack enable && corepack prepare pnpm@latest --activate)
  • wails3 CLI: go install github.com/wailsapp/wails/v3/cmd/wails3@latest
  • task: go install github.com/go-task/task/v3/cmd/task@latest
  • A running NetBird daemon (default: unix:///var/run/netbird.sock, Windows tcp://127.0.0.1:41731)
  • Linux only: libwebkit2gtk-4.1-dev, libgtk-3-dev, libayatana-appindicator3-dev

Develop without rebuilding

cd client/ui
task dev

task dev runs Vite (port 9245) + the Go binary + a *.go watcher. Frontend edits hot-reload instantly. Go edits trigger a rebuild and relaunch. Pass daemon flags after --:

task dev -- --daemon-addr=tcp://127.0.0.1:41731

For pure UI work (no native window, fastest loop):

cd frontend && pnpm dev

Production build

task build

Output in bin/. Frontend assets are embedded into the binary.

Cross-compile Windows from Linux

Install the mingw-w64 toolchain once:

sudo apt install gcc-mingw-w64-x86-64           # Debian/Ubuntu
sudo dnf install mingw64-gcc                    # Fedora
sudo pacman -S mingw-w64-gcc                    # Arch

Then:

CGO_ENABLED=1 task windows:build

Produces bin/netbird-ui.exe. macOS cross-compile from Linux is not supported (signing and notarization need a real Mac).

Windows console build (logs in the terminal)

Default windows:build links the binary as a Windows GUI app, which detaches from the launching console — logrus output, fmt.Println, and panics go nowhere visible. To debug tray/event/daemon issues:

CGO_ENABLED=1 task windows:build:console

Produces bin/netbird-ui-console.exe. Run it from cmd.exe / PowerShell / Windows Terminal and stdout/stderr land in that terminal. Same flag works on a native Windows build (drop the CGO_ENABLED=1 if your toolchain already has it set).

Regenerating bindings

When a Go service signature changes:

wails3 generate bindings

task dev does this automatically on *.go save.

Tray icons

Source SVGs live in assets/svg/ (state.svg + state-macos.svg). After editing any SVG, rasterize to the PNGs the Go side embeds:

task common:generate:tray:icons

Requires Inkscape. Commit the resulting assets/*.png files alongside the SVG change so CI doesn't need Inkscape installed.