Files
proxy/internal/tui/theme.go
T

94 lines
2.6 KiB
Go

package tui
import (
"io"
"os"
"github.com/charmbracelet/lipgloss"
"github.com/muesli/termenv"
"proxyctl/internal/target"
)
type semanticTheme struct {
title lipgloss.Style
section lipgloss.Style
selected lipgloss.Style
status lipgloss.Style
warning lipgloss.Style
enabled lipgloss.Style
disabled lipgloss.Style
external lipgloss.Style
drifted lipgloss.Style
applying lipgloss.Style
}
func newSemanticTheme(mode themeMode, output io.Writer) semanticTheme {
if output == nil {
output = os.Stdout
}
renderer := lipgloss.NewRenderer(output)
explicit := mode != themeModeAuto
if _, noColor := os.LookupEnv("NO_COLOR"); noColor && mode == themeModeAuto {
mode = themeModeNoColor
explicit = true
}
if mode == themeModeAuto {
if renderer.ColorProfile() == termenv.Ascii {
mode = themeModeNoColor
} else if renderer.HasDarkBackground() {
mode = themeModeDark
} else {
mode = themeModeLight
}
}
if mode == themeModeNoColor {
renderer.SetColorProfile(termenv.Ascii)
} else if explicit {
// Explicit modes make fixed-size render tests deterministic. Production
// auto mode retains capabilities detected from the injected output.
renderer.SetColorProfile(termenv.ANSI256)
}
foreground, subtle, accent := "252", "245", "75"
if mode == themeModeLight {
foreground, subtle, accent = "235", "242", "25"
}
style := renderer.NewStyle
return semanticTheme{
title: style().Bold(true).Foreground(lipgloss.Color(accent)),
section: style().Bold(true).Foreground(lipgloss.Color(foreground)),
selected: style().Bold(true).Underline(true).Foreground(lipgloss.Color(accent)),
status: style().Foreground(lipgloss.Color(foreground)),
warning: style().Bold(true).Foreground(lipgloss.Color("172")),
enabled: style().Foreground(lipgloss.Color("34")),
disabled: style().Foreground(lipgloss.Color(subtle)),
external: style().Foreground(lipgloss.Color("172")),
drifted: style().Bold(true).Foreground(lipgloss.Color("160")),
applying: style().Foreground(lipgloss.Color(accent)),
}
}
func (t semanticTheme) renderTargetState(state target.State, applying bool) string {
if applying {
return t.applying.Render("Applying…")
}
label := string(state)
switch state {
case target.Enabled:
return t.enabled.Render(paddedState(label))
case target.Disabled:
return t.disabled.Render(paddedState(label))
case target.External:
return t.external.Render(paddedState(label))
case target.Drifted:
return t.drifted.Render(paddedState(label))
default:
return paddedState(label)
}
}
func paddedState(label string) string {
return lipgloss.NewStyle().Width(9).Render(label)
}