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 roborev review

sspaeti d6e7d5c6 3b08db86

+34 -23
+1 -1
README.md
··· 52 52 Process -->|< 2 min?<br/>Do it now| Action[Reply/Handle<br/>Immediately] 53 53 Process -->|Waiting for others<br/>Press Mw| Waiting[⏳ Waiting] 54 54 Process -->|Not now, later<br/>Press Mm| Someday[📅 Someday] 55 - Process -->|Time-specific<br/>Press Mc then c| Scheduled[🗓️ Scheduled] 55 + Process -->|Time-specific<br/>Press Mc| Scheduled[🗓️ Scheduled] 56 56 Process -->|Delete<br/>Press x| Trash[🗑️ Trash] 57 57 Process -->|Reference only<br/>Press Mp or P| PaperTrail 58 58 Process -->|Newsletter<br/>Press F or Mf| Feed
+1 -1
docs/content/docs/_index.md
··· 59 59 Process -->|< 2 min?<br/>Do it now| Action[Reply/Handle<br/>Immediately] 60 60 Process -->|Waiting for others<br/>Press Mw| Waiting[⏳ Waiting] 61 61 Process -->|Not now, later<br/>Press Mm| Someday[📅 Someday] 62 - Process -->|Time-specific<br/>Press Mc then c| Scheduled[🗓️ Scheduled] 62 + Process -->|Time-specific<br/>Press Mc| Scheduled[🗓️ Scheduled] 63 63 Process -->|Delete<br/>Press x| Trash[🗑️ Trash] 64 64 Process -->|Reference only<br/>Press Mp or P| PaperTrail 65 65 Process -->|Newsletter<br/>Press F or Mf| Feed
+32 -21
internal/ui/model.go
··· 1664 1664 // Mark as seen: either immediately (if config = 0) or after timer 1665 1665 uid := msg.email.UID 1666 1666 folder := msg.email.Folder 1667 - if m.cfg.UI.MarkAsReadAfterSecs <= 0 { 1668 - // Immediate marking (config = 0) 1667 + markImmediately := func() { 1669 1668 go func() { _ = m.imapCli().MarkSeen(nil, folder, uid) }() 1670 1669 // Update local state immediately 1671 1670 for i := range m.emails { ··· 1674 1673 break 1675 1674 } 1676 1675 } 1676 + } 1677 + if m.cfg.UI.MarkAsReadAfterSecs <= 0 { 1678 + // Immediate marking (config = 0) 1679 + markImmediately() 1677 1680 } else { 1678 - // Schedule timer-based marking 1681 + // Schedule timer-based marking (for normal reading flow only) 1679 1682 m.markAsReadUID = uid 1680 1683 m.markAsReadFolder = folder 1681 1684 } 1682 - if m.pendingForward { 1683 - m.pendingForward = false 1684 - return m.launchForwardCmd() 1685 - } 1686 - if m.pendingReply { 1687 - m.pendingReply = false 1688 - return m.launchReplyCmd() 1689 - } 1690 - if m.pendingReplyAll { 1691 - m.pendingReplyAll = false 1692 - return m.launchReplyAllCmd() 1693 - } 1694 - if m.pendingReaction { 1695 - m.pendingReaction = false 1696 - return m.enterReactionMode(msg.email) 1685 + // Handle pending actions - always mark immediately before launching 1686 + if m.pendingForward || m.pendingReply || m.pendingReplyAll || m.pendingReaction { 1687 + // Mark as read immediately when launching compose actions (preserves original behavior) 1688 + if m.cfg.UI.MarkAsReadAfterSecs > 0 { 1689 + markImmediately() 1690 + } 1691 + if m.pendingForward { 1692 + m.pendingForward = false 1693 + return m.launchForwardCmd() 1694 + } 1695 + if m.pendingReply { 1696 + m.pendingReply = false 1697 + return m.launchReplyCmd() 1698 + } 1699 + if m.pendingReplyAll { 1700 + m.pendingReplyAll = false 1701 + return m.launchReplyAllCmd() 1702 + } 1703 + if m.pendingReaction { 1704 + m.pendingReaction = false 1705 + return m.enterReactionMode(msg.email) 1706 + } 1697 1707 } 1698 1708 m.openLinks = extractLinks(msg.body) 1699 1709 _ = loadEmailIntoReader(&m.reader, msg.email, msg.body, msg.attachments, m.openLinks, m.cfg.UI.Theme, m.width) 1700 1710 m.state = stateReading 1701 - // Start mark-as-read timer if configured 1702 - if m.cfg.UI.MarkAsReadAfterSecs > 0 { 1711 + // Refresh inbox list if immediate mode, or start timer 1712 + if m.cfg.UI.MarkAsReadAfterSecs <= 0 { 1713 + return m, m.applyFilter() 1714 + } else { 1703 1715 return m, m.scheduleMarkAsReadTimer(uid, folder) 1704 1716 } 1705 - return m, nil 1706 1717 1707 1718 case sendDoneMsg: 1708 1719 m.loading = false