@@ -237,6 +237,7 @@ type Draft struct {
AccountID string `json:"account_id"`
InReplyTo string `json:"in_reply_to,omitempty"`
References []string `json:"references,omitempty"`
+ QuotedText string `json:"quoted_text,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
@@ -495,20 +495,32 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tui.ReplyToEmailMsg:
to := msg.Email.From
- subject := "Re: " + msg.Email.Subject
- body := fmt.Sprintf("\n\nOn %s, %s wrote:\n> %s", msg.Email.Date.Format("Jan 2, 2006 at 3:04 PM"), msg.Email.From, strings.ReplaceAll(msg.Email.Body, "\n", "\n> "))
+ subject := msg.Email.Subject
+ normalizedSubject := strings.ToLower(strings.TrimSpace(subject))
+ if !strings.HasPrefix(normalizedSubject, "re:") {
+ subject = "Re: " + subject
+ }
+ quotedText := fmt.Sprintf("\n\nOn %s, %s wrote:\n> %s", msg.Email.Date.Format("Jan 2, 2006 at 3:04 PM"), msg.Email.From, strings.ReplaceAll(msg.Email.Body, "\n", "\n> "))
+ var composer *tui.Composer
if m.config != nil && len(m.config.Accounts) > 0 {
// Use the account that received the email
accountID := msg.Email.AccountID
if accountID == "" {
accountID = m.config.GetFirstAccount().ID
}
- composer := tui.NewComposerWithAccounts(m.config.Accounts, accountID, to, subject, body)
- m.current = composer
+ composer = tui.NewComposerWithAccounts(m.config.Accounts, accountID, to, subject, "")
} else {
- m.current = tui.NewComposer("", to, subject, body)
+ composer = tui.NewComposer("", to, subject, "")
}
+ composer.SetQuotedText(quotedText)
+
+ // Set reply headers
+ inReplyTo := msg.Email.MessageID
+ references := append(msg.Email.References, msg.Email.MessageID)
+ composer.SetReplyContext(inReplyTo, references)
+
+ m.current = composer
m.current, _ = m.current.Update(tea.WindowSizeMsg{Width: m.width, Height: m.height})
return m, m.current.Init()
@@ -1024,6 +1036,10 @@ func sendEmail(account *config.Account, msg tui.SendEmailMsg) tea.Cmd {
recipients := []string{msg.To}
body := msg.Body
+ // Append quoted text if present (for replies)
+ if msg.QuotedText != "" {
+ body = body + msg.QuotedText
+ }
images := make(map[string][]byte)
attachments := make(map[string][]byte)
@@ -69,6 +69,9 @@ type Composer struct {
// Reply context
inReplyTo string
references []string
+
+ // Hidden quoted text (appended to body when sending, but not shown in editor)
+ quotedText string
}
// NewComposer initializes a new composer model.
@@ -303,6 +306,9 @@ func (m *Composer) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
Body: m.bodyInput.Value(),
AttachmentPath: m.attachmentPath,
AccountID: accountID,
+ QuotedText: m.quotedText,
+ InReplyTo: m.inReplyTo,
+ References: m.references,
}
}
}
@@ -502,6 +508,16 @@ func (m *Composer) SetReplyContext(inReplyTo string, references []string) {
m.references = references
}
+// SetQuotedText sets the hidden quoted text that will be appended when sending.
+func (m *Composer) SetQuotedText(text string) {
+ m.quotedText = text
+}
+
+// GetQuotedText returns the hidden quoted text.
+func (m *Composer) GetQuotedText() string {
+ return m.quotedText
+}
+
// GetInReplyTo returns the In-Reply-To header value.
func (m *Composer) GetInReplyTo() string {
return m.inReplyTo
@@ -523,6 +539,7 @@ func (m *Composer) ToDraft() config.Draft {
AccountID: m.GetSelectedAccountID(),
InReplyTo: m.inReplyTo,
References: m.references,
+ QuotedText: m.quotedText,
}
}
@@ -533,5 +550,6 @@ func NewComposerFromDraft(draft config.Draft, accounts []config.Account) *Compos
m.attachmentPath = draft.AttachmentPath
m.inReplyTo = draft.InReplyTo
m.references = draft.References
+ m.quotedText = draft.QuotedText
return m
}
@@ -27,6 +27,7 @@ type SendEmailMsg struct {
InReplyTo string
References []string
AccountID string // ID of the account to send from
+ QuotedText string // Hidden quoted text appended when sending
}
type Credentials struct {