aboutsummaryrefslogtreecommitdiffstats
path: root/mail-overview/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'mail-overview/README.md')
-rw-r--r--mail-overview/README.md254
1 files changed, 0 insertions, 254 deletions
diff --git a/mail-overview/README.md b/mail-overview/README.md
deleted file mode 100644
index 34eaa53..0000000
--- a/mail-overview/README.md
+++ /dev/null
@@ -1,254 +0,0 @@
-# mail-overview
-
-Unread mail across every account, as one waybar number and a drawer behind it.
-Clicking the icon opens the drawer; Escape or a click outside closes it.
-
- ┌──────────────────────────────────────────────────┐
- │ Mail 33 unread │
- ├──────────────────────────────────────────────────┤
- │ ● Account A 2 │
- │ Some Sender Today 06:18 │
- │ [a-list] a subject line that is elided... │
- ├──────────────────────────────────────────────────┤
- │ ● Account B 0 │
- │ ● Account C 31 │
- │ ...three newest threads... │
- │ ● Account D 0 │
- │ ● Account E 0 │
- ├──────────────────────────────────────────────────┤
- │ ● watcher ok · 25 folders │
- ├──────────────────────────────────────────────────┤
- │ [Open qtmaildir] │
- └──────────────────────────────────────────────────┘
-
-## Running it
-
- qs -p .
-
-It is started from `autostart.lua` and reached over IPC, so the shell has to be
-running for the waybar click to do anything:
-
- qs -p ~/Programming/GIT/quickshell/mail-overview ipc call mail toggle
-
-Write that path out in full in the real config. Waybar's `exec` and `on-click`
-have no shell to expand `~`, and neither do Hyprland's Lua strings.
-
-## The accounts are not listed here
-
-`qtmaildir.conf` already has one `[account.<key>]` section per account, and
-`<key>` is exactly the suffix of the notmuch tag `account-<key>`, with a short
-`label` and a `color` alongside. The panel parses that file, in file order, so
-adding an account to qtmaildir makes it appear here with no edit to any QML.
-The file is watched, so that happens without a restart.
-
-The `color` is the account's own, from its config section. It marks the dot
-beside each row and nothing else: per-account identity is not a palette, and
-`Theme` still owns every other colour.
-
-**Parsing walks lines rather than matching one regex over the file.** The
-obvious pattern for a section body, everything up to the next `[`, is wrong
-here: several accounts have folders named like `[Gmail]/Bozze`, so the body
-ended at that bracket, before the `label` line, and three of five accounts
-quietly fell back to displaying their raw key. Nothing errored, because falling
-back is a legitimate path for an account with no label.
-
-## Counting
-
-Every count is `tag:unread and tag:inbox`. Plain `tag:unread` also counts
-archived-but-unread mail and mailing list traffic that was never in the inbox,
-which for one account here was the difference between 32 and 41; the
-inbox-scoped number is the one that means new mail worth looking at.
-
-**By the `account-*` tag, never by a `path:` glob.** qtmaildir itself scopes an
-account with `path:"<maildir>/**"`, and copying that query would be wrong for
-this panel. notmuch deduplicates by message id, so a message that arrived at
-two of the configured addresses is one message with two file paths: a glob
-counts it under both accounts, and the rows then sum to more than the total the
-waybar icon shows. Measured here, five accounts summed to 102 against a global
-total of 101. The tag is a property of the message, so it is singular and the
-rows always sum to the header. The cost is that such a message appears under
-only the account the `post-new` hook attributed it to, which is the right trade
-for an overview whose headline figure is a single number.
-
-## notmuch fails two ways, and only one is detectable
-
-A query notmuch rejects prints nothing and exits 1:
-
- notmuch count 'tag:unread and ('
-
-A query Xapian merely misparses returns a plausible wrong number and exits 0:
-
- notmuch count 'tag:unread and ((' # 41, exit 0
- notmuch count 'tag:unread and tag:' # 3, exit 0
-
-So the exit status is not the test, and neither is any check that a wrong
-number could pass. Counts are validated as non-negative integers, which catches
-the first case, where empty output would otherwise render as an empty inbox.
-The second is not catchable; the defence against it is that the queries are
-fixed strings and are never built from anything.
-
-An unknown count shows a dash, never a zero, for the same reason the VM panel
-refuses to substitute libvirt's host-side figures: a number that is actually a
-failure reads as a fact.
-
-## The waybar module
-
-A `custom` module in **continuous mode**: the script never exits, prints one
-JSON line per database commit, and waybar redraws on each line. There is no
-`interval`, and no daemon to supervise, because waybar owns the process.
-
- "custom/mail": {
- "format": "<span font='18px'>󰇮</span> {}",
- "return-type": "json",
- "exec": "~/Programming/GIT/quickshell/mail-overview/waybar-mail.sh",
- "on-click": "qs -p ~/Programming/GIT/quickshell/mail-overview ipc call mail toggle",
- "on-click-right": "~/bin/mailsync.sh",
- "tooltip": false
- }
-
-The count drops the moment mail is read in qtmaildir and rises the moment
-mbsync commits, because both write the database and the watcher sees either.
-
-Two details in that loop are load-bearing. The watch is on the **xapian
-directory**, not on a file inside it: a commit replaces files, and a watch held
-on a filename dies with it. And a short sleep coalesces the several writes of
-one commit, without which the module redraws three or four times per sync with
-intermediate counts.
-
-The old setup this replaces ran three copies of a python script polling the
-Gmail API with stored credentials, covered three of the five accounts, and
-opened Thunderbird. Everything it fetched over the network was already in the
-local index.
-
-## Notifications on arrival
-
-`mail-notify.sh` sends one notification per account when mail lands, with the
-newest three threads' sender and subject and a `+N more` line when the batch is
-bigger. Each message is a bullet on its own line with a blank line between,
-under a bold `New Mail` heading with the account and count as the second line.
-Started from `autostart.lua`, it runs for the whole session.
-
- /home/you/Programming/GIT/quickshell/mail-overview/mail-notify.sh
-
-It watches the same xapian directory the waybar module does, for the same
-reason and with the same 0.3s debounce. It is a **separate process rather
-than part of `waybar-mail.sh`**, which already has that edge: waybar owns
-that script's process, so a bar restart would stop notifications with nothing
-reporting it.
-
-**A commit is not the same as new mail.** Reading a message in qtmaildir drops
-its `unread` tag and commits; so does tagging. Arrival is found with notmuch's
-revision counter instead:
-
- notmuch count --lastmod 'tag:unread and tag:inbox'
-
-which prints count, database UUID and revision, tab separated. Each account is
-then asked what it gained in `lastmod:<prev+1>..<cur>`, the lower bound one
-revision after the stored one so it is exclusive. A count delta was
-rejected because it cannot name senders, and a `date:` watermark because
-`date:` is the message's own Date header: backdated mail would never notify
-and future-dated mail would notify forever.
-
-Position is kept in `~/.local/state/mail-notify.lastmod`, written by atomic
-replace, holding the **UUID as well as the revision**. Revisions only compare
-within one database, so a rebuild restarts the counter and a stored revision
-from the old one means nothing.
-
-**Missing, corrupt or mismatched state seeds silently**, recording the current
-revision and notifying nothing. Without that floor, `lastmod:0..` matches every
-unread message ever: 101 of them here, which is a wall of popups at every
-login. The same applies to a restart, so mail that arrived while it was down is
-never notified. The waybar count is still right and the drawer still shows it,
-so nothing is lost but a stale popup.
-
-The notification is sent with dunstify. The icon is passed as an **absolute
-path** to the active icon theme's `mail-unread-multiple`, because this dunst
-resolves an icon *name* only through the `icon_path` in `dunstrc`, and that
-path holds no mail icon, so a name renders no icon at all. The click action
-(`-A default,open`) makes dunst prepend an `(A)` action indicator when
-`show_indicators` is on, so `dunstrc` sets `show_indicators = no`. A middle
-click (dunst's `do_action`) opens qtmaildir, but not the account it belongs
-to: `qtmaildir` takes no arguments and `startup_account` is a static setting
-rather than a flag, the same limitation the thread rows have.
-
-`test-mail-notify.sh` is the check. It sources the script as a library and
-asserts on the functions that only move text around, so it needs no mail and no
-notmuch:
-
- ./test-mail-notify.sh
-
-## Geometry, and the one property that differs from the other panels
-
-The window is a fullscreen overlay with a dimmed backdrop and the drawer itself
-anchored to its top right, which is the idiom `vm-manager` and `appearance` both
-use and what gives click-outside-to-close and a focusable item for Escape.
-
-It differs from those two in one property: `exclusionMode` is `Normal`, not
-`Ignore`. Waybar claims an exclusive zone at the top of this screen, so
-respecting it places the overlay below the bar with no height constant here to
-drift. Measured: waybar at `y=-540 h=42`, this overlay at `y=-498 h=1038`,
-starting exactly where the bar ends. The backdrop therefore never dims waybar.
-
-**Key events reach a focused item, not a window.** Setting
-`WlrLayershell.keyboardFocus` is necessary but not sufficient, and
-`Keys.onEscapePressed` on a `PanelWindow` never fires, so the focus sits on an
-inner `Item`.
-
-## Thread rows are read-only
-
-Each account shows its three newest unread threads as author, relative date and
-subject. Clicking one launches qtmaildir and nothing more, because `qtmaildir`
-accepts no command line arguments: there is no way to tell it which thread to
-open. `startup_account` in its config is a static setting, not a flag, so
-"open on this account" is unavailable for the same reason. Marking read from
-here was rejected rather than deferred: it would write the database behind a
-possibly running client.
-
-"Open qtmaildir" launches the client. There is no "Sync now" button: the
-watcher triggers a sync the moment mail lands, the cron tick is the backstop,
-and `on-click-right` on the waybar module runs `~/bin/mailsync.sh` for a manual
-pull.
-
-## The watcher status dot
-
-Below the accounts, above the button, a coloured dot reports whether
-`mail-watcher` is alive and sane, read from its heartbeat at
-`~/.local/state/mail-watcher.heartbeat`:
-
-- **green** (`watcher ok · N folders`) heartbeat fresh, no dead threads.
-- **yellow** (`N folder(s) dead, check the log`) heartbeat fresh, but a folder
- gave up permanently.
-- **red** (`watcher not running`) heartbeat missing, unparseable, or older than
- 300s.
-
-Backoff never turns the dot: it is normal recovery from a dropped IDLE
-connection, and the watcher's own health check treats it as healthy. The
-staleness rule is the same one `mail-watcher` uses (`heartbeat_is_healthy`),
-reimplemented here in a few lines rather than shelling out to
-`mail-watcher.py --status` on every open.
-
-The heartbeat is read once per open. The drawer is a `LazyLoader`, so closing
-and reopening rebuilds the `FileView` and reads the file current. A file watch
-is deliberately not used: the heartbeat is written by atomic replace (tmpfile
-then rename), so an inotify watch held on the old inode dies with it, which is
-the same trap as watching a file inside the Xapian directory. The cost is that
-a drawer left open does not update until reopened, which for 60s heartbeat data
-is not worth a timer.
-
-## Theme and blur
-
-`Theme.qml` is the shared one: the palette comes from
-`~/.cache/wal/udt-palette.qml` and is watched. It is a fourth identical copy,
-which is a known loose end, not a palette to grow.
-
-Frosting is Hyprland's, matched on this window's namespace:
-
- hl.layer_rule({
- name = "blur-mail",
- match = { namespace = "^(quickshell-mail)$" },
- blur = true,
- xray = false,
- ignore_alpha = 0.1,
- })
-
-Without the rule it still works, rendering flat translucent.