package theme import ( "fmt" "os" "sync" "github.com/charmbracelet/lipgloss" ) // ColorScheme defines the color palette for the theme type ColorScheme struct { Primary lipgloss.Color Success lipgloss.Color Warning lipgloss.Color Error lipgloss.Color Muted lipgloss.Color Background lipgloss.Color } // Theme represents a color theme with its name and color scheme type Theme struct { Name string Scheme ColorScheme } // Global variables for current theme and styles var ( currentTheme *Theme once sync.Once // Global styles that can be used throughout the application StylePrimary lipgloss.Style StyleSuccess lipgloss.Style StyleWarning lipgloss.Style StyleError lipgloss.Style StyleMuted lipgloss.Style StyleTitle lipgloss.Style StyleSubtitle lipgloss.Style StyleHelpKey lipgloss.Style StyleValue lipgloss.Style StyleDimmed lipgloss.Style StyleTableHeader lipgloss.Style StyleTableSelected lipgloss.Style StyleBorder lipgloss.Color StyleBackground lipgloss.Color ) // DefaultTheme is the standard blue-based theme var DefaultTheme = &Theme{ Name: "default", Scheme: ColorScheme{ Primary: lipgloss.Color("62"), // Blue Success: lipgloss.Color("46"), // Green Warning: lipgloss.Color("208"), // Orange Error: lipgloss.Color("196"), // Red Muted: lipgloss.Color("241"), // Gray Background: lipgloss.Color(""), // Default terminal background }, } // DarkTheme is a purple-based dark theme var DarkTheme = &Theme{ Name: "dark", Scheme: ColorScheme{ Primary: lipgloss.Color("141"), // Purple Success: lipgloss.Color("51"), // Cyan Warning: lipgloss.Color("226"), // Yellow Error: lipgloss.Color("196"), // Red Muted: lipgloss.Color("245"), // Light gray Background: lipgloss.Color(""), // Default terminal background }, } // LightTheme is a green-based light theme var LightTheme = &Theme{ Name: "light", Scheme: ColorScheme{ Primary: lipgloss.Color("34"), // Green Success: lipgloss.Color("36"), // Teal Warning: lipgloss.Color("214"), // Amber Error: lipgloss.Color("196"), // Red Muted: lipgloss.Color("244"), // Gray Background: lipgloss.Color(""), // Default terminal background }, } // DraculaTheme is the popular Dracula dark color scheme var DraculaTheme = &Theme{ Name: "dracula", Scheme: ColorScheme{ Primary: lipgloss.Color("#BD93F9"), // Purple Success: lipgloss.Color("#50FA7B"), // Green Warning: lipgloss.Color("#FFB86C"), // Orange Error: lipgloss.Color("#FF5555"), // Red Muted: lipgloss.Color("#6272A4"), // Light purple-gray Background: lipgloss.Color("#282A36"), // Dark background }, } // EverforestTheme is the natural Everforest dark color scheme var EverforestTheme = &Theme{ Name: "everforest", Scheme: ColorScheme{ Primary: lipgloss.Color("#7fbbb3"), // Blue Success: lipgloss.Color("#a7c080"), // Green Warning: lipgloss.Color("#dbbc7f"), // Yellow Error: lipgloss.Color("#e67e80"), // Red Muted: lipgloss.Color("#414b50"), // Gray Background: lipgloss.Color("#272e33"), // Dark background }, } // ThemeRegistry holds all available themes var ThemeRegistry = map[string]*Theme{ "default": DefaultTheme, "dark": DarkTheme, "light": LightTheme, "dracula": DraculaTheme, "everforest": EverforestTheme, } // GetTheme loads the theme from config or environment variable // Returns the default theme if no theme is specified func GetTheme() (*Theme, error) { once.Do(func() { // Try to get theme from environment variable first themeName := os.Getenv("THEME") if themeName == "" { themeName = "everforest" } // Look up the theme in the registry if theme, ok := ThemeRegistry[themeName]; ok { currentTheme = theme } else { // If theme not found, use everforest as default currentTheme = EverforestTheme } // Apply the theme to initialize styles ApplyTheme(currentTheme) }) return currentTheme, nil } // ApplyTheme applies the given theme to all global styles func ApplyTheme(theme *Theme) { currentTheme = theme // Primary style StylePrimary = lipgloss.NewStyle(). Foreground(theme.Scheme.Primary) // Success style StyleSuccess = lipgloss.NewStyle(). Foreground(theme.Scheme.Success) // Warning style StyleWarning = lipgloss.NewStyle(). Foreground(theme.Scheme.Warning) // Error style StyleError = lipgloss.NewStyle(). Foreground(theme.Scheme.Error) // Muted style StyleMuted = lipgloss.NewStyle(). Foreground(theme.Scheme.Muted) // Title style (bold primary) StyleTitle = lipgloss.NewStyle(). Foreground(theme.Scheme.Primary). Bold(true) // Subtitle style (muted) StyleSubtitle = lipgloss.NewStyle(). Foreground(theme.Scheme.Muted) // Help key style (bold primary, slightly different shade) StyleHelpKey = lipgloss.NewStyle(). Foreground(theme.Scheme.Primary). Bold(true) // Value style for content values StyleValue = lipgloss.NewStyle(). Foreground(lipgloss.Color("255")) // Dimmed style for overlay content StyleDimmed = lipgloss.NewStyle(). Foreground(theme.Scheme.Muted) // Table header style StyleTableHeader = lipgloss.NewStyle(). BorderStyle(lipgloss.NormalBorder()). BorderForeground(lipgloss.Color("240")). BorderBottom(true). Bold(true) // Table selected style StyleTableSelected = lipgloss.NewStyle(). Foreground(lipgloss.Color("229")). Background(lipgloss.Color("57")). Bold(false) // Border color StyleBorder = lipgloss.Color("240") // Background color for modals StyleBackground = lipgloss.Color("235") } // Modal styles var ( // ModalBaseStyle is the base style for all modals ModalBaseStyle = func() lipgloss.Style { return lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(lipgloss.Color("196")). Padding(1, 2). Background(StyleBackground) } // ModalTitleStyle is the style for modal titles ModalTitleStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("226")). Bold(true) // ModalMessageStyle is the style for modal messages ModalMessageStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("255")). Width(50) // ModalHelpStyle is the style for modal help text ModalHelpStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("241")). MarginTop(1) // ModalSelectedStyle is the style for selected modal options ModalSelectedStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("57")). Bold(true). Underline(true) // ModalUnselectedStyle is the style for unselected modal options ModalUnselectedStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("241")) ) // Status icon styles var ( // StatusConnectedStyle is the style for connected status icons StatusConnectedStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("46")) // StatusDisconnectedStyle is the style for disconnected status icons StatusDisconnectedStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("196")) ) // GetThemeNames returns a list of available theme names func GetThemeNames() []string { names := make([]string, 0, len(ThemeRegistry)) for name := range ThemeRegistry { names = append(names, name) } return names } // SetTheme changes the current theme by name func SetTheme(name string) error { theme, ok := ThemeRegistry[name] if !ok { return fmt.Errorf("theme '%s' not found. Available themes: %v", name, GetThemeNames()) } ApplyTheme(theme) return nil } // GetCurrentTheme returns the currently active theme func GetCurrentTheme() *Theme { if currentTheme == nil { currentTheme = DefaultTheme ApplyTheme(currentTheme) } return currentTheme } // GetColorScheme returns the color scheme of the current theme func GetColorScheme() ColorScheme { return GetCurrentTheme().Scheme }