From 671e76f691b4027c3c9707fdae6ea6d4b4690832 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Tue, 25 Aug 2026 09:19:49 +0200 Subject: docs(backlog): record items 160 and 161 from the notes Both are composer feedback, and both causes are verified in the code rather than copied from the note. 160: autosave works and is silent on success. The only feedback is the failure banner, whose comment explicitly rejected the fading status line that success actually wants. m_dirty and the draftSaved signal already carry both states; nothing displays them. 161: the composer has no menu bar, and Save draft does not exist as an action at all. saveDraftNow() is reachable only from the timer, Send and closeEvent, so there is no way for the user to ask for a save. Notes that the composer's actions stay out of KeyMap per item 148, that everyActionIsReachableFromAMenu() walks the main window only, and that "duplicate the other actions" needs the user to say which, since most message actions are meaningless over a message being written. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UUQS6n3cmsFrsjCNmwNtf8 --- .../plans/2026-08-03-post-0.1.0-usability.md | 87 ++++++++++++++++++++++ 1 file changed, 87 insertions(+) (limited to 'docs/superpowers') diff --git a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md index 4aeff26..755caa5 100644 --- a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md +++ b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md @@ -232,6 +232,9 @@ taking that too literally. | 159 | The Drafts view lists threads, so a draft is unreachable by double-click | defect | S | **done** 2026-08-25, unreleased. Reverses item 138's own decision, confirmed with the user. `generatorIsFlat()` in `config.cpp` is now the single closed set of flat generators, replacing three hardcoded comparisons against `"sent"`: the built-in filter, the reader that reapplies the mode, and the writer that skips storing what the generator implies. Those three had to agree and nothing made them; a `drafts` entry saved and reloaded would otherwise have come back THREADED while the button was flat. `builtinFilter()` sets `flat` once from the helper rather than in a branch, so the set cannot drift from the labels | +| 160 | The composer never says a draft was autosaved | feedback | S | open, 2026-08-25, from the notes. Autosave works (`m_autosaveTimer`, `saveDraftNow()`) and is SILENT on success: the only feedback is `m_banner`, which appears on FAILURE. The user asks for a status bar reporting "last autosave 20s ago" progressing to "Draft autosaved", plus an eye-catching "unsaved content" cue whenever `m_dirty` is true. Both states already exist as data; nothing displays them | +| 161 | The composer has no menu bar | discoverability | S | open, 2026-08-25, from the notes. Save draft on `Ctrl+S` is asked for and does not exist at all: `saveDraftNow()` is reachable only from the timer, Send and `closeEvent`. The composer's actions are ad-hoc `QAction`s parented to the window (Send, Close, and the formatting toolbar's), none registered in `KeyMap`, so item 132's menu-reachability rule does not currently reach them. Depends on 160 for what "saved" then reports | + Sizes are rough: XS under an hour, S a sitting, M a session. --- @@ -1333,3 +1336,87 @@ half of the fork above, and it would look exactly like this. The 70-second duration recorded above fits a `QTRY_*` waiting for a file that is never going to appear, which is consistent with a wrong destination rather than a slow one. + +## 160. The composer never says a draft was autosaved + +**Observed (user, 2026-08-25):** "we should add a status bar to the compose +window, to report every time a draft is autosaved. With a timer like 'last +autosave 20s ago' progressing into 'Draft autosaved' and next to it a visually +highlighted notification 'unsaved content' (blinking, color yellow, something +eye catching) whenever there's new content since the last autosave." + +**Cause (verified in code):** autosave is built and works. `m_autosaveTimer` +is a single-shot timer restarted on every edit (`composewindow.cpp:960-963`), +so it fires once the user pauses rather than once per character, and +`saveDraftNow()` writes the Maildir file. It reports **nothing on success**. + +The only feedback that exists is `m_banner`, and it is deliberately a FAILURE +channel: the comment at `composewindow.cpp:1026` records the reasoning, "a +PERSISTENT banner, not a modal and not a status-bar line that fades", because +a failed save must survive until it is dealt with. Success is the opposite +case and wants the fading line that comment rejected for failure. + +**Both states already exist as data**, which is what makes this small: + +- `m_dirty` is true exactly when there is content newer than the last save. +- `m_savedFingerprint` and the `draftSaved` signal mark a successful write. + +Nothing displays either. There is no `QStatusBar` on the composer at all. + +**Approach.** A `QStatusBar` on the composer, with the age line as a permanent +widget and the unsaved cue beside it. The age needs a second timer of its own, +since "20s ago" changes with no edit to drive it; a one-second tick is wasteful +for a label that reads in tens of seconds, so tick slower and accept the +granularity. + +**Constraints.** + +- **Do not reuse `m_banner`.** Its persistence is load-bearing for the quit + path's honesty, and a success message that shares it would either fade the + failure away or make success sticky. +- The "blinking" the note asks for should be treated as "eye-catching", not + literally: a blinking widget is an accessibility problem and Qt has no + blink facility to reach for. A yellow ground matching item 151's warning + severity is the established vocabulary here. +- `saveDraftNow()` returns false on failure and the banner takes over, so the + status line must not claim a save the write did not make. + +## 161. The composer has no menu bar + +**Observed (user, 2026-08-25):** "the compose window should have a menu bar on +top", with "save draft (ctrl+s)" and "duplicate the other actions like we do +on the main window". + +**Cause (verified in code):** the composer has no `QMenuBar`, and **Save draft +does not exist as an action at all.** `saveDraftNow()` is reachable only from +the autosave timer, from the send path, and from `closeEvent`; there is no way +for the user to ask for a save, and no `Ctrl+S` anywhere in the composer or in +`KeyMap`. + +The actions the composer does have are ad-hoc `QAction`s parented to the +window: `m_sendAction` and a Close action (`composewindow.cpp:583-608`), plus +the formatting toolbar's own. **None is registered in `KeyMap`**, which is +deliberate and documented for `Ctrl+W` in item 148: they are WindowShortcuts +dispatched to the active composer only, so they never touch the main window's +namespace. A consequence worth stating before this item is built: +`everyActionIsReachableFromAMenu()` walks the MAIN window's menu bar, so it +does not currently constrain these, and adding a composer menu bar does not +automatically bring them under item 132's rules. + +**Approach.** A `QMenuBar` on the composer, with Save draft (`Ctrl+S`) as the +new action and the existing ones gathered under it rather than duplicated. + +**Constraints.** + +- **"Duplicate the other actions" means the main window's MESSAGE actions, and + most of them are meaningless here.** A composer has no thread, no selection + and no tag surface. Ask which the user actually wants before building a menu + that offers Archive or Mark all read over a message being written. +- Keep the composer's actions out of `KeyMap`, per item 148's reasoning. A + composer menu bar is not a reason to move them. +- Save draft must go through `saveDraftNow()`, which already handles the + failure banner and emits `draftSaved` for the indexing item 158 added. A + second write path would reintroduce the ghost-file problem that fixed. +- **Depends on item 160** for what a manual save then reports: with no status + line, a successful `Ctrl+S` would be as silent as an autosave is now. + -- cgit v1.2.3