// Package theme is glint's single source of color truth. Every styled span in // every theme gets an explicit foreground — no terminal-default fallbacks — so // the editor reads cleanly on both light and dark terminals. package theme import "github.com/charmbracelet/lipgloss" // Theme holds every color glint paints, plus its name and the glamour style the // read-preview should use to stay visually in sync. type Theme struct { Name string GlamourStyle string // Markdown element colors. Text lipgloss.Color // base prose Emphasis lipgloss.Color // bold/italic — higher contrast than Text Heading lipgloss.Color // heading text (bold) Code lipgloss.Color // inline + fenced code Link lipgloss.Color // links, URLs, and wikilink targets Wikilink lipgloss.Color // retained == Link (kept for the all-colors check) ListMarker lipgloss.Color // list bullets / numbers Blockquote lipgloss.Color // blockquote marker + border (muted tone) Comment lipgloss.Color // HTML / %% comments — visible, not dimmed Accent lipgloss.Color // frontmatter keys, selection Highlight lipgloss.Color // ==highlight== background tint Spell lipgloss.Color // misspelled-word undercurl (red) // UI colors. Background lipgloss.Color Muted lipgloss.Color // markup punctuation, dimmed StatusFg lipgloss.Color StatusBg lipgloss.Color SelFg lipgloss.Color SelBg lipgloss.Color Pointer lipgloss.Color } // CycleOrder is the order Ctrl+T steps through themes. var CycleOrder = []string{"flexoki-light", "flexoki-dark", "charm"} func registry() map[string]Theme { return map[string]Theme{ "flexoki-light": FlexokiLight(), "flexoki-dark": FlexokiDark(), "charm": Charm(), } } // ByName looks up a registered theme. func ByName(name string) (Theme, bool) { t, ok := registry()[name] return t, ok } // Next returns the theme after name in CycleOrder, wrapping around. An unknown // name yields the first theme in the cycle. func Next(name string) Theme { idx := 0 for i, n := range CycleOrder { if n == name { idx = (i + 1) % len(CycleOrder) break } } t, _ := ByName(CycleOrder[idx]) return t } // Resolve picks a theme from a config value: "auto"/"" → OS detection; a known // name → that theme; an unknown name → OS detection. It never errors and never // returns an empty theme. func Resolve(configValue string) Theme { if configValue == "" || configValue == "auto" { t, _ := ByName(Detect()) return t } if t, ok := ByName(configValue); ok { return t } t, _ := ByName(Detect()) return t }