Files
agentu/internal/tui/theme.go
T
loveuer a431d1ec95 v0.0.1: agentu mvp
Add OpenAI-compatible providers, TUI, slash commands, and model switching.
Add local file tools plus optional yolo shell execution.
Document config, usage, and development workflow.
2026-06-22 22:40:38 +08:00

200 lines
4.8 KiB
Go

package tui
import (
"fmt"
"strings"
"github.com/charmbracelet/bubbles/textarea"
"github.com/charmbracelet/lipgloss"
)
type ThemeMode string
const (
ThemeLight ThemeMode = "light"
ThemeDark ThemeMode = "dark"
)
func ParseThemeMode(value string) (ThemeMode, error) {
switch ThemeMode(strings.ToLower(strings.TrimSpace(value))) {
case "", ThemeLight:
return ThemeLight, nil
case ThemeDark:
return ThemeDark, nil
default:
return "", fmt.Errorf("unsupported theme %q; expected light or dark", value)
}
}
type theme struct {
Mode ThemeMode
Background string
Surface string
SurfaceAlt string
Border string
Text string
Muted string
Title string
User string
Assistant string
Tool string
Error string
System string
}
type styles struct {
App lipgloss.Style
Header lipgloss.Style
Title lipgloss.Style
Muted lipgloss.Style
Status lipgloss.Style
Viewport lipgloss.Style
InputBox lipgloss.Style
CommandHint lipgloss.Style
UserLabel lipgloss.Style
AssistantLabel lipgloss.Style
ToolLabel lipgloss.Style
ErrorLabel lipgloss.Style
SystemLabel lipgloss.Style
UserMessage lipgloss.Style
AssistantMsg lipgloss.Style
ToolMessage lipgloss.Style
ErrorMessage lipgloss.Style
SystemMessage lipgloss.Style
}
func themeForMode(mode ThemeMode) theme {
if mode == ThemeDark {
return darkTheme()
}
return lightTheme()
}
func lightTheme() theme {
return theme{
Mode: ThemeLight,
Background: "#F8FAFC",
Surface: "#FFFFFF",
SurfaceAlt: "#EEF2F7",
Border: "#CBD5E1",
Text: "#111827",
Muted: "#64748B",
Title: "#0F766E",
User: "#2563EB",
Assistant: "#0F766E",
Tool: "#B45309",
Error: "#DC2626",
System: "#64748B",
}
}
func darkTheme() theme {
return theme{
Mode: ThemeDark,
Background: "#111827",
Surface: "#1F2937",
SurfaceAlt: "#0F172A",
Border: "#475569",
Text: "#E5E7EB",
Muted: "#94A3B8",
Title: "#5EEAD4",
User: "#93C5FD",
Assistant: "#5EEAD4",
Tool: "#FBBF24",
Error: "#FCA5A5",
System: "#94A3B8",
}
}
func (t theme) styles() styles {
return styles{
App: lipgloss.NewStyle().
Foreground(lipgloss.Color(t.Text)).
Background(lipgloss.Color(t.Background)),
Header: lipgloss.NewStyle().
Foreground(lipgloss.Color(t.Text)).
Background(lipgloss.Color(t.SurfaceAlt)).
Padding(0, 1),
Title: lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color(t.Title)),
Muted: lipgloss.NewStyle().
Foreground(lipgloss.Color(t.Muted)),
Status: lipgloss.NewStyle().
Foreground(lipgloss.Color(t.Muted)).
Background(lipgloss.Color(t.SurfaceAlt)).
Padding(0, 1),
Viewport: lipgloss.NewStyle().
Foreground(lipgloss.Color(t.Text)).
Background(lipgloss.Color(t.Background)),
InputBox: lipgloss.NewStyle().
Foreground(lipgloss.Color(t.Text)).
Background(lipgloss.Color(t.Surface)).
Border(lipgloss.NormalBorder()).
BorderForeground(lipgloss.Color(t.Border)).
Padding(0, 1),
CommandHint: lipgloss.NewStyle().
Foreground(lipgloss.Color(t.Muted)).
Background(lipgloss.Color(t.SurfaceAlt)).
Padding(0, 1),
UserLabel: labelStyle(t.User),
AssistantLabel: labelStyle(t.Assistant),
ToolLabel: labelStyle(t.Tool),
ErrorLabel: labelStyle(t.Error),
SystemLabel: labelStyle(t.System),
UserMessage: messageStyle(t.Text, t.User),
AssistantMsg: messageStyle(t.Text, t.Assistant),
ToolMessage: messageStyle(t.Text, t.Tool),
ErrorMessage: messageStyle(t.Error, t.Error),
SystemMessage: lipgloss.NewStyle().Foreground(lipgloss.Color(t.System)).PaddingLeft(1),
}
}
func labelStyle(color string) lipgloss.Style {
return lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color(color))
}
func messageStyle(textColor, accentColor string) lipgloss.Style {
return lipgloss.NewStyle().
Foreground(lipgloss.Color(textColor)).
Border(lipgloss.ThickBorder(), false, false, false, true).
BorderForeground(lipgloss.Color(accentColor)).
PaddingLeft(1)
}
func applyTextareaTheme(input *textarea.Model, t theme) {
focused := textarea.Style{
Base: lipgloss.NewStyle().
Foreground(lipgloss.Color(t.Text)).
Background(lipgloss.Color(t.Surface)),
CursorLine: lipgloss.NewStyle().
Background(lipgloss.Color(t.Surface)),
Placeholder: lipgloss.NewStyle().
Foreground(lipgloss.Color(t.Muted)),
Prompt: lipgloss.NewStyle().
Foreground(lipgloss.Color(t.Muted)),
Text: lipgloss.NewStyle().
Foreground(lipgloss.Color(t.Text)),
EndOfBuffer: lipgloss.NewStyle().
Foreground(lipgloss.Color(t.Surface)),
}
blurred := focused
blurred.CursorLine = lipgloss.NewStyle().Background(lipgloss.Color(t.Surface))
input.FocusedStyle = focused
input.BlurredStyle = blurred
}