@linktr.ee/messaging-react 3.5.6-rc-1783097662 → 3.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/{Card-XSDNvOZf.js → Card-B5lE5A2D.js} +3 -3
  2. package/dist/{Card-XSDNvOZf.js.map → Card-B5lE5A2D.js.map} +1 -1
  3. package/dist/{Card-Bxu71IQM.cjs → Card-CAJnEq1K.cjs} +2 -2
  4. package/dist/{Card-Bxu71IQM.cjs.map → Card-CAJnEq1K.cjs.map} +1 -1
  5. package/dist/{Card-4eHS-HQB.cjs → Card-DYe76Xh3.cjs} +2 -2
  6. package/dist/{Card-4eHS-HQB.cjs.map → Card-DYe76Xh3.cjs.map} +1 -1
  7. package/dist/{Card-By-eDX0U.cjs → Card-D_N5hclc.cjs} +2 -2
  8. package/dist/{Card-By-eDX0U.cjs.map → Card-D_N5hclc.cjs.map} +1 -1
  9. package/dist/{Card-mDB7sZiu.js → Card-MBp3Wg6r.js} +2 -2
  10. package/dist/{Card-mDB7sZiu.js.map → Card-MBp3Wg6r.js.map} +1 -1
  11. package/dist/{Card-CmLNgIdO.js → Card-rblNjC58.js} +2 -2
  12. package/dist/{Card-CmLNgIdO.js.map → Card-rblNjC58.js.map} +1 -1
  13. package/dist/{LockedThumbnail-C44Wkle9.js → LockedThumbnail-BqyPPVrO.js} +3 -3
  14. package/dist/{LockedThumbnail-C44Wkle9.js.map → LockedThumbnail-BqyPPVrO.js.map} +1 -1
  15. package/dist/{LockedThumbnail-CWhzO3wx.cjs → LockedThumbnail-C7PpVZQ0.cjs} +2 -2
  16. package/dist/{LockedThumbnail-CWhzO3wx.cjs.map → LockedThumbnail-C7PpVZQ0.cjs.map} +1 -1
  17. package/dist/assets/index.css +1 -1
  18. package/dist/{index-BRV621wo.js → index-BXzwYXLT.js} +67 -55
  19. package/dist/index-BXzwYXLT.js.map +1 -0
  20. package/dist/index-BflWZq0e.cjs +2 -0
  21. package/dist/index-BflWZq0e.cjs.map +1 -0
  22. package/dist/index.cjs +1 -1
  23. package/dist/index.js +1 -1
  24. package/package.json +2 -2
  25. package/src/components/AttachmentCard/index.tsx +1 -1
  26. package/src/components/CustomLinkPreviewList/CustomLinkPreviewCard.tsx +1 -1
  27. package/src/components/CustomLinkPreviewList/index.tsx +12 -6
  28. package/src/components/CustomMessage/CustomMessage.stories.tsx +0 -27
  29. package/src/components/CustomMessage/MessageAttachmentConversations.stories.tsx +2 -2
  30. package/src/components/CustomMessage/MessageTag.stories.tsx +2 -2
  31. package/src/components/CustomMessageInput/index.tsx +12 -4
  32. package/src/components/LinkAttachment/components/_shared/CardShell.tsx +1 -1
  33. package/src/components/LockedAttachment/components/_shared/LockedCardShell.tsx +1 -1
  34. package/src/components/MediaMessage/index.tsx +1 -8
  35. package/src/components/MessageAttachment/_shared/Bubble.tsx +7 -7
  36. package/src/components/MessageAttachment/_shared/CompactDocumentRow.tsx +3 -3
  37. package/src/styles.css +9 -104
  38. package/dist/index-BRV621wo.js.map +0 -1
  39. package/dist/index-DBVgTHrA.cjs +0 -2
  40. package/dist/index-DBVgTHrA.cjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-DBVgTHrA.cjs");exports.ActionButton=e.ActionButton;exports.Avatar=e.Avatar;exports.ChannelEmptyState=e.ChannelEmptyState;exports.ChannelList=e.ChannelList;exports.ChannelView=e.ChannelView;exports.CustomMessageProvider=e.CustomMessageProvider;exports.FaqList=e.FaqList;exports.FaqListItem=e.FaqListItem;exports.LinkAttachment=e.LinkAttachment;exports.LockedAttachment=e.LockedAttachment;exports.MediaMessage=e.MediaMessage;exports.MessageAttachment=e.MessageAttachment;exports.MessageVoteButtons=e.MessageVoteButtons;exports.MessagingProvider=e.MessagingProvider;exports.MessagingShell=e.MessagingShell;exports.buildCompactMetaLabel=e.buildCompactMetaLabel;exports.formatFileSize=e.formatFileSize;exports.formatRelativeTime=e.formatRelativeTime;exports.getFileExtensionLabel=e.getFileExtensionLabel;exports.getMessageDisplayText=e.getMessageDisplayText;exports.isLinkAttachment=e.isLinkAttachment;exports.isUuidLike=e.isUuidLike;exports.messageAttachmentGroupPositionFromStream=e.bubbleGroupPositionFromStream;exports.normalizeLanguageCode=e.normalizeLanguageCode;exports.resolveConversationParticipant=e.resolveConversationParticipant;exports.resolveLinkAttachment=e.resolveLinkAttachment;exports.resolveMediaFromMessage=e.resolveMediaFromMessage;exports.resolveParticipantDisplayName=e.resolveParticipantDisplayName;exports.useComposerLocked=e.useComposerLocked;exports.useCustomMessage=e.useCustomMessage;exports.useMessageVote=e.useMessageVote;exports.useMessaging=e.useMessaging;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-BflWZq0e.cjs");exports.ActionButton=e.ActionButton;exports.Avatar=e.Avatar;exports.ChannelEmptyState=e.ChannelEmptyState;exports.ChannelList=e.ChannelList;exports.ChannelView=e.ChannelView;exports.CustomMessageProvider=e.CustomMessageProvider;exports.FaqList=e.FaqList;exports.FaqListItem=e.FaqListItem;exports.LinkAttachment=e.LinkAttachment;exports.LockedAttachment=e.LockedAttachment;exports.MediaMessage=e.MediaMessage;exports.MessageAttachment=e.MessageAttachment;exports.MessageVoteButtons=e.MessageVoteButtons;exports.MessagingProvider=e.MessagingProvider;exports.MessagingShell=e.MessagingShell;exports.buildCompactMetaLabel=e.buildCompactMetaLabel;exports.formatFileSize=e.formatFileSize;exports.formatRelativeTime=e.formatRelativeTime;exports.getFileExtensionLabel=e.getFileExtensionLabel;exports.getMessageDisplayText=e.getMessageDisplayText;exports.isLinkAttachment=e.isLinkAttachment;exports.isUuidLike=e.isUuidLike;exports.messageAttachmentGroupPositionFromStream=e.bubbleGroupPositionFromStream;exports.normalizeLanguageCode=e.normalizeLanguageCode;exports.resolveConversationParticipant=e.resolveConversationParticipant;exports.resolveLinkAttachment=e.resolveLinkAttachment;exports.resolveMediaFromMessage=e.resolveMediaFromMessage;exports.resolveParticipantDisplayName=e.resolveParticipantDisplayName;exports.useComposerLocked=e.useComposerLocked;exports.useCustomMessage=e.useCustomMessage;exports.useMessageVote=e.useMessageVote;exports.useMessaging=e.useMessaging;
