fix(inbox): reply quote no longer overflows or single-lines (#165)

A long reply quote was forcing the entire 3-panel inbox layout
wider, pushing the contact sidebar off-screen and making the page
horizontally scrollable. Two root causes (both correctly diagnosed
in the issue by @tekrexllp):

1. ReplyQuote applied `truncate` to the preview text, which sets
   `white-space: nowrap`. Long previews became one impossibly-wide
   line instead of wrapping.

2. The flex chain from MessageBubble outward (MessageActions →
   middle-panel wrapper → page) had no `min-w-0` on its flex
   children. Default `min-width: auto` means a flex child grows to
   its content's natural width, so the long quote propagated all the
   way up to the inbox-page flex container and pushed the contact
   sidebar past the viewport.

Fix:
  - ReplyQuote preview now uses `whitespace-pre-wrap break-words`
    instead of `truncate`. The author label stays single-line (it's
    a name, ellipsis is right for it). `overflow-hidden` on the
    inner text container as belt-and-braces.
  - MessageActions' .group/actions wrapper gets `min-w-0` so the
    `max-w-[75%]` cap actually holds even with wide content.
  - inbox/page.tsx middle-panel wrapper gets `min-w-0` so the
    MessageThread can shrink below its natural content width when
    the conversation list and contact sidebar are also flexing.

Audited the other 38 `truncate` usages across the codebase —
they're all on intentionally single-line content (contact names,
file paths, last-message previews in lists, headers, node
summaries) where ellipsis is the right behavior. ReplyQuote was
the only multi-line `truncate` misuse.

Reported and diagnosed by @tekrexllp in #165.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Arnas Donauskas 2026-05-31 12:28:49 +03:00
parent 1b66431acf
commit 13bafa5747
No known key found for this signature in database
3 changed files with 25 additions and 5 deletions

View File

@ -530,10 +530,16 @@ export default function InboxPage() {
{/* Center panel: Message thread. {/* Center panel: Message thread.
Hidden on mobile when no conversation is selected so the Hidden on mobile when no conversation is selected so the
list can occupy the full width. Always visible on lg+ list can occupy the full width. Always visible on lg+
(shows its own empty-state if no thread is picked yet). */} (shows its own empty-state if no thread is picked yet).
`min-w-0` is load-bearing: without it, a single wide piece
of content inside the thread (long quote preview, very
long URL in a message body) forces the flex child past
its share and pushes the contact-sidebar panel off-screen
on the right. Issue #165. */}
<div <div
className={cn( className={cn(
"flex h-full flex-1 lg:flex", "flex h-full min-w-0 flex-1 lg:flex",
hasActiveConv ? "flex" : "hidden lg:flex", hasActiveConv ? "flex" : "hidden lg:flex",
)} )}
> >

View File

@ -85,7 +85,12 @@ export function MessageActions({
onContextMenu={handleContextMenu} onContextMenu={handleContextMenu}
onBlur={() => setTouchOpen(false)} onBlur={() => setTouchOpen(false)}
> >
<div className="group/actions relative max-w-[75%]"> {/* `min-w-0` lets this flex child actually respect the 75% cap.
* Default `min-width: auto` lets content (a long quote preview,
* an unbroken URL) push past the cap and shove the row past
* 100%, which used to bleed across into the contact-sidebar
* area. See issue #165. */}
<div className="group/actions relative min-w-0 max-w-[75%]">
{children} {children}
<div <div
data-touch-open={touchOpen || pickerOpen ? "true" : undefined} data-touch-open={touchOpen || pickerOpen ? "true" : undefined}

View File

@ -31,11 +31,20 @@ export function ReplyQuote({
: "mb-1.5 rounded-md bg-black/20", : "mb-1.5 rounded-md bg-black/20",
)} )}
> >
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1 overflow-hidden">
<div className="truncate text-[11px] font-medium text-primary"> <div className="truncate text-[11px] font-medium text-primary">
{authorLabel} {authorLabel}
</div> </div>
<div className="truncate text-xs text-slate-200/80">{preview}</div> {/* Wrap the preview instead of truncating to a single line.
* `truncate` (white-space: nowrap) forced the quote onto one
* impossibly-wide line and — because the parent flex chain
* lacked `min-w-0` at every step — pushed the entire inbox
* layout wider, shoving the contact sidebar off-screen.
* `break-words` also wraps long URLs that have no whitespace
* to break on. Issue #165. */}
<div className="whitespace-pre-wrap break-words text-xs text-slate-200/80">
{preview}
</div>
</div> </div>
{onDismiss && ( {onDismiss && (
<button <button