// 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) )