aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-13 19:19:58 +0200
committerDanilo M. <danix@danix.xyz>2026-08-13 19:19:58 +0200
commit0c5eea8f0d0ccc5b8eb6220814c9e212d6c1ccc2 (patch)
tree4275734ed0c31f46609f9fd5b78412209dbeec1c /README.md
parent5e30d1805656895387ba83865d9635caf2e51618 (diff)
downloadqtmaildir-0c5eea8f0d0ccc5b8eb6220814c9e212d6c1ccc2.tar.gz
qtmaildir-0c5eea8f0d0ccc5b8eb6220814c9e212d6c1ccc2.zip
feat(queries): save a query from the UI, and split the buttons off the query row
Second half of item 23, on top of the storage change. A query can now be kept without hand-editing a file, and the row of buttons no longer grows without bound. Ctrl+S opens a dialog on whatever is in the query bar, taking a name, an optional account scope and whether the query is pinned. It preselects the account already chosen in the dropdown, since that is the scope the user is looking at, and it says so when a name is about to replace an existing query rather than refusing the name: overwriting a saved query on purpose is a normal edit, and the only thing worth preventing is doing it without noticing. Saving over an entry keeps the stored entry's unknown fields rather than the dialog's fresh value, so a field written by a later build survives being edited here. The saved queries move to a row of their own beneath the query bar, pinned ones as buttons and the rest behind a More queries menu that only exists when something is in it. The ponytail note that stood in the query row predicted exactly this: an unbounded list of buttons sharing the row squeezed the field. Sent moves down with them and is still not a saved query, for the reason already recorded there. A saved query's account scope goes through the account DROPDOWN rather than being baked into the query text. runQuery() already wraps the query in the selected account's path, so pre-scoping here would apply it twice, and setting the dropdown also shows the user which scope they are in. An unscoped query clears the selection rather than inheriting whatever the last one left, which is the same defect the rules preview had. Seven tests, three mutations. Ignoring the pinned flag fails two of them, pre-scoping the text instead of setting the dropdown fails two, and letting an unscoped query inherit the previous account fails one. The menu-absence test initially passed against no implementation at all, since it only asserted a widget was missing; it now proves the row was populated first, which is the guard that class of test needs. Two existing invariants caught real omissions rather than needing adjustment: every registered action must appear in KeyMap::knownActions(), which is what gives it a configurable binding, and every action needs its own icon. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'README.md')
-rw-r--r--README.md58
1 files changed, 44 insertions, 14 deletions
diff --git a/README.md b/README.md
index 0921791..81ec7d8 100644
--- a/README.md
+++ b/README.md
@@ -106,7 +106,7 @@ identity.
; point: once you zoom with Ctrl+wheel or Ctrl+/Ctrl-, that is remembered
; separately and this value no longer applies.
; message_zoom = 1.0
-; Optional. Which [queries] entry to open at startup, by name. Defaults to
+; Optional. Which saved query to open at startup, by name. Defaults to
; Unread. Falls back to the first saved query if no query by this name
; exists, and warns if you named one explicitly.
; startup_query = Unread
@@ -191,11 +191,6 @@ shopping = #3366cc ; also colours shopping/amazon, shopping/nike, ...
shopping/amazon = #ff9900 ; ... unless the exact tag overrides it
work = #cc4444
-[queries]
-Inbox = tag:inbox
-Unread = tag:unread
-Important = tag:flagged
-
[keys]
Ctrl+E = archive
Ctrl+D = delete
@@ -203,14 +198,47 @@ j = next_thread
k = prev_thread
```
-Saved-query buttons appear in alphabetical order rather than file order:
-QSettings returns keys sorted, and preserving file order would mean
-hand-rolling an INI parser. Which query opens at startup is therefore a
-separate setting, `[general] startup_query`, rather than "the first one".
+### Saved queries
+
+Saved queries live in `~/.config/qtmaildir/queries.json`, not in the config
+file. They are written by the **Save query** action (`Ctrl+S`), which names the
+query in the bar, optionally scopes it to one account, and chooses whether it
+gets a button:
+
+```json
+{
+ "version": 1,
+ "queries": [
+ { "name": "Inbox", "query": "tag:inbox", "pinned": true },
+ { "name": "Unread", "query": "tag:unread", "pinned": true },
+ { "name": "Billing", "query": "from:billing", "account": "work" }
+ ]
+}
+```
-The button text is the key you write here, so these names are yours to
-choose. `Important = tag:flagged` and `Flagged = tag:flagged` run the same
-query and differ only in what the button says.
+The order in the file is the order the buttons appear in, so rearranging them
+is a matter of moving lines. `pinned` decides between a button and the **More
+queries** menu, which keeps the row usable once you have more than a handful.
+`account` names an `[account.<key>]` section and scopes the query to it, the
+same as choosing that account in the dropdown; leave it out for a query that
+spans every account.
+
+The name is what the button says, so `Important` and `Flagged` can run the same
+query and differ only in the label.
+
+**Upgrading from 0.17.0 or earlier.** Saved queries used to live in a
+`[queries]` section of `qtmaildir.conf`. The first launch after upgrading reads
+that section, writes `queries.json` from it, and marks every entry pinned so
+your buttons stay where they were. Your config file is not modified: the old
+`[queries]` section is left exactly as it is, ignored from then on, and you can
+delete it by hand whenever you like. The reason it is not removed for you is
+that rewriting the file would drop your comments and reorder your keys.
+
+One behaviour changes with the move. Buttons used to appear in alphabetical
+order, because the INI backend returns keys sorted and preserving file order
+would have meant hand-rolling a parser. They now follow the file. If
+`startup_query` names a query that does not exist, the fallback is likewise the
+first query in the file rather than the alphabetically first one.
### Sent mail
@@ -253,7 +281,8 @@ behaves like any other query, threads and all.
## The query bar
The bar at the top takes a notmuch query and shows the matching threads.
-Saved queries from `[queries]` sit beside it as buttons.
+Saved queries sit on their own row beneath it: the pinned ones as buttons, the
+rest behind **More queries**. `Ctrl+S` keeps the current query as a new one.
Completion helps with the syntax rather than replacing it. `Ctrl+Space`
opens the popup, and ordinary typing keeps it up to date. Candidates carry a
@@ -478,6 +507,7 @@ Defaults, all rebindable through `[keys]`:
| `Ctrl+I` | `flag` | Mark important (adds `flagged`) |
| `Ctrl+L` | `focus_query` | Focus and select the query bar |
| `Ctrl+Space` | `complete_query` | Focus the query bar and offer completions |
+| `Ctrl+S` | `save_query` | Keep the current query as a saved query |
| `Ctrl+H` | `toggle_html` | Switch the thread between HTML and plain text |
| `Ctrl+M` | `load_remote` | Load remote images for the current thread |
| `Ctrl+T` | `edit_tags` | Add or remove any tag on the selected threads |