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
53 return m
54}
55
56func (m *Composer) Init() tea.Cmd {
57 return textinput.Blink
58}
59
60func (m *Composer) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
61 var cmds []tea.Cmd
62 var cmd tea.Cmd
63
64 switch msg := msg.(type) {
65 case tea.WindowSizeMsg:
66 // When the window is resized, update the width of the inputs.
67 inputWidth := msg.Width - 6 // Subtract for padding and prompt
68 m.toInput.Width = inputWidth
69 m.subjectInput.Width = inputWidth
70 m.bodyInput.SetWidth(inputWidth)
71
72 case tea.KeyMsg:
73 switch msg.Type {
74 // IMPORTANT: Removed tea.KeyEsc from this case
75 case tea.KeyCtrlC:
76 return m, tea.Quit
77
78 // Handle Tab and Shift+Tab to cycle focus between inputs.
79 case tea.KeyTab, tea.KeyShiftTab:
80 if msg.Type == tea.KeyShiftTab {
81 m.focusIndex--
82 } else {
83 m.focusIndex++
84 }
85
86 // Wrap around
87 if m.focusIndex > 3 { // 3 is the Send button
88 m.focusIndex = 0
89 } else if m.focusIndex < 0 {
90 m.focusIndex = 3
91 }
92
93 // Blur all inputs
94 m.toInput.Blur()
95 m.subjectInput.Blur()
96 m.bodyInput.Blur()
97
98 // Focus the correct input
99 switch m.focusIndex {
100 case 0:
101 cmds = append(cmds, m.toInput.Focus())
102 case 1:
103 cmds = append(cmds, m.subjectInput.Focus())
104 case 2:
105 cmds = append(cmds, m.bodyInput.Focus())
106 }
107 return m, tea.Batch(cmds...)
108
109 // Handle Enter key.
110 case tea.KeyEnter:
111 // If on the Send button, send the email.
112 if m.focusIndex == 3 {
113 return m, func() tea.Msg {
114 return SendEmailMsg{
115 To: m.toInput.Value(),
116 Subject: m.subjectInput.Value(),
117 Body: m.bodyInput.Value(),
118 }
119 }
120 }
121 }
122 }
123
124 // Update the focused input.
125 switch m.focusIndex {
126 case 0:
127 m.toInput, cmd = m.toInput.Update(msg)
128 cmds = append(cmds, cmd)
129 case 1:
130 m.subjectInput, cmd = m.subjectInput.Update(msg)
131 cmds = append(cmds, cmd)
132 case 2:
133 m.bodyInput, cmd = m.bodyInput.Update(msg)
134 cmds = append(cmds, cmd)
135 }
136
137 return m, tea.Batch(cmds...)
138}
139
140// View renders the UI.
141func (m *Composer) View() string {
142 button := &blurredButton
143 if m.focusIndex == 3 {
144 button = &focusedButton
145 }
146
147 return lipgloss.JoinVertical(lipgloss.Left,
148 "Compose New Email",
149 "From: "+emailRecipientStyle.Render(m.fromAddr),
150 m.toInput.View(),
151 m.subjectInput.View(),
152 m.bodyInput.View(),
153 *button,
154 helpStyle.Render("tab: next field • esc: back to menu • enter: send"),
155 )
156}