@@ -117,7 +117,13 @@ export interface ChatBubbleProps {
117117 onDownloadAttachment ?: ( attachment : Attachment , message : ChatMessage ) => void ;
118118 /** Controls shown at the bubble's outer edge on hover: menus, star, retry. */
119119 renderActions ?: ( message : ChatMessage ) => ReactNode ;
120- /** Anything below the body, inside the bubble: a reply box, an AI notice. */
120+ /**
121+ * Anything below the body, inside the bubble: a reply box, an AI notice.
122+ *
123+ * A bubble holding a designed mail carries no padding of its own — the
124+ * document reaches the card's edge — so a footer that needs breathing room
125+ * has to bring it. Style it against `.sec-bubble--doc` if it must differ.
126+ */
121127 renderFooter ?: ( message : ChatMessage ) => ReactNode ;
122128 className ?: string ;
123129}
@@ -174,10 +180,15 @@ export function ChatBubble({
174180 ) ;
175181
176182 const attachments = message . attachments ?? [ ] ;
183+ // A designed mail is not a chat line, it is a finished document: it brings
184+ // its own background, its own margins and often its own colour scheme. Tint
185+ // and pad it like a bubble and the reader gets a card inside a card. So a
186+ // framed body keeps the container and loses the costume.
187+ const isDocument = shape . kind === 'rich' ;
177188 // A framed body needs a sized containing block, so its bubble takes the full
178189 // column. A short inline one hugs its text, which is what makes a two-word
179190 // reply look like a two-word reply.
180- const hug = shape . kind !== 'rich' ;
191+ const hug = ! isDocument ;
181192
182193 const rowClasses = [ 'sec-row' , isMine ? 'sec-row--mine' : 'sec-row--theirs' , className ]
183194 . filter ( Boolean )
@@ -187,6 +198,7 @@ export function ChatBubble({
187198 isMine ? 'sec-bubble--mine' : 'sec-bubble--theirs' ,
188199 compact ? '' : 'sec-bubble--tail' ,
189200 hug ? '' : 'sec-bubble--wide' ,
201+ isDocument ? 'sec-bubble--doc' : '' ,
190202 ]
191203 . filter ( Boolean )
192204 . join ( ' ' ) ;
@@ -244,7 +256,10 @@ export function ChatBubble({
244256 // The audit trail, in the DOM. "Why did part of my email disappear?"
245257 // is answerable with devtools instead of a rebuild.
246258 data-sec-applied = { message . applied ?. length ? message . applied . join ( ' ' ) : undefined }
247- style = { ! isMine && color ? { backgroundColor : color . bubble } : undefined }
259+ // The sender tint is skipped for a document: it would sit behind the
260+ // mail's own background, visible only as a coloured rim. The avatar
261+ // and the sender name above the bubble carry that colour instead.
262+ style = { ! isMine && color && ! isDocument ? { backgroundColor : color . bubble } : undefined }
248263 >
249264 < MessageBody
250265 message = { message }
0 commit comments