d247b19608
Move the Go TUI from github.com/charmbracelet/{bubbletea,lipgloss} to the v2
ecosystem at charm.land/{bubbletea,lipgloss}/v2 (Go 1.25). v2's kitty keyboard
disambiguation is on by default, so Shift+Enter now reliably inserts a newline
in the composer (Ctrl+J / Alt+Enter kept as fallbacks for non-kitty terminals).
Approach: rather than rewrite ~190 key-match sites to v2's (Code, Mod) idiom, a
small shim (internal/app/key.go) converts a v2 KeyPressMsg into the v1-shaped
keyMsg the handlers already expect, at the single Update boundary. The rest is
mechanical:
- key constants tea.Key* → shim consts; tea.KeyMsg → keyMsg.
- lipgloss.Color is now a func returning color.Color, not a type → fields/params
retyped to image/color.Color (theme/diff/overlays/view).
- v2 View() returns tea.View: render() builds the string, View() wraps it and
carries AltScreen (alt-screen is a per-frame View field now, not a program opt).
- WithWhitespaceBackground/Foreground → WithWhitespaceStyle(Style).
- preview: drop lipgloss.SetColorProfile (v2 renders truecolor by default).
Build, vet, gofmt, and the full test suite are green; preview renders truecolor
across kinds.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
153 lines
4.8 KiB
Go
153 lines
4.8 KiB
Go
package app
|
|
|
|
import (
|
|
"image/color"
|
|
|
|
"charm.land/lipgloss/v2"
|
|
)
|
|
|
|
// Palette is the "Soft" chrome from docs/visual/ref with the blue accent:
|
|
// rounded borders, opaque dark panels, generous spacing.
|
|
type Palette struct {
|
|
Bg color.Color // panel/background fill
|
|
BgDeep color.Color // outermost backdrop (slightly darker)
|
|
BgPanel color.Color // inside-panel fill
|
|
Sel color.Color // selection row background
|
|
Border color.Color // idle panel border
|
|
BorderHi color.Color // active panel border
|
|
Fg color.Color // primary text
|
|
FgStrong color.Color // headings / emphasis
|
|
Dim color.Color // labels, secondary text
|
|
Faint color.Color // tertiary, hints
|
|
Accent color.Color // blue accent
|
|
Accent2 color.Color // secondary accent
|
|
|
|
OK color.Color
|
|
Warn color.Color
|
|
Bad color.Color
|
|
|
|
DiffAdd color.Color // added-line cell background
|
|
DiffDel color.Color // removed-line cell background
|
|
|
|
CatLifecycle color.Color
|
|
CatContext color.Color
|
|
CatInference color.Color
|
|
CatTool color.Color
|
|
CatDomain color.Color
|
|
CatApproval color.Color
|
|
}
|
|
|
|
// SoftBlue mirrors the reference Soft theme (#14161a bg, #ced3da fg) with the
|
|
// blue accent the user chose.
|
|
var SoftBlue = Palette{
|
|
BgDeep: lipgloss.Color("#101216"),
|
|
Bg: lipgloss.Color("#14161a"),
|
|
BgPanel: lipgloss.Color("#171a1f"),
|
|
Sel: lipgloss.Color("#1b2733"),
|
|
Border: lipgloss.Color("#262b33"),
|
|
BorderHi: lipgloss.Color("#4a9eff"),
|
|
Fg: lipgloss.Color("#ced3da"),
|
|
FgStrong: lipgloss.Color("#e8edf2"),
|
|
Dim: lipgloss.Color("#7c8794"),
|
|
Faint: lipgloss.Color("#525c68"),
|
|
Accent: lipgloss.Color("#4a9eff"),
|
|
Accent2: lipgloss.Color("#56c8d8"),
|
|
|
|
OK: lipgloss.Color("#7fd88f"),
|
|
Warn: lipgloss.Color("#e8c06a"),
|
|
Bad: lipgloss.Color("#e87f7f"),
|
|
|
|
DiffAdd: lipgloss.Color("#1b2a1d"),
|
|
DiffDel: lipgloss.Color("#2c1c1f"),
|
|
|
|
CatLifecycle: lipgloss.Color("#c98fd9"),
|
|
CatContext: lipgloss.Color("#b9c0c9"),
|
|
CatInference: lipgloss.Color("#56c8d8"),
|
|
CatTool: lipgloss.Color("#8fd96a"),
|
|
CatDomain: lipgloss.Color("#e8b765"),
|
|
CatApproval: lipgloss.Color("#e8c06a"),
|
|
}
|
|
|
|
// Theme holds prebuilt lipgloss styles derived from a Palette. Every style sets
|
|
// a Background so the panels are fully opaque (no terminal wallpaper bleed).
|
|
type Theme struct {
|
|
P Palette
|
|
|
|
Screen lipgloss.Style // full backdrop fill
|
|
Panel lipgloss.Style // idle rounded panel
|
|
PanelHi lipgloss.Style // active rounded panel
|
|
PanelTitle lipgloss.Style // uppercase dim header label
|
|
TitleHi lipgloss.Style // active header label
|
|
|
|
Text lipgloss.Style
|
|
Strong lipgloss.Style
|
|
Dim lipgloss.Style
|
|
Faint lipgloss.Style
|
|
Accent lipgloss.Style
|
|
SelRow lipgloss.Style
|
|
Status lipgloss.Style // top bar
|
|
Footer lipgloss.Style // bottom hints
|
|
Input lipgloss.Style // input bar panel
|
|
Overlay lipgloss.Style // modal box
|
|
Scrim lipgloss.Style // dim backdrop behind modals
|
|
}
|
|
|
|
// NewTheme builds the style set for a palette.
|
|
func NewTheme(p Palette) Theme {
|
|
base := lipgloss.NewStyle().Background(p.Bg).Foreground(p.Fg)
|
|
roundBase := func(border color.Color) lipgloss.Style {
|
|
return lipgloss.NewStyle().
|
|
Border(lipgloss.RoundedBorder()).
|
|
BorderForeground(border).
|
|
BorderBackground(p.Bg).
|
|
Background(p.Bg).
|
|
Foreground(p.Fg).
|
|
Padding(0, 1)
|
|
}
|
|
return Theme{
|
|
P: p,
|
|
Screen: lipgloss.NewStyle().Background(p.BgDeep).Foreground(p.Fg),
|
|
Panel: roundBase(p.Border),
|
|
PanelHi: roundBase(p.BorderHi),
|
|
PanelTitle: lipgloss.NewStyle().Background(p.Bg).Foreground(p.Dim).Bold(true),
|
|
TitleHi: lipgloss.NewStyle().Background(p.Bg).Foreground(p.Accent).Bold(true),
|
|
Text: base,
|
|
Strong: lipgloss.NewStyle().Background(p.Bg).Foreground(p.FgStrong).Bold(true),
|
|
Dim: lipgloss.NewStyle().Background(p.Bg).Foreground(p.Dim),
|
|
Faint: lipgloss.NewStyle().Background(p.Bg).Foreground(p.Faint),
|
|
Accent: lipgloss.NewStyle().Background(p.Bg).Foreground(p.Accent).Bold(true),
|
|
SelRow: lipgloss.NewStyle().Background(p.Sel).Foreground(p.FgStrong),
|
|
Status: lipgloss.NewStyle().Background(p.BgPanel).Foreground(p.Dim),
|
|
Footer: lipgloss.NewStyle().Background(p.BgDeep).Foreground(p.Faint),
|
|
Input: roundBase(p.BorderHi),
|
|
Overlay: lipgloss.NewStyle().
|
|
Border(lipgloss.RoundedBorder()).
|
|
BorderForeground(p.Accent).
|
|
BorderBackground(p.BgPanel).
|
|
Background(p.BgPanel).
|
|
Foreground(p.Fg).
|
|
Padding(1, 2),
|
|
Scrim: lipgloss.NewStyle().Background(p.BgDeep).Foreground(p.Faint),
|
|
}
|
|
}
|
|
|
|
// categoryColor maps an event category label to its accent color.
|
|
func (t Theme) categoryColor(cat string) color.Color {
|
|
switch cat {
|
|
case "Lifecycle":
|
|
return t.P.CatLifecycle
|
|
case "Context":
|
|
return t.P.CatContext
|
|
case "Inference":
|
|
return t.P.CatInference
|
|
case "Tool":
|
|
return t.P.CatTool
|
|
case "Domain":
|
|
return t.P.CatDomain
|
|
case "Approval":
|
|
return t.P.CatApproval
|
|
default:
|
|
return t.P.Dim
|
|
}
|
|
}
|