Add prototype UI clients

This commit is contained in:
pascal
2025-11-01 12:17:02 +01:00
parent 96f71ff1e1
commit 76b1003810
180 changed files with 44281 additions and 18 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

+58 -16
View File
@@ -77,6 +77,11 @@ func main() {
a := app.NewWithID("NetBird")
a.SetIcon(fyne.NewStaticResource("netbird", iconDisconnected))
// Apply custom dark mode theme with icy blue colors if flag is set
if flags.useDarkMode {
a.Settings().SetTheme(&GlassTheme{})
}
// Show error message window if needed.
if flags.errorMsg != "" {
showErrorMessage(flags.errorMsg)
@@ -85,14 +90,15 @@ func main() {
// Create the service client (this also builds the settings or networks UI if requested).
client := newServiceClient(&newServiceClientArgs{
addr: flags.daemonAddr,
logFile: logFile,
app: a,
showSettings: flags.showSettings,
showNetworks: flags.showNetworks,
showLoginURL: flags.showLoginURL,
showDebug: flags.showDebug,
showProfiles: flags.showProfiles,
addr: flags.daemonAddr,
logFile: logFile,
app: a,
showSettings: flags.showSettings,
showNetworks: flags.showNetworks,
showLoginURL: flags.showLoginURL,
showDebug: flags.showDebug,
showProfiles: flags.showProfiles,
useDarkMode: flags.useDarkMode,
})
// Watch for theme/settings changes to update the icon.
@@ -128,6 +134,7 @@ type cliFlags struct {
showLoginURL bool
errorMsg string
saveLogsInFile bool
useDarkMode bool
}
// parseFlags reads and returns all needed command-line flags.
@@ -146,6 +153,7 @@ func parseFlags() *cliFlags {
flag.StringVar(&flags.errorMsg, "error-msg", "", "displays an error message window")
flag.BoolVar(&flags.saveLogsInFile, "use-log-file", false, fmt.Sprintf("save logs in a file: %s/netbird-ui-PID.log", os.TempDir()))
flag.BoolVar(&flags.showLoginURL, "login-url", false, "show login URL in a popup window")
flag.BoolVar(&flags.useDarkMode, "dark-mode", false, "use icy blue dark mode theme with white monochrome icons")
flag.Parse()
return &flags
}
@@ -195,6 +203,24 @@ var iconConnectingMacOS []byte
//go:embed assets/netbird-systemtray-error-macos.png
var iconErrorMacOS []byte
//go:embed assets/netbird-systemtray-connected-white-monochrome.png
var iconConnectedWhiteMonochrome []byte
//go:embed assets/netbird-systemtray-disconnected-white-monochrome.png
var iconDisconnectedWhiteMonochrome []byte
//go:embed assets/netbird-systemtray-update-disconnected-white-monochrome.png
var iconUpdateDisconnectedWhiteMonochrome []byte
//go:embed assets/netbird-systemtray-update-connected-white-monochrome.png
var iconUpdateConnectedWhiteMonochrome []byte
//go:embed assets/netbird-systemtray-connecting-white-monochrome.png
var iconConnectingWhiteMonochrome []byte
//go:embed assets/netbird-systemtray-error-white-monochrome.png
var iconErrorWhiteMonochrome []byte
//go:embed assets/connected.png
var iconConnectedDot []byte
@@ -285,6 +311,7 @@ type serviceClient struct {
updateIndicationLock sync.Mutex
isUpdateIconActive bool
showNetworks bool
useDarkMode bool
wNetworks fyne.Window
wProfiles fyne.Window
@@ -304,14 +331,15 @@ type menuHandler struct {
}
type newServiceClientArgs struct {
addr string
logFile string
app fyne.App
showSettings bool
showNetworks bool
showDebug bool
showLoginURL bool
showProfiles bool
addr string
logFile string
app fyne.App
showSettings bool
showNetworks bool
showDebug bool
showLoginURL bool
showProfiles bool
useDarkMode bool
}
// newServiceClient instance constructor
@@ -329,6 +357,7 @@ func newServiceClient(args *newServiceClientArgs) *serviceClient {
showAdvancedSettings: args.showSettings,
showNetworks: args.showNetworks,
useDarkMode: args.useDarkMode,
update: version.NewUpdate("nb/client-ui"),
}
@@ -356,6 +385,19 @@ func (s *serviceClient) setNewIcons() {
s.icAbout = iconAbout
s.icConnectedDot = iconConnectedDot
s.icDisconnectedDot = iconDisconnectedDot
// If dark mode flag is set, use white monochrome icons everywhere
if s.useDarkMode {
s.icConnected = iconConnectedWhiteMonochrome
s.icDisconnected = iconDisconnectedWhiteMonochrome
s.icUpdateConnected = iconUpdateConnectedWhiteMonochrome
s.icUpdateDisconnected = iconUpdateDisconnectedWhiteMonochrome
s.icConnecting = iconConnectingWhiteMonochrome
s.icError = iconErrorWhiteMonochrome
return
}
// Otherwise use colored icons based on theme
if s.app.Settings().ThemeVariant() == theme.VariantDark {
s.icConnected = iconConnectedDark
s.icDisconnected = iconDisconnected
+12 -2
View File
@@ -207,10 +207,20 @@ func (h *eventHandler) runSelfCommand(ctx context.Context, command, arg string)
return
}
cmd := exec.CommandContext(ctx, proc,
args := []string{
fmt.Sprintf("--%s=%s", command, arg),
fmt.Sprintf("--daemon-addr=%s", h.client.addr),
)
}
// Pass dark mode flag if it's enabled
if h.client.useDarkMode {
args = append(args, "--dark-mode")
log.Debugf("Spawning %s with dark mode enabled", command)
} else {
log.Debugf("Spawning %s without dark mode (useDarkMode=%v)", command, h.client.useDarkMode)
}
cmd := exec.CommandContext(ctx, proc, args...)
if out := h.client.attachOutput(cmd); out != nil {
defer func() {
+146
View File
@@ -0,0 +1,146 @@
package main
import (
"image/color"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/theme"
)
// GlassTheme implements a custom Fyne theme with icy blue glass aesthetic
type GlassTheme struct{}
var _ fyne.Theme = (*GlassTheme)(nil)
// Modern icy blue color palette with better contrast
var (
icyBlue = color.NRGBA{R: 163, G: 215, B: 229, A: 255} // #a3d7e5
icyBlueDark = color.NRGBA{R: 140, G: 200, B: 215, A: 255}
icyBlueLight = color.NRGBA{R: 200, G: 235, B: 245, A: 255}
icyBlueAlpha = color.NRGBA{R: 163, G: 215, B: 229, A: 77} // 0.3 opacity
// Darker, more sophisticated backgrounds
darkBg = color.NRGBA{R: 18, G: 18, B: 24, A: 255} // Solid for modern look
darkBgLight = color.NRGBA{R: 24, G: 24, B: 30, A: 255}
darkBgCard = color.NRGBA{R: 28, G: 28, B: 35, A: 255}
darkView = color.NRGBA{R: 16, G: 16, B: 20, A: 255}
textLight = color.NRGBA{R: 248, G: 248, B: 252, A: 255}
textMuted = color.NRGBA{R: 160, G: 160, B: 170, A: 255}
textDark = color.NRGBA{R: 10, G: 10, B: 15, A: 255}
borderColor = color.NRGBA{R: 163, G: 215, B: 229, A: 20} // Subtle icy blue border
errorRed = color.NRGBA{R: 239, G: 68, B: 68, A: 255} // Modern vibrant red
warningYellow = color.NRGBA{R: 251, G: 191, B: 36, A: 255} // Modern vibrant yellow
successGreen = color.NRGBA{R: 34, G: 197, B: 94, A: 255} // Modern vibrant green
)
func (g *GlassTheme) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color {
// We only support dark variant for the glass theme
switch name {
// Primary colors
case theme.ColorNamePrimary:
return icyBlue
// Background colors
case theme.ColorNameBackground:
return darkBg
case theme.ColorNameOverlayBackground:
return darkBgCard
case theme.ColorNameMenuBackground:
return darkBgCard
case theme.ColorNameInputBackground:
return darkView
// Foreground/text colors
case theme.ColorNameForeground:
return textLight
case theme.ColorNamePlaceHolder:
return textMuted
case theme.ColorNameDisabled:
return textMuted
// Button colors - more vibrant
case theme.ColorNameButton:
return icyBlue
case theme.ColorNameHover:
return icyBlueLight
case theme.ColorNamePressed:
return icyBlueDark
case theme.ColorNameFocus:
return icyBlue
// Selection colors
case theme.ColorNameSelection:
return icyBlueAlpha
// Border/separator colors
case theme.ColorNameSeparator:
return borderColor
case theme.ColorNameInputBorder:
return borderColor
// Scrollbar
case theme.ColorNameScrollBar:
return color.NRGBA{R: 163, G: 215, B: 229, A: 51} // 0.2 opacity
// Shadow (subtle for glass effect)
case theme.ColorNameShadow:
return color.NRGBA{R: 0, G: 0, B: 0, A: 51} // 0.2 opacity
// Header/toolbar
case theme.ColorNameHeaderBackground:
return darkBgLight
// Status colors
case theme.ColorNameError:
return errorRed
case theme.ColorNameWarning:
return warningYellow
case theme.ColorNameSuccess:
return successGreen
// Hyperlinks
case theme.ColorNameHyperlink:
return icyBlueLight
default:
// Fallback to default dark theme
return theme.DefaultTheme().Color(name, theme.VariantDark)
}
}
func (g *GlassTheme) Font(style fyne.TextStyle) fyne.Resource {
// Use default Fyne fonts but we could customize here if needed
// Fyne uses Go's built-in fonts which are clean and modern
return theme.DefaultTheme().Font(style)
}
func (g *GlassTheme) Icon(name fyne.ThemeIconName) fyne.Resource {
return theme.DefaultTheme().Icon(name)
}
func (g *GlassTheme) Size(name fyne.ThemeSizeName) float32 {
switch name {
// Increase padding for modern spacing
case theme.SizeNamePadding:
return 8
case theme.SizeNameInlineIcon:
return 24
case theme.SizeNameScrollBar:
return 12
case theme.SizeNameScrollBarSmall:
return 6
case theme.SizeNameSeparatorThickness:
return 1
case theme.SizeNameInputBorder:
return 2
case theme.SizeNameInputRadius:
return 8 // More rounded corners
case theme.SizeNameSelectionRadius:
return 8
default:
return theme.DefaultTheme().Size(name)
}
}