k9s/internal/config/styles.go

572 lines
14 KiB
Go

// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of K9s
package config
import (
"fmt"
"os"
"github.com/derailed/k9s/internal/config/data"
"github.com/derailed/k9s/internal/config/json"
"github.com/derailed/tcell/v2"
"github.com/derailed/tview"
"gopkg.in/yaml.v2"
)
// StyleListener represents a skin's listener.
type StyleListener interface {
// StylesChanged notifies listener the skin changed.
StylesChanged(*Styles)
}
type (
// Styles tracks K9s styling options.
Styles struct {
K9s Style `json:"k9s" yaml:"k9s"`
listeners []StyleListener
}
// Style tracks K9s styles.
Style struct {
Body Body `json:"body" yaml:"body"`
Prompt Prompt `json:"prompt" yaml:"prompt"`
Help Help `json:"help" yaml:"help"`
Frame Frame `json:"frame" yaml:"frame"`
Info Info `json:"info" yaml:"info"`
Views Views `json:"views" yaml:"views"`
Dialog Dialog `json:"dialog" yaml:"dialog"`
}
// Prompt tracks command styles
Prompt struct {
FgColor Color `json:"fgColor" yaml:"fgColor"`
BgColor Color `json:"bgColor" yaml:"bgColor"`
SuggestColor Color `json:"" yaml:"suggestColor"`
Border PromptBorder `json:"" yaml:"border"`
}
// PromptBorder tracks the color of the prompt depending on its kind (e.g., command or filter)
PromptBorder struct {
CommandColor Color `json:"command" yaml:"command"`
DefaultColor Color `json:"default" yaml:"default"`
}
// Help tracks help styles.
Help struct {
FgColor Color `json:"fgColor" yaml:"fgColor"`
BgColor Color `json:"bgColor" yaml:"bgColor"`
SectionColor Color `json:"sectionColor" yaml:"sectionColor"`
KeyColor Color `json:"keyColor" yaml:"keyColor"`
NumKeyColor Color `json:"numKeyColor" yaml:"numKeyColor"`
}
// Body tracks body styles.
Body struct {
FgColor Color `json:"fgColor" yaml:"fgColor"`
BgColor Color `json:"bgColor" yaml:"bgColor"`
LogoColor Color `json:"logoColor" yaml:"logoColor"`
LogoColorMsg Color `json:"logoColorMsg" yaml:"logoColorMsg"`
LogoColorInfo Color `json:"logoColorInfo" yaml:"logoColorInfo"`
LogoColorWarn Color `json:"logoColorWarn" yaml:"logoColorWarn"`
LogoColorError Color `json:"logoColorError" yaml:"logoColorError"`
}
// Dialog tracks dialog styles.
Dialog struct {
FgColor Color `json:"fgColor" yaml:"fgColor"`
BgColor Color `json:"bgColor" yaml:"bgColor"`
ButtonFgColor Color `json:"buttonFgColor" yaml:"buttonFgColor"`
ButtonBgColor Color `json:"buttonBgColor" yaml:"buttonBgColor"`
ButtonFocusFgColor Color `json:"buttonFocusFgColor" yaml:"buttonFocusFgColor"`
ButtonFocusBgColor Color `json:"buttonFocusBgColor" yaml:"buttonFocusBgColor"`
LabelFgColor Color `json:"labelFgColor" yaml:"labelFgColor"`
FieldFgColor Color `json:"fieldFgColor" yaml:"fieldFgColor"`
}
// Frame tracks frame styles.
Frame struct {
Title Title `json:"title" yaml:"title"`
Border Border `json:"border" yaml:"border"`
Menu Menu `json:"menu" yaml:"menu"`
Crumb Crumb `json:"crumbs" yaml:"crumbs"`
Status Status `json:"status" yaml:"status"`
}
// Views tracks individual view styles.
Views struct {
Table Table `json:"table" yaml:"table"`
Xray Xray `json:"xray" yaml:"xray"`
Charts Charts `json:"charts" yaml:"charts"`
Yaml Yaml `json:"yaml" yaml:"yaml"`
Picker Picker `json:"picker" yaml:"picker"`
Log Log `json:"logs" yaml:"logs"`
}
// Status tracks resource status styles.
Status struct {
NewColor Color `json:"newColor" yaml:"newColor"`
ModifyColor Color `json:"modifyColor" yaml:"modifyColor"`
AddColor Color `json:"addColor" yaml:"addColor"`
PendingColor Color `json:"pendingColor" yaml:"pendingColor"`
ErrorColor Color `json:"errorColor" yaml:"errorColor"`
HighlightColor Color `json:"highlightColor" yaml:"highlightColor"`
KillColor Color `json:"killColor" yaml:"killColor"`
CompletedColor Color `json:"completedColor" yaml:"completedColor"`
}
// Log tracks Log styles.
Log struct {
FgColor Color `json:"fgColor" yaml:"fgColor"`
BgColor Color `json:"bgColor" yaml:"bgColor"`
Indicator LogIndicator `json:"indicator" yaml:"indicator"`
}
// Picker tracks color when selecting containers
Picker struct {
MainColor Color `json:"mainColor" yaml:"mainColor"`
FocusColor Color `json:"focusColor" yaml:"focusColor"`
ShortcutColor Color `json:"shortcutColor" yaml:"shortcutColor"`
}
// LogIndicator tracks log view indicator.
LogIndicator struct {
FgColor Color `json:"fgColor" yaml:"fgColor"`
BgColor Color `json:"bgColor" yaml:"bgColor"`
ToggleOnColor Color `json:"toggleOnColor" yaml:"toggleOnColor"`
ToggleOffColor Color `json:"toggleOffColor" yaml:"toggleOffColor"`
}
// Yaml tracks yaml styles.
Yaml struct {
KeyColor Color `json:"keyColor" yaml:"keyColor"`
ValueColor Color `json:"valueColor" yaml:"valueColor"`
ColonColor Color `json:"colonColor" yaml:"colonColor"`
}
// Title tracks title styles.
Title struct {
FgColor Color `json:"fgColor" yaml:"fgColor"`
BgColor Color `json:"bgColor" yaml:"bgColor"`
HighlightColor Color `json:"highlightColor" yaml:"highlightColor"`
CounterColor Color `json:"counterColor" yaml:"counterColor"`
FilterColor Color `json:"filterColor" yaml:"filterColor"`
}
// Info tracks info styles.
Info struct {
SectionColor Color `json:"sectionColor" yaml:"sectionColor"`
FgColor Color `json:"fgColor" yaml:"fgColor"`
}
// Border tracks border styles.
Border struct {
FgColor Color `json:"fgColor" yaml:"fgColor"`
FocusColor Color `json:"focusColor" yaml:"focusColor"`
}
// Crumb tracks crumbs styles.
Crumb struct {
FgColor Color `json:"fgColor" yaml:"fgColor"`
BgColor Color `json:"bgColor" yaml:"bgColor"`
ActiveColor Color `json:"activeColor" yaml:"activeColor"`
}
// Table tracks table styles.
Table struct {
FgColor Color `json:"fgColor" yaml:"fgColor"`
BgColor Color `json:"bgColor" yaml:"bgColor"`
CursorFgColor Color `json:"cursorFgColor" yaml:"cursorFgColor"`
CursorBgColor Color `json:"cursorBgColor" yaml:"cursorBgColor"`
MarkColor Color `json:"markColor" yaml:"markColor"`
Header TableHeader `json:"header" yaml:"header"`
}
// TableHeader tracks table header styles.
TableHeader struct {
FgColor Color `json:"fgColor" yaml:"fgColor"`
BgColor Color `json:"bgColor" yaml:"bgColor"`
SorterColor Color `json:"sorterColor" yaml:"sorterColor"`
}
// Xray tracks xray styles.
Xray struct {
FgColor Color `json:"fgColor" yaml:"fgColor"`
BgColor Color `json:"bgColor" yaml:"bgColor"`
CursorColor Color `json:"cursorColor" yaml:"cursorColor"`
CursorTextColor Color `json:"cursorTextColor" yaml:"cursorTextColor"`
GraphicColor Color `json:"graphicColor" yaml:"graphicColor"`
}
// Menu tracks menu styles.
Menu struct {
FgColor Color `json:"fgColor" yaml:"fgColor"`
KeyColor Color `json:"keyColor" yaml:"keyColor"`
NumKeyColor Color `json:"numKeyColor" yaml:"numKeyColor"`
}
// Charts tracks charts styles.
Charts struct {
BgColor Color `json:"bgColor" yaml:"bgColor"`
DialBgColor Color `json:"dialBgColor" yaml:"dialBgColor"`
ChartBgColor Color `json:"chartBgColor" yaml:"chartBgColor"`
DefaultDialColors Colors `json:"defaultDialColors" yaml:"defaultDialColors"`
DefaultChartColors Colors `json:"defaultChartColors" yaml:"defaultChartColors"`
ResourceColors map[string]Colors `json:"resourceColors" yaml:"resourceColors"`
}
)
func newStyle() Style {
return Style{
Body: newBody(),
Prompt: newPrompt(),
Help: newHelp(),
Frame: newFrame(),
Info: newInfo(),
Views: newViews(),
Dialog: newDialog(),
}
}
func newDialog() Dialog {
return Dialog{
FgColor: "cadetblue",
BgColor: "black",
ButtonBgColor: "darkslateblue",
ButtonFgColor: "black",
ButtonFocusBgColor: "dodgerblue",
ButtonFocusFgColor: "black",
LabelFgColor: "white",
FieldFgColor: "white",
}
}
func newPrompt() Prompt {
return Prompt{
FgColor: "cadetblue",
BgColor: "black",
SuggestColor: "dodgerblue",
Border: PromptBorder{
DefaultColor: "seagreen",
CommandColor: "aqua",
},
}
}
func newCharts() Charts {
return Charts{
BgColor: "black",
DialBgColor: "black",
ChartBgColor: "black",
DefaultDialColors: Colors{Color("palegreen"), Color("orangered")},
DefaultChartColors: Colors{Color("palegreen"), Color("orangered")},
ResourceColors: map[string]Colors{
"cpu": {Color("dodgerblue"), Color("darkslateblue")},
"mem": {Color("yellow"), Color("goldenrod")},
},
}
}
func newViews() Views {
return Views{
Table: newTable(),
Xray: newXray(),
Charts: newCharts(),
Yaml: newYaml(),
Picker: newPicker(),
Log: newLog(),
}
}
func newFrame() Frame {
return Frame{
Title: newTitle(),
Border: newBorder(),
Menu: newMenu(),
Crumb: newCrumb(),
Status: newStatus(),
}
}
func newHelp() Help {
return Help{
FgColor: "cadetblue",
BgColor: "black",
SectionColor: "green",
KeyColor: "dodgerblue",
NumKeyColor: "fuchsia",
}
}
func newBody() Body {
return Body{
FgColor: "cadetblue",
BgColor: "black",
LogoColor: "orange",
LogoColorMsg: "white",
LogoColorInfo: "green",
LogoColorWarn: "mediumvioletred",
LogoColorError: "red",
}
}
func newStatus() Status {
return Status{
NewColor: "lightskyblue",
ModifyColor: "greenyellow",
AddColor: "dodgerblue",
PendingColor: "darkorange",
ErrorColor: "orangered",
HighlightColor: "aqua",
KillColor: "mediumpurple",
CompletedColor: "lightslategray",
}
}
func newPicker() Picker {
return Picker{
MainColor: "white",
FocusColor: "aqua",
ShortcutColor: "aqua",
}
}
func newLog() Log {
return Log{
FgColor: "lightskyblue",
BgColor: "black",
Indicator: newLogIndicator(),
}
}
func newLogIndicator() LogIndicator {
return LogIndicator{
FgColor: "dodgerblue",
BgColor: "black",
ToggleOnColor: "limegreen",
ToggleOffColor: "gray",
}
}
func newYaml() Yaml {
return Yaml{
KeyColor: "steelblue",
ColonColor: "white",
ValueColor: "papayawhip",
}
}
func newTitle() Title {
return Title{
FgColor: "aqua",
BgColor: "black",
HighlightColor: "fuchsia",
CounterColor: "papayawhip",
FilterColor: "seagreen",
}
}
func newInfo() Info {
return Info{
SectionColor: "white",
FgColor: "orange",
}
}
func newXray() Xray {
return Xray{
FgColor: "aqua",
BgColor: "black",
CursorColor: "dodgerblue",
CursorTextColor: "black",
GraphicColor: "cadetblue",
}
}
func newTable() Table {
return Table{
FgColor: "aqua",
BgColor: "black",
CursorFgColor: "black",
CursorBgColor: "aqua",
MarkColor: "palegreen",
Header: newTableHeader(),
}
}
func newTableHeader() TableHeader {
return TableHeader{
FgColor: "white",
BgColor: "black",
SorterColor: "aqua",
}
}
func newCrumb() Crumb {
return Crumb{
FgColor: "black",
BgColor: "aqua",
ActiveColor: "orange",
}
}
func newBorder() Border {
return Border{
FgColor: "dodgerblue",
FocusColor: "lightskyblue",
}
}
func newMenu() Menu {
return Menu{
FgColor: "white",
KeyColor: "dodgerblue",
NumKeyColor: "fuchsia",
}
}
// NewStyles creates a new default config.
func NewStyles() *Styles {
var s Styles
if err := yaml.Unmarshal(stockSkinTpl, &s); err == nil {
return &s
}
return &Styles{
K9s: newStyle(),
}
}
// Reset resets styles.
func (s *Styles) Reset() {
if err := yaml.Unmarshal(stockSkinTpl, s); err != nil {
s.K9s = newStyle()
}
}
// FgColor returns the foreground color.
func (s *Styles) FgColor() tcell.Color {
return s.Body().FgColor.Color()
}
// BgColor returns the background color.
func (s *Styles) BgColor() tcell.Color {
return s.Body().BgColor.Color()
}
// AddListener registers a new listener.
func (s *Styles) AddListener(l StyleListener) {
s.listeners = append(s.listeners, l)
}
// RemoveListener removes a listener.
func (s *Styles) RemoveListener(l StyleListener) {
victim := -1
for i, lis := range s.listeners {
if lis == l {
victim = i
break
}
}
if victim == -1 {
return
}
s.listeners = append(s.listeners[:victim], s.listeners[victim+1:]...)
}
func (s *Styles) fireStylesChanged() {
for _, list := range s.listeners {
list.StylesChanged(s)
}
}
// Body returns body styles.
func (s *Styles) Body() Body {
return s.K9s.Body
}
// Prompt returns prompt styles.
func (s *Styles) Prompt() Prompt {
return s.K9s.Prompt
}
// Frame returns frame styles.
func (s *Styles) Frame() Frame {
return s.K9s.Frame
}
// Crumb returns crumb styles.
func (s *Styles) Crumb() Crumb {
return s.Frame().Crumb
}
// Title returns title styles.
func (s *Styles) Title() Title {
return s.Frame().Title
}
// Charts returns charts styles.
func (s *Styles) Charts() Charts {
return s.K9s.Views.Charts
}
// Dialog returns dialog styles.
func (s *Styles) Dialog() Dialog {
return s.K9s.Dialog
}
// Table returns table styles.
func (s *Styles) Table() Table {
return s.K9s.Views.Table
}
// Xray returns xray styles.
func (s *Styles) Xray() Xray {
return s.K9s.Views.Xray
}
// Views returns views styles.
func (s *Styles) Views() Views {
return s.K9s.Views
}
// Load K9s configuration from file.
func (s *Styles) Load(path string) error {
bb, err := os.ReadFile(path)
if err != nil {
return err
}
if err := data.JSONValidator.Validate(json.SkinSchema, bb); err != nil {
return err
}
if err := yaml.Unmarshal(bb, s); err != nil {
return err
}
return nil
}
// Update apply terminal colors based on styles.
func (s *Styles) Update() {
tview.Styles.PrimitiveBackgroundColor = s.BgColor()
tview.Styles.ContrastBackgroundColor = s.BgColor()
tview.Styles.MoreContrastBackgroundColor = s.BgColor()
tview.Styles.PrimaryTextColor = s.FgColor()
tview.Styles.BorderColor = s.K9s.Frame.Border.FgColor.Color()
tview.Styles.FocusColor = s.K9s.Frame.Border.FocusColor.Color()
tview.Styles.TitleColor = s.FgColor()
tview.Styles.GraphicsColor = s.FgColor()
tview.Styles.SecondaryTextColor = s.FgColor()
tview.Styles.TertiaryTextColor = s.FgColor()
tview.Styles.InverseTextColor = s.FgColor()
tview.Styles.ContrastSecondaryTextColor = s.FgColor()
s.fireStylesChanged()
}
// Dump for debug.
func (s *Styles) Dump() {
bb, _ := yaml.Marshal(s)
fmt.Println(string(bb))
}