stream-chat-react 14.5.0 → 14.6.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 (164) hide show
  1. package/dist/cjs/{ReactPlayerWrapper.963d6170.js → ReactPlayerWrapper.30240f76.js} +2 -2
  2. package/dist/cjs/{ReactPlayerWrapper.963d6170.js.map → ReactPlayerWrapper.30240f76.js.map} +1 -1
  3. package/dist/cjs/channel-detail.js +3007 -0
  4. package/dist/cjs/channel-detail.js.map +1 -0
  5. package/dist/cjs/emojis.js +5 -4
  6. package/dist/cjs/emojis.js.map +1 -1
  7. package/dist/cjs/index.js +1804 -4064
  8. package/dist/cjs/index.js.map +1 -1
  9. package/dist/cjs/useChannelHeaderOnlineStatus.6546ac83.js +4143 -0
  10. package/dist/cjs/useChannelHeaderOnlineStatus.6546ac83.js.map +1 -0
  11. package/dist/cjs/useMessageComposerController.c0dad9bc.js +99 -0
  12. package/dist/cjs/useMessageComposerController.c0dad9bc.js.map +1 -0
  13. package/dist/cjs/{useNotificationApi.e9312774.js → useNotificationApi.eb753f31.js} +121 -166
  14. package/dist/cjs/useNotificationApi.eb753f31.js.map +1 -0
  15. package/dist/css/channel-detail.css +825 -0
  16. package/dist/css/channel-detail.css.map +1 -0
  17. package/dist/css/index.css +111 -31
  18. package/dist/css/index.css.map +1 -1
  19. package/dist/es/channel-detail.mjs +2950 -0
  20. package/dist/es/channel-detail.mjs.map +1 -0
  21. package/dist/es/emojis.mjs +2 -1
  22. package/dist/es/emojis.mjs.map +1 -1
  23. package/dist/es/index.mjs +1399 -3669
  24. package/dist/es/index.mjs.map +1 -1
  25. package/dist/es/useChannelHeaderOnlineStatus.c5215b13.mjs +3600 -0
  26. package/dist/es/useChannelHeaderOnlineStatus.c5215b13.mjs.map +1 -0
  27. package/dist/es/useMessageComposerController.29f189b4.mjs +69 -0
  28. package/dist/es/useMessageComposerController.29f189b4.mjs.map +1 -0
  29. package/dist/es/{useNotificationApi.4be515a0.mjs → useNotificationApi.fa5cddf9.mjs} +104 -137
  30. package/dist/es/useNotificationApi.fa5cddf9.mjs.map +1 -0
  31. package/dist/types/components/AudioPlayback/components/index.d.ts +1 -0
  32. package/dist/types/components/AudioPlayback/components/index.d.ts.map +1 -1
  33. package/dist/types/components/Avatar/ChannelAvatar.d.ts.map +1 -1
  34. package/dist/types/components/ChannelHeader/hooks/useChannelHasMembersOnline.d.ts +7 -0
  35. package/dist/types/components/ChannelHeader/hooks/useChannelHasMembersOnline.d.ts.map +1 -0
  36. package/dist/types/components/ChannelHeader/hooks/useChannelHeaderOnlineStatus.d.ts +6 -1
  37. package/dist/types/components/ChannelHeader/hooks/useChannelHeaderOnlineStatus.d.ts.map +1 -1
  38. package/dist/types/components/ChannelListItem/hooks/index.d.ts +1 -0
  39. package/dist/types/components/ChannelListItem/hooks/index.d.ts.map +1 -1
  40. package/dist/types/components/ChannelListItem/hooks/useChannelPreviewInfo.d.ts.map +1 -1
  41. package/dist/types/components/ChannelListItem/hooks/useIsUserMuted.d.ts +2 -0
  42. package/dist/types/components/ChannelListItem/hooks/useIsUserMuted.d.ts.map +1 -0
  43. package/dist/types/components/Chat/Chat.d.ts.map +1 -1
  44. package/dist/types/components/Dialog/components/Prompt.d.ts +7 -4
  45. package/dist/types/components/Dialog/components/Prompt.d.ts.map +1 -1
  46. package/dist/types/components/Dialog/service/DialogPortal.d.ts +5 -1
  47. package/dist/types/components/Dialog/service/DialogPortal.d.ts.map +1 -1
  48. package/dist/types/components/FileIcon/iconMap.d.ts.map +1 -1
  49. package/dist/types/components/Form/Checkbox.d.ts +8 -0
  50. package/dist/types/components/Form/Checkbox.d.ts.map +1 -0
  51. package/dist/types/components/Form/SwitchField.d.ts +6 -0
  52. package/dist/types/components/Form/SwitchField.d.ts.map +1 -1
  53. package/dist/types/components/Form/index.d.ts +1 -0
  54. package/dist/types/components/Form/index.d.ts.map +1 -1
  55. package/dist/types/components/Icons/icons.d.ts +12 -0
  56. package/dist/types/components/Icons/icons.d.ts.map +1 -1
  57. package/dist/types/components/InfiniteScrollPaginator/index.d.ts +1 -0
  58. package/dist/types/components/InfiniteScrollPaginator/index.d.ts.map +1 -1
  59. package/dist/types/components/Message/hooks/useMessageTextStreaming.d.ts +1 -1
  60. package/dist/types/components/Message/hooks/useReactionsFetcher.d.ts +1 -1
  61. package/dist/types/components/Message/hooks/useReactionsFetcher.d.ts.map +1 -1
  62. package/dist/types/components/Modal/GlobalModal.d.ts +3 -1
  63. package/dist/types/components/Modal/GlobalModal.d.ts.map +1 -1
  64. package/dist/types/components/Notifications/hooks/useNotificationApi.d.ts.map +1 -1
  65. package/dist/types/components/Poll/PollCreationDialog/MultipleAnswersField.d.ts.map +1 -1
  66. package/dist/types/components/Poll/PollOptionSelector.d.ts +0 -4
  67. package/dist/types/components/Poll/PollOptionSelector.d.ts.map +1 -1
  68. package/dist/types/context/DialogManagerContext.d.ts +3 -1
  69. package/dist/types/context/DialogManagerContext.d.ts.map +1 -1
  70. package/dist/types/i18n/Streami18n.d.ts +99 -0
  71. package/dist/types/i18n/Streami18n.d.ts.map +1 -1
  72. package/dist/types/plugins/ChannelDetail/AvatarWithChannelDetail.d.ts +9 -0
  73. package/dist/types/plugins/ChannelDetail/AvatarWithChannelDetail.d.ts.map +1 -0
  74. package/dist/types/plugins/ChannelDetail/ChannelDetail.d.ts +14 -0
  75. package/dist/types/plugins/ChannelDetail/ChannelDetail.d.ts.map +1 -0
  76. package/dist/types/plugins/ChannelDetail/ChannelDetailContext.d.ts +11 -0
  77. package/dist/types/plugins/ChannelDetail/ChannelDetailContext.d.ts.map +1 -0
  78. package/dist/types/plugins/ChannelDetail/ChannelDetailEmptyList.d.ts +3 -0
  79. package/dist/types/plugins/ChannelDetail/ChannelDetailEmptyList.d.ts.map +1 -0
  80. package/dist/types/plugins/ChannelDetail/ChannelDetailListLoadingIndicator.d.ts +6 -0
  81. package/dist/types/plugins/ChannelDetail/ChannelDetailListLoadingIndicator.d.ts.map +1 -0
  82. package/dist/types/plugins/ChannelDetail/ChannelDetailNavButton.d.ts +15 -0
  83. package/dist/types/plugins/ChannelDetail/ChannelDetailNavButton.d.ts.map +1 -0
  84. package/dist/types/plugins/ChannelDetail/ChannelDetailSearchInput.d.ts +8 -0
  85. package/dist/types/plugins/ChannelDetail/ChannelDetailSearchInput.d.ts.map +1 -0
  86. package/dist/types/plugins/ChannelDetail/SectionNavigator/SectionNavigator.d.ts +52 -0
  87. package/dist/types/plugins/ChannelDetail/SectionNavigator/SectionNavigator.d.ts.map +1 -0
  88. package/dist/types/plugins/ChannelDetail/SectionNavigator/SectionNavigatorHeader.d.ts +11 -0
  89. package/dist/types/plugins/ChannelDetail/SectionNavigator/SectionNavigatorHeader.d.ts.map +1 -0
  90. package/dist/types/plugins/ChannelDetail/SectionNavigator/index.d.ts +3 -0
  91. package/dist/types/plugins/ChannelDetail/SectionNavigator/index.d.ts.map +1 -0
  92. package/dist/types/plugins/ChannelDetail/Views/ChannelFilesView/ChannelFilesEmptyList.d.ts +2 -0
  93. package/dist/types/plugins/ChannelDetail/Views/ChannelFilesView/ChannelFilesEmptyList.d.ts.map +1 -0
  94. package/dist/types/plugins/ChannelDetail/Views/ChannelFilesView/ChannelFilesView.d.ts +5 -0
  95. package/dist/types/plugins/ChannelDetail/Views/ChannelFilesView/ChannelFilesView.d.ts.map +1 -0
  96. package/dist/types/plugins/ChannelDetail/Views/ChannelFilesView/ChannelFilesView.utils.d.ts +40 -0
  97. package/dist/types/plugins/ChannelDetail/Views/ChannelFilesView/ChannelFilesView.utils.d.ts.map +1 -0
  98. package/dist/types/plugins/ChannelDetail/Views/ChannelFilesView/index.d.ts +5 -0
  99. package/dist/types/plugins/ChannelDetail/Views/ChannelFilesView/index.d.ts.map +1 -0
  100. package/dist/types/plugins/ChannelDetail/Views/ChannelFilesView/useChannelFilesSearch.d.ts +10 -0
  101. package/dist/types/plugins/ChannelDetail/Views/ChannelFilesView/useChannelFilesSearch.d.ts.map +1 -0
  102. package/dist/types/plugins/ChannelDetail/Views/ChannelManagementView/ChannelManagementActions.defaults.d.ts +16 -0
  103. package/dist/types/plugins/ChannelDetail/Views/ChannelManagementView/ChannelManagementActions.defaults.d.ts.map +1 -0
  104. package/dist/types/plugins/ChannelDetail/Views/ChannelManagementView/ChannelManagementView.d.ts +20 -0
  105. package/dist/types/plugins/ChannelDetail/Views/ChannelManagementView/ChannelManagementView.d.ts.map +1 -0
  106. package/dist/types/plugins/ChannelDetail/Views/ChannelManagementView/index.d.ts +3 -0
  107. package/dist/types/plugins/ChannelDetail/Views/ChannelManagementView/index.d.ts.map +1 -0
  108. package/dist/types/plugins/ChannelDetail/Views/ChannelMediaView/ChannelMediaEmptyList.d.ts +2 -0
  109. package/dist/types/plugins/ChannelDetail/Views/ChannelMediaView/ChannelMediaEmptyList.d.ts.map +1 -0
  110. package/dist/types/plugins/ChannelDetail/Views/ChannelMediaView/ChannelMediaView.d.ts +8 -0
  111. package/dist/types/plugins/ChannelDetail/Views/ChannelMediaView/ChannelMediaView.d.ts.map +1 -0
  112. package/dist/types/plugins/ChannelDetail/Views/ChannelMediaView/ChannelMediaView.utils.d.ts +22 -0
  113. package/dist/types/plugins/ChannelDetail/Views/ChannelMediaView/ChannelMediaView.utils.d.ts.map +1 -0
  114. package/dist/types/plugins/ChannelDetail/Views/ChannelMediaView/index.d.ts +5 -0
  115. package/dist/types/plugins/ChannelDetail/Views/ChannelMediaView/index.d.ts.map +1 -0
  116. package/dist/types/plugins/ChannelDetail/Views/ChannelMediaView/useChannelMediaSearch.d.ts +9 -0
  117. package/dist/types/plugins/ChannelDetail/Views/ChannelMediaView/useChannelMediaSearch.d.ts.map +1 -0
  118. package/dist/types/plugins/ChannelDetail/Views/ChannelMemberDetailView/ChannelMemberActions.defaults.d.ts +26 -0
  119. package/dist/types/plugins/ChannelDetail/Views/ChannelMemberDetailView/ChannelMemberActions.defaults.d.ts.map +1 -0
  120. package/dist/types/plugins/ChannelDetail/Views/ChannelMemberDetailView/ChannelMemberDetail.d.ts +12 -0
  121. package/dist/types/plugins/ChannelDetail/Views/ChannelMemberDetailView/ChannelMemberDetail.d.ts.map +1 -0
  122. package/dist/types/plugins/ChannelDetail/Views/ChannelMemberDetailView/index.d.ts +3 -0
  123. package/dist/types/plugins/ChannelDetail/Views/ChannelMemberDetailView/index.d.ts.map +1 -0
  124. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersAddView.d.ts +7 -0
  125. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersAddView.d.ts.map +1 -0
  126. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersBrowseView.d.ts +6 -0
  127. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersBrowseView.d.ts.map +1 -0
  128. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersHeaderActions.defaults.d.ts +45 -0
  129. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersHeaderActions.defaults.d.ts.map +1 -0
  130. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersView.d.ts +46 -0
  131. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersView.d.ts.map +1 -0
  132. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersView.utils.d.ts +8 -0
  133. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersView.utils.d.ts.map +1 -0
  134. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/index.d.ts +6 -0
  135. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/index.d.ts.map +1 -0
  136. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/useChannelMemberCount.d.ts +4 -0
  137. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/useChannelMemberCount.d.ts.map +1 -0
  138. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/useChannelMemberIds.d.ts +4 -0
  139. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/useChannelMemberIds.d.ts.map +1 -0
  140. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/useChannelMembersSearch.d.ts +12 -0
  141. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/useChannelMembersSearch.d.ts.map +1 -0
  142. package/dist/types/plugins/ChannelDetail/Views/PinnedMessagesView/PinnedMessagesEmptyList.d.ts +2 -0
  143. package/dist/types/plugins/ChannelDetail/Views/PinnedMessagesView/PinnedMessagesEmptyList.d.ts.map +1 -0
  144. package/dist/types/plugins/ChannelDetail/Views/PinnedMessagesView/PinnedMessagesView.d.ts +9 -0
  145. package/dist/types/plugins/ChannelDetail/Views/PinnedMessagesView/PinnedMessagesView.d.ts.map +1 -0
  146. package/dist/types/plugins/ChannelDetail/Views/PinnedMessagesView/index.d.ts +4 -0
  147. package/dist/types/plugins/ChannelDetail/Views/PinnedMessagesView/index.d.ts.map +1 -0
  148. package/dist/types/plugins/ChannelDetail/Views/PinnedMessagesView/usePinnedMessagesCount.d.ts +4 -0
  149. package/dist/types/plugins/ChannelDetail/Views/PinnedMessagesView/usePinnedMessagesCount.d.ts.map +1 -0
  150. package/dist/types/plugins/ChannelDetail/Views/PinnedMessagesView/usePinnedMessagesSearch.d.ts +16 -0
  151. package/dist/types/plugins/ChannelDetail/Views/PinnedMessagesView/usePinnedMessagesSearch.d.ts.map +1 -0
  152. package/dist/types/plugins/ChannelDetail/VirtualizedList/VirtualizedList.d.ts +28 -0
  153. package/dist/types/plugins/ChannelDetail/VirtualizedList/VirtualizedList.d.ts.map +1 -0
  154. package/dist/types/plugins/ChannelDetail/VirtualizedList/index.d.ts +2 -0
  155. package/dist/types/plugins/ChannelDetail/VirtualizedList/index.d.ts.map +1 -0
  156. package/dist/types/plugins/ChannelDetail/index.d.ts +16 -0
  157. package/dist/types/plugins/ChannelDetail/index.d.ts.map +1 -0
  158. package/dist/types/utils/index.d.ts +2 -0
  159. package/dist/types/utils/index.d.ts.map +1 -1
  160. package/dist/types/utils/isDmChannel.d.ts +6 -0
  161. package/dist/types/utils/isDmChannel.d.ts.map +1 -0
  162. package/package.json +11 -2
  163. package/dist/cjs/useNotificationApi.e9312774.js.map +0 -1
  164. package/dist/es/useNotificationApi.4be515a0.mjs.map +0 -1
@@ -0,0 +1,3600 @@
1
+ import { Ar as useDialogIsOpen, Er as DialogPortalEntry, Fr as useModalDialogIsTopmost, Ft as IconChevronLeft, It as IconChevronRight, Jn as IconXmark, Jt as IconExclamationTriangleFill, Kt as IconExclamationMark, Lr as useStateStore, Mr as useDialogOnNearestManager, Nr as useModalDialog, Pr as useModalDialogIsOpen, Qn as useStableId, R as usePopoverPosition, Tn as IconRefresh, Vr as useComponentContext, Vt as IconDownload, Wr as useChatContext, Z as isMessageDeleted, Zn as Button, bn as IconPlayFill, bt as IconArrowLeft, en as IconImage, in as IconLoading, jt as IconCheckmark, kr as useDialog, mr as isDate, n as hasSystemNotificationTag, pr as getDateString, qr as useChannelStateContext, qt as IconExclamationMarkFill, r as useNotificationApi, rr as isNotificationForPanel, ur as useTranslationContext, xr as modalDialogManagerId, yr as DialogManagerProvider, yt as IconArrowDownCircle, zn as IconUser } from "./useNotificationApi.fa5cddf9.mjs";
2
+ import React, { createContext, createElement, forwardRef, isValidElement, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
3
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
4
+ import { isGiphyAttachment, isImageAttachment, isLocalImageAttachment, isLocalVideoAttachment, isScrapedContent, isVideoAttachment } from "stream-chat";
5
+ import clsx from "clsx";
6
+ import { useSyncExternalStore } from "use-sync-external-store/shim";
7
+ import { FocusScope } from "@react-aria/focus";
8
+ import { SKIP, visit } from "unist-util-visit";
9
+ import ReactMarkdown from "react-markdown";
10
+ import remarkGfm from "remark-gfm";
11
+ import { sanitizeUrl } from "@braintree/sanitize-url";
12
+ //#region src/context/MessageContext.tsx
13
+ var MessageContext = React.createContext(void 0);
14
+ var MessageProvider = ({ children, value }) => /* @__PURE__ */ jsx(MessageContext.Provider, {
15
+ value,
16
+ children
17
+ });
18
+ var useMessageContext = (_componentName) => {
19
+ const contextValue = useContext(MessageContext);
20
+ if (!contextValue) return {};
21
+ return contextValue;
22
+ };
23
+ //#endregion
24
+ //#region src/context/MessageTranslationViewContext.tsx
25
+ /**
26
+ * Message translation view context: user-specific state for whether each message
27
+ * shows original text or a translation.
28
+ *
29
+ * ## Spec
30
+ *
31
+ * - **State**: Per message list (channel vs thread), we store a map
32
+ * `messageId → 'original' | 'translated'`. Default for messages with `message.i18n`
33
+ * is `'translated'`; otherwise `'original'`.
34
+ *
35
+ * - **Provider placement**: The provider is tied to the **message list**, not the channel.
36
+ * It is rendered inside `MessageList` and `VirtualizedMessageList`. That way the
37
+ * main channel list and the thread list each have their own translation view state
38
+ * (e.g. Thread.tsx gets correct behavior without sharing channel state).
39
+ *
40
+ * - **Multiple translations**: `message.i18n` can contain multiple languages, e.g.:
41
+ * `{ en_text: "Good morning", fr_text: "Bonjour", it_text: "Buongiorno", language: "en" }`.
42
+ * Which translation is shown is determined by the app’s **user language**
43
+ * (`useTranslationContext().userLanguage`). We use `message.i18n[userLanguage + '_text']`;
44
+ * if missing, we fall back to `message.text`. Only one translation is shown at a time.
45
+ *
46
+ * - **Source language**: When present, `message.i18n.language` is the original/source
47
+ * language of `message.text`. It can be used for the indicator label, e.g.
48
+ * "Translated from English · View original".
49
+ *
50
+ * - **Invariants**: The original message content, layout, grouping, and order stay
51
+ * unchanged. Removing or toggling translation only changes the annotation and which
52
+ * text is displayed. "View original" / "View translation" switch the displayed
53
+ * text and update the annotation (e.g. "Original · View translation" when showing
54
+ * original).
55
+ *
56
+ * - **Translation indicator visibility**: The translation indicator (e.g. "Translated ·
57
+ * View original") is **not** shown when the currently viewed text already corresponds
58
+ * to `userLanguage` — for example when viewing original and the original text is the
59
+ * user-language version (i.e. `getTranslatedMessageText({ language: userLanguage, message })`
60
+ * equals `message.text`). In that case there is no meaningful original/translated choice,
61
+ * so the indicator is hidden.
62
+ */
63
+ /**
64
+ * Returns the translated message text for a given language from `message.i18n`, or
65
+ * undefined if not present. Used to resolve which of the multiple translations to show.
66
+ */
67
+ var getTranslatedMessageText = ({ language, message }) => message?.i18n?.[`${language}_text`];
68
+ var defaultContextValue = {
69
+ getTranslationView: (_messageId, hasI18n) => hasI18n ? "translated" : "original",
70
+ setTranslationView: () => {}
71
+ };
72
+ var MessageTranslationViewContext = createContext(defaultContextValue);
73
+ var MessageTranslationViewProvider = ({ children }) => {
74
+ const [viewByMessageId, setViewByMessageId] = useState({});
75
+ const setTranslationView = useCallback((messageId, view) => {
76
+ setViewByMessageId((prev) => ({
77
+ ...prev,
78
+ [messageId]: view
79
+ }));
80
+ }, []);
81
+ const getTranslationView = useCallback((messageId, hasI18n) => viewByMessageId[messageId] ?? (hasI18n ? "translated" : "original"), [viewByMessageId]);
82
+ const stableValue = React.useMemo(() => ({
83
+ getTranslationView,
84
+ setTranslationView
85
+ }), [getTranslationView, setTranslationView]);
86
+ return /* @__PURE__ */ jsx(MessageTranslationViewContext.Provider, {
87
+ value: stableValue,
88
+ children
89
+ });
90
+ };
91
+ var useMessageTranslationViewContext = () => {
92
+ return useContext(MessageTranslationViewContext) ?? defaultContextValue;
93
+ };
94
+ //#endregion
95
+ //#region src/context/ModalContext.tsx
96
+ var ModalContext = React.createContext(void 0);
97
+ var ModalContextProvider = ({ children, value }) => /* @__PURE__ */ jsx(ModalContext.Provider, {
98
+ value,
99
+ children
100
+ });
101
+ var useModalContext = () => {
102
+ const contextValue = useContext(ModalContext);
103
+ if (!contextValue) {
104
+ console.warn(`The useModalContext hook was called outside of the ModalContext provider. Make sure this hook is called within a child of the GlobalModal.`);
105
+ return { close: () => null };
106
+ }
107
+ return contextValue;
108
+ };
109
+ //#endregion
110
+ //#region src/components/Notifications/NotificationConfigurationContext.tsx
111
+ var defaultNotificationDisplayFilter = () => true;
112
+ var defaultNotificationConfigurationContextValue = { displayFilter: defaultNotificationDisplayFilter };
113
+ var NotificationConfigurationContext = React.createContext(defaultNotificationConfigurationContextValue);
114
+ var NotificationConfigurationProvider = ({ children, displayFilter }) => {
115
+ const parentConfiguration = useContext(NotificationConfigurationContext);
116
+ const value = useMemo(() => ({ displayFilter: displayFilter ?? parentConfiguration.displayFilter }), [displayFilter, parentConfiguration.displayFilter]);
117
+ return /* @__PURE__ */ jsx(NotificationConfigurationContext.Provider, {
118
+ value,
119
+ children
120
+ });
121
+ };
122
+ var useNotificationConfigurationContext = () => useContext(NotificationConfigurationContext);
123
+ //#endregion
124
+ //#region src/components/Notifications/hooks/useNotifications.ts
125
+ /**
126
+ * Subscribes to client.notifications.store and returns the list of notifications.
127
+ * Optionally pass a filter so only notifications that match are returned (e.g. for a specific NotificationList).
128
+ */
129
+ var useNotifications = (options) => {
130
+ const { client } = useChatContext();
131
+ const { displayFilter } = useNotificationConfigurationContext();
132
+ const { applyDisplayFilter, fallbackPanel, filter, panel } = options ?? {};
133
+ const selector = useCallback((state) => {
134
+ return { notifications: state.notifications.filter((notification) => {
135
+ if (panel && !isNotificationForPanel(notification, panel, { fallbackPanel })) return false;
136
+ if (applyDisplayFilter && !displayFilter({
137
+ fallbackPanel,
138
+ filter,
139
+ notification,
140
+ panel
141
+ })) return false;
142
+ return filter ? filter(notification) : true;
143
+ }) };
144
+ }, [
145
+ applyDisplayFilter,
146
+ displayFilter,
147
+ fallbackPanel,
148
+ filter,
149
+ panel
150
+ ]);
151
+ const { notifications } = useStateStore(client.notifications.store, selector);
152
+ return notifications;
153
+ };
154
+ //#endregion
155
+ //#region src/components/Loading/LoadingIndicator.tsx
156
+ var LoadingIndicator = (props) => /* @__PURE__ */ jsx(IconLoading, {
157
+ ...props,
158
+ className: "str-chat__loading-indicator"
159
+ });
160
+ //#endregion
161
+ //#region src/components/MessageComposer/hooks/utils.ts
162
+ function prettifyFileSize(bytes, precision = 3) {
163
+ const units = [
164
+ "B",
165
+ "kB",
166
+ "MB",
167
+ "GB"
168
+ ];
169
+ const exponent = bytes === 0 ? 0 : Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
170
+ const mantissa = bytes / 1024 ** exponent;
171
+ return `${precision === 0 ? Math.round(mantissa).toString() : mantissa.toPrecision(precision)} ${units[exponent]}`;
172
+ }
173
+ //#endregion
174
+ //#region src/components/Attachment/components/FileSizeIndicator.tsx
175
+ var FileSizeIndicator = ({ fileSize, maximumFractionDigits }) => {
176
+ const actualFileSize = typeof fileSize === "string" ? parseFloat(fileSize) : fileSize;
177
+ if (typeof actualFileSize === "undefined" || !Number.isFinite(Number(actualFileSize))) return null;
178
+ return /* @__PURE__ */ jsx("span", {
179
+ className: "str-chat__message-attachment-file--item-size",
180
+ "data-testid": "file-size-indicator",
181
+ children: prettifyFileSize(actualFileSize, maximumFractionDigits)
182
+ });
183
+ };
184
+ //#endregion
185
+ //#region src/utils/useStableCallback.ts
186
+ /**
187
+ * A utility hook implementing a stable callback. It takes in an unstable method that
188
+ * is supposed to be invoked somewhere deeper in the DOM tree without making it
189
+ * change its reference every time the parent component rerenders. It will also return
190
+ * the value of the callback if it does return one.
191
+ * A common use-case would be having a function whose invocation depends on state
192
+ * somewhere high up in the DOM tree and wanting to use the same function deeper
193
+ * down, for example in a leaf node and simply using useCallback results in
194
+ * cascading dependency hell. If we wrap it in useStableCallback, we would be able
195
+ * to:
196
+ * - Use the same function as a dependency of another hook (since it is stable)
197
+ * - Still invoke it and get the latest state
198
+ *
199
+ * **Caveats:**
200
+ * - Never wrap a function that is supposed to return a React.ReactElement in
201
+ * useStableCallback, since React will not know that the DOM needs to be updated
202
+ * whenever the callback value changes (for example, renderItem from FlatList must
203
+ * never be wrapped in this hook)
204
+ * - Always prefer using a standard useCallback/stable function wherever possible
205
+ * (the purpose of useStableCallback is to bridge the gap between top level contexts
206
+ * and cascading rereders in downstream components - **not** as an escape hatch)
207
+ * @param callback - the callback we want to stabilize
208
+ */
209
+ var useStableCallback = (callback) => {
210
+ const ref = useRef(callback);
211
+ ref.current = callback;
212
+ return useCallback((...args) => ref.current(...args), []);
213
+ };
214
+ //#endregion
215
+ //#region src/a11y/hooks/useAriaIdentifiers.ts
216
+ var sanitizeAriaRootId = (rootId) => rootId?.trim().replace(/[^A-Za-z0-9:_-]/g, "-") ?? "";
217
+ var buildAriaIdentifier = (sanitizedRootId, descriptor) => sanitizedRootId ? `${sanitizedRootId}-${descriptor}` : void 0;
218
+ /**
219
+ * Derives stable ARIA identifier IDs from a single root ID.
220
+ *
221
+ * Use this to keep dialog/component labeling conventions consistent without
222
+ * manually building `*-title` and `*-description` IDs at each call site.
223
+ *
224
+ * Behavior:
225
+ * - Root ID is trimmed and sanitized to `[A-Za-z0-9:_-]` before use.
226
+ * - Returns `undefined` IDs when root ID is missing/empty after sanitization.
227
+ */
228
+ var useAriaIdentifiers = (rootId) => {
229
+ const sanitizedRootId = sanitizeAriaRootId(rootId);
230
+ return useMemo(() => ({
231
+ descriptionId: buildAriaIdentifier(sanitizedRootId, "description"),
232
+ titleId: buildAriaIdentifier(sanitizedRootId, "title")
233
+ }), [sanitizedRootId]);
234
+ };
235
+ //#endregion
236
+ //#region src/components/Dialog/components/Alert.tsx
237
+ var Root = forwardRef(function AlertRoot({ children, className, ...props }, ref) {
238
+ return /* @__PURE__ */ jsx("div", {
239
+ ...props,
240
+ className: clsx("str-chat__alert-root", className),
241
+ ref,
242
+ children
243
+ });
244
+ });
245
+ var Header = forwardRef(function AlertHeader({ children, className, description, descriptionId, Icon, title, titleId, ...props }, ref) {
246
+ const { dialogId } = useModalContext();
247
+ const { descriptionId: derivedDescriptionId, titleId: derivedTitleId } = useAriaIdentifiers(dialogId);
248
+ const resolvedTitleId = titleId ?? derivedTitleId;
249
+ const resolvedDescriptionId = descriptionId ?? derivedDescriptionId;
250
+ return /* @__PURE__ */ jsx("div", {
251
+ ...props,
252
+ className: clsx("str-chat__alert-header", className),
253
+ ref,
254
+ children: title ? /* @__PURE__ */ jsxs(Fragment$1, { children: [Icon && /* @__PURE__ */ jsx(Icon, {}), /* @__PURE__ */ jsxs("div", {
255
+ className: "str-chat__alert-header__copy",
256
+ children: [/* @__PURE__ */ jsx("h2", {
257
+ className: "str-chat__alert-header__title",
258
+ id: resolvedTitleId,
259
+ children: title
260
+ }), description && /* @__PURE__ */ jsx("p", {
261
+ className: "str-chat__alert-header__description",
262
+ id: resolvedDescriptionId,
263
+ children: description
264
+ })]
265
+ })] }) : children
266
+ });
267
+ });
268
+ var Alert = {
269
+ Actions: forwardRef(function AlertActions({ children, className, ...props }, ref) {
270
+ return /* @__PURE__ */ jsx("div", {
271
+ ...props,
272
+ className: clsx("str-chat__alert-actions", className),
273
+ ref,
274
+ children
275
+ });
276
+ }),
277
+ Header,
278
+ Root
279
+ };
280
+ //#endregion
281
+ //#region src/components/Dialog/service/DialogAnchor.tsx
282
+ function useDialogAnchor({ allowFlip, offset, open, placement, referenceElement, updateKey, updatePositionOnContentResize = false }) {
283
+ const [popperElement, setPopperElement] = useState(null);
284
+ const [stabilisedChosenPlacement, setStabilisedChosenPlacement] = useState(null);
285
+ const { placement: chosenPlacement, refs, strategy, update, x, y } = usePopoverPosition({
286
+ allowFlip,
287
+ freeze: true,
288
+ offset,
289
+ placement: stabilisedChosenPlacement ?? placement
290
+ });
291
+ if (!stabilisedChosenPlacement && popperElement && placement !== chosenPlacement) setStabilisedChosenPlacement(chosenPlacement);
292
+ else if (stabilisedChosenPlacement && !popperElement) setStabilisedChosenPlacement(null);
293
+ const frozenReferenceRef = useRef(null);
294
+ if (open && referenceElement && !frozenReferenceRef.current) frozenReferenceRef.current = referenceElement;
295
+ if (!open) frozenReferenceRef.current = null;
296
+ const effectiveReference = open ? frozenReferenceRef.current : referenceElement;
297
+ useEffect(() => {
298
+ refs.setReference(effectiveReference);
299
+ }, [effectiveReference, refs]);
300
+ useEffect(() => {
301
+ refs.setFloating(popperElement);
302
+ }, [popperElement, refs]);
303
+ useEffect(() => {
304
+ if (open && popperElement && effectiveReference) update?.();
305
+ }, [
306
+ open,
307
+ placement,
308
+ popperElement,
309
+ update,
310
+ updateKey,
311
+ effectiveReference
312
+ ]);
313
+ useEffect(() => {
314
+ if (!popperElement || !updatePositionOnContentResize) return;
315
+ const resizeObserver = new ResizeObserver(update);
316
+ resizeObserver.observe(popperElement);
317
+ return () => {
318
+ resizeObserver.disconnect();
319
+ };
320
+ }, [
321
+ popperElement,
322
+ update,
323
+ updatePositionOnContentResize
324
+ ]);
325
+ if (popperElement && !open) setPopperElement(null);
326
+ return {
327
+ placement: stabilisedChosenPlacement ?? chosenPlacement,
328
+ setPopperElement,
329
+ styles: {
330
+ left: x ?? 0,
331
+ position: strategy,
332
+ top: y ?? 0
333
+ }
334
+ };
335
+ }
336
+ var DialogAnchor = ({ allowFlip = true, children, className, closeOnClickOutside, closeTransitionMs = 0, dialogManagerId, focus = true, id, offset, placement = "auto", referenceElement = null, tabIndex, trapFocus, updateKey, updatePositionOnContentResize, ...restDivProps }) => {
337
+ /**
338
+ * Rendering lifecycle notes:
339
+ * - `open=true` renders dialog contents immediately.
340
+ * - `open=false` can keep contents mounted for `closeTransitionMs` to allow CSS exit
341
+ * animations before unmount.
342
+ *
343
+ * State exposed to CSS:
344
+ * - `data-str-chat-dialog-state="open"` while actively open.
345
+ * - `data-str-chat-dialog-state="closing"` during delayed unmount window.
346
+ *
347
+ * Consumers like `ContextMenu` combine:
348
+ * - `data-str-chat-dialog-state` from this component, and
349
+ * - `data-str-chat-placement` (e.g. `top-start`, `right-end`)
350
+ * to select the correct closing keyframe in CSS (horizontal vs vertical roll direction).
351
+ * In practice, JS only toggles state and timing (`closeTransitionMs`); CSS owns the
352
+ * visual motion details (transform/easing), so animation behavior stays declarative.
353
+ */
354
+ const dialog = useDialog({
355
+ closeOnClickOutside,
356
+ dialogManagerId,
357
+ id
358
+ });
359
+ const open = useDialogIsOpen(id, dialogManagerId);
360
+ const [shouldRender, setShouldRender] = useState(open);
361
+ const closeTimeoutRef = useRef(null);
362
+ const isClosing = !open && shouldRender;
363
+ useEffect(() => {
364
+ if (open) {
365
+ setShouldRender(true);
366
+ if (closeTimeoutRef.current) {
367
+ clearTimeout(closeTimeoutRef.current);
368
+ closeTimeoutRef.current = null;
369
+ }
370
+ return;
371
+ }
372
+ if (!shouldRender) return;
373
+ if (!closeTransitionMs) {
374
+ setShouldRender(false);
375
+ return;
376
+ }
377
+ closeTimeoutRef.current = setTimeout(() => {
378
+ setShouldRender(false);
379
+ closeTimeoutRef.current = null;
380
+ }, closeTransitionMs);
381
+ }, [
382
+ closeTransitionMs,
383
+ open,
384
+ shouldRender
385
+ ]);
386
+ useEffect(() => () => {
387
+ if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current);
388
+ }, []);
389
+ const { placement: chosenPlacement, setPopperElement, styles } = useDialogAnchor({
390
+ allowFlip,
391
+ offset,
392
+ open: shouldRender,
393
+ placement,
394
+ referenceElement,
395
+ updateKey,
396
+ updatePositionOnContentResize
397
+ });
398
+ useEffect(() => {
399
+ if (!open) return;
400
+ const hideOnEscape = (event) => {
401
+ if (event.key !== "Escape" || event.defaultPrevented) return;
402
+ dialog?.close();
403
+ };
404
+ document.addEventListener("keyup", hideOnEscape);
405
+ return () => {
406
+ document.removeEventListener("keyup", hideOnEscape);
407
+ };
408
+ }, [dialog, open]);
409
+ if (!shouldRender) return null;
410
+ const { ["aria-describedby"]: ariaDescribedBy, ["aria-label"]: ariaLabel, ["aria-labelledby"]: ariaLabelledBy, ["aria-modal"]: ariaModal, role, ...anchorDivProps } = restDivProps;
411
+ const resolvedRole = trapFocus ? role ?? "dialog" : role;
412
+ const resolvedAriaModal = trapFocus ? true : ariaModal;
413
+ const resolvedAriaLabel = ariaLabelledBy ? void 0 : ariaLabel;
414
+ return /* @__PURE__ */ jsx(DialogPortalEntry, {
415
+ dialogId: id,
416
+ dialogManagerId,
417
+ children: /* @__PURE__ */ jsx(FocusScope, {
418
+ autoFocus: focus,
419
+ contain: trapFocus,
420
+ restoreFocus: true,
421
+ children: /* @__PURE__ */ jsx("div", {
422
+ ...anchorDivProps,
423
+ "aria-describedby": ariaDescribedBy,
424
+ "aria-label": resolvedAriaLabel,
425
+ "aria-labelledby": ariaLabelledBy,
426
+ "aria-modal": resolvedAriaModal,
427
+ className: clsx("str-chat__dialog-contents", className),
428
+ "data-str-chat-dialog-state": isClosing ? "closing" : "open",
429
+ "data-str-chat-placement": chosenPlacement,
430
+ "data-testid": "str-chat__dialog-contents",
431
+ ref: setPopperElement,
432
+ role: resolvedRole,
433
+ style: styles,
434
+ tabIndex: typeof tabIndex !== "undefined" ? tabIndex : 0,
435
+ children
436
+ })
437
+ })
438
+ });
439
+ };
440
+ //#endregion
441
+ //#region src/components/Avatar/Avatar.tsx
442
+ var getInitials = (name) => {
443
+ const regex = /(\p{L}{1})\p{L}+/gu;
444
+ if (!name || name.trim().length === 0) return "";
445
+ const initials = Array.from(name?.matchAll(regex) || []);
446
+ if (!initials.length) return "";
447
+ return `${initials.at(0)[1]}${initials.length > 1 ? initials.at(-1)[1] : ""}`;
448
+ };
449
+ /**
450
+ * A round avatar image with fallback to username's first letter
451
+ */
452
+ var Avatar = ({ className, FallbackIcon = IconUser, imageUrl, isOnline, size, userName, ...rest }) => {
453
+ const [error, setError] = useState(false);
454
+ useEffect(() => () => setError(false), [imageUrl]);
455
+ const nameString = userName?.toString() || "";
456
+ const avatarImageAlt = nameString.trim();
457
+ const sizeAwareInitials = useMemo(() => {
458
+ const initials = getInitials(nameString);
459
+ if (size === "sm" || size === "xs") return initials.charAt(0);
460
+ return initials;
461
+ }, [nameString, size]);
462
+ const showImage = typeof imageUrl === "string" && imageUrl && !error;
463
+ return /* @__PURE__ */ jsxs("div", {
464
+ className: clsx(`str-chat__avatar`, className, {
465
+ "str-chat__avatar--multiple-letters": sizeAwareInitials.length > 1,
466
+ "str-chat__avatar--no-letters": !sizeAwareInitials.length,
467
+ "str-chat__avatar--one-letter": sizeAwareInitials.length === 1,
468
+ [`str-chat__avatar--size-${size}`]: typeof size === "string"
469
+ }),
470
+ "data-testid": "avatar",
471
+ role: "button",
472
+ title: userName,
473
+ ...rest,
474
+ children: [typeof isOnline === "boolean" && /* @__PURE__ */ jsx("div", { className: clsx("str-chat__avatar-status-badge", {
475
+ "str-chat__avatar-status-badge--offline": !isOnline,
476
+ "str-chat__avatar-status-badge--online": isOnline
477
+ }) }), showImage ? /* @__PURE__ */ jsx("img", {
478
+ alt: avatarImageAlt,
479
+ className: "str-chat__avatar-image",
480
+ "data-testid": "avatar-img",
481
+ onError: () => setError(true),
482
+ src: imageUrl
483
+ }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [!!sizeAwareInitials.length && /* @__PURE__ */ jsx("div", {
484
+ className: "str-chat__avatar-initials",
485
+ "data-testid": "avatar-fallback",
486
+ children: sizeAwareInitials
487
+ }), !sizeAwareInitials.length && /* @__PURE__ */ jsx(FallbackIcon, {})] })]
488
+ });
489
+ };
490
+ //#endregion
491
+ //#region src/components/Badge/Badge.tsx
492
+ /**
493
+ * Compact pill/circle badge for counts and labels.
494
+ * Uses design tokens: --badge-bg-*, --badge-text-*, --badge-border.
495
+ */
496
+ var Badge = ({ children, className, size = "md", variant = "default", ...spanProps }) => /* @__PURE__ */ jsx("div", {
497
+ ...spanProps,
498
+ className: clsx("str-chat__badge", `str-chat__badge--variant-${variant}`, { [`str-chat__badge--size-${size}`]: size }, className),
499
+ children
500
+ });
501
+ var ErrorBadge = ({ className, size = "sm", ...rest }) => /* @__PURE__ */ jsx(Badge, {
502
+ ...rest,
503
+ className,
504
+ size,
505
+ variant: "error",
506
+ children: /* @__PURE__ */ jsx(IconExclamationMarkFill, {})
507
+ });
508
+ //#endregion
509
+ //#region src/components/Avatar/ChannelAvatar.tsx
510
+ var ChannelAvatar = ({ displayMembers, imageUrl, size, userName, ...sharedProps }) => {
511
+ return /* @__PURE__ */ jsx(GroupAvatar, {
512
+ displayMembers: useMemo(() => {
513
+ if (!imageUrl && displayMembers && displayMembers.length > 0) return displayMembers;
514
+ return [{
515
+ imageUrl,
516
+ userName
517
+ }];
518
+ }, [
519
+ displayMembers,
520
+ imageUrl,
521
+ userName
522
+ ]),
523
+ size,
524
+ ...sharedProps
525
+ });
526
+ };
527
+ //#endregion
528
+ //#region src/components/Avatar/GroupAvatar.tsx
529
+ /**
530
+ * Avatar component to display multiple users' avatars in a group.
531
+ * Renders a single Avatar if fewer than 2 members. Otherwise, renders up to 2 avatars (when overflowCount is set) or 4, plus an optional +N badge.
532
+ */
533
+ var GroupAvatar = ({ badgeSize, className, displayMembers = [], isOnline, size, ...rest }) => {
534
+ const displayMembersToRender = useMemo(() => displayMembers.length > 4 ? displayMembers.slice(0, 2) : displayMembers, [displayMembers]);
535
+ const overflowCount = displayMembers.length - displayMembersToRender.length;
536
+ if (displayMembers.length < 2) {
537
+ const firstUser = displayMembers[0];
538
+ return /* @__PURE__ */ jsx(Avatar, {
539
+ imageUrl: firstUser?.imageUrl,
540
+ isOnline,
541
+ size,
542
+ userName: firstUser?.userName,
543
+ ...rest
544
+ });
545
+ }
546
+ let avatarSize = null;
547
+ if (size === "2xl") avatarSize = "lg";
548
+ else if (size === "xl") avatarSize = "md";
549
+ else if (size === "lg") avatarSize = "sm";
550
+ return /* @__PURE__ */ jsxs("div", {
551
+ className: clsx("str-chat__avatar-group", {
552
+ "str-chat__avatar-group--offline": typeof isOnline === "boolean" && !isOnline,
553
+ "str-chat__avatar-group--online": typeof isOnline === "boolean" && isOnline,
554
+ [`str-chat__avatar-group--size-${size}`]: typeof size === "string"
555
+ }, className),
556
+ "data-testid": "group-avatar",
557
+ role: "button",
558
+ ...rest,
559
+ children: [displayMembersToRender.map(({ id, imageUrl, userName }, index) => /* @__PURE__ */ jsx(Avatar, {
560
+ imageUrl,
561
+ size: avatarSize,
562
+ userName
563
+ }, id || `${userName}-${imageUrl}-${index}`)), typeof overflowCount === "number" && overflowCount > 0 && /* @__PURE__ */ jsxs(Badge, {
564
+ className: "str-chat__avatar-group__count-badge",
565
+ "data-testid": "group-avatar-count-badge",
566
+ size: badgeSize,
567
+ variant: "counter",
568
+ children: ["+", overflowCount]
569
+ })]
570
+ });
571
+ };
572
+ //#endregion
573
+ //#region src/a11y/a11yUtils.ts
574
+ var MENU_KEYBOARD_NAVIGATION_KEYS = [
575
+ "ArrowDown",
576
+ "ArrowUp",
577
+ "End",
578
+ "Home"
579
+ ];
580
+ var isMenuKeyboardNavigationKey = (key) => MENU_KEYBOARD_NAVIGATION_KEYS.includes(key);
581
+ var getNextRovingFocusIndex = ({ activeIndex, itemCount, key }) => {
582
+ if (itemCount <= 0 || !isMenuKeyboardNavigationKey(key)) return null;
583
+ const lastIndex = itemCount - 1;
584
+ if (key === "Home") return 0;
585
+ if (key === "End") return lastIndex;
586
+ if (activeIndex === -1) return key === "ArrowUp" ? lastIndex : 0;
587
+ if (key === "ArrowUp") return activeIndex <= 0 ? lastIndex : activeIndex - 1;
588
+ return activeIndex >= lastIndex ? 0 : activeIndex + 1;
589
+ };
590
+ var getDefaultActiveIndex = (items) => {
591
+ const activeElement = document.activeElement;
592
+ if (!(activeElement instanceof Element)) return -1;
593
+ return items.findIndex((item) => item instanceof Element && item === activeElement);
594
+ };
595
+ var createRovingFocusKeyDownHandler = ({ focusItem = (item) => item.focus(), getActiveIndex = (items) => getDefaultActiveIndex(items), getItems }) => {
596
+ const handleKeyDown = (event) => {
597
+ const items = getItems(event);
598
+ const nextIndex = getNextRovingFocusIndex({
599
+ activeIndex: getActiveIndex(items, event),
600
+ itemCount: items.length,
601
+ key: event.key
602
+ });
603
+ if (nextIndex === null) return;
604
+ event.preventDefault();
605
+ const nextItem = items[nextIndex];
606
+ if (!nextItem) return;
607
+ focusItem(nextItem, event);
608
+ };
609
+ return handleKeyDown;
610
+ };
611
+ //#endregion
612
+ //#region src/components/Dialog/components/ContextMenu.tsx
613
+ var BaseContextMenuButton = ({ children, className, details, hasSubMenu, Icon, label, role = "menuitem", SubmenuIcon = IconChevronRight, variant, ...props }) => /* @__PURE__ */ jsxs("button", {
614
+ ...props,
615
+ className: clsx("str-chat__context-menu__button", {
616
+ "str-chat__context-menu__button--with-submenu": hasSubMenu,
617
+ [`str-chat__context-menu__button--${variant}`]: typeof variant === "string"
618
+ }, className),
619
+ role,
620
+ type: "button",
621
+ children: [
622
+ Icon && /* @__PURE__ */ jsx(Icon, { className: "str-chat__context-menu__button__icon" }),
623
+ label ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
624
+ className: "str-chat__context-menu__button__label",
625
+ children: label
626
+ }), /* @__PURE__ */ jsx("div", {
627
+ className: "str-chat__context-menu__button__details",
628
+ children: details
629
+ })] }) : /* @__PURE__ */ jsx("div", {
630
+ className: "str-chat__context-menu__button__label",
631
+ children
632
+ }),
633
+ !!hasSubMenu && /* @__PURE__ */ jsx(SubmenuIcon, { className: "str-chat__context-menu__button__submenu-icon" })
634
+ ]
635
+ });
636
+ var UserContextMenuButton = ({ children, className, imageUrl, role = "menuitem", userName, ...props }) => /* @__PURE__ */ jsxs("button", {
637
+ ...props,
638
+ className: clsx("str-chat__context-menu__button str-chat__user-context-menu__button", className),
639
+ role,
640
+ type: "button",
641
+ children: [/* @__PURE__ */ jsx(Avatar, {
642
+ imageUrl,
643
+ size: "sm",
644
+ userName
645
+ }), /* @__PURE__ */ jsx("div", {
646
+ className: "str-chat__context-menu__button__label",
647
+ children: children ?? userName
648
+ })]
649
+ });
650
+ var EmojiContextMenuButton = ({ children, className, emoji, label, role = "menuitem", ...props }) => /* @__PURE__ */ jsxs("button", {
651
+ ...props,
652
+ className: clsx("str-chat__context-menu__button str-chat__emoji-context-menu__button", className),
653
+ role,
654
+ type: "button",
655
+ children: [/* @__PURE__ */ jsx("span", {
656
+ className: "str-chat__context-menu__button__emoji str-chat__emoji-item--entity",
657
+ children: emoji
658
+ }), /* @__PURE__ */ jsx("div", {
659
+ className: "str-chat__context-menu__button__label",
660
+ children: children ?? label
661
+ })]
662
+ });
663
+ var ContextMenuButtonWithSubmenu = ({ children, className, Submenu, submenuContainerProps, submenuPlacement = "right-start", submenuRollAxis = "x", ...buttonProps }) => {
664
+ const { className: submenuClassName, ...submenuContainerRestProps } = submenuContainerProps ?? {};
665
+ const { registerDialogSubmenu, unregisterDialogSubmenu } = useContextMenuContext();
666
+ const buttonRef = useRef(null);
667
+ const [dialogContainer, setDialogContainer] = useState(null);
668
+ const keepSubmenuOpenFlag = useRef(false);
669
+ const dialogCloseTimeout = useRef(null);
670
+ const dialogId = useMemo(() => `submenu-${Math.random().toString(36).slice(2)}`, []);
671
+ const { dialog, dialogManager } = useDialogOnNearestManager({ id: dialogId });
672
+ const dialogIsOpen = useDialogIsOpen(dialogId, dialogManager?.id);
673
+ useEffect(() => {
674
+ if (!dialogIsOpen) return;
675
+ registerDialogSubmenu();
676
+ return () => unregisterDialogSubmenu();
677
+ }, [
678
+ dialogIsOpen,
679
+ registerDialogSubmenu,
680
+ unregisterDialogSubmenu
681
+ ]);
682
+ const { placement: chosenPlacement, setPopperElement, styles } = useDialogAnchor({
683
+ offset: 8,
684
+ open: dialogIsOpen,
685
+ placement: submenuPlacement,
686
+ referenceElement: buttonRef.current
687
+ });
688
+ const closeDialogLazily = useCallback(() => {
689
+ if (dialogCloseTimeout.current) clearTimeout(dialogCloseTimeout.current);
690
+ dialogCloseTimeout.current = setTimeout(() => {
691
+ if (keepSubmenuOpenFlag.current) return;
692
+ dialog.close();
693
+ }, 100);
694
+ }, [dialog]);
695
+ const keepSubmenuOpen = useCallback(() => {
696
+ keepSubmenuOpenFlag.current = true;
697
+ }, []);
698
+ const allowToCloseSubmenu = useCallback(() => {
699
+ keepSubmenuOpenFlag.current = false;
700
+ }, []);
701
+ const closeSubmenu = useCallback(() => {
702
+ allowToCloseSubmenu();
703
+ closeDialogLazily();
704
+ }, [allowToCloseSubmenu, closeDialogLazily]);
705
+ const handleClose = useCallback((event) => {
706
+ const parentButton = buttonRef.current;
707
+ if (!dialogIsOpen || !parentButton) return;
708
+ event.stopPropagation();
709
+ closeDialogLazily();
710
+ parentButton.focus();
711
+ }, [
712
+ closeDialogLazily,
713
+ dialogIsOpen,
714
+ buttonRef
715
+ ]);
716
+ const handleFocusParentButton = () => {
717
+ if (dialogIsOpen) return;
718
+ dialog.open();
719
+ keepSubmenuOpen();
720
+ };
721
+ useEffect(() => {
722
+ const parentButton = buttonRef.current;
723
+ if (!dialogIsOpen || !parentButton) return;
724
+ const hideOnEscape = (event) => {
725
+ if (event.key !== "Escape") return;
726
+ handleClose(event);
727
+ closeSubmenu();
728
+ };
729
+ document.addEventListener("keyup", hideOnEscape, { capture: true });
730
+ return () => {
731
+ document.removeEventListener("keyup", hideOnEscape, { capture: true });
732
+ };
733
+ }, [
734
+ dialogIsOpen,
735
+ handleClose,
736
+ closeSubmenu
737
+ ]);
738
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(BaseContextMenuButton, {
739
+ "aria-expanded": dialogIsOpen,
740
+ "aria-haspopup": "menu",
741
+ className: clsx(className, "str_chat__button-with-submenu", { "str_chat__button-with-submenu--submenu-open": dialogIsOpen }),
742
+ hasSubMenu: true,
743
+ onBlur: closeSubmenu,
744
+ onClick: (event) => {
745
+ event.stopPropagation();
746
+ dialog.toggle();
747
+ },
748
+ onFocus: handleFocusParentButton,
749
+ onMouseEnter: handleFocusParentButton,
750
+ onMouseLeave: closeSubmenu,
751
+ ...buttonProps,
752
+ ref: buttonRef,
753
+ children
754
+ }), dialogIsOpen && /* @__PURE__ */ jsx("div", {
755
+ className: clsx("str-chat__context-menu__submenu-container", submenuClassName),
756
+ "data-str-chat-placement": chosenPlacement,
757
+ "data-str-chat-roll-axis": submenuRollAxis,
758
+ onBlur: (event) => {
759
+ if (event.relatedTarget instanceof Node && dialogContainer?.contains(event.relatedTarget)) return;
760
+ closeSubmenu();
761
+ },
762
+ onFocus: keepSubmenuOpen,
763
+ onMouseEnter: keepSubmenuOpen,
764
+ onMouseLeave: closeSubmenu,
765
+ ref: (element) => {
766
+ setPopperElement(element);
767
+ setDialogContainer(element);
768
+ },
769
+ style: styles,
770
+ tabIndex: -1,
771
+ ...submenuContainerRestProps,
772
+ children: /* @__PURE__ */ jsx(Submenu, {})
773
+ })] });
774
+ };
775
+ var ContextMenuButton = (props) => {
776
+ const { Submenu, submenuContainerProps, submenuPlacement, submenuRollAxis, ...buttonProps } = props;
777
+ if (Submenu) return /* @__PURE__ */ jsx(ContextMenuButtonWithSubmenu, {
778
+ ...buttonProps,
779
+ Submenu,
780
+ submenuContainerProps,
781
+ submenuPlacement,
782
+ submenuRollAxis
783
+ });
784
+ return /* @__PURE__ */ jsx(BaseContextMenuButton, { ...buttonProps });
785
+ };
786
+ var ContextMenuBackButton = ({ children, className, role = "menuitem", ...props }) => /* @__PURE__ */ jsx("button", {
787
+ ...props,
788
+ className: clsx("str-chat__context-menu__back-button", className),
789
+ role,
790
+ type: "button",
791
+ children
792
+ });
793
+ var ContextMenuHeader = ({ children, className, ...props }) => /* @__PURE__ */ jsx("div", {
794
+ ...props,
795
+ className: clsx("str-chat__context-menu__header", className),
796
+ children
797
+ });
798
+ var ContextMenuBody = ({ children, className, ...props }) => /* @__PURE__ */ jsx("div", {
799
+ ...props,
800
+ className: clsx("str-chat__context-menu__body", className),
801
+ children
802
+ });
803
+ var ContextMenuRoot = React.forwardRef(function ContextMenuRoot({ className, role = "menu", ...props }, ref) {
804
+ return /* @__PURE__ */ jsx("div", {
805
+ ...props,
806
+ className: clsx("str-chat__context-menu", className),
807
+ ref,
808
+ role
809
+ });
810
+ });
811
+ var DEFAULT_CONTEXT_MENU_KEYBOARD_NAVIGATION_ITEM_SELECTOR = [
812
+ "[role=\"menuitem\"]:not(:disabled)",
813
+ "[role=\"menuitemradio\"]:not(:disabled)",
814
+ "[role=\"menuitemcheckbox\"]:not(:disabled)"
815
+ ].join(",");
816
+ var isVisibleContextMenuKeyboardNavigationItem = (item) => {
817
+ if (item.offsetParent !== null || item.offsetHeight > 0) return true;
818
+ if (item.hidden) return false;
819
+ if (item.style.display === "none") return false;
820
+ return true;
821
+ };
822
+ var getVisibleContextMenuKeyboardNavigationItems = (contextMenuRoot, itemSelector = DEFAULT_CONTEXT_MENU_KEYBOARD_NAVIGATION_ITEM_SELECTOR) => Array.from(contextMenuRoot?.querySelectorAll(itemSelector) ?? []).filter(isVisibleContextMenuKeyboardNavigationItem);
823
+ var createContextMenuFocusRestoreRequest = ({ contextMenuRoot, focusReturnTarget }) => {
824
+ const target = focusReturnTarget ?? (document.activeElement instanceof HTMLElement ? document.activeElement : null);
825
+ if (!target) return {
826
+ index: -1,
827
+ target: null
828
+ };
829
+ return {
830
+ index: getVisibleContextMenuKeyboardNavigationItems(contextMenuRoot).findIndex((menuItem) => menuItem === target),
831
+ target
832
+ };
833
+ };
834
+ var resolveContextMenuFocusRestoreTarget = ({ contextMenuRoot, request }) => {
835
+ if (!request) return null;
836
+ if (request.target?.isConnected) return request.target;
837
+ if (request.index >= 0) return getVisibleContextMenuKeyboardNavigationItems(contextMenuRoot)[request.index] ?? null;
838
+ return null;
839
+ };
840
+ var ContextMenuContext = React.createContext(void 0);
841
+ var useContextMenuContext = () => useContext(ContextMenuContext);
842
+ /**
843
+ * Internal/default content renderer for {@link ContextMenu}.
844
+ *
845
+ * Override this through `ComponentContext.ContextMenuContent` when you need to
846
+ * customize submenu/back navigation behavior while keeping the same anchor/focus
847
+ * handling from `ContextMenu`.
848
+ */
849
+ function ContextMenuContent({ anchorReferenceElement, backLabel = "Back", children, className, enableAnimations = true, Header, items, ItemsWrapper, keyboardNavigation, menuClassName, onClose, onMenuLevelChange, transitionDirection, ...props }) {
850
+ const rootLevel = useMemo(() => ({
851
+ Header,
852
+ items,
853
+ ItemsWrapper,
854
+ menuClassName
855
+ }), [
856
+ Header,
857
+ items,
858
+ ItemsWrapper,
859
+ menuClassName
860
+ ]);
861
+ const [menuStack, setMenuStack] = useState(() => [rootLevel]);
862
+ const [menuBodyAnimationKey, setMenuBodyAnimationKey] = useState(0);
863
+ const contextMenuRootRef = useRef(null);
864
+ const focusRestoreRequestRef = useRef(null);
865
+ const activeMenu = menuStack[menuStack.length - 1];
866
+ const ActiveMenuItemsWrapper = activeMenu.ItemsWrapper ?? React.Fragment;
867
+ const closeMenu = useCallback(() => {
868
+ onClose?.();
869
+ }, [onClose]);
870
+ const openSubmenu = useCallback(({ focusReturnTarget, Header, ItemsWrapper: SubmenuItemsWrapper, menuClassName, Submenu }) => {
871
+ const nextLevel = {
872
+ focusRestoreRequest: createContextMenuFocusRestoreRequest({
873
+ contextMenuRoot: contextMenuRootRef.current,
874
+ focusReturnTarget
875
+ }),
876
+ Header,
877
+ ItemsWrapper: SubmenuItemsWrapper ?? ItemsWrapper,
878
+ menuClassName,
879
+ Submenu
880
+ };
881
+ setMenuStack((current) => [...current, nextLevel]);
882
+ }, [ItemsWrapper]);
883
+ const returnToParentMenu = useCallback(() => {
884
+ setMenuStack((current) => {
885
+ if (current.length <= 1) return current;
886
+ focusRestoreRequestRef.current = current[current.length - 1]?.focusRestoreRequest ?? null;
887
+ return current.slice(0, -1);
888
+ });
889
+ }, []);
890
+ useEffect(() => {
891
+ setMenuStack((current) => {
892
+ if (current.length === 1 && current[0] === rootLevel) return current;
893
+ return [rootLevel];
894
+ });
895
+ }, [rootLevel]);
896
+ useEffect(() => {
897
+ onMenuLevelChange?.(menuStack.length);
898
+ }, [menuStack.length, onMenuLevelChange]);
899
+ useEffect(() => {
900
+ const focusRestoreRequest = focusRestoreRequestRef.current;
901
+ if (!focusRestoreRequest) return;
902
+ focusRestoreRequestRef.current = null;
903
+ requestAnimationFrame(() => {
904
+ resolveContextMenuFocusRestoreTarget({
905
+ contextMenuRoot: contextMenuRootRef.current,
906
+ request: focusRestoreRequest
907
+ })?.focus();
908
+ });
909
+ }, [menuStack.length]);
910
+ useEffect(() => {
911
+ if (!transitionDirection) return;
912
+ setMenuBodyAnimationKey((value) => value + 1);
913
+ }, [transitionDirection, menuStack.length]);
914
+ const dialogSubmenuOpenCountRef = useRef(0);
915
+ const registerDialogSubmenu = useCallback(() => {
916
+ dialogSubmenuOpenCountRef.current += 1;
917
+ }, []);
918
+ const unregisterDialogSubmenu = useCallback(() => {
919
+ dialogSubmenuOpenCountRef.current = Math.max(0, dialogSubmenuOpenCountRef.current - 1);
920
+ }, []);
921
+ const rovingFocusKeyDownHandler = useMemo(() => {
922
+ const itemSelector = keyboardNavigation?.itemSelector ?? DEFAULT_CONTEXT_MENU_KEYBOARD_NAVIGATION_ITEM_SELECTOR;
923
+ return createRovingFocusKeyDownHandler({ getItems: (event) => getVisibleContextMenuKeyboardNavigationItems(event.currentTarget, itemSelector) });
924
+ }, [keyboardNavigation]);
925
+ const escapeConsumedRef = useRef(false);
926
+ const keyboardNavigationHandler = useCallback((event) => {
927
+ if (event.key === "Escape") {
928
+ if (dialogSubmenuOpenCountRef.current > 0) return;
929
+ event.preventDefault();
930
+ event.stopPropagation();
931
+ if (menuStack.length > 1) {
932
+ escapeConsumedRef.current = true;
933
+ returnToParentMenu();
934
+ } else closeMenu();
935
+ return;
936
+ }
937
+ rovingFocusKeyDownHandler(event);
938
+ }, [
939
+ closeMenu,
940
+ menuStack.length,
941
+ returnToParentMenu,
942
+ rovingFocusKeyDownHandler
943
+ ]);
944
+ const suppressEscapeKeyUp = useCallback((event) => {
945
+ if (event.key === "Escape" && escapeConsumedRef.current) {
946
+ escapeConsumedRef.current = false;
947
+ event.stopPropagation();
948
+ }
949
+ }, []);
950
+ return /* @__PURE__ */ jsx(ContextMenuContext.Provider, {
951
+ value: {
952
+ anchorReferenceElement,
953
+ closeMenu,
954
+ openSubmenu,
955
+ registerDialogSubmenu,
956
+ returnToParentMenu,
957
+ unregisterDialogSubmenu
958
+ },
959
+ children: /* @__PURE__ */ jsxs(ContextMenuRoot, {
960
+ className: clsx(className, activeMenu.menuClassName),
961
+ "data-str-chat-enable-animations": enableAnimations,
962
+ onKeyDownCapture: keyboardNavigationHandler,
963
+ onKeyUpCapture: suppressEscapeKeyUp,
964
+ ref: contextMenuRootRef,
965
+ ...props,
966
+ children: [activeMenu.Header ? /* @__PURE__ */ jsx(activeMenu.Header, {}) : menuStack.length > 1 ? /* @__PURE__ */ jsx(ContextMenuHeader, { children: /* @__PURE__ */ jsxs(ContextMenuBackButton, {
967
+ onClick: returnToParentMenu,
968
+ children: [/* @__PURE__ */ jsx(IconChevronLeft, {}), /* @__PURE__ */ jsx("span", { children: backLabel })]
969
+ }) }) : null, /* @__PURE__ */ jsx(ContextMenuBody, {
970
+ className: clsx({
971
+ "str-chat__context-menu__body--submenu-backward": transitionDirection === "backward",
972
+ "str-chat__context-menu__body--submenu-forward": transitionDirection === "forward"
973
+ }),
974
+ children: activeMenu.Submenu ? /* @__PURE__ */ jsx(activeMenu.Submenu, {}) : /* @__PURE__ */ jsx(ActiveMenuItemsWrapper, { children: typeof children !== "undefined" ? children : activeMenu.items?.map((Item, index) => /* @__PURE__ */ jsx(Item, {}, `context-menu-item-${index}`)) })
975
+ }, `context-menu-body-${menuStack.length}-${menuBodyAnimationKey}`)]
976
+ })
977
+ });
978
+ }
979
+ /**
980
+ * Contextual actions menu that can be used in two modes:
981
+ *
982
+ * - Anchored dialog mode: pass `id` + `referenceElement` (submenu-aware positioning).
983
+ * - Inline mode: omit `id` and render a plain contextual list.
984
+ *
985
+ * Customization via `ComponentContext`:
986
+ *
987
+ * - `ContextMenu`: replace the whole menu container/behavior.
988
+ * - `ContextMenuContent`: keep default container behavior but customize menu content
989
+ * rendering (items, submenu transitions, back navigation UI).
990
+ * - To customize outside-click dismissal via `ComponentContext`, provide a custom
991
+ * `ContextMenu` that sets `closeOnClickOutside`:
992
+ *
993
+ * ```tsx
994
+ * const CustomContextMenu = (props: ContextMenuProps) => (
995
+ * <ContextMenu {...props} closeOnClickOutside={false} />
996
+ * );
997
+ *
998
+ * <ComponentProvider value={{ ContextMenu: CustomContextMenu }}>
999
+ * <Chat client={client}>{children}</Chat>
1000
+ * </ComponentProvider>
1001
+ * ```
1002
+ *
1003
+ * Example:
1004
+ * ```tsx
1005
+ * <ComponentProvider
1006
+ * value={{
1007
+ * ContextMenu: MyContextMenu,
1008
+ * ContextMenuContent: MyContextMenuContent,
1009
+ * }}
1010
+ * >
1011
+ * <Chat client={client}>{children}</Chat>
1012
+ * </ComponentProvider>
1013
+ * ```
1014
+ */
1015
+ var ContextMenu = (props) => {
1016
+ const { ContextMenuContent: ContextMenuContentComponent = ContextMenuContent } = useComponentContext();
1017
+ const { allowFlip, closeOnClickOutside, closeTransitionMs = 130, dialogManagerId, focus, id, offset = 8, onMenuLevelChange: onMenuLevelChangeProp, placement, referenceElement, submenuTransitionDurationMs, tabIndex, trapFocus, ...menuProps } = props;
1018
+ const resolvedSubmenuTransitionDurationMs = submenuTransitionDurationMs ?? 460;
1019
+ const isAnchored = id != null;
1020
+ const [menuLevel, setMenuLevel] = useState(1);
1021
+ const [transitionDirection, setTransitionDirection] = useState(void 0);
1022
+ const [contentResetToken, setContentResetToken] = useState(0);
1023
+ const transitionTimeoutRef = useRef(null);
1024
+ const previousMenuLevelRef = useRef(1);
1025
+ const open = useDialogIsOpen(id ?? "", dialogManagerId);
1026
+ const previousOpenRef = useRef(open);
1027
+ useEffect(() => {
1028
+ if (!isAnchored) return;
1029
+ if (previousOpenRef.current && !open) {
1030
+ setMenuLevel(1);
1031
+ setTransitionDirection(void 0);
1032
+ setContentResetToken((value) => value + 1);
1033
+ previousMenuLevelRef.current = 1;
1034
+ if (transitionTimeoutRef.current) {
1035
+ clearTimeout(transitionTimeoutRef.current);
1036
+ transitionTimeoutRef.current = null;
1037
+ }
1038
+ }
1039
+ previousOpenRef.current = open;
1040
+ }, [isAnchored, open]);
1041
+ useEffect(() => () => {
1042
+ if (transitionTimeoutRef.current) clearTimeout(transitionTimeoutRef.current);
1043
+ }, []);
1044
+ const handleMenuLevelChange = useCallback((level) => {
1045
+ if (isAnchored) {
1046
+ const previousLevel = previousMenuLevelRef.current;
1047
+ if (level !== previousLevel) {
1048
+ setTransitionDirection(level > previousLevel ? "forward" : "backward");
1049
+ if (transitionTimeoutRef.current) clearTimeout(transitionTimeoutRef.current);
1050
+ transitionTimeoutRef.current = setTimeout(() => {
1051
+ setTransitionDirection(void 0);
1052
+ transitionTimeoutRef.current = null;
1053
+ }, resolvedSubmenuTransitionDurationMs);
1054
+ }
1055
+ previousMenuLevelRef.current = level;
1056
+ setMenuLevel(level);
1057
+ return;
1058
+ }
1059
+ onMenuLevelChangeProp?.(level);
1060
+ }, [
1061
+ isAnchored,
1062
+ onMenuLevelChangeProp,
1063
+ resolvedSubmenuTransitionDurationMs
1064
+ ]);
1065
+ const content = /* @__PURE__ */ createElement(ContextMenuContentComponent, {
1066
+ anchorReferenceElement: isAnchored ? referenceElement : void 0,
1067
+ ...menuProps,
1068
+ key: `context-menu-content-${contentResetToken}`,
1069
+ onMenuLevelChange: handleMenuLevelChange,
1070
+ transitionDirection
1071
+ });
1072
+ if (isAnchored) {
1073
+ const { backLabel: _b, enableAnimations: _ea, Header: _h, items: _i, ItemsWrapper: _w, keyboardNavigation: _kn, menuClassName: _m, onClose: _c, role: _r, ...anchorDivProps } = menuProps;
1074
+ return /* @__PURE__ */ jsx(DialogAnchor, {
1075
+ allowFlip,
1076
+ closeOnClickOutside,
1077
+ closeTransitionMs,
1078
+ dialogManagerId,
1079
+ focus,
1080
+ id,
1081
+ offset,
1082
+ placement,
1083
+ referenceElement,
1084
+ tabIndex,
1085
+ trapFocus,
1086
+ updateKey: menuLevel,
1087
+ ...anchorDivProps,
1088
+ children: content
1089
+ });
1090
+ }
1091
+ return content;
1092
+ };
1093
+ //#endregion
1094
+ //#region src/components/Dialog/components/Prompt.tsx
1095
+ var PromptRoot = ({ children, className, ...props }) => /* @__PURE__ */ jsx("div", {
1096
+ ...props,
1097
+ className: clsx("str-chat__prompt", className),
1098
+ children
1099
+ });
1100
+ var PromptHeader = ({ className, close, description, descriptionId, goBack, LeadingContent, title, titleId, TrailingContent }) => {
1101
+ const { t } = useTranslationContext();
1102
+ const { dialogId } = useModalContext();
1103
+ const { descriptionId: derivedDescriptionId, titleId: derivedTitleId } = useAriaIdentifiers(dialogId);
1104
+ const resolvedTitleId = titleId ?? derivedTitleId;
1105
+ const resolvedDescriptionId = descriptionId ?? derivedDescriptionId;
1106
+ const hasDescription = description != null && description !== "";
1107
+ return /* @__PURE__ */ jsxs("div", {
1108
+ className: clsx("str-chat__prompt__header", className, {
1109
+ "str-chat__prompt__header--withDescription": hasDescription,
1110
+ "str-chat__prompt__header--withGoBack": goBack
1111
+ }),
1112
+ children: [
1113
+ LeadingContent && /* @__PURE__ */ jsx("div", {
1114
+ className: "str-chat__prompt__header__leading-content",
1115
+ children: /* @__PURE__ */ jsx(LeadingContent, {})
1116
+ }),
1117
+ /* @__PURE__ */ jsxs("div", {
1118
+ className: clsx("str-chat__prompt__header__title-group"),
1119
+ children: [
1120
+ goBack && /* @__PURE__ */ jsx(Button, {
1121
+ appearance: "ghost",
1122
+ "aria-label": t("Go back"),
1123
+ circular: true,
1124
+ className: "str-chat__prompt__header__go-back-button",
1125
+ onClick: goBack,
1126
+ size: "md",
1127
+ variant: "secondary",
1128
+ children: /* @__PURE__ */ jsx(IconArrowLeft, {})
1129
+ }),
1130
+ /* @__PURE__ */ jsx("h2", {
1131
+ className: "str-chat__prompt__header__title",
1132
+ id: resolvedTitleId,
1133
+ children: title
1134
+ }),
1135
+ hasDescription && /* @__PURE__ */ jsx("p", {
1136
+ className: "str-chat__prompt__header__description",
1137
+ id: resolvedDescriptionId,
1138
+ children: description
1139
+ })
1140
+ ]
1141
+ }),
1142
+ (close || TrailingContent) && /* @__PURE__ */ jsxs("div", {
1143
+ className: "str-chat__prompt__header__trailing-content",
1144
+ children: [TrailingContent && /* @__PURE__ */ jsx(TrailingContent, {}), close && /* @__PURE__ */ jsx(Button, {
1145
+ appearance: "ghost",
1146
+ "aria-describedby": hasDescription ? resolvedDescriptionId : void 0,
1147
+ "aria-label": typeof title === "string" ? t("Close prompt: {{ title }}", { title }) : t("Close"),
1148
+ circular: true,
1149
+ className: "str-chat__prompt__header__close-button",
1150
+ onClick: close,
1151
+ size: "md",
1152
+ variant: "secondary",
1153
+ children: /* @__PURE__ */ jsx(IconXmark, {})
1154
+ })]
1155
+ })
1156
+ ]
1157
+ });
1158
+ };
1159
+ var PromptBody = ({ children, className }) => /* @__PURE__ */ jsx("div", {
1160
+ className: clsx("str-chat__prompt__body", className),
1161
+ children
1162
+ });
1163
+ var PromptFooter = ({ children, className }) => /* @__PURE__ */ jsx("div", {
1164
+ className: clsx("str-chat__prompt__footer", className),
1165
+ children
1166
+ });
1167
+ var PromptFooterControls = ({ children, className }) => /* @__PURE__ */ jsx("div", {
1168
+ className: clsx("str-chat__prompt__footer__controls", className),
1169
+ children
1170
+ });
1171
+ var PromptFooterControlsButtonSecondary = ({ className, ...props }) => /* @__PURE__ */ jsx(Button, {
1172
+ appearance: "ghost",
1173
+ className: clsx("str-chat__prompt__footer__controls-button", className),
1174
+ size: "md",
1175
+ variant: "secondary",
1176
+ ...props
1177
+ });
1178
+ var PromptFooterControlsButtonPrimary = ({ className, ...props }) => /* @__PURE__ */ jsx(Button, {
1179
+ appearance: "solid",
1180
+ className: clsx("str-chat__prompt__footer__controls-button", className),
1181
+ size: "md",
1182
+ variant: "primary",
1183
+ ...props
1184
+ });
1185
+ var Prompt = {
1186
+ Body: PromptBody,
1187
+ Footer: PromptFooter,
1188
+ FooterControls: PromptFooterControls,
1189
+ FooterControlsButtonPrimary: PromptFooterControlsButtonPrimary,
1190
+ FooterControlsButtonSecondary: PromptFooterControlsButtonSecondary,
1191
+ Header: PromptHeader,
1192
+ Root: PromptRoot
1193
+ };
1194
+ //#endregion
1195
+ //#region src/a11y/hooks/useResolvedModalAriaProps.ts
1196
+ /**
1197
+ * Resolves modal labeling/description attributes from explicit props first,
1198
+ * then from the modal dialog id convention (`${dialogId}-title|description`).
1199
+ *
1200
+ * Rules:
1201
+ * - `aria-labelledby` wins over `aria-label`.
1202
+ * - `aria-describedby` defaults to inferred id when explicit value is absent.
1203
+ */
1204
+ var useResolvedModalAriaProps = ({ ariaDescribedby, ariaLabel, ariaLabelledby, dialogId }) => {
1205
+ const { descriptionId, titleId } = useAriaIdentifiers(dialogId);
1206
+ return useMemo(() => {
1207
+ const resolvedAriaLabelledby = ariaLabel ? ariaLabelledby : ariaLabelledby ?? titleId;
1208
+ return {
1209
+ "aria-describedby": ariaDescribedby ?? descriptionId,
1210
+ "aria-label": resolvedAriaLabelledby ? void 0 : ariaLabel,
1211
+ "aria-labelledby": resolvedAriaLabelledby
1212
+ };
1213
+ }, [
1214
+ ariaDescribedby,
1215
+ ariaLabel,
1216
+ ariaLabelledby,
1217
+ descriptionId,
1218
+ titleId
1219
+ ]);
1220
+ };
1221
+ //#endregion
1222
+ //#region src/components/Modal/GlobalModal.tsx
1223
+ var GlobalModal = ({ "aria-describedby": ariaDescribedby, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, children, className, CloseButtonOnOverlay, dialogId, dialogRootProps, onClose, onCloseAttempt, open, role = "dialog" }) => {
1224
+ const generatedDialogId = useStableId();
1225
+ const resolvedDialogId = dialogId ?? `modal-dialog-${generatedDialogId}`;
1226
+ const dialog = useModalDialog(resolvedDialogId);
1227
+ const isOpen = useModalDialogIsOpen(resolvedDialogId);
1228
+ const isTopmost = useModalDialogIsTopmost(resolvedDialogId);
1229
+ const overlayRef = useRef(null);
1230
+ const closeButtonRef = useRef(null);
1231
+ const closingRef = useRef(false);
1232
+ const { theme } = useChatContext();
1233
+ const { NotificationList: NotificationList$1 = NotificationList } = useComponentContext();
1234
+ const { className: dialogRootClassName, onKeyDown: dialogRootOnKeyDown, ...dialogRootPropsRest } = dialogRootProps ?? {};
1235
+ const dialogLabelingBaseId = dialog.id;
1236
+ const resolvedModalAriaProps = useResolvedModalAriaProps({
1237
+ ariaDescribedby,
1238
+ ariaLabel,
1239
+ ariaLabelledby,
1240
+ dialogId: dialogLabelingBaseId
1241
+ });
1242
+ const maybeClose = useCallback((source, event) => {
1243
+ if (onCloseAttempt?.(source, event) !== false) {
1244
+ dialog.close();
1245
+ closingRef.current = true;
1246
+ onClose?.(event);
1247
+ }
1248
+ }, [
1249
+ dialog,
1250
+ onClose,
1251
+ onCloseAttempt
1252
+ ]);
1253
+ const modalContextValue = useMemo(() => ({
1254
+ close: () => maybeClose("button", {}),
1255
+ dialogId: dialogLabelingBaseId
1256
+ }), [dialogLabelingBaseId, maybeClose]);
1257
+ const handleOverlayClick = (event) => {
1258
+ if (!isTopmost) return;
1259
+ const target = event.target;
1260
+ if (overlayRef.current === target) maybeClose("overlay", event);
1261
+ };
1262
+ const handleCloseButtonClick = (event) => {
1263
+ if (!isTopmost) return;
1264
+ maybeClose("button", event);
1265
+ };
1266
+ const handleDialogKeyDown = (event) => {
1267
+ dialogRootOnKeyDown?.(event);
1268
+ if (event.defaultPrevented || event.key !== "Escape" || !isTopmost) return;
1269
+ maybeClose("escape", event);
1270
+ };
1271
+ useEffect(() => {
1272
+ if (!open) {
1273
+ closingRef.current = false;
1274
+ if (isOpen) dialog.close();
1275
+ return;
1276
+ }
1277
+ if (open && !isOpen && !closingRef.current) dialog.open();
1278
+ }, [
1279
+ dialog,
1280
+ isOpen,
1281
+ open
1282
+ ]);
1283
+ if (!open || !isOpen) return null;
1284
+ return /* @__PURE__ */ jsx(DialogPortalEntry, {
1285
+ dialogId: resolvedDialogId,
1286
+ dialogManagerId: modalDialogManagerId,
1287
+ children: /* @__PURE__ */ jsx(ModalContextProvider, {
1288
+ value: modalContextValue,
1289
+ children: /* @__PURE__ */ jsxs("div", {
1290
+ className: clsx("str-chat", theme, "str-chat__modal str-chat-react__modal str-chat__modal--open", className),
1291
+ onClick: handleOverlayClick,
1292
+ ref: overlayRef,
1293
+ children: [
1294
+ /* @__PURE__ */ jsx(FocusScope, {
1295
+ autoFocus: isTopmost,
1296
+ contain: isTopmost,
1297
+ restoreFocus: true,
1298
+ children: /* @__PURE__ */ jsx("div", {
1299
+ ...dialogRootPropsRest,
1300
+ "aria-describedby": resolvedModalAriaProps["aria-describedby"],
1301
+ "aria-label": resolvedModalAriaProps["aria-label"],
1302
+ "aria-labelledby": resolvedModalAriaProps["aria-labelledby"],
1303
+ "aria-modal": isTopmost ? "true" : void 0,
1304
+ className: clsx("str-chat__modal__dialog", dialogRootClassName),
1305
+ inert: isTopmost ? void 0 : true,
1306
+ onKeyDown: handleDialogKeyDown,
1307
+ role,
1308
+ tabIndex: isTopmost ? 0 : -1,
1309
+ children: /* @__PURE__ */ jsx(DialogManagerProvider, {
1310
+ id: `${resolvedDialogId}-floating-dialog-manager`,
1311
+ portalDestinationProps: {
1312
+ captureOutsideClicks: true,
1313
+ className: "str-chat__modal__floating-dialog-overlay"
1314
+ },
1315
+ children
1316
+ })
1317
+ })
1318
+ }),
1319
+ /* @__PURE__ */ jsx(NotificationList$1, {
1320
+ className: "str-chat__modal__notification-list",
1321
+ panel: "modal",
1322
+ verticalAlignment: "top"
1323
+ }),
1324
+ CloseButtonOnOverlay && /* @__PURE__ */ jsx(CloseButtonOnOverlay, {
1325
+ onClick: handleCloseButtonClick,
1326
+ ref: closeButtonRef
1327
+ })
1328
+ ]
1329
+ })
1330
+ })
1331
+ });
1332
+ };
1333
+ //#endregion
1334
+ //#region src/components/Message/renderText/remarkPlugins/htmlToTextPlugin.ts
1335
+ var visitor = (node) => {
1336
+ if (node.type !== "html") return;
1337
+ node.type = "text";
1338
+ };
1339
+ var transform = (tree) => {
1340
+ visit(tree, visitor);
1341
+ };
1342
+ var htmlToTextPlugin = () => transform;
1343
+ //#endregion
1344
+ //#region src/components/Message/renderText/remarkPlugins/imageToLink.ts
1345
+ var text = (value) => ({
1346
+ type: "text",
1347
+ value
1348
+ });
1349
+ /**
1350
+ * Converts image Markdown links (![Minion](https://octodex.github.com/images/minion.png))
1351
+ * to HTML <a href={url}>{url | title | alt}</a>
1352
+ *
1353
+ * By default, the anchor text content is the image url so that image preview can be generated / enriched on the server.
1354
+ * @param getTextLabelFrom
1355
+ */
1356
+ function imageToLink({ getTextLabelFrom = "url" } = {}) {
1357
+ return (tree) => {
1358
+ const visitor = (node, index, parent) => {
1359
+ if (parent == null || index == null) return;
1360
+ const link = {
1361
+ children: [text(node[getTextLabelFrom] ?? node.url)],
1362
+ title: node.title ?? node.alt ?? node.url,
1363
+ type: "link",
1364
+ url: node.url
1365
+ };
1366
+ parent.children.splice(index, 1, link);
1367
+ return [SKIP, index + 1];
1368
+ };
1369
+ visit(tree, "image", visitor);
1370
+ };
1371
+ }
1372
+ //#endregion
1373
+ //#region src/components/Message/renderText/remarkPlugins/plusPlusToEmphasis.ts
1374
+ /**
1375
+ * \S → first char must be non-whitespace
1376
+ * (?:...)?→ optional middle+closing when length > 1
1377
+ * [\s\S]*?→ anything (including newlines), lazy
1378
+ * final \S→ last char non-whitespace (only required when there’s more than 1)
1379
+ *
1380
+ * Matches:
1381
+ * ++a++
1382
+ * Does not match:
1383
+ * ++++
1384
+ * ++ ++
1385
+ */
1386
+ var INS_REGEX = /\+\+(\S(?:[\s\S]*?\S)?)\+\+/g;
1387
+ var IGNORE_NODE_TYPES = new Set([
1388
+ "code",
1389
+ "inlineCode",
1390
+ "link",
1391
+ "linkReference",
1392
+ "definition",
1393
+ "math",
1394
+ "inlineMath"
1395
+ ]);
1396
+ /**
1397
+ * Converts MD "++Some text++" to inserted text element rendered in HTML as <ins>Some text</ins>
1398
+ * https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/ins
1399
+ */
1400
+ var plusPlusToEmphasis = () => {
1401
+ const visitor = (node, index, parent) => {
1402
+ if (IGNORE_NODE_TYPES.has(node.type)) return SKIP;
1403
+ if (node.type !== "text" || parent == null || typeof index !== "number") return;
1404
+ const value = node.value;
1405
+ INS_REGEX.lastIndex = 0;
1406
+ let match;
1407
+ let last = 0;
1408
+ const out = [];
1409
+ while (match = INS_REGEX.exec(value)) {
1410
+ const [full, inner] = match;
1411
+ const start = match.index;
1412
+ if (start > last) out.push({
1413
+ type: "text",
1414
+ value: value.slice(last, start)
1415
+ });
1416
+ out.push({
1417
+ children: [{
1418
+ type: "text",
1419
+ value: inner
1420
+ }],
1421
+ data: { hName: "ins" },
1422
+ type: "emphasis"
1423
+ });
1424
+ last = start + full.length;
1425
+ }
1426
+ if (out.length === 0) return;
1427
+ if (last < value.length) out.push({
1428
+ type: "text",
1429
+ value: value.slice(last)
1430
+ });
1431
+ parent.children.splice(index, 1, ...out);
1432
+ return [SKIP, index + out.length];
1433
+ };
1434
+ return (tree) => visit(tree, visitor);
1435
+ };
1436
+ //#endregion
1437
+ //#region src/components/Message/Timestamp.tsx
1438
+ function Timestamp(props) {
1439
+ const { calendar, calendarFormats, customClass, format, timestamp } = props;
1440
+ const { formatDate } = useMessageContext("MessageTimestamp");
1441
+ const { t, tDateTimeParser } = useTranslationContext("MessageTimestamp");
1442
+ const normalizedTimestamp = timestamp && isDate(timestamp) ? timestamp.toISOString() : timestamp;
1443
+ const when = useMemo(() => getDateString({
1444
+ calendar,
1445
+ calendarFormats,
1446
+ format,
1447
+ formatDate,
1448
+ messageCreatedAt: normalizedTimestamp,
1449
+ t,
1450
+ tDateTimeParser,
1451
+ timestampTranslationKey: "timestamp/MessageTimestamp"
1452
+ }), [
1453
+ calendar,
1454
+ calendarFormats,
1455
+ format,
1456
+ formatDate,
1457
+ normalizedTimestamp,
1458
+ t,
1459
+ tDateTimeParser
1460
+ ]);
1461
+ if (!when) return null;
1462
+ return /* @__PURE__ */ jsx("time", {
1463
+ className: customClass,
1464
+ dateTime: normalizedTimestamp,
1465
+ title: normalizedTimestamp,
1466
+ children: when
1467
+ });
1468
+ }
1469
+ //#endregion
1470
+ //#region src/components/Message/MessageTimestamp.tsx
1471
+ var UnMemoizedMessageTimestamp = (props) => {
1472
+ const { message: propMessage, ...timestampProps } = props;
1473
+ const { message: contextMessage } = useMessageContext("MessageTimestamp");
1474
+ const { Timestamp: Timestamp$1 = Timestamp } = useComponentContext("MessageTimestamp");
1475
+ return /* @__PURE__ */ jsx(Timestamp$1, {
1476
+ timestamp: (propMessage || contextMessage).created_at,
1477
+ ...timestampProps
1478
+ });
1479
+ };
1480
+ var MessageTimestamp = React.memo(UnMemoizedMessageTimestamp);
1481
+ //#endregion
1482
+ //#region src/components/Attachment/components/DownloadButton.tsx
1483
+ /**
1484
+ * Icon download control for {@link Audio} and {@link FileAttachment} rows.
1485
+ * (BaseImage defines its own small download link when `showDownloadButtonOnError` is used.)
1486
+ */
1487
+ var DownloadButton = ({ assetUrl, className, suggestedFileName, tooltipTitle }) => {
1488
+ const { t } = useTranslationContext();
1489
+ if (!assetUrl) return null;
1490
+ const href = sanitizeUrl(assetUrl);
1491
+ if (!href) return null;
1492
+ return /* @__PURE__ */ jsx("a", {
1493
+ "aria-label": t("aria/Download attachment"),
1494
+ className: clsx("str-chat__button", "str-chat__button--secondary", "str-chat__button--outline", "str-chat__button--circular", "str-chat__button--size-sm", "str-chat__audio-attachment-download-button", className),
1495
+ download: suggestedFileName ?? "",
1496
+ href,
1497
+ rel: "noopener noreferrer",
1498
+ target: "_blank",
1499
+ title: tooltipTitle ?? t("Download Attachment"),
1500
+ children: /* @__PURE__ */ jsx("div", {
1501
+ className: "str-chat__button__content",
1502
+ children: /* @__PURE__ */ jsx(IconDownload, { className: "str-chat__icon str-chat__audio-attachment-download-button__icon" })
1503
+ })
1504
+ });
1505
+ };
1506
+ //#endregion
1507
+ //#region src/components/FileIcon/FileIconSet.tsx
1508
+ var BASE_FILE_ICON_CLASSNAME = "str-chat__file-icon";
1509
+ var FILE_ICON_GRAPHIC_CLASSNAME = "str-chat__file-icon__graphic";
1510
+ /** Add this class (e.g. via className) when hiding the label with CSS to center the icon graphic. */
1511
+ var FILE_ICON_NO_LABEL_CLASSNAME = "str-chat__file-icon--no-label";
1512
+ /** Rendered dimensions (px) and label position in viewBox coords for consistent spacing. */
1513
+ var FILE_ICON_SIZE_CONFIG = {
1514
+ lg: {
1515
+ height: 40,
1516
+ labelX: 16,
1517
+ labelY: 36,
1518
+ width: 32
1519
+ },
1520
+ md: {
1521
+ height: 32,
1522
+ labelX: 16,
1523
+ labelY: 35,
1524
+ width: 26
1525
+ },
1526
+ sm: {
1527
+ height: 24,
1528
+ labelX: 16,
1529
+ labelY: 31.5,
1530
+ width: 19
1531
+ },
1532
+ xl: {
1533
+ height: 48,
1534
+ labelX: 16,
1535
+ labelY: 36,
1536
+ width: 40
1537
+ }
1538
+ };
1539
+ /** Merge partial overrides with default config. Use for Chat-level fileIconSizeConfig. */
1540
+ var mergeFileIconSizeConfig = (overrides) => {
1541
+ if (!overrides) return FILE_ICON_SIZE_CONFIG;
1542
+ return [
1543
+ "sm",
1544
+ "md",
1545
+ "lg",
1546
+ "xl"
1547
+ ].reduce((acc, size) => ({
1548
+ ...acc,
1549
+ [size]: {
1550
+ ...FILE_ICON_SIZE_CONFIG[size],
1551
+ ...overrides[size]
1552
+ }
1553
+ }), {});
1554
+ };
1555
+ var FILE_ICON_VIEWBOX = {
1556
+ height: 40,
1557
+ width: 32
1558
+ };
1559
+ var FILE_ICON_PAPER_PATH = "M0 4C0 1.79086 1.79086 0 4 0H22.4L32 10V36C32 38.2091 30.2091 40 28 40H4C1.79086 40 0 38.2091 0 36V4Z";
1560
+ var FILE_ICON_FOLD_PATH = "M32 10H25.4C23.7431 10 22.4 8.65685 22.4 7V0L32 10Z";
1561
+ var FILE_ICON_WITHOUT_CAPTION_SYMBOL_SCALE_X = 32 / 26;
1562
+ var FILE_ICON_WITHOUT_CAPTION_SYMBOL_SCALE_Y = 40 / 32;
1563
+ var Svg = ({ className, size, sizeConfig, ...props }) => {
1564
+ const dimensions = size ? (sizeConfig ?? FILE_ICON_SIZE_CONFIG)[size] : void 0;
1565
+ const dimensionsStyle = dimensions ? {
1566
+ flexShrink: 0,
1567
+ height: `${dimensions.height}px`,
1568
+ width: `${dimensions.width}px`
1569
+ } : void 0;
1570
+ return /* @__PURE__ */ jsx("svg", {
1571
+ height: dimensions?.height,
1572
+ viewBox: `0 0 ${FILE_ICON_VIEWBOX.width} ${FILE_ICON_VIEWBOX.height}`,
1573
+ width: dimensions?.width,
1574
+ xmlns: "http://www.w3.org/2000/svg",
1575
+ ...props,
1576
+ className: clsx(BASE_FILE_ICON_CLASSNAME, { [`${BASE_FILE_ICON_CLASSNAME}--size-${size}`]: size }, className),
1577
+ style: {
1578
+ ...dimensionsStyle,
1579
+ ...props.style
1580
+ }
1581
+ });
1582
+ };
1583
+ var FileIconLabel = ({ label, size, sizeConfig }) => {
1584
+ const config = size ? (sizeConfig ?? FILE_ICON_SIZE_CONFIG)[size] : {
1585
+ labelX: 16,
1586
+ labelY: 33
1587
+ };
1588
+ return /* @__PURE__ */ jsx("text", {
1589
+ className: "str-chat__file-icon__label",
1590
+ x: config.labelX,
1591
+ y: config.labelY,
1592
+ children: label
1593
+ });
1594
+ };
1595
+ var TEXT_SYMBOLS = {
1596
+ withCaption: /* @__PURE__ */ jsxs(Fragment$1, { children: [
1597
+ /* @__PURE__ */ jsx("rect", {
1598
+ fill: "white",
1599
+ height: "1.6",
1600
+ rx: "0.8",
1601
+ width: "14.4",
1602
+ x: "8",
1603
+ y: "12.2"
1604
+ }),
1605
+ /* @__PURE__ */ jsx("rect", {
1606
+ fill: "white",
1607
+ height: "1.6",
1608
+ rx: "0.8",
1609
+ width: "14.4",
1610
+ x: "8",
1611
+ y: "20.2"
1612
+ }),
1613
+ /* @__PURE__ */ jsx("rect", {
1614
+ fill: "white",
1615
+ height: "1.6",
1616
+ rx: "0.8",
1617
+ width: "9.6",
1618
+ x: "8",
1619
+ y: "16.2"
1620
+ })
1621
+ ] }),
1622
+ withoutCaption: /* @__PURE__ */ jsxs(Fragment$1, { children: [
1623
+ /* @__PURE__ */ jsx("rect", {
1624
+ fill: "white",
1625
+ height: "1.4",
1626
+ rx: "0.7",
1627
+ width: "12.6",
1628
+ x: "6",
1629
+ y: "14.8"
1630
+ }),
1631
+ /* @__PURE__ */ jsx("rect", {
1632
+ fill: "white",
1633
+ height: "1.4",
1634
+ rx: "0.7",
1635
+ width: "12.6",
1636
+ x: "6",
1637
+ y: "21.8"
1638
+ }),
1639
+ /* @__PURE__ */ jsx("rect", {
1640
+ fill: "white",
1641
+ height: "1.4",
1642
+ rx: "0.7",
1643
+ width: "8.4",
1644
+ x: "6",
1645
+ y: "18.3"
1646
+ })
1647
+ ] })
1648
+ };
1649
+ var OTHER_SYMBOLS = {
1650
+ withCaption: /* @__PURE__ */ jsxs(Fragment$1, { children: [
1651
+ /* @__PURE__ */ jsx("rect", {
1652
+ fill: "white",
1653
+ height: "1.6",
1654
+ rx: "0.8",
1655
+ width: "14.4",
1656
+ x: "8",
1657
+ y: "13.2"
1658
+ }),
1659
+ /* @__PURE__ */ jsx("rect", {
1660
+ fill: "white",
1661
+ height: "1.6",
1662
+ rx: "0.8",
1663
+ width: "14.4",
1664
+ x: "8",
1665
+ y: "21.2"
1666
+ }),
1667
+ /* @__PURE__ */ jsx("rect", {
1668
+ fill: "white",
1669
+ height: "1.6",
1670
+ rx: "0.8",
1671
+ width: "9.6",
1672
+ x: "8",
1673
+ y: "17.2"
1674
+ })
1675
+ ] }),
1676
+ withoutCaption: TEXT_SYMBOLS.withoutCaption
1677
+ };
1678
+ var CODE_SYMBOLS = {
1679
+ withCaption: /* @__PURE__ */ jsx("path", {
1680
+ d: "M14.5 22.5L17.5 11.5M20.1666 14.1667L21.9732 16.0862C22.4564 16.5996 22.4564 17.4004 21.9732 17.9138L20.1666 19.8333M11.8333 19.8333L10.0267 17.9138C9.54351 17.4004 9.54351 16.5996 10.0267 16.0862L11.8333 14.1667",
1681
+ stroke: "white",
1682
+ strokeLinecap: "round",
1683
+ strokeLinejoin: "round",
1684
+ strokeWidth: "1.2"
1685
+ }),
1686
+ withoutCaption: /* @__PURE__ */ jsx("path", {
1687
+ d: "M11.6876 23.8125L14.3126 14.1875M16.646 16.5208L18.2267 18.2004C18.6495 18.6496 18.6495 19.3503 18.2267 19.7996L16.646 21.4792M9.3543 21.4792L7.77352 19.7996C7.35072 19.3503 7.35072 18.6496 7.77352 18.2004L9.3543 16.5208",
1688
+ stroke: "white",
1689
+ strokeLinecap: "round",
1690
+ strokeLinejoin: "round",
1691
+ strokeWidth: "1.33333"
1692
+ })
1693
+ };
1694
+ var AUDIO_SYMBOLS = {
1695
+ withCaption: /* @__PURE__ */ jsx("path", {
1696
+ d: "M20.5 15.5V18.5M22.5 14.5V19.5M13.5 19.5H10.5C10.3674 19.5 10.2402 19.4473 10.1464 19.3536C10.0527 19.2598 10 19.1326 10 19V15C10 14.8674 10.0527 14.7402 10.1464 14.6464C10.2402 14.5527 10.3674 14.5 10.5 14.5H13.5L18 11V23L13.5 19.5Z",
1697
+ stroke: "white",
1698
+ strokeLinecap: "round",
1699
+ strokeLinejoin: "round",
1700
+ strokeWidth: "1.2"
1701
+ }),
1702
+ withoutCaption: /* @__PURE__ */ jsx("path", {
1703
+ d: "M17.5 17.5V20.5M19.5 16.5V21.5M10.5 21.5H7.5C7.36739 21.5 7.24021 21.4473 7.14645 21.3536C7.05268 21.2598 7 21.1326 7 21V17C7 16.8674 7.05268 16.7402 7.14645 16.6464C7.24021 16.5527 7.36739 16.5 7.5 16.5H10.5L15 13V25L10.5 21.5Z",
1704
+ stroke: "white",
1705
+ strokeLinecap: "round",
1706
+ strokeLinejoin: "round",
1707
+ strokeWidth: "1.33333"
1708
+ })
1709
+ };
1710
+ var PRESENTATION_SYMBOLS = {
1711
+ withCaption: /* @__PURE__ */ jsx("path", {
1712
+ d: "M12.5 15.5H19.5M16 17.5V22.5M14 22.5H18M9.49999 17.5C9.41472 17.5001 9.33085 17.4783 9.25635 17.4368C9.18185 17.3953 9.1192 17.3355 9.07436 17.263C9.02952 17.1904 9.00397 17.1076 9.00014 17.0224C8.99631 16.9373 9.01433 16.8525 9.05249 16.7762L11.5525 11.7763C11.5941 11.6932 11.6579 11.6233 11.737 11.5745C11.816 11.5257 11.9071 11.4999 12 11.5H20C20.0929 11.4999 20.184 11.5257 20.263 11.5745C20.342 11.6233 20.4059 11.6932 20.4475 11.7763L22.9475 16.7762C22.9857 16.8525 23.0037 16.9373 22.9998 17.0224C22.996 17.1076 22.9705 17.1904 22.9256 17.263C22.8808 17.3355 22.8181 17.3953 22.7436 17.4368C22.6691 17.4783 22.5853 17.5001 22.5 17.5H9.49999Z",
1713
+ stroke: "white",
1714
+ strokeLinecap: "round",
1715
+ strokeLinejoin: "round",
1716
+ strokeWidth: "1.2"
1717
+ }),
1718
+ withoutCaption: /* @__PURE__ */ jsx("path", {
1719
+ d: "M9.49999 17.5H16.5M13 19.5V24.5M11 24.5H15M6.49999 19.5C6.41472 19.5001 6.33085 19.4783 6.25635 19.4368C6.18185 19.3953 6.11921 19.3355 6.07436 19.263C6.02952 19.1904 6.00397 19.1076 6.00014 19.0224C5.99631 18.9373 6.01433 18.8525 6.05249 18.7763L8.55249 13.7763C8.59406 13.6932 8.65795 13.6233 8.73699 13.5745C8.81603 13.5257 8.9071 13.4999 8.99999 13.5H17C17.0929 13.4999 17.184 13.5257 17.263 13.5745C17.342 13.6233 17.4059 13.6932 17.4475 13.7763L19.9475 18.7763C19.9857 18.8525 20.0037 18.9373 19.9998 19.0224C19.996 19.1076 19.9705 19.1904 19.9256 19.263C19.8808 19.3355 19.8181 19.3953 19.7436 19.4368C19.6691 19.4783 19.5853 19.5001 19.5 19.5H6.49999Z",
1720
+ stroke: "white",
1721
+ strokeLinecap: "round",
1722
+ strokeLinejoin: "round",
1723
+ strokeWidth: "1.33333"
1724
+ })
1725
+ };
1726
+ var SPREADSHEET_SYMBOLS = {
1727
+ withCaption: /* @__PURE__ */ jsx("path", {
1728
+ d: "M10 15.5H22M10 18.5H22M13.5 15.5V21.5M10 12.5H22V21C22 21.1326 21.9473 21.2598 21.8536 21.3536C21.7598 21.4473 21.6326 21.5 21.5 21.5H10.5C10.3674 21.5 10.2402 21.4473 10.1464 21.3536C10.0527 21.2598 10 21.1326 10 21V12.5Z",
1729
+ stroke: "white",
1730
+ strokeLinecap: "round",
1731
+ strokeLinejoin: "round",
1732
+ strokeWidth: "1.5"
1733
+ }),
1734
+ withoutCaption: /* @__PURE__ */ jsx("path", {
1735
+ d: "M7 17.5H19M7 20.5H19M10.5 17.5V23.5M7 14.5H19V23C19 23.1326 18.9473 23.2598 18.8536 23.3536C18.7598 23.4473 18.6326 23.5 18.5 23.5H7.5C7.36739 23.5 7.24021 23.4473 7.14645 23.3536C7.05268 23.2598 7 23.1326 7 23V14.5Z",
1736
+ stroke: "white",
1737
+ strokeLinecap: "round",
1738
+ strokeLinejoin: "round",
1739
+ strokeWidth: "1.2"
1740
+ })
1741
+ };
1742
+ var COMPRESSION_SYMBOLS = {
1743
+ withCaption: /* @__PURE__ */ jsx("path", {
1744
+ clipRule: "evenodd",
1745
+ d: "M9.41177 0H7.52942V2H9.41177V4H7.52942V6H9.41177V8H7.52942V10H9.41177V12H7.52942V14H9.41177V12H11.2941V10H9.41177V8H11.2941V6H9.41177V4H11.2941V2H9.41177V0ZM7.52942 17C7.52942 16.4477 7.9508 16 8.4706 16H10.3529C10.8727 16 11.2941 16.4477 11.2941 17V23C11.2941 23.5523 10.8727 24 10.3529 24H8.4706C7.9508 24 7.52942 23.5523 7.52942 23V17ZM8.4706 23V20H10.3529V23H8.4706Z",
1746
+ fill: "white",
1747
+ fillRule: "evenodd"
1748
+ }),
1749
+ withoutCaption: /* @__PURE__ */ jsx("path", {
1750
+ clipRule: "evenodd",
1751
+ d: "M8.17031 0H6.11768V2.14737H8.17031V4.29474H6.11768V6.4421H8.17031V8.58947H6.11768V10.7368H8.17031V12.8842H6.11768V15.0316H8.17031V12.8842H10.2229V10.7368H8.17031V8.58947H10.2229V6.4421H8.17031V4.29474H10.2229V2.14737H8.17031V0ZM6.11768 18.2526C6.11768 17.6597 6.57717 17.1789 7.14399 17.1789H9.19662C9.76344 17.1789 10.2229 17.6597 10.2229 18.2526V24.6947C10.2229 25.2877 9.76344 25.7684 9.19662 25.7684H7.14399C6.57717 25.7684 6.11768 25.2877 6.11768 24.6947V18.2526ZM7.14399 24.6947V21.4737H9.19662V24.6947H7.14399Z",
1752
+ fill: "white",
1753
+ fillRule: "evenodd"
1754
+ })
1755
+ };
1756
+ var VIDEO_SYMBOLS = {
1757
+ withCaption: /* @__PURE__ */ jsx("path", {
1758
+ d: "M20.5 16L23.5 14V20L20.5 18M10 13H20C20.2761 13 20.5 13.2239 20.5 13.5V20.5C20.5 20.7761 20.2761 21 20 21H10C9.72386 21 9.5 20.7761 9.5 20.5V13.5C9.5 13.2239 9.72386 13 10 13Z",
1759
+ stroke: "white",
1760
+ strokeLinecap: "round",
1761
+ strokeLinejoin: "round",
1762
+ strokeWidth: "1.2"
1763
+ }),
1764
+ withoutCaption: /* @__PURE__ */ jsx("path", {
1765
+ d: "M17.5001 18L20.5001 16V22L17.5001 20M7.00012 15H17.0001C17.2763 15 17.5001 15.2239 17.5001 15.5V22.5C17.5001 22.7761 17.2763 23 17.0001 23H7.00012C6.72398 23 6.50012 22.7761 6.50012 22.5V15.5C6.50012 15.2239 6.72398 15 7.00012 15Z",
1766
+ stroke: "white",
1767
+ strokeLinecap: "round",
1768
+ strokeLinejoin: "round",
1769
+ strokeWidth: "1.33333"
1770
+ })
1771
+ };
1772
+ var StandardFileTypeIcon = ({ className, color, fileTypeClassName, label, size, sizeConfig, symbols, ...props }) => {
1773
+ const renderLabel = !!label;
1774
+ const resolvedLabel = renderLabel ? label : void 0;
1775
+ const symbolVariant = renderLabel ? "withCaption" : "withoutCaption";
1776
+ return /* @__PURE__ */ jsxs(Svg, {
1777
+ ...props,
1778
+ className: clsx(fileTypeClassName, className),
1779
+ size,
1780
+ sizeConfig,
1781
+ children: [/* @__PURE__ */ jsxs("g", {
1782
+ className: FILE_ICON_GRAPHIC_CLASSNAME,
1783
+ children: [
1784
+ /* @__PURE__ */ jsx("path", {
1785
+ d: FILE_ICON_PAPER_PATH,
1786
+ fill: color
1787
+ }),
1788
+ renderLabel ? symbols[symbolVariant] : /* @__PURE__ */ jsx("g", {
1789
+ transform: `scale(${FILE_ICON_WITHOUT_CAPTION_SYMBOL_SCALE_X} ${FILE_ICON_WITHOUT_CAPTION_SYMBOL_SCALE_Y})`,
1790
+ children: symbols[symbolVariant]
1791
+ }),
1792
+ /* @__PURE__ */ jsx("path", {
1793
+ d: FILE_ICON_FOLD_PATH,
1794
+ fill: "white",
1795
+ opacity: "0.5"
1796
+ })
1797
+ ]
1798
+ }), resolvedLabel && /* @__PURE__ */ jsx(FileIconLabel, {
1799
+ label: resolvedLabel,
1800
+ size,
1801
+ sizeConfig
1802
+ })]
1803
+ });
1804
+ };
1805
+ var PDF_SMALL_SYMBOL = /* @__PURE__ */ jsx("path", {
1806
+ d: "M20.7533 19.5337C20.28 19.037 19.3093 18.7537 17.9373 18.7537C17.204 18.7537 16.3526 18.8244 15.43 18.9897C14.8647 18.4461 14.3499 17.8523 13.892 17.2157C13.5373 16.7424 13.2293 16.2224 12.9453 15.725C13.49 14.069 13.75 12.7204 13.75 11.7504C13.75 10.6624 13.348 9.52637 12.1886 9.52637C11.834 9.52637 11.4786 9.7397 11.2893 10.047C10.7693 10.9697 11.006 12.9804 11.9046 14.9677C11.5664 15.984 11.1876 16.9863 10.7693 17.9724C10.3906 18.8717 9.96465 19.7944 9.49131 20.6457C6.88931 21.687 5.20931 22.8937 5.01998 23.839C4.94931 24.1944 5.06731 24.5257 5.32798 24.7857C5.42265 24.857 5.75398 25.141 6.32131 25.141C8.04798 25.141 9.86998 22.349 10.7926 20.6697C11.5026 20.433 12.2126 20.1964 12.922 20.007C13.6704 19.8038 14.4284 19.638 15.1933 19.5104C17.0146 21.1424 18.6233 21.403 19.428 21.403C20.4213 21.403 20.7766 21.0004 20.8946 20.6697C21.108 20.243 20.966 19.7704 20.7533 19.5337ZM19.8066 20.2204C19.7353 20.575 19.38 20.8117 18.884 20.8117C18.742 20.8117 18.624 20.7877 18.4813 20.7644C17.5826 20.551 16.7306 20.1017 15.8793 19.3917C16.5357 19.2807 17.2003 19.2254 17.866 19.2264C18.3633 19.2264 18.7893 19.2497 19.0726 19.321C19.404 19.3917 19.9246 19.605 19.806 20.2197L19.8066 20.2204ZM14.7906 19.1084C14.1305 19.2321 13.4755 19.382 12.8273 19.5577C12.262 19.7047 11.7017 19.8703 11.1473 20.0544C11.4355 19.4962 11.7039 18.9281 11.952 18.351C12.236 17.6884 12.472 17.0024 12.7093 16.3637C12.9453 16.7657 13.206 17.1684 13.466 17.523C13.8911 18.065 14.3329 18.5937 14.7906 19.1084ZM11.692 10.307C11.7338 10.2232 11.7978 10.1525 11.877 10.1025C11.9563 10.0526 12.0477 10.0253 12.1413 10.0237C12.638 10.0237 12.7326 10.591 12.7326 11.041C12.7326 11.7977 12.496 12.957 12.094 14.2817C11.4073 12.4364 11.3606 10.899 11.692 10.307ZM9.08931 21.3317C7.88265 23.319 6.72331 24.549 6.01398 24.549C5.88599 24.5498 5.76132 24.5083 5.65931 24.431C5.51731 24.289 5.44598 24.1237 5.49331 23.9344C5.63531 23.2244 7.00731 22.231 9.08931 21.3317Z",
1807
+ fill: "white"
1808
+ });
1809
+ var PDF_LEGACY_SYMBOL = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("path", {
1810
+ d: "M23.7533 19.2C23.28 18.7033 22.3093 18.42 20.9373 18.42C20.204 18.42 19.3526 18.4906 18.43 18.656C17.8647 18.1124 17.3499 17.5186 16.892 16.882C16.5373 16.4086 16.2293 15.8886 15.9453 15.3913C16.49 13.7353 16.75 12.3866 16.75 11.4166C16.75 10.3286 16.348 9.19263 15.1886 9.19263C14.834 9.19263 14.4786 9.40596 14.2893 9.71329C13.7693 10.636 14.006 12.6466 14.9046 14.634C14.5664 15.6502 14.1877 16.6526 13.7693 17.6386C13.3906 18.538 12.9646 19.4606 12.4913 20.312C9.88931 21.3533 8.20931 22.56 8.01998 23.5053C7.94931 23.8606 8.06731 24.192 8.32798 24.452C8.42265 24.5233 8.75398 24.8073 9.32131 24.8073C11.048 24.8073 12.87 22.0153 13.7926 20.336C14.5026 20.0993 15.2126 19.8626 15.922 19.6733C16.6704 19.4701 17.4284 19.3043 18.1933 19.1766C20.0146 20.8086 21.6233 21.0693 22.428 21.0693C23.4213 21.0693 23.7766 20.6666 23.8946 20.336C24.108 19.9093 23.966 19.4366 23.7533 19.2ZM22.8066 19.8866C22.7353 20.2413 22.38 20.478 21.884 20.478C21.742 20.478 21.624 20.454 21.4813 20.4306C20.5826 20.2173 19.7306 19.768 18.8793 19.058C19.5357 18.947 20.2003 18.8917 20.866 18.8926C21.3633 18.8926 21.7893 18.916 22.0726 18.9873C22.404 19.058 22.9246 19.2713 22.806 19.886L22.8066 19.8866ZM17.7906 18.7746C17.1305 18.8983 16.4755 19.0482 15.8273 19.224C15.262 19.3709 14.7017 19.5366 14.1473 19.7206C14.4355 19.1625 14.7039 18.5944 14.952 18.0173C15.236 17.3546 15.472 16.6686 15.7093 16.03C15.9453 16.432 16.206 16.8346 16.466 17.1893C16.8911 17.7313 17.3329 18.26 17.7906 18.7746ZM14.692 9.97329C14.7338 9.88949 14.7978 9.81875 14.877 9.7688C14.9563 9.71884 15.0477 9.69157 15.1413 9.68996C15.638 9.68996 15.7326 10.2573 15.7326 10.7073C15.7326 11.464 15.496 12.6233 15.094 13.948C14.4073 12.1026 14.3606 10.5653 14.692 9.97329ZM12.0893 20.998C10.8826 22.9853 9.72331 24.2153 9.01398 24.2153C8.88599 24.2161 8.76132 24.1746 8.65931 24.0973C8.51731 23.9553 8.44598 23.79 8.49331 23.6006C8.63531 22.8906 10.0073 21.8973 12.0893 20.998Z",
1811
+ fill: "white"
1812
+ }), /* @__PURE__ */ jsx("path", {
1813
+ d: "M9.74219 34.4258V28.6992H10.8828V29.3633H10.9531C11.0286 29.2096 11.1276 29.0781 11.25 28.9688C11.375 28.8594 11.5208 28.776 11.6875 28.7188C11.8542 28.6589 12.0391 28.6289 12.2422 28.6289C12.6016 28.6289 12.9115 28.7188 13.1719 28.8984C13.4323 29.0781 13.6328 29.3333 13.7734 29.6641C13.9167 29.9922 13.9883 30.3854 13.9883 30.8438V30.8516C13.9883 31.3125 13.918 31.7083 13.7773 32.0391C13.6367 32.3698 13.4362 32.6237 13.1758 32.8008C12.9154 32.9779 12.6042 33.0664 12.2422 33.0664C12.0443 33.0664 11.8607 33.0365 11.6914 32.9766C11.5247 32.9141 11.3776 32.8268 11.25 32.7148C11.125 32.6029 11.026 32.4688 10.9531 32.3125H10.8828V34.4258H9.74219ZM11.8516 32.1211C12.0547 32.1211 12.2279 32.0703 12.3711 31.9688C12.5169 31.8672 12.6289 31.7214 12.707 31.5312C12.7878 31.3411 12.8281 31.1146 12.8281 30.8516V30.8438C12.8281 30.5807 12.7878 30.3542 12.707 30.1641C12.6289 29.974 12.5169 29.8281 12.3711 29.7266C12.2279 29.625 12.0547 29.5742 11.8516 29.5742C11.651 29.5742 11.4766 29.625 11.3281 29.7266C11.1823 29.8281 11.069 29.974 10.9883 30.1641C10.9102 30.3516 10.8711 30.5781 10.8711 30.8438V30.8516C10.8711 31.112 10.9115 31.3385 10.9922 31.5312C11.0729 31.7214 11.1862 31.8672 11.332 31.9688C11.4805 32.0703 11.6536 32.1211 11.8516 32.1211ZM16.457 33.0664C16.1003 33.0664 15.7904 32.9779 15.5273 32.8008C15.2669 32.6211 15.0651 32.3659 14.9219 32.0352C14.7812 31.7044 14.7109 31.3099 14.7109 30.8516V30.8438C14.7109 30.3828 14.7799 29.987 14.918 29.6562C15.0586 29.3255 15.2591 29.0716 15.5195 28.8945C15.7799 28.7174 16.0924 28.6289 16.457 28.6289C16.6523 28.6289 16.8333 28.6602 17 28.7227C17.1693 28.7826 17.3177 28.8685 17.4453 28.9805C17.5729 29.0924 17.6719 29.2279 17.7422 29.3867H17.8125V27.0547H18.9531V33H17.8125V32.332H17.7422C17.6693 32.4857 17.5703 32.6172 17.4453 32.7266C17.3229 32.8359 17.1784 32.9206 17.0117 32.9805C16.8451 33.0378 16.6602 33.0664 16.457 33.0664ZM16.8438 32.1211C17.0469 32.1211 17.2214 32.0703 17.3672 31.9688C17.513 31.8672 17.625 31.7214 17.7031 31.5312C17.7839 31.3411 17.8242 31.1159 17.8242 30.8555V30.8477C17.8242 30.582 17.7839 30.3555 17.7031 30.168C17.625 29.9779 17.5117 29.832 17.3633 29.7305C17.2174 29.6263 17.0443 29.5742 16.8438 29.5742C16.6458 29.5742 16.4727 29.6263 16.3242 29.7305C16.1784 29.832 16.0664 29.9779 15.9883 30.168C15.9102 30.3555 15.8711 30.5807 15.8711 30.8438V30.8516C15.8711 31.1146 15.9102 31.3411 15.9883 31.5312C16.0664 31.7214 16.1784 31.8672 16.3242 31.9688C16.4701 32.0703 16.6432 32.1211 16.8438 32.1211ZM20.4648 33V29.5586H19.7695V28.6992H20.4648V28.2969C20.4648 28.0104 20.5156 27.7721 20.6172 27.582C20.7188 27.3919 20.8776 27.25 21.0938 27.1562C21.3125 27.0599 21.5951 27.0117 21.9414 27.0117C22.0586 27.0117 22.1641 27.0156 22.2578 27.0234C22.3542 27.0312 22.444 27.0417 22.5273 27.0547V27.8164C22.4909 27.8086 22.4427 27.8034 22.3828 27.8008C22.3255 27.7956 22.2604 27.793 22.1875 27.793C21.9661 27.793 21.8099 27.8438 21.7188 27.9453C21.6276 28.0443 21.582 28.1875 21.582 28.375V28.6992H22.4922V29.5586H21.6055V33H20.4648Z",
1814
+ fill: "white"
1815
+ })] });
1816
+ var FilePdfIcon = ({ className, label, size, sizeConfig, ...props }) => {
1817
+ const useLegacyPdfMarkup = !!label;
1818
+ return /* @__PURE__ */ jsx(Svg, {
1819
+ ...props,
1820
+ className: clsx("str-chat__file-icon--pdf", useLegacyPdfMarkup && "str-chat__file-icon--no-label", className),
1821
+ size,
1822
+ sizeConfig,
1823
+ children: /* @__PURE__ */ jsxs("g", {
1824
+ className: FILE_ICON_GRAPHIC_CLASSNAME,
1825
+ children: [
1826
+ /* @__PURE__ */ jsx("path", {
1827
+ d: FILE_ICON_PAPER_PATH,
1828
+ fill: "#E71A01"
1829
+ }),
1830
+ useLegacyPdfMarkup ? PDF_LEGACY_SYMBOL : /* @__PURE__ */ jsx("g", {
1831
+ transform: `scale(${FILE_ICON_WITHOUT_CAPTION_SYMBOL_SCALE_X} ${FILE_ICON_WITHOUT_CAPTION_SYMBOL_SCALE_Y})`,
1832
+ children: PDF_SMALL_SYMBOL
1833
+ }),
1834
+ /* @__PURE__ */ jsx("path", {
1835
+ d: FILE_ICON_FOLD_PATH,
1836
+ fill: "white",
1837
+ opacity: "0.5"
1838
+ })
1839
+ ]
1840
+ })
1841
+ });
1842
+ };
1843
+ var FileWordIcon = ({ className, label, ...props }) => /* @__PURE__ */ jsx(StandardFileTypeIcon, {
1844
+ ...props,
1845
+ className,
1846
+ color: "#3375E2",
1847
+ fileTypeClassName: "str-chat__file-icon--doc",
1848
+ label,
1849
+ symbols: TEXT_SYMBOLS
1850
+ });
1851
+ var FilePowerPointIcon = ({ className, label, ...props }) => /* @__PURE__ */ jsx(StandardFileTypeIcon, {
1852
+ ...props,
1853
+ className,
1854
+ color: "#D14423",
1855
+ fileTypeClassName: "str-chat__file-icon--ppt",
1856
+ label,
1857
+ symbols: PRESENTATION_SYMBOLS
1858
+ });
1859
+ var FileExcelIcon = ({ className = "", label, ...props }) => /* @__PURE__ */ jsx(StandardFileTypeIcon, {
1860
+ ...props,
1861
+ className,
1862
+ color: "#0C864B",
1863
+ fileTypeClassName: "str-chat__file-icon--xls",
1864
+ label,
1865
+ symbols: SPREADSHEET_SYMBOLS
1866
+ });
1867
+ var FileArchiveIcon = ({ className = "", label = "", ...props }) => /* @__PURE__ */ jsx(StandardFileTypeIcon, {
1868
+ ...props,
1869
+ className,
1870
+ color: "#E59E34",
1871
+ fileTypeClassName: "str-chat__file-icon--compressed",
1872
+ label,
1873
+ symbols: COMPRESSION_SYMBOLS
1874
+ });
1875
+ var FileCodeIcon = ({ className = "", label, ...props }) => /* @__PURE__ */ jsx(StandardFileTypeIcon, {
1876
+ ...props,
1877
+ className,
1878
+ color: "#00ACA1",
1879
+ fileTypeClassName: "str-chat__file-icon--code",
1880
+ label,
1881
+ symbols: CODE_SYMBOLS
1882
+ });
1883
+ var FileAudioIcon = ({ className = "", label, ...props }) => /* @__PURE__ */ jsx(StandardFileTypeIcon, {
1884
+ ...props,
1885
+ className,
1886
+ color: "#2727B0",
1887
+ fileTypeClassName: "str-chat__file-icon--audio",
1888
+ label,
1889
+ symbols: AUDIO_SYMBOLS
1890
+ });
1891
+ var FileVideoIcon = ({ className = "", label, ...props }) => /* @__PURE__ */ jsx(StandardFileTypeIcon, {
1892
+ ...props,
1893
+ className,
1894
+ color: "#A847B7",
1895
+ fileTypeClassName: "str-chat__file-icon--video",
1896
+ label,
1897
+ symbols: VIDEO_SYMBOLS
1898
+ });
1899
+ var FileFallbackIcon = ({ className = "", label = "", ...props }) => /* @__PURE__ */ jsx(StandardFileTypeIcon, {
1900
+ ...props,
1901
+ className,
1902
+ color: "#888888",
1903
+ fileTypeClassName: "str-chat__file-icon--other",
1904
+ label,
1905
+ symbols: OTHER_SYMBOLS
1906
+ });
1907
+ //#endregion
1908
+ //#region src/components/FileIcon/mimeTypes.ts
1909
+ var wordMimeTypes = [
1910
+ "application/msword",
1911
+ "application/msword-template",
1912
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1913
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
1914
+ "application/vnd.ms-word.document.macroEnabled.12",
1915
+ "application/vnd.ms-word.template.macroEnabled.12",
1916
+ "application/vnd.oasis.opendocument.text",
1917
+ "application/vnd.oasis.opendocument.text-template",
1918
+ "application/vnd.oasis.opendocument.text-flat-xml"
1919
+ ];
1920
+ var excelMimeTypes = [
1921
+ "text/csv",
1922
+ "application/vnd.ms-excel",
1923
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
1924
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
1925
+ "application/vnd.ms-excel.sheet.macroEnabled.12",
1926
+ "application/vnd.ms-excel.template.macroEnabled.12",
1927
+ "application/vnd.ms-excel.addin.macroEnabled.12",
1928
+ "application/vnd.ms-excel.addin.macroEnabled.12",
1929
+ "application/vnd.oasis.opendocument.spreadsheet",
1930
+ "application/vnd.oasis.opendocument.spreadsheet-template",
1931
+ "application/vnd.oasis.opendocument.spreadsheet-flat-xml"
1932
+ ];
1933
+ var powerpointMimeTypes = [
1934
+ "application/vnd.ms-powerpoint",
1935
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
1936
+ "application/vnd.openxmlformats-officedocument.presentationml.template",
1937
+ "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
1938
+ "application/vnd.ms-powerpoint.addin.macroEnabled.12",
1939
+ "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
1940
+ "application/vnd.ms-powerpoint.template.macroEnabled.12",
1941
+ "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
1942
+ "application/vnd.oasis.opendocument.presentation",
1943
+ "application/vnd.oasis.opendocument.presentation-template",
1944
+ "application/vnd.oasis.opendocument.presentation-flat-xml"
1945
+ ];
1946
+ var archiveFileTypes = [
1947
+ "application/zip",
1948
+ "application/x-7z-compressed",
1949
+ "application/x-archive",
1950
+ "application/x-tar",
1951
+ "application/gzip",
1952
+ "application/x-compress",
1953
+ "application/x-bzip",
1954
+ "application/x-lzip",
1955
+ "application/x-lz4",
1956
+ "application/x-lzma",
1957
+ "application/x-lzop",
1958
+ "application/x-xz",
1959
+ "application/x-webarchive",
1960
+ "application/vnd.rar"
1961
+ ];
1962
+ var codeFileTypes = [
1963
+ "text/html",
1964
+ "text/css",
1965
+ "application/x-javascript",
1966
+ "text/javascript",
1967
+ "application/json",
1968
+ "text/x-python",
1969
+ "text/x-go",
1970
+ "text/x-csrc",
1971
+ "text/x-c++src",
1972
+ "application/x-ruby",
1973
+ "text/rust",
1974
+ "text/x-java",
1975
+ "application/x-php",
1976
+ "text/x-csharp",
1977
+ "text/x-scala",
1978
+ "text/x-erlang",
1979
+ "application/x-shellscript"
1980
+ ];
1981
+ var mimeTypeToExtensionMap = {
1982
+ "application/epub+zip": "epub",
1983
+ "application/gzip": "gz",
1984
+ "application/java-archive": "jar",
1985
+ "application/json": "json",
1986
+ "application/ld+json": "jsonld",
1987
+ "application/msword": "doc",
1988
+ "application/msword-template": "dot",
1989
+ "application/octet-stream": "bin",
1990
+ "application/ogg": "ogx",
1991
+ "application/pdf": "pdf",
1992
+ "application/postscript": "ps",
1993
+ "application/rtf": "rtf",
1994
+ "application/vnd.amazon.ebook": "azw",
1995
+ "application/vnd.apple.installer+xml": "mpkg",
1996
+ "application/vnd.mozilla.xul+xml": "xul",
1997
+ "application/vnd.ms-excel": "xls",
1998
+ "application/vnd.ms-excel.addin.macroEnabled.12": "xlam",
1999
+ "application/vnd.ms-excel.sheet.macroEnabled.12": "xlsm",
2000
+ "application/vnd.ms-excel.template.macroEnabled.12": "xltm",
2001
+ "application/vnd.ms-fontobject": "eot",
2002
+ "application/vnd.ms-powerpoint": "ppt",
2003
+ "application/vnd.ms-powerpoint.addin.macroEnabled.12": "ppam",
2004
+ "application/vnd.ms-powerpoint.presentation.macroEnabled.12": "pptm",
2005
+ "application/vnd.ms-powerpoint.slideshow.macroEnabled.12": "ppsm",
2006
+ "application/vnd.ms-powerpoint.template.macroEnabled.12": "potm",
2007
+ "application/vnd.ms-word.document.macroEnabled.12": "docm",
2008
+ "application/vnd.ms-word.template.macroEnabled.12": "dotm",
2009
+ "application/vnd.oasis.opendocument.presentation": "odp",
2010
+ "application/vnd.oasis.opendocument.presentation-flat-xml": "fodp",
2011
+ "application/vnd.oasis.opendocument.presentation-template": "otp",
2012
+ "application/vnd.oasis.opendocument.spreadsheet": "ods",
2013
+ "application/vnd.oasis.opendocument.spreadsheet-flat-xml": "fods",
2014
+ "application/vnd.oasis.opendocument.spreadsheet-template": "ots",
2015
+ "application/vnd.oasis.opendocument.text": "odt",
2016
+ "application/vnd.oasis.opendocument.text-flat-xml": "fodt",
2017
+ "application/vnd.oasis.opendocument.text-template": "ott",
2018
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation": "pptx",
2019
+ "application/vnd.openxmlformats-officedocument.presentationml.slideshow": "ppsx",
2020
+ "application/vnd.openxmlformats-officedocument.presentationml.template": "potx",
2021
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx",
2022
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.template": "xltx",
2023
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
2024
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.template": "dotx",
2025
+ "application/vnd.rar": "rar",
2026
+ "application/vnd.visio": "vsd",
2027
+ "application/wasm": "wasm",
2028
+ "application/x-7z-compressed": "7z",
2029
+ "application/x-abiword": "abw",
2030
+ "application/x-archive": "ar",
2031
+ "application/x-bzip": "bz",
2032
+ "application/x-bzip2": "bz2",
2033
+ "application/x-cdf": "cda",
2034
+ "application/x-compress": "Z",
2035
+ "application/x-csh": "csh",
2036
+ "application/x-dosexec": "exe",
2037
+ "application/x-freearc": "arc",
2038
+ "application/x-httpd-php": "php",
2039
+ "application/x-iso9660-image": "iso",
2040
+ "application/x-javascript": "js",
2041
+ "application/x-lz4": "lz4",
2042
+ "application/x-lzip": "lz",
2043
+ "application/x-lzma": "lzma",
2044
+ "application/x-lzop": "lzo",
2045
+ "application/x-mobipocket-ebook": "mobi",
2046
+ "application/x-msdownload": "exe",
2047
+ "application/x-perl": "pl",
2048
+ "application/x-php": "php",
2049
+ "application/x-rar-compressed": "rar",
2050
+ "application/x-ruby": "rb",
2051
+ "application/x-sh": "sh",
2052
+ "application/x-shellscript": "sh",
2053
+ "application/x-shockwave-flash": "swf",
2054
+ "application/x-sql": "sql",
2055
+ "application/x-stuffit": "sit",
2056
+ "application/x-tar": "tar",
2057
+ "application/x-webarchive": "war",
2058
+ "application/x-xz": "xz",
2059
+ "application/x-yaml": "yaml",
2060
+ "application/xhtml+xml": "xhtml",
2061
+ "application/xml": "xml",
2062
+ "application/zip": "zip",
2063
+ "audio/aac": "aac",
2064
+ "audio/flac": "flac",
2065
+ "audio/midi": "midi",
2066
+ "audio/mp4": "m4a",
2067
+ "audio/mpeg": "mp3",
2068
+ "audio/ogg": "oga",
2069
+ "audio/opus": "opus",
2070
+ "audio/wav": "wav",
2071
+ "audio/webm": "weba",
2072
+ "audio/x-aiff": "aiff",
2073
+ "audio/x-m4a": "m4a",
2074
+ "audio/x-midi": "midi",
2075
+ "audio/x-ms-wma": "wma",
2076
+ "audio/x-wav": "wav",
2077
+ "font/otf": "otf",
2078
+ "font/ttf": "ttf",
2079
+ "font/woff": "woff",
2080
+ "font/woff2": "woff2",
2081
+ "image/apng": "apng",
2082
+ "image/avif": "avif",
2083
+ "image/bmp": "bmp",
2084
+ "image/gif": "gif",
2085
+ "image/heic": "heic",
2086
+ "image/heif": "heif",
2087
+ "image/jpeg": "jpg",
2088
+ "image/png": "png",
2089
+ "image/svg+xml": "svg",
2090
+ "image/tiff": "tiff",
2091
+ "image/vnd.microsoft.icon": "ico",
2092
+ "image/webp": "webp",
2093
+ "image/x-icon": "ico",
2094
+ "text/calendar": "ics",
2095
+ "text/css": "css",
2096
+ "text/csv": "csv",
2097
+ "text/html": "html",
2098
+ "text/javascript": "js",
2099
+ "text/markdown": "md",
2100
+ "text/plain": "txt",
2101
+ "text/rtf": "rtf",
2102
+ "text/rust": "rs",
2103
+ "text/tab-separated-values": "tsv",
2104
+ "text/vcard": "vcf",
2105
+ "text/x-c": "c",
2106
+ "text/x-c++src": "cpp",
2107
+ "text/x-csharp": "cs",
2108
+ "text/x-csrc": "c",
2109
+ "text/x-diff": "diff",
2110
+ "text/x-erlang": "erl",
2111
+ "text/x-go": "go",
2112
+ "text/x-java": "java",
2113
+ "text/x-java-source": "java",
2114
+ "text/x-kotlin": "kt",
2115
+ "text/x-lua": "lua",
2116
+ "text/x-markdown": "md",
2117
+ "text/x-objectivec": "m",
2118
+ "text/x-pascal": "pas",
2119
+ "text/x-perl": "pl",
2120
+ "text/x-python": "py",
2121
+ "text/x-ruby": "rb",
2122
+ "text/x-rust": "rs",
2123
+ "text/x-scala": "scala",
2124
+ "text/x-sh": "sh",
2125
+ "text/x-shellscript": "sh",
2126
+ "text/x-sql": "sql",
2127
+ "text/x-swift": "swift",
2128
+ "text/x-typescript": "ts",
2129
+ "text/x-yaml": "yaml",
2130
+ "text/xml": "xml",
2131
+ "text/yaml": "yaml",
2132
+ "video/3gpp": "3gp",
2133
+ "video/3gpp2": "3g2",
2134
+ "video/mp2t": "ts",
2135
+ "video/mp4": "mp4",
2136
+ "video/mpeg": "mpeg",
2137
+ "video/ogg": "ogv",
2138
+ "video/quicktime": "mov",
2139
+ "video/webm": "webm",
2140
+ "video/x-flv": "flv",
2141
+ "video/x-m4v": "m4v",
2142
+ "video/x-matroska": "mkv",
2143
+ "video/x-ms-wmv": "wmv",
2144
+ "video/x-msvideo": "avi"
2145
+ };
2146
+ //#endregion
2147
+ //#region src/components/FileIcon/iconMap.ts
2148
+ function generateMimeTypeToIconMap({ FileArchiveIcon, FileCodeIcon, FileExcelIcon, FilePdfIcon, FilePowerPointIcon, FileWordIcon }) {
2149
+ const mimeTypeToIconMap = { "application/pdf": FilePdfIcon };
2150
+ for (const type of wordMimeTypes) mimeTypeToIconMap[type] = FileWordIcon;
2151
+ for (const type of excelMimeTypes) mimeTypeToIconMap[type] = FileExcelIcon;
2152
+ for (const type of powerpointMimeTypes) mimeTypeToIconMap[type] = FilePowerPointIcon;
2153
+ for (const type of archiveFileTypes) mimeTypeToIconMap[type] = FileArchiveIcon;
2154
+ for (const type of codeFileTypes) mimeTypeToIconMap[type] = FileCodeIcon;
2155
+ return mimeTypeToIconMap;
2156
+ }
2157
+ function generateGeneralTypeToIconMap({ FileAltIcon, FileAudioIcon, FileFallbackIcon, FileVideoIcon }) {
2158
+ return {
2159
+ "audio/": FileAudioIcon,
2160
+ "image/": FileFallbackIcon,
2161
+ "text/": FileAltIcon,
2162
+ "video/": FileVideoIcon
2163
+ };
2164
+ }
2165
+ var iconMap = { standard: {
2166
+ ...generateMimeTypeToIconMap({
2167
+ FileArchiveIcon,
2168
+ FileCodeIcon,
2169
+ FileExcelIcon,
2170
+ FilePdfIcon,
2171
+ FilePowerPointIcon,
2172
+ FileWordIcon
2173
+ }),
2174
+ ...generateGeneralTypeToIconMap({
2175
+ FileAltIcon: FileFallbackIcon,
2176
+ FileAudioIcon,
2177
+ FileFallbackIcon,
2178
+ FileVideoIcon
2179
+ }),
2180
+ fallback: FileFallbackIcon
2181
+ } };
2182
+ //#endregion
2183
+ //#region src/components/FileIcon/FileIcon.tsx
2184
+ function mimeTypeToIcon(mimeType) {
2185
+ const theMap = iconMap["standard"];
2186
+ if (!mimeType) return theMap.fallback;
2187
+ const icon = theMap[mimeType];
2188
+ if (icon) return icon;
2189
+ if (mimeType.startsWith("audio/")) return theMap["audio/"] ?? theMap.fallback;
2190
+ if (mimeType.startsWith("video/")) return theMap["video/"] ?? theMap.fallback;
2191
+ if (mimeType.startsWith("image/")) return theMap["image/"] ?? theMap.fallback;
2192
+ if (mimeType.startsWith("text/")) return theMap["text/"] ?? theMap.fallback;
2193
+ return theMap.fallback;
2194
+ }
2195
+ var labelFromMimeType = ({ fileName, mimeType }) => {
2196
+ let label;
2197
+ if (mimeType) label = mimeTypeToExtensionMap[mimeType];
2198
+ if (!label && fileName) label = fileName.split(".").slice(-1)[0];
2199
+ return label;
2200
+ };
2201
+ var FileIcon = (props) => {
2202
+ const { className, fileName, mimeType, size = "md", sizeConfig: sizeConfigOverride, ...rest } = props;
2203
+ const sizeConfig = useMemo(() => mergeFileIconSizeConfig(sizeConfigOverride), [sizeConfigOverride]);
2204
+ const Icon = mimeTypeToIcon(mimeType);
2205
+ const label = fileName ? labelFromMimeType({
2206
+ fileName,
2207
+ mimeType
2208
+ }) : void 0;
2209
+ return /* @__PURE__ */ jsx(Icon, {
2210
+ ...rest,
2211
+ className,
2212
+ label,
2213
+ size,
2214
+ sizeConfig
2215
+ });
2216
+ };
2217
+ //#endregion
2218
+ //#region src/components/AudioPlayback/components/formatTime.ts
2219
+ var formatTime = (totalSeconds, rounding = "ceil") => {
2220
+ if (totalSeconds == null || Number.isNaN(totalSeconds) || totalSeconds < 0) return null;
2221
+ const roundedSeconds = rounding === "floor" ? Math.floor(totalSeconds) : Math.ceil(totalSeconds);
2222
+ const hours = Math.floor(roundedSeconds / 3600);
2223
+ const minutes = Math.floor(roundedSeconds % 3600 / 60);
2224
+ const seconds = roundedSeconds % 60;
2225
+ const minSec = `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
2226
+ return hours ? `${String(hours).padStart(2, "0")}:${minSec}` : minSec;
2227
+ };
2228
+ //#endregion
2229
+ //#region src/components/BaseImage/ImagePlaceholder.tsx
2230
+ var ImagePlaceholder = ({ className }) => {
2231
+ const { t } = useTranslationContext();
2232
+ return /* @__PURE__ */ jsx("div", {
2233
+ "aria-label": t("aria/Image failed to load"),
2234
+ className: clsx("str-chat__image-placeholder", className),
2235
+ "data-testid": "str-chat__base-image-placeholder",
2236
+ role: "img",
2237
+ children: /* @__PURE__ */ jsx(IconImage, {})
2238
+ });
2239
+ };
2240
+ //#endregion
2241
+ //#region src/components/BaseImage/BaseImage.tsx
2242
+ var BaseImage = forwardRef(function BaseImage({ src, ...props }, ref) {
2243
+ const { alt: propsAlt, className: propsClassName, onError: propsOnError, showDownloadButtonOnError = false, ...imgProps } = props;
2244
+ const [failedSrc, setFailedSrc] = useState(null);
2245
+ const { ImagePlaceholder: ImagePlaceholderComponent = ImagePlaceholder } = useComponentContext();
2246
+ const sanitizedUrl = useMemo(() => sanitizeUrl(src), [src]);
2247
+ const error = failedSrc === sanitizedUrl;
2248
+ useEffect(() => () => {
2249
+ setFailedSrc(null);
2250
+ }, [sanitizedUrl]);
2251
+ if (error) return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ImagePlaceholderComponent, { className: clsx(propsClassName, "str-chat__base-image--load-failed") }), showDownloadButtonOnError && /* @__PURE__ */ jsx(DownloadButton, { assetUrl: sanitizedUrl })] });
2252
+ return /* @__PURE__ */ jsx("img", {
2253
+ "data-testid": "str-chat__base-image",
2254
+ ...imgProps,
2255
+ alt: propsAlt ?? "",
2256
+ className: clsx(propsClassName, "str-chat__base-image"),
2257
+ onError: (e) => {
2258
+ setFailedSrc(sanitizedUrl);
2259
+ propsOnError?.(e);
2260
+ },
2261
+ ref,
2262
+ src: sanitizedUrl
2263
+ });
2264
+ });
2265
+ //#endregion
2266
+ //#region src/components/BaseImage/toBaseImageDescriptors.ts
2267
+ /**
2268
+ * Maps an attachment (or link preview) to image/video URLs and metadata for {@link BaseImage} or the gallery.
2269
+ */
2270
+ var toBaseImageDescriptors = (attachment, options = {}) => {
2271
+ if (isGiphyAttachment(attachment)) {
2272
+ const giphyVersion = options?.giphyVersionName && attachment.giphy ? attachment.giphy[options.giphyVersionName] : void 0;
2273
+ return {
2274
+ alt: giphyVersion?.url || attachment.thumb_url,
2275
+ dimensions: giphyVersion ? {
2276
+ height: giphyVersion.height,
2277
+ width: giphyVersion.width
2278
+ } : void 0,
2279
+ imageUrl: attachment.thumb_url,
2280
+ title: attachment.title || attachment.thumb_url
2281
+ };
2282
+ }
2283
+ if (isScrapedContent(attachment)) {
2284
+ const imageUrl = attachment.image_url || attachment.thumb_url;
2285
+ return {
2286
+ alt: attachment.title || imageUrl,
2287
+ imageUrl,
2288
+ title: attachment.title
2289
+ };
2290
+ }
2291
+ if (isLocalVideoAttachment(attachment)) return {
2292
+ title: attachment.title,
2293
+ videoThumbnailUrl: attachment.thumb_url ?? attachment.localMetadata.previewUri,
2294
+ videoUrl: attachment.asset_url ?? attachment.localMetadata.previewUri
2295
+ };
2296
+ if (isVideoAttachment(attachment)) return {
2297
+ title: attachment.title,
2298
+ videoThumbnailUrl: attachment.thumb_url,
2299
+ videoUrl: attachment.asset_url
2300
+ };
2301
+ if (isLocalImageAttachment(attachment)) {
2302
+ const imageUrl = attachment.image_url || attachment.localMetadata.previewUri;
2303
+ return {
2304
+ alt: attachment.title || imageUrl,
2305
+ imageUrl,
2306
+ title: attachment.title
2307
+ };
2308
+ }
2309
+ if (isImageAttachment(attachment)) {
2310
+ const imageUrl = attachment.image_url;
2311
+ return {
2312
+ alt: attachment.title || imageUrl,
2313
+ imageUrl,
2314
+ title: attachment.title
2315
+ };
2316
+ }
2317
+ };
2318
+ //#endregion
2319
+ //#region src/components/Gallery/GalleryContext.tsx
2320
+ /**
2321
+ * Maps an attachment (or link preview) to gallery item fields.
2322
+ * Delegates to {@link toBaseImageDescriptors}.
2323
+ */
2324
+ var toGalleryItemDescriptors = (...args) => toBaseImageDescriptors(...args);
2325
+ var GalleryContext = createContext(void 0);
2326
+ var useGalleryContext = () => {
2327
+ const contextValue = useContext(GalleryContext);
2328
+ if (!contextValue) {
2329
+ console.warn(`The useGalleryContext hook was called outside of the GalleryContext provider. Make sure this hook is called within a child of the Gallery component.`);
2330
+ return {};
2331
+ }
2332
+ return contextValue;
2333
+ };
2334
+ //#endregion
2335
+ //#region src/components/Gallery/GalleryHeader.tsx
2336
+ var GalleryHeader = ({ currentItem }) => {
2337
+ const { t } = useTranslationContext();
2338
+ const { MessageTimestamp: MessageTimestamp$1 = MessageTimestamp } = useComponentContext("GalleryUI");
2339
+ const { isMyMessage, message } = useMessageContext("GalleryUI");
2340
+ const modalContext = useContext(ModalContext);
2341
+ const headerTitle = isMyMessage?.() && t("You") || message?.user?.name || message?.user?.id || currentItem.title || t("User uploaded content");
2342
+ const downloadUrl = useMemo(() => {
2343
+ const rawDownloadUrl = currentItem.videoUrl ?? currentItem.imageUrl;
2344
+ if (!rawDownloadUrl) return void 0;
2345
+ const sanitizedUrl = sanitizeUrl(rawDownloadUrl);
2346
+ return sanitizedUrl === "about:blank" ? void 0 : sanitizedUrl;
2347
+ }, [currentItem.imageUrl, currentItem.videoUrl]);
2348
+ const downloadLabel = t("aria/Download attachment");
2349
+ return /* @__PURE__ */ jsxs("div", {
2350
+ className: "str-chat__gallery__header",
2351
+ children: [
2352
+ /* @__PURE__ */ jsx("div", {
2353
+ "aria-hidden": "true",
2354
+ className: "str-chat__gallery__header-spacer"
2355
+ }),
2356
+ /* @__PURE__ */ jsxs("div", {
2357
+ className: "str-chat__gallery__header-meta",
2358
+ children: [/* @__PURE__ */ jsx("div", {
2359
+ className: "str-chat__gallery__title",
2360
+ children: headerTitle
2361
+ }), message?.created_at ? /* @__PURE__ */ jsx(MessageTimestamp$1, { customClass: "str-chat__gallery__timestamp" }) : null]
2362
+ }),
2363
+ /* @__PURE__ */ jsxs("div", {
2364
+ className: "str-chat__gallery__header-actions",
2365
+ children: [downloadUrl ? /* @__PURE__ */ jsx("a", {
2366
+ "aria-label": downloadLabel,
2367
+ className: "str-chat__gallery__action-button str-chat__gallery__action-button--download",
2368
+ download: true,
2369
+ href: downloadUrl,
2370
+ rel: "noreferrer",
2371
+ target: "_blank",
2372
+ title: downloadLabel,
2373
+ children: /* @__PURE__ */ jsx(IconArrowDownCircle, {})
2374
+ }) : null, modalContext?.close ? /* @__PURE__ */ jsx(Button, {
2375
+ "aria-label": t("Close"),
2376
+ className: "str-chat__gallery__action-button str-chat__gallery__action-button--close",
2377
+ onClick: modalContext.close,
2378
+ title: t("Close"),
2379
+ children: /* @__PURE__ */ jsx(IconXmark, {})
2380
+ }) : null]
2381
+ })
2382
+ ]
2383
+ });
2384
+ };
2385
+ //#endregion
2386
+ //#region src/components/VideoPlayer/VideoPlayer.tsx
2387
+ var ReactPlayer = React.lazy(() => import("./ReactPlayerWrapper.d2ae5941.mjs"));
2388
+ var VideoPlayer = ({ isPlaying, thumbnailUrl, videoUrl }) => {
2389
+ const { LoadingIndicator: LoadingIndicator$1 = LoadingIndicator, VideoPlayer: VideoPlayerContext } = useComponentContext();
2390
+ return VideoPlayerContext ? /* @__PURE__ */ jsx(VideoPlayerContext, {
2391
+ thumbnailUrl,
2392
+ videoUrl
2393
+ }) : /* @__PURE__ */ jsx(React.Suspense, {
2394
+ fallback: /* @__PURE__ */ jsx("div", {
2395
+ className: "str-chat__video-player-loading",
2396
+ children: /* @__PURE__ */ jsx(LoadingIndicator$1, {})
2397
+ }),
2398
+ children: /* @__PURE__ */ jsx(ReactPlayer, {
2399
+ isPlaying,
2400
+ thumbnailUrl,
2401
+ videoUrl
2402
+ })
2403
+ });
2404
+ };
2405
+ //#endregion
2406
+ //#region src/components/VideoPlayer/VideoThumbnail.tsx
2407
+ var VideoThumbnail = ({ className, onPlay, ...imageProps }) => {
2408
+ const { t } = useTranslationContext();
2409
+ return /* @__PURE__ */ jsxs("div", {
2410
+ className: "str-chat__message-attachment__video-thumbnail",
2411
+ children: [/* @__PURE__ */ jsx(BaseImage, {
2412
+ className: clsx("str-chat__message-attachment__video-thumbnail-image", className),
2413
+ ...imageProps
2414
+ }), onPlay ? /* @__PURE__ */ jsx(Button, {
2415
+ appearance: "solid",
2416
+ "aria-label": t("Play video"),
2417
+ circular: true,
2418
+ className: clsx("str-chat__message-attachment__video-thumbnail__play-indicator"),
2419
+ onClick: onPlay,
2420
+ size: "lg",
2421
+ variant: "secondary",
2422
+ children: /* @__PURE__ */ jsx(IconPlayFill, {})
2423
+ }) : /* @__PURE__ */ jsx("div", {
2424
+ className: "str-chat__message-attachment__video-thumbnail__play-indicator",
2425
+ children: /* @__PURE__ */ jsx(IconPlayFill, {})
2426
+ })]
2427
+ });
2428
+ };
2429
+ //#endregion
2430
+ //#region src/components/Gallery/GalleryUI.tsx
2431
+ var SWIPE_THRESHOLD = 50;
2432
+ var TRANSITION_DURATION = 300;
2433
+ var GalleryUI = () => {
2434
+ const { t } = useTranslationContext();
2435
+ const { closeOnBackgroundClick, currentIndex, currentItem, goToNext, goToPrevious, hasNext, hasPrevious, itemCount, onRequestClose } = useGalleryContext();
2436
+ const modalContext = useContext(ModalContext);
2437
+ const [showVideo, setShowVideo] = useState(false);
2438
+ const isTransitioningRef = useRef(false);
2439
+ const [slideOffset, setSlideOffset] = useState(0);
2440
+ const [isDragging, setIsDragging] = useState(false);
2441
+ const [slideDirection, setSlideDirection] = useState(null);
2442
+ const ignoreNextClickRef = useRef(false);
2443
+ const touchStartRef = useRef(null);
2444
+ const isVerticalSwipeRef = useRef(false);
2445
+ const containerRef = useRef(null);
2446
+ useEffect(() => {
2447
+ setShowVideo(false);
2448
+ }, [currentIndex]);
2449
+ const prevIndexRef = useRef(currentIndex);
2450
+ useEffect(() => {
2451
+ if (prevIndexRef.current === currentIndex) return;
2452
+ setSlideDirection(currentIndex > prevIndexRef.current ? "forward" : "backward");
2453
+ setSlideOffset(0);
2454
+ setIsDragging(false);
2455
+ isTransitioningRef.current = true;
2456
+ const timer = setTimeout(() => {
2457
+ setSlideDirection(null);
2458
+ isTransitioningRef.current = false;
2459
+ }, TRANSITION_DURATION);
2460
+ prevIndexRef.current = currentIndex;
2461
+ return () => clearTimeout(timer);
2462
+ }, [currentIndex]);
2463
+ const handleGoToNext = useCallback(() => {
2464
+ if (isTransitioningRef.current) return;
2465
+ goToNext();
2466
+ }, [goToNext]);
2467
+ const handleGoToPrevious = useCallback(() => {
2468
+ if (isTransitioningRef.current) return;
2469
+ goToPrevious();
2470
+ }, [goToPrevious]);
2471
+ const handleKeyDown = useCallback((event) => {
2472
+ if (event.key === "ArrowLeft") {
2473
+ event.preventDefault();
2474
+ handleGoToPrevious();
2475
+ } else if (event.key === "ArrowRight") {
2476
+ event.preventDefault();
2477
+ handleGoToNext();
2478
+ }
2479
+ }, [handleGoToNext, handleGoToPrevious]);
2480
+ useEffect(() => {
2481
+ document.addEventListener("keydown", handleKeyDown);
2482
+ return () => document.removeEventListener("keydown", handleKeyDown);
2483
+ }, [handleKeyDown]);
2484
+ const handleTouchStart = useCallback((event) => {
2485
+ if (isTransitioningRef.current) return;
2486
+ const touch = event.touches[0];
2487
+ ignoreNextClickRef.current = false;
2488
+ touchStartRef.current = {
2489
+ x: touch.clientX,
2490
+ y: touch.clientY
2491
+ };
2492
+ isVerticalSwipeRef.current = false;
2493
+ }, []);
2494
+ const handleTouchMove = useCallback((event) => {
2495
+ if (!touchStartRef.current || isTransitioningRef.current) return;
2496
+ const touch = event.touches[0];
2497
+ const deltaX = touch.clientX - touchStartRef.current.x;
2498
+ const deltaY = touch.clientY - touchStartRef.current.y;
2499
+ if (!isDragging && !isVerticalSwipeRef.current) {
2500
+ if (Math.abs(deltaY) > Math.abs(deltaX) && Math.abs(deltaY) > 10) {
2501
+ ignoreNextClickRef.current = true;
2502
+ isVerticalSwipeRef.current = true;
2503
+ return;
2504
+ }
2505
+ if (Math.abs(deltaX) > 10) {
2506
+ ignoreNextClickRef.current = true;
2507
+ setIsDragging(true);
2508
+ }
2509
+ }
2510
+ if (isVerticalSwipeRef.current) return;
2511
+ if (!hasNext && deltaX < 0 || !hasPrevious && deltaX > 0) setSlideOffset(deltaX * .3);
2512
+ else setSlideOffset(deltaX);
2513
+ }, [
2514
+ isDragging,
2515
+ hasNext,
2516
+ hasPrevious
2517
+ ]);
2518
+ const handleTouchEnd = useCallback(() => {
2519
+ if (!touchStartRef.current || isVerticalSwipeRef.current) {
2520
+ if (isVerticalSwipeRef.current) ignoreNextClickRef.current = true;
2521
+ touchStartRef.current = null;
2522
+ return;
2523
+ }
2524
+ const offset = slideOffset;
2525
+ if (isDragging || Math.abs(offset) > 10) ignoreNextClickRef.current = true;
2526
+ touchStartRef.current = null;
2527
+ if (Math.abs(offset) >= SWIPE_THRESHOLD) if (offset < 0 && hasNext) goToNext();
2528
+ else if (offset > 0 && hasPrevious) goToPrevious();
2529
+ else setSlideOffset(0);
2530
+ else setSlideOffset(0);
2531
+ setIsDragging(false);
2532
+ }, [
2533
+ slideOffset,
2534
+ hasNext,
2535
+ hasPrevious,
2536
+ goToNext,
2537
+ goToPrevious,
2538
+ isDragging
2539
+ ]);
2540
+ const requestClose = modalContext?.close ?? onRequestClose;
2541
+ const handleBackgroundClick = useCallback((event) => {
2542
+ if (event.target !== event.currentTarget) return;
2543
+ if (ignoreNextClickRef.current) {
2544
+ ignoreNextClickRef.current = false;
2545
+ return;
2546
+ }
2547
+ if (!closeOnBackgroundClick) return;
2548
+ requestClose?.();
2549
+ }, [closeOnBackgroundClick, requestClose]);
2550
+ const mediaStyle = isDragging || slideOffset !== 0 && slideDirection === null ? { transform: `translateX(${slideOffset}px)` } : {};
2551
+ return /* @__PURE__ */ jsxs("div", {
2552
+ className: "str-chat__gallery",
2553
+ children: [/* @__PURE__ */ jsxs("div", {
2554
+ className: "str-chat__gallery__main",
2555
+ children: [
2556
+ /* @__PURE__ */ jsx(GalleryHeader, { currentItem }),
2557
+ /* @__PURE__ */ jsx(NavButton, {
2558
+ "aria-label": t("Previous image"),
2559
+ className: clsx("str-chat__gallery__nav-button--prev", !hasPrevious && "str-chat__gallery__nav-button--hidden"),
2560
+ disabled: !hasPrevious,
2561
+ onClick: handleGoToPrevious,
2562
+ children: /* @__PURE__ */ jsx(IconChevronLeft, {})
2563
+ }),
2564
+ /* @__PURE__ */ jsx("div", {
2565
+ className: "str-chat__gallery__slide-container",
2566
+ onClick: handleBackgroundClick,
2567
+ onTouchEnd: handleTouchEnd,
2568
+ onTouchMove: handleTouchMove,
2569
+ onTouchStart: handleTouchStart,
2570
+ ref: containerRef,
2571
+ children: /* @__PURE__ */ jsx("div", {
2572
+ className: clsx("str-chat__gallery__media-container", {
2573
+ "str-chat__gallery__media--dragging": isDragging,
2574
+ "str-chat__gallery__media--slide-backward": !isDragging && slideDirection === "backward",
2575
+ "str-chat__gallery__media--slide-forward": !isDragging && slideDirection === "forward"
2576
+ }),
2577
+ style: mediaStyle,
2578
+ children: currentItem.videoUrl && currentItem.videoThumbnailUrl ? /* @__PURE__ */ jsx("div", {
2579
+ className: "str-chat__gallery__media str-chat__gallery__media--video",
2580
+ children: showVideo ? /* @__PURE__ */ jsx(VideoPlayer, {
2581
+ isPlaying: true,
2582
+ videoUrl: currentItem.videoUrl
2583
+ }) : /* @__PURE__ */ jsx(VideoThumbnail, {
2584
+ alt: currentItem.title ?? "",
2585
+ onPlay: () => setShowVideo(true),
2586
+ src: currentItem.videoThumbnailUrl
2587
+ })
2588
+ }) : /* @__PURE__ */ jsx("div", {
2589
+ className: "str-chat__gallery__media str-chat__gallery__media--image",
2590
+ children: /* @__PURE__ */ jsx(BaseImage, {
2591
+ alt: currentItem.alt,
2592
+ src: currentItem.imageUrl
2593
+ })
2594
+ })
2595
+ })
2596
+ }),
2597
+ /* @__PURE__ */ jsx(NavButton, {
2598
+ "aria-label": t("Next image"),
2599
+ className: clsx("str-chat__gallery__nav-button--next", !hasNext && "str-chat__gallery__nav-button--hidden"),
2600
+ disabled: !hasNext,
2601
+ onClick: handleGoToNext,
2602
+ children: /* @__PURE__ */ jsx(IconChevronRight, {})
2603
+ })
2604
+ ]
2605
+ }), itemCount > 1 && /* @__PURE__ */ jsxs("div", {
2606
+ className: "str-chat__gallery__position-indicator",
2607
+ children: [
2608
+ currentIndex + 1,
2609
+ " of ",
2610
+ itemCount
2611
+ ]
2612
+ })]
2613
+ });
2614
+ };
2615
+ var NavButton = ({ className, ...props }) => /* @__PURE__ */ jsx(Button, {
2616
+ ...props,
2617
+ className: clsx("str-chat__gallery__nav-button", className)
2618
+ });
2619
+ //#endregion
2620
+ //#region src/components/Gallery/Gallery.tsx
2621
+ var Gallery = ({ closeOnBackgroundClick = true, GalleryUI: GalleryUI$1, initialIndex = 0, items, onIndexChange, onRequestClose }) => {
2622
+ const { GalleryUI: ContextGalleryUI } = useComponentContext();
2623
+ const ResolvedGalleryUI = GalleryUI$1 ?? ContextGalleryUI ?? GalleryUI;
2624
+ const [currentIndex, setCurrentIndex] = useState(initialIndex);
2625
+ const itemCount = items.length;
2626
+ const goToIndex = useCallback((index) => {
2627
+ if (index >= 0 && index < itemCount) setCurrentIndex(index);
2628
+ }, [itemCount]);
2629
+ const goToNext = useCallback(() => {
2630
+ setCurrentIndex((prev) => prev < itemCount - 1 ? prev + 1 : prev);
2631
+ }, [itemCount]);
2632
+ const goToPrevious = useCallback(() => {
2633
+ setCurrentIndex((prev) => prev > 0 ? prev - 1 : prev);
2634
+ }, []);
2635
+ useEffect(() => {
2636
+ onIndexChange?.(currentIndex);
2637
+ }, [currentIndex, onIndexChange]);
2638
+ const hasNext = currentIndex < itemCount - 1;
2639
+ const hasPrevious = currentIndex > 0;
2640
+ const currentItem = items[currentIndex];
2641
+ const contextValue = useMemo(() => ({
2642
+ closeOnBackgroundClick,
2643
+ currentIndex,
2644
+ currentItem,
2645
+ goToIndex,
2646
+ goToNext,
2647
+ goToPrevious,
2648
+ hasNext,
2649
+ hasPrevious,
2650
+ itemCount,
2651
+ items,
2652
+ onRequestClose
2653
+ }), [
2654
+ closeOnBackgroundClick,
2655
+ currentIndex,
2656
+ currentItem,
2657
+ goToIndex,
2658
+ goToNext,
2659
+ goToPrevious,
2660
+ hasNext,
2661
+ hasPrevious,
2662
+ itemCount,
2663
+ items,
2664
+ onRequestClose
2665
+ ]);
2666
+ return /* @__PURE__ */ jsx(GalleryContext.Provider, {
2667
+ value: contextValue,
2668
+ children: /* @__PURE__ */ jsx(ResolvedGalleryUI, {})
2669
+ });
2670
+ };
2671
+ //#endregion
2672
+ //#region src/components/Form/SwitchField.tsx
2673
+ var SwitchField = ({ children, description, fieldClassName, title, ...props }) => {
2674
+ const { "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, checked, defaultChecked, disabled, id, onChange, onKeyDown, ...rest } = props;
2675
+ const generatedSwitchId = useStableId();
2676
+ const switchId = id ?? `str-chat__switch-field-${generatedSwitchId}`;
2677
+ const switchLabelId = `${switchId}-label`;
2678
+ const inputRef = useRef(null);
2679
+ const [uncontrolledChecked, setUncontrolledChecked] = useState(Boolean(defaultChecked));
2680
+ const isControlled = checked !== void 0;
2681
+ const isOn = isControlled ? checked : uncontrolledChecked;
2682
+ const isReadOnly = isControlled && onChange === void 0;
2683
+ const handleChange = (event) => {
2684
+ if (!isControlled) setUncontrolledChecked(event.target.checked);
2685
+ onChange?.(event);
2686
+ };
2687
+ const handleKeyDown = (event) => {
2688
+ onKeyDown?.(event);
2689
+ if (event.defaultPrevented || event.key !== " ") return;
2690
+ event.preventDefault();
2691
+ event.currentTarget.click();
2692
+ };
2693
+ const handleSwitchClick = (event) => {
2694
+ if (disabled || event.target === inputRef.current) return;
2695
+ inputRef.current?.click();
2696
+ };
2697
+ const childLabelId = (isValidElement(children) ? children : void 0)?.props.id;
2698
+ const resolvedAriaLabelledBy = ariaLabelledBy ?? (!ariaLabel ? title ? switchLabelId : childLabelId : void 0);
2699
+ return /* @__PURE__ */ jsxs("div", {
2700
+ className: clsx("str-chat__form__switch-field", fieldClassName, disabled && "str-chat__form__switch-field--disabled"),
2701
+ children: [title ? /* @__PURE__ */ jsx(SwitchFieldLabel, {
2702
+ description,
2703
+ htmlFor: switchId,
2704
+ id: switchLabelId,
2705
+ title
2706
+ }) : children, /* @__PURE__ */ jsx(Switch, {
2707
+ ...rest,
2708
+ "aria-label": ariaLabel,
2709
+ "aria-labelledby": resolvedAriaLabelledBy,
2710
+ checked: isOn,
2711
+ disabled,
2712
+ id: switchId,
2713
+ on: isOn,
2714
+ onChange: handleChange,
2715
+ onKeyDown: handleKeyDown,
2716
+ onSwitchClick: handleSwitchClick,
2717
+ readOnly: isReadOnly,
2718
+ switchRef: inputRef
2719
+ })]
2720
+ });
2721
+ };
2722
+ var Switch = ({ className, on, onSwitchClick, presentation, switchRef, ...props }) => /* @__PURE__ */ jsxs("div", {
2723
+ "aria-hidden": presentation ? true : void 0,
2724
+ className: clsx("str-chat__form__switch-field__switch", { "str-chat__form__switch-field__switch--on": on }),
2725
+ onClick: presentation ? void 0 : onSwitchClick,
2726
+ children: [!presentation && /* @__PURE__ */ jsx("input", {
2727
+ ...props,
2728
+ className: clsx("str-chat__form__switch-field__input", className),
2729
+ ref: switchRef,
2730
+ role: "switch",
2731
+ type: "checkbox"
2732
+ }), /* @__PURE__ */ jsx("span", { className: "str-chat__form__switch-field__switch-handle" })]
2733
+ });
2734
+ var SwitchFieldLabel = ({ asError, children, className, description, title, ...props }) => /* @__PURE__ */ jsx("label", {
2735
+ className: clsx("str-chat__form__switch-field__label", { "str-chat__form__switch-field__label--as-error": asError }, className),
2736
+ ...props,
2737
+ children: /* @__PURE__ */ jsx("div", {
2738
+ className: "str-chat__form__switch-field__label__content",
2739
+ children: title ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(SwitchFieldTitle, { children: title }), description != null && description !== "" && /* @__PURE__ */ jsx(SwitchFieldDescription, { children: description })] }) : children
2740
+ })
2741
+ });
2742
+ var SwitchFieldTitle = ({ children, className, title, ...props }) => /* @__PURE__ */ jsx("div", {
2743
+ className: clsx("str-chat__form__switch-field__label__text", className),
2744
+ ...props,
2745
+ children: children ?? title
2746
+ });
2747
+ var SwitchFieldDescription = ({ children, className, description, ...props }) => /* @__PURE__ */ jsx("div", {
2748
+ className: clsx("str-chat__form__switch-field__label__description", className),
2749
+ ...props,
2750
+ children: children ?? description
2751
+ });
2752
+ //#endregion
2753
+ //#region src/components/Form/Checkbox.tsx
2754
+ var Checkbox = ({ checked, ...props }) => /* @__PURE__ */ jsx("div", {
2755
+ ...props,
2756
+ className: clsx("str-chat__checkmark str-chat__checkbox", {
2757
+ "str-chat__checkbox--checked": checked,
2758
+ "str-chat__checkmark--checked": checked
2759
+ })
2760
+ });
2761
+ var Checkmark = Checkbox;
2762
+ //#endregion
2763
+ //#region src/components/Form/TextInput.tsx
2764
+ var TextInputIconMessageLine = ({ icon, text }) => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
2765
+ "aria-hidden": true,
2766
+ className: "str-chat__form-text-input__message-icon",
2767
+ children: icon
2768
+ }), /* @__PURE__ */ jsx("span", {
2769
+ className: "str-chat__form-text-input__message-text",
2770
+ children: text
2771
+ })] });
2772
+ var TextInputFieldMessage = (props) => {
2773
+ if (props.kind === "neutral") return /* @__PURE__ */ jsx("div", {
2774
+ className: clsx("str-chat__form-text-input__message", props.insidePlacement && "str-chat__form-text-input__message--field-message-inside"),
2775
+ id: props.id,
2776
+ children: props.text
2777
+ });
2778
+ else if (props.kind === "success") return /* @__PURE__ */ jsx("div", {
2779
+ className: clsx("str-chat__form-text-input__message", "str-chat__form-text-input__message--success", props.insidePlacement && "str-chat__form-text-input__message--field-message-inside"),
2780
+ id: props.id,
2781
+ children: /* @__PURE__ */ jsx(TextInputIconMessageLine, {
2782
+ icon: props.successMessageIcon ?? /* @__PURE__ */ jsx(IconCheckmark, {}),
2783
+ text: props.text
2784
+ })
2785
+ });
2786
+ else if (props.kind === "error") return /* @__PURE__ */ jsx("div", {
2787
+ className: clsx("str-chat__form-text-input__message", "str-chat__form-field-error", props.insidePlacement && "str-chat__form-text-input__message--field-message-inside"),
2788
+ id: props.id,
2789
+ role: "alert",
2790
+ children: /* @__PURE__ */ jsx(TextInputIconMessageLine, {
2791
+ icon: props.errorMessageIcon ?? /* @__PURE__ */ jsx(IconExclamationMark, {}),
2792
+ text: props.text
2793
+ })
2794
+ });
2795
+ return null;
2796
+ };
2797
+ var TextInput = forwardRef(function TextInput({ className, disabled, error = false, errorMessage, errorMessageIcon, fieldMessagePlacement = "outside", id: idProp, label, leading, message, successMessage, successMessageIcon, trailing, trailingText, variant = "outline", ...inputProps }, ref) {
2798
+ const autoId = useStableId();
2799
+ const id = idProp ?? autoId;
2800
+ const hasError = error && (errorMessage != null || message != null);
2801
+ const showSuccess = !hasError && successMessage != null;
2802
+ const showNeutral = !hasError && !showSuccess && message != null;
2803
+ const messageInside = fieldMessagePlacement === "inside" && (hasError || showSuccess || showNeutral);
2804
+ const messageId = hasError ? `${id}-field-error` : showSuccess || showNeutral ? `${id}-message` : void 0;
2805
+ const describedBy = [inputProps["aria-describedby"], messageId].filter((value) => !!value).join(" ");
2806
+ const fieldMessage = hasError ? /* @__PURE__ */ jsx(TextInputFieldMessage, {
2807
+ errorMessageIcon,
2808
+ id: messageId,
2809
+ insidePlacement: messageInside,
2810
+ kind: "error",
2811
+ text: errorMessage ?? message
2812
+ }) : showSuccess ? /* @__PURE__ */ jsx(TextInputFieldMessage, {
2813
+ id: messageId,
2814
+ insidePlacement: messageInside,
2815
+ kind: "success",
2816
+ successMessageIcon,
2817
+ text: successMessage
2818
+ }) : showNeutral ? /* @__PURE__ */ jsx(TextInputFieldMessage, {
2819
+ id: messageId,
2820
+ insidePlacement: messageInside,
2821
+ kind: "neutral",
2822
+ text: message
2823
+ }) : null;
2824
+ return /* @__PURE__ */ jsxs("div", {
2825
+ className: clsx("str-chat__form-text-input", error && "str-chat__form-text-input--error", showSuccess && "str-chat__form-text-input--success", disabled && "str-chat__form-text-input--disabled", messageInside && "str-chat__form-text-input--field-message-inside", className),
2826
+ children: [
2827
+ label ? /* @__PURE__ */ jsx("label", {
2828
+ className: "str-chat__form-text-input__label",
2829
+ htmlFor: id,
2830
+ children: label
2831
+ }) : null,
2832
+ /* @__PURE__ */ jsxs("div", {
2833
+ className: clsx("str-chat__form-text-input__wrapper", `str-chat__form-text-input__wrapper--${variant}`, messageInside && "str-chat__form-text-input__wrapper--field-message-inside"),
2834
+ children: [/* @__PURE__ */ jsxs("div", {
2835
+ className: "str-chat__form-text-input__control-row",
2836
+ children: [
2837
+ leading ? /* @__PURE__ */ jsx("span", {
2838
+ "aria-hidden": true,
2839
+ className: "str-chat__form-text-input__leading",
2840
+ children: leading
2841
+ }) : null,
2842
+ /* @__PURE__ */ jsx("input", {
2843
+ "aria-describedby": describedBy,
2844
+ "aria-invalid": error,
2845
+ className: "str-chat__form-text-input__input",
2846
+ disabled,
2847
+ id,
2848
+ ref,
2849
+ ...inputProps
2850
+ }),
2851
+ trailingText != null ? /* @__PURE__ */ jsx("span", {
2852
+ "aria-hidden": true,
2853
+ className: "str-chat__form-text-input__suffix",
2854
+ children: trailingText
2855
+ }) : null,
2856
+ trailing ? /* @__PURE__ */ jsx("span", {
2857
+ "aria-hidden": true,
2858
+ className: "str-chat__form-text-input__trailing",
2859
+ children: trailing
2860
+ }) : null
2861
+ ]
2862
+ }), messageInside ? fieldMessage : null]
2863
+ }),
2864
+ messageInside ? null : fieldMessage
2865
+ ]
2866
+ });
2867
+ });
2868
+ //#endregion
2869
+ //#region src/components/ListItemLayout/ListItemLayout.tsx
2870
+ var ListItemLayout = ({ ContentSlot = ListItemLayoutContent, contentClassName, description, descriptionClassName, destructive, LeadingIcon, LeadingSlot, RootElement, rootProps, selected, subtitle, subtitleClassName, title, titleClassName, TrailingIcon, TrailingSlot }) => {
2871
+ const ContainerComponent = RootElement ?? "div";
2872
+ return /* @__PURE__ */ jsx("div", {
2873
+ className: "str-chat__list-item-layout",
2874
+ children: /* @__PURE__ */ jsxs(ContainerComponent, {
2875
+ ...ContainerComponent === "button" ? { type: "button" } : void 0,
2876
+ ...rootProps,
2877
+ className: clsx("str-chat__list-item-layout__container", rootProps?.className, destructive && "str-chat__list-item-layout__container--destructive", selected && "str-chat__list-item-layout__container--selected"),
2878
+ children: [
2879
+ LeadingIcon && /* @__PURE__ */ jsx("div", {
2880
+ className: "str-chat__list-item-layout__leading-icon",
2881
+ children: /* @__PURE__ */ jsx(LeadingIcon, {})
2882
+ }),
2883
+ LeadingSlot && /* @__PURE__ */ jsx(LeadingSlot, {}),
2884
+ /* @__PURE__ */ jsx(ContentSlot, {
2885
+ className: contentClassName,
2886
+ description,
2887
+ descriptionClassName,
2888
+ subtitle,
2889
+ subtitleClassName,
2890
+ title,
2891
+ titleClassName
2892
+ }),
2893
+ TrailingIcon && /* @__PURE__ */ jsx("div", {
2894
+ className: "str-chat__list-item-layout__trailing-icon",
2895
+ children: /* @__PURE__ */ jsx(TrailingIcon, {})
2896
+ }),
2897
+ TrailingSlot && /* @__PURE__ */ jsx(TrailingSlot, {})
2898
+ ]
2899
+ })
2900
+ });
2901
+ };
2902
+ var ListItemLayoutContent = ({ className, description, descriptionClassName, subtitle, subtitleClassName, title, titleClassName, ...props }) => /* @__PURE__ */ jsxs("div", {
2903
+ ...props,
2904
+ className: clsx("str-chat__list-item-layout__content", className, {
2905
+ "str-chat__list-item-layout__content--withDescription": description,
2906
+ "str-chat__list-item-layout__content--withSubtitle": subtitle,
2907
+ "str-chat__list-item-layout__content--withTitle": title
2908
+ }),
2909
+ children: [
2910
+ title && /* @__PURE__ */ jsx("div", {
2911
+ className: clsx("str-chat__list-item-layout__title", titleClassName),
2912
+ children: title
2913
+ }),
2914
+ subtitle && /* @__PURE__ */ jsx("div", {
2915
+ className: clsx("str-chat__list-item-layout__subtitle", subtitleClassName),
2916
+ children: subtitle
2917
+ }),
2918
+ description && /* @__PURE__ */ jsx("div", {
2919
+ className: clsx("str-chat__list-item-layout__description", descriptionClassName),
2920
+ children: description
2921
+ })
2922
+ ]
2923
+ });
2924
+ //#endregion
2925
+ //#region src/components/ChannelListItem/utils.tsx
2926
+ var remarkPlugins = [
2927
+ htmlToTextPlugin,
2928
+ [remarkGfm, { singleTilde: false }],
2929
+ plusPlusToEmphasis,
2930
+ imageToLink
2931
+ ];
2932
+ var renderPreviewText = (text) => /* @__PURE__ */ jsx(ReactMarkdown, {
2933
+ remarkPlugins,
2934
+ skipHtml: true,
2935
+ children: text
2936
+ });
2937
+ var getLatestPollVote = (latestVotesByOption) => {
2938
+ let latestVote;
2939
+ for (const optionVotes of Object.values(latestVotesByOption)) optionVotes.forEach((vote) => {
2940
+ if (latestVote && new Date(latestVote.updated_at) >= new Date(vote.created_at)) return;
2941
+ latestVote = vote;
2942
+ });
2943
+ return latestVote;
2944
+ };
2945
+ var getLatestMessagePreview = (channel, t, userLanguage = "en", isMessageAIGenerated) => {
2946
+ const latestMessage = channel.state.latestMessages[channel.state.latestMessages.length - 1];
2947
+ const previewTextToRender = getTranslatedMessageText({
2948
+ language: userLanguage,
2949
+ message: latestMessage
2950
+ }) || latestMessage?.text;
2951
+ const poll = latestMessage?.poll;
2952
+ if (!latestMessage) return t("Nothing yet...");
2953
+ if (isMessageDeleted(latestMessage)) return t("Message deleted");
2954
+ if (poll) if (!poll.vote_count) return t("📊 {{createdBy}} created: {{ pollName}}", {
2955
+ createdBy: poll.created_by?.id === channel.getClient().userID ? t("You") : poll.created_by?.name ?? t("Poll"),
2956
+ pollName: poll.name
2957
+ });
2958
+ else {
2959
+ const latestVote = getLatestPollVote(poll.latest_votes_by_option);
2960
+ const option = latestVote && poll.options.find((opt) => opt.id === latestVote.option_id);
2961
+ if (option && latestVote) return t("📊 {{votedBy}} voted: {{pollOptionText}}", {
2962
+ pollOptionText: option.text,
2963
+ votedBy: latestVote?.user?.id === channel.getClient().userID ? t("You") : latestVote.user?.name ?? t("Poll")
2964
+ });
2965
+ }
2966
+ if (previewTextToRender) return isMessageAIGenerated?.(latestMessage) ? previewTextToRender : renderPreviewText(previewTextToRender);
2967
+ if (latestMessage.command) return `/${latestMessage.command}`;
2968
+ if (latestMessage.attachments?.length) return t("🏙 Attachment...");
2969
+ if (latestMessage.shared_location) return t("📍Shared location");
2970
+ return t("Empty message...");
2971
+ };
2972
+ /**
2973
+ * Channel display image: channel.data.image, or for DM (2 members) the other member's user.image.
2974
+ */
2975
+ var getChannelDisplayImage = (channel, currentUserId) => {
2976
+ const data = channel.data;
2977
+ if (data?.image && typeof data.image === "string") return data.image;
2978
+ const memberList = Object.values(channel.state.members);
2979
+ if (memberList.length === 2) {
2980
+ const image = memberList.find((m) => m.user?.id !== currentUserId)?.user?.image;
2981
+ if (image && typeof image === "string") return image;
2982
+ }
2983
+ };
2984
+ var getGroupChannelDisplayInfo = (channel) => {
2985
+ const members = Object.values(channel.state.members);
2986
+ if (members.length <= 2) return;
2987
+ const memberList = [];
2988
+ for (const member of members) {
2989
+ const { user } = member;
2990
+ if (!user?.name && !user?.image) continue;
2991
+ memberList.push({
2992
+ imageUrl: user.image,
2993
+ userName: user.name
2994
+ });
2995
+ }
2996
+ return { members: memberList };
2997
+ };
2998
+ //#endregion
2999
+ //#region src/components/ChannelListItem/hooks/useChannelDisplayName.ts
3000
+ /**
3001
+ * 1. channel.data.name
3002
+ * 2. DM (exactly 2 members): other member's name, then directMessageLabel
3003
+ * 3. Group (3+ members): comma-separated list of 2 other members' names (no ellipsis)
3004
+ * 4. undefined otherwise
3005
+ */
3006
+ function computeChannelDisplayName(channel, directMessageLabel, currentUserId) {
3007
+ const data = channel.data;
3008
+ if (data?.name && typeof data.name === "string") return data.name;
3009
+ const memberList = Object.values(channel.state.members);
3010
+ const otherMembers = memberList.filter((m) => m.user?.id !== currentUserId);
3011
+ if (memberList.length === 2 && otherMembers.length === 1) return otherMembers[0].user?.name || directMessageLabel;
3012
+ if (otherMembers.length >= 2) {
3013
+ const names = otherMembers.map((m) => m.user?.name).filter(Boolean).slice(0, 2);
3014
+ if (names.length > 0) return names.join(", ");
3015
+ }
3016
+ }
3017
+ /**
3018
+ * Channel display name with translation context.
3019
+ * 1. channel.data.name
3020
+ * 2. DM (exactly 2 members): other member's name, then translated "Direct message"
3021
+ * 3. Group (3+ members): comma-separated list of 2 other members' names (no ellipsis)
3022
+ * 4. undefined otherwise
3023
+ */
3024
+ var useChannelDisplayName = (channel) => {
3025
+ const { client } = useChatContext("useChannelDisplayName");
3026
+ const { t } = useTranslationContext("useChannelDisplayName");
3027
+ const directMessageLabel = t("Direct message");
3028
+ const [displayName, setDisplayName] = useState(() => channel ? computeChannelDisplayName(channel, directMessageLabel, client.userID ?? void 0) : void 0);
3029
+ useEffect(() => {
3030
+ if (!channel) {
3031
+ setDisplayName(void 0);
3032
+ return;
3033
+ }
3034
+ const updateDisplayName = () => setDisplayName(computeChannelDisplayName(channel, directMessageLabel, client.userID ?? void 0));
3035
+ updateDisplayName();
3036
+ client.on("user.updated", updateDisplayName);
3037
+ return () => {
3038
+ client.off("user.updated", updateDisplayName);
3039
+ };
3040
+ }, [
3041
+ channel,
3042
+ channel?.data,
3043
+ client,
3044
+ directMessageLabel
3045
+ ]);
3046
+ return displayName;
3047
+ };
3048
+ //#endregion
3049
+ //#region src/components/ChannelListItem/hooks/useChannelPreviewInfo.ts
3050
+ var emptyGroupInfo = {
3051
+ members: [],
3052
+ overflowCount: void 0
3053
+ };
3054
+ var useChannelPreviewInfo = (props) => {
3055
+ const { channel, overrideImage, overrideTitle } = props;
3056
+ const { client } = useChatContext();
3057
+ const channelDisplayName = useChannelDisplayName(channel);
3058
+ const displayTitle = overrideTitle ?? channelDisplayName;
3059
+ const [displayImage, setDisplayImage] = useState(() => channel ? overrideImage ?? getChannelDisplayImage(channel, client.userID ?? void 0) : void 0);
3060
+ const [groupChannelDisplayInfo, setGroupChannelDisplayInfo] = useState(() => channel ? getGroupChannelDisplayInfo(channel) ?? emptyGroupInfo : emptyGroupInfo);
3061
+ useEffect(() => {
3062
+ if (!channel) return;
3063
+ if (overrideImage) return;
3064
+ const updateInfo = () => {
3065
+ setDisplayImage(getChannelDisplayImage(channel, client.userID ?? void 0));
3066
+ setGroupChannelDisplayInfo(getGroupChannelDisplayInfo(channel) ?? emptyGroupInfo);
3067
+ };
3068
+ updateInfo();
3069
+ const { unsubscribe: unsubscribeChannelUpdated } = channel.on("channel.updated", updateInfo);
3070
+ const { unsubscribe: unsubscribeUserUpdated } = client.on("user.updated", updateInfo);
3071
+ return () => {
3072
+ unsubscribeChannelUpdated();
3073
+ unsubscribeUserUpdated();
3074
+ };
3075
+ }, [
3076
+ channel,
3077
+ channel?.data,
3078
+ client,
3079
+ overrideImage
3080
+ ]);
3081
+ return useMemo(() => ({
3082
+ displayImage: overrideImage ?? displayImage,
3083
+ displayTitle,
3084
+ groupChannelDisplayInfo
3085
+ }), [
3086
+ displayImage,
3087
+ displayTitle,
3088
+ groupChannelDisplayInfo,
3089
+ overrideImage
3090
+ ]);
3091
+ };
3092
+ //#endregion
3093
+ //#region src/utils/isDmChannel.ts
3094
+ var isDmChannel = ({ channel, ownUserId }) => {
3095
+ const memberCount = channel.data?.member_count ?? 0;
3096
+ return memberCount === 1 || memberCount === 2 && !!ownUserId && Object.values(channel.state?.members ?? {}).some(({ user }) => user?.id === ownUserId);
3097
+ };
3098
+ //#endregion
3099
+ //#region src/components/ChannelList/hooks/useSelectedChannelState.ts
3100
+ var noop = () => {};
3101
+ function useSelectedChannelState({ channel, selector, stateChangeEventKeys = ["all"] }) {
3102
+ return useSyncExternalStore(useCallback((onStoreChange) => {
3103
+ if (!channel) return noop;
3104
+ const subscriptions = stateChangeEventKeys.map((et) => channel.on(et, () => {
3105
+ onStoreChange(selector(channel));
3106
+ }));
3107
+ return () => subscriptions.forEach((subscription) => subscription.unsubscribe());
3108
+ }, [
3109
+ channel,
3110
+ selector,
3111
+ stateChangeEventKeys
3112
+ ]), useCallback(() => {
3113
+ if (!channel) return void 0;
3114
+ return selector(channel);
3115
+ }, [channel, selector]));
3116
+ }
3117
+ //#endregion
3118
+ //#region src/components/ChannelList/hooks/useChannelMembershipState.ts
3119
+ var selector = (c) => c.state.membership;
3120
+ var keys = ["member.updated"];
3121
+ function useChannelMembershipState(channel) {
3122
+ return useSelectedChannelState({
3123
+ channel,
3124
+ selector,
3125
+ stateChangeEventKeys: keys
3126
+ });
3127
+ }
3128
+ //#endregion
3129
+ //#region src/components/ChannelListItem/hooks/useIsChannelMuted.ts
3130
+ var useIsChannelMuted = (channel) => {
3131
+ const { client } = useChatContext("useIsChannelMuted");
3132
+ const [muted, setMuted] = useState(channel.muteStatus());
3133
+ useEffect(() => {
3134
+ const handleEvent = () => setMuted(channel.muteStatus());
3135
+ client.on("notification.channel_mutes_updated", handleEvent);
3136
+ return () => client.off("notification.channel_mutes_updated", handleEvent);
3137
+ }, [muted]);
3138
+ return muted;
3139
+ };
3140
+ //#endregion
3141
+ //#region src/components/ChannelListItem/hooks/useIsUserMuted.ts
3142
+ var useIsUserMuted = (targetUserId) => {
3143
+ const { mutes } = useChatContext();
3144
+ return useMemo(() => !!targetUserId && mutes.some((mute) => mute.target.id === targetUserId), [mutes, targetUserId]);
3145
+ };
3146
+ //#endregion
3147
+ //#region src/components/Notifications/Notification.tsx
3148
+ var IconsBySeverity = {
3149
+ error: IconExclamationMark,
3150
+ info: null,
3151
+ loading: IconRefresh,
3152
+ success: IconCheckmark,
3153
+ warning: IconExclamationTriangleFill
3154
+ };
3155
+ var DefaultNotificationIcon = ({ notification }) => {
3156
+ if (!notification.severity) return null;
3157
+ const Icon = IconsBySeverity[notification.severity] ?? null;
3158
+ return Icon && /* @__PURE__ */ jsx("div", {
3159
+ className: "str-chat__notification-icon",
3160
+ children: /* @__PURE__ */ jsx(Icon, {})
3161
+ });
3162
+ };
3163
+ var Notification = forwardRef(({ className, entryDirection, Icon = DefaultNotificationIcon, notification, onDismiss, showClose = false, transitionState }, ref) => {
3164
+ const { removeNotification } = useNotificationApi();
3165
+ const { t } = useTranslationContext();
3166
+ const displayMessage = t("translationBuilderTopic/notification", {
3167
+ notification,
3168
+ value: notification.message
3169
+ });
3170
+ const handleDismiss = () => {
3171
+ if (onDismiss) {
3172
+ onDismiss();
3173
+ return;
3174
+ }
3175
+ removeNotification(notification.id);
3176
+ };
3177
+ const isPersistent = !notification.duration;
3178
+ const severity = notification.severity;
3179
+ const livePriority = severity === "error" ? "assertive" : "polite";
3180
+ return /* @__PURE__ */ jsxs("div", {
3181
+ className: clsx("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),
3182
+ "data-testid": "notification",
3183
+ ref,
3184
+ children: [
3185
+ /* @__PURE__ */ jsxs("div", {
3186
+ className: "str-chat__notification-content",
3187
+ children: [Icon && /* @__PURE__ */ jsx(Icon, { notification }), /* @__PURE__ */ jsx("div", {
3188
+ "aria-atomic": "true",
3189
+ "aria-live": livePriority,
3190
+ className: "str-chat__notification-message",
3191
+ role: livePriority === "assertive" ? "alert" : "status",
3192
+ children: displayMessage
3193
+ })]
3194
+ }),
3195
+ notification.actions && notification.actions.length > 0 && /* @__PURE__ */ jsx("div", {
3196
+ className: "str-chat__notification-actions",
3197
+ children: notification.actions.map((action, index) => /* @__PURE__ */ jsx(Button, {
3198
+ appearance: "outline",
3199
+ className: "str-chat__notification-action",
3200
+ inverseTheme: true,
3201
+ onClick: () => {
3202
+ action.handler();
3203
+ },
3204
+ size: "sm",
3205
+ variant: "secondary",
3206
+ children: action.label
3207
+ }, index))
3208
+ }),
3209
+ (showClose || isPersistent) && /* @__PURE__ */ jsx(Button, {
3210
+ appearance: "ghost",
3211
+ "aria-label": t("aria/Dismiss notification"),
3212
+ circular: true,
3213
+ className: "str-chat__notification-close-button",
3214
+ inverseTheme: true,
3215
+ onClick: handleDismiss,
3216
+ size: "sm",
3217
+ variant: "secondary",
3218
+ children: /* @__PURE__ */ jsx(IconXmark, {})
3219
+ })
3220
+ ]
3221
+ });
3222
+ });
3223
+ Notification.displayName = "Notification";
3224
+ //#endregion
3225
+ //#region src/components/Notifications/NotificationList.tsx
3226
+ var ENTER_TRANSLATION = {
3227
+ bottom: {
3228
+ x: "0%",
3229
+ y: "100%"
3230
+ },
3231
+ left: {
3232
+ x: "-100%",
3233
+ y: "0%"
3234
+ },
3235
+ right: {
3236
+ x: "100%",
3237
+ y: "0%"
3238
+ },
3239
+ top: {
3240
+ x: "0%",
3241
+ y: "-100%"
3242
+ }
3243
+ };
3244
+ var EXIT_ANIMATION_MS = 340;
3245
+ var DEFAULT_MIN_DISPLAY_MS = 1e3;
3246
+ var isEnterFrom = (value) => value === "bottom" || value === "left" || value === "right" || value === "top";
3247
+ var getNotificationEnterFrom = (notification, fallbackEnterFrom) => {
3248
+ if (!notification) return fallbackEnterFrom;
3249
+ const metadataEnterFrom = notification.metadata?.entryDirection;
3250
+ if (isEnterFrom(metadataEnterFrom)) return metadataEnterFrom;
3251
+ const originEnterFrom = notification.origin.context?.entryDirection;
3252
+ if (isEnterFrom(originEnterFrom)) return originEnterFrom;
3253
+ return fallbackEnterFrom;
3254
+ };
3255
+ var isPersistent = (notification) => !notification.duration;
3256
+ var haveSameType = (a, b) => !!a?.type && !!b?.type && a.type === b.type;
3257
+ /** FIFO queue selector — oldest `createdAt` other than `displayed` wins. */
3258
+ var pickOldest = (notifications, displayed) => {
3259
+ const excludeId = displayed?.id ?? null;
3260
+ let oldest = null;
3261
+ for (const notification of notifications) {
3262
+ if (notification.id === excludeId) continue;
3263
+ if (!oldest || notification.createdAt < oldest.createdAt) oldest = notification;
3264
+ }
3265
+ return oldest;
3266
+ };
3267
+ /** LIFO queue selector — newest `createdAt` other than `displayed` wins. */
3268
+ var pickNewest = (notifications, displayed) => {
3269
+ const excludeId = displayed?.id ?? null;
3270
+ let newest = null;
3271
+ for (const notification of notifications) {
3272
+ if (notification.id === excludeId) continue;
3273
+ if (!newest || notification.createdAt > newest.createdAt) newest = notification;
3274
+ }
3275
+ return newest;
3276
+ };
3277
+ var pickNewestPersistent = (notifications, excludeId) => {
3278
+ let newest = null;
3279
+ for (const notification of notifications) {
3280
+ if (notification.id === excludeId) continue;
3281
+ if (!isPersistent(notification)) continue;
3282
+ if (!newest || notification.createdAt > newest.createdAt) newest = notification;
3283
+ }
3284
+ return newest;
3285
+ };
3286
+ var pickNewestOfType = (notifications, type, excludeId) => {
3287
+ if (!type) return null;
3288
+ let newest = null;
3289
+ for (const notification of notifications) {
3290
+ if (notification.id === excludeId) continue;
3291
+ if (notification.type !== type) continue;
3292
+ if (!newest || notification.createdAt > newest.createdAt) newest = notification;
3293
+ }
3294
+ return newest;
3295
+ };
3296
+ /**
3297
+ * Builds the default `PickNextNotification` selector with a configurable queue fallback.
3298
+ * Encodes the snackbar concurrency rules from the design spec — the scheduling effect
3299
+ * only decides *when* to swap, the returned function decides *what* to swap to.
3300
+ *
3301
+ * Rules, in order of precedence:
3302
+ * 1. **Persistent wins.** Persistent variants (no `duration`) jump ahead of any
3303
+ * queued transient because they carry an action the user must acknowledge.
3304
+ * 2. **Persistent ↛ replaced by transient.** While a persistent is displayed it stays
3305
+ * put until dismissed externally or a *newer* persistent arrives.
3306
+ * 3. **Same-type refresh.** While a transient is displayed, a repeated trigger of the
3307
+ * same `type` collapses to its latest occurrence. (The scheduling effect detects
3308
+ * this via `haveSameType` and bypasses the dwell window so the refresh feels
3309
+ * instant; this function just makes sure the latest same-type is returned.)
3310
+ * 4. **Queue fallback.** Otherwise, `pickFromQueue` selects the next notification
3311
+ * (default: `pickOldest` / FIFO).
3312
+ *
3313
+ * Exported so callers that want to layer behavior on top of the design rules can wrap
3314
+ * the result instead of rewriting it.
3315
+ */
3316
+ var createDefaultPickNext = (pickFromQueue = pickOldest) => (notifications, displayed) => {
3317
+ if (notifications.length === 0) return null;
3318
+ const newestPersistent = pickNewestPersistent(notifications, null);
3319
+ if (!displayed) return newestPersistent ?? pickFromQueue(notifications, null);
3320
+ if (!notifications.some(({ id }) => id === displayed.id)) return newestPersistent ?? pickFromQueue(notifications, null);
3321
+ if (isPersistent(displayed)) {
3322
+ const newerPersistent = newestPersistent && newestPersistent.id !== displayed.id ? newestPersistent : pickNewestPersistent(notifications, displayed.id);
3323
+ if (newerPersistent && newerPersistent.createdAt > displayed.createdAt) return newerPersistent;
3324
+ return displayed;
3325
+ }
3326
+ const sameTypeNewest = pickNewestOfType(notifications, displayed.type, displayed.id);
3327
+ if (sameTypeNewest) return sameTypeNewest;
3328
+ if (newestPersistent) return newestPersistent;
3329
+ return pickFromQueue(notifications, displayed) ?? displayed;
3330
+ };
3331
+ /** Default selector — design-spec rules with FIFO queue fallback (`pickOldest`). */
3332
+ var defaultPickNext = createDefaultPickNext(pickOldest);
3333
+ var NotificationList = ({ className, enterFrom = "bottom", fallbackPanel, filter, minDisplayMs = DEFAULT_MIN_DISPLAY_MS, panel, pickNext = defaultPickNext, verticalAlignment = "bottom" }) => {
3334
+ const { Notification: NotificationComponent = Notification } = useComponentContext();
3335
+ const { t } = useTranslationContext();
3336
+ const { removeNotification, startNotificationTimeout } = useNotificationApi();
3337
+ const exitTimeoutRef = useRef(null);
3338
+ const replacementTimeoutRef = useRef(null);
3339
+ const candidateRef = useRef(null);
3340
+ const displayedAtRef = useRef(null);
3341
+ const listRef = useRef(null);
3342
+ const observedElementRef = useRef(null);
3343
+ const startedTimeoutIdsRef = useRef(null);
3344
+ if (!startedTimeoutIdsRef.current) startedTimeoutIdsRef.current = /* @__PURE__ */ new Set();
3345
+ const [displayedNotification, setDisplayedNotification] = useState(null);
3346
+ const [transitionState, setTransitionState] = useState("enter");
3347
+ const notifications = useNotifications({
3348
+ applyDisplayFilter: true,
3349
+ fallbackPanel,
3350
+ filter: useCallback((notification) => {
3351
+ if (hasSystemNotificationTag(notification)) return false;
3352
+ return filter ? filter(notification) : true;
3353
+ }, [filter]),
3354
+ panel
3355
+ });
3356
+ const dismiss = useCallback((id) => {
3357
+ startedTimeoutIdsRef.current?.delete(id);
3358
+ removeNotification(id);
3359
+ }, [removeNotification]);
3360
+ useEffect(() => {
3361
+ const notificationIds = new Set(notifications.map(({ id }) => id));
3362
+ startedTimeoutIdsRef.current?.forEach((id) => {
3363
+ if (!notificationIds.has(id)) startedTimeoutIdsRef.current?.delete(id);
3364
+ });
3365
+ }, [notifications]);
3366
+ const clearReplacementTimeout = useCallback(() => {
3367
+ if (replacementTimeoutRef.current !== null) {
3368
+ window.clearTimeout(replacementTimeoutRef.current);
3369
+ replacementTimeoutRef.current = null;
3370
+ }
3371
+ }, []);
3372
+ useEffect(() => () => {
3373
+ clearReplacementTimeout();
3374
+ if (exitTimeoutRef.current !== null) {
3375
+ window.clearTimeout(exitTimeoutRef.current);
3376
+ exitTimeoutRef.current = null;
3377
+ }
3378
+ }, [clearReplacementTimeout]);
3379
+ useEffect(() => {
3380
+ candidateRef.current = pickNext(notifications, displayedNotification);
3381
+ }, [
3382
+ displayedNotification,
3383
+ notifications,
3384
+ pickNext
3385
+ ]);
3386
+ useEffect(() => {
3387
+ if (transitionState === "exit") return;
3388
+ if (!displayedNotification) {
3389
+ const candidate = pickNext(notifications, null);
3390
+ if (candidate) {
3391
+ displayedAtRef.current = Date.now();
3392
+ setDisplayedNotification(candidate);
3393
+ setTransitionState("enter");
3394
+ }
3395
+ return;
3396
+ }
3397
+ const candidate = pickNext(notifications, displayedNotification);
3398
+ if (!candidate) {
3399
+ clearReplacementTimeout();
3400
+ setTransitionState("exit");
3401
+ exitTimeoutRef.current = window.setTimeout(() => {
3402
+ exitTimeoutRef.current = null;
3403
+ displayedAtRef.current = null;
3404
+ setDisplayedNotification(null);
3405
+ setTransitionState("enter");
3406
+ }, EXIT_ANIMATION_MS);
3407
+ return;
3408
+ }
3409
+ if (candidate.id === displayedNotification.id) {
3410
+ clearReplacementTimeout();
3411
+ return;
3412
+ }
3413
+ const displayedInStore = notifications.some(({ id }) => id === displayedNotification.id);
3414
+ const startSwap = () => {
3415
+ replacementTimeoutRef.current = null;
3416
+ setTransitionState("exit");
3417
+ const previousId = displayedNotification.id;
3418
+ const wasInStore = displayedInStore;
3419
+ exitTimeoutRef.current = window.setTimeout(() => {
3420
+ exitTimeoutRef.current = null;
3421
+ if (wasInStore) {
3422
+ startedTimeoutIdsRef.current?.delete(previousId);
3423
+ removeNotification(previousId);
3424
+ }
3425
+ const next = candidateRef.current;
3426
+ if (next && next.id !== previousId) {
3427
+ displayedAtRef.current = Date.now();
3428
+ setDisplayedNotification(next);
3429
+ setTransitionState("enter");
3430
+ } else {
3431
+ displayedAtRef.current = null;
3432
+ setDisplayedNotification(null);
3433
+ setTransitionState("enter");
3434
+ }
3435
+ }, EXIT_ANIMATION_MS);
3436
+ };
3437
+ if (!displayedInStore) {
3438
+ clearReplacementTimeout();
3439
+ startSwap();
3440
+ return;
3441
+ }
3442
+ if (haveSameType(displayedNotification, candidate)) {
3443
+ clearReplacementTimeout();
3444
+ startSwap();
3445
+ return;
3446
+ }
3447
+ const elapsed = displayedAtRef.current ? Date.now() - displayedAtRef.current : 0;
3448
+ const remaining = Math.max(0, minDisplayMs - elapsed);
3449
+ if (remaining === 0) {
3450
+ clearReplacementTimeout();
3451
+ startSwap();
3452
+ } else if (replacementTimeoutRef.current === null) replacementTimeoutRef.current = window.setTimeout(startSwap, remaining);
3453
+ }, [
3454
+ clearReplacementTimeout,
3455
+ displayedNotification,
3456
+ minDisplayMs,
3457
+ notifications,
3458
+ pickNext,
3459
+ removeNotification,
3460
+ transitionState
3461
+ ]);
3462
+ const notification = displayedNotification;
3463
+ const notificationEnterFrom = getNotificationEnterFrom(notification, enterFrom);
3464
+ useEffect(() => {
3465
+ const element = observedElementRef.current;
3466
+ if (!element || !notification || transitionState === "exit") return;
3467
+ const startTimeout = () => {
3468
+ if (!startedTimeoutIdsRef.current || startedTimeoutIdsRef.current.has(notification.id)) return;
3469
+ startedTimeoutIdsRef.current.add(notification.id);
3470
+ startNotificationTimeout(notification.id);
3471
+ };
3472
+ if (typeof IntersectionObserver === "undefined") {
3473
+ startTimeout();
3474
+ return;
3475
+ }
3476
+ const observer = new IntersectionObserver((entries) => {
3477
+ const [entry] = entries;
3478
+ if (!entry?.isIntersecting) return;
3479
+ startTimeout();
3480
+ observer.disconnect();
3481
+ }, {
3482
+ root: listRef.current,
3483
+ threshold: .5
3484
+ });
3485
+ observer.observe(element);
3486
+ return () => {
3487
+ observer.disconnect();
3488
+ };
3489
+ }, [
3490
+ notification,
3491
+ startNotificationTimeout,
3492
+ transitionState
3493
+ ]);
3494
+ if (!notification) return null;
3495
+ return /* @__PURE__ */ jsxs("div", {
3496
+ "aria-label": t("aria/Notifications"),
3497
+ className: clsx("str-chat__notification-list", `str-chat__notification-list--enter-from-${notificationEnterFrom}`, `str-chat__notification-list--position-${verticalAlignment}`, panel && `str-chat__notification-list--${panel}`, className),
3498
+ "data-testid": "notification-list",
3499
+ ref: listRef,
3500
+ role: "region",
3501
+ style: {
3502
+ "--str-chat__notification-list-enter-x": ENTER_TRANSLATION[notificationEnterFrom].x,
3503
+ "--str-chat__notification-list-enter-y": ENTER_TRANSLATION[notificationEnterFrom].y
3504
+ },
3505
+ children: [
3506
+ /* @__PURE__ */ jsx("div", {
3507
+ "aria-hidden": true,
3508
+ className: "str-chat__notification-list__edge str-chat__notification-list__edge--top"
3509
+ }),
3510
+ /* @__PURE__ */ jsx(NotificationComponent, {
3511
+ entryDirection: notificationEnterFrom,
3512
+ notification,
3513
+ onDismiss: () => dismiss(notification.id),
3514
+ ref: (element) => {
3515
+ observedElementRef.current = element;
3516
+ },
3517
+ showClose: !notification.duration,
3518
+ transitionState
3519
+ }, notification.id),
3520
+ /* @__PURE__ */ jsx("div", {
3521
+ "aria-hidden": true,
3522
+ className: "str-chat__notification-list__edge str-chat__notification-list__edge--bottom"
3523
+ })
3524
+ ]
3525
+ });
3526
+ };
3527
+ //#endregion
3528
+ //#region src/components/ChannelHeader/hooks/useChannelHasMembersOnline.ts
3529
+ var getOtherWatchers = (watchers, ownUserId) => {
3530
+ const next = Object.assign({}, watchers ?? {});
3531
+ if (ownUserId) delete next[ownUserId];
3532
+ return next;
3533
+ };
3534
+ var useChannelHasMembersOnline = ({ channel: channelOverride, enabled = true } = {}) => {
3535
+ const { channel: contextChannel } = useChannelStateContext();
3536
+ const { client } = useChatContext();
3537
+ const channel = channelOverride ?? contextChannel;
3538
+ const ownUserId = client.user?.id;
3539
+ const [watchers, setWatchers] = useState(() => getOtherWatchers(channel?.state?.watchers, ownUserId));
3540
+ useEffect(() => {
3541
+ setWatchers(getOtherWatchers(channel?.state?.watchers, ownUserId));
3542
+ }, [channel, ownUserId]);
3543
+ useEffect(() => {
3544
+ if (!enabled || !channel) return;
3545
+ const startSubscription = channel.on("user.watching.start", (event) => {
3546
+ setWatchers((prev) => {
3547
+ if (!event.user?.id || event.user.id === ownUserId) return prev;
3548
+ if (prev[event.user.id]) return prev;
3549
+ return Object.assign({ [event.user.id]: event.user }, prev);
3550
+ });
3551
+ });
3552
+ const stopSubscription = channel.on("user.watching.stop", (event) => {
3553
+ setWatchers((prev) => {
3554
+ if (!event.user?.id || !prev[event.user.id]) return prev;
3555
+ const next = Object.assign({}, prev);
3556
+ delete next[event.user.id];
3557
+ return next;
3558
+ });
3559
+ });
3560
+ return () => {
3561
+ startSubscription.unsubscribe();
3562
+ stopSubscription.unsubscribe();
3563
+ };
3564
+ }, [
3565
+ channel,
3566
+ enabled,
3567
+ ownUserId
3568
+ ]);
3569
+ if (!enabled) return false;
3570
+ return Object.keys(watchers).length > 0;
3571
+ };
3572
+ //#endregion
3573
+ //#region src/components/ChannelHeader/hooks/useChannelHeaderOnlineStatus.ts
3574
+ /**
3575
+ * Returns the channel header online status text (e.g. "Online", "Offline", or "X members, Y online").
3576
+ * Returns null when the channel has no members (nothing to show).
3577
+ */
3578
+ function useChannelHeaderOnlineStatus({ channel: channelOverride, watcherCount: watcherCountOverride } = {}) {
3579
+ const { t } = useTranslationContext();
3580
+ const { client } = useChatContext();
3581
+ const { channel: contextChannel, watcherCount: contextWatcherCount = 0 } = useChannelStateContext();
3582
+ const channel = channelOverride ?? contextChannel;
3583
+ const watcherCount = watcherCountOverride ?? contextWatcherCount;
3584
+ const { member_count: memberCount = 0 } = channel?.data || {};
3585
+ const isDirectMessagingChannel = isDmChannel({
3586
+ channel,
3587
+ ownUserId: client.user?.id
3588
+ });
3589
+ const hasMembersOnline = useChannelHasMembersOnline({
3590
+ channel,
3591
+ enabled: isDirectMessagingChannel
3592
+ });
3593
+ if (!memberCount) return null;
3594
+ if (isDirectMessagingChannel) return hasMembersOnline ? t("Online") : t("Offline");
3595
+ return `${t("{{ memberCount }} members", { memberCount })} · ${t("{{ watcherCount }} online", { watcherCount })}`;
3596
+ }
3597
+ //#endregion
3598
+ export { ContextMenuBackButton as $, GalleryUI as A, MessageTranslationViewContext as At, FileIcon as B, Checkmark as C, LoadingIndicator as Ct, SwitchFieldLabel as D, ModalContext as Dt, SwitchFieldDescription as E, useNotificationConfigurationContext as Et, useGalleryContext as F, MessageProvider as Ft, Timestamp as G, FILE_ICON_NO_LABEL_CLASSNAME as H, toBaseImageDescriptors as I, useMessageContext as It, htmlToTextPlugin as J, plusPlusToEmphasis as K, BaseImage as L, VideoPlayer as M, getTranslatedMessageText as Mt, GalleryContext as N, useMessageTranslationViewContext as Nt, SwitchFieldTitle as O, ModalContextProvider as Ot, toGalleryItemDescriptors as P, MessageContext as Pt, ContextMenu as Q, ImagePlaceholder as R, Checkbox as S, FileSizeIndicator as St, SwitchField as T, NotificationConfigurationProvider as Tt, DownloadButton as U, FILE_ICON_GRAPHIC_CLASSNAME as V, MessageTimestamp as W, Prompt as X, GlobalModal as Y, BaseContextMenuButton as Z, getLatestMessagePreview as _, Alert as _t, pickNewest as a, DEFAULT_CONTEXT_MENU_KEYBOARD_NAVIGATION_ITEM_SELECTOR as at, ListItemLayoutContent as b, useAriaIdentifiers as bt, useIsUserMuted as c, useContextMenuContext as ct, useSelectedChannelState as d, ChannelAvatar as dt, ContextMenuBody as et, isDmChannel as f, Badge as ft, getGroupChannelDisplayInfo as g, useDialogAnchor as gt, getChannelDisplayImage as h, DialogAnchor as ht, createDefaultPickNext as i, ContextMenuRoot as it, VideoThumbnail as j, MessageTranslationViewProvider as jt, Gallery as k, useModalContext as kt, useIsChannelMuted as l, createRovingFocusKeyDownHandler as lt, useChannelDisplayName as m, Avatar as mt, useChannelHasMembersOnline as n, ContextMenuContent as nt, pickOldest as o, EmojiContextMenuButton as ot, useChannelPreviewInfo as p, ErrorBadge as pt, imageToLink as q, NotificationList as r, ContextMenuHeader as rt, Notification as s, UserContextMenuButton as st, useChannelHeaderOnlineStatus as t, ContextMenuButton as tt, useChannelMembershipState as u, GroupAvatar as ut, renderPreviewText as v, Header as vt, Switch as w, useNotifications as wt, TextInput as x, useStableCallback as xt, ListItemLayout as y, Root as yt, formatTime as z };
3599
+
3600
+ //# sourceMappingURL=useChannelHeaderOnlineStatus.c5215b13.mjs.map