message: a long reply quote stays inside its column (own and received); demo gets a long quote to reply to

The row around a bubble sized to its content in a right-aligned column, and the quote's nowrap text made
that content 782px, so on a phone the column ran off the left edge. The row, column and bubble now carry
minWidth 0 / maxWidth 100%, and ReplyContent minWidth 0 so the quote truncates. Starfire 390x844:
own quote 782px at left -425 -> 332px at left 25; desktop inside the bubble. No test yet.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Void 2026-11-10 19:22:26 +00:00
commit 28cc3da3cc
5 changed files with 49 additions and 14 deletions

View file

@ -31,6 +31,7 @@ export const Reply = style({
});
export const ReplyContent = style({
minWidth: 0,
opacity: config.opacity.P300,
selectors: {

View file

@ -67,7 +67,11 @@ export const BubbleLayout = as<'div', BubbleLayoutProps>(
<Box
grow={own ? 'No' : 'Yes'}
direction="Column"
className={classNames(poseRow && css.BubbleColumnPose, own && css.BubbleColumnOwn)}
className={classNames(
css.BubbleColumn,
poseRow && css.BubbleColumnPose,
own && css.BubbleColumnOwn
)}
>
{header && (
<div
@ -83,7 +87,7 @@ export const BubbleLayout = as<'div', BubbleLayoutProps>(
{hideBubble ? (
children
) : (
<Box>
<Box className={css.BubbleContentRow}>
<Box
className={classNames(
css.BubbleContent,

View file

@ -139,8 +139,22 @@ export const BubbleBefore = style({
minWidth: toRem(36),
});
// minWidth 0 down the column: a flex item never shrinks below its content (a reply quote's unbroken text),
// so without it a long quote pushes the column past the row, and a right-aligned row pushes it off the left edge.
export const BubbleColumn = style({
minWidth: 0,
});
// The row around a bubble sizes to its content in a right-aligned (own) column, and a quote's nowrap text
// makes that content as wide as the quote: cap the row at the column so the bubble can shrink inside it.
export const BubbleContentRow = style({
minWidth: 0,
maxWidth: '100%',
});
export const BubbleContent = style({
maxWidth: toRem(800),
minWidth: 0,
maxWidth: `min(100%, ${toRem(800)})`,
padding: config.space.S200,
backgroundColor: color.SurfaceVariant.Container,
color: color.SurfaceVariant.OnContainer,

View file

@ -33,6 +33,8 @@ export type DemoLine = {
body: string;
html?: string;
pose?: string;
// Index of an earlier line in the same room this one replies to.
replyTo?: number;
minutesAgo: number;
};
export type DemoRoom = {
@ -113,6 +115,14 @@ export const ROOMS: DemoRoom[] = [
},
// A long display name: the name pill has to show all of it on a phone.
{ from: user('heimdall'), body: 'Ok.', pose: 'thinking', minutesAgo: 20 },
// A long message to reply to: the quote above each reply must stay inside its column on a phone.
{
from: user('visitor'),
body: "The read exists from 2026-11-04. It's research only, nothing is wired up yet, so please do not treat the notes_in_a_single_unbroken_token_that_keeps_going_past_the_edge as a decision.",
minutesAgo: 12,
},
{ from: ME, body: 'Noted, thank you.', replyTo: 8, minutesAgo: 11 },
{ from: user('void'), body: 'Same here.', replyTo: 8, minutesAgo: 10 },
],
},
{

View file

@ -136,21 +136,27 @@ export const createDemoServer = (now: () => number = Date.now): DemoServer => {
ROOMS.forEach((room) => {
stateOf.set(room.id, buildState(room));
const lineIds: string[] = [];
room.lines.forEach((l) => {
const event = mk(
'm.room.message',
l.from,
{
msgtype: 'm.text',
body: l.body,
...(l.html ? { format: 'org.matrix.custom.html', formatted_body: l.html } : {}),
...(l.pose ? { [POSE_KEY]: l.pose } : {}),
...(l.replyTo === undefined
? {}
: { 'm.relates_to': { 'm.in_reply_to': { event_id: lineIds[l.replyTo] } } }),
},
now() - l.minutesAgo * 60 * 1000
);
lineIds.push(event.event_id);
timeline.push({
version: 1,
roomId: room.id,
event: mk(
'm.room.message',
l.from,
{
msgtype: 'm.text',
body: l.body,
...(l.html ? { format: 'org.matrix.custom.html', formatted_body: l.html } : {}),
...(l.pose ? { [POSE_KEY]: l.pose } : {}),
},
now() - l.minutesAgo * 60 * 1000
),
event,
});
});
});