@@ -193,6 +193,25 @@ func (m *Composer) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.subjectInput.SetWidth(inputWidth)
m.bodyInput.SetWidth(inputWidth)
m.signatureInput.SetWidth(inputWidth)
+ if msg.Height > 0 {
+ // Fixed rows: title, from, to, cc, bcc, subject, sig label,
+ // attachment, smime, button, blank, tip, help = 13
+ const fixedRows = 13
+ available := msg.Height - fixedRows
+ if available < 6 {
+ available = 6
+ }
+ bodyHeight := (available * 55) / 100
+ sigHeight := (available * 15) / 100
+ if bodyHeight < 3 {
+ bodyHeight = 3
+ }
+ if sigHeight < 2 {
+ sigHeight = 2
+ }
+ m.bodyInput.SetHeight(bodyHeight)
+ m.signatureInput.SetHeight(sigHeight)
+ }
case FileSelectedMsg:
// Avoid duplicates
@@ -286,3 +286,38 @@ func TestComposerSetSelectedAccount(t *testing.T) {
t.Errorf("Expected selectedAccountIdx to remain 2, got %d", composer.selectedAccountIdx)
}
}
+
+// TestComposerDynamicHeight verifies that window resize updates textarea heights.
+func TestComposerDynamicHeight(t *testing.T) {
+ composer := NewComposer("", "", "", "", false)
+
+ model, _ := composer.Update(tea.WindowSizeMsg{Width: 120, Height: 40})
+ composer = model.(*Composer)
+
+ if composer.height != 40 {
+ t.Errorf("Expected height 40, got %d", composer.height)
+ }
+
+ bodyH := composer.bodyInput.Height()
+ sigH := composer.signatureInput.Height()
+
+ if bodyH <= 3 {
+ t.Errorf("Expected bodyInput height > 3, got %d", bodyH)
+ }
+ if sigH <= 1 {
+ t.Errorf("Expected signatureInput height > 1, got %d", sigH)
+ }
+ if bodyH <= sigH {
+ t.Errorf("Expected bodyInput height (%d) > signatureInput height (%d)", bodyH, sigH)
+ }
+
+ // Small window: heights should not go below minimums
+ model, _ = composer.Update(tea.WindowSizeMsg{Width: 80, Height: 10})
+ composer = model.(*Composer)
+ if composer.bodyInput.Height() < 3 {
+ t.Errorf("bodyInput height should be at least 3, got %d", composer.bodyInput.Height())
+ }
+ if composer.signatureInput.Height() < 2 {
+ t.Errorf("signatureInput height should be at least 2, got %d", composer.signatureInput.Height())
+ }
+}
@@ -24,7 +24,7 @@ var (
tabBarStyle = lipgloss.NewStyle().BorderStyle(lipgloss.NormalBorder()).BorderBottom(true).PaddingBottom(1).MarginBottom(1)
)
-var dateStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("243"))
+var dateStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("243"))
var senderStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("250")).Bold(true)
type item struct {
@@ -184,23 +184,23 @@ type AccountTab struct {
}
type Inbox struct {
- list list.Model
- isFetching bool
- isRefreshing bool
- emailsCount int
- accounts []config.Account
- emailsByAccount map[string][]fetcher.Email
- allEmails []fetcher.Email
- tabs []AccountTab
- activeTabIndex int
- width int
- height int
- currentAccountID string // Empty means "ALL"
- emailCountByAcct map[string]int
- mailbox MailboxKind
- folderName string // Custom folder name override for title
- noMoreByAccount map[string]bool // Per-account: true when pagination returns 0 results
- extraShortHelpKeys []key.Binding
+ list list.Model
+ isFetching bool
+ isRefreshing bool
+ emailsCount int
+ accounts []config.Account
+ emailsByAccount map[string][]fetcher.Email
+ allEmails []fetcher.Email
+ tabs []AccountTab
+ activeTabIndex int
+ width int
+ height int
+ currentAccountID string // Empty means "ALL"
+ emailCountByAcct map[string]int
+ mailbox MailboxKind
+ folderName string // Custom folder name override for title
+ noMoreByAccount map[string]bool // Per-account: true when pagination returns 0 results
+ extraShortHelpKeys []key.Binding
}
func NewInbox(emails []fetcher.Email, accounts []config.Account) *Inbox {