aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md')
-rw-r--r--docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md113
1 files changed, 109 insertions, 4 deletions
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 8847b9b..915c8cb 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
@@ -36,15 +36,17 @@ taking that too literally.
|---|------|---------|------|--------|
| 1 | Splitter/column widths do not survive restart | persistence | S | open |
| 2 | No way to see full message details (From/To/Cc/Subject) | information | M | open |
-| 3 | Too few clickable affordances, shortcuts are the only route | discoverability | M | open |
+| 3 | Too few clickable affordances, shortcuts are the only route | discoverability | M | **done** |
| 4 | Message-pane font size does not survive restart | persistence | S | open |
| 5 | Thread list is cramped, poor readability | presentation | S | open |
| 6 | Opened message stays unread | behavior | S | open |
| 7 | HTML view should be default for HTML messages | behavior | XS | **verify first, may already be done** |
-| 8 | No buttons or menu entries for archive, undo, etc | discoverability | M | open |
-| 9 | No in-app view of configured shortcuts | discoverability | S | open |
+| 8 | No buttons or menu entries for archive, undo, etc | discoverability | M | **done** |
+| 9 | No in-app view of configured shortcuts | discoverability | S | **done** |
| 10 | Reaching an account's inbox takes two steps | workflow | S | open |
-| 11 | Icon, `.desktop` file, SlackBuild | packaging | M | open |
+| 11 | Icon, `.desktop` file, SlackBuild | packaging | M | **partly done**: icon and `.desktop` landed, SlackBuild open |
+| 13 | No visual feedback that an action stuck | feedback | S | **done** |
+| 14 | Tag column unreadable, tags need another home | presentation | M | **done** |
Sizes are rough: XS under an hour, S a sitting, M a session.
@@ -167,6 +169,35 @@ smuggle in a "Are you sure?" for Delete.
**Verification:** the existing keymap test must still pass unchanged, proving
user bindings survive the conversion. That is the load-bearing check here.
+### Outcome (done)
+
+Built as described: menu bar, toolbar, and a generated shortcut reference.
+Four things the plan did not anticipate, all verified by probe rather than
+assumed:
+
+- **The event filter was removable, but not for the stated reason.** The plan
+ worried that `QAction` shortcuts might lose to `QAbstractItemView`'s
+ type-to-search. They do not: shortcut dispatch runs before the focused
+ widget sees the key. The filter is gone, and the thread view no longer
+ needs its own.
+- **Qt already solves the query-bar case.** A plain-letter shortcut is
+ suppressed while an editable widget has focus, so the `hasFocus()` guard
+ was unnecessary. Removing it also fixed `Ctrl+Q`, which the old filter
+ swallowed while typing a query.
+- **Three default bindings had never worked.** `N`, `F` and `G` stored the
+ unshifted key, which no keystroke emits, so `toggle_unread`, `flag` and
+ `sync` were dead in 0.1.0. Fixed in `KeyMap::normalizeSequence()` and
+ committed separately from the menu work.
+- **The drift test did become unnecessary**, as the plan hoped.
+ `registeredActionNames()` is now derived from the `QAction`s, and
+ `defaultBindings()` is the single source for the defaults. The two tests
+ that pinned the hand-maintained lists together were replaced by ones that
+ check a configured binding actually reaches its action.
+
+Defaults moved to modifier shortcuts, since a single letter cannot be a menu
+accelerator without claiming that letter window-wide. Existing `[keys]`
+entries are unaffected.
+
## 4. Message-pane font size does not survive restart
**Observed:** described as "very annoying", more so than item 1.
@@ -331,6 +362,80 @@ Packaging, independent of everything above, and can proceed in parallel.
---
+## 13. No visual feedback that an action stuck
+
+**Observed:** selecting a thread and hitting Delete changed nothing on screen.
+No way to tell whether the thread was really going to be deleted on the next
+sync, which is bad UX for every tag action, not only delete.
+
+**Cause:** not a missing update. `ThreadListModel::applyTagChange()` already
+added the tag and emitted `dataChanged` across the whole row, so the Tags
+column did change. But `SubjectColumn` was set to `QHeaderView::Stretch` while
+`TagsColumn` came after it, so Subject absorbed all free width and pushed Tags
+out of view. The feedback existed in the one column that could not be seen.
+
+**Approach:** two changes, since the cause was two things.
+
+- Column order is now Tags, Date, From, Subject. Subject stretches and is
+ last, so nothing sits to its right to be pushed out. The other three size
+ to their contents.
+- A thread tagged `deleted` or `spam` styles its entire row: muted dark red
+ (`#8b2c2c`) or orange (`#a85c18`) fill, white text, struck through. Applied
+ through `Qt::BackgroundRole`, `Qt::ForegroundRole` and `Qt::FontRole` for
+ every column, so no cue depends on a single column staying visible.
+
+Strike-through rides along with the fill deliberately: it survives a theme
+that overrides background colours, a colourblind reader, and a screenshot.
+Bold for unread still composes with it.
+
+**Decisions:** no status-bar or toast changes, the existing `tagSelected()`
+message stays as it is. Archive removes `inbox` and adds nothing, so an
+archived thread gets no row styling; whether it should disappear from an inbox
+query is deliberately left open rather than guessed at.
+
+**Verification:** four model tests covering the colours, the strike-through,
+that styling spans every column, and that undo restores a plain row. Rendered
+and inspected: normal, unread, deleted, spam, and deleted-plus-unread rows.
+
+---
+
+## 14. Tag column unreadable
+
+**Observed:** with tags spelled out per row the column ran to 500 pixels of
+mostly repeated text ("account-privateemail-danilo.macri attachment flagged
+inbox passed replied"), dominated by the account prefix, and consumed most of
+the list's width.
+
+**Cause:** presentation, not data. 96 tags in this database, many hierarchical
+(`shopping/amazon`, `mailing-list/SBo`), rendered as a joined string.
+
+**Approach:** the column is gone. Tags now render as coloured chips in two
+places, split by taxonomy:
+
+- The **account tag** says which mailbox a thread came from. It draws as a chip
+ in front of the subject, coloured and labelled from its own `[account.<key>]`
+ stanza via new `color` and `label` keys. `label` is display-only; the notmuch
+ tag is never renamed.
+- **Functional tags** say what state a thread is in. They fill a single row
+ under the message pane, with overflow collapsing into a `+N` chip whose
+ tooltip lists the hidden ones. A single row keeps the message area from
+ shifting between threads with different tag counts.
+
+Colours resolve exact tag first, then top-level prefix, so one `shopping` entry
+covers the hierarchy without listing all 96. Unconfigured tags fall back to a
+hash of the name, stable so a chip never changes colour as the list scrolls.
+
+**Defect found while building:** QSettings treats `/` in a key as a group
+separator, so `shopping/amazon` becomes a nested key that `childKeys()` never
+returns. Reading `[tagcolors]` with `childKeys()` silently dropped every
+hierarchical tag, and each fell through to its prefix colour. Fixed by reading
+`allKeys()`, with a regression test. The same gotcha is already documented in
+`CLAUDE.md` for `[account.work]` section names.
+
+**Deferred:** clicking a chip to search that tag. Display only for now.
+
+---
+
## Deferred, unsized, or split out
Items noted while triaging but not part of the original list. Same numbering