111 lines
3.1 KiB
Go
111 lines
3.1 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
|
|
unavailable lipgloss.Style
|
|
checking lipgloss.Style
|
|
checkFailed 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")),
|
|
unavailable: style().Foreground(lipgloss.Color(subtle)),
|
|
checking: style().Foreground(lipgloss.Color(accent)),
|
|
checkFailed: 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 := targetStateLabel(state)
|
|
switch state {
|
|
case target.Enabled:
|
|
return t.enabled.Render(label)
|
|
case target.Disabled:
|
|
return t.disabled.Render(label)
|
|
case target.External:
|
|
return t.external.Render(label)
|
|
case target.Drifted:
|
|
return t.drifted.Render(label)
|
|
default:
|
|
return label
|
|
}
|
|
}
|
|
|
|
func targetStateLabel(state target.State) string {
|
|
switch state {
|
|
case target.Enabled:
|
|
return "● Enabled"
|
|
case target.Disabled:
|
|
return "○ Disabled"
|
|
case target.External:
|
|
return "◇ External"
|
|
case target.Drifted:
|
|
return "! Drifted"
|
|
default:
|
|
return "? Unknown"
|
|
}
|
|
}
|