stream-chat-react 14.7.0 → 14.8.0

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 (150) hide show
  1. package/dist/cjs/{ReactPlayerWrapper.c340ab05.js → ReactPlayerWrapper.60a93742.js} +2 -2
  2. package/dist/cjs/{ReactPlayerWrapper.c340ab05.js.map → ReactPlayerWrapper.60a93742.js.map} +1 -1
  3. package/dist/cjs/channel-detail.js +2 -2
  4. package/dist/cjs/emojis.js +2 -2
  5. package/dist/cjs/index.js +2888 -730
  6. package/dist/cjs/index.js.map +1 -1
  7. package/dist/cjs/{useChannelHeaderOnlineStatus.d39ede24.js → useChannelHeaderOnlineStatus.72753090.js} +373 -63
  8. package/dist/cjs/useChannelHeaderOnlineStatus.72753090.js.map +1 -0
  9. package/dist/cjs/{useMessageComposerController.f1a3d182.js → useMessageComposerController.f8f20e3d.js} +2 -2
  10. package/dist/cjs/{useMessageComposerController.f1a3d182.js.map → useMessageComposerController.f8f20e3d.js.map} +1 -1
  11. package/dist/cjs/{useNotificationApi.87ba0b39.js → useNotificationApi.bf76a16b.js} +294 -42
  12. package/dist/cjs/useNotificationApi.bf76a16b.js.map +1 -0
  13. package/dist/css/index.css +34 -7
  14. package/dist/css/index.css.map +1 -1
  15. package/dist/es/channel-detail.mjs +2 -2
  16. package/dist/es/emojis.mjs +2 -2
  17. package/dist/es/index.mjs +2868 -728
  18. package/dist/es/index.mjs.map +1 -1
  19. package/dist/es/{useChannelHeaderOnlineStatus.b3a51ca7.mjs → useChannelHeaderOnlineStatus.c86db868.mjs} +337 -64
  20. package/dist/es/useChannelHeaderOnlineStatus.c86db868.mjs.map +1 -0
  21. package/dist/es/{useMessageComposerController.2309f433.mjs → useMessageComposerController.d50f6168.mjs} +2 -2
  22. package/dist/es/{useMessageComposerController.2309f433.mjs.map → useMessageComposerController.d50f6168.mjs.map} +1 -1
  23. package/dist/es/{useNotificationApi.eff88f29.mjs → useNotificationApi.ed6bff88.mjs} +271 -43
  24. package/dist/es/useNotificationApi.ed6bff88.mjs.map +1 -0
  25. package/dist/types/a11y/accessibleLabel.d.ts +38 -0
  26. package/dist/types/a11y/accessibleLabel.d.ts.map +1 -0
  27. package/dist/types/a11y/hooks/useListboxKeyboardNavigation.d.ts +73 -0
  28. package/dist/types/a11y/hooks/useListboxKeyboardNavigation.d.ts.map +1 -0
  29. package/dist/types/a11y/hooks/useVirtualizedListboxKeyboardNavigation.d.ts +41 -0
  30. package/dist/types/a11y/hooks/useVirtualizedListboxKeyboardNavigation.d.ts.map +1 -0
  31. package/dist/types/components/Accessibility/AriaLiveAnnouncerProvider.d.ts +13 -0
  32. package/dist/types/components/Accessibility/AriaLiveAnnouncerProvider.d.ts.map +1 -0
  33. package/dist/types/components/Accessibility/AriaLiveOutlet.d.ts +24 -0
  34. package/dist/types/components/Accessibility/AriaLiveOutlet.d.ts.map +1 -0
  35. package/dist/types/components/Accessibility/AriaLiveOutletContext.d.ts +25 -0
  36. package/dist/types/components/Accessibility/AriaLiveOutletContext.d.ts.map +1 -0
  37. package/dist/types/components/Accessibility/NotificationAnnouncer.d.ts +21 -1
  38. package/dist/types/components/Accessibility/NotificationAnnouncer.d.ts.map +1 -1
  39. package/dist/types/components/Accessibility/hooks/useAudioPlaybackChangeAnnouncements.d.ts +3 -0
  40. package/dist/types/components/Accessibility/hooks/useAudioPlaybackChangeAnnouncements.d.ts.map +1 -0
  41. package/dist/types/components/Accessibility/hooks/useFocusReturn.d.ts +44 -0
  42. package/dist/types/components/Accessibility/hooks/useFocusReturn.d.ts.map +1 -0
  43. package/dist/types/components/Accessibility/hooks/useInertWhenHidden.d.ts +88 -0
  44. package/dist/types/components/Accessibility/hooks/useInertWhenHidden.d.ts.map +1 -0
  45. package/dist/types/components/Accessibility/hooks/useInteractionAnnouncements.d.ts +114 -0
  46. package/dist/types/components/Accessibility/hooks/useInteractionAnnouncements.d.ts.map +1 -0
  47. package/dist/types/components/Accessibility/index.d.ts +7 -1
  48. package/dist/types/components/Accessibility/index.d.ts.map +1 -1
  49. package/dist/types/components/Accessibility/scheduling/index.d.ts +4 -0
  50. package/dist/types/components/Accessibility/scheduling/index.d.ts.map +1 -0
  51. package/dist/types/components/Accessibility/scheduling/useAnnouncementQueue.d.ts +25 -0
  52. package/dist/types/components/Accessibility/scheduling/useAnnouncementQueue.d.ts.map +1 -0
  53. package/dist/types/components/Accessibility/scheduling/useDebouncedAnnounce.d.ts +54 -0
  54. package/dist/types/components/Accessibility/scheduling/useDebouncedAnnounce.d.ts.map +1 -0
  55. package/dist/types/components/Accessibility/scheduling/useSettledAnnouncement.d.ts +74 -0
  56. package/dist/types/components/Accessibility/scheduling/useSettledAnnouncement.d.ts.map +1 -0
  57. package/dist/types/components/Accessibility/useAriaLiveAnnouncer.d.ts +24 -1
  58. package/dist/types/components/Accessibility/useAriaLiveAnnouncer.d.ts.map +1 -1
  59. package/dist/types/components/Attachment/AttachmentActions.d.ts.map +1 -1
  60. package/dist/types/components/Attachment/Giphy.d.ts.map +1 -1
  61. package/dist/types/components/Attachment/giphyAccessibility.d.ts +11 -0
  62. package/dist/types/components/Attachment/giphyAccessibility.d.ts.map +1 -0
  63. package/dist/types/components/AudioPlayback/WithAudioPlayback.d.ts.map +1 -1
  64. package/dist/types/components/ChannelList/ChannelListUI.d.ts.map +1 -1
  65. package/dist/types/components/ChannelList/hooks/index.d.ts +1 -0
  66. package/dist/types/components/ChannelList/hooks/index.d.ts.map +1 -1
  67. package/dist/types/components/ChannelList/hooks/useChannelListKeyboardNavigation.d.ts +17 -0
  68. package/dist/types/components/ChannelList/hooks/useChannelListKeyboardNavigation.d.ts.map +1 -0
  69. package/dist/types/components/ChannelListItem/ChannelListItem.d.ts +7 -0
  70. package/dist/types/components/ChannelListItem/ChannelListItem.d.ts.map +1 -1
  71. package/dist/types/components/ChannelListItem/ChannelListItemActionButtons.defaults.d.ts.map +1 -1
  72. package/dist/types/components/ChannelListItem/ChannelListItemUI.d.ts.map +1 -1
  73. package/dist/types/components/ChannelListItem/index.d.ts +1 -0
  74. package/dist/types/components/ChannelListItem/index.d.ts.map +1 -1
  75. package/dist/types/components/ChannelListItem/utils.a11y.d.ts +112 -0
  76. package/dist/types/components/ChannelListItem/utils.a11y.d.ts.map +1 -0
  77. package/dist/types/components/ChannelListItem/utils.d.ts +10 -2
  78. package/dist/types/components/ChannelListItem/utils.d.ts.map +1 -1
  79. package/dist/types/components/Chat/Chat.d.ts.map +1 -1
  80. package/dist/types/components/ChatView/ChatView.a11y.utility.d.ts +9 -17
  81. package/dist/types/components/ChatView/ChatView.a11y.utility.d.ts.map +1 -1
  82. package/dist/types/components/ChatView/ChatView.d.ts +7 -7
  83. package/dist/types/components/ChatView/ChatView.d.ts.map +1 -1
  84. package/dist/types/components/Dialog/components/ContextMenu.d.ts +17 -2
  85. package/dist/types/components/Dialog/components/ContextMenu.d.ts.map +1 -1
  86. package/dist/types/components/Dialog/components/Prompt.d.ts.map +1 -1
  87. package/dist/types/components/Dialog/service/DialogAnchor.d.ts.map +1 -1
  88. package/dist/types/components/Form/SwitchField.d.ts.map +1 -1
  89. package/dist/types/components/Form/TextInput.d.ts.map +1 -1
  90. package/dist/types/components/InfiniteScrollPaginator/InfiniteScrollPaginator.d.ts +7 -0
  91. package/dist/types/components/InfiniteScrollPaginator/InfiniteScrollPaginator.d.ts.map +1 -1
  92. package/dist/types/components/MediaRecorder/hooks/useMediaRecorder.d.ts +2 -2
  93. package/dist/types/components/MediaRecorder/hooks/useMediaRecorder.d.ts.map +1 -1
  94. package/dist/types/components/MessageActions/DownloadSubmenu.d.ts.map +1 -1
  95. package/dist/types/components/MessageActions/RemindMeSubmenu.d.ts.map +1 -1
  96. package/dist/types/components/MessageComposer/AttachmentSelector/AttachmentSelector.d.ts +8 -2
  97. package/dist/types/components/MessageComposer/AttachmentSelector/AttachmentSelector.d.ts.map +1 -1
  98. package/dist/types/components/MessageComposer/AttachmentSelector/CommandsMenu.d.ts.map +1 -1
  99. package/dist/types/components/MessageComposer/CommandChip.d.ts.map +1 -1
  100. package/dist/types/components/MessageComposer/MessageComposerActions.d.ts.map +1 -1
  101. package/dist/types/components/MessageList/MessageList.d.ts.map +1 -1
  102. package/dist/types/components/MessageList/VirtualizedMessageList.d.ts.map +1 -1
  103. package/dist/types/components/Modal/GlobalModal.d.ts +6 -1
  104. package/dist/types/components/Modal/GlobalModal.d.ts.map +1 -1
  105. package/dist/types/components/Notifications/Notification.d.ts.map +1 -1
  106. package/dist/types/components/Notifications/hooks/useNotificationApi.d.ts +7 -0
  107. package/dist/types/components/Notifications/hooks/useNotificationApi.d.ts.map +1 -1
  108. package/dist/types/components/Poll/PollCreationDialog/NameField.d.ts.map +1 -1
  109. package/dist/types/components/Poll/PollCreationDialog/OptionFieldSet.d.ts.map +1 -1
  110. package/dist/types/components/Poll/PollCreationDialog/PollCreationDialog.d.ts.map +1 -1
  111. package/dist/types/components/Poll/PollCreationDialog/PollCreationDialogControls.d.ts.map +1 -1
  112. package/dist/types/components/Poll/PollCreationDialog/PollOptionReorderHandle.d.ts +17 -0
  113. package/dist/types/components/Poll/PollCreationDialog/PollOptionReorderHandle.d.ts.map +1 -0
  114. package/dist/types/components/Search/Search.d.ts +9 -1
  115. package/dist/types/components/Search/Search.d.ts.map +1 -1
  116. package/dist/types/components/Search/SearchBar/SearchBar.d.ts.map +1 -1
  117. package/dist/types/components/Search/SearchContext.d.ts +1 -1
  118. package/dist/types/components/Search/SearchContext.d.ts.map +1 -1
  119. package/dist/types/components/Search/SearchResults/SearchResults.d.ts.map +1 -1
  120. package/dist/types/components/Search/SearchResults/SearchSourceResultListFooter.d.ts.map +1 -1
  121. package/dist/types/components/Search/hooks/index.d.ts +2 -0
  122. package/dist/types/components/Search/hooks/index.d.ts.map +1 -1
  123. package/dist/types/components/Search/hooks/useAnnounceSearchResultCount.d.ts +27 -0
  124. package/dist/types/components/Search/hooks/useAnnounceSearchResultCount.d.ts.map +1 -0
  125. package/dist/types/components/Search/hooks/useSearchResultsKeyboardNavigation.d.ts +13 -0
  126. package/dist/types/components/Search/hooks/useSearchResultsKeyboardNavigation.d.ts.map +1 -0
  127. package/dist/types/components/TextareaComposer/SuggestionList/MentionItem/UserItem.d.ts.map +1 -1
  128. package/dist/types/components/TextareaComposer/SuggestionList/SuggestionList.d.ts +21 -1
  129. package/dist/types/components/TextareaComposer/SuggestionList/SuggestionList.d.ts.map +1 -1
  130. package/dist/types/components/TextareaComposer/TextareaComposer.d.ts +9 -1
  131. package/dist/types/components/TextareaComposer/TextareaComposer.d.ts.map +1 -1
  132. package/dist/types/components/Threads/ThreadList/ThreadList.d.ts.map +1 -1
  133. package/dist/types/components/Threads/ThreadList/ThreadListItemUI.d.ts +7 -1
  134. package/dist/types/components/Threads/ThreadList/ThreadListItemUI.d.ts.map +1 -1
  135. package/dist/types/components/Threads/ThreadList/index.d.ts +1 -0
  136. package/dist/types/components/Threads/ThreadList/index.d.ts.map +1 -1
  137. package/dist/types/components/Threads/ThreadList/utils.a11y.d.ts +111 -0
  138. package/dist/types/components/Threads/ThreadList/utils.a11y.d.ts.map +1 -0
  139. package/dist/types/components/Threads/UnreadCountBadge.d.ts.map +1 -1
  140. package/dist/types/components/UtilityComponents/hooks/useMutationObserver.d.ts +23 -0
  141. package/dist/types/components/UtilityComponents/hooks/useMutationObserver.d.ts.map +1 -0
  142. package/dist/types/i18n/Streami18n.d.ts +79 -13
  143. package/dist/types/i18n/Streami18n.d.ts.map +1 -1
  144. package/package.json +4 -1
  145. package/dist/cjs/useChannelHeaderOnlineStatus.d39ede24.js.map +0 -1
  146. package/dist/cjs/useNotificationApi.87ba0b39.js.map +0 -1
  147. package/dist/es/useChannelHeaderOnlineStatus.b3a51ca7.mjs.map +0 -1
  148. package/dist/es/useNotificationApi.eff88f29.mjs.map +0 -1
  149. package/dist/types/components/Accessibility/AriaLiveRegion.d.ts +0 -3
  150. package/dist/types/components/Accessibility/AriaLiveRegion.d.ts.map +0 -1
@@ -1,4 +1,4 @@
1
- const require_useNotificationApi = require("./useNotificationApi.87ba0b39.js");
1
+ const require_useNotificationApi = require("./useNotificationApi.bf76a16b.js");
2
2
  let react = require("react");
3
3
  react = require_useNotificationApi.__toESM(react);
4
4
  let react_jsx_runtime = require("react/jsx-runtime");
@@ -13,6 +13,10 @@ react_markdown = require_useNotificationApi.__toESM(react_markdown);
13
13
  let remark_gfm = require("remark-gfm");
14
14
  remark_gfm = require_useNotificationApi.__toESM(remark_gfm);
15
15
  let _braintree_sanitize_url = require("@braintree/sanitize-url");
16
+ let mdast_util_to_string = require("mdast-util-to-string");
17
+ let remark_parse = require("remark-parse");
18
+ remark_parse = require_useNotificationApi.__toESM(remark_parse);
19
+ let unified = require("unified");
16
20
  //#region src/context/MessageContext.tsx
17
21
  var MessageContext = react.default.createContext(void 0);
18
22
  var MessageProvider = ({ children, value }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageContext.Provider, {
@@ -111,6 +115,84 @@ var useModalContext = () => {
111
115
  return contextValue;
112
116
  };
113
117
  //#endregion
118
+ //#region src/components/Accessibility/AriaLiveOutletContext.ts
119
+ var AriaLiveOutletContext = (0, react.createContext)(void 0);
120
+ var useAriaLiveOutletContext = () => (0, react.useContext)(AriaLiveOutletContext);
121
+ //#endregion
122
+ //#region src/components/VisuallyHidden/VisuallyHidden.tsx
123
+ var visuallyHiddenStyle = {
124
+ border: 0,
125
+ clip: "rect(0, 0, 0, 0)",
126
+ height: "1px",
127
+ margin: "-1px",
128
+ overflow: "hidden",
129
+ padding: 0,
130
+ position: "absolute",
131
+ whiteSpace: "nowrap",
132
+ width: "1px"
133
+ };
134
+ /**
135
+ * Hides content visually while preserving it for assistive technologies.
136
+ */
137
+ var VisuallyHidden = ({ children, style, ...rest }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
138
+ ...rest,
139
+ style: {
140
+ ...visuallyHiddenStyle,
141
+ ...style
142
+ },
143
+ children
144
+ });
145
+ //#endregion
146
+ //#region src/components/Accessibility/AriaLiveOutlet.tsx
147
+ /**
148
+ * Dumb render target for the unified announcer. Registers itself with the nearest
149
+ * {@link AriaLiveAnnouncerProvider} and renders the polite/assertive live regions ONLY when
150
+ * it is the active (innermost) outlet, so exactly one region is live at a time — always in
151
+ * the active accessibility-tree scope. Mount one at the `Chat` root and one inside each
152
+ * `aria-modal` dialog (with a higher `layer`).
153
+ */
154
+ var AriaLiveOutlet = ({ layer = 0, portal = false }) => {
155
+ const context = useAriaLiveOutletContext();
156
+ const idRef = (0, react.useRef)(null);
157
+ if (idRef.current === null) idRef.current = Symbol("aria-live-outlet");
158
+ const registerOutlet = context?.registerOutlet;
159
+ const unregisterOutlet = context?.unregisterOutlet;
160
+ (0, react.useEffect)(() => {
161
+ const id = idRef.current;
162
+ if (!id || !registerOutlet || !unregisterOutlet) return;
163
+ registerOutlet(id, layer);
164
+ return () => unregisterOutlet(id);
165
+ }, [
166
+ layer,
167
+ registerOutlet,
168
+ unregisterOutlet
169
+ ]);
170
+ const getPortalDestination = (0, react.useCallback)(() => document.body, []);
171
+ if (!context || context.activeOutletId !== idRef.current) return null;
172
+ const { announcementsByPriority } = context;
173
+ const liveRegions = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(VisuallyHidden, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
174
+ "aria-atomic": "false",
175
+ "aria-live": "polite",
176
+ "aria-relevant": "additions",
177
+ "data-testid": "str-chat__aria-live-region--polite",
178
+ role: "status",
179
+ children: announcementsByPriority.polite.map((announcement) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: announcement.message }, announcement.id))
180
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
181
+ "aria-atomic": "false",
182
+ "aria-live": "assertive",
183
+ "aria-relevant": "additions",
184
+ "data-testid": "str-chat__aria-live-region--assertive",
185
+ role: "alert",
186
+ children: announcementsByPriority.assertive.map((announcement) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: announcement.message }, announcement.id))
187
+ })] });
188
+ if (!portal) return liveRegions;
189
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_useNotificationApi.Portal, {
190
+ getPortalDestination,
191
+ isOpen: true,
192
+ children: liveRegions
193
+ });
194
+ };
195
+ //#endregion
114
196
  //#region src/components/Notifications/NotificationConfigurationContext.tsx
115
197
  var defaultNotificationDisplayFilter = () => true;
116
198
  var defaultNotificationConfigurationContextValue = { displayFilter: defaultNotificationDisplayFilter };
@@ -422,7 +504,7 @@ var DialogAnchor = ({ allowFlip = true, children, className, closeOnClickOutside
422
504
  autoFocus: focus,
423
505
  contain: trapFocus,
424
506
  restoreFocus: true,
425
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
507
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
426
508
  ...anchorDivProps,
427
509
  "aria-describedby": ariaDescribedBy,
428
510
  "aria-label": resolvedAriaLabel,
@@ -436,7 +518,7 @@ var DialogAnchor = ({ allowFlip = true, children, className, closeOnClickOutside
436
518
  role: resolvedRole,
437
519
  style: styles,
438
520
  tabIndex: typeof tabIndex !== "undefined" ? tabIndex : 0,
439
- children
521
+ children: [children, resolvedAriaModal && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AriaLiveOutlet, { layer: 1 })]
440
522
  })
441
523
  })
442
524
  });
@@ -643,6 +725,7 @@ var UserContextMenuButton = ({ children, className, imageUrl, role = "menuitem",
643
725
  role,
644
726
  type: "button",
645
727
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Avatar, {
728
+ "aria-hidden": true,
646
729
  imageUrl,
647
730
  size: "sm",
648
731
  userName
@@ -787,13 +870,28 @@ var ContextMenuButton = (props) => {
787
870
  });
788
871
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BaseContextMenuButton, { ...buttonProps });
789
872
  };
790
- var ContextMenuBackButton = ({ children, className, role = "menuitem", ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
791
- ...props,
792
- className: (0, clsx.default)("str-chat__context-menu__back-button", className),
793
- role,
794
- type: "button",
795
- children
796
- });
873
+ var ContextMenuBackButton = ({ "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, children, className, role = "menuitem", ...props }) => {
874
+ const { t } = require_useNotificationApi.useTranslationContext();
875
+ const generatedBackNavigationLabelId = require_useNotificationApi.useStableId();
876
+ const generatedVisibleLabelId = require_useNotificationApi.useStableId();
877
+ const resolvedAriaLabel = ariaLabel ?? t("aria/Back to parent menu button");
878
+ const resolvedAriaLabelledBy = ariaLabelledBy ?? `${generatedVisibleLabelId} ${generatedBackNavigationLabelId}`;
879
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
880
+ ...props,
881
+ "aria-labelledby": resolvedAriaLabelledBy,
882
+ className: (0, clsx.default)("str-chat__context-menu__back-button", className),
883
+ role,
884
+ type: "button",
885
+ children: [!ariaLabelledBy && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VisuallyHidden, {
886
+ id: generatedBackNavigationLabelId,
887
+ children: resolvedAriaLabel
888
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
889
+ className: "str-chat__context-menu__back-button__content",
890
+ id: generatedVisibleLabelId,
891
+ children
892
+ })]
893
+ });
894
+ };
797
895
  var ContextMenuHeader = ({ children, className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
798
896
  ...props,
799
897
  className: (0, clsx.default)("str-chat__context-menu__header", className),
@@ -841,6 +939,13 @@ var resolveContextMenuFocusRestoreTarget = ({ contextMenuRoot, request }) => {
841
939
  if (request.index >= 0) return getVisibleContextMenuKeyboardNavigationItems(contextMenuRoot)[request.index] ?? null;
842
940
  return null;
843
941
  };
942
+ var CONTEXT_MENU_HEADER_SELECTOR = ".str-chat__context-menu__header";
943
+ var resolveSubmenuInitialFocusTarget = ({ contextMenuRoot, initialFocus, itemSelector }) => {
944
+ if (initialFocus === "none") return null;
945
+ const items = getVisibleContextMenuKeyboardNavigationItems(contextMenuRoot, itemSelector);
946
+ if (initialFocus === "first") return items[0] ?? null;
947
+ return items.find((item) => !item.closest(CONTEXT_MENU_HEADER_SELECTOR)) ?? items[0] ?? null;
948
+ };
844
949
  var ContextMenuContext = react.default.createContext(void 0);
845
950
  var useContextMenuContext = () => (0, react.useContext)(ContextMenuContext);
846
951
  /**
@@ -850,7 +955,10 @@ var useContextMenuContext = () => (0, react.useContext)(ContextMenuContext);
850
955
  * customize submenu/back navigation behavior while keeping the same anchor/focus
851
956
  * handling from `ContextMenu`.
852
957
  */
853
- function ContextMenuContent({ anchorReferenceElement, backLabel = "Back", children, className, enableAnimations = true, Header, items, ItemsWrapper, keyboardNavigation, menuClassName, onClose, onMenuLevelChange, transitionDirection, ...props }) {
958
+ function ContextMenuContent({ anchorReferenceElement, backButtonAriaLabel, backLabel, children, className, enableAnimations = true, Header, items, ItemsWrapper, keyboardNavigation, menuClassName, onClose, onMenuLevelChange, transitionDirection, ...props }) {
959
+ const { t } = require_useNotificationApi.useTranslationContext();
960
+ const resolvedBackLabel = backLabel ?? t("Back");
961
+ const { ["aria-describedby"]: rootAriaDescribedBy, ["aria-label"]: rootAriaLabel, ["aria-labelledby"]: rootAriaLabelledBy, ...contextMenuRootProps } = props;
854
962
  const rootLevel = (0, react.useMemo)(() => ({
855
963
  Header,
856
964
  items,
@@ -866,18 +974,23 @@ function ContextMenuContent({ anchorReferenceElement, backLabel = "Back", childr
866
974
  const [menuBodyAnimationKey, setMenuBodyAnimationKey] = (0, react.useState)(0);
867
975
  const contextMenuRootRef = (0, react.useRef)(null);
868
976
  const focusRestoreRequestRef = (0, react.useRef)(null);
977
+ const pendingForwardFocusRef = (0, react.useRef)(null);
869
978
  const activeMenu = menuStack[menuStack.length - 1];
979
+ const keyboardNavigationItemSelector = keyboardNavigation?.itemSelector ?? DEFAULT_CONTEXT_MENU_KEYBOARD_NAVIGATION_ITEM_SELECTOR;
870
980
  const ActiveMenuItemsWrapper = activeMenu.ItemsWrapper ?? react.default.Fragment;
871
981
  const closeMenu = (0, react.useCallback)(() => {
872
982
  onClose?.();
873
983
  }, [onClose]);
874
- const openSubmenu = (0, react.useCallback)(({ focusReturnTarget, Header, ItemsWrapper: SubmenuItemsWrapper, menuClassName, Submenu }) => {
984
+ const openSubmenu = (0, react.useCallback)(({ focusReturnTarget, Header, initialFocus = "first-item", ItemsWrapper: SubmenuItemsWrapper, menuClassName, Submenu }) => {
985
+ pendingForwardFocusRef.current = initialFocus;
986
+ focusRestoreRequestRef.current = null;
875
987
  const nextLevel = {
876
988
  focusRestoreRequest: createContextMenuFocusRestoreRequest({
877
989
  contextMenuRoot: contextMenuRootRef.current,
878
990
  focusReturnTarget
879
991
  }),
880
992
  Header,
993
+ initialFocus,
881
994
  ItemsWrapper: SubmenuItemsWrapper ?? ItemsWrapper,
882
995
  menuClassName,
883
996
  Submenu
@@ -888,6 +1001,7 @@ function ContextMenuContent({ anchorReferenceElement, backLabel = "Back", childr
888
1001
  setMenuStack((current) => {
889
1002
  if (current.length <= 1) return current;
890
1003
  focusRestoreRequestRef.current = current[current.length - 1]?.focusRestoreRequest ?? null;
1004
+ pendingForwardFocusRef.current = null;
891
1005
  return current.slice(0, -1);
892
1006
  });
893
1007
  }, []);
@@ -900,17 +1014,24 @@ function ContextMenuContent({ anchorReferenceElement, backLabel = "Back", childr
900
1014
  (0, react.useEffect)(() => {
901
1015
  onMenuLevelChange?.(menuStack.length);
902
1016
  }, [menuStack.length, onMenuLevelChange]);
903
- (0, react.useEffect)(() => {
1017
+ (0, react.useLayoutEffect)(() => {
904
1018
  const focusRestoreRequest = focusRestoreRequestRef.current;
905
- if (!focusRestoreRequest) return;
906
- focusRestoreRequestRef.current = null;
907
- requestAnimationFrame(() => {
908
- resolveContextMenuFocusRestoreTarget({
909
- contextMenuRoot: contextMenuRootRef.current,
910
- request: focusRestoreRequest
911
- })?.focus();
912
- });
913
- }, [menuStack.length]);
1019
+ const pendingForwardFocus = pendingForwardFocusRef.current;
1020
+ if (!focusRestoreRequest && !pendingForwardFocus) return;
1021
+ const contextMenuRoot = contextMenuRootRef.current;
1022
+ (focusRestoreRequest ? resolveContextMenuFocusRestoreTarget({
1023
+ contextMenuRoot,
1024
+ request: focusRestoreRequest
1025
+ }) : resolveSubmenuInitialFocusTarget({
1026
+ contextMenuRoot,
1027
+ initialFocus: pendingForwardFocus,
1028
+ itemSelector: keyboardNavigationItemSelector
1029
+ }))?.focus();
1030
+ }, [
1031
+ keyboardNavigationItemSelector,
1032
+ menuBodyAnimationKey,
1033
+ menuStack.length
1034
+ ]);
914
1035
  (0, react.useEffect)(() => {
915
1036
  if (!transitionDirection) return;
916
1037
  setMenuBodyAnimationKey((value) => value + 1);
@@ -922,10 +1043,7 @@ function ContextMenuContent({ anchorReferenceElement, backLabel = "Back", childr
922
1043
  const unregisterDialogSubmenu = (0, react.useCallback)(() => {
923
1044
  dialogSubmenuOpenCountRef.current = Math.max(0, dialogSubmenuOpenCountRef.current - 1);
924
1045
  }, []);
925
- const rovingFocusKeyDownHandler = (0, react.useMemo)(() => {
926
- const itemSelector = keyboardNavigation?.itemSelector ?? DEFAULT_CONTEXT_MENU_KEYBOARD_NAVIGATION_ITEM_SELECTOR;
927
- return createRovingFocusKeyDownHandler({ getItems: (event) => getVisibleContextMenuKeyboardNavigationItems(event.currentTarget, itemSelector) });
928
- }, [keyboardNavigation]);
1046
+ const rovingFocusKeyDownHandler = (0, react.useMemo)(() => createRovingFocusKeyDownHandler({ getItems: (event) => getVisibleContextMenuKeyboardNavigationItems(event.currentTarget, keyboardNavigationItemSelector) }), [keyboardNavigationItemSelector]);
929
1047
  const escapeConsumedRef = (0, react.useRef)(false);
930
1048
  const keyboardNavigationHandler = (0, react.useCallback)((event) => {
931
1049
  if (event.key === "Escape") {
@@ -951,6 +1069,7 @@ function ContextMenuContent({ anchorReferenceElement, backLabel = "Back", childr
951
1069
  event.stopPropagation();
952
1070
  }
953
1071
  }, []);
1072
+ const isSubmenuLevel = menuStack.length > 1;
954
1073
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextMenuContext.Provider, {
955
1074
  value: {
956
1075
  anchorReferenceElement,
@@ -961,15 +1080,19 @@ function ContextMenuContent({ anchorReferenceElement, backLabel = "Back", childr
961
1080
  unregisterDialogSubmenu
962
1081
  },
963
1082
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ContextMenuRoot, {
1083
+ "aria-describedby": isSubmenuLevel ? void 0 : rootAriaDescribedBy,
1084
+ "aria-label": isSubmenuLevel ? t("aria/Submenu") : rootAriaLabel,
1085
+ "aria-labelledby": isSubmenuLevel ? void 0 : rootAriaLabelledBy,
964
1086
  className: (0, clsx.default)(className, activeMenu.menuClassName),
965
1087
  "data-str-chat-enable-animations": enableAnimations,
966
1088
  onKeyDownCapture: keyboardNavigationHandler,
967
1089
  onKeyUpCapture: suppressEscapeKeyUp,
968
1090
  ref: contextMenuRootRef,
969
- ...props,
1091
+ ...contextMenuRootProps,
970
1092
  children: [activeMenu.Header ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(activeMenu.Header, {}) : menuStack.length > 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextMenuHeader, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ContextMenuBackButton, {
1093
+ "aria-label": backButtonAriaLabel,
971
1094
  onClick: returnToParentMenu,
972
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_useNotificationApi.IconChevronLeft, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: backLabel })]
1095
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_useNotificationApi.IconChevronLeft, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: resolvedBackLabel })]
973
1096
  }) }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextMenuBody, {
974
1097
  className: (0, clsx.default)({
975
1098
  "str-chat__context-menu__body--submenu-backward": transitionDirection === "backward",
@@ -1074,9 +1197,13 @@ var ContextMenu = (props) => {
1074
1197
  transitionDirection
1075
1198
  });
1076
1199
  if (isAnchored) {
1077
- const { backLabel: _b, enableAnimations: _ea, Header: _h, items: _i, ItemsWrapper: _w, keyboardNavigation: _kn, menuClassName: _m, onClose: _c, role: _r, ...anchorDivProps } = menuProps;
1200
+ const { ["aria-describedby"]: ariaDescribedBy, ["aria-label"]: ariaLabel, ["aria-labelledby"]: ariaLabelledBy, backButtonAriaLabel: _bbal, backLabel: _b, enableAnimations: _ea, Header: _h, items: _i, ItemsWrapper: _w, keyboardNavigation: _kn, menuClassName: _m, onClose: _c, role: _r, ...anchorDivProps } = menuProps;
1201
+ const isSubmenuLevel = menuLevel > 1;
1078
1202
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogAnchor, {
1079
1203
  allowFlip,
1204
+ "aria-describedby": isSubmenuLevel ? void 0 : ariaDescribedBy,
1205
+ "aria-label": isSubmenuLevel ? void 0 : ariaLabel,
1206
+ "aria-labelledby": isSubmenuLevel ? void 0 : ariaLabelledBy,
1080
1207
  closeOnClickOutside,
1081
1208
  closeTransitionMs,
1082
1209
  dialogManagerId,
@@ -1147,8 +1274,7 @@ var PromptHeader = ({ className, close, description, descriptionId, goBack, Lead
1147
1274
  className: "str-chat__prompt__header__trailing-content",
1148
1275
  children: [TrailingContent && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrailingContent, {}), close && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_useNotificationApi.Button, {
1149
1276
  appearance: "ghost",
1150
- "aria-describedby": hasDescription ? resolvedDescriptionId : void 0,
1151
- "aria-label": typeof title === "string" ? t("Close prompt: {{ title }}", { title }) : t("Close"),
1277
+ "aria-label": t("Close"),
1152
1278
  circular: true,
1153
1279
  className: "str-chat__prompt__header__close-button",
1154
1280
  onClick: close,
@@ -1224,19 +1350,21 @@ var useResolvedModalAriaProps = ({ ariaDescribedby, ariaLabel, ariaLabelledby, d
1224
1350
  };
1225
1351
  //#endregion
1226
1352
  //#region src/components/Modal/GlobalModal.tsx
1227
- var GlobalModal = ({ "aria-describedby": ariaDescribedby, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, children, className, CloseButtonOnOverlay, dialogId, dialogRootProps, onClose, onCloseAttempt, open, role = "dialog" }) => {
1353
+ var GlobalModal = ({ "aria-describedby": ariaDescribedby, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, children, className, CloseButtonOnOverlay, dialogId, dialogRootProps, getInitialFocusElement, initialFocusStrategy, onClose, onCloseAttempt, open, role = "dialog" }) => {
1228
1354
  const generatedDialogId = require_useNotificationApi.useStableId();
1229
1355
  const resolvedDialogId = dialogId ?? `modal-dialog-${generatedDialogId}`;
1230
1356
  const dialog = require_useNotificationApi.useModalDialog(resolvedDialogId);
1231
1357
  const isOpen = require_useNotificationApi.useModalDialogIsOpen(resolvedDialogId);
1232
1358
  const isTopmost = require_useNotificationApi.useModalDialogIsTopmost(resolvedDialogId);
1233
1359
  const overlayRef = (0, react.useRef)(null);
1360
+ const dialogRef = (0, react.useRef)(null);
1234
1361
  const closeButtonRef = (0, react.useRef)(null);
1235
1362
  const closingRef = (0, react.useRef)(false);
1236
1363
  const { theme } = require_useNotificationApi.useChatContext();
1237
1364
  const { NotificationList: NotificationList$1 = NotificationList } = require_useNotificationApi.useComponentContext();
1238
1365
  const { className: dialogRootClassName, onKeyDown: dialogRootOnKeyDown, ...dialogRootPropsRest } = dialogRootProps ?? {};
1239
- const dialogLabelingBaseId = dialog.id;
1366
+ const dialogLabelingBaseId = resolvedDialogId;
1367
+ const resolvedInitialFocusStrategy = initialFocusStrategy ?? "firstElement";
1240
1368
  const resolvedModalAriaProps = useResolvedModalAriaProps({
1241
1369
  ariaDescribedby,
1242
1370
  ariaLabel,
@@ -1269,8 +1397,18 @@ var GlobalModal = ({ "aria-describedby": ariaDescribedby, "aria-label": ariaLabe
1269
1397
  };
1270
1398
  const handleDialogKeyDown = (event) => {
1271
1399
  dialogRootOnKeyDown?.(event);
1272
- if (event.defaultPrevented || event.key !== "Escape" || !isTopmost) return;
1273
- maybeClose("escape", event);
1400
+ if (event.defaultPrevented || !isTopmost) return;
1401
+ if (event.key === "Escape") {
1402
+ maybeClose("escape", event);
1403
+ return;
1404
+ }
1405
+ if (event.key === "Enter" && event.target === dialogRef.current) {
1406
+ const target = dialogRef.current?.querySelector("[data-autofocus]");
1407
+ if (target) {
1408
+ event.preventDefault();
1409
+ target.focus();
1410
+ }
1411
+ }
1274
1412
  };
1275
1413
  (0, react.useEffect)(() => {
1276
1414
  if (!open) {
@@ -1284,6 +1422,21 @@ var GlobalModal = ({ "aria-describedby": ariaDescribedby, "aria-label": ariaLabe
1284
1422
  isOpen,
1285
1423
  open
1286
1424
  ]);
1425
+ (0, react.useEffect)(() => {
1426
+ if (!open || !isOpen || !isTopmost || !getInitialFocusElement && resolvedInitialFocusStrategy === "firstElement") return;
1427
+ const frame = requestAnimationFrame(() => {
1428
+ const dialogElement = dialogRef.current;
1429
+ if (!dialogElement) return;
1430
+ (getInitialFocusElement?.(dialogElement) ?? dialogElement).focus();
1431
+ });
1432
+ return () => cancelAnimationFrame(frame);
1433
+ }, [
1434
+ getInitialFocusElement,
1435
+ isOpen,
1436
+ isTopmost,
1437
+ open,
1438
+ resolvedInitialFocusStrategy
1439
+ ]);
1287
1440
  if (!open || !isOpen) return null;
1288
1441
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_useNotificationApi.DialogPortalEntry, {
1289
1442
  dialogId: resolvedDialogId,
@@ -1296,7 +1449,7 @@ var GlobalModal = ({ "aria-describedby": ariaDescribedby, "aria-label": ariaLabe
1296
1449
  ref: overlayRef,
1297
1450
  children: [
1298
1451
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_aria_focus.FocusScope, {
1299
- autoFocus: isTopmost,
1452
+ autoFocus: isTopmost && !getInitialFocusElement && resolvedInitialFocusStrategy === "firstElement",
1300
1453
  contain: isTopmost,
1301
1454
  restoreFocus: true,
1302
1455
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -1308,15 +1461,16 @@ var GlobalModal = ({ "aria-describedby": ariaDescribedby, "aria-label": ariaLabe
1308
1461
  className: (0, clsx.default)("str-chat__modal__dialog", dialogRootClassName),
1309
1462
  inert: isTopmost ? void 0 : true,
1310
1463
  onKeyDown: handleDialogKeyDown,
1464
+ ref: dialogRef,
1311
1465
  role,
1312
1466
  tabIndex: isTopmost ? 0 : -1,
1313
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_useNotificationApi.DialogManagerProvider, {
1467
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_useNotificationApi.DialogManagerProvider, {
1314
1468
  id: `${resolvedDialogId}-floating-dialog-manager`,
1315
1469
  portalDestinationProps: {
1316
1470
  captureOutsideClicks: true,
1317
1471
  className: "str-chat__modal__floating-dialog-overlay"
1318
1472
  },
1319
- children
1473
+ children: [children, isTopmost && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AriaLiveOutlet, { layer: 1 })]
1320
1474
  })
1321
1475
  })
1322
1476
  }),
@@ -2388,7 +2542,7 @@ var GalleryHeader = ({ currentItem }) => {
2388
2542
  };
2389
2543
  //#endregion
2390
2544
  //#region src/components/VideoPlayer/VideoPlayer.tsx
2391
- var ReactPlayer = react.default.lazy(() => Promise.resolve().then(() => require("./ReactPlayerWrapper.c340ab05.js")));
2545
+ var ReactPlayer = react.default.lazy(() => Promise.resolve().then(() => require("./ReactPlayerWrapper.60a93742.js")));
2392
2546
  var VideoPlayer = ({ isPlaying, thumbnailUrl, videoUrl }) => {
2393
2547
  const { LoadingIndicator: LoadingIndicator$1 = LoadingIndicator, VideoPlayer: VideoPlayerContext } = require_useNotificationApi.useComponentContext();
2394
2548
  return VideoPlayerContext ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VideoPlayerContext, {
@@ -2680,13 +2834,28 @@ var SwitchField = ({ children, description, fieldClassName, title, ...props }) =
2680
2834
  const switchId = id ?? `str-chat__switch-field-${generatedSwitchId}`;
2681
2835
  const switchLabelId = `${switchId}-label`;
2682
2836
  const inputRef = (0, react.useRef)(null);
2837
+ const announce = (0, react.useContext)(require_useNotificationApi.AriaLiveAnnouncerContext)?.announce;
2838
+ const { t } = require_useNotificationApi.useTranslationContext("SwitchField");
2683
2839
  const [uncontrolledChecked, setUncontrolledChecked] = (0, react.useState)(Boolean(defaultChecked));
2684
2840
  const isControlled = checked !== void 0;
2685
2841
  const isOn = isControlled ? checked : uncontrolledChecked;
2686
2842
  const isReadOnly = isControlled && onChange === void 0;
2843
+ const childLabelId = ((0, react.isValidElement)(children) ? children : void 0)?.props.id;
2844
+ const resolvedAriaLabelledBy = ariaLabelledBy ?? (!ariaLabel ? title ? switchLabelId : childLabelId : void 0);
2845
+ const resolveAnnouncementName = () => {
2846
+ if (title) return title;
2847
+ if (ariaLabel) return ariaLabel;
2848
+ if (resolvedAriaLabelledBy) {
2849
+ const labelEl = document.getElementById(resolvedAriaLabelledBy);
2850
+ return ((labelEl?.querySelector(".str-chat__form__switch-field__label__text"))?.textContent || labelEl?.textContent || "").trim() || void 0;
2851
+ }
2852
+ };
2687
2853
  const handleChange = (event) => {
2688
2854
  if (!isControlled) setUncontrolledChecked(event.target.checked);
2689
2855
  onChange?.(event);
2856
+ const setting = resolveAnnouncementName();
2857
+ if (!announce || !setting) return;
2858
+ announce(event.target.checked ? t("aria/{{ setting }} enabled", { setting }) : t("aria/{{ setting }} disabled", { setting }), { priority: "polite" });
2690
2859
  };
2691
2860
  const handleKeyDown = (event) => {
2692
2861
  onKeyDown?.(event);
@@ -2698,8 +2867,6 @@ var SwitchField = ({ children, description, fieldClassName, title, ...props }) =
2698
2867
  if (disabled || event.target === inputRef.current) return;
2699
2868
  inputRef.current?.click();
2700
2869
  };
2701
- const childLabelId = ((0, react.isValidElement)(children) ? children : void 0)?.props.id;
2702
- const resolvedAriaLabelledBy = ariaLabelledBy ?? (!ariaLabel ? title ? switchLabelId : childLabelId : void 0);
2703
2870
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2704
2871
  className: (0, clsx.default)("str-chat__form__switch-field", fieldClassName, disabled && "str-chat__form__switch-field--disabled"),
2705
2872
  children: [title ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SwitchFieldLabel, {
@@ -2839,7 +3006,6 @@ var TextInput = (0, react.forwardRef)(function TextInput({ className, disabled,
2839
3006
  className: "str-chat__form-text-input__control-row",
2840
3007
  children: [
2841
3008
  leading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2842
- "aria-hidden": true,
2843
3009
  className: "str-chat__form-text-input__leading",
2844
3010
  children: leading
2845
3011
  }) : null,
@@ -2858,7 +3024,6 @@ var TextInput = (0, react.forwardRef)(function TextInput({ className, disabled,
2858
3024
  children: trailingText
2859
3025
  }) : null,
2860
3026
  trailing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2861
- "aria-hidden": true,
2862
3027
  className: "str-chat__form-text-input__trailing",
2863
3028
  children: trailing
2864
3029
  }) : null
@@ -2938,6 +3103,16 @@ var renderPreviewText = (text) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(rea
2938
3103
  skipHtml: true,
2939
3104
  children: text
2940
3105
  });
3106
+ var createPlainTextProcessor = () => (0, unified.unified)().use(remark_parse.default).use(remarkPlugins);
3107
+ var plainTextProcessor;
3108
+ var stripMarkdownToText = (text) => {
3109
+ try {
3110
+ const processor = plainTextProcessor ?? (plainTextProcessor = createPlainTextProcessor());
3111
+ return (0, mdast_util_to_string.toString)(processor.runSync(processor.parse(text))) || text;
3112
+ } catch {
3113
+ return text;
3114
+ }
3115
+ };
2941
3116
  var getLatestPollVote = (latestVotesByOption) => {
2942
3117
  let latestVote;
2943
3118
  for (const optionVotes of Object.values(latestVotesByOption)) optionVotes.forEach((vote) => {
@@ -2946,32 +3121,130 @@ var getLatestPollVote = (latestVotesByOption) => {
2946
3121
  });
2947
3122
  return latestVote;
2948
3123
  };
2949
- var getLatestMessagePreview = (channel, t, userLanguage = "en", isMessageAIGenerated) => {
2950
- const latestMessage = channel.state.latestMessages[channel.state.latestMessages.length - 1];
3124
+ /**
3125
+ * Resolves the channel's latest-message preview once: the localized DISPLAY string, a `kind`
3126
+ * discriminant, and the bits announcements need (poll question, attachment type). Single source of
3127
+ * truth for the display variant ({@link getLatestMessagePreview}) and the announcement variant
3128
+ * ({@link getLatestMessagePreviewText}), so the translation lookup and branching run once per call.
3129
+ */
3130
+ var getLatestMessagePreviewParts = (channel, t, userLanguage = "en", latestMessageArg) => {
3131
+ const latestMessage = latestMessageArg ?? channel.state.latestMessages[channel.state.latestMessages.length - 1];
2951
3132
  const previewTextToRender = getTranslatedMessageText({
2952
3133
  language: userLanguage,
2953
3134
  message: latestMessage
2954
3135
  }) || latestMessage?.text;
2955
3136
  const poll = latestMessage?.poll;
2956
- if (!latestMessage) return t("Nothing yet...");
2957
- if (require_useNotificationApi.isMessageDeleted(latestMessage)) return t("Message deleted");
2958
- if (poll) if (!poll.vote_count) return t("📊 {{createdBy}} created: {{ pollName}}", {
2959
- createdBy: poll.created_by?.id === channel.getClient().userID ? t("You") : poll.created_by?.name ?? t("Poll"),
2960
- pollName: poll.name
2961
- });
2962
- else {
3137
+ if (!latestMessage) return {
3138
+ isUserMessageText: false,
3139
+ kind: "empty",
3140
+ latestMessage,
3141
+ text: t("Nothing yet...")
3142
+ };
3143
+ if (require_useNotificationApi.isMessageDeleted(latestMessage)) return {
3144
+ isUserMessageText: false,
3145
+ kind: "deleted",
3146
+ latestMessage,
3147
+ text: t("Message deleted")
3148
+ };
3149
+ if (poll) if (!poll.vote_count) {
3150
+ const createdBy = poll.created_by?.id === channel.getClient().userID ? t("You") : poll.created_by?.name ?? t("Poll");
3151
+ return {
3152
+ isUserMessageText: false,
3153
+ kind: "poll",
3154
+ latestMessage,
3155
+ pollName: poll.name,
3156
+ text: t("📊 {{createdBy}} created: {{ pollName}}", {
3157
+ createdBy,
3158
+ pollName: poll.name
3159
+ })
3160
+ };
3161
+ } else {
2963
3162
  const latestVote = getLatestPollVote(poll.latest_votes_by_option);
2964
3163
  const option = latestVote && poll.options.find((opt) => opt.id === latestVote.option_id);
2965
- if (option && latestVote) return t("📊 {{votedBy}} voted: {{pollOptionText}}", {
2966
- pollOptionText: option.text,
2967
- votedBy: latestVote?.user?.id === channel.getClient().userID ? t("You") : latestVote.user?.name ?? t("Poll")
2968
- });
3164
+ if (option && latestVote) return {
3165
+ isUserMessageText: false,
3166
+ kind: "poll",
3167
+ latestMessage,
3168
+ pollName: poll.name,
3169
+ text: t("📊 {{votedBy}} voted: {{pollOptionText}}", {
3170
+ pollOptionText: option.text,
3171
+ votedBy: latestVote?.user?.id === channel.getClient().userID ? t("You") : latestVote.user?.name ?? t("Poll")
3172
+ })
3173
+ };
3174
+ }
3175
+ if (previewTextToRender) return {
3176
+ isUserMessageText: true,
3177
+ kind: "text",
3178
+ latestMessage,
3179
+ text: previewTextToRender
3180
+ };
3181
+ if (latestMessage.command) return {
3182
+ isUserMessageText: false,
3183
+ kind: "command",
3184
+ latestMessage,
3185
+ text: `/${latestMessage.command}`
3186
+ };
3187
+ if (latestMessage.attachments?.length) return {
3188
+ isUserMessageText: false,
3189
+ kind: "attachment",
3190
+ latestMessage,
3191
+ text: t("🏙 Attachment...")
3192
+ };
3193
+ if (latestMessage.shared_location) return {
3194
+ isUserMessageText: false,
3195
+ kind: "location",
3196
+ latestMessage,
3197
+ text: t("📍Shared location")
3198
+ };
3199
+ return {
3200
+ isUserMessageText: false,
3201
+ kind: "text",
3202
+ latestMessage,
3203
+ text: t("Empty message...")
3204
+ };
3205
+ };
3206
+ /**
3207
+ * Maps a known attachment `type` to a localized, human-readable word (e.g. "image" → "Image"). The
3208
+ * cases are literal `t('aria/…')` calls so `i18next-cli` extracts them. Unknown/custom types return
3209
+ * `undefined`, so the announcement falls back to a generic "Attachment".
3210
+ */
3211
+ var getAttachmentTypeLabel = (type, t) => {
3212
+ switch (type) {
3213
+ case "audio": return t("aria/audio");
3214
+ case "file": return t("aria/file");
3215
+ case "giphy": return t("aria/GIF");
3216
+ case "image": return t("aria/image");
3217
+ case "video": return t("aria/video");
3218
+ case "voiceRecording": return t("aria/voice message");
3219
+ default: return;
2969
3220
  }
2970
- if (previewTextToRender) return isMessageAIGenerated?.(latestMessage) ? previewTextToRender : renderPreviewText(previewTextToRender);
2971
- if (latestMessage.command) return `/${latestMessage.command}`;
2972
- if (latestMessage.attachments?.length) return t("🏙 Attachment...");
2973
- if (latestMessage.shared_location) return t("📍Shared location");
2974
- return t("Empty message...");
3221
+ };
3222
+ /**
3223
+ * The text announced for the channel's latest message (for an accessible name / live region).
3224
+ * Non-text content gets a concise, screen-reader-friendly phrasing that may differ from the visible
3225
+ * preview: a poll announces its question, an attachment its type, a shared location and a deleted
3226
+ * message a plain phrase. User-message text is stripped of markdown so the words are read, not the
3227
+ * syntax (AI-generated text is returned verbatim, matching the display path).
3228
+ */
3229
+ var getLatestMessagePreviewText = (channel, t, userLanguage = "en", isMessageAIGenerated, latestMessage) => {
3230
+ const { isUserMessageText, kind, latestMessage: resolvedLatestMessage, pollName, text } = getLatestMessagePreviewParts(channel, t, userLanguage, latestMessage);
3231
+ switch (kind) {
3232
+ case "poll": return t("aria/Poll: {{ pollName }}", { pollName: pollName ?? "" });
3233
+ case "attachment": {
3234
+ const realAttachments = resolvedLatestMessage?.attachments?.filter((attachment) => !attachment.og_scrape_url) ?? [];
3235
+ if (realAttachments.length > 1) return t("aria/Message with attachments");
3236
+ const typeLabel = getAttachmentTypeLabel(realAttachments[0]?.type, t);
3237
+ return typeLabel ? t("aria/Attachment {{ attachmentType }}", { attachmentType: typeLabel }) : t("aria/Attachment");
3238
+ }
3239
+ case "location": return t("aria/Shared location");
3240
+ case "empty": return t("aria/There are no messages in this chat.");
3241
+ case "text": return isUserMessageText && resolvedLatestMessage && !isMessageAIGenerated?.(resolvedLatestMessage) ? stripMarkdownToText(text) : text;
3242
+ default: return text;
3243
+ }
3244
+ };
3245
+ var getLatestMessagePreview = (channel, t, userLanguage = "en", isMessageAIGenerated, latestMessage) => {
3246
+ const { isUserMessageText, latestMessage: resolvedLatestMessage, text } = getLatestMessagePreviewParts(channel, t, userLanguage, latestMessage);
3247
+ return isUserMessageText && resolvedLatestMessage && !isMessageAIGenerated?.(resolvedLatestMessage) ? renderPreviewText(text) : text;
2975
3248
  };
2976
3249
  /**
2977
3250
  * Channel display image: channel.data.image, or for DM (2 members) the other member's user.image.
@@ -3196,7 +3469,8 @@ var Notification = (0, react.forwardRef)(({ className, entryDirection, Icon = De
3196
3469
  };
3197
3470
  const isPersistent = !notification.duration;
3198
3471
  const severity = notification.severity;
3199
- const livePriority = severity === "error" ? "assertive" : "polite";
3472
+ const ariaLiveOverride = notification.metadata?.ariaLive;
3473
+ const livePriority = ariaLiveOverride === "assertive" || ariaLiveOverride === "polite" ? ariaLiveOverride : severity === "error" ? "assertive" : "polite";
3200
3474
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3201
3475
  className: (0, clsx.default)("str-chat__notification", entryDirection && `str-chat__notification--enter-from-${entryDirection}`, transitionState === "enter" && "str-chat__notification--is-entering", transitionState === "exit" && "str-chat__notification--is-exiting", severity && `str-chat__notification--${severity}`, severity === "loading" && "str-chat__notification--loading", className),
3202
3476
  "data-testid": "notification",
@@ -3621,6 +3895,18 @@ Object.defineProperty(exports, "Alert", {
3621
3895
  return Alert;
3622
3896
  }
3623
3897
  });
3898
+ Object.defineProperty(exports, "AriaLiveOutlet", {
3899
+ enumerable: true,
3900
+ get: function() {
3901
+ return AriaLiveOutlet;
3902
+ }
3903
+ });
3904
+ Object.defineProperty(exports, "AriaLiveOutletContext", {
3905
+ enumerable: true,
3906
+ get: function() {
3907
+ return AriaLiveOutletContext;
3908
+ }
3909
+ });
3624
3910
  Object.defineProperty(exports, "Avatar", {
3625
3911
  enumerable: true,
3626
3912
  get: function() {
@@ -3951,6 +4237,12 @@ Object.defineProperty(exports, "VideoThumbnail", {
3951
4237
  return VideoThumbnail;
3952
4238
  }
3953
4239
  });
4240
+ Object.defineProperty(exports, "VisuallyHidden", {
4241
+ enumerable: true,
4242
+ get: function() {
4243
+ return VisuallyHidden;
4244
+ }
4245
+ });
3954
4246
  Object.defineProperty(exports, "createDefaultPickNext", {
3955
4247
  enumerable: true,
3956
4248
  get: function() {
@@ -3987,6 +4279,18 @@ Object.defineProperty(exports, "getLatestMessagePreview", {
3987
4279
  return getLatestMessagePreview;
3988
4280
  }
3989
4281
  });
4282
+ Object.defineProperty(exports, "getLatestMessagePreviewText", {
4283
+ enumerable: true,
4284
+ get: function() {
4285
+ return getLatestMessagePreviewText;
4286
+ }
4287
+ });
4288
+ Object.defineProperty(exports, "getNextRovingFocusIndex", {
4289
+ enumerable: true,
4290
+ get: function() {
4291
+ return getNextRovingFocusIndex;
4292
+ }
4293
+ });
3990
4294
  Object.defineProperty(exports, "getTranslatedMessageText", {
3991
4295
  enumerable: true,
3992
4296
  get: function() {
@@ -4053,6 +4357,12 @@ Object.defineProperty(exports, "useAriaIdentifiers", {
4053
4357
  return useAriaIdentifiers;
4054
4358
  }
4055
4359
  });
4360
+ Object.defineProperty(exports, "useAriaLiveOutletContext", {
4361
+ enumerable: true,
4362
+ get: function() {
4363
+ return useAriaLiveOutletContext;
4364
+ }
4365
+ });
4056
4366
  Object.defineProperty(exports, "useChannelDisplayName", {
4057
4367
  enumerable: true,
4058
4368
  get: function() {
@@ -4156,4 +4466,4 @@ Object.defineProperty(exports, "useStableCallback", {
4156
4466
  }
4157
4467
  });
4158
4468
 
4159
- //# sourceMappingURL=useChannelHeaderOnlineStatus.d39ede24.js.map
4469
+ //# sourceMappingURL=useChannelHeaderOnlineStatus.72753090.js.map