mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-30 02:29:08 +02:00
Add prototype UI clients
This commit is contained in:
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
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user