A minimal email TUI where you read with Markdown and write in Neovim. neomd.ssp.sh/docs
email markdown neovim tui
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix memory growth after hibernation

sspaeti 1794d525 12de16eb

+17 -1
+1
CHANGELOG.md
··· 1 1 # Changelog 2 2 3 3 # 2026-04-21 4 + - **Fix: potential memory leak in background sync** — fixed infinite loop that occurred when IMAP errors (e.g., after suspend/resume) triggered immediate retry instead of waiting for next scheduled interval; `bgFetchInboxCmd()` now returns nil on error instead of `bgSyncTickMsg{}`, preventing tight loop that consumes large amounts of RAM; added `bgSyncInProgress` flag to prevent concurrent background syncs from piling up during slow network conditions 4 5 - **Fix: reply-all excludes all own addresses** — `ctrl+r` reply-all now excludes all configured email addresses (accounts + sender aliases) from the CC field; previously only excluded the active account's address, causing your own email to appear in CC when the original email was sent to one of your other addresses 5 6 6 7 # 2026-04-18
+16 -1
internal/ui/model.go
··· 490 490 pendingReply bool 491 491 pendingReplyAll bool 492 492 493 + // bgSyncInProgress prevents concurrent background syncs from piling up 494 + bgSyncInProgress bool 495 + 493 496 // Chord prefix: "g" or "M" while waiting for second key 494 497 pendingKey string 495 498 ··· 1434 1437 m.imapCli().ResetMailboxSelection() // force fresh SELECT to see new messages 1435 1438 emails, err := m.imapCli().FetchHeaders(nil, m.cfg.Folders.Inbox, m.cfg.UI.InboxCount) 1436 1439 if err != nil { 1437 - return bgSyncTickMsg{} // reschedule retry on next tick instead of errMsg 1440 + // Return nil to let the next scheduled tick retry naturally. 1441 + // Returning bgSyncTickMsg{} here creates an infinite loop on persistent errors! 1442 + return bgInboxFetchedMsg{emails: nil} // signal completion even on error 1438 1443 } 1439 1444 return bgInboxFetchedMsg{emails: emails} 1440 1445 } ··· 1949 1954 return m, tea.Batch(m.spinner.Tick, m.fetchFolderCmd(m.activeFolder())) 1950 1955 1951 1956 case bgSyncTickMsg: 1957 + // Skip if a background sync is already in progress to prevent pileup. 1958 + if m.bgSyncInProgress { 1959 + return m, m.scheduleBgSync() // just reschedule the next tick 1960 + } 1952 1961 // Fire background inbox fetch; reschedule next tick in parallel. 1962 + m.bgSyncInProgress = true 1953 1963 return m, tea.Batch(m.bgFetchInboxCmd(), m.scheduleBgSync()) 1954 1964 1955 1965 case bgInboxFetchedMsg: 1966 + m.bgSyncInProgress = false // clear flag regardless of success/failure 1967 + if msg.emails == nil { 1968 + // Error case (network down, etc.) - silently skip until next tick 1969 + return m, nil 1970 + } 1956 1971 if err := m.validateScreenerSafety(); err != nil { 1957 1972 m.status = err.Error() 1958 1973 m.isError = true