Vectrum
Sign In
77 lines · 3.5 KB · Go
// Package tui is a full-screen terminal front end built on Bubble Tea.
//
// It observes the same agent.Event stream the plain renderer does and answers
// the same approval.Prompt signature, so the dependency direction is unchanged:
// tui imports agent, never the reverse, and the eval runner still drives the
// loop with no terminal attached.
package tui

import "github.com/charmbracelet/lipgloss"

// The palette is warm rather than neutral: one coral accent carries the
// identity and everything else is a grey at some distance from the text
// colour. Every colour is adaptive, because a palette tuned only for a dark
// terminal is the usual way a hand-rolled TUI becomes unreadable on a light
// one.
var (
	colCoral  = lipgloss.AdaptiveColor{Light: "#b4532f", Dark: "#d97757"}
	colText   = lipgloss.AdaptiveColor{Light: "#1c1b1a", Dark: "#e8e6e3"}
	colUser   = lipgloss.AdaptiveColor{Light: "#4b4945", Dark: "#b3ada4"}
	colDim    = lipgloss.AdaptiveColor{Light: "#6f6a66", Dark: "#8a847d"}
	colFaint  = lipgloss.AdaptiveColor{Light: "#c9c4be", Dark: "#3f3b38"}
	colOK     = lipgloss.AdaptiveColor{Light: "#2f7d46", Dark: "#5dbb7a"}
	colErr    = lipgloss.AdaptiveColor{Light: "#b3261e", Dark: "#eb6f6f"}
	colWarn   = lipgloss.AdaptiveColor{Light: "#8a5a00", Dark: "#e0a458"}
	colBlue   = lipgloss.AdaptiveColor{Light: "#2f5fa8", Dark: "#7aa2e3"}
	colPillBg = lipgloss.AdaptiveColor{Light: "#ebe7e2", Dark: "#2b2825"}
)

var (
	styText  = lipgloss.NewStyle().Foreground(colText)
	styDim   = lipgloss.NewStyle().Foreground(colDim)
	styFaint = lipgloss.NewStyle().Foreground(colFaint)
	styCoral = lipgloss.NewStyle().Foreground(colCoral)
	styBold  = lipgloss.NewStyle().Bold(true)
	styErr   = lipgloss.NewStyle().Foreground(colErr)
	styWarn  = lipgloss.NewStyle().Foreground(colWarn)
	styOK    = lipgloss.NewStyle().Foreground(colOK)

	// The transcript's grammar: every turn of events opens with a dot, and
	// anything hanging off it is indented under that dot.
	styUserMark = lipgloss.NewStyle().Foreground(colDim)
	styUserText = lipgloss.NewStyle().Foreground(colUser)
	styToolDot  = lipgloss.NewStyle().Foreground(colCoral)
	styTextDot  = lipgloss.NewStyle().Foreground(colText)
	styErrDot   = lipgloss.NewStyle().Foreground(colErr)
	styToolName = lipgloss.NewStyle().Foreground(colText).Bold(true)
	styToolArgs = lipgloss.NewStyle().Foreground(colDim)
	styResult   = lipgloss.NewStyle().Foreground(colDim)

	styDiffAdd  = lipgloss.NewStyle().Foreground(colOK)
	styDiffDel  = lipgloss.NewStyle().Foreground(colErr)
	styDiffHunk = lipgloss.NewStyle().Foreground(colCoral)
	styDiffHead = lipgloss.NewStyle().Foreground(colDim).Bold(true)

	styName    = lipgloss.NewStyle().Foreground(colBlue).Background(colPillBg).Padding(0, 1)
	styPill    = lipgloss.NewStyle().Foreground(colDim).Background(colPillBg).Padding(0, 1)
	styKey     = lipgloss.NewStyle().Foreground(colCoral).Bold(true)
	styHeading = lipgloss.NewStyle().Foreground(colCoral).Bold(true)

	// The input frame is the only always-present chrome, so it carries the
	// mode: coral when it is yours to type into, faint while the agent has it.
	styBoxFocus = lipgloss.NewStyle().
			Border(lipgloss.RoundedBorder()).
			BorderForeground(colCoral).
			Padding(0, 1)
	styBoxBlur = lipgloss.NewStyle().
			Border(lipgloss.RoundedBorder()).
			BorderForeground(colFaint).
			Padding(0, 1)

	// The approval box is the one place the UI is allowed to shout: it is the
	// only moment a human has to decide something.
	styModal = lipgloss.NewStyle().
			Border(lipgloss.RoundedBorder()).
			BorderForeground(colCoral).
			Padding(0, 1)
)