2
2
  //# sourceMappingURL=index.cjs.map
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as e, b as t, C as i, c as o, d as n, e as m, F as r, f as g, L as l, h as M, M as c, i as u, j as L, k as h, l as d, m as p, n as v, o as C, p as A, q as k, s as F, t as P, u as b, v as f, w as x, x as y, y as S, z as q, B as z, D as B, E as D, G as E } from "./index-BRV621wo.js";
1
+ import { a as e, b as t, C as i, c as o, d as n, e as m, F as r, f as g, L as l, h as M, M as c, i as u, j as L, k as h, l as d, m as p, n as v, o as C, p as A, q as k, s as F, t as P, u as b, v as f, w as x, x as y, y as S, z as q, B as z, D as B, E as D, G as E } from "./index-BXzwYXLT.js";
2
2
  export {
3
3
  e as ActionButton,
4
4
  t as Avatar,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linktr.ee/messaging-react",
3
- "version": "3.5.6-rc-1783097662",
3
+ "version": "3.5.6",
4
4
  "description": "React messaging components built on messaging-core for web applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@linktr.ee/component-library": "11.8.6",
41
- "@linktr.ee/messaging-core": "2.3.0-rc-1783097662",
41
+ "@linktr.ee/messaging-core": "^2.2.0",
42
42
  "@phosphor-icons/react": "^2.1.10"
43
43
  },
