fancy theme

This commit is contained in:
2026-11-04 17:18:09 +01:00
parent e6ca4aef8f
commit d0860c26ad
11 changed files with 351 additions and 44 deletions
+89
View File
@@ -0,0 +1,89 @@
package main
import (
"os"
"strings"
"github.com/charmbracelet/lipgloss"
"github.com/mattn/go-runewidth"
)
// A restrained, high-contrast palette. Text always includes its meaning, so
// colors supplement the UI rather than carrying state by themselves.
type uiTheme struct {
title, accent, muted, warning, success, active, selected lipgloss.Style
border lipgloss.Style
plain bool
}
func newTheme() uiTheme {
t := uiTheme{plain: os.Getenv("NO_COLOR") != "" || strings.EqualFold(os.Getenv("TERM"), "dumb")}
if t.plain {
t.title = lipgloss.NewStyle()
t.accent = lipgloss.NewStyle()
t.muted = lipgloss.NewStyle()
t.warning = lipgloss.NewStyle()
t.success = lipgloss.NewStyle()
t.active = lipgloss.NewStyle()
t.selected = lipgloss.NewStyle()
t.border = lipgloss.NewStyle()
return t
}
t.title = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("#D7E9EF"))
t.accent = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("#70D6D1"))
t.muted = lipgloss.NewStyle().Foreground(lipgloss.Color("#8FA6B2"))
t.warning = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("#FFC078"))
t.success = lipgloss.NewStyle().Foreground(lipgloss.Color("#A8DDA2"))
t.active = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFC078"))
t.selected = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("#F4FAFC")).Background(lipgloss.Color("#244553"))
t.border = lipgloss.NewStyle().Foreground(lipgloss.Color("#426573"))
return t
}
func (t uiTheme) status(status string) string {
label := "[" + safe(status) + "]"
switch strings.ToLower(status) {
case "closed", "done":
return t.muted.Render(label)
case "open":
return t.success.Render(label)
default:
return t.active.Render(label)
}
}
func (t uiTheme) heading(section string, count string, width int) string {
prefix := "▌ TODOAPP / "
section = runewidth.Truncate(safe(section), max(1, width-runewidth.StringWidth(prefix)), "…")
return t.accent.Render("▌ ") + t.title.Render("TODOAPP") + t.muted.Render(" / "+section) + "\n" +
t.muted.Render(runewidth.Truncate(safe(count), max(1, width), "…"))
}
// box frames content only when the terminal has room for a border. The width
// argument is the terminal width, not the inner content width.
func (t uiTheme) box(content string, width, height int) string {
if width < 50 || height < 14 || t.plain {
return content
}
return t.border.Border(lipgloss.RoundedBorder()).Padding(0, 1).Width(min(width, 100) - 2).Render(content)
}
func (t uiTheme) row(label string, selected bool, width int) string {
prefix := " "
if selected {
prefix = "› "
}
text := runewidth.Truncate(prefix+label, max(1, width), "…")
if selected {
return t.selected.Width(min(width, 100)).Render(text)
}
return text
}
func (t uiTheme) hint(text string, width int) string {
return t.muted.Render(runewidth.Truncate(text, max(1, width), "…"))
}
func (t uiTheme) alert(text string, width int) string {
return t.warning.Render(runewidth.Truncate("! "+safe(text), max(1, width), "…"))
}