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.
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user