diff options
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.md | 116 |
1 files changed, 8 insertions, 108 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 5e97337..e60b88c 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 @@ -87,7 +87,7 @@ taking that too literally. | 20 | Thread view does not match the user's mental model | presentation | L | **done** 2026-08-10, as the card list; see 53 | | 21 | Default shortcuts are not sensible enough | discoverability | S | open | | 22 | Translatability audit and i18n wiring | correctness | M | open | -| 23 | No way to save a search query from the UI | workflow | M | open, specified 2026-08-13; see `specs/2026-08-13-saved-queries-design.md`. Saved queries move to `queries.json`, carrying order, `pinned` and account scope | +| 23 | No way to save a search query from the UI | workflow | M | **done** 2026-08-13, shipped in 0.18.0; see `specs/2026-08-13-saved-queries-design.md` | | 24 | No right-click actions on the thread list | discoverability | S | **done** | | 25 | No select-all, and bulk actions are undiscoverable | workflow | S | **done** | | 26 | No way to add or remove an arbitrary tag from the UI | workflow | S | **done** | @@ -139,14 +139,15 @@ taking that too literally. | 72 | No khard/khal integration | workflow | ? | open, unspecified; the user places it after send, so v2 at the earliest | | 73 | This backlog is past four thousand lines | maintenance | S | **done** 2026-08-13; 5056 lines to 578, closed sections moved to `2026-08-03-post-0.1.0-usability-closed.md` | | 74 | "Searching..." keeps claiming a query is running while rows are already arriving | feedback | XS | open; cause measured 2026-08-11, the delay itself is the cold page cache and is not fixable here | -| 75 | The tagging rules window forgets its size and its column widths | persistence | S | **done** 2026-08-13 on `rule-builder`, unreleased. The window-kind question is left open, see the closed-items file | -| 76 | Every field in the rules dialog is free text, so a rule is easy to get wrong | workflow | M | **done** 2026-08-13 on `rule-builder`, unreleased. See `specs/2026-08-13-rule-builder-design.md` | -| 77 | No way to see what a rule would collect, in the thread list | workflow | S | **done** 2026-08-13 on `rule-builder`, unreleased | +| 75 | The tagging rules window forgets its size and its column widths | persistence | S | **done** 2026-08-13, shipped in 0.17.0. The window-kind question is left open, see the closed-items file | +| 76 | Every field in the rules dialog is free text, so a rule is easy to get wrong | workflow | M | **done** 2026-08-13, shipped in 0.17.0. See `specs/2026-08-13-rule-builder-design.md` | +| 77 | No way to see what a rule would collect, in the thread list | workflow | S | **done** 2026-08-13, shipped in 0.17.0 | | 78 | No way to build a rule from something visible in a message | workflow | M | open; wants 76 first, so the created rule lands in a form that can hold it | -| 80 | A rule with many conditions squeezes the rule list to one visible row | defect | XS | **done** 2026-08-13 on `rule-builder`, unreleased. Follows item 76 | +| 80 | A rule with many conditions squeezes the rule list to one visible row | defect | XS | **done** 2026-08-13, shipped in 0.17.0. Follows item 76 | | 79 | Opening the rules dialog and saving destroys the first rule | defect | XS | **fixed on `rule-builder`** 2026-08-13, unreleased. Shipped in 0.16.0; damaged one real rule, repaired by hand | -| 81 | No way to turn a saved query into a tagging rule | workflow | S | open; depends on 23, which builds the dialog, and is excluded from its spec on purpose. Writes to the shared rules file, so it spans this repo and `mailctl` | -| 82 | A saved query cannot be edited, unpinned or deleted from the UI | defect | S | **done** 2026-08-13 on `saved-queries`, unreleased. Right-click offers Edit, Pin/Unpin and Delete | +| 81 | No way to turn a saved query into a tagging rule | workflow | S | open; 23 has shipped, so the dialog it depends on exists. Writes to the shared rules file, so it spans this repo and `mailctl` | +| 82 | A saved query cannot be edited, unpinned or deleted from the UI | defect | S | **done** 2026-08-13, shipped in 0.18.0. Right-click offers Edit, Pin/Unpin and Delete | +| 83 | A rule named with spaces is written to the file and dropped by every reader | defect | S | **done** 2026-08-14, unreleased. The name is sanitised into an id, save validates, a bad id loads for repair | Sizes are rough: XS under an hour, S a sitting, M a session. @@ -210,40 +211,6 @@ literal, the descriptions are prose. **Verification:** run `lupdate` and read the generated `.ts`. A string that does not appear there is not translatable, whatever the source looks like. -## 23. No way to save a search query from the UI - -**Observed (user, 2026-08-04):** saved queries live in the config file only. -There is no way to keep a query you have just written without editing -`qtmaildir.conf` by hand. Underneath it, every saved query becomes a button -(`src/mainwindow.cpp:557`), so the query row grows without bound and cannot tell -Inbox from a one-off search. - -**Specified 2026-08-13. Read -`specs/2026-08-13-saved-queries-design.md` instead of planning from here.** - -The three things that decide whether this can be picked up: - -- **Saved queries move out of `[queries]` into - `~/.config/qtmaildir/queries.json`**, gaining an order, a `pinned` flag and a - per-query account scope. The INI cannot express order at all: `childKeys()` - returns keys alphabetically and `src/config.cpp:401` already records that a - hand-rolled parser would be needed to change it. -- **It stays a single-repo change.** The format takes the shape of `rules.json`, - a versioned document preserving unknown fields, but none of its - two-implementation machinery, because queries have one reader. -- **`startup_query`'s fallback changes meaning**, from alphabetically-first to - first-in-the-user's-order. User-visible, so this is a minor bump and wants a - changelog line. The README documents `[queries]` in three places, one of which - explains the alphabetical ordering this removes. - -**Relation to item 10.** Item 10 is postponed, but its second half proposed -exactly this: "saved queries that carry their own account scope, so one action -gets there". The account scope in the save dialog answers it as a side effect. -Do not reopen item 10 to do it: the user postponed it and asked that the -remaining work not be proposed unprompted. - -**Item 81 depends on this** and is deliberately not part of it. - ## 36. `test_mainwindow` cannot reach the worker **Observed:** twice in one session (0.8.0), a defect could not be given a @@ -575,73 +542,6 @@ failing silently. **Size: S** on top of 23, and not meaningful before it. -## 82. A saved query cannot be edited, unpinned or deleted from the UI - -**Observed (user, 2026-08-13):** hand-testing item 23. The user saved a query, -then asked how to unpin it, and there is no answer that does not involve either -a text editor or retyping the whole query. - -**Cause:** item 23 specified saving and nothing else, and that is exactly what -shipped. `SaveQueryDialog` opens on the contents of the query BAR, not on a -stored entry, so the only route to changing one field of an existing query is to -reconstruct the whole query, name it identically, and let -`MainWindow::saveCurrentQuery()` replace it by name. There is no delete at any -price: nothing in the UI removes an entry from `queries.json`. - -This is a defect rather than a missing enhancement. An action that creates -something the UI cannot then edit or remove is incomplete, and the user hit it -within minutes of the first hand test. - -**Approach.** A context menu on a saved-query button and on each **More -queries** entry, offering Edit, Unpin (or Pin) and Delete. - -- **Edit** opens `SaveQueryDialog` prefilled from the STORED entry rather than - from the query bar. The dialog already carries every field it needs; what it - lacks is a constructor that takes a `SavedQuery`. -- **Unpin** is a one-field write and does not need the dialog at all. -- **Delete** removes the entry and rewrites the file. - -**Constraints.** - -- `saveCurrentQuery()` already merges an existing entry's `unknown` fields over - the dialog's fresh value, and every one of these paths must do the same or a - field written by a later build is dropped by an edit here. -- Renaming through Edit is a rename, not a second entry: match on the name the - dialog was OPENED with, not the one it returns, or renaming silently creates a - duplicate and leaves the original behind. -- Delete is destructive and the file is user config, so it is one of the few - places in this application that wants a confirmation. The no-confirmation rule - in CLAUDE.md is about tag mutations, which are undoable through the undo - stack; this is not on that stack and cannot be undone. -- A test must exercise every route the way item 75's did not: the dialog's - Cancel goes through `done(int)` and never sends a `QCloseEvent`. - -**Size: S**, and it should land before the saved-query work is called done. - -**Done 2026-08-13.** A context menu on each button and each menu entry, with -Edit, Move to menu / Show as a button, and Delete. Every path goes through one -`replaceSavedQuery()`, matched on the name the dialog was OPENED with, so a -rename replaces rather than duplicating, and merging the stored entry's unknown -fields in one place rather than three. - -Two things the approach above did not anticipate. A GENERATED entry has no -query to edit, so the dialog shows its composed query read-only rather than -offering a field that changes nothing, and carries `generated` and `flat` -through an edit rather than letting it decay into a plain entry holding a -snapshot. And the overwrite notice had to learn to ignore the entry being -edited: warning that "Inbox" already exists while editing Inbox is noise. - -It also exposed a defect that predated it. `rebuildSavedQueryRow()` called -`deleteLater()` on the old row, which defers destruction to the event loop, so -the stale row went on answering `findChild()` and every lookup after a rebuild -saw the state from before the edit. It was already reachable from the save path. -Fixed by reparenting the row out immediately. - -The unknown-fields test initially passed against the merge being deleted: it -drove UNPIN, which copies the stored entry and therefore carries `unknown` -along by itself. It now goes through the edit path with a replacement that has -none, which is what the dialog actually returns. - ## Deferred, unsized, or split out Items noted while triaging but not part of the original list. Same numbering |
