diff options
| -rw-r--r-- | docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md | 75 | ||||
| -rw-r--r-- | docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md | 73 |
2 files changed, 76 insertions, 72 deletions
diff --git a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md index 7314700..94a02d2 100644 --- a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md +++ b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md @@ -7349,6 +7349,81 @@ importing the other. --- +## 152. Signatures are not managed at all + +**Observed (user, 2026-08-24, from the notes):** listed under "some basic +functionalities not brainstormed which didn't enter the first Send +implementation", as: + +> signatures: +> - not tied to an account, with a switch in the editor bar UI. + +**Specified 2026-08-24.** The design is in +`docs/superpowers/specs/2026-08-24-signatures-design.md`. Read that rather +than this section, which records only what the brainstorm settled and why. + +**The constraint is the shape of the item, and it survived a second key.** +Not tied to an account rules out `[account.*] signature` as the whole answer. +The user then asked for that key anyway, as a convenience, and it does not +reopen the constraint: the account supplies a STARTING value, the editor-bar +switch keeps every signature reachable under any account, and changing From: +stops re-seeding the moment the user touches the switch. Seeding is not +binding. + +**One choice must serve both forms, and that costs nothing.** +`MessageBuilder` already derives `text/plain` from `markdownBody` verbatim and +`text/html` from `MarkdownRenderer::toHtml()` over the same string +(`messagebuilder.cpp:321-324`). A markdown signature in the buffer therefore +gets both, correctly, with no change to `MessageBuilder` and no second code +path. The user's "transparent to the user" requirement is a property the +pipeline already has. + +A two-file variant (`work.md` plus an optional `work.html` overriding the +rendered form) was chosen and then dropped by the user the same session: it +buys designed HTML signatures at the cost of the signature no longer being +visible in the editor, since the two parts diverge and the buffer can hold +only one of them. + +**The switch is stateless, by using the delimiter rather than tracking a +range.** `seedBody()` deliberately refuses to track "my text" and "the quote" +as separate pieces (`composewindow.cpp:640-644`), and a signature switch is a +toggle by definition, so it cannot duck that question the way the quote did. +It answers it without state: the signature is the last `-- ` block not +followed by quoted lines, found by scanning. Nothing to desync from the undo +stack, and it survives editing above it. + +**`signature_position` covers both placements over one scan.** The user's own +habit is `end`, which is the default; `above_quote` exists because other +clients offer it. The scan needed the quote-aware clause for `above_quote` +anyway, so the key is roughly ten lines rather than one, and a naive tail rule +would have eaten the quote under the other placement. + +**A delimiter alone must not authorise a deletion.** The block after `-- ` is +replaced only when its text matches one of the signatures on disk; otherwise +the new one is inserted and nothing is removed. `-- ` can reach the buffer +pasted in with quoted text from another client, and the unguarded scan would +have silently deleted everything after it. The failure is now directional: a +wrong guess adds a visible duplicate rather than losing the user's writing. +Raised by the user against the first draft of this design. + +Two markers were considered for the same problem and refused. A zero-width +character SHIPS in the sent message, fingerprinting the client in outgoing +mail, and must survive the draft round trip through GMime, quoted-printable +and `MimeParser`, which is exactly what normalises such characters away. A +doubled delimiter (`--` plus two spaces) is not the RFC 3676 separator, so no +receiving client would fold or strip the signature, and trailing whitespace is +unreliable through the same pipeline. + +**Two things the design refuses.** No signature editor: the directory is +edited with the user's own editor, and a text editor inside a mail client is +not this project's to build. And a resumed draft seeds nothing, because the +saved body already carries whatever signature it was written with, and +seeding again would put a second one on a message written once. + +**Closed 2026-08-24** (unreleased). See the status table row for the outcome. + +--- + ## 153. A draft cannot be opened for editing, so it is write-only **Observed (user, 2026-08-24).** Found the moment item 138 gave drafts a diff --git a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md index c2c82ea..b86740e 100644 --- a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md +++ b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md @@ -221,7 +221,7 @@ taking that too literally. | 149 | A reply's cursor lands on the attribution line, not on blank space | defect | XS | **done** 2026-08-24, unreleased, in TWO passes. The first fixed the cursor within each branch (`End` under Above, `Start` under Below) and the user still saw the old layout, because the branches were already right and the DEFAULT was wrong: `above` shipped, and the layout asked for is what `below` produces. Default flipped, and the composer now focuses the body whenever To: is already filled, which a Reply and a Forward always are. Both halves were invisible to the existing `theQuotePositionDecidesWhereTheQuoteLands`, which asserts the quote's position and never the cursor's | | 150 | The receive-only ribbon stays up after the message that raised it is gone | defect | S | **done** 2026-08-24, unreleased. One line in `MessageView::clear()`, beside the blocked-content bar, the stale notice and the attachment bar it already reset by hand. Only `setReceiveOnlyAccount()` hid the ribbon, which every SELECTION change reaches, so a row-to-row move was never the reproducer: it survived the FOUR routes that blank the pane without one (`clear_pane`, `clear_selection`, a new query, a multi-row selection). The first test written for it passed against the defect for exactly that reason | | 151 | The message-pane bars blend into the UI and carry no severity | presentation | S | **done** 2026-08-24, unreleased. Two severities as the user asked: yellow for a warning that only explains (the receive-only ribbon), blue for one offering an action (remote content blocked, stale thread), each with its own light and dark set read off `QPalette::Base` as `HtmlBuilder` does. The blocked row had to become a WIDGET first: it was a bare `QHBoxLayout`, which has nothing to paint a ground on, and its six `hide()` sites then had to move to the wrapper or a painted empty strip would show. Both action bars put the button right of a stretch | -| 152 | Signatures are not managed at all | v2 | S | **specified** 2026-08-24, unbuilt. Design in `specs/2026-08-24-signatures-design.md`; read that, not this row. Sized `?` until the brainstorm; it is an S. One markdown file per signature under `~/.config/qtmaildir/signatures/`, spliced into the composer buffer, so `MessageBuilder` needs NO change at all: it already derives both parts from one string, which is the transparency the user asked for. The constraint holds, and the per-account key the user then asked for does not break it: an account SEEDS the choice, the editor-bar switch keeps every signature reachable | +| 152 | Signatures are not managed at all | v2 | S | **done** 2026-08-24, unreleased. One markdown file per signature under `~/.config/qtmaildir/signatures/`, spliced into the composer buffer by the `Signatures` namespace and chosen from a `QToolButton` switch on the editor bar. `[compose] signature` seeds a new message, `[account.<key>] signature` overrides per account, `[compose] signature_position` picks end or above_quote. `MessageBuilder` is untouched: it already derives both parts from one string, the transparency the user asked for, and the per-account key does not break "not tied to an account", since an account SEEDS the choice while the switch keeps every signature reachable. A hand test caught one guard defect (a trailing newline defeated the replace guard); fixed and regression-tested | | 153 | A draft cannot be opened for editing, so it is write-only | defect | M | **done** 2026-08-24, unreleased. `ComposeContextBuilder::forDraft()` reads a draft back into a context; a new `Kind::Draft` seeds the fields verbatim, takes the body with no quote framing, and carries `draftPath` so the autosave REPLACES the file instead of leaving a second copy. `MimeParser` gained `bcc`, which nothing read before: `MessageBuilder` writes Bcc into the draft deliberately, so a resumed draft that ignored it would silently drop every blind recipient. Reachable by double-click and by an `edit_draft` action, gated on the file being in a configured drafts folder because opening ordinary mail this way would make the first autosave DELETE a received message. Found a live defect on the way, see the section | | 154 | No read confirmation | v2 | ? | open, 2026-08-24, from the notes. `Disposition-Notification-To`, which is a header `MessageBuilder` would add and a request the message pane would have to honour or ignore on the receiving side. Unspecified: whether this is send-side only, and what the reader is asked | | 155 | No urgency switch on an outgoing message | v2 | S | open, 2026-08-24, from the notes: low, regular, high. `X-Priority` and `Importance`, headers `MessageBuilder` adds; regular writes neither. A control in the composer, and the same question item 144 answered for the HTML toggle applies to where it sits | @@ -1334,77 +1334,6 @@ than a slow one. --- -## 152. Signatures are not managed at all - -**Observed (user, 2026-08-24, from the notes):** listed under "some basic -functionalities not brainstormed which didn't enter the first Send -implementation", as: - -> signatures: -> - not tied to an account, with a switch in the editor bar UI. - -**Specified 2026-08-24.** The design is in -`docs/superpowers/specs/2026-08-24-signatures-design.md`. Read that rather -than this section, which records only what the brainstorm settled and why. - -**The constraint is the shape of the item, and it survived a second key.** -Not tied to an account rules out `[account.*] signature` as the whole answer. -The user then asked for that key anyway, as a convenience, and it does not -reopen the constraint: the account supplies a STARTING value, the editor-bar -switch keeps every signature reachable under any account, and changing From: -stops re-seeding the moment the user touches the switch. Seeding is not -binding. - -**One choice must serve both forms, and that costs nothing.** -`MessageBuilder` already derives `text/plain` from `markdownBody` verbatim and -`text/html` from `MarkdownRenderer::toHtml()` over the same string -(`messagebuilder.cpp:321-324`). A markdown signature in the buffer therefore -gets both, correctly, with no change to `MessageBuilder` and no second code -path. The user's "transparent to the user" requirement is a property the -pipeline already has. - -A two-file variant (`work.md` plus an optional `work.html` overriding the -rendered form) was chosen and then dropped by the user the same session: it -buys designed HTML signatures at the cost of the signature no longer being -visible in the editor, since the two parts diverge and the buffer can hold -only one of them. - -**The switch is stateless, by using the delimiter rather than tracking a -range.** `seedBody()` deliberately refuses to track "my text" and "the quote" -as separate pieces (`composewindow.cpp:640-644`), and a signature switch is a -toggle by definition, so it cannot duck that question the way the quote did. -It answers it without state: the signature is the last `-- ` block not -followed by quoted lines, found by scanning. Nothing to desync from the undo -stack, and it survives editing above it. - -**`signature_position` covers both placements over one scan.** The user's own -habit is `end`, which is the default; `above_quote` exists because other -clients offer it. The scan needed the quote-aware clause for `above_quote` -anyway, so the key is roughly ten lines rather than one, and a naive tail rule -would have eaten the quote under the other placement. - -**A delimiter alone must not authorise a deletion.** The block after `-- ` is -replaced only when its text matches one of the signatures on disk; otherwise -the new one is inserted and nothing is removed. `-- ` can reach the buffer -pasted in with quoted text from another client, and the unguarded scan would -have silently deleted everything after it. The failure is now directional: a -wrong guess adds a visible duplicate rather than losing the user's writing. -Raised by the user against the first draft of this design. - -Two markers were considered for the same problem and refused. A zero-width -character SHIPS in the sent message, fingerprinting the client in outgoing -mail, and must survive the draft round trip through GMime, quoted-printable -and `MimeParser`, which is exactly what normalises such characters away. A -doubled delimiter (`--` plus two spaces) is not the RFC 3676 separator, so no -receiving client would fold or strip the signature, and trailing whitespace is -unreliable through the same pipeline. - -**Two things the design refuses.** No signature editor: the directory is -edited with the user's own editor, and a text editor inside a mail client is -not this project's to build. And a resumed draft seeds nothing, because the -saved body already carries whatever signature it was written with, and -seeding again would put a second one on a message written once. - ## 158. A freshly saved draft is invisible until a sync indexes it **Observed (user, 2026-08-24, by hand).** Composing a new message or resuming |
