summaryrefslogtreecommitdiffhomepage
path: root/packages/tui/internal/theme/manager.go
diff options
context:
space:
mode:
Diffstat (limited to 'packages/tui/internal/theme/manager.go')
-rw-r--r--packages/tui/internal/theme/manager.go134
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())
+}