205 lines
4.7 KiB
Go
205 lines
4.7 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
|
|
Muted lipgloss.Style
|
|
Activity lipgloss.Style
|
|
ModelMeta lipgloss.Style
|
|
Footer lipgloss.Style
|
|
Viewport lipgloss.Style
|
|
InputBox lipgloss.Style
|
|
CommandHint lipgloss.Style
|
|
UserLabel 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)),
|
|
|
|
Muted: lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color(t.Muted)),
|
|
|
|
Activity: lipgloss.NewStyle().
|
|
Bold(true).
|
|
Foreground(lipgloss.Color(t.Background)).
|
|
Background(lipgloss.Color(t.Title)).
|
|
Padding(0, 1),
|
|
|
|
ModelMeta: lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color(t.Text)).
|
|
Background(lipgloss.Color(t.SurfaceAlt)).
|
|
Padding(0, 1),
|
|
|
|
Footer: lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color(t.Muted)).
|
|
Padding(0, 1),
|
|
|
|
Viewport: lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color(t.Text)),
|
|
|
|
InputBox: lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color(t.Text)).
|
|
Border(lipgloss.ThickBorder()).
|
|
BorderForeground(lipgloss.Color(t.Border)).
|
|
Padding(0, 2),
|
|
|
|
CommandHint: lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color(t.Muted)).
|
|
Background(lipgloss.Color(t.SurfaceAlt)).
|
|
Padding(0, 1),
|
|
|
|
UserLabel: labelStyle(t.User),
|
|
ToolLabel: labelStyle(t.Tool),
|
|
ErrorLabel: labelStyle(t.Error),
|
|
SystemLabel: labelStyle(t.System),
|
|
|
|
UserMessage: bubbleMessageStyle(t.Text, t.SurfaceAlt),
|
|
AssistantMsg: assistantMessageStyle(t.Text),
|
|
ToolMessage: messageStyle(t.Text),
|
|
ErrorMessage: messageStyle(t.Error),
|
|
SystemMessage: lipgloss.NewStyle().Foreground(lipgloss.Color(t.System)).Padding(0, 1),
|
|
}
|
|
}
|
|
|
|
func labelStyle(color string) lipgloss.Style {
|
|
return lipgloss.NewStyle().
|
|
Bold(true).
|
|
Foreground(lipgloss.Color(color))
|
|
}
|
|
|
|
func messageStyle(textColor string) lipgloss.Style {
|
|
return lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color(textColor)).
|
|
Padding(0, 1)
|
|
}
|
|
|
|
func assistantMessageStyle(textColor string) lipgloss.Style {
|
|
return lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color(textColor)).
|
|
Padding(1, 2)
|
|
}
|
|
|
|
func bubbleMessageStyle(textColor, backgroundColor string) lipgloss.Style {
|
|
return lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color(textColor)).
|
|
Background(lipgloss.Color(backgroundColor)).
|
|
Padding(1, 2)
|
|
}
|
|
|
|
func applyTextareaTheme(input *textarea.Model, t theme) {
|
|
focused := textarea.Style{
|
|
Base: lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color(t.Text)),
|
|
CursorLine: lipgloss.NewStyle(),
|
|
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()
|
|
|
|
input.FocusedStyle = focused
|
|
input.BlurredStyle = blurred
|
|
}
|