Nest labels, and let each one say how prominent it is

A flat list is fine at five labels and unreadable at thirty, and there was
no way to keep one that matters occasionally without it holding a row for
ever.

A label can now sit under another, and each says whether it belongs in the
sidebar always, only while it has unread mail, or never.

Nesting is display only. The keywords stay flat on the message, which is
what keeps them readable by every other client: moving a label under
another rewrites nothing in the mailbox, and a client that knows nothing
about ihasmail sees exactly what it always did. Both new fields are
optional, so a settings file written before this parses unchanged and
means what it did.

Settings sync between devices, so the tree has to survive shapes that
should not exist. A label whose parent was deleted on another device comes
back to the top level rather than vanishing -- a label that disappears
because something else was deleted is one the reader cannot get back. A
cycle arriving from an older device is broken by treating the label that
closes the loop as a root, so nothing is lost and nothing hangs. The
parent picker will not offer a label's own descendants, so one cannot be
built here in the first place.

A label kept by the unread rule keeps its ancestors, whatever they were
set to. A child cannot be drawn under a parent that is not there, and
promoting it to the top level would silently rearrange the tree at the
moment the reader is least able to explain why. The parent comes back as a
container instead, and its own count still says whether it has anything of
its own.

Unread counts come from one request carrying a query per label rather than
a request each, with limit 0 so the server does not send ids that would
only be thrown away. They refresh on the same beat as the folder counts,
since the things that move them are the same things, and a failure is
swallowed: a count is decoration, and the sidebar draws the label without
one.

Also corrects the Labels page, which said names and colours are kept in
this browser. They live in the account's own Files and follow it between
devices, like every other setting that is not about this screen.
This commit is contained in:
2026-09-01 23:09:21 -07:00
parent d300107be0
commit f7ef886b45
7 changed files with 389 additions and 8 deletions
+19
View File
@@ -215,6 +215,25 @@ and they survive ihasmail entirely. A message can carry any number. They are
managed in Settings Labels, applied from `l` or the context menu, and
optionally listed in the sidebar.
- **Nesting.** A label can sit under another, and the sidebar indents it.
Nesting is **display only** — the keywords stay flat on the message, so
moving a label under another rewrites nothing in the mailbox and a client
that knows nothing about ihasmail sees exactly what it always did. The parent
picker will not offer a label's own descendants, so a loop cannot be built;
and because settings sync between devices, a label whose parent was deleted
elsewhere comes back to the top level rather than disappearing, while a cycle
arriving from an older device is broken rather than hung on.
- **How prominent each one is**: always in the sidebar, only while it has
unread mail, or never. "Only when unread" is the useful one — something filed
two years ago should not hold a row for ever.
- A label kept by that rule **keeps its ancestors**, whatever they were set to.
A child cannot be drawn under a parent that is not there, and promoting it to
the top level would silently rearrange the tree at the moment the reader is
least able to explain why. The parent comes back as a container instead.
- **Unread counts** sit beside each label, fetched for every label in a single
request rather than one apiece, and refreshed on the same beat as the folder
counts — the things that move them are the same things.
## Search
The query runs on the **server**, over the whole mailbox, not over the part the