1package tui
2
3import (
4 "github.com/charmbracelet/bubbles/textarea"
5 "github.com/charmbracelet/bubbles/textinput"
6 tea "github.com/charmbracelet/bubbletea"
7 "github.com/charmbracelet/lipgloss"
8)
9
10// Styles for the UI
11var (
12 focusedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("205"))
13 blurredStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("240"))
14 cursorStyle = focusedStyle.Copy()
15 noStyle = lipgloss.NewStyle()
16 focusedButton = focusedStyle.Copy().Render("[ Send ]")
17 blurredButton = blurredStyle.Copy().Render("[ Send ]")
18 emailRecipientStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("205")).Bold(true)
19)
20
21// Composer model holds the state of the email composition UI.
22type Composer struct {
23 focusIndex int
24 toInput textinput.Model
25 subjectInput textinput.Model
26 bodyInput textarea.Model
27 fromAddr string
28}
29
30// NewComposer initializes a new composer model.
31func NewComposer(from string) *Composer {
32 m := &Composer{fromAddr: from}
33
34 m.toInput = textinput.New()
35 m.toInput.Cursor.Style = cursorStyle
36 m.toInput.Placeholder = "To"
37 m.toInput.Focus()
38 m.toInput.Prompt = "> "
39 m.toInput.CharLimit = 256
40
41 m.subjectInput = textinput.New()
42 m.subjectInput.Cursor.Style = cursorStyle
43 m.subjectInput.Placeholder = "Subject"
44 m.subjectInput.Prompt = "> "
45 m.subjectInput.CharLimit = 256
46
47 m.bodyInput = textarea.New()
48 m.bodyInput.Cursor.Style = cursorStyle
49 m.bodyInput.Placeholder = "Body..."
50 m.bodyInput.Prompt = "> "
51 m.bodyInput.SetHeight(10)
52 m.bodyInput.SetWidth(60)
53
54 return m
55}
56
57func (m *Composer) Init() tea.Cmd {
58 return textinput.Blink
59}
60
61func (m *Composer) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
62 var cmds []tea.Cmd
63 var cmd tea.Cmd
64
65 switch msg := msg.(type) {
66 case tea.KeyMsg:
67 switch msg.Type {
68 // IMPORTANT: Removed tea.KeyEsc from this case
69 case tea.KeyCtrlC:
70 return m, tea.Quit
71
72 // Handle Tab and Shift+Tab to cycle focus between inputs.
73 case tea.KeyTab, tea.KeyShiftTab:
74 if msg.Type == tea.KeyShiftTab {
75 m.focusIndex--
76 } else {
77 m.focusIndex++
78 }
79
80 // Wrap around
81 if m.focusIndex > 3 { // 3 is the Send button
82 m.focusIndex = 0
83 } else if m.focusIndex < 0 {
84 m.focusIndex = 3
85 }
86
87 // Blur all inputs
88 m.toInput.Blur()
89 m.subjectInput.Blur()
90 m.bodyInput.Blur()
91
92 // Focus the correct input
93 switch m.focusIndex {
94 case 0:
95 cmds = append(cmds, m.toInput.Focus())
96 case 1:
97 cmds = append(cmds, m.subjectInput.Focus())
98 case 2:
99 cmds = append(cmds, m.bodyInput.Focus())
100 }
101 return m, tea.Batch(cmds...)
102
103 // Handle Enter key.
104 case tea.KeyEnter:
105 // If on the Send button, send the email.
106 if m.focusIndex == 3 {
107 return m, func() tea.Msg {
108 return SendEmailMsg{
109 To: m.toInput.Value(),
110 Subject: m.subjectInput.Value(),
111 Body: m.bodyInput.Value(),
112 }
113 }
114 }
115 }
116 }
117
118 // Update the focused input.
119 switch m.focusIndex {
120 case 0:
121 m.toInput, cmd = m.toInput.Update(msg)
122 cmds = append(cmds, cmd)
123 case 1:
124 m.subjectInput, cmd = m.subjectInput.Update(msg)
125 cmds = append(cmds, cmd)
126 case 2:
127 m.bodyInput, cmd = m.bodyInput.Update(msg)
128 cmds = append(cmds, cmd)
129 }
130
131 return m, tea.Batch(cmds...)
132}
133
134// View renders the UI.
135func (m *Composer) View() string {
136 button := &blurredButton
137 if m.focusIndex == 3 {
138 button = &focusedButton
139 }
140
141 return lipgloss.JoinVertical(lipgloss.Left,
142 "Compose New Email",
143 "From: "+emailRecipientStyle.Render(m.fromAddr),
144 m.toInput.View(),
145 m.subjectInput.View(),
146 m.bodyInput.View(),
147 *button,
148 helpStyle.Render("tab: next field • esc: back to menu • enter: send"),
149 )
150}