# Changelog

All notable changes to this project are documented here.

The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

While the version is below 1.0.0, the configuration file format and the
keybinding action names may change in a minor release. 1.0.0 will mark the
point at which they are stable.

## [Unreleased]

### Added

- Menu bar covering every action: File, Edit, Message, View and Help.
- Toolbar with the frequent subset, Sync, Archive, Delete and Undo.
- **Help > Keyboard shortcuts**, listing the current bindings. Generated from
  the actions themselves, so it shows configured overrides rather than a
  hand-written copy of the defaults.
- **Help > About**.
- Default bindings for `spam` and `load_remote`, which previously had none
  and were unreachable until bound by hand.

### Fixed

- Acting on a thread now visibly changes its row. A thread tagged `deleted`
  or `spam` is filled dark red or orange, in white struck-through text, across
  every column. The tag change was already applied, but `Tags` sat after the
  stretching `Subject` column and was pushed off-screen, so Delete looked like
  it had done nothing.
- Thread list columns reordered to Tags, Date, From, Subject. Subject stretches
  and is now last, so no column can be pushed out of view.
- Three default bindings never fired. Typing a capital sends `Shift`+the key,
  but `N`, `F` and `G` were stored as the unshifted key, which no keystroke
  produces, leaving `toggle_unread`, `flag` and `sync` dead. A bare capital in
  `[keys]` is now read as `Shift`+that letter. As a side effect `y` and `Y`
  are two distinct keys rather than a collision that silently dropped one.
- Modifier shortcuts such as `Ctrl+Q` now work while the query bar has focus.
  The old event filter suppressed every binding there, not only the plain
  letters that would have interfered with typing.

### Changed

- Default bindings moved to modifier shortcuts (`Ctrl+E` archive, `Ctrl+D`
  delete, and so on). Existing `[keys]` entries are unaffected, and single
  letters are still safe to bind. See "Upgrading from 0.1.0" in the README.
- Actions are `QAction`s dispatched by shortcut rather than a hash of
  callbacks behind an event filter, which is what lets them appear in menus.
  The hand-maintained list of registered action names is now derived from the
  actions, so it can no longer drift from them.

## [0.1.0] - 2026-08-03

First release. Reads and organizes a local notmuch-indexed Maildir; it does
no network protocol work at all, since fetching and sending are left to
external commands.

### Added

- Permanent notmuch query bar with saved-query buttons and per-account
  scoping, over a two-pane thread list and message view.
- Threads streamed from the database in batches of 200, so a query over tens
  of thousands of threads paints its first rows immediately and fills in
  behind. Measured at 21 ms to the first batch against a 36,000-thread
  database.
- Whole-thread rendering: every message in a thread renders into one
  document, oldest first, with messages that did not match the current query
  collapsed to one-line stubs. The last message always renders expanded, so
  a thread is never nothing but stubs.
- HTML mail rendered through QtWebEngine, with a locked-down profile:
  off-the-record, no cookies, no cache, JavaScript disabled, and a
  deny-by-default request interceptor. Remote content is blocked until the
  user asks for it, which defeats tracking pixels and read receipts; the
  grant applies to one render and is never remembered.
- Inline `cid:` images served from an in-memory map scoped to the displayed
  thread. References are namespaced per message, so two newsletters sharing
  a Content-ID do not resolve to each other's images.
- Attachment handling that treats MIME filenames as untrusted: names are
  reduced to a basename and refused if the resolved path escapes the chosen
  directory.
- Tagging (archive, delete, spam, flag, read/unread, custom) over a
  multi-thread selection, applied optimistically and reverted if the write
  fails. Thread ids are resolved to message ids in a single combined query
  rather than one query per thread.
- Undo for tag changes, backed by a `QUndoStack`. Entries store thread ids
  and re-resolve them, so undo stays correct after the selection moves on.
  There is deliberately no dry-run and no destructive-action confirmation:
  undo is the better answer for a human at a GUI.
- Sync by running the user's existing script through `QProcess`, joining the
  `flock` that already serializes it against cron rather than reimplementing
  mbsync orchestration.
- Configurable keybindings via the `[keys]` section, validated against a
  known action list so a typo warns instead of binding silently.
- `--version` and `--help`.

### Known limitations

- Compose and send are not implemented; they are planned for v2 and need a
  companion send script that does not exist yet.
- A thread renders as a flat chronological list. Reply structure is
  available from notmuch but is not drawn as an indented tree.
- MIME parsing happens on the UI thread. Opening a very long thread parses
  every message in it, which could stutter; deferred until measured.
- The sync log pane shows what the sync command writes to stdout and stderr.
  A script that redirects its own output to a file will leave the pane
  empty.
