update ui for win11

This commit is contained in:
Eduard Gert
2026-05-29 15:40:34 +02:00
parent d0d7252c24
commit 0b19a99693
7 changed files with 120 additions and 133 deletions

View File

@@ -11,7 +11,7 @@ const Root = forwardRef<
<Tabs.Root
ref={ref}
orientation={"vertical"}
className={cn("flex flex-1 min-h-0 gap-4", className)}
className={cn("flex flex-1 min-h-0", className)}
{...props}
/>
);
@@ -22,7 +22,7 @@ const List = forwardRef<HTMLDivElement, Tabs.TabsListProps>(
return (
<Tabs.List
ref={ref}
className={cn("w-full flex flex-col gap-1", className)}
className={cn("w-full flex flex-col gap-1 p-4 pr-0", className)}
{...props}
/>
);

View File

@@ -20,7 +20,7 @@ export const AppRightPanel = ({ children, overlay, overlayOpen = false }: Props)
return (
<div
className={cn(
"wails-no-draggable relative",
"wails-no-draggable relative m-4",
"bg-nb-gray-940 border border-nb-gray-920",
"flex-1 min-h-0 min-w-0 flex flex-col rounded-xl rounded-br-2xl overflow-hidden",
)}

View File

@@ -143,10 +143,10 @@ export const MainHeader = () => {
<div
className={cn(
"shrink-0 cursor-default wails-draggable relative",
"flex items-center h-12 px-3 top-2.5",
"flex items-center h-12 top-2.5",
)}
>
<div className={"grid grid-cols-3 items-center w-[356px] shrink-0"}>
<div className={"grid grid-cols-3 items-center w-[364px] shrink-0"}>
<div />
<div className={"flex justify-center ml-4"}>{profileSlot}</div>
<div />

View File

@@ -35,9 +35,9 @@ const MainBody = () => {
const isAdvanced = viewMode === "advanced";
return (
<div className={"wails-draggable flex flex-1 min-h-0 p-4 gap-4"}>
<div className={"wails-draggable flex flex-1 min-h-0 gap-4r"}>
<div
className={"flex flex-col items-center shrink-0 w-[348px]"}
className={"flex flex-col items-center shrink-0 w-[364px]"}
>
<MainConnectionStatusSwitch />
</div>

View File

@@ -3,6 +3,7 @@ import { useLocation } from "react-router-dom";
import { Events } from "@wailsio/runtime";
import * as ScrollArea from "@radix-ui/react-scroll-area";
import { cn } from "@/lib/cn";
import { isMacOS } from "@/lib/platform";
import { AppRightPanel } from "@/layouts/AppRightPanel.tsx";
import { VerticalTabs } from "@/components/VerticalTabs.tsx";
import { SettingsNavigation } from "@/modules/settings/SettingsNavigation.tsx";
@@ -50,15 +51,18 @@ export const SettingsPage = () => {
return (
<>
<div
className={
"wails-draggable cursor-default select-none h-12 shrink-0"
}
/>
{isMacOS() ? (
<div
className={
"wails-draggable cursor-default select-none h-12 shrink-0"
}
/>
) : (
<div className={"h-px shrink-0 bg-nb-gray-920/0"} />
)}
<VerticalTabs
value={active}
onValueChange={setActive}
className={"p-4"}
>
<SettingsNavigation />
<AppRightPanel>

View File

@@ -192,19 +192,13 @@ func main() {
Width: initialWidth,
Height: 640,
Hidden: true,
BackgroundColour: application.NewRGB(24, 26, 29),
BackgroundColour: services.WindowBackgroundColour,
URL: "/",
DisableResize: true,
MinimiseButtonState: application.ButtonHidden,
MaximiseButtonState: application.ButtonHidden,
Mac: application.MacWindow{
InvisibleTitleBarHeight: 38,
Backdrop: application.MacBackdropNormal,
TitleBar: application.MacTitleBarHiddenInset,
CollectionBehavior: application.MacWindowCollectionBehaviorFullScreenNone,
},
Windows: application.WindowsWindow{
Theme: application.Dark,
},
Mac: services.AppleMacOSAppearanceOptions(),
Windows: services.MicrosoftWindowsAppearanceOptions(),
Linux: application.LinuxWindow{
Icon: iconWindow,
},

View File

@@ -28,6 +28,81 @@ const EventBrowserLoginCancel = "browser-login:cancel"
// flashed the SettingsSkeleton between opens.
const EventSettingsOpen = "netbird:settings:open"
// WindowBackgroundColour is the shared in-window background for every
// NetBird webview (matches the bg-nb-gray utility in the Tailwind config
// at #181A1D / nb-gray-950, used by AppLayout's <html> background).
var WindowBackgroundColour = application.NewRGB(24, 26, 29)
// Wails reads CustomTheme colours as 0x00BBGGRR (RGB byte order reversed).
// Border + title bar match AppRightPanel's bg-nb-gray-940 (#1C1E21);
// title text matches text-nb-gray-100 (#E4E7E9). u32ptr exists only
// because WindowTheme fields are *uint32 and Go has no literal address-of.
func u32ptr(v uint32) *uint32 { return &v }
var microsoftWindowsTheme = &application.WindowTheme{
BorderColour: u32ptr(0x00211E1C),
TitleBarColour: u32ptr(0x00211E1C),
TitleTextColour: u32ptr(0x00E9E7E4),
}
// MicrosoftWindowsAppearanceOptions is the per-window Microsoft Windows OS
// chrome shared by every NetBird webview window. Mica backdrop (no-op on
// pre-22621), dark theme, custom title bar/border colours so the chrome
// reads as an extension of the in-window AppRightPanel.
func MicrosoftWindowsAppearanceOptions() application.WindowsWindow {
return application.WindowsWindow{
BackdropType: application.Mica,
Theme: application.Dark,
CustomTheme: application.ThemeSettings{
DarkModeActive: microsoftWindowsTheme,
DarkModeInactive: microsoftWindowsTheme,
LightModeActive: microsoftWindowsTheme,
LightModeInactive: microsoftWindowsTheme,
},
}
}
// AppleMacOSAppearanceOptions is the per-window macOS chrome shared by
// every NetBird webview window. The hidden title bar inset clears space
// for the traffic-light buttons; the FullScreenNone collection behavior
// keeps the green button from offering a full-screen mode that breaks
// our fixed-size layouts.
func AppleMacOSAppearanceOptions() application.MacWindow {
return application.MacWindow{
InvisibleTitleBarHeight: 38,
Backdrop: application.MacBackdropNormal,
TitleBar: application.MacTitleBarHiddenInset,
CollectionBehavior: application.MacWindowCollectionBehaviorFullScreenNone,
}
}
// DialogWindowOptions is the baseline for every auxiliary dialog window
// (BrowserLogin, SessionExpired, SessionAboutToExpire, InstallProgress).
// All four share size (360x320), the no-resize / no-min / no-max chrome,
// Hidden-on-create (so the React side can auto-size before first paint),
// AlwaysOnTop (the dialogs interrupt the user, the SSO popup overrides
// this), and the shared background/Mac/Windows appearance. Callers fill
// in per-dialog overrides (URL params, screen targeting, etc.) on the
// returned value before passing it to Window.NewWithOptions.
func DialogWindowOptions(name, title, url string) application.WebviewWindowOptions {
return application.WebviewWindowOptions{
Name: name,
Title: title,
Width: 360,
Height: 320,
DisableResize: true,
AlwaysOnTop: true,
Hidden: true,
MinimiseButtonState: application.ButtonHidden,
MaximiseButtonState: application.ButtonHidden,
CloseButtonState: application.ButtonEnabled,
BackgroundColour: WindowBackgroundColour,
URL: url,
Mac: AppleMacOSAppearanceOptions(),
Windows: MicrosoftWindowsAppearanceOptions(),
}
}
// WindowManager opens auxiliary application windows on demand from the
// frontend. The main window is created up-front in main.go; this service is
// for secondary surfaces (Settings, BrowserLogin, Session*, InstallProgress).
@@ -68,7 +143,7 @@ func NewWindowManager(app *application.App, mainWindow *application.WebviewWindo
s := &WindowManager{app: app, mainWindow: mainWindow}
s.settings = app.Window.NewWithOptions(application.WebviewWindowOptions{
Name: "settings",
Title: "NetBird Settings",
Title: "Settings",
Width: 900,
Height: 640,
Hidden: true,
@@ -76,17 +151,10 @@ func NewWindowManager(app *application.App, mainWindow *application.WebviewWindo
MinimiseButtonState: application.ButtonHidden,
MaximiseButtonState: application.ButtonHidden,
CloseButtonState: application.ButtonEnabled,
BackgroundColour: application.NewRGB(24, 26, 29),
BackgroundColour: WindowBackgroundColour,
URL: "/#/settings",
Mac: application.MacWindow{
InvisibleTitleBarHeight: 38,
Backdrop: application.MacBackdropNormal,
TitleBar: application.MacTitleBarHiddenInset,
CollectionBehavior: application.MacWindowCollectionBehaviorFullScreenNone,
},
Windows: application.WindowsWindow{
Theme: application.Dark,
},
Mac: AppleMacOSAppearanceOptions(),
Windows: MicrosoftWindowsAppearanceOptions(),
})
// Hide on close instead of destroying — preserves in-window React state
// across reopens. Mirrors the main window's close behaviour. Resetting
@@ -143,36 +211,17 @@ func (s *WindowManager) OpenBrowserLogin(uri string) {
screen = sc
}
}
s.browserLogin = s.app.Window.NewWithOptions(application.WebviewWindowOptions{
Name: "browser-login",
Title: "NetBird Sign-in",
Width: 360,
Height: 320,
DisableResize: true,
// Hidden so the React side can measure its content via
// useAutoSizeWindow and call Window.SetSize + Show before the
// user sees the placeholder snapping to the measured height,
// matching the Session* windows.
Hidden: true,
// WindowCentered + Screen centers on the chosen display's
// WorkArea (see WebviewWindowOptions.Screen docs).
InitialPosition: application.WindowCentered,
Screen: screen,
MinimiseButtonState: application.ButtonHidden,
MaximiseButtonState: application.ButtonHidden,
CloseButtonState: application.ButtonEnabled,
BackgroundColour: application.NewRGB(24, 26, 29),
URL: startURL,
Mac: application.MacWindow{
InvisibleTitleBarHeight: 38,
Backdrop: application.MacBackdropTranslucent,
TitleBar: application.MacTitleBarHiddenInset,
CollectionBehavior: application.MacWindowCollectionBehaviorFullScreenNone,
},
Windows: application.WindowsWindow{
Theme: application.Dark,
},
})
opts := DialogWindowOptions("browser-login", "NetBird Sign-in", startURL)
// SSO popup deliberately is NOT always-on-top — the user moves
// between the browser tab and our popup; pinning it would obscure
// the browser at the moment they need to interact with it.
opts.AlwaysOnTop = false
// WindowCentered + Screen centers on the chosen display's
// WorkArea (see WebviewWindowOptions.Screen docs) so the popup
// follows the user onto the screen they're already looking at.
opts.InitialPosition = application.WindowCentered
opts.Screen = screen
s.browserLogin = s.app.Window.NewWithOptions(opts)
bl := s.browserLogin
// User-initiated close (red X) means cancel. Emit the event so
// startLogin() can tear the SSO wait down, then let the window
@@ -271,29 +320,9 @@ func (s *WindowManager) OpenSessionExpired() {
s.mu.Lock()
defer s.mu.Unlock()
if s.sessionExpired == nil {
s.sessionExpired = s.app.Window.NewWithOptions(application.WebviewWindowOptions{
Name: "session-expired",
Title: "NetBird",
Width: 360,
Height: 320,
DisableResize: true,
AlwaysOnTop: true,
Hidden: true,
MinimiseButtonState: application.ButtonHidden,
MaximiseButtonState: application.ButtonHidden,
CloseButtonState: application.ButtonEnabled,
BackgroundColour: application.NewRGB(24, 26, 29),
URL: "/#/dialog/session-expired",
Mac: application.MacWindow{
InvisibleTitleBarHeight: 38,
Backdrop: application.MacBackdropTranslucent,
TitleBar: application.MacTitleBarHiddenInset,
CollectionBehavior: application.MacWindowCollectionBehaviorFullScreenNone,
},
Windows: application.WindowsWindow{
Theme: application.Dark,
},
})
s.sessionExpired = s.app.Window.NewWithOptions(
DialogWindowOptions("session-expired", "NetBird", "/#/dialog/session-expired"),
)
s.sessionExpired.OnWindowEvent(events.Common.WindowClosing, func(_ *application.WindowEvent) {
s.mu.Lock()
s.sessionExpired = nil
@@ -326,29 +355,9 @@ func (s *WindowManager) OpenSessionAboutToExpire(seconds int) {
defer s.mu.Unlock()
startURL := "/#/dialog/session-about-to-expire?seconds=" + strconv.Itoa(seconds)
if s.sessionAboutToExpire == nil {
s.sessionAboutToExpire = s.app.Window.NewWithOptions(application.WebviewWindowOptions{
Name: "session-about-to-expire",
Title: "NetBird",
Width: 360,
Height: 320,
DisableResize: true,
AlwaysOnTop: true,
Hidden: true,
MinimiseButtonState: application.ButtonHidden,
MaximiseButtonState: application.ButtonHidden,
CloseButtonState: application.ButtonEnabled,
BackgroundColour: application.NewRGB(24, 26, 29),
URL: startURL,
Mac: application.MacWindow{
InvisibleTitleBarHeight: 38,
Backdrop: application.MacBackdropTranslucent,
TitleBar: application.MacTitleBarHiddenInset,
CollectionBehavior: application.MacWindowCollectionBehaviorFullScreenNone,
},
Windows: application.WindowsWindow{
Theme: application.Dark,
},
})
s.sessionAboutToExpire = s.app.Window.NewWithOptions(
DialogWindowOptions("session-about-to-expire", "NetBird", startURL),
)
s.sessionAboutToExpire.OnWindowEvent(events.Common.WindowClosing, func(_ *application.WindowEvent) {
s.mu.Lock()
s.sessionAboutToExpire = nil
@@ -392,29 +401,9 @@ func (s *WindowManager) OpenInstallProgress(version string) {
}
if s.installProgress == nil {
s.hideOtherWindowsLocked("install-progress")
s.installProgress = s.app.Window.NewWithOptions(application.WebviewWindowOptions{
Name: "install-progress",
Title: "NetBird",
Width: 360,
Height: 320,
DisableResize: true,
AlwaysOnTop: true,
Hidden: true,
MinimiseButtonState: application.ButtonHidden,
MaximiseButtonState: application.ButtonHidden,
CloseButtonState: application.ButtonEnabled,
BackgroundColour: application.NewRGB(24, 26, 29),
URL: startURL,
Mac: application.MacWindow{
InvisibleTitleBarHeight: 38,
Backdrop: application.MacBackdropTranslucent,
TitleBar: application.MacTitleBarHiddenInset,
CollectionBehavior: application.MacWindowCollectionBehaviorFullScreenNone,
},
Windows: application.WindowsWindow{
Theme: application.Dark,
},
})
s.installProgress = s.app.Window.NewWithOptions(
DialogWindowOptions("install-progress", "NetBird", startURL),
)
s.installProgress.OnWindowEvent(events.Common.WindowClosing, func(_ *application.WindowEvent) {
s.mu.Lock()
s.installProgress = nil