feat(theme): add catppuccin themes with transparency and 256-color support

Add a theme package providing all four catppuccin flavors (mocha,
frappe, macchiato, latte) mapped to the xterm-256 palette. Themes are
selected with -theme or the CTOP_THEME environment variable; -i inverts
the flavor. Background slots use the terminal's native color for
transparency (opt out with -solid / CTOP_SOLID), and termbox is switched
to Output256 so colors render correctly. Replace hardcoded colors in the
compact and single views with ctheme portrait slots.
This commit is contained in:
ahp
2026-09-14 14:06:49 +03:30
parent 59f00dd6aa
commit 78e85dc4df
12 changed files with 454 additions and 67 deletions
+12 -49
View File
@@ -1,61 +1,24 @@
package main package main
import ( import (
"regexp" "github.com/bcicen/ctop/theme"
ui "github.com/gizak/termui" ui "github.com/gizak/termui"
) )
/* var ColorMap map[string]ui.Attribute
Valid colors:
ui.ColorDefault
ui.ColorBlack
ui.ColorRed
ui.ColorGreen
ui.ColorYellow
ui.ColorBlue
ui.ColorMagenta
ui.ColorCyan
ui.ColorWhite
*/
var ColorMap = map[string]ui.Attribute{ // SetColorMap installs the color scheme for the currently selected theme
"fg": ui.ColorWhite, func SetColorMap() {
"bg": ui.ColorDefault, ColorMap = theme.BuildColorMap()
"block.bg": ui.ColorDefault,
"border.bg": ui.ColorDefault,
"border.fg": ui.ColorWhite,
"label.bg": ui.ColorDefault,
"label.fg": ui.ColorGreen,
"menu.text.fg": ui.ColorWhite,
"menu.text.bg": ui.ColorDefault,
"menu.border.fg": ui.ColorCyan,
"menu.label.fg": ui.ColorGreen,
"header.fg": ui.ColorBlack,
"header.bg": ui.ColorWhite,
"gauge.bar.bg": ui.ColorGreen,
"gauge.percent.fg": ui.ColorWhite,
"linechart.axes.fg": ui.ColorDefault,
"linechart.line.fg": ui.ColorGreen,
"mbarchart.bar.bg": ui.ColorGreen,
"mbarchart.num.fg": ui.ColorWhite,
"mbarchart.text.fg": ui.ColorWhite,
"par.text.fg": ui.ColorWhite,
"par.text.bg": ui.ColorDefault,
"par.text.hi": ui.ColorBlack,
"sparkline.line.fg": ui.ColorGreen,
"sparkline.title.fg": ui.ColorWhite,
"status.ok": ui.ColorGreen,
"status.warn": ui.ColorYellow,
"status.danger": ui.ColorRed,
} }
// InvertColorMap switches to the inverse (light/dark) flavor
func InvertColorMap() { func InvertColorMap() {
re := regexp.MustCompile(".*.fg") if theme.Current() == theme.Latte {
for k := range ColorMap { theme.Set(theme.Mocha)
if re.FindAllString(k, 1) != nil { } else {
ColorMap[k] = ui.ColorBlack theme.Set(theme.Latte)
}
} }
ColorMap["par.text.hi"] = ui.ColorWhite SetColorMap()
} }
+2 -1
View File
@@ -5,6 +5,7 @@ import (
"github.com/bcicen/ctop/cwidgets" "github.com/bcicen/ctop/cwidgets"
"github.com/bcicen/ctop/models" "github.com/bcicen/ctop/models"
"github.com/bcicen/ctop/theme"
ui "github.com/gizak/termui" ui "github.com/gizak/termui"
) )
@@ -75,7 +76,7 @@ func (w *GaugeCol) Buffer() ui.Buffer {
// percentage value and low-contrast color for structure // percentage value and low-contrast color for structure
if w.Percent < 5 { if w.Percent < 5 {
w.Percent = 5 w.Percent = 5
w.BarColor = ui.ColorBlack w.BarColor = theme.Attr("crust")
} }
return w.Gauge.Buffer() return w.Gauge.Buffer()
+5 -4
View File
@@ -2,6 +2,7 @@ package compact
import ( import (
"github.com/bcicen/ctop/models" "github.com/bcicen/ctop/models"
"github.com/bcicen/ctop/theme"
ui "github.com/gizak/termui" ui "github.com/gizak/termui"
) )
@@ -45,7 +46,7 @@ func (s *Status) Header() string { return "" }
func (s *Status) FixedWidth() int { return 3 } func (s *Status) FixedWidth() int { return 3 }
func (s *Status) setState(val string) { func (s *Status) setState(val string) {
color := ui.ColorDefault color := theme.Attr("text")
var mark string var mark string
switch val { switch val {
@@ -66,11 +67,11 @@ func (s *Status) setState(val string) {
log.Warningf("unknown status string: \"%v\"", val) log.Warningf("unknown status string: \"%v\"", val)
} }
s.status = ui.TextCells(mark, color, ui.ColorDefault) s.status = ui.TextCells(mark, color, theme.Attr("bg"))
} }
func (s *Status) setHealth(val string) { func (s *Status) setHealth(val string) {
color := ui.ColorDefault color := theme.Attr("text")
var mark string var mark string
switch val { switch val {
@@ -90,5 +91,5 @@ func (s *Status) setHealth(val string) {
log.Warningf("unknown health state string: \"%v\"", val) log.Warningf("unknown health state string: \"%v\"", val)
} }
s.health = ui.TextCells(mark, color, ui.ColorDefault) s.health = ui.TextCells(mark, color, theme.Attr("bg"))
} }
+3 -2
View File
@@ -5,6 +5,7 @@ import (
"strings" "strings"
"github.com/bcicen/ctop/cwidgets" "github.com/bcicen/ctop/cwidgets"
"github.com/bcicen/ctop/theme"
ui "github.com/gizak/termui" ui "github.com/gizak/termui"
) )
@@ -26,13 +27,13 @@ func NewIO() *IO {
read.Title = "READ" read.Title = "READ"
read.Height = 1 read.Height = 1
read.Data = io.readHist.Data read.Data = io.readHist.Data
read.LineColor = ui.ColorGreen read.LineColor = theme.Attr("io.read")
write := ui.NewSparkline() write := ui.NewSparkline()
write.Title = "WRITE" write.Title = "WRITE"
write.Height = 1 write.Height = 1
write.Data = io.writeHist.Data write.Data = io.writeHist.Data
write.LineColor = ui.ColorYellow write.LineColor = theme.Attr("io.write")
io.Lines = []ui.Sparkline{read, write} io.Lines = []ui.Sparkline{read, write}
return io return io
+3 -2
View File
@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"github.com/bcicen/ctop/cwidgets" "github.com/bcicen/ctop/cwidgets"
"github.com/bcicen/ctop/theme"
ui "github.com/gizak/termui" ui "github.com/gizak/termui"
) )
@@ -67,8 +68,8 @@ func newMemChart() *ui.MBarChart {
mbar.BarGap = 1 mbar.BarGap = 1
mbar.BarWidth = 6 mbar.BarWidth = 6
mbar.BarColor[1] = ui.ColorBlack mbar.BarColor[1] = theme.Attr("crust")
mbar.NumColor[1] = ui.ColorBlack mbar.NumColor[1] = theme.Attr("crust")
mbar.NumFmt = cwidgets.ByteFormatShort mbar.NumFmt = cwidgets.ByteFormatShort
//mbar.ShowScale = true //mbar.ShowScale = true
+3 -2
View File
@@ -5,6 +5,7 @@ import (
"strings" "strings"
"github.com/bcicen/ctop/cwidgets" "github.com/bcicen/ctop/cwidgets"
"github.com/bcicen/ctop/theme"
ui "github.com/gizak/termui" ui "github.com/gizak/termui"
) )
@@ -26,13 +27,13 @@ func NewNet() *Net {
rx.Title = "RX" rx.Title = "RX"
rx.Height = 1 rx.Height = 1
rx.Data = net.rxHist.Data rx.Data = net.rxHist.Data
rx.LineColor = ui.ColorGreen rx.LineColor = theme.Attr("net.rx")
tx := ui.NewSparkline() tx := ui.NewSparkline()
tx.Title = "TX" tx.Title = "TX"
tx.Height = 1 tx.Height = 1
tx.Data = net.txHist.Data tx.Data = net.txHist.Data
tx.LineColor = ui.ColorYellow tx.LineColor = theme.Attr("net.tx")
net.Lines = []ui.Sparkline{rx, tx} net.Lines = []ui.Sparkline{rx, tx}
return net return net
+1 -5
View File
@@ -5,7 +5,7 @@ require (
github.com/c9s/goprocinfo v0.0.0-20170609001544-b34328d6e0cd github.com/c9s/goprocinfo v0.0.0-20170609001544-b34328d6e0cd
github.com/fsouza/go-dockerclient v1.7.0 github.com/fsouza/go-dockerclient v1.7.0
github.com/gizak/termui v2.3.1-0.20180817033724-8d4faad06196+incompatible github.com/gizak/termui v2.3.1-0.20180817033724-8d4faad06196+incompatible
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b // indirect github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b
github.com/jgautheron/codename-generator v0.0.0-20150829203204-16d037c7cc3c github.com/jgautheron/codename-generator v0.0.0-20150829203204-16d037c7cc3c
github.com/mattn/go-runewidth v0.0.2 github.com/mattn/go-runewidth v0.0.2
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d
@@ -14,7 +14,6 @@ require (
github.com/opencontainers/runc v1.1.0 github.com/opencontainers/runc v1.1.0
github.com/pkg/browser v0.0.0-20201207095918-0426ae3fba23 github.com/pkg/browser v0.0.0-20201207095918-0426ae3fba23
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.4.0
) )
require ( require (
@@ -29,7 +28,6 @@ require (
github.com/containerd/continuity v0.0.0-20200928162600-f2cc35102c2a // indirect github.com/containerd/continuity v0.0.0-20200928162600-f2cc35102c2a // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/cyphar/filepath-securejoin v0.2.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/docker v20.10.0-beta1.0.20201113105859-b6bfff2a628f+incompatible // indirect github.com/docker/docker v20.10.0-beta1.0.20201113105859-b6bfff2a628f+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect
@@ -47,7 +45,6 @@ require (
github.com/opencontainers/image-spec v1.0.1 // indirect github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 // indirect github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 // indirect
github.com/opencontainers/selinux v1.10.0 // indirect github.com/opencontainers/selinux v1.10.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921 // indirect github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect github.com/sirupsen/logrus v1.8.1 // indirect
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
@@ -58,7 +55,6 @@ require (
golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c // indirect golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c // indirect
google.golang.org/protobuf v1.27.1 // indirect google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/yaml.v2 v2.2.8 // indirect
) )
go 1.18 go 1.18
-1
View File
@@ -242,7 +242,6 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+29
View File
@@ -12,6 +12,7 @@ import (
"github.com/bcicen/ctop/container" "github.com/bcicen/ctop/container"
"github.com/bcicen/ctop/cwidgets/compact" "github.com/bcicen/ctop/cwidgets/compact"
"github.com/bcicen/ctop/logging" "github.com/bcicen/ctop/logging"
"github.com/bcicen/ctop/theme"
"github.com/bcicen/ctop/widgets" "github.com/bcicen/ctop/widgets"
ui "github.com/gizak/termui" ui "github.com/gizak/termui"
tm "github.com/nsf/termbox-go" tm "github.com/nsf/termbox-go"
@@ -45,6 +46,8 @@ func main() {
reverseSortFlag = flag.Bool("r", false, "reverse container sort order") reverseSortFlag = flag.Bool("r", false, "reverse container sort order")
invertFlag = flag.Bool("i", false, "invert default colors") invertFlag = flag.Bool("i", false, "invert default colors")
connectorFlag = flag.String("connector", "docker", "container connector to use") connectorFlag = flag.String("connector", "docker", "container connector to use")
themeFlag = flag.String("theme", "", "catppuccin color theme (mocha, frappe, macchiato, latte)")
solidFlag = flag.Bool("solid", false, "paint opaque catppuccin background instead of transparent")
) )
flag.Parse() flag.Parse()
@@ -85,15 +88,39 @@ func main() {
config.Toggle("sortReversed") config.Toggle("sortReversed")
} }
// resolve catppuccin theme in order: -theme flag, CTOP_THEME env, default
themeName := "mocha"
switch {
case *themeFlag != "":
themeName = *themeFlag
case os.Getenv("CTOP_THEME") != "":
themeName = os.Getenv("CTOP_THEME")
}
flavor, ok := theme.ParseFlavor(themeName)
if !ok {
fmt.Printf("invalid theme: %q (available: %s)\n", themeName, strings.Join(theme.Flavors(), ", "))
os.Exit(1)
}
theme.Set(flavor)
solid := *solidFlag
switch os.Getenv("CTOP_SOLID") {
case "1", "true", "TRUE", "yes", "YES":
solid = true
}
theme.SetOpaque(solid)
// init ui // init ui
if *invertFlag { if *invertFlag {
InvertColorMap() InvertColorMap()
} else {
SetColorMap()
} }
ui.ColorMap = ColorMap // override default colormap ui.ColorMap = ColorMap // override default colormap
if err := ui.Init(); err != nil { if err := ui.Init(); err != nil {
panic(err) panic(err)
} }
tm.SetInputMode(tm.InputAlt) tm.SetInputMode(tm.InputAlt)
tm.SetOutputMode(tm.Output256) // enable 256-color catppuccin palette
defer Shutdown() defer Shutdown()
// init grid, cursor, header // init grid, cursor, header
@@ -150,6 +177,8 @@ options:
func printHelp() { func printHelp() {
fmt.Println(helpMsg) fmt.Println(helpMsg)
flag.PrintDefaults() flag.PrintDefaults()
fmt.Printf("\ncatppuccin themes: mocha (default), frappe, macchiato, latte\n")
fmt.Printf("select a theme with -theme or the CTOP_THEME environment variable\n")
fmt.Printf("\navailable connectors: ") fmt.Printf("\navailable connectors: ")
fmt.Println(strings.Join(connector.Enabled(), ", ")) fmt.Println(strings.Join(connector.Enabled(), ", "))
} }
+217
View File
@@ -0,0 +1,217 @@
package theme
import ui "github.com/gizak/termui"
type Flavor string
const (
Mocha Flavor = "mocha"
Frappe Flavor = "frappe"
Macchiato Flavor = "macchiato"
Latte Flavor = "latte"
)
func Flavors() []string { return []string{"mocha", "frappe", "macchiato", "latte"} }
func ParseFlavor(s string) (Flavor, bool) {
if s == "mocha" { return Mocha, true }
if s == "frappe" { return Frappe, true }
if s == "macchiato" { return Macchiato, true }
if s == "latte" { return Latte, true }
return "", false
}
var paletteMocha = map[string]int{
"rosewater": 224,
"flamingo": 224,
"pink": 218,
"mauve": 183,
"red": 211,
"maroon": 181,
"peach": 216,
"yellow": 223,
"green": 151,
"teal": 116,
"sky": 116,
"sapphire": 117,
"blue": 111,
"lavender": 147,
"text": 189,
"subtext1": 146,
"subtext0": 146,
"overlay2": 103,
"overlay1": 103,
"overlay0": 243,
"surface2": 241,
"surface1": 239,
"surface0": 237,
"base": 235,
"mantle": 234,
"crust": 233,
}
var paletteFrappe = map[string]int{
"rosewater": 224,
"flamingo": 217,
"pink": 218,
"mauve": 182,
"red": 174,
"maroon": 175,
"peach": 216,
"yellow": 186,
"green": 150,
"teal": 115,
"sky": 116,
"sapphire": 110,
"blue": 111,
"lavender": 147,
"text": 189,
"subtext1": 146,
"subtext0": 146,
"overlay2": 109,
"overlay1": 103,
"overlay0": 243,
"surface2": 60,
"surface1": 59,
"surface0": 239,
"base": 237,
"mantle": 236,
"crust": 235,
}
var paletteMacchiato = map[string]int{
"rosewater": 224,
"flamingo": 224,
"pink": 218,
"mauve": 183,
"red": 210,
"maroon": 211,
"peach": 216,
"yellow": 223,
"green": 150,
"teal": 116,
"sky": 116,
"sapphire": 116,
"blue": 111,
"lavender": 147,
"text": 189,
"subtext1": 146,
"subtext0": 146,
"overlay2": 103,
"overlay1": 103,
"overlay0": 243,
"surface2": 60,
"surface1": 240,
"surface0": 238,
"base": 236,
"mantle": 235,
"crust": 234,
}
var paletteLatte = map[string]int{
"rosewater": 174,
"flamingo": 174,
"pink": 176,
"mauve": 99,
"red": 161,
"maroon": 167,
"peach": 202,
"yellow": 172,
"green": 70,
"teal": 30,
"sky": 38,
"sapphire": 37,
"blue": 27,
"lavender": 69,
"text": 240,
"subtext1": 60,
"subtext0": 243,
"overlay2": 102,
"overlay1": 246,
"overlay0": 248,
"surface2": 249,
"surface1": 7,
"surface0": 252,
"base": 255,
"mantle": 255,
"crust": 254,
}
var palettes = map[Flavor]map[string]int{
Mocha: paletteMocha,
Frappe: paletteFrappe,
Macchiato: paletteMacchiato,
Latte: paletteLatte,
}
var slots = map[string]string{
"fg": "text",
"bg": "",
"block.bg": "",
"border.bg": "",
"border.fg": "overlay1",
"label.fg": "mauve",
"menu.text.fg": "text",
"menu.text.bg": "",
"menu.border.fg": "mauve",
"menu.label.fg": "mauve",
"header.fg": "crust",
"header.bg": "mauve",
"gauge.bar.bg": "teal",
"gauge.percent.fg": "text",
"linechart.axes.fg": "overlay1",
"linechart.line.fg": "blue",
"mbarchart.bar.bg": "teal",
"mbarchart.num.fg": "text",
"mbarchart.text.fg": "text",
"par.text.fg": "text",
"par.text.bg": "",
"par.text.hi": "crust",
"sparkline.line.fg": "blue",
"sparkline.title.fg": "subtext1",
"status.ok": "green",
"status.warn": "yellow",
"status.danger": "red",
"io.read": "teal",
"io.write": "peach",
"net.rx": "teal",
"net.tx": "peach",
}
var current = Mocha
var opaque = false
func Set(f Flavor) { current = f }
func Current() Flavor { return current }
// SetOpaque toggles whether background slots paint the flavor's base color
// (opaque) or leave the terminal background untouched (transparent).
func SetOpaque(v bool) { opaque = v }
func Color(name string) ui.Attribute {
if p, ok := palettes[current]; ok {
if idx, ok := p[name]; ok {
return ui.Attribute(idx + 1)
}
}
return ui.ColorDefault
}
func Attr(slot string) ui.Attribute {
if name, ok := slots[slot]; ok {
if name == "" && opaque {
return Color("base")
}
return Color(name)
}
return ui.ColorDefault
}
func BuildColorMap() map[string]ui.Attribute {
m := make(map[string]ui.Attribute, len(slots)+4)
for slot := range slots {
m[slot] = Attr(slot)
}
m["par.fg"] = Color("text")
m["par.label.bg"] = Color("surface0")
return m
}
+177
View File
@@ -0,0 +1,177 @@
package theme
import (
"testing"
ui "github.com/gizak/termui"
)
func TestPaletteIndices(t *testing.T) {}
var want = map[Flavor]map[string]int{
Mocha: {
"rosewater": 224,
"flamingo": 224,
"pink": 218,
"mauve": 183,
"red": 211,
"maroon": 181,
"peach": 216,
"yellow": 223,
"green": 151,
"teal": 116,
"sky": 116,
"sapphire": 117,
"blue": 111,
"lavender": 147,
"text": 189,
"subtext1": 146,
"subtext0": 146,
"overlay2": 103,
"overlay1": 103,
"overlay0": 243,
"surface2": 241,
"surface1": 239,
"surface0": 237,
"base": 235,
"mantle": 234,
"crust": 233,
},
Frappe: {
"rosewater": 224,
"flamingo": 217,
"pink": 218,
"mauve": 182,
"red": 174,
"maroon": 175,
"peach": 216,
"yellow": 186,
"green": 150,
"teal": 115,
"sky": 116,
"sapphire": 110,
"blue": 111,
"lavender": 147,
"text": 189,
"subtext1": 146,
"subtext0": 146,
"overlay2": 109,
"overlay1": 103,
"overlay0": 243,
"surface2": 60,
"surface1": 59,
"surface0": 239,
"base": 237,
"mantle": 236,
"crust": 235,
},
Macchiato: {
"rosewater": 224,
"flamingo": 224,
"pink": 218,
"mauve": 183,
"red": 210,
"maroon": 211,
"peach": 216,
"yellow": 223,
"green": 150,
"teal": 116,
"sky": 116,
"sapphire": 116,
"blue": 111,
"lavender": 147,
"text": 189,
"subtext1": 146,
"subtext0": 146,
"overlay2": 103,
"overlay1": 103,
"overlay0": 243,
"surface2": 60,
"surface1": 240,
"surface0": 238,
"base": 236,
"mantle": 235,
"crust": 234,
},
Latte: {
"rosewater": 174,
"flamingo": 174,
"pink": 176,
"mauve": 99,
"red": 161,
"maroon": 167,
"peach": 202,
"yellow": 172,
"green": 70,
"teal": 30,
"sky": 38,
"sapphire": 37,
"blue": 27,
"lavender": 69,
"text": 240,
"subtext1": 60,
"subtext0": 243,
"overlay2": 102,
"overlay1": 246,
"overlay0": 248,
"surface2": 249,
"surface1": 7,
"surface0": 252,
"base": 255,
"mantle": 255,
"crust": 254,
},
}
func TestPaletteColors(t *testing.T) {
for flavor, m := range want {
Set(flavor)
for name, idx := range m {
got := int(Color(name))
if got != idx+1 {
t.Errorf("%s/%s: Color=%d want %d", flavor, name, got, idx+1)
}
}
}
}
func TestSlotsResolve(t *testing.T) {
for _, f := range []Flavor{Mocha, Frappe, Macchiato, Latte} {
Set(f)
m := BuildColorMap()
for slot := range slots {
if _, ok := m[slot]; !ok { t.Fatalf("%s: missing slot %s", f, slot) }
}
}
}
func TestParseFlavor(t *testing.T) {
for _, s := range []string{"mocha","frappe","macchiato","latte"} {
if _, ok := ParseFlavor(s); !ok { t.Errorf("ParseFlavor(%s) failed", s) }
}
if _, ok := ParseFlavor("bogus"); ok { t.Error("bogus flavor accepted") }
}
func TestTransparentSlots(t *testing.T) {
Set(Mocha)
SetOpaque(false)
for _, slot := range []string{"bg", "block.bg", "border.bg", "par.text.bg", "menu.text.bg"} {
if got := Attr(slot); got != ui.ColorDefault {
t.Errorf("transparent %s: got %d want ColorDefault(0)", slot, got)
}
}
}
func TestOpaqueSlots(t *testing.T) {
Set(Mocha)
SetOpaque(true)
base := Color("base")
if base == 0 { t.Fatal("Color(base) should not be ColorDefault") }
for _, slot := range []string{"bg", "block.bg", "border.bg", "par.text.bg", "menu.text.bg"} {
if got := Attr(slot); got != base {
t.Errorf("opaque %s: got %d want base(%d)", slot, got, base)
}
}
SetOpaque(false) // restore
}
var _ = ui.ColorDefault // keep import
+2 -1
View File
@@ -3,6 +3,7 @@ package menu
import ( import (
"sort" "sort"
"github.com/bcicen/ctop/theme"
ui "github.com/gizak/termui" ui "github.com/gizak/termui"
) )
@@ -105,7 +106,7 @@ func (m *Menu) Buffer() ui.Buffer {
for _, ch := range item.Text() { for _, ch := range item.Text() {
// invert bg/fg colors on currently selected row // invert bg/fg colors on currently selected row
if m.Selectable && n == m.cursorPos { if m.Selectable && n == m.cursorPos {
cell = ui.Cell{Ch: ch, Fg: ui.ColorBlack, Bg: m.TextFgColor} cell = ui.Cell{Ch: ch, Fg: theme.Attr("crust"), Bg: m.TextFgColor}
} else { } else {
cell = ui.Cell{Ch: ch, Fg: m.TextFgColor, Bg: m.TextBgColor} cell = ui.Cell{Ch: ch, Fg: m.TextFgColor, Bg: m.TextBgColor}
} }