From 0527f4c2941aa4bf256542d4659a4ff7517bf7f7 Mon Sep 17 00:00:00 2001 From: Drew Smirnoff Date: Thu, 4 Jun 2026 11:29:33 +0400 Subject: [PATCH] fix: save drafts on quit (#1424) ## What? Checks if the composer has contents at the moment of interrupt signal (`ctrl+c`). If so, it saves the draft before quitting ## Why? It is easy to accidentally quit, and it was unforgiving, without saving anything. Especially including that the default quit keybind is the same as "copy" on linux and windows (`ctrl+c`) Signed-off-by: drew --- main.go | 7 +++++++ tui/composer.go | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/main.go b/main.go index 07db2972f1f6a1640c0b50a0db01f090e4d08e92..c7918eaf20ff7f2c0d292894f0f355ab849a5a89 100644 --- a/main.go +++ b/main.go @@ -340,6 +340,13 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo switch msg := msg.(type) { case tea.KeyPressMsg: if msg.String() == "ctrl+c" { + // Persist an in-progress draft so quitting the composer + // doesn't discard the user's work. + if composer, ok := m.current.(*tui.Composer); ok && composer.HasContent() { + if err := config.SaveDraft(composer.ToDraft()); err != nil { + log.Printf("Error saving draft on quit: %v", err) + } + } m.idleWatcher.StopAll() if m.service != nil { m.service.Close() //nolint:errcheck,gosec diff --git a/tui/composer.go b/tui/composer.go index 2e032b0a3fd2e7d7672a2ab29f781be40bb39af9..52f573a69001afa67750aa1b2c27596031774002 100644 --- a/tui/composer.go +++ b/tui/composer.go @@ -1528,6 +1528,15 @@ func (m *Composer) HidePluginPrompt() { m.showPluginPrompt = false } +// HasContent reports whether the composer holds anything worth persisting. +// It is used to avoid saving empty drafts when the user quits the composer. +func (m *Composer) HasContent() bool { + return m.hasAnyRecipient() || + strings.TrimSpace(m.subjectInput.Value()) != "" || + strings.TrimSpace(m.bodyInput.Value()) != "" || + len(m.attachmentPaths) > 0 +} + // ToDraft converts the composer state to a Draft for saving. func (m *Composer) ToDraft() config.Draft { return config.Draft{