aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/specs/2026-08-09-card-list-design.md
AgeCommit message (Collapse)AuthorFilesLines
2026-08-09docs: make the account colour the card's accent, not a chipDanilo M.1-3/+51
The plan left the account chip unspecified, because where it sits on a card was never decided and inventing a place would have been a guess. The user's answer replaces it rather than placing it: a coloured bar down the card's left edge, the reply spines inheriting that colour, and matching swatches in the account dropdown. It is a net simplification. The chip ate a third of line 2 on every card to repeat a name the user already knows, which is the table-of-records texture item 53 is about; the bar says the same thing in a few pixels and leaves line 2 to the subject. Three details that are easy to get wrong and are specified rather than left to the implementer: - The raw account colour is never drawn as a line. It is chosen to be a chip's FILL, with text drawn on top in whatever stays legible against it. A thin line on the pane's own background has a different job: followable down a long expansion without competing with the senders beside it. The accent blends toward QPalette::Base by the same 0.35 weight threadLineColour() already uses, keeping the hue and dropping the shout. The dropdown swatch does use the raw colour, being a filled patch rather than a line. - A reply resolves its THREAD's colour by walking to the root. AccountColourRole is empty on a message row, so a spine reading its own index would fall back to the neutral line under an accented root and break the continuous edge the design is built on. - Reply cards carry no bar of their own. Two vertical lines a few pixels apart in one gutter is what option B looked like, and the spine already carries the accent. colourFor() never failing is kept deliberately: an account with no colour= key gets a stable colour derived from its tag name, so adding an account and forgetting to colour it degrades to something usable rather than to nothing. kAccentWidth ships at 3px as a starting value. Whether five accounts are tellable apart at that width, on this user's screen and theme, is not decidable from a mockup or a test, so Task 10 gains a step that settles it against real cards, in both themes, with the guidance to widen the bar before touching the user's own colour choices.
2026-08-09docs: close the card list's open questions, and record a navigation defectDanilo M.1-3/+75
Both items the spec left open are settled, and one of them turned out to be a defect rather than the addition it was filed as. Item 20 deferred "move between messages without returning to the list" as an addition on top. It is a repair: next_thread and prev_thread are selectRow(current.row() + 1), and a tree numbers rows per parent, so from the last reply of an expanded thread row+1 names a sibling that does not exist and the action silently does nothing. rowCount() with no argument counts top-level threads, compounding it. Recorded as item 60, since it exists on the branch whether or not the card list is built, and it is a fresh instance of the exact rule that branch's own commit message states. The user asked for arrow keys to skip within a thread and Shift+arrows to skip between threads, scoped so the keys still work normally in the message pane, menus and entry bars. Two findings changed the shape of that: - Up/Down need no binding and no code. QTreeView's built-in navigation walks VISIBLE rows, so it already steps into an expanded thread's replies, and being the view's own key handling rather than a shortcut it is inert whenever focus is elsewhere. The requested focus behaviour is automatic. - Arrow keys must never become keymap actions. Every action is a QAction with WindowShortcut, dispatched before the focused widget sees the key, and Qt withholds only plain LETTERS from editable widgets. Return already proved this by breaking the query bar and needing a per-widget ShortcutOverride filter scoped to one widget and one key. Up as a window shortcut would break the query bar, the tag dialog and the web view at once. Shift+Up/Down was rejected for thread-jumping: it is the built-in extend-selection that multi-row tagging and item 20's action scope depend on. Alt+Up/Down instead, verified free across the whole keymap. The way back to a whole thread after clicking a reply is the root card, which is always visible above its replies. Escape is deliberately not overloaded, since it already means clear-selection with clear-pane on Shift+Escape, and a third meaning is the half-an-action problem item 50 exists to fix. The remaining open item is only whether the message pane should change to match, which stays out of scope: this spec is the left pane.
2026-08-09docs: specify the thread pane as a card listDanilo M.1-0/+229
Item 53 recorded that message rows read as a table and left the approach unspecified, with four directions ranging from spanning columns on reply rows to abandoning message rows entirely. The user's answer is wider than all four: the column grid is wrong for the WHOLE left pane, threads included. Threads and replies both become cards in a single column, three lines each, at one uniform height. Sender and date, then the subject with the flag, attachment and reply-count marks inline, then the tag chips. Replies indent by depth with a continuous spine, capped at depth 4. Three decisions worth their reasoning, since each closed an option that looked cheaper: - Uniform height keeps setUniformRowHeights(true), which is the single cheapest property of the design. A blank third line under untagged cards buys constant sizeHint arithmetic everywhere else. - Uncapped indent with a horizontally scrollable pane was asked for and rejected: it reopens item 51 in a worse form. Cards are viewport width, so the pane has no horizontal scroll range at all, and item 51 is resolved for free rather than fought. - A reply's line 3 shows only the tags its thread does not have. The full per-message set was rejected on measurement, not taste: of 48691 messages in the user's database, 7 carry unread and 75 carry flagged, and both are already shown as the sender's weight and the mark on line 2. Everything else is applied per thread and identical on every message in it, so full sets would render blank on nearly every reply and identical chips on the rest. The design is a net removal. ThreadListView::paintEvent, the tag strip's band arithmetic, SubjectDelegate, RowStyleDelegate, the five Column enumerators and the decorative header all go; one CardDelegate paints the whole card. That retires the two bug classes CLAUDE.md documents for the strip, a deleted row cut in half and every other row showing a bare stripe, both of which existed because the strip spanned cells it did not own. The column header was decorative, so a sort dropdown adds a feature rather than replacing one. Two entries only, newest and oldest, passed to notmuch. Sorting by sender or subject would have to happen in the model after results arrive, which fights the batching that makes a 10k-thread query paint immediately. Item 51 is marked resolved by 53 rather than left as separate work.