fix: colors in a TTY with TMUX (#356)

Drew Smirnoff created

Change summary

theme/theme.go      | 30 +++++++++++-----------
tui/composer.go     |  9 +++++++
tui/filepicker.go   |  1 
tui/login.go        |  2 +
tui/mailing_list.go |  4 +++
tui/settings.go     |  4 +++
tui/signature.go    |  1 
tui/theme.go        | 59 +++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 95 insertions(+), 15 deletions(-)

Detailed changes

theme/theme.go 🔗

@@ -72,9 +72,9 @@ var Matcha = Theme{
 	Accent:     lipgloss.Color("42"),
 	AccentDark: lipgloss.Color("#25A065"),
 	AccentText: lipgloss.Color("#FFFDF5"),
-	Secondary:  lipgloss.Color("240"),
-	SubtleText: lipgloss.Color("241"),
-	MutedText:  lipgloss.Color("243"),
+	Secondary:  lipgloss.Color("244"),
+	SubtleText: lipgloss.Color("245"),
+	MutedText:  lipgloss.Color("247"),
 	DimText:    lipgloss.Color("250"),
 	Danger:     lipgloss.Color("196"),
 	Warning:    lipgloss.Color("208"),
@@ -89,9 +89,9 @@ var Rose = Theme{
 	Accent:     lipgloss.Color("#E8729B"),
 	AccentDark: lipgloss.Color("#B5547A"),
 	AccentText: lipgloss.Color("#FFFDF5"),
-	Secondary:  lipgloss.Color("240"),
-	SubtleText: lipgloss.Color("241"),
-	MutedText:  lipgloss.Color("243"),
+	Secondary:  lipgloss.Color("244"),
+	SubtleText: lipgloss.Color("245"),
+	MutedText:  lipgloss.Color("247"),
 	DimText:    lipgloss.Color("250"),
 	Danger:     lipgloss.Color("196"),
 	Warning:    lipgloss.Color("208"),
@@ -106,9 +106,9 @@ var Lavender = Theme{
 	Accent:     lipgloss.Color("#B4A7D6"),
 	AccentDark: lipgloss.Color("#8E7CC3"),
 	AccentText: lipgloss.Color("#FFFDF5"),
-	Secondary:  lipgloss.Color("240"),
-	SubtleText: lipgloss.Color("241"),
-	MutedText:  lipgloss.Color("243"),
+	Secondary:  lipgloss.Color("244"),
+	SubtleText: lipgloss.Color("245"),
+	MutedText:  lipgloss.Color("247"),
 	DimText:    lipgloss.Color("250"),
 	Danger:     lipgloss.Color("196"),
 	Warning:    lipgloss.Color("208"),
@@ -123,9 +123,9 @@ var Ocean = Theme{
 	Accent:     lipgloss.Color("#5B9BD5"),
 	AccentDark: lipgloss.Color("#3A7BBF"),
 	AccentText: lipgloss.Color("#FFFDF5"),
-	Secondary:  lipgloss.Color("240"),
-	SubtleText: lipgloss.Color("241"),
-	MutedText:  lipgloss.Color("243"),
+	Secondary:  lipgloss.Color("244"),
+	SubtleText: lipgloss.Color("245"),
+	MutedText:  lipgloss.Color("247"),
 	DimText:    lipgloss.Color("250"),
 	Danger:     lipgloss.Color("196"),
 	Warning:    lipgloss.Color("208"),
@@ -140,9 +140,9 @@ var Peach = Theme{
 	Accent:     lipgloss.Color("#FAB387"),
 	AccentDark: lipgloss.Color("#E0956E"),
 	AccentText: lipgloss.Color("#1E1E2E"),
-	Secondary:  lipgloss.Color("240"),
-	SubtleText: lipgloss.Color("241"),
-	MutedText:  lipgloss.Color("243"),
+	Secondary:  lipgloss.Color("244"),
+	SubtleText: lipgloss.Color("245"),
+	MutedText:  lipgloss.Color("247"),
 	DimText:    lipgloss.Color("250"),
 	Danger:     lipgloss.Color("#F38BA8"),
 	Warning:    lipgloss.Color("#F9E2AF"),

tui/composer.go 🔗

@@ -91,38 +91,47 @@ func NewComposer(from, to, subject, body string, hideTips bool) *Composer {
 		hideTips: hideTips,
 	}
 
+	tiStyles := ThemedTextInputStyles()
+	taStyles := ThemedTextAreaStyles()
+
 	m.toInput = textinput.New()
 	m.toInput.Placeholder = "To"
 	m.toInput.SetValue(to)
 	m.toInput.Prompt = "> "
 	m.toInput.CharLimit = 256
+	m.toInput.SetStyles(tiStyles)
 
 	m.ccInput = textinput.New()
 	m.ccInput.Placeholder = "Cc"
 	m.ccInput.Prompt = "> "
 	m.ccInput.CharLimit = 256
+	m.ccInput.SetStyles(tiStyles)
 
 	m.bccInput = textinput.New()
 	m.bccInput.Placeholder = "Bcc"
 	m.bccInput.Prompt = "> "
 	m.bccInput.CharLimit = 256
+	m.bccInput.SetStyles(tiStyles)
 
 	m.subjectInput = textinput.New()
 	m.subjectInput.Placeholder = "Subject"
 	m.subjectInput.SetValue(subject)
 	m.subjectInput.Prompt = "> "
 	m.subjectInput.CharLimit = 256
+	m.subjectInput.SetStyles(tiStyles)
 
 	m.bodyInput = textarea.New()
 	m.bodyInput.Placeholder = "Body (Markdown supported)..."
 	m.bodyInput.SetValue(body)
 	m.bodyInput.Prompt = "> "
 	m.bodyInput.SetHeight(10)
+	m.bodyInput.SetStyles(taStyles)
 
 	m.signatureInput = textarea.New()
 	m.signatureInput.Placeholder = "Signature (optional)..."
 	m.signatureInput.Prompt = "> "
 	m.signatureInput.SetHeight(3)
+	m.signatureInput.SetStyles(taStyles)
 	// Load default signature
 	if sig, err := config.LoadSignature(); err == nil && sig != "" {
 		m.signatureInput.SetValue(sig)

tui/filepicker.go 🔗

@@ -35,6 +35,7 @@ func NewFilePicker(startPath string) *FilePicker {
 	pi.Placeholder = "Type a path and press Enter..."
 	pi.Prompt = "Go to: "
 	pi.CharLimit = 512
+	pi.SetStyles(ThemedTextInputStyles())
 
 	fp := &FilePicker{
 		currentPath: startPath,

tui/login.go 🔗

@@ -40,10 +40,12 @@ func NewLogin(hideTips bool) *Login {
 		hideTips: hideTips,
 	}
 
+	tiStyles := ThemedTextInputStyles()
 	var t textinput.Model
 	for i := range m.inputs {
 		t = textinput.New()
 		t.CharLimit = 128
+		t.SetStyles(tiStyles)
 
 		switch i {
 		case inputProvider:

tui/mailing_list.go 🔗

@@ -21,12 +21,16 @@ type MailingListEditor struct {
 
 // NewMailingListEditor creates a new mailing list editor model.
 func NewMailingListEditor() *MailingListEditor {
+	tiStyles := ThemedTextInputStyles()
+
 	name := textinput.New()
 	name.Placeholder = "e.g., Team"
+	name.SetStyles(tiStyles)
 	name.Focus()
 
 	addr := textinput.New()
 	addr.Placeholder = "e.g., alice@example.com, bob@example.com"
+	addr.SetStyles(tiStyles)
 
 	return &MailingListEditor{
 		nameInput: name,

tui/settings.go 🔗

@@ -53,15 +53,19 @@ func NewSettings(cfg *config.Config) *Settings {
 		cfg = &config.Config{}
 	}
 
+	tiStyles := ThemedTextInputStyles()
+
 	certInput := textinput.New()
 	certInput.Placeholder = "/path/to/cert.pem"
 	certInput.Prompt = "> "
 	certInput.CharLimit = 256
+	certInput.SetStyles(tiStyles)
 
 	keyInput := textinput.New()
 	keyInput.Placeholder = "/path/to/private_key.pem"
 	keyInput.Prompt = "> "
 	keyInput.CharLimit = 256
+	keyInput.SetStyles(tiStyles)
 
 	return &Settings{
 		cfg:            cfg,

tui/signature.go 🔗

@@ -19,6 +19,7 @@ func NewSignatureEditor() *SignatureEditor {
 	ta := textarea.New()
 	ta.Placeholder = "Enter your email signature...\n\nExample:\nBest regards,\nDrew"
 	ta.SetHeight(10)
+	ta.SetStyles(ThemedTextAreaStyles())
 	ta.Focus()
 
 	// Load existing signature

tui/theme.go 🔗

@@ -1,6 +1,9 @@
 package tui
 
 import (
+	"charm.land/bubbles/v2/textarea"
+	"charm.land/bubbles/v2/textinput"
+	tea "charm.land/bubbletea/v2"
 	"charm.land/lipgloss/v2"
 	"github.com/floatpane/matcha/theme"
 )
@@ -118,3 +121,59 @@ func RebuildStyles() {
 	mailboxTabStyle = lipgloss.NewStyle().Padding(0, 3)
 	activeMailboxTabStyle = lipgloss.NewStyle().Padding(0, 3).Foreground(t.Accent).Bold(true).Underline(true)
 }
+
+// ThemedTextInputStyles returns textinput.Styles using the active theme colors.
+func ThemedTextInputStyles() textinput.Styles {
+	t := theme.ActiveTheme
+	return textinput.Styles{
+		Focused: textinput.StyleState{
+			Placeholder: lipgloss.NewStyle().Foreground(t.DimText),
+			Suggestion:  lipgloss.NewStyle().Foreground(t.DimText),
+			Prompt:      lipgloss.NewStyle().Foreground(t.AccentText),
+			Text:        lipgloss.NewStyle(),
+		},
+		Blurred: textinput.StyleState{
+			Placeholder: lipgloss.NewStyle().Foreground(t.DimText),
+			Suggestion:  lipgloss.NewStyle().Foreground(t.DimText),
+			Prompt:      lipgloss.NewStyle().Foreground(t.MutedText),
+			Text:        lipgloss.NewStyle().Foreground(t.DimText),
+		},
+		Cursor: textinput.CursorStyle{
+			Color: t.AccentText,
+			Shape: tea.CursorBlock,
+			Blink: true,
+		},
+	}
+}
+
+// ThemedTextAreaStyles returns textarea.Styles using the active theme colors.
+func ThemedTextAreaStyles() textarea.Styles {
+	t := theme.ActiveTheme
+	return textarea.Styles{
+		Focused: textarea.StyleState{
+			Base:             lipgloss.NewStyle(),
+			CursorLine:       lipgloss.NewStyle(),
+			CursorLineNumber: lipgloss.NewStyle().Foreground(t.MutedText),
+			EndOfBuffer:      lipgloss.NewStyle().Foreground(t.Secondary),
+			LineNumber:       lipgloss.NewStyle().Foreground(t.MutedText),
+			Placeholder:      lipgloss.NewStyle().Foreground(t.DimText),
+			Prompt:           lipgloss.NewStyle().Foreground(t.AccentText),
+			Text:             lipgloss.NewStyle(),
+		},
+		Blurred: textarea.StyleState{
+			Base:             lipgloss.NewStyle(),
+			CursorLine:       lipgloss.NewStyle().Foreground(t.DimText),
+			CursorLineNumber: lipgloss.NewStyle().Foreground(t.MutedText),
+			EndOfBuffer:      lipgloss.NewStyle().Foreground(t.Secondary),
+			LineNumber:       lipgloss.NewStyle().Foreground(t.MutedText),
+			Placeholder:      lipgloss.NewStyle().Foreground(t.DimText),
+			Prompt:           lipgloss.NewStyle().Foreground(t.MutedText),
+			Text:             lipgloss.NewStyle().Foreground(t.DimText),
+		},
+		Cursor: textarea.CursorStyle{
+			Color: t.AccentText,
+			Shape: tea.CursorBlock,
+			Blink: true,
+		},
+	}
+}