aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md')
-rw-r--r--docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md105
1 files changed, 105 insertions, 0 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.