44
44
  "devDependencies": {
@@ -57,7 +57,7 @@ const AttachmentCard: React.FC<AttachmentCardProps> = ({
57
57
  data-testid={dataTestId}
58
58
  className={classNames(
59
59
  'relative w-[280px] select-none overflow-hidden rounded-[24px] shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_4px_8px_rgba(0,0,0,0.06)]',
60
- isDark ? 'bg-[#1e2330]' : 'bg-white'
60
+ isDark ? 'bg-[#121110]' : 'bg-white'
61
61
  )}
62
62
  >
63
63
  {topLeft ? (
@@ -23,7 +23,7 @@ const CustomLinkPreviewCard: React.FC<CustomLinkPreviewCardProps> = ({
23
23
  href={og_scrape_url}
24
24
  target="_blank"
25
25
  rel="noopener noreferrer"
26
- className="relative block w-full rounded-[24px] bg-[#1e2330] p-2 no-underline transition-opacity hover:opacity-90"
26
+ className="relative block w-full rounded-[24px] bg-[#121110] p-2 no-underline transition-opacity hover:opacity-90"
27
27
  >
28
28
  {image_url && (
29
29
  <img
@@ -32,13 +32,19 @@ export const CustomLinkPreviewList = () => {
32
32
  if (!showLinkPreviews) return null
33
33
 
34
34
  return (
35
- <div className="flex flex-col items-stretch w-full gap-2 mb-4">
35
+ // Layout contract: this list is the composer's *shrinkable* region. The
36
+ // mobile visitor surface sizes itself to the keyboard-shrunk
37
+ // visualViewport, so when vertical space runs out this container gives
38
+ // way (min-h-0) and scrolls, while the textarea + send row stays visible
39
+ // (shrink-0 in CustomMessageInput). Each card is wrapped in a shrink-0
40
+ // block so it keeps its intrinsic height and the container scrolls — a
41
+ // bare flex-column child would be shrunk on the column axis and its
42
+ // fixed-height hero would spill out of the collapsed card box.
43
+ <div className="messaging-link-preview-list flex min-h-0 flex-col items-stretch w-full gap-2 mb-4 overflow-y-auto overscroll-contain">
36
44
  {stateLinkPreviews.map((linkPreview) => (
37
- <CustomLinkPreviewCard
38
- key={linkPreview.og_scrape_url}
39
- link={linkPreview}
40
- onDismiss={handleDismiss}
41
- />
45
+ <div key={linkPreview.og_scrape_url} className="w-full shrink-0">
46
+ <CustomLinkPreviewCard link={linkPreview} onDismiss={handleDismiss} />
47
+ </div>
42
48
  ))}
43
49
  </div>
44
50
  )
@@ -247,33 +247,6 @@ WithTipTag.args = {
247
247
  ],
248
248
  }
249
249
 
250
- export const ThreeConsecutiveSentMessages: StoryFn<TemplateProps> =
251
- Template.bind({})
252
- ThreeConsecutiveSentMessages.args = {
253
- messages: [
254
- {
255
- id: 'msg-1',
256
- text: 'why is it that the sun',
257
- user: storyUsers.visitor,
258
- },
259
- {
260
- id: 'msg-2',
261
- text: 'has rays',
262
- user: storyUsers.creator,
263
- },
264
- {
265
- id: 'msg-3',
266
- text: 'how how how',
267
- user: storyUsers.creator,
268
- },
269
- {
270
- id: 'msg-4',
271
- text: 'thanks for the note',
272
- user: storyUsers.visitor,
273
- },
274
- ],
275
- }
276
-
277
250
  export const TipOnly: StoryFn<TemplateProps> = Template.bind({})
278
251
  TipOnly.args = {
279
252
  messages: [
@@ -42,8 +42,8 @@ interface ChatNode {
42
42
  }
43
43
 
44
44
  const TEXT_BG_BY_ROLE: Record<ChatRole, string> = {
45
- sender: 'bg-[#1e2330] text-white',
46
- receiver: 'bg-[#f2f1ef] text-[#000000]',
45
+ sender: 'bg-[#121110] text-white',
46
+ receiver: 'bg-[#e9eaed] text-[#080707]',
47
47
  }
48
48
 
49
49
  const TextBubble: React.FC<{ role: ChatRole; children: React.ReactNode }> = ({
@@ -83,7 +83,7 @@ ChatbotReceiverText.parameters = skipInChromatic
83
83
  export const ChatbotSenderText: StoryFn<ComponentProps> = (args) => {
84
84
  return (
85
85
  <div className="p-12">
86
- <div className="bg-[#1e2330] rounded-[24px] px-4 py-3 w-[280px]">
86
+ <div className="bg-[#121110] rounded-[24px] px-4 py-3 w-[280px]">
87
87
  <MessageTag {...args} />
88
88
  </div>
89
89
  </div>
@@ -150,7 +150,7 @@ export const AllVariants: StoryFn = () => {
150
150
  </div>
151
151
  <div className="flex items-center gap-4">
152
152
  <span className="text-sm w-32">Chatbot (sender):</span>
153
- <div className="bg-[#1e2330] rounded-[24px] px-4 py-3 w-[280px]">
153
+ <div className="bg-[#121110] rounded-[24px] px-4 py-3 w-[280px]">
154
154
  <MessageTag
155
155
  message={createMockMessage({
156
156
  metadata: { custom_type: 'MESSAGE_CHATBOT' },
@@ -61,14 +61,22 @@ const CustomMessageInputInner: React.FC = () => {
61
61
  } = useComponentContext('CustomMessageInput')
62
62
 
63
63
  return (
64
+ // min-h-0 lets the composer shrink under keyboard squeeze (the link
65
+ // preview list inside is the scrollable region); the textarea + send row
66
+ // is shrink-0 so it can never be pushed out of view. self-stretch matters
67
+ // because the parent `.message-input` row uses items-end, which suppresses
68
+ // the default cross-axis stretch — without it this container keeps its
69
+ // content height and the squeeze never reaches the preview list. Stream's
70
+ // <MessageInput> renders its Input directly (context providers only, no
71
+ // wrapper element), so this is a direct flex item of that row.
64
72
  <div
65
- className="central-container flex flex-col gap-2 min-w-0 w-full p-2 bg-white rounded-[1.5rem] shadow-[0_4px_16px_0_rgba(0,0,0,0.08),0_1px_2px_0_rgba(0,0,0,0.04),0_0_0_1px_rgba(0,0,0,0.04)]"
73
+ className="central-container flex min-h-0 flex-col gap-2 min-w-0 w-full self-stretch p-2 bg-white rounded-[1.5rem] shadow-[0_4px_16px_0_rgba(0,0,0,0.08),0_1px_2px_0_rgba(0,0,0,0.04),0_0_0_1px_rgba(0,0,0,0.04)]"
66
74
  data-dd-privacy="mask"
67
75
  >
68
76
  <QuotedMessagePreview />
69
77
  <CustomLinkPreviewList />
70
78
  <AttachmentPreviewList />
71
- <div className="flex">
79
+ <div className="flex shrink-0">
72
80
  <div className="w-full ml-2 mr-4 self-center leading-[0]">
73
81
  <TextareaComposer
74
82
  aria-disabled={disabled || undefined}
@@ -155,12 +163,12 @@ export const CustomMessageInput: React.FC<CustomMessageInputProps> = ({
155
163
  }
156
164
 
157
165
  return (
158
- <div className="flex flex-col gap-4 p-4">
166
+ <div className="flex min-h-0 flex-col gap-4 p-4">
159
167
  <div
160
168
  // @ts-expect-error Only React 19 onwards has `inert` in its types.
161
169
  inert={isFrozen ? '' : undefined}
162
170
  aria-disabled={isFrozen || undefined}
163
- className="message-input flex items-end gap-4 aria-disabled:opacity-40"
171
+ className="message-input flex min-h-0 items-end gap-4 aria-disabled:opacity-40"
164
172
  >
165
173
  {renderActions && (
166
174
  <div className="flex h-12 shrink-0 items-center justify-center">
@@ -64,7 +64,7 @@ const CardShell: React.FC<CardShellProps> = ({
64
64
  const isInteractive = href != null || onClick != null
65
65
  const className = classNames(
66
66
  SHELL_CLASS,
67
- bgClassName ?? (variant === 'dark' ? 'bg-[#1e2330]' : 'bg-white'),
67
+ bgClassName ?? (variant === 'dark' ? 'bg-[#121110]' : 'bg-white'),
68
68
  // `focus-ring` is a design-system utility from the component-library
69
69
  // tailwind preset — outline-none + a black 2px focus-visible ring
70
70
  // with offset, so keyboard users can see the focused card.
@@ -26,7 +26,7 @@ const LockedCardShell: React.FC<LockedCardShellProps> = ({
26
26
  className={classNames(
27
27
  'relative w-[280px] select-none overflow-hidden rounded-md',
28
28
  'shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_1px_2px_rgba(0,0,0,0.04),0_8px_32px_rgba(0,0,0,0.1)]',
29
- variant === 'dark' ? 'bg-[#1e2330]' : 'bg-white'
29
+ variant === 'dark' ? 'bg-[#121110]' : 'bg-white'
30
30
  )}
31
31
  >
32
32
  {children}
@@ -15,7 +15,7 @@ function formatBytes(bytes: number): string {
15
15
  }
16
16
 
17
17
  function linkCardShellClass(isMyMessage: boolean) {
18
- const bg = isMyMessage ? 'bg-[#1e2330]' : 'bg-[#F3F3F1]'
18
+ const bg = isMyMessage ? 'bg-[#121110]' : 'bg-[#F3F3F1]'
19
19
  return `w-[280px] select-none overflow-hidden rounded-[24px] ${bg} shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_4px_8px_rgba(0,0,0,0.06)]`
20
20
  }
21
21
 
@@ -318,13 +318,6 @@ const MediaMessageRoot: React.FC<MediaMessageProps> = ({
318
318
  <div className="str-chat__message-bubble-wrapper">
319
319
  <div
320
320
  className="str-chat__message-bubble"
321
- // The visible child is a self-contained AttachmentCard (white /
322
- // #1e2330 card with its own shadow + radius), not a speech
323
- // bubble, and this wrapper is transparent. Mark it so the
324
- // received/sent tail in `styles.css` skips it — otherwise a
325
- // tail tinted from the text-bubble surface (#f2f1ef) hangs off
326
- // a white card and reads as a detached grey nub.
327
- data-attachment-bubble="true"
328
321
  style={{ padding: 0, borderRadius: 0, overflow: 'visible', background: 'transparent' }}
329
322
  >
330
323
  {linkAttachment ? (
@@ -29,8 +29,8 @@ export interface BubbleProps {
29
29
  // Colors and metrics tracked from `stream-chat-react`'s default
30
30
  // `.str-chat__message-bubble` token set so attachments visually drop
31
31
  // into a normal `CustomMessage` thread without any seams:
32
- // - light → `--str-chat__message-bubble-background-color` in `styles.css` (#f2f1ef)
33
- // - dark → `.str-chat__message--me` override in `styles.css` (#1e2330)
32
+ // - light → `--str-chat__secondary-surface-color` = grey200 (#e9eaed)
33
+ // - dark → `.str-chat__message--me` override in `styles.css` (#121110)
34
34
  // - text padding → `--str-chat__spacing-2 --str-chat__spacing-4` = 8px 16px
35
35
  // - border-radius → `--str-chat__border-radius-md` = 18px
36
36
  //
@@ -41,13 +41,13 @@ export interface BubbleProps {
41
41
  // rebrand would need to touch every site. A central token would
42
42
  // collapse the migration to one definition.
43
43
  const BUBBLE_BG_BY_VARIANT: Record<BubbleVariant, string> = {
44
- dark: 'bg-[#1e2330]',
45
- light: 'bg-[#f2f1ef]',
44
+ dark: 'bg-[#121110]',
45
+ light: 'bg-[#e9eaed]',
46
46
  }
47
47
 
48
48
  const BUBBLE_TEXT_BY_VARIANT: Record<BubbleVariant, string> = {
49
49
  dark: 'text-white',
50
- light: 'text-[#000000]',
50
+ light: 'text-[#080707]',
51
51
  }
52
52
 
53
53
  const BUBBLE_BORDER_BY_VARIANT: Record<BubbleVariant, string> = {
@@ -104,8 +104,8 @@ const CORNER_CLASSES_BY_SIDE_AND_POSITION: Record<
104
104
  * is the file', 'Here is the image').
105
105
  *
106
106
  * Two visual variants:
107
- * - `dark` — sender-side (Composer / Sent), white text on `#1e2330`.
108
- * - `light` — recipient-side (Received), dark text on `#f2f1ef`.
107
+ * - `dark` — sender-side (Composer / Sent), white text on `#121110`.
108
+ * - `light` — recipient-side (Received), dark text on `#e9eaed`.
109
109
  *
110
110
  * Background colors, padding, border-radius, and inherited font come
111
111
  * from the stream-chat-react `.str-chat__message-bubble` token set so
@@ -41,11 +41,11 @@ export interface CompactDocumentRowProps {
41
41
  // so they stay variant-aware. The title inherits the bubble's text
42
42
  // color (set on the parent `Bubble`).
43
43
  //
44
- // `text-black/55` on the `#f2f1ef` light bubble lands right at the
44
+ // `text-black/55` on the `#e9eaed` light bubble lands right at the
45
45
  // WCAG AA 4.5:1 small-text threshold (≈4.5:1) — bump to `/65` so
46
46
  // the meta line has a comfortable margin on the light bubble. The
47
- // dark variant (`text-white/55` on `#1e2330`) already passes AA,
48
- // so we leave it alone.
47
+ // dark variant (`text-white/55` on `#121110`) already passes AA at
48
+ // ~6:1, so we leave it alone.
49
49
  const META_CLASS_BY_VARIANT: Record<BubbleVariant, string> = {
50
50
  dark: 'text-white/55',
51
51
  light: 'text-black/65',
package/src/styles.css CHANGED
@@ -8,10 +8,10 @@
8
8
  --str-chat__font-family: inherit;
9
9
 
10
10
  --str-chat__message-bubble-border-radius: 1.5rem;
11
- --str-chat__message-bubble-background-color: #f2f1ef;
12
- --str-chat__message-bubble-color: #000000;
11
+ --str-chat__message-bubble-background-color: #f1f0ee;
12
+ --str-chat__message-bubble-color: #181818;
13
13
 
14
- --str-chat__own-message-bubble-background-color: #1e2330;
14
+ --str-chat__own-message-bubble-background-color: #121110;
15
15
  --str-chat__own-message-bubble-color: #fff;
16
16
  }
17
17
 
@@ -560,36 +560,7 @@
560
560
 
561
561
  .str-chat__message .str-chat__message-bubble {
562
562
  font-size: 0.875rem;
563
- line-height: 1.3125rem;
564
- letter-spacing: 0.14px;
565
- }
566
-
567
- /* Keep every bubble a full pill regardless of its position in a same-author
568
- run. stream-chat-react's default theme flattens the inner corners of
569
- grouped bubbles (bottom-/top-inline-end for `--me`, inline-start for
570
- `--other`) via `.str-chat__li--{top,middle,bottom} .str-chat__message--{me,other}
571
- .str-chat__message-bubble` rules at specificity (0,3,0). We prepend the
572
- `.str-chat` root class to reach (0,4,0) so this wins independently of
573
- stylesheet load order and restores the fully-rounded pill from the Figma
574
- spec — only the trailing bubble's tail (`::after`, below) breaks the pill. */
575
- .str-chat .str-chat__li--single .str-chat__message--me .str-chat__message-bubble,
576
- .str-chat .str-chat__li--top .str-chat__message--me .str-chat__message-bubble,
577
- .str-chat .str-chat__li--middle .str-chat__message--me .str-chat__message-bubble,
578
- .str-chat .str-chat__li--bottom .str-chat__message--me .str-chat__message-bubble,
579
- .str-chat
580
- .str-chat__li--single
581
- .str-chat__message--other
582
- .str-chat__message-bubble,
583
- .str-chat .str-chat__li--top .str-chat__message--other .str-chat__message-bubble,
584
- .str-chat
585
- .str-chat__li--middle
586
- .str-chat__message--other
587
- .str-chat__message-bubble,
588
- .str-chat
589
- .str-chat__li--bottom
590
- .str-chat__message--other
591
- .str-chat__message-bubble {
592
- border-radius: var(--str-chat__message-bubble-border-radius);
563
+ line-height: 1.25rem;
593
564
  }
594
565
 
595
566
  /* Chatbot reply markdown — restore semantics that consumer Tailwind preflight
@@ -696,87 +667,21 @@
696
667
  margin-bottom: 0;
697
668
  }
698
669
 
699
- /* Roomier, even vertical gap between bubbles within a same-author run.
700
- stream defaults grouped messages to 0.125rem (2px), which reads as
701
- cramped now that each bubble is a full pill; the Figma grouped-message
702
- spacing is closer to 6px. `.str-chat` prefix → (0,3,0) so it beats
703
- stream's (0,2,0) grouping rule regardless of stylesheet load order. */
704
- .str-chat .str-chat__li--top .str-chat__message,
705
- .str-chat .str-chat__li--middle .str-chat__message {
706
- margin-block-end: 0.375rem;
707
- }
708
-
709
670
  .str-chat__li--single,
710
671
  .str-chat__li--bottom {
711
672
  .str-chat__message {
712
673
  margin-block-end: 0.875rem;
713
674
  }
714
675
 
715
- /* `:not([data-attachment-bubble])` keeps the tail off self-contained
716
- attachment/media cards (see `MediaMessage`): those render their own
717
- shadowed card inside a transparent bubble, so a tail tinted from the
718
- text-bubble surface would hang off the card rather than read as part
719
- of it. Only genuine text/message bubbles get a tail. */
720
- .str-chat__message--other
721
- .str-chat__message-bubble-wrapper
722
- > .str-chat__message-bubble:last-child:not([data-attachment-bubble]) {
723
- position: relative;
724
- /* Form a stacking context so the `z-index: -1` tail below paints
725
- *after* this bubble's own (opaque, same-color) background but
726
- *behind* its in-flow content. Without this the tail would either
727
- sit on top of content (a positioned pseudo-element paints above
728
- in-flow siblings — switching to `::before` would NOT change that)
729
- or drop behind the whole bubble. This matters most for the
730
- zero-padding `MediaMessageRoot` bubble, whose media card reaches
731
- the corner: the tail must tuck behind the card, not cover it. */
732
- isolation: isolate;
733
-
734
- &::after {
735
- content: '';
736
- position: absolute;
737
- inset-block-end: 0;
738
- inset-inline-start: calc(-1 * 0.25rem);
739
- z-index: -1;
740
- width: 1.25rem;
741
- height: 1.4375rem;
742
- background-color: var(--str-chat__message-bubble-background-color);
743
- pointer-events: none;
744
- transform: scaleX(-1);
745
- transform-origin: center;
746
- -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='23' fill='none'%3E%3Cpath fill='black' d='M0 0H15C15 10.1934 15.859 15.2345 19.168 21.4893C19.527 22.168 19.039 22.9917 18.274 22.9178C5.176 21.6506 0.32 6.0737 0 0Z'/%3E%3C/svg%3E")
747
- no-repeat center / 100% 100%;
748
- mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='23' fill='none'%3E%3Cpath fill='black' d='M0 0H15C15 10.1934 15.859 15.2345 19.168 21.4893C19.527 22.168 19.039 22.9917 18.274 22.9178C5.176 21.6506 0.32 6.0737 0 0Z'/%3E%3C/svg%3E")
749
- no-repeat center / 100% 100%;
750
- }
676
+ .str-chat__message--other .str-chat__message-bubble {
677
+ border-end-start-radius: var(--str-chat__message-bubble-border-radius);
751
678
  }
752
679
 
753
- .str-chat__message--me
754
- .str-chat__message-bubble-wrapper
755
- > .str-chat__message-bubble:last-child:not([data-attachment-bubble]) {
756
- position: relative;
757
- /* See the `--other` rule above: stacking context so the tail tucks
758
- behind in-flow content (e.g. the corner of a zero-padding
759
- `MediaMessageRoot` card) instead of painting over it. */
760
- isolation: isolate;
761
-
762
- &::after {
763
- content: '';
764
- position: absolute;
765
- inset-block-end: 0;
766
- inset-inline-end: calc(-1 * 0.25rem);
767
- z-index: -1;
768
- width: 1.25rem;
769
- height: 1.4375rem;
770
- background-color: var(--str-chat__own-message-bubble-background-color);
771
- pointer-events: none;
772
- -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='23' fill='none'%3E%3Cpath fill='black' d='M0 0H15C15 10.1934 15.859 15.2345 19.168 21.4893C19.527 22.168 19.039 22.9917 18.274 22.9178C5.176 21.6506 0.32 6.0737 0 0Z'/%3E%3C/svg%3E")
773
- no-repeat center / 100% 100%;
774
- mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='23' fill='none'%3E%3Cpath fill='black' d='M0 0H15C15 10.1934 15.859 15.2345 19.168 21.4893C19.527 22.168 19.039 22.9917 18.274 22.9178C5.176 21.6506 0.32 6.0737 0 0Z'/%3E%3C/svg%3E")
775
- no-repeat center / 100% 100%;
776
- }
680
+ .str-chat__message--me .str-chat__message-bubble {
681
+ border-end-end-radius: var(--str-chat__message-bubble-border-radius);
777
682
  }
778
-
779
683
  }
684
+
780
685
  /* Locked attachment wrapper: stack card + text bubble in the correct direction */
781
686
  .str-chat__li .str-chat__message--me .str-chat__message-bubble-wrapper {
782
687
  display: flex;