Files
ctop/theme/theme.go
T
ahp 78e85dc4df 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.
2026-09-14 14:06:49 +03:30

218 lines
4.6 KiB
Go

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
}