| Age | Commit message (Collapse) | Author | Files | Lines |
|
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>
|
|
QAbstractTableModel over query results, appended in batches so a large
query paints its first screenful immediately. Tag changes apply locally
for optimistic UI; reverting a failed write means calling applyTagChange
again with added and removed swapped, which the round-trip test pins.
Two additions to the drafted version:
- A ThreadIdRole, so a view's QModelIndex maps back to the thread id the
worker speaks without every caller reaching around the model.
- data() checks its own row and column bounds. Qt will not hand out an
out-of-range index and invalidates persistent ones on reset, so this is
unreachable defence rather than a live path; the test says so instead of
pretending to cover it.
Verified by mutation that the empty-batch guard, the ThreadIdRole, and the
full-row dataChanged range each fail exactly one test when removed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Owns the only notmuch database handle. Queries run read-only and emit
threads in batches of 200 with a generation counter so the UI can discard
superseded results. Tag mutation closes the read-only handle, opens
read-write, applies, and closes, holding the process-wide write lock for
milliseconds rather than blocking a concurrent `notmuch new`.
Tested against a throwaway database built in a QTemporaryDir, superseding
the spec's original "no unit test" position: applyTags is the only code
here that writes to a notmuch index. The fixture never touches ~/Mail or
~/.notmuch-config.
Two fixes against the drafted implementation, both caught by mutating the
code and confirming exactly one test failed:
- loadThread conflated "no query given" with "query matched nothing in
this thread", so filtering a thread down to zero matches rendered every
message expanded. Tracked with an explicit haveMatchSet flag.
- applyTags now documents why a stale message id must skip rather than
abort: notmuch_database_find_message reports SUCCESS with a null message
for an unknown id, and the live ids alongside it still need tagging.
Note for fixture authors: notmuch synchronizes maildir flags with tags at
index time, so a file named `...:2,S` is indexed without the unread tag no
matter what [new] tags requests. Unread fixture messages go in new/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
HtmlBuilder renders parsed messages (and whole threads, as one document,
so newsletter threads don't spawn one Chromium process per message) into
the HTML string the web view loads. Plain text is escaped and quote lines
marked; the cid: rewrite is namespaced per message ("<prefix>!<id>") so
two thread messages sharing a Content-ID don't collide.
Hardened namespaceCids beyond the initial sketch after attacking it:
handles unquoted cid: attribute values, background=/poster= (not just
src/href), and CSS url(cid:...) in both style="" attributes and <style>
blocks, all case-insensitively. Replaced the greedy [^"']+ capture with
per-quote-style alternation so two cid: refs on one line can't bleed into
each other.
CidSchemeHandler serves cid: requests from the thread's inline-parts map,
keyed by the same namespaced string, replaced wholesale per thread.
|
|
|
|
|
|
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.
|
|
Maps key sequences to action name strings, with hardcoded vim-style
defaults and QSettings-based [keys] overrides. Unknown actions and
unparseable sequences are collected as warnings rather than treated
as fatal, so a typo in the config cannot silently misbind or crash.
Note: QKeySequence::fromString() on Qt 6.11 does not return an empty
sequence for unparseable input (e.g. "NotAKey++") -- it returns a
non-empty sequence whose toString() is empty. Detection uses that
instead of isEmpty().
|
|
|