| Age | Commit message (Collapse) | Author | Files | Lines |
|
Twelve tasks, TDD throughout, tokenizer first: the parsing rules are where
the defects will be and they need neither a widget nor a database to test.
CompletionEntry gets its own header. Config needs it and QueryCompleter
needs Config, so declaring it in querycompleter.h would make the two headers
include each other.
Also corrects the spec: NmTags already exists in nmraii.h, it does not need
adding.
|
|
Mimetypes are the one completion list with no enumerator and an open-ended
set, so [completion] extra_mimetypes appends to the built-ins. Appending
rather than replacing means a typo or a short list cannot leave completion
worse off than the defaults.
Entries are comma-separated with an optional description after '|'. The two
separators differ because QSettings splits comma lists itself, so a
description containing a comma would otherwise be torn into two entries.
The other lists stay fixed: prefixes come from notmuch, paths from the
configured accounts, dates are closed once symbolic and relative forms are
covered, and tags are read from the database. A user-editable copy of any of
them would only drift from its source. For prefixes specifically, the upgrade
path is deriving the list from the installed notmuch, not making it editable.
|
|
Design for backlog item 17. Completion covers query prefixes, tag values,
date values, path values and mimetype values, each carrying a description
so the bar documents the query language while it is typed.
A new QueryCompleter class owns it, with the cursor-context tokenizer as a
pure function so the parsing rules are testable without a widget or a
database. NotmuchWorker gains an all-tags call, which did not exist.
Addresses for from:/to: are out of scope: libnotmuch exposes no all-addresses
call. Both prefixes still appear so the vocabulary reads complete.
Account maildirs belong to path:, not folder:. Account::scopedQuery builds
path:"<maildir>/**", and folder: is a different matcher in notmuch, against
the folder name rather than the directory path.
Also records the translatable-strings rule in CLAUDE.md. Existing code is
not yet audited against it.
|
|
The manual verification notes named the real threads used to test against a
live mailbox. Most of it was incidental, but one was not: the automated
sender identified in items 12, 13 and 16-18 is a healthcare provider's
patient-notification service, and naming it disclosed a medical
relationship. Also removed a correspondent's username, a locatable GitHub
thread id, a real maildir name in the spec's example config, and absolute
home paths.
Every finding is restated generically and none lost substance: "a message
with 3 inline cid: parts" carries the same technical weight as naming the
sender did. Counts and timings are kept deliberately, since those are the
evidence behind the claims rather than anything identifying.
Commit history and the v0.1.0 tag were checked and were already clean, so no
history rewrite is needed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Runs the configured sync script through QProcess, merging stdout and
stderr into one log so a failing mbsync run has something to show. The
script is never run through a shell: the command is a config value, and
splitCommand keeps its arguments literal.
Two fixes against the drafted version:
- start() no longer calls waitForStarted(). It blocked the UI thread for
up to five seconds, which contradicts the spec's requirement that the UI
stay usable during sync, and it swallowed launch failures into a bare
false return. A missing script now surfaces asynchronously through
errorOccurred as finished(false, -1) with an explanatory log line, so
the spinner cannot hang with nothing to explain it.
- Removed a double-emit guard I had added on the assumption that QProcess
follows errorOccurred(FailedToStart) with finished(). Verified it does
not: FailedToStart is emitted instead of finished, never before it. The
guard was dead state and the comment justifying it was wrong.
Also corrects the sync interval throughout: the user's cron runs every 10
minutes, not hourly. The shorter interval strengthens the flock rationale
rather than weakening it, since collisions with a manual sync are that
much more likely.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
The spec deferred NotmuchWorker to manual verification on the grounds
that testing it needs a real notmuch database. Building a fake one in a
temporary directory answers that objection, so Task 8 now gets real
coverage.
This is the only code in the project that writes to a notmuch index, so
a bug there corrupts real mail state. It was the wrong place to have the
weakest verification.
Task 13's manual checklist stays, as confirmation against real data
rather than as the only coverage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
The interceptor previously trusted the whole qtmaildir: scheme, so a
hostile message body referencing qtmaildir://anything would have been
allowed. That made the interceptor's correctness depend on the scheme
handler in a different, later task.
It now trusts only the exact base URL passed to setHtml() and fails
closed when none is set. Records the resulting obligation on MessageView
to call setDocumentUrl() with that same URL.
Also corrects the attachment path guard to a separator-aware comparison
and notes that it is unreachable defence-in-depth: safeFilename() runs
first, so no caller-supplied name can reach it. A test driving saveTo()
expecting refusal cannot pass; test safeFilename() instead.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Accounts use [account.work] rather than [account/work]: QSettings' INI
backend treats "/" as its own hierarchical group separator, so a literal
slash in a section header parses as a nested group and trips
QSettings::FormatError, silently breaking childGroups() enumeration. A
dot carries no such meaning and keeps the format flat.
Saved-query order is alphabetical (QSettings::childKeys() sorts), not
file order; documented in code and tests rather than left to a false
assumption.
|
|
Both were narrowed in the first draft and are now in scope. Newsletter
threads make whole-thread rendering fundamental, and bulk archive is the
main thing a GUI buys over neomutt.
Two consequences fell out of thread rendering and are recorded in both
documents. The thread renders as one document in one web view rather than
one view per message, since a view each would spawn a Chromium render
process each. Sharing a document makes cid: references collide when two
messages use the same Content-ID, which is common across newsletters from
one sender, so every reference is namespaced to cid:<prefix>!<id>.
Deciding which messages render expanded needs the query, so loadThread now
intersects it with the thread and MessageRef carries a matched flag.
Batch tagging resolves thread ids to message ids on the worker in one
combined query. Undo stores thread ids and re-resolves, staying correct
after the selection moves.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Qt6/notmuch mail client, GUI counterpart to neomutt. Read and organize
only in v1; compose and send deferred to v2.
Key decisions and their reasoning are recorded in the spec: C++/Qt6 over
PySide6, QtWebEngine over QTextDocument for HTML mail, GMime for parsing,
direct libnotmuch rather than shelling out to mailctl, and calling the
existing sync script rather than reimplementing it so qtmaildir stays
inside the flock guard shared with the hourly cron sync.
Dependency availability verified on the development machine: Qt6 6.11.1
(WebEngine included in the monolithic package), libnotmuch 0.39, GMime
3.2.15. Notably notmuch installs no notmuch.pc, so CMake must locate it
without pkg-config.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|