diff options
Diffstat (limited to 'docs/superpowers')
| -rw-r--r-- | docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md | 87 |
1 files changed, 56 insertions, 31 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 2e5e8c9..aefa6a5 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 @@ -155,7 +155,7 @@ taking that too literally. | 86 | A right-click search can replace or narrow, but never exclude | workflow | S | **done** 2026-08-14, unreleased; see `specs/2026-08-14-exclude-from-search-design.md`. Follows 85. The `extend` bool became a `SearchMode` enum across four signatures | | 89 | A sync moves the list under the user's hands, and the auto-sync skips rather than retries | workflow | XS | **done** 2026-08-15, unreleased. The timer half only: a skipped auto-sync re-arms instead of giving up. The list-churn half is **dropped**, not built: the user resolved it as a mental-model question, an Unread view is SUPPOSED to be volatile | | 90 | A saved-query button clears the account selection | workflow | S | **folded into 93** 2026-08-15. Not fixed in place: the button that misbehaves stops being a saved query at all. See `specs/2026-08-15-builtin-filters-design.md` | -| 91 | Double-clicking a thread should open it on its own | workflow | S | open, fully specified 2026-08-15 and ready to build. A drill-down to that one thread, the same result an `id:` query gives. Nothing is built for getting back: the filter buttons already are that | +| 91 | Double-clicking a thread should open it on its own | workflow | S | open, fully specified 2026-08-15 and ready to build. The view is always the whole thread, EXPANDED; the pane shows whichever row was double-clicked, so a reply drills to its thread and not to itself. It is `recoverStaleThread()` triggered by a gesture | | 92 | Nothing distinguishes a tag written by a rule from one the user applied | information | M | **postponed** 2026-08-15 at the user's request: "I don't see the utility, so I don't really know how to answer." Needs per-MESSAGE provenance nothing records, a two-repo format change blank on all existing mail. Reopen only if the need appears in use | | 93 | The query buttons are whatever the user pinned, not a designed set of filters | workflow | M | **done** 2026-08-15, unreleased; see `specs/2026-08-15-builtin-filters-design.md`. Absorbs item 90. Four built-in filters composing with the account dropdown; the user's own queries unpinned, never deleted | | 95 | A query in the overflow menu cannot be run | defect | XS | **done** 2026-08-15, unreleased. Pre-existing and not caused by 93: the entry's action owned a submenu, and Qt emits no `triggered` for those, so the connection had never fired. Surfaced because 93 moved every query into the menu | @@ -498,42 +498,67 @@ bump either way: an ignored optional field is not a breaking change. **Observed (user, notes):** "double clicking a message/thread should open it in its own view (?) UX not sure." -**Clarified by the user, 2026-08-15**, and it is NOT a second window: "I didn't -mean open in a new window, I meant that double clicking on a message/thread in -the left pane should open it by itself, same as searching by id and displaying -only that message." So this is a drill-down into the existing view, the same -result an `id:` query produces, reached with a gesture instead of by typing. +**Specified by the user, 2026-08-15**, in three cases. The view is always the +whole THREAD; what changes is which message the pane shows: -That removes the constraint this entry was mostly made of. No second -`QWebEngineView`, no second render process, nothing reparented: the pane and -the list are the ones already on screen. Size drops from `?` to S. +- Double-click a single message: that message in a view by itself, loaded in + the right pane. +- Double-click a thread: the whole thread, expanded, in a view by itself, with + the FIRST message in the right pane. +- Double-click a reply inside a thread: the whole thread, expanded, in a view by + itself, with THAT REPLY in the right pane. -**Cause (verified in code):** nothing is wired to `doubleClicked` on -`ThreadListView`, so a double-click today is two single-clicks and selects the -row twice. The machinery a drill-down needs already exists and is exercised by -the query bar: `SearchTerm` builds the `id:` query and `runQuery()` runs it. +So a reply does not drill down to itself alone. It drills to its thread, with +itself selected. That is the part an implementation is most likely to get wrong, +because the obvious reading of "open it by itself" is an `id:` query on the +reply, and it is not what was asked for. -**Approach.** On `doubleClicked`, run the query naming that row: `id:` for a -message row, `thread:` for a thread row, built through `SearchTerm` so the -quoting rules stay in one place. +**It is NOT a second window.** The user ruled that out explicitly: "I didn't +mean open in a new window." No second `QWebEngineView`, no extra Chromium render +process, nothing reparented. -**How the user gets back: nothing is built for it.** Answered by the user, -2026-08-15: "I didn't think of a back action, usually I'd go back to a known -list like unread or inbox at that point." The filter buttons are already that, -and they are one click away in every view. A Back action, a history stack and a +**Cause (verified in code):** nothing is connected to `doubleClicked` on +`ThreadListView`, so a double-click today is two single-clicks and selects the +row twice. + +**Approach: this is `recoverStaleThread()` triggered by a gesture.** That +function already does the entire job for the stale-thread notice: it sets +`m_recoverThreadId` and `m_recoverMessageId`, runs a `thread:` query, expands +the thread when the row arrives, selects the target reply once the replies land, +and falls back to selecting the thread when the message has gone. Every case +above maps onto it, with the recovery target set to the double-clicked row's own +message. Reuse it rather than writing a second selection-after-query path. + +Two properties of that code are exactly what this item needs and are already +commented in place: the root card IS the thread's first message and is not among +the children, so a thread double-click is the empty-target case; and a reply +cannot be selected until the replies exist, so the first pass selects the thread +provisionally and refines on the next. + +**Expansion is not automatic and must be asked for.** Nothing in the tree +auto-expands: `expandAll` and `setExpanded` appear nowhere in `mainwindow.cpp`, +so a plain `thread:` query lands on ONE COLLAPSED CARD and the user would still +have to click the expander. `recoverStaleThread()` calls `expand()` on the +thread, which is also what asks the worker for the replies. + +**How the user gets back: nothing is built for it.** Answered by the user: +"I didn't think of a back action, usually I'd go back to a known list like +unread or inbox at that point." The filter buttons already are that and are one +click away in every view. A Back action, a history stack and a restore-the-previous-query scheme were all drafted and are all unnecessary. -That collapses the only open question in this item. A drill-down is an ordinary -query, entered by gesture instead of by typing, and it is left the way every -other query is left. - -**The undo-stack note below therefore stops being a concern.** `runQuery()` -clears the undo stack because its rows are about to be discarded, and a -drill-down discards them exactly as a typed query does. The user already -expects that from the query bar, so the gesture inherits a behaviour that is -consistent rather than surprising. No filter-over-the-model alternative is -needed, and the item stays S rather than growing into item 40's machinery. - +**The undo-stack cost is inherited and acceptable.** `runQuery()` clears the +undo stack because its rows are about to be discarded, and a drill-down discards +them exactly as a typed query does. The user already expects that from the query +bar, so the gesture inherits consistent behaviour rather than a surprise. No +filter-over-the-model alternative is needed, which is what would have made this +item 40's machinery and M rather than S. + +**Constraint.** A double-click also delivers a single click first, which selects +the row and arms the mark-read timer. The drill-down must not mark a message +read that the user only passed through, and the existing single-click path is +what arms it, so the double-click handler has to cancel it the way the +multi-row branch of `onThreadSelected()` does. ## Deferred, unsized, or split out |
