aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md')
-rw-r--r--docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md61
1 files changed, 61 insertions, 0 deletions
diff --git a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md
index 8cfa6b0..8ae8dff 100644
--- a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md
+++ b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md
@@ -4741,3 +4741,64 @@ how this was found: the suite ran past its two-minute timeout with no output.
The cause is not the account at all, it is `showWarnings()` raising a modal
from the MainWindow constructor for any config problem, with nothing offscreen
to dismiss it. Filed as item 84.
+
+## 85. Nothing on screen can be searched for by right-clicking it
+
+**Observed (2026-08-14).** The user asked, while discussing item 78, to be able
+to right-click a sender, a subject, a date, a tag chip or a piece of selected
+body text and be offered a search built from it, both replacing the query bar
+and narrowing what is already in it. The narrowing case decided the shape: a
+query returning a thousand threads is refined by adding a second condition, and
+before this that meant retyping a query the user never typed.
+
+**Cause.** Not a defect, unbuilt. `MessageView` had no context menu on any
+surface, and `TagStrip` painted chips with no hit test and no signals.
+
+**Done 2026-08-14**, designed in
+`specs/2026-08-14-search-from-message-design.md` and built over eight tasks.
+Five surfaces offer **Search for this** and **Add to search**: the header's
+subject and date, its From/To/Cc for a single-message thread, a tag chip, a
+body selection, and every header per message in the details dialog.
+
+**What is worth keeping from it.**
+
+`SearchTerm` (`src/searchterm.h`) owns the whole query grammar and holds no
+widget, so the quoting is tested without a painter or a web engine. That
+matters because **a mis-quoted notmuch query is not an error, it matches zero**:
+nothing downstream would ever report the feature being broken. `extend()`
+parenthesises both sides, since `a or b AND c` binds as `a or (b AND c)` and
+silently WIDENS a search the user asked to narrow.
+
+**`Qt::RFC2822Date` validates the weekday against the date**, so `Thu, 14 Aug
+2026` parses as invalid: that day is a Friday. Two test fixtures carried a wrong
+weekday, one of them pre-existing, and the failure is indistinguishable from the
+trailing-comment trap `MimeParser::parseDate` exists to handle. Check a date
+fixture with `date -d <yyyy-mm-dd> +%A` rather than writing one from memory.
+
+The Date: parse was **extracted** into `MimeParser::parseDate` rather than
+rewritten, because the existing copy inside a file-local function already
+carried the fix for `Qt::RFC2822Date` rejecting a string with a trailing
+timezone comment. A second parser without it would have lost the date on a
+large share of real mail, showing up only as a menu entry that never appears.
+
+**The header lists its fields rather than hit-testing them.** It is one
+rich-text `QLabel` of up to four lines, and mapping a point through laid-out
+rich text breaks as soon as the label wraps. From/To/Cc are offered exactly when
+the header displays them, which is a single-message thread, so the menu can
+never name a value the header is withholding.
+
+**The details dialog stopped being a `QPlainTextEdit`**, which the user had
+disliked independently. Its plain-textness was a security property rather than a
+style: header values come from strangers and plain text cannot interpret markup.
+Every value label therefore states `Qt::PlainText` explicitly, because a
+`QLabel` guesses under `Qt::AutoText`, and a test enumerates every label and
+asserts the format.
+
+**A modal dialog must close BEFORE the search it asked for runs.** The
+connection is direct, so emitting first runs the query synchronously: the model
+clears and the pane blanks while the dialog is still up, holding the `m_items`
+it was built from. Caught by a test whose mutation check HUNG rather than
+failed, which is what a missing `accept()` does to `exec()`.
+
+**Item 78 stays open**, carrying the rule shortcut alone. The road from a search
+to a rule already exists: save the query, then create a rule from it.