diff options
Diffstat (limited to 'packages/tui/internal/theme/manager.go')
| -rw-r--r-- | packages/tui/internal/theme/manager.go | 134 |
1 files changed, 129 insertions, 5 deletions
diff --git a/packages/tui/internal/theme/manager.go b/packages/tui/internal/theme/manager.go index 87fbc131d..583e8c491 100644 --- a/packages/tui/internal/theme/manager.go +++ b/packages/tui/internal/theme/manager.go @@ -2,19 +2,25 @@ package theme import ( "fmt" + "image/color" "slices" + "strconv" "strings" "sync" "github.com/alecthomas/chroma/v2/styles" + "github.com/charmbracelet/lipgloss/v2" + "github.com/charmbracelet/lipgloss/v2/compat" + "github.com/charmbracelet/x/ansi" ) // Manager handles theme registration, selection, and retrieval. // It maintains a registry of available themes and tracks the currently active theme. type Manager struct { - themes map[string]Theme - currentName string - mu sync.RWMutex + themes map[string]Theme + currentName string + currentUsesAnsiCache bool // Cache whether current theme uses ANSI colors + mu sync.RWMutex } // Global instance of the theme manager @@ -34,6 +40,7 @@ func RegisterTheme(name string, theme Theme) { // If this is the first theme, make it the default if globalManager.currentName == "" { globalManager.currentName = name + globalManager.currentUsesAnsiCache = themeUsesAnsiColors(theme) } } @@ -44,11 +51,13 @@ func SetTheme(name string) error { defer globalManager.mu.Unlock() delete(styles.Registry, "charm") - if _, exists := globalManager.themes[name]; !exists { + theme, exists := globalManager.themes[name] + if !exists { return fmt.Errorf("theme '%s' not found", name) } globalManager.currentName = name + globalManager.currentUsesAnsiCache = themeUsesAnsiColors(theme) return nil } @@ -84,7 +93,11 @@ func AvailableThemes() []string { names = append(names, name) } slices.SortFunc(names, func(a, b string) int { - // list system theme first + if a == "system" { + return -1 + } else if b == "system" { + return 1 + } if a == "opencode" { return -1 } else if b == "opencode" { @@ -103,3 +116,114 @@ func GetTheme(name string) Theme { return globalManager.themes[name] } + +// UpdateSystemTheme updates the system theme with terminal background info +func UpdateSystemTheme(terminalBg color.Color, isDark bool) { + globalManager.mu.Lock() + defer globalManager.mu.Unlock() + + dynamicTheme := NewSystemTheme(terminalBg, isDark) + globalManager.themes["system"] = dynamicTheme + if globalManager.currentName == "system" { + globalManager.currentUsesAnsiCache = themeUsesAnsiColors(dynamicTheme) + } +} + +// CurrentThemeUsesAnsiColors returns true if the current theme uses ANSI 0-16 colors +func CurrentThemeUsesAnsiColors() bool { + // globalManager.mu.RLock() + // defer globalManager.mu.RUnlock() + + return globalManager.currentUsesAnsiCache +} + +// isAnsiColor checks if a color represents an ANSI 0-16 color +func isAnsiColor(c color.Color) bool { + if _, ok := c.(lipgloss.NoColor); ok { + return false + } + if _, ok := c.(ansi.BasicColor); ok { + return true + } + + // For other color types, check if they represent ANSI colors + // by examining their string representation + if stringer, ok := c.(fmt.Stringer); ok { + str := stringer.String() + // Check if it's a numeric ANSI color (0-15) + if num, err := strconv.Atoi(str); err == nil && num >= 0 && num <= 15 { + return true + } + } + + return false +} + +// adaptiveColorUsesAnsi checks if an AdaptiveColor uses ANSI colors +func adaptiveColorUsesAnsi(ac compat.AdaptiveColor) bool { + if isAnsiColor(ac.Dark) { + return true + } + if isAnsiColor(ac.Light) { + return true + } + return false +} + +// themeUsesAnsiColors checks if a theme uses any ANSI 0-16 colors +func themeUsesAnsiColors(theme Theme) bool { + if theme == nil { + return false + } + + return adaptiveColorUsesAnsi(theme.Primary()) || + adaptiveColorUsesAnsi(theme.Secondary()) || + adaptiveColorUsesAnsi(theme.Accent()) || + adaptiveColorUsesAnsi(theme.Error()) || + adaptiveColorUsesAnsi(theme.Warning()) || + adaptiveColorUsesAnsi(theme.Success()) || + adaptiveColorUsesAnsi(theme.Info()) || + adaptiveColorUsesAnsi(theme.Text()) || + adaptiveColorUsesAnsi(theme.TextMuted()) || + adaptiveColorUsesAnsi(theme.Background()) || + adaptiveColorUsesAnsi(theme.BackgroundPanel()) || + adaptiveColorUsesAnsi(theme.BackgroundElement()) || + adaptiveColorUsesAnsi(theme.Border()) || + adaptiveColorUsesAnsi(theme.BorderActive()) || + adaptiveColorUsesAnsi(theme.BorderSubtle()) || + adaptiveColorUsesAnsi(theme.DiffAdded()) || + adaptiveColorUsesAnsi(theme.DiffRemoved()) || + adaptiveColorUsesAnsi(theme.DiffContext()) || + adaptiveColorUsesAnsi(theme.DiffHunkHeader()) || + adaptiveColorUsesAnsi(theme.DiffHighlightAdded()) || + adaptiveColorUsesAnsi(theme.DiffHighlightRemoved()) || + adaptiveColorUsesAnsi(theme.DiffAddedBg()) || + adaptiveColorUsesAnsi(theme.DiffRemovedBg()) || + adaptiveColorUsesAnsi(theme.DiffContextBg()) || + adaptiveColorUsesAnsi(theme.DiffLineNumber()) || + adaptiveColorUsesAnsi(theme.DiffAddedLineNumberBg()) || + adaptiveColorUsesAnsi(theme.DiffRemovedLineNumberBg()) || + adaptiveColorUsesAnsi(theme.MarkdownText()) || + adaptiveColorUsesAnsi(theme.MarkdownHeading()) || + adaptiveColorUsesAnsi(theme.MarkdownLink()) || + adaptiveColorUsesAnsi(theme.MarkdownLinkText()) || + adaptiveColorUsesAnsi(theme.MarkdownCode()) || + adaptiveColorUsesAnsi(theme.MarkdownBlockQuote()) || + adaptiveColorUsesAnsi(theme.MarkdownEmph()) || + adaptiveColorUsesAnsi(theme.MarkdownStrong()) || + adaptiveColorUsesAnsi(theme.MarkdownHorizontalRule()) || + adaptiveColorUsesAnsi(theme.MarkdownListItem()) || + adaptiveColorUsesAnsi(theme.MarkdownListEnumeration()) || + adaptiveColorUsesAnsi(theme.MarkdownImage()) || + adaptiveColorUsesAnsi(theme.MarkdownImageText()) || + adaptiveColorUsesAnsi(theme.MarkdownCodeBlock()) || + adaptiveColorUsesAnsi(theme.SyntaxComment()) || + adaptiveColorUsesAnsi(theme.SyntaxKeyword()) || + adaptiveColorUsesAnsi(theme.SyntaxFunction()) || + adaptiveColorUsesAnsi(theme.SyntaxVariable()) || + adaptiveColorUsesAnsi(theme.SyntaxString()) || + adaptiveColorUsesAnsi(theme.SyntaxNumber()) || + adaptiveColorUsesAnsi(theme.SyntaxType()) || + adaptiveColorUsesAnsi(theme.SyntaxOperator()) || + adaptiveColorUsesAnsi(theme.SyntaxPunctuation()) +} |
