Skip to content

fix(ui): wrap long URLs inside message bubbles (#212)#213

Merged
sanity merged 2 commits intomainfrom
fix-212
Apr 14, 2026
Merged

fix(ui): wrap long URLs inside message bubbles (#212)#213
sanity merged 2 commits intomainfrom
fix-212

Conversation

@sanity
Copy link
Copy Markdown
Contributor

@sanity sanity commented Apr 14, 2026

Problem

Posting a long link that a text formatter cannot break up (e.g. a URL with a very long unbroken segment) stretched the message bubble wider than other bubbles. PR #209 added break-words (overflow-wrap: break-word) on the prose body, but break-word only inserts soft breaks when a word would otherwise overflow, and crucially does NOT lower the element's min-content size. So a sibling flex/grid parent can still be forced wider by the intrinsic min-content of a long token.

Approach

Replace break-words with [overflow-wrap:anywhere]. anywhere is a strict superset of break-word: it keeps the soft-break behavior but also lowers the element's min-content to a single character, allowing the bubble's flex ancestors to shrink around the wrapped content.

Testing

  • Added an example-data entry with a long unbreakable URL so the regression is visually exercised by the dev build and by Playwright.
  • Added a Playwright regression test (Long lines cause too wide bubbles #212) that asserts a bubble containing the long URL stays within the viewport at narrow widths.
  • Full message-layout.spec.ts suite (7 tests) passes on chromium locally.

Closes #212

[AI-assisted - Claude]

sanity added 2 commits April 14, 2026 17:06
## Problem

Posting a long link that a text formatter cannot break up (e.g. a URL
with a very long unbroken segment) stretched the message bubble wider
than other bubbles. PR #209 added `break-words` (overflow-wrap:
break-word) on the prose body, but `break-word` only inserts soft
breaks when a word would otherwise overflow, and crucially does NOT
lower the element's min-content size. So a sibling flex/grid parent
can still be forced wider by the intrinsic min-content of a long
token.

## Approach

Replace `break-words` with `[overflow-wrap:anywhere]`. `anywhere` is a
strict superset of `break-word`: it keeps the soft-break behavior but
also lowers the element's min-content to a single character, allowing
the bubble's flex ancestors to shrink around the wrapped content.

## Testing

- Added example-data entry with a long unbreakable URL so the regression
  is visually exercised by the dev build and by Playwright.
- Added a Playwright regression test (#212) that asserts a bubble
  containing the long URL stays within the viewport at narrow widths.
- Full `message-layout.spec.ts` suite (7 tests) passes on chromium.

Closes #212

[AI-assisted - Claude]
Addresses testing-reviewer feedback on PR #213. The original
assertion (`bubbleWidth <= 480`) was too loose to distinguish the
fixed and buggy states. Replace with three stricter checks:

1. Inner prose `.scrollWidth <= .clientWidth` — verifies the long
   URL actually wraps inside its container rather than overflowing.
2. Rendered `<a>` element width fits within the prose box.
3. `document.documentElement.scrollWidth <= .clientWidth` — defense
   in depth against the long-URL bubble pushing the chat column
   wider than siblings.

Tests run at 1024x900 where `max-w-prose` (~65ch ≈ 520px) is the
binding constraint rather than the viewport.

Verified full `message-layout.spec.ts` suite (7 tests) passes on
chromium and the new test passes on webkit as well.

[AI-assisted - Claude]
@sanity
Copy link
Copy Markdown
Contributor Author

sanity commented Apr 14, 2026

Review feedback addressed

Ran four internal review agents plus codex review --base main in parallel.

Actioned (3258ed4): Testing reviewer flagged the Playwright assertion as too loose. Tightened to three stricter checks:

  • Inner .prose scrollWidth <= clientWidth (verifies the URL actually wraps)
  • Rendered <a> fits within the prose box
  • document.documentElement.scrollWidth <= clientWidth (no horizontal page overflow)

Test now runs at 1024×900 where max-w-prose is the binding constraint rather than the viewport. Full message-layout.spec.ts (7 tests) passes on chromium; the new #212 test also passes on webkit.

Caveat worth flagging: I could not reliably reproduce the exact regression from the issue screenshot locally with either break-words OR [overflow-wrap:anywhere] — both produce visually identical results in my test harness (bubble pinned at max-w-prose, content wrapped, no overflow). The fix is still correct on first principles: overflow-wrap: anywhere is a strict superset of break-word and additionally lowers min-content sizing, which is the documented failure mode when a flex ancestor considers intrinsic min-content. It's strictly better than the previous class; there's no scenario where it's worse. The tightened test is therefore a smoke test + documentation rather than a proof-of-fix. If the issue recurs, we'll need the user's exact viewport/browser/message to drive a tighter repro.

Other findings (not blocking):

  • Skeptical: suggests keeping break-words as a fallback for iOS Safari 15.0-15.3. Skipped: those versions are 4 years old in April 2026, and the Tailwind-generated source order would have break-words win over [overflow-wrap:anywhere] on supporting browsers, defeating the fix.
  • Big-picture: no agents/skills/docs reference break-words, no WASM or delegate impact, no bug-prevention patterns apply.
  • Code-first / Codex: no correctness issues found.

[AI-assisted - Claude]

@sanity sanity merged commit cb0ac8e into main Apr 14, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Long lines cause too wide bubbles

1 participant