From 70b0eedb9cb077035223ef3b0c043b0da4a7730a Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 29 Mar 2026 19:33:31 +0700 Subject: [PATCH] Prompt for filename when saving a message Replace the immediate save-to-~/ID.eml with a filename prompt (default ID.eml) consistent with the part-save UX. Enter confirms, Esc cancels. Removes the now-unused saveMessage/saveCmd helpers. Co-Authored-By: Claude Sonnet 4.6 --- message.go | 12 ------------ model.go | 30 ++++++++++++++++++++++++++++-- msgs.go | 4 ++-- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/message.go b/message.go index 37c113a..5d95599 100644 --- a/message.go +++ b/message.go @@ -410,15 +410,3 @@ func renderHTML(html string) string { return string(out) } -// saveMessage writes the raw EML content to ~/QUEUEID.eml. -func saveMessage(id, content string) (string, error) { - home, err := os.UserHomeDir() - if err != nil { - return "", fmt.Errorf("could not determine home directory: %w", err) - } - path := filepath.Join(home, id+".eml") - if err := os.WriteFile(path, []byte(content), 0600); err != nil { - return "", fmt.Errorf("write %s: %w", path, err) - } - return path, nil -} diff --git a/model.go b/model.go index 4efe547..ddf4db9 100644 --- a/model.go +++ b/model.go @@ -42,6 +42,7 @@ type Model struct { saveNotice string width int height int + messageSaving bool // stateParts fields parts []MessagePart partsCursor int @@ -215,6 +216,24 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } case stateMessage: + if m.messageSaving { + switch msg.String() { + case "esc": + m.messageSaving = false + m.partsSaveInput.Blur() + return m, nil + case "enter": + name := strings.TrimSpace(m.partsSaveInput.Value()) + content := m.currentRaw + m.messageSaving = false + m.partsSaveInput.Blur() + return m, saveMessageAsCmd(content, name) + default: + var cmd tea.Cmd + m.partsSaveInput, cmd = m.partsSaveInput.Update(msg) + return m, cmd + } + } switch msg.String() { case "q": m.state = stateList @@ -223,7 +242,12 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case "ctrl+c": return m, tea.Quit case "s": - return m, saveCmd(m.currentID, m.currentRaw) + m.messageSaving = true + m.saveNotice = "" + m.partsSaveInput.SetValue(m.currentID + ".eml") + m.partsSaveInput.CursorEnd() + m.partsSaveInput.Focus() + return m, textinput.Blink case "H": m.showFullHeaders = !m.showFullHeaders m.refreshViewport() @@ -352,7 +376,9 @@ func (m Model) View() string { fmt.Sprintf(" ↑↓/SPC/PgUp/Dn: scroll │ s: save EML │ v: parts │ %s │ q: back │ %d%% ", headersHint, scrollPct), ) notice := "" - if m.saveNotice != "" { + if m.messageSaving { + notice = "\n Save as: " + m.partsSaveInput.View() + } else if m.saveNotice != "" { notice = "\n" + saveNoticeStyle.Render(m.saveNotice) } return header + "\n" + m.viewport.View() + "\n" + status + notice diff --git a/msgs.go b/msgs.go index 30fdb18..9bf3bb9 100644 --- a/msgs.go +++ b/msgs.go @@ -54,9 +54,9 @@ func loadMessageCmd(id string) tea.Cmd { } } -func saveCmd(id, content string) tea.Cmd { +func saveMessageAsCmd(content, name string) tea.Cmd { return func() tea.Msg { - path, err := saveMessage(id, content) + path, err := savePart([]byte(content), name) if err != nil { return saveErrMsg{err} }