diff options
Diffstat (limited to 'docs/superpowers')
| -rw-r--r-- | docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md | 105 | ||||
| -rw-r--r-- | docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md | 95 |
2 files changed, 109 insertions, 91 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 3c1c8fd..e5a046e 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 @@ -7215,3 +7215,108 @@ key. A test asserting the resulting 28 alone would stop meaning anything the moment it did, so it asserts the relation as well. --- + +## 142. The composer's formatting buttons share a toolbar with Send and Attach + +**Observed.** The user reads the composer's top row as a menu bar that is not +one, and asks for the formatting controls to move down beside the HTML +checkbox, directly above the editor. + +**Cause, verified 2026-08-23.** `composewindow.cpp:326-397` builds ONE +`addToolBar`, which carries Bold, Italic, Code, the heading and list actions, +Link and Quote, then a separator, then Attach, Remove attachment and Send. +Three different scopes in one row: text formatting, message composition, and +the terminal action. + +**Approach.** Split it. The formatting half moves to a row directly above the +editor, where the text it formats is; Attach, Remove attachment and Send stay +in the window's own toolbar. The HTML checkbox already sits under the editor +and is the anchor the user names. + +**Constraints.** `setInputsEnabled()` disables `m_formatToolbar` wholesale +during a send (`composewindow.cpp:692`), so a split needs both halves disabled, +and a test for the send path that asserts on only one of them would pass +against a live Attach button during a send. + +**Outcome, and the constraint was right twice over.** The existing +`disablingInputsCoversEveryFieldAndTheToolbar` did assert on the toolbar as +one widget, exactly as predicted, so it was strengthened to name every +control BEFORE the split. It then failed on the first build, reporting +`compose_attach` live during a countdown, which is the fault the entry +describes: a file appended to `m_attachments` after `MessageBuilder` has run +is either silently dropped or added to bytes already handed to the send +command, and neither reports anything. + +**The second fault was not predicted and is worth more than the first.** +With every control named, the test then failed on `format_bold`. Disabling a +`QToolBar` greys its buttons but leaves each `QAction` ENABLED, so the +keyboard shortcut still fires: `Ctrl+B` during a send would have edited a +message already being built, through a button that looked unavailable. The +old code was never wrong about this only because the whole row was one +widget whose actions nothing else could reach. `setInputsEnabled()` now walks +`m_formatToolbar->actions()` as well as disabling the bar. + +--- + +## 143. The formatting buttons are text where every editor uses icons + +**Observed.** The user asks for icon-only formatting buttons, "like any other +text editor". + +**Cause.** They were built as text actions, and the composer's toolbar has no +icons at all. + +**Approach.** `QIcon::fromTheme` per CLAUDE.md's rule that chrome is the +system's, keeping the current text as the tooltip so nothing becomes +unnameable. Cheap once item 142 has moved the row, and awkward before, since +the same row would then mix icon-only formatting with text Send and Attach. + +**Constraints.** `format-text-bold` and its siblings are standard freedesktop +names, but a theme may not carry all of them; an action with no icon must fall +back to its text rather than rendering as an empty button. Icon-only is also +the state where the tooltip stops being decoration, so every one needs to be +right. + +--- + +## 144. "Also send a formatted copy" is prominent and says nothing + +**Observed.** The user's note: "I suppose it means 'format/send as html', but +that flag is secondary, doesn't need to be so prominent." + +**Cause, verified 2026-08-23.** `composewindow.cpp:265`. The label describes a +mechanism ("a formatted copy") without naming it, so the reader has to infer +that "formatted" means HTML and that "copy" means an additional MIME part +rather than a second message. + +**Approach.** Two independent halves, and they can ship separately. Say what it +does: something closer to "Send an HTML version as well". And demote it: it is +a per-message override of a config default (`[compose] send_html`), which is +secondary to writing the message. + +**Constraints.** The string is translated, so changing it makes the Italian +entry stale; `ctest -R translations` fails on an untranslated string, which is +the intended safety net rather than an obstacle. Nothing matches on this text, +so CLAUDE.md's "translating a string something matches on" trap does not apply. + +--- + +## 145. Cc and Bcc are permanent rows on every composer + +**Observed.** The user asks for them hidden behind a disclosure next to the To: +field. + +**Cause, verified 2026-08-23.** `composewindow.cpp:251` and `255` add both as +unconditional form rows. Most messages address neither, so two of the four +header rows are usually empty. + +**Approach.** A disclosure beside To: that reveals both together. They expand +automatically, and stay expanded, whenever either already carries a value: a +reply that carries Cc, or a reopened draft, must not hide a recipient the +message is actually addressed to. + +**Constraints.** That auto-expansion is the load-bearing half. A hidden field +holding an address is a message going somewhere the sender cannot see, which is +worse than the clutter this removes. The seeding runs before `buildUi()`'s +`markDirty()` connections per the constructor's ordering comment, so whatever +decides the initial state has to read the seeded values rather than the widgets. 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 5a42fb3..2227019 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 @@ -211,10 +211,10 @@ taking that too literally. | 139 | Forward is reachable only from the Message menu | discoverability | XS | **done** 2026-08-24, unreleased, inside 140/141 as that entry said it would be. Forward is on the message pane's own bar with Compose and Reply | | 140 | Compose, Reply and Forward belong over the message pane, not on the main toolbar | presentation | M | **done** 2026-08-24, unreleased, with 139 and 141, then REVISED the same day after the user looked at it. Reply and Forward move; **Compose stays on the main toolbar**, because the split that survives contact is what the action NEEDS rather than what it is about, and composing needs no message at all. The moved actions leave the toolbar rather than gaining a second home. Same `QAction` objects shown twice over, never copies, so enablement and the menu entries stay single-sourced | | 141 | The message pane has no button bar of its own | presentation | M | **done** 2026-08-24, unreleased, with 139 and 140. The design question the entry flagged was settled with the user: message actions left, view controls right, separated by an expanding spacer, with `toggle_html` the first of the latter. It sits directly above the web view, BELOW the subject and details rows, which was the user's correction after seeing it at the top of the pane read as window chrome. Icons are 7/8 of `toolbar_icon_size` (28 against the user's 32), derived so the relation survives a change to that key. `MessageView::setBarActions()` is the seam, so the pane still knows nothing about `MainWindow`'s action map. Two traps: a toolbar has no `addStretch()`, and `noTwoActionsShareAnIcon` took an UNNAMED `findChild<QToolBar*>` which now has two candidates, so it is pinned to `main_toolbar` or it would assert against the wrong bar and pass while the rule went unchecked | -| 142 | The composer's formatting buttons share a toolbar with Send and Attach | presentation | S | open, 2026-08-23, from the notes. Verified: one `addToolBar` carries Bold through Quote, then Attach, Remove attachment and Send. The user reads the row as a menu bar that is not one. Move the formatting half down to sit directly above the editor, beside the HTML checkbox | -| 143 | The formatting buttons are text, where every editor uses icons | presentation | XS | open, 2026-08-23, from the notes. Follows 142, and cheap once the row moves. `QIcon::fromTheme` per CLAUDE.md's chrome rule, with the text kept as the tooltip | -| 144 | "Also send a formatted copy" is prominent and does not say what it does | presentation | XS | open, 2026-08-23, from the notes. It means "send an HTML part as well as plain text", which the label never says. Secondary to writing the message, so it should read as such | -| 145 | Cc and Bcc are permanent rows on every composer | presentation | S | open, 2026-08-23, from the notes. Verified: both are unconditional `form->addRow` calls. Most messages use neither. Collapse behind a disclosure next to To:, expanded automatically when a draft or a reply already carries a value | +| 142 | The composer's formatting buttons share a toolbar with Send and Attach | presentation | S | **done** 2026-08-24, unreleased, with 143/144/145 to the user's own layout. The one `addToolBar` is gone: the composer has no window toolbar at all. Formatting is a `QToolBar` WIDGET in the central column directly above the editor, Send is a big icon-above-text button beside the headers, Attach and the HTML toggle ride the right end of the editor bar, Remove attachment sits with the attachment list. The constraint this entry named came true: the strengthened send-lock test caught Attach live during a countdown, and then a SECOND fault the entry did not predict, see the section | +| 143 | The formatting buttons are text, where every editor uses icons | presentation | XS | **done** 2026-08-24, unreleased, inside 142. `QIcon::fromTheme` per CLAUDE.md's chrome rule, the words kept as the tooltip, and an action whose theme lacks the name keeps its text rather than rendering an empty button | +| 144 | "Also send a formatted copy" is prominent and does not say what it does | presentation | XS | **done** 2026-08-24, unreleased, inside 142. "Send as HTML", icon and text, alone at the right end of the editor bar where it reads as a control of the editor rather than as a formatting button. The Italian entry was refreshed with it, and `lrelease` reports 477 finished, 0 unfinished | +| 145 | Cc and Bcc are permanent rows on every composer | presentation | S | **done** 2026-08-24, unreleased, inside 142. A `QToolButton` disclosure beside To:. `revealCcBccIfUsed()` is the load-bearing half the entry called for: it only ever SHOWS, never hides, so nothing but the user's own click can make a field holding an address invisible. `ComposeContext` carries no `bcc` at all, so the seeded-Bcc case can only arrive from a reopened draft, which is what its test drives. The LABEL is hidden with each field: a `QFormLayout` holds the two as separate items, so hiding the line edit alone strands a `Cc:` over empty space | | 146 | The unsynced-changes count cannot be opened to see what it counts | information | S | **duplicate of 119**, recorded 2026-08-23 from the notes. Same request, and 119 already carries the blocker: one of the four things the count sums holds no message ids, so a list cannot be complete without changing how the count is kept | | 147 | Toggle unread reads the same whichever way it will go | presentation | S | **duplicate of 99**, recorded 2026-08-23 from the notes. The notes ask for exactly what 99 describes: "Mark as read" on an unread message and the reverse. 99 already records that the label is harder than it looks, since a multi-row selection has no single direction | | 148 | Ctrl+W does not close the composer | discoverability | XS | open, 2026-08-23, from the notes. Verified: nothing binds `Ctrl+W` anywhere, and the composer has no close action of its own. Belongs with item 21's table rather than bound in isolation | @@ -1374,90 +1374,3 @@ what a drafts folder is; they are separate readers and neither should start importing the other. --- - -## 142. The composer's formatting buttons share a toolbar with Send and Attach - -**Observed.** The user reads the composer's top row as a menu bar that is not -one, and asks for the formatting controls to move down beside the HTML -checkbox, directly above the editor. - -**Cause, verified 2026-08-23.** `composewindow.cpp:326-397` builds ONE -`addToolBar`, which carries Bold, Italic, Code, the heading and list actions, -Link and Quote, then a separator, then Attach, Remove attachment and Send. -Three different scopes in one row: text formatting, message composition, and -the terminal action. - -**Approach.** Split it. The formatting half moves to a row directly above the -editor, where the text it formats is; Attach, Remove attachment and Send stay -in the window's own toolbar. The HTML checkbox already sits under the editor -and is the anchor the user names. - -**Constraints.** `setInputsEnabled()` disables `m_formatToolbar` wholesale -during a send (`composewindow.cpp:692`), so a split needs both halves disabled, -and a test for the send path that asserts on only one of them would pass -against a live Attach button during a send. - ---- - -## 143. The formatting buttons are text where every editor uses icons - -**Observed.** The user asks for icon-only formatting buttons, "like any other -text editor". - -**Cause.** They were built as text actions, and the composer's toolbar has no -icons at all. - -**Approach.** `QIcon::fromTheme` per CLAUDE.md's rule that chrome is the -system's, keeping the current text as the tooltip so nothing becomes -unnameable. Cheap once item 142 has moved the row, and awkward before, since -the same row would then mix icon-only formatting with text Send and Attach. - -**Constraints.** `format-text-bold` and its siblings are standard freedesktop -names, but a theme may not carry all of them; an action with no icon must fall -back to its text rather than rendering as an empty button. Icon-only is also -the state where the tooltip stops being decoration, so every one needs to be -right. - ---- - -## 144. "Also send a formatted copy" is prominent and says nothing - -**Observed.** The user's note: "I suppose it means 'format/send as html', but -that flag is secondary, doesn't need to be so prominent." - -**Cause, verified 2026-08-23.** `composewindow.cpp:265`. The label describes a -mechanism ("a formatted copy") without naming it, so the reader has to infer -that "formatted" means HTML and that "copy" means an additional MIME part -rather than a second message. - -**Approach.** Two independent halves, and they can ship separately. Say what it -does: something closer to "Send an HTML version as well". And demote it: it is -a per-message override of a config default (`[compose] send_html`), which is -secondary to writing the message. - -**Constraints.** The string is translated, so changing it makes the Italian -entry stale; `ctest -R translations` fails on an untranslated string, which is -the intended safety net rather than an obstacle. Nothing matches on this text, -so CLAUDE.md's "translating a string something matches on" trap does not apply. - ---- - -## 145. Cc and Bcc are permanent rows on every composer - -**Observed.** The user asks for them hidden behind a disclosure next to the To: -field. - -**Cause, verified 2026-08-23.** `composewindow.cpp:251` and `255` add both as -unconditional form rows. Most messages address neither, so two of the four -header rows are usually empty. - -**Approach.** A disclosure beside To: that reveals both together. They expand -automatically, and stay expanded, whenever either already carries a value: a -reply that carries Cc, or a reopened draft, must not hide a recipient the -message is actually addressed to. - -**Constraints.** That auto-expansion is the load-bearing half. A hidden field -holding an address is a message going somewhere the sender cannot see, which is -worse than the clutter this removes. The seeding runs before `buildUi()`'s -`markDirty()` connections per the constructor's ordering comment, so whatever -decides the initial state has to read the seeded values rather than the widgets. |
