package app import ( "image/color" "charm.land/lipgloss/v2" ) // Palette is the "Soft" chrome from docs/visual/ref with the blue accent: // rounded borders, opaque dark panels, generous spacing. type Palette struct { Bg color.Color // panel/background fill BgDeep color.Color // outermost backdrop (slightly darker) BgPanel color.Color // inside-panel fill Sel color.Color // selection row background Border color.Color // idle panel border BorderHi color.Color // active panel border Fg color.Color // primary text FgStrong color.Color // headings / emphasis Dim color.Color // labels, secondary text Faint color.Color // tertiary, hints Accent color.Color // blue accent Accent2 color.Color // secondary accent OK color.Color Warn color.Color Bad color.Color DiffAdd color.Color // added-line cell background DiffDel color.Color // removed-line cell background CatLifecycle color.Color CatContext color.Color CatInference color.Color CatTool color.Color CatDomain color.Color CatApproval color.Color } // SoftBlue mirrors the reference Soft theme (#14161a bg, #ced3da fg) with the // blue accent the user chose. var SoftBlue = Palette{ BgDeep: lipgloss.Color("#101216"), Bg: lipgloss.Color("#14161a"), BgPanel: lipgloss.Color("#171a1f"), Sel: lipgloss.Color("#1b2733"), Border: lipgloss.Color("#262b33"), BorderHi: lipgloss.Color("#4a9eff"), Fg: lipgloss.Color("#ced3da"), FgStrong: lipgloss.Color("#e8edf2"), Dim: lipgloss.Color("#7c8794"), Faint: lipgloss.Color("#525c68"), Accent: lipgloss.Color("#4a9eff"), Accent2: lipgloss.Color("#56c8d8"), OK: lipgloss.Color("#7fd88f"), Warn: lipgloss.Color("#e8c06a"), Bad: lipgloss.Color("#e87f7f"), DiffAdd: lipgloss.Color("#1b2a1d"), DiffDel: lipgloss.Color("#2c1c1f"), CatLifecycle: lipgloss.Color("#c98fd9"), CatContext: lipgloss.Color("#b9c0c9"), CatInference: lipgloss.Color("#56c8d8"), CatTool: lipgloss.Color("#8fd96a"), CatDomain: lipgloss.Color("#e8b765"), CatApproval: lipgloss.Color("#e8c06a"), } // Theme holds prebuilt lipgloss styles derived from a Palette. Every style sets // a Background so the panels are fully opaque (no terminal wallpaper bleed). type Theme struct { P Palette Screen lipgloss.Style // full backdrop fill Panel lipgloss.Style // idle rounded panel PanelHi lipgloss.Style // active rounded panel PanelTitle lipgloss.Style // uppercase dim header label TitleHi lipgloss.Style // active header label Text lipgloss.Style Strong lipgloss.Style Dim lipgloss.Style Faint lipgloss.Style Accent lipgloss.Style SelRow lipgloss.Style Status lipgloss.Style // top bar Footer lipgloss.Style // bottom hints Input lipgloss.Style // input bar panel Overlay lipgloss.Style // modal box Scrim lipgloss.Style // dim backdrop behind modals } // NewTheme builds the style set for a palette. func NewTheme(p Palette) Theme { base := lipgloss.NewStyle().Background(p.Bg).Foreground(p.Fg) roundBase := func(border color.Color) lipgloss.Style { return lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(border). BorderBackground(p.Bg). Background(p.Bg). Foreground(p.Fg). Padding(0, 1) } return Theme{ P: p, Screen: lipgloss.NewStyle().Background(p.BgDeep).Foreground(p.Fg), Panel: roundBase(p.Border), PanelHi: roundBase(p.BorderHi), PanelTitle: lipgloss.NewStyle().Background(p.Bg).Foreground(p.Dim).Bold(true), TitleHi: lipgloss.NewStyle().Background(p.Bg).Foreground(p.Accent).Bold(true), Text: base, Strong: lipgloss.NewStyle().Background(p.Bg).Foreground(p.FgStrong).Bold(true), Dim: lipgloss.NewStyle().Background(p.Bg).Foreground(p.Dim), Faint: lipgloss.NewStyle().Background(p.Bg).Foreground(p.Faint), Accent: lipgloss.NewStyle().Background(p.Bg).Foreground(p.Accent).Bold(true), SelRow: lipgloss.NewStyle().Background(p.Sel).Foreground(p.FgStrong), Status: lipgloss.NewStyle().Background(p.BgPanel).Foreground(p.Dim), Footer: lipgloss.NewStyle().Background(p.BgDeep).Foreground(p.Faint), Input: roundBase(p.BorderHi), Overlay: lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(p.Accent). BorderBackground(p.BgPanel). Background(p.BgPanel). Foreground(p.Fg). Padding(1, 2), Scrim: lipgloss.NewStyle().Background(p.BgDeep).Foreground(p.Faint), } } // categoryColor maps an event category label to its accent color. func (t Theme) categoryColor(cat string) color.Color { switch cat { case "Lifecycle": return t.P.CatLifecycle case "Context": return t.P.CatContext case "Inference": return t.P.CatInference case "Tool": return t.P.CatTool case "Domain": return t.P.CatDomain case "Approval": return t.P.CatApproval default: return t.P.Dim } }