601 lines
15 KiB
Go
601 lines
15 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.v3"
|
|
)
|
|
|
|
// StyleListener represents a skin's listener.
|
|
type StyleListener interface {
|
|
// StylesChanged notifies listener the skin changed.
|
|
StylesChanged(*Styles)
|
|
}
|
|
|
|
// TextStyle tracks text styles.
|
|
type TextStyle string
|
|
|
|
const (
|
|
// TextStyleNormal is the default text style.
|
|
TextStyleNormal TextStyle = "normal"
|
|
|
|
// TextStyleBold is the bold text style.
|
|
TextStyleBold TextStyle = "bold"
|
|
|
|
// TextStyleDim is the dim text style.
|
|
TextStyleDim TextStyle = "dim"
|
|
)
|
|
|
|
// ToShortString returns a short string representation of the text style.
|
|
func (ts TextStyle) ToShortString() string {
|
|
switch ts {
|
|
case TextStyleNormal:
|
|
return "-"
|
|
case TextStyleBold:
|
|
return "b"
|
|
case TextStyleDim:
|
|
return "d"
|
|
default:
|
|
return "d"
|
|
}
|
|
}
|
|
|
|
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"`
|
|
FgStyle TextStyle `json:"fgStyle" yaml:"fgStyle"`
|
|
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))
|
|
}
|