Commit Graph

3181 Commits

Author SHA1 Message Date
Zoltán Papp
dfee5252a3 client/ui: open main window on tray left-click on Linux
KDE Plasma routes a tray left-click to the SNI Activate method (right-click
opens the context menu), but NetBird wired no Activate action, so on KDE a
left-click appeared completely dead while only right-click surfaced the menu.

Bind the Linux tray OnClick handler to ShowWindow(). OpenMenu() is not an
option on Linux: Wails v3 leaves linuxSystemTray.openMenu unimplemented (it
only logs), so left-click→OpenMenu would still do nothing on KDE. ShowWindow()
is the same call Windows already runs from its double-click handler, and it
does not reproduce the macOS OpenMenu freeze (c77e5cef8) — that came from
NSStatusItem's blocking embedded menu loop, whereas Show/Focus return
immediately.

Split the Linux click handler into its own tray_click_linux.go and narrow the
macOS no-op bindTrayClick build tag accordingly. The context menu stays on
right-click on every host. On hosts that already open the menu on left-click
natively (GNOME Shell + AppIndicator) left-click now opens the window instead;
the menu remains on right-click.
2026-06-01 22:04:49 +02:00
Zoltán Papp
072d789463 client/ui: retry XEmbed-tray probe before claiming SNI watcher
The XEmbed tray (panel) can come up after the autostarted UI on minimal
WMs, so the single startup probe added in #6320 could miss a tray that
appears a second or two later, leaving the icon silently absent. Re-probe
for a ~10s grace period in a goroutine, claiming the watcher as soon as a
tray shows up; back off cleanly if none ever appears (headless/Wayland).
2026-06-01 21:55:04 +02:00
Pascal Fischer
8d05fe07bf Fix watcher registration on wayland (#6320)
* Fix hover label on linux

* Fix watcher registration on wayland
2026-06-01 21:48:39 +02:00
Zoltán Papp
61da51ed2e client/peer: don't fan out unchanged management/signal state
MarkManagement{Connected,Disconnected} and MarkSignal{Connected,
Disconnected} fired notifyStateChange unconditionally. The connect
goroutine re-marks the same state on every health-check cycle, so a
steady "connected -> connected" re-mark pushed a full SubscribeStatus
snapshot to every consumer each time — flooding the desktop UI (and its
tray) with identical Connected snapshots.

Guard each with an early return when neither the state nor the error
actually changed, so only real transitions wake SubscribeStatus
subscribers. The notifier already deduplicates, so collapsing both calls
under one guard is safe.
2026-06-01 21:11:32 +02:00
Zoltán Papp
60c86c63aa client/server: throttle and single-flight health probes
Status(GetFullPeerStatus=true) RPCs trigger a full health probe
(network round-trips to management, signal and the relays). The
desktop UI issues these frequently and concurrently, and a burst of
parallel Get() calls each fired its own probe — the lastProbe guard
was unprotected against concurrent access and only advanced when every
component was healthy, so a sustained unhealthy state (e.g. relay down)
disabled the throttle entirely and let every call re-probe.

Extract the throttle/single-flight policy into probeThrottle:
  - single-flight: only one probe runs at a time; concurrent callers
    that piled up while it ran share its result instead of each
    launching another, even when that probe failed.
  - throttle: lastOK only advances on a fully successful probe, so
    while anything is unhealthy callers keep probing frequently and
    notice recovery quickly (preserved from the original design).

RunHealthProbes now takes a context so a caller that gives up (e.g. a
Status RPC whose client disconnected) cancels the in-flight STUN/TURN
probe instead of letting it run to its per-component timeout. The
engine's own lifetime ctx still applies independently.
2026-06-01 21:07:12 +02:00
Zoltán Papp
4cee07bef5 client/ui: use monochrome tray icons on Linux
Linux now shows monochrome (black/white silhouette) tray icons instead
of the colored orange PNGs, matching the macOS template look. Since
Wails' Linux SNI backend ignores SetDarkModeIcon (its setDarkModeIcon
just calls setIcon, last-write-wins) and the SNI spec carries no panel
light/dark hint, the panel color scheme is detected in-process and the
black-vs-white silhouette is chosen in iconForState, pushed via a single
SetIcon.

Detection order (tray_theme_linux.go): freedesktop Settings portal
(org.freedesktop.appearance/color-scheme) -> GTK_THEME env (:dark
suffix) -> default dark. A SettingChanged subscription repaints live on
theme flips. macOS (template) and Windows (colored) paths are unchanged.

Icons are 48x48 mono PNGs (3% margin) generated from the macOS
silhouettes.
2026-06-01 20:23:46 +02:00
Zoltán Papp
5bebecc427 ui: disable WebKit sandbox when unprivileged userns are blocked
WebKitGTK crashes at startup when its bubblewrap sandbox can't create an
unprivileged user namespace (bwrap: setting up uid map: Permission denied
-> Failed to fully launch dbus-proxy -> panic in webkit_web_view_load_uri).
This happens in containers/VMs and on Ubuntu 24.04+ where AppArmor
restricts unprivileged user namespaces. Detect that the kernel blocks
userns via procfs and set WEBKIT_DISABLE_SANDBOX_THIS_IS_DANGEROUS so the
UI stays usable; honor an explicit user override either way.
2026-06-01 20:06:44 +02:00
Zoltán Papp
3dbd96b172 Add Version service exposing GUI version to frontend 2026-06-01 19:23:41 +02:00
Eduard Gert
6fe35cae83 Merge remote-tracking branch 'origin/ui-refactor' into ui-refactor 2026-06-01 17:50:38 +02:00
Eduard Gert
88bd1f91a8 update session expire dialog to account for hours, days etc. 2026-06-01 17:50:21 +02:00
Eduard Gert
acfd680560 remove icons from profile dropdown 2026-06-01 17:37:05 +02:00
Pascal Fischer
0b8aae4566 Fix hover label on linux (#6318) 2026-06-01 17:36:39 +02:00
Eduard Gert
daf9a74d8f update dialog error for connection switch 2026-06-01 17:18:31 +02:00
Eduard Gert
8af90e40d5 Merge remote-tracking branch 'origin/ui-refactor' into ui-refactor
# Conflicts:
#	client/ui/frontend/src/contexts/SettingsContext.tsx
2026-06-01 17:15:38 +02:00
Eduard Gert
3f989f69cb update about gui version, add mock data, truncate some strings 2026-06-01 17:14:30 +02:00
Zoltan Papp
53d43980ad fix(ui): keep main window closable after a native dialog on Windows (#6319)
A native Windows MessageBox attached to a parent window disables that
window (WS_DISABLED) for its lifetime and re-enables it on dismissal.
When the parent is the main window — whose WindowClosing hook hides
instead of closes — the enable/hide sequence races and leaves the window
unable to process its close (X) button afterwards, so e.g. a rejected
login error dialog left the main window stuck open.

Route all native dialogs through src/lib/dialogs.ts, which forces
Detached: true on Windows (NULL owner, no window ever disabled) and is a
no-op on macOS/Linux (keeps the attached sheet-style presentation).
2026-06-01 17:03:53 +02:00
Zoltán Papp
49df24b18c escape ampersand in tray menu labels on Windows
Win32 swallows a lone & in an MFT_STRING menu item as the mnemonic
prefix, so "Help & Support" rendered as "Help  Support". Add a
build-tagged menuLabel() helper that doubles & to && on Windows and is
the identity on macOS/Linux (which render & literally), and apply it to
the About submenu label.
2026-06-01 16:44:18 +02:00
Zoltán Papp
c5611dd766 open main window on tray double-click on Windows
Wire the Windows systray's double-click to ShowWindow(), matching the
Windows-native convention for tray apps. The Wails v3 systray dispatches
WM_LBUTTONDBLCLK to the doubleClickHandler, so OnDoubleClick fires; left-
and right-click continue to open the menu. macOS/Linux are unchanged.
2026-06-01 16:41:44 +02:00
Eduard Gert
a4ad93008b add skeleton to launch netbird ui at login and own context 2026-06-01 14:57:31 +02:00
Eduard Gert
101e04f9fb update wails alpha from .95 to .97 2026-06-01 11:19:02 +02:00
Eduard Gert
710d5c6182 apply different window width on windows 2026-06-01 11:07:33 +02:00
Eduard Gert
7538a9a133 update window width and dialogs top padding on macos 2026-06-01 09:51:59 +02:00
Zoltan Papp
5f7657b95e Merge branch 'main' into ui-refactor 2026-05-31 12:33:50 +02:00
Zoltan Papp
27873866c2 Revert needs login icon 2026-05-31 04:34:17 +02:00
Zoltan Papp
18348e1491 client+ui: remove SSO handoff flicker and clean up abandoned login via context
Two follow-ups to the "hold NeedsLogin during the SSO browser wait" change.
Both target the visible state churn the tray showed during the auto-login
handoff (Connect / profile-switch lands on NeedsLogin -> the UI's startLogin
kicks off the SSO flow) and the broken recovery after the user dismisses the
browser-login popup with the window's X.

Background
----------
When a connect attempt lands on NeedsLogin, the UI's startLogin() drives the
SSO flow: Connection.Login() -> (NeedsSSOLogin) open the browser-login popup
-> Connection.WaitSSOLogin() blocks until the browser leg completes. The tray
and the React status page both paint the raw daemon status, so any transient
state the daemon publishes during this handoff is visible as a flicker.

Previously the handoff churned the daemon status through
  NeedsLogin -> Idle -> Connecting -> NeedsLogin
which read as a flicker on the tray icon and the status dot. Two distinct
sources produced the two intermediate states:

  * Idle       came from the UI's defensive cli.Down() at the top of
                Connection.Login (services/connection.go): it tore the engine
                down before every login to dislodge a possibly-parked
                WaitSSOLogin, emitting a StatusIdle on the way.
  * Connecting  came from server.go Login() unconditionally setting
                StatusConnecting before deciding whether the request is an
                SSO flow (which immediately returns NeedsLogin) or a
                setup-key flow (which actually dials Management).

Changes
-------
1. server.go Login(): only set StatusConnecting on the setup-key path, where
   we are about to dial Management with the key and the Connecting paint is
   meaningful. The SSO path returns NeedsLogin and parks on the browser leg,
   so it no longer flashes Connecting first. Removes the Connecting blip.

2. services/connection.go Login(): drop the pre-Login cli.Down(). The daemon
   already dislodges a pending WaitSSOLogin at Login entry (actCancel), and an
   abandoned browser leg is now torn down by cancelling the WaitSSOLogin RPC
   (see 3/4). Removing the Down removes the Idle blip on every login.

3. MainConnectionStatusSwitch.tsx startLogin(): on cancel (the browser-login
   popup's Cancel button or its window X, both routed through
   EventBrowserLoginCancel), cancel the in-flight WaitSSOLogin gRPC call via
   waitPromise.cancel() instead of issuing a heavy Connection.Down(). The
   daemon ties the wait to this call's context, so cancelling the call ends
   the wait cleanly with no engine teardown and no Idle paint.

4. server.go WaitSSOLogin(): when the wait unblocks with context.Canceled and
   the cancellation came from our caller (callerCtx.Err() != nil — the client
   cancelled the RPC or went away), clear the cached oauthAuthFlow so a fresh
   Login starts a new device code instead of reusing the abandoned one. The
   entry NeedsLogin stays in place, so a reattaching client still shows the
   login affordance. An internal abort (actCancel fired by a newer
   Login/WaitSSOLogin while our callerCtx is still live) is left untouched so
   the new owner's flow is not clobbered.

Effect
------
The auto-login handoff now goes Connected -> Connecting -> NeedsLogin and
holds, with no Idle/Connecting flicker in between. Dismissing the browser-login
popup with X now recovers the same way as the Cancel button: the WaitSSOLogin
RPC is cancelled, the stale OAuth flow is cleared, and the next connect opens a
fresh browser-login window instead of getting stuck.
2026-05-31 04:26:15 +02:00
Zoltan Papp
9569ac2081 internal: add temporary debug log for state.Set value and caller 2026-05-31 03:45:35 +02:00
Zoltan Papp
0b484133b2 client: hold NeedsLogin during SSO browser wait and tie it to the caller
WaitSSOLogin set StatusConnecting on entry and ran the browser wait on
rootCtx. If the client that drove the login went away mid-wait (UI restart,
CLI Ctrl+C), the wait orphaned on rootCtx until the OAuth device-code window
expired, and the daemon stayed stuck reporting Connecting — a reattaching
client saw a spinner that never resolved instead of a login prompt.

Hold StatusNeedsLogin for the whole browser wait (also in the Login
cached-flow path) so any client attaching mid-wait reads 'login required',
and bridge the wait to callerCtx so a departing client cancels it. On that
cancel the defer leaves NeedsLogin in place, so the next client shows the
login affordance instead of a stale Connecting.
2026-05-31 03:45:13 +02:00
Zoltan Papp
5df570feb8 ui: localize tray status labels for connect/login states
StatusLabel only mapped Idle and DaemonUnavailable, so Connected,
Connecting, NeedsLogin, LoginFailed and SessionExpired leaked the raw
daemon enum into the tray menu — untranslated in de/hu. Map all five to
tray.status.* keys (added in en/de/hu); keep the raw-enum default as a
fallback for any future status.
2026-05-31 03:15:34 +02:00
Zoltan Papp
ed4d823755 ui: auto-trigger browser login when profile switch lands on NeedsLogin
Add a second, longer-lived switchLoginWatch flag alongside switchInProgress
in DaemonFeed. Suppression still clears on the first Connecting push from the
new Up, but the login watcher survives past it to catch the eventual
NeedsLogin / LoginFailed / SessionExpired terminal and emit EventTriggerLogin,
so the React orchestrator opens the browser-login flow without a second
Connect click. shouldSuppress becomes consumeForSwitch, returning both the
suppress and triggerLogin signals. CancelProfileSwitch disarms the watch so
an aborted switch does not pop a login window.
2026-05-31 03:15:25 +02:00
Zoltan Papp
cedfa2ebf7 peer: add temporary debug log for notifyStateChange caller 2026-05-31 02:59:14 +02:00
Zoltan Papp
8b03c96851 ui: add launch-at-login (autostart) toggle for the UI
Add an Autostart Wails service wrapping app.Autostart and a toggle in
the General settings tab. The OS login-item registration is the single
source of truth (nothing mirrored to the preferences file). Affects the
graphical UI only, not the daemon. The toggle hides itself on platforms
where autostart is unsupported.
2026-05-31 02:01:02 +02:00
Zoltan Papp
b830a45333 ui: request macOS notification authorization on startup
Without an explicit authorization request the macOS notification center
keeps the app at .notDetermined and silently drops every toast. Request
it from the ApplicationStarted hook (after the notifier's Startup has
initialised the delegate), off the main goroutine since the call blocks
until the user responds. Linux/Windows notifier stubs report authorized,
so this is a no-op there.
2026-05-31 01:34:56 +02:00
Eduard Gert
b0d8ac6489 fix auto size detection 2026-05-29 17:21:45 +02:00
Eduard Gert
558769e671 localize window titles, fix size for windows (and other platforms?) 2026-05-29 16:58:08 +02:00
Pascal Fischer
9189625487 [management] enrich context in permissions manager (#6286) 2026-05-29 16:36:38 +02:00
Bethuel Mmbaga
e9dbf9db6f [management] Extend combined server initialization (#6156) 2026-05-29 17:35:35 +03:00
Zoltán Papp
fb6138a3ba tray: open menu on left-click on Windows
Wails v3 does not auto-show the tray menu on left-click on Windows — its
default left-click handler only logs and does nothing visible, so only
right-click opened the menu. macOS (NSStatusItem) and Linux
(StatusNotifierItem host) give us click→menu natively.

Add a build-tag-split bindTrayClick: the Windows variant wires
OnClick→OpenMenu (the same menu.ShowAt path right-click uses), while the
macOS/Linux variant stays a no-op — binding OnClick→OpenMenu on macOS
freezes the tray via NSStatusItem's blocking mouseDown on the main GCD
queue (the reason commit c77e5cef8 reverted the earlier wiring).
2026-05-29 16:02:41 +02:00
Zoltán Papp
b111c38b7c Merge remote-tracking branch 'origin/ui-refactor' into ui-refactor 2026-05-29 15:54:57 +02:00
Zoltán Papp
f54121ebfa Merge branch 'main' into ui-refactor
# Conflicts:
#	.github/workflows/golang-test-darwin.yml
#	.github/workflows/release.yml
2026-05-29 15:51:43 +02:00
Eduard Gert
122d172f33 Merge remote-tracking branch 'origin/ui-refactor' into ui-refactor 2026-05-29 15:40:41 +02:00
Eduard Gert
0b19a99693 update ui for win11 2026-05-29 15:40:34 +02:00
Zoltán Papp
0309f992ad ssh/server: keep testing dep out of the wasm build
test.go is a non-_test.go file so its exported StartTestServer helper is
visible to the ssh/proxy and ssh/client external test packages. That drags
the testing/flag/regexp chain into every build that links ssh/server,
including the wasm client (via the engine). Gate the file with //go:build
!js: native test packages still see the helper, wasm drops the dependency.
2026-05-29 15:37:04 +02:00
Zoltán Papp
1ad2d90d3b client: keep sessionwatch out of the wasm build
The wasm client never runs the engine's session-warning flow, so linking
the full sessionwatch package (timers, event composition) only bloats the
binary. Put the watcher behind a sessionDeadlineWatcher interface and split
the constructor by build tag: !js wires the real sessionwatch.Watcher, js
gets a no-op stub that still mirrors the deadline into the status recorder
(so the Status snapshot stays correct) but drops the timers. Removes the
sessionwatch package from the wasm dependency graph.
2026-05-29 15:26:21 +02:00
Theodor Midtlien
5a9e9e7bc9 [Infrastructure] Pin actions with SHA and improve workflows (#6249)
* Pin actions with SHA, replace unmaintained, add dependabot for actions

* Update FreeBSD to version 15 for tests

* Use shared actions

* Update sign-pipelines version
2026-05-29 15:24:30 +02:00
Viktor Liu
43e041cf9f [client] Apply netroute unspecified-destination workaround on android (#6192) 2026-05-29 15:15:22 +02:00
Viktor Liu
77e5693200 [client] Recognize NetBird DNS forwarder port in capture text format (#6177) 2026-05-29 15:14:32 +02:00
Zoltán Papp
93a1547871 ci: skip non-English UI locales in codespell
de/hu translations contain real foreign words (Sie, oder, ist) that
codespell flags as misspellings. Only en/common.json is the spell-check
source of truth; add each new locale dir to the skip list as languages land.
2026-05-29 15:07:53 +02:00
Zoltán Papp
04ab9b5bad ui: split main() into focused setup helpers
Extract parseFlagsAndInitLog, newApplication, buildI18n, registerServices,
and newMainWindow so main() stays under the 100-line limit. Wiring order and
shared service instances are unchanged.
2026-05-29 15:02:44 +02:00
Zoltán Papp
61431801ea ui: extract subscribeAndStreamEvents to cut toastStreamLoop complexity
Move the event backoff op body into subscribeAndStreamEvents and the
per-event fan-out into dispatchSystemEvent, bringing toastStreamLoop under
the 20 cognitive-complexity limit. No behavior change.
2026-05-29 14:58:17 +02:00
Zoltán Papp
02e3cb9987 ui: document why startStatusNotifierWatcher is empty on non-Linux
Explain that macOS/Windows have a native tray and the SNI+XEmbed bridge is
Linux-WM-only, so the body is intentionally empty to let main.go call it
unconditionally across all build targets.
2026-05-29 14:56:49 +02:00