UNPKG

@cometchat/chat-uikit-react

Version:

Ready-to-use Chat UI Components for React

1,173 lines (1,168 loc) 581 kB
import './index.css'; export { CometChatToolCallResultBubble } from './chunk-Z3ROIO5Q.js'; import { CometChatReactionsOverflow, CometChatReactionsInfo, CometChatReactionsChip, CometChatReactionsBar, CometChatReactionsRoot } from './chunk-TIYHRPEL.js'; export { CometChatReactionList, useCometChatReactionList, useCometChatReactionListContext, useCometChatReactionsContext } from './chunk-TIYHRPEL.js'; import { CometChatMessageInformationEmptyState, CometChatMessageInformationErrorState, CometChatMessageInformationLoadingState, CometChatMessageInformationReceiptList, CometChatMessageInformationMessagePreview, CometChatMessageInformationHeader, CometChatMessageInformationRoot } from './chunk-KBT2B2BM.js'; export { useCometChatMessageInformationContext } from './chunk-KBT2B2BM.js'; export { CometChatRichTextFormatter } from './chunk-ACMQ6JJD.js'; import { CometChatLogger, CometChatCheckbox, CometChatRadioButton, list_error_state_icon_default, groups_empty_state_default, CometChatConfirmDialog, useCometChatEvents, CometChatUIKitUtility, CometChatSoundManager, CometChatUrlFormatter, isVoiceNote, subscribeToStreamState, getStreamState, CometChatOngoingCall, setStreamError, file_type_unsupported_default, file_type_jpg_default, file_type_mov_default, file_type_mp3_default, file_type_zip_default, file_type_txt_default, file_type_ppt_default, file_type_xlsx_default, file_type_word_default, file_type_pdf_default, stopExclusivePlayback, pause_default, play_arrow_default, startExclusivePlayback, CometChatCallButtons } from './chunk-54LL2RSF.js'; export { CometChatAIAssistantChat, CometChatAIAssistantChatHistory, CometChatCallButtons, CometChatChangeScope, CometChatCheckbox, CometChatConfirmDialog, CometChatConversationStarter, CometChatConversationSummary, CometChatCreatePoll, CometChatFlagMessageDialog, CometChatGroupMembers, CometChatLogger, CometChatMessageBubbleRenderer, CometChatMessageBubbleWrapper, CometChatMessageComposer, CometChatMessageHeader, CometChatMessageList, CometChatMessageListAlignment, CometChatModerationView, CometChatOngoingCall, CometChatOutgoingCall, CometChatRadioButton, CometChatSmartReplies, CometChatSoundManager, CometChatUIKitUtility, CometChatUrlFormatter, CometChatUsers, LogLevel, clone, computeBatchPosition, createActionMessage, getAIAssistantTools, getMessageBatchId, getStreamSpeed, getStreamState, groupAndOrderTrayItems, handleWebsocketMessage, initialMessageListState, isStreaming, setAIAssistantTools, setStreamError, setStreamSpeed, startStreamingMessage, stopStreamingMessage, subscribeToStreamState, useCometChatCallButtons, useCometChatChangeScopeContext, useCometChatConfirmDialogContext, useCometChatConversationStarterContext, useCometChatConversationSummaryContext, useCometChatEvents, useCometChatFlagMessageDialogContext, useCometChatGroupMembers, useCometChatGroupMembersContext, useCometChatMessageComposerContext, useCometChatMessageHeaderContext, useCometChatMessageList, useCometChatMessageListContext, useCometChatSmartRepliesContext, useCometChatUsers, useCometChatUsersContext, useDefaultMessageCategories, useDefaultMessageTypes, usePluginRegistry } from './chunk-54LL2RSF.js'; import './chunk-GI7Y3RZV.js'; import { CometChatSearchBar } from './chunk-3IC2CRN3.js'; export { CometChatSearchBar, useCometChatSearchBarContext } from './chunk-3IC2CRN3.js'; import { CometChatPluginRegistryContext, CometChatDate, CometChatMessageBubble, CometChatContextMenu, useGlobalConfig, getReceiptStatus, GlobalConfigProvider } from './chunk-3WCEOEGP.js'; export { CometChatContextMenu, CometChatDate, CometChatMessageBubble, getMessageError, getReceiptStatus, hasMessageError, isMessageModerated, isMessagePendingModeration, isPermissionDeniedError, useCometChatContextMenuContext, useCometChatDate, useCometChatDateContext } from './chunk-3WCEOEGP.js'; import './chunk-IUQ25MDY.js'; import { CometChatFullScreenViewerNavigation, CometChatFullScreenViewerBody, CometChatFullScreenViewerHeader, CometChatFullScreenViewerRoot, unsupported_default } from './chunk-ESSC6TLT.js'; export { useCometChatFullScreenViewerContext } from './chunk-ESSC6TLT.js'; export { CometChatButton, useCometChatButtonContext } from './chunk-KOCEARYG.js'; import { CometChatAvatar } from './chunk-LDXTHTWH.js'; export { CometChatAvatar, getInitials, useCometChatAvatarContext } from './chunk-LDXTHTWH.js'; export { CometChatPollBubble } from './chunk-3VW37XMH.js'; export { CometChatStickerBubble } from './chunk-XWYQZSOH.js'; import { POLLS_CONSTANTS, STICKERS_CONSTANTS } from './chunk-TNRMDFEG.js'; export { CometChatCollaborativeDocumentBubble } from './chunk-DL7PZQIA.js'; export { CometChatCollaborativeWhiteboardBubble } from './chunk-W4WLYZ5I.js'; export { extractExtensionUrl } from './chunk-KJSAV5II.js'; import './chunk-TUVCMOSH.js'; import { getBubbleAlignment } from './chunk-PXEKTS5H.js'; import { useLoggedInUser, CometChatLocalize, CometChatUIKit, CometChatUIKitCalls } from './chunk-UKFBODV7.js'; export { CometChatLocalize, CometChatMessageStatus, CometChatUIKit, CometChatUIKitCalls, UIKitSettings, UIKitSettingsBuilder, initCallsSDK, loadCallsSDK, useLoggedInUser } from './chunk-UKFBODV7.js'; import './chunk-PJV2HJD5.js'; import { sanitizeAIHtml, Copy_default } from './chunk-ZT46V6ZY.js'; export { CometChatAIAssistantBubble } from './chunk-ZT46V6ZY.js'; import { CometChatMarkdownFormatter, sanitizeHtml, usePublishEvent, escapeUserHtml, stripInvalidMentionFormats, CometChatEventsContext } from './chunk-MI3XOQGY.js'; export { CometChatMarkdownFormatter, usePublishEvent } from './chunk-MI3XOQGY.js'; import { CometChatUIKitConstants } from './chunk-3DZVPW3I.js'; import { CometChatTextFormatter } from './chunk-OZUCO4ZL.js'; export { CometChatTextFormatter } from './chunk-OZUCO4ZL.js'; import { useTheme, CometChatThemeContext } from './chunk-SXG4FP55.js'; export { useTheme } from './chunk-SXG4FP55.js'; import { useCometChatFrameContext } from './chunk-UTFM6ET6.js'; export { CometChatFrameProvider, useCometChatFrameContext } from './chunk-UTFM6ET6.js'; export { CometChatToolCallArgumentBubble } from './chunk-LG4MZDAT.js'; import { useLocale, LocaleContext } from './chunk-UVWJYQJ3.js'; export { useLocale } from './chunk-UVWJYQJ3.js'; import './chunk-ERWUU2OZ.js'; import { __export } from './chunk-WZOKFQUZ.js'; import React27, { lazy, createContext, useRef, useCallback, useEffect, useMemo, useContext, useState, useId, useReducer, Suspense, useSyncExternalStore } from 'react'; import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; import { CometChat, CometChatException } from '@cometchat/chat-sdk-javascript'; import { CometChatCardView } from '@cometchat/cards-react'; var CometChatThemeProvider = ({ theme: themeProp = "light", children }) => { const [internalTheme, setInternalTheme] = useState(themeProp); useEffect(() => { setInternalTheme(themeProp); }, [themeProp]); const setTheme = useCallback((newTheme) => { setInternalTheme(newTheme); }, []); const contextValue = useMemo( () => ({ theme: internalTheme, setTheme }), [internalTheme, setTheme] ); return /* @__PURE__ */ jsx(CometChatThemeContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx("div", { "data-theme": internalTheme, className: "cometchat", children }) }); }; CometChatThemeProvider.displayName = "CometChatThemeProvider"; function LocaleProvider({ locale, localizeInstance, children }) { const language = locale ?? localizeInstance?.getDefaultLanguage() ?? new CometChatLocalize().getDefaultLanguage(); const localize = useMemo(() => { if (localizeInstance) { CometChatLocalize.setSharedInstance(localizeInstance); return localizeInstance; } const existingShared = CometChatLocalize.getSharedInstance(); if (existingShared) { if (existingShared.currentLanguage !== language) { existingShared.setCurrentLanguage(language); } return existingShared; } const instance = new CometChatLocalize({ language }); CometChatLocalize.setSharedInstance(instance); return instance; }, [localizeInstance, language]); const [translators, setTranslators] = useState(() => { const translator = localize.getTranslators(); return { getLocalizedString: translator.t, tDateTimeParser: translator.tDateTimeParser, language: localize.currentLanguage, timezone: localize.getTimezone(), calendarObject: localize.getCalendarObject(), dateLocaleLanguage: localize.getDateLocaleLanguage() }; }); useEffect(() => { localize.registerSetLanguageCallback((t) => { setTranslators((prev) => ({ ...prev, getLocalizedString: t, dateLocaleLanguage: localize.getDateLocaleLanguage() })); }); const translator = localize.getTranslators(); setTranslators({ getLocalizedString: translator.t, tDateTimeParser: translator.tDateTimeParser, language: localize.currentLanguage, timezone: localize.getTimezone(), calendarObject: localize.getCalendarObject(), dateLocaleLanguage: localize.getDateLocaleLanguage() }); }, [localize]); return /* @__PURE__ */ jsx(LocaleContext.Provider, { value: translators, children }); } var CometChatEventsProvider = ({ children }) => { const instanceId = useId(); const subscribersRef = useRef(/* @__PURE__ */ new Set()); const emit = useCallback((event) => { subscribersRef.current.forEach((handler) => { try { handler(event); } catch (error) { CometChatLogger.error("CometChatEventsProvider", "Subscriber error", error); } }); }, []); const subscribe = useCallback((handler) => { subscribersRef.current.add(handler); return () => { subscribersRef.current.delete(handler); }; }, []); const publish = useCallback( (event) => { emit(event); }, [emit] ); useEffect(() => { CometChatUIKit._setEmit(emit); return () => { CometChatUIKit._setEmit(null); }; }, [emit]); useEffect(() => { const id = `CometChatEvents_msg_${instanceId}`; CometChat.addMessageListener( id, new CometChat.MessageListener({ onTextMessageReceived: (msg) => { emit({ type: "message/text-received", message: msg }); }, onMediaMessageReceived: (msg) => { emit({ type: "message/media-received", message: msg }); }, onCustomMessageReceived: (msg) => { emit({ type: "message/custom-received", message: msg }); }, onInteractiveMessageReceived: (msg) => { emit({ type: "message/interactive-received", message: msg }); }, onCardMessageReceived: (msg) => { emit({ type: "message/card-received", message: msg }); }, onAIAssistantMessageReceived: (msg) => { emit({ type: "message/ai-assistant-received", message: msg }); }, onMessageEdited: (msg) => { emit({ type: "message/edited", message: msg }); }, onMessageDeleted: (msg) => { emit({ type: "message/deleted", message: msg }); }, onMessageModerated: (msg) => { emit({ type: "message/moderated", message: msg }); }, onMessagesDelivered: (receipt) => { emit({ type: "receipt/delivered", receipt }); }, onMessagesRead: (receipt) => { emit({ type: "receipt/read", receipt }); }, onMessagesDeliveredToAll: (receipt) => { emit({ type: "receipt/delivered-to-all", receipt }); }, onMessagesReadByAll: (receipt) => { emit({ type: "receipt/read-by-all", receipt }); }, onMessageReactionAdded: (event) => { emit({ type: "reaction/added", event }); }, onMessageReactionRemoved: (event) => { emit({ type: "reaction/removed", event }); }, onTypingStarted: (indicator) => { emit({ type: "typing/started", indicator }); }, onTypingEnded: (indicator) => { emit({ type: "typing/ended", indicator }); } }) ); return () => { CometChat.removeMessageListener(id); }; }, [instanceId, emit]); useEffect(() => { const id = `CometChatEvents_user_${instanceId}`; CometChat.addUserListener( id, new CometChat.UserListener({ onUserOnline: (user) => { emit({ type: "user/online", user }); }, onUserOffline: (user) => { emit({ type: "user/offline", user }); } }) ); return () => { CometChat.removeUserListener(id); }; }, [instanceId, emit]); useEffect(() => { const id = `CometChatEvents_group_${instanceId}`; CometChat.addGroupListener( id, new CometChat.GroupListener({ onGroupMemberJoined: (action, joinedUser, joinedGroup) => { emit({ type: "group/member-joined", action, joinedUser, joinedGroup }); }, onGroupMemberLeft: (action, leftUser, leftGroup) => { emit({ type: "group/member-left", action, leftUser, leftGroup }); }, onGroupMemberKicked: (action, kickedUser, kickedBy, kickedFrom) => { emit({ type: "group/member-kicked", action, kickedUser, kickedBy, kickedFrom }); }, onGroupMemberBanned: (action, bannedUser, bannedBy, bannedFrom) => { emit({ type: "group/member-banned", action, bannedUser, bannedBy, bannedFrom }); }, onGroupMemberUnbanned: (action, unbannedUser, unbannedBy, unbannedFrom) => { emit({ type: "group/member-unbanned", action, unbannedUser, unbannedBy, unbannedFrom }); }, onMemberAddedToGroup: (action, addedBy, addedUser, addedTo) => { emit({ type: "group/member-added", action, addedBy, addedUser, addedTo }); }, onGroupMemberScopeChanged: (action, changedUser, newScope, oldScope, changedGroup) => { emit({ type: "group/member-scope-changed", action, changedUser, newScope, oldScope, changedGroup }); } }) ); return () => { CometChat.removeGroupListener(id); }; }, [instanceId, emit]); useEffect(() => { const id = `CometChatEvents_call_${instanceId}`; CometChat.addCallListener( id, new CometChat.CallListener({ onIncomingCallReceived: (call) => { emit({ type: "call/incoming", call }); }, onOutgoingCallAccepted: (call) => { emit({ type: "call/accepted", call }); }, onOutgoingCallRejected: (call) => { emit({ type: "call/rejected", call }); }, onIncomingCallCancelled: (call) => { emit({ type: "call/cancelled", call }); }, onCallEndedMessageReceived: (call) => { emit({ type: "call/ended", call }); } }) ); return () => { CometChat.removeCallListener(id); }; }, [instanceId, emit]); useEffect(() => { const id = `CometChatEvents_conn_${instanceId}`; CometChat.addConnectionListener( id, new CometChat.ConnectionListener({ onConnected: () => { emit({ type: "connection/connected" }); }, onDisconnected: () => { emit({ type: "connection/disconnected" }); } }) ); return () => { CometChat.removeConnectionListener(id); }; }, [instanceId, emit]); const contextValue = useMemo(() => ({ subscribe, publish }), [subscribe, publish]); return /* @__PURE__ */ jsx(CometChatEventsContext.Provider, { value: contextValue, children }); }; CometChatEventsProvider.displayName = "CometChatEventsProvider"; // src/plugins/CometChatPluginRegistry.ts var CometChatPluginRegistry = class _CometChatPluginRegistry { constructor(plugins = []) { this.plugins = Object.freeze([...plugins]); } /** Return a new registry with the plugin added. */ register(plugin) { return new _CometChatPluginRegistry([...this.plugins, plugin]); } /** * Find the plugin that handles a given message. * * Resolution order: * 1. If the message is deleted (getDeletedAt() !== null), return the DeletePlugin. * 2. Otherwise, find the first plugin whose messageCategories includes the message's * category AND whose messageTypes includes the message's type — OR whose messageTypes * is empty, which acts as a category-only render wildcard. Note: an empty messageTypes * contributes nothing to the request builder (getAllMessageTypes), so a plugin that * needs its messages fetched must declare its type explicitly. Strict type+category * matching is unchanged for every plugin that declares a non-empty messageTypes. * 3. If no plugin matches, return undefined. */ findPlugin(message) { if (message.getDeletedAt()) { return this.plugins.find((p) => p.id === "delete"); } const type = message.getType(); const category = message.getCategory(); return this.plugins.find( (p) => p.messageCategories.includes(category) && (p.messageTypes.length === 0 || p.messageTypes.includes(type)) ); } /** Get all registered plugins. */ getAll() { return this.plugins; } /** Get all message types across all plugins (for SDK request builders). */ getAllMessageTypes() { return [...new Set(this.plugins.flatMap((p) => p.messageTypes))]; } /** Get all message categories across all plugins (for SDK request builders). */ getAllMessageCategories() { return [...new Set(this.plugins.flatMap((p) => p.messageCategories))]; } /** * Get a plain-text preview for the conversation list subtitle. * * Finds the plugin that handles the message and delegates to its * `getLastMessagePreview()` method. Returns undefined if no plugin * matches or the matched plugin doesn't implement the method. */ getLastMessagePreview(message, loggedInUser, t) { const plugin = this.findPlugin(message); if (!plugin?.getLastMessagePreview) return void 0; return plugin.getLastMessagePreview(message, loggedInUser, t); } /** * Get text formatters from the text plugin (or the first plugin that provides them). * Used by media plugins for caption rendering and by conversations/search for subtitles. */ getTextFormatters() { for (const plugin of this.plugins) { if (plugin.getTextFormatters) { const formatters = plugin.getTextFormatters(); if (formatters.length > 0) return formatters; } } return []; } }; // src/formatters/CometChatMentionsFormatter.ts var USER_MENTION_REGEX_SOURCE = "<@uid:(.*?)>"; var CHANNEL_MENTION_REGEX_SOURCE = "<@all:(.*?)>"; function escapeHtml(str) { return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;"); } var CometChatMentionsFormatter = class extends CometChatTextFormatter { constructor() { super(...arguments); this.id = "mentions-formatter"; this.priority = 20; this.mentions = []; this.users = /* @__PURE__ */ new Map(); this.loggedInUser = null; this.allMentionLabel = "all"; } setLoggedInUser(user) { this.loggedInUser = user; } setUsers(users) { this.users.clear(); for (const user of users) { this.users.set(user.getName().toLowerCase(), user); } } setMessageBubbleAlignment(alignment) { this.messageBubbleAlignment = alignment; } getRegex() { return /@(\w+)/g; } /** Check if text contains SDK format mentions. */ hasSdkMentions(text) { const userRegex = new RegExp(USER_MENTION_REGEX_SOURCE, "g"); const channelRegex = new RegExp(CHANNEL_MENTION_REGEX_SOURCE, "g"); return userRegex.test(text) || channelRegex.test(text); } /** * Format plain text @mentions by matching against the user list. */ format(text) { if (!text) { this.originalText = ""; this.formattedText = ""; this.mentions = []; this.metadata = { mentions: [] }; return ""; } this.originalText = text; this.mentions = []; const regex = this.getRegex(); let result = ""; let lastIndex = 0; let match; while ((match = regex.exec(text)) !== null) { const fullMatch = match[0]; const username = match[1] ?? ""; const matchStart = match.index; const matchEnd = matchStart + fullMatch.length; result += text.slice(lastIndex, matchStart); if (username.toLowerCase() === this.allMentionLabel.toLowerCase()) { this.mentions.push({ uid: "all", name: this.allMentionLabel, startIndex: matchStart, endIndex: matchEnd, type: "channel" }); const css = this.buildCssClasses("channel"); result += `<span class="${css}" data-uid="all" data-mention-type="channel">@${escapeHtml(this.allMentionLabel)}</span>`; } else { const user = this.users.get(username.toLowerCase()); if (user) { const uid = user.getUid(); const displayName = user.getName(); const mentionType = this.getMentionType(uid); this.mentions.push({ uid, name: displayName, startIndex: matchStart, endIndex: matchEnd, type: mentionType }); const css = this.buildCssClasses(mentionType); result += `<span class="${css}" data-uid="${escapeHtml(uid)}" data-mention-type="${mentionType}">@${escapeHtml(displayName)}</span>`; } else { result += fullMatch; } } lastIndex = matchEnd; } result += text.slice(lastIndex); this.formattedText = result; this.metadata = { mentions: this.mentions }; return this.formattedText; } /** * Format SDK-format mentions: `<@uid:xxx>` and `<@all:xxx>`. * Uses the mentionedUsers array to resolve display names. */ formatSdkMentions(text, mentionedUsers = []) { this.originalText = text; this.mentions = []; const userMap = /* @__PURE__ */ new Map(); for (const user of mentionedUsers) { userMap.set(user.getUid(), user); } const codeSegmentRegex = /(<pre><code>[\s\S]*?<\/code><\/pre>|<code>[\s\S]*?<\/code>)/g; const segments = text.split(codeSegmentRegex); const result = segments.map((segment) => { if (segment.startsWith("<code>") || segment.startsWith("<pre><code>")) { let plain = segment.replace( new RegExp(CHANNEL_MENTION_REGEX_SOURCE, "g"), (_match, label) => { return `@${escapeHtml(label || this.allMentionLabel)}`; } ); plain = plain.replace( new RegExp(USER_MENTION_REGEX_SOURCE, "g"), (_match, uid) => { const user = userMap.get(uid); const displayName = user ? user.getName() : uid; return `@${escapeHtml(displayName)}`; } ); return plain; } let seg = segment; const channelRegex = new RegExp(CHANNEL_MENTION_REGEX_SOURCE, "g"); seg = seg.replace(channelRegex, (fullMatch, label, offset) => { const displayLabel = label || this.allMentionLabel; this.mentions.push({ uid: "all", name: displayLabel, startIndex: offset, endIndex: offset + fullMatch.length, type: "channel" }); const css = this.buildCssClasses("channel"); return `<span class="${css}" data-uid="all" data-mention-type="channel" contenteditable="false">@${escapeHtml(displayLabel)}</span>`; }); const userRegex = new RegExp(USER_MENTION_REGEX_SOURCE, "g"); seg = seg.replace(userRegex, (fullMatch, uid, offset) => { if (!uid || uid.trim() === "") return ""; const user = userMap.get(uid); const displayName = user ? user.getName() : uid; if (!displayName || displayName.trim() === "") return ""; const mentionType = this.getMentionType(uid); this.mentions.push({ uid, name: displayName, startIndex: offset, endIndex: offset + fullMatch.length, type: mentionType }); const css = this.buildCssClasses(mentionType); return `<span class="${css}" data-uid="${escapeHtml(uid)}" data-mention-type="${mentionType}" contenteditable="false">@${escapeHtml(displayName)}</span>`; }); return seg; }).join(""); this.formattedText = result; this.metadata = { mentions: this.mentions }; return this.formattedText; } getMentions() { return [...this.mentions]; } reset() { super.reset(); this.mentions = []; } getMentionType(uid) { if (this.loggedInUser?.getUid() === uid) { return "self"; } return "other"; } buildCssClasses(mentionType) { const classes = ["cometchat-mentions"]; if (mentionType === "self" || mentionType === "channel") { classes.push("cometchat-mentions-you"); } else { classes.push("cometchat-mentions-other"); } if (this.messageBubbleAlignment === "left") { classes.push("cometchat-mentions-incoming"); } else if (this.messageBubbleAlignment === "right") { classes.push("cometchat-mentions-outgoing"); } return classes.join(" "); } }; // src/utils/HtmlToMarkdown.ts function htmlToMarkdown(html) { if (!html) return ""; if (typeof document === "undefined") return html; const container = document.createElement("div"); container.innerHTML = html; return processNode(container).trim(); } function wrapInlineMarker(content, openMarker, closeMarker) { const close = closeMarker ?? openMarker; if (!content.includes("\n")) { return `${openMarker}${content}${close}`; } return content.split("\n").map((line) => line.trim() === "" ? line : `${openMarker}${line}${close}`).join("\n"); } function processNode(node, depth = 0) { let result = ""; for (const child of Array.from(node.childNodes)) { result += processChildNode(child, depth); } return result; } function processChildNode(node, depth = 0) { if (node.nodeType === Node.TEXT_NODE) { return node.textContent ?? ""; } if (node.nodeType !== Node.ELEMENT_NODE) { return ""; } const element = node; const tagName = element.tagName.toUpperCase(); const innerContent = processNode(element, depth); switch (tagName) { case "B": case "STRONG": if (element.querySelector("pre")) return innerContent; return wrapInlineMarker(innerContent, "**"); case "I": case "EM": if (element.querySelector("pre")) return innerContent; return wrapInlineMarker(innerContent, "_"); case "U": if (element.querySelector("pre")) return innerContent; return wrapInlineMarker(innerContent, "<u>", "</u>"); case "S": case "STRIKE": case "DEL": if (element.querySelector("pre")) return innerContent; return wrapInlineMarker(innerContent, "~~"); case "PRE": { const codeEl = element.querySelector("code") ?? element; const codeContent = extractCodeContent(codeEl); return `\`\`\`${codeContent}\`\`\``; } case "CODE": { if (element.parentElement?.tagName === "PRE") return innerContent; const codeText = extractCodeContent(element).replace(/\u200B/g, "").trim(); if (!codeText) return ""; if (codeText.includes("\n")) { return codeText.split("\n").map((line) => line.trim() ? `\`${line}\`` : "").join("\n"); } return `\`${codeText}\``; } case "BLOCKQUOTE": { const lines = innerContent.split("\n"); while (lines.length > 0 && (lines[lines.length - 1] ?? "").trim() === "") { lines.pop(); } return lines.map((line) => `> ${line}`).join("\n"); } case "A": { const href = element.getAttribute("href") ?? ""; const text = innerContent || href; return `[${text}](${href})`; } case "OL": { const startAttr = element.getAttribute("start"); let index = startAttr ? parseInt(startAttr, 10) : 1; if (isNaN(index)) index = 1; let listResult = ""; const indent = " ".repeat(depth); for (const child of Array.from(element.childNodes)) { if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "LI") { const { text, nested } = extractListItemContent(child, depth); const trimmed = text.replace(/\n/g, "").trim(); if (trimmed) { listResult += `${indent}${String(index)}. ${text} `; index++; } if (nested) listResult += nested; } } return listResult; } case "UL": { let listResult = ""; const indent = " ".repeat(depth); for (const child of Array.from(element.childNodes)) { if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "LI") { const { text, nested } = extractListItemContent(child, depth); const trimmed = text.replace(/\n/g, "").trim(); if (trimmed) { listResult += `${indent}\u2022 ${text} `; } if (nested) listResult += nested; } } return listResult; } case "LI": return innerContent; case "BR": return "\n"; case "P": case "DIV": return innerContent + "\n"; case "SPAN": { const className = element.className || ""; if (className.includes("cometchat-mentions")) { const uid = element.getAttribute("data-uid") ?? ""; const mentionType = element.getAttribute("data-mention-type") ?? ""; if (uid === "all" || mentionType === "channel") { return `<@all:${uid}>`; } if (uid) { return `<@uid:${uid}>`; } return element.textContent ?? innerContent; } return innerContent; } default: return innerContent; } } function extractCodeContent(node) { let out = ""; for (const child of Array.from(node.childNodes)) { if (child.nodeType === Node.TEXT_NODE) { out += child.textContent ?? ""; } else if (child.nodeType === Node.ELEMENT_NODE) { const el = child; const tag = el.tagName.toUpperCase(); if (el.classList.contains("cometchat-mentions")) { const uid = el.getAttribute("data-uid") ?? ""; const mentionType = el.getAttribute("data-mention-type") ?? ""; if (uid === "all" || mentionType === "channel") { out += `<@all:${uid}>`; } else if (uid) { out += `<@uid:${uid}>`; } else { out += el.textContent ?? ""; } } else if (tag === "BR") { out += "\n"; } else if (tag === "DIV" || tag === "P") { if (out.length > 0 && !out.endsWith("\n")) out += "\n"; out += extractCodeContent(el); } else { out += extractCodeContent(el); } } } return out; } function extractListItemContent(li, depth) { let text = ""; let nested = ""; for (const child of Array.from(li.childNodes)) { if (child.nodeType === Node.ELEMENT_NODE) { const tag = child.tagName.toUpperCase(); if (tag === "OL" || tag === "UL") { nested += processChildNode(child, depth + 1); } else { text += processChildNode(child, depth); } } else { text += child.textContent ?? ""; } } return { text, nested }; } function cleanMarkdown(markdown) { return markdown.replace(/```(?:\u200B|\u200C|\u200D|\uFEFF|\s)*```/g, "").replace(/(?<!`)`(?!`)(?:[^\S\n]|\u200B|\u200C|\u200D|\uFEFF)*`(?!`)/g, "").replace(/\n{3,}/g, "\n\n").trim(); } function convertHtmlToMarkdown(html) { const markdown = htmlToMarkdown(html); return cleanMarkdown(markdown); } function detectSingleEmoji(text) { if (!text || typeof text !== "string") return false; const trimmed = text.trim(); if (trimmed.length === 0) return false; const basicRegex = /^(?:\p{Extended_Pictographic}(?:\p{Emoji_Modifier}|\uFE0F|\u200D\p{Extended_Pictographic})*|\p{Regional_Indicator}{2}|[0-9#*]\uFE0F?\u20E3)$/u; if (basicRegex.test(trimmed)) return true; if (typeof Intl !== "undefined" && "Segmenter" in Intl) { try { const SegmenterCtor = Intl.Segmenter; const segmenter = new SegmenterCtor("en", { granularity: "grapheme" }); const segments = Array.from(segmenter.segment(trimmed)); if (segments.length !== 1) return false; const segment = segments[0]?.segment ?? ""; return /^\p{Extended_Pictographic}/u.test(segment) || /^\p{Regional_Indicator}{2}$/u.test(segment) || /^[0-9#*]\uFE0F?\u20E3$/u.test(segment); } catch { } } const complexRegex = /^(?:(?:\p{Extended_Pictographic}|\p{Regional_Indicator}{2})(?:\p{Emoji_Modifier}|\uFE0F)?(?:\u200D(?:\p{Extended_Pictographic}|\p{Regional_Indicator}{2})(?:\p{Emoji_Modifier}|\uFE0F)?)*|[0-9#*]\uFE0F?\u20E3)$/u; return complexRegex.test(trimmed); } function extractLinkPreviews(message) { if (!message) return []; try { const metadata = message.getMetadata?.(); if (!metadata || typeof metadata !== "object") return []; const injected = metadata["@injected"]; if (!injected || typeof injected !== "object") return []; const extensions = injected.extensions; if (!extensions || typeof extensions !== "object") return []; const linkPreview = extensions["link-preview"]; if (!linkPreview || typeof linkPreview !== "object") return []; const links = linkPreview.links; if (!Array.isArray(links)) return []; return links.filter( (link) => link != null && typeof link === "object" && typeof link.url === "string" ).map((link) => ({ url: link.url, title: typeof link.title === "string" ? link.title : void 0, description: typeof link.description === "string" ? link.description : void 0, image: typeof link.image === "string" ? link.image : void 0, favicon: typeof link.favicon === "string" ? link.favicon : void 0 })); } catch { return []; } } function extractTranslation(message) { if (!message) return ""; try { const metadata = message.getMetadata?.(); if (!metadata || typeof metadata !== "object") return ""; const translated = metadata.translated_message; return typeof translated === "string" ? translated : ""; } catch { return ""; } } function getDomainFromUrl(url) { try { return new URL(url).hostname; } catch { return ""; } } function applyFormatters(text, formatters, message) { if (!text) return ""; let processedText = text; const formattingTagPattern = /<\/?(b|strong|i|em|u|s|strike|del|code|pre|blockquote|a|ol|ul|li|p|div)[\s>]/i; if (formattingTagPattern.test(text)) { processedText = convertFormattingHtmlToMarkdown(text); } let result = escapeUserHtml(processedText); const sorted = [...formatters].sort((a, b) => a.priority - b.priority); for (const formatter of sorted) { try { if (!formatter.shouldFormat(result)) continue; if (formatter instanceof CometChatMentionsFormatter && formatter.hasSdkMentions(result)) { const mentionedUsers = message?.getMentionedUsers?.() ?? []; const formatted = formatter.formatSdkMentions(result, mentionedUsers); if (typeof formatted === "string") { result = formatted; } } else { const formatted = formatter.format(result); if (typeof formatted === "string") { result = formatted; } } } catch (error) { console.warn("CometChatTextBubble: Error applying text formatter", error); } } result = stripInvalidMentionFormats(result); return sanitizeHtml(result); } function convertFormattingHtmlToMarkdown(text) { if (!text || typeof text !== "string") return text; if (typeof document === "undefined") return text; const codePlaceholders = []; let decoded = text.replace(/```[\s\S]*?```|`[^`]*`/g, (match) => { const idx = codePlaceholders.length; codePlaceholders.push(match); return `\u200B__CODE_${String(idx)}__\u200B`; }); const entityPattern = /&lt;(\/?)(b|strong|i|em|u|s|strike|del|code|pre|blockquote|a|ol|ul|li)((?:\s[^&]*)?)&gt;/gi; if (entityPattern.test(decoded)) { decoded = decoded.replace( /&lt;(\/?)(b|strong|i|em|u|s|strike|del|code|pre|blockquote|a|ol|ul|li)((?:\s[^&]*)?)&gt;/gi, "<$1$2$3>" ); } const mentionPlaceholders = []; decoded = decoded.replace(/<@(uid|all):[^>]+>/g, (match) => { const idx = mentionPlaceholders.length; mentionPlaceholders.push(match); return `\u200B__MENTION_${String(idx)}__\u200B`; }); decoded = sanitizeHtml(decoded); const markdown = convertHtmlToMarkdown(decoded); const result = markdown.replace(/\u200B__MENTION_(\d+)__\u200B/g, (_, idx) => { return mentionPlaceholders[parseInt(idx, 10)] ?? ""; }); const withCode = result.replace(/\u200B__CODE_(\d+)__\u200B/g, (_, idx) => { return codePlaceholders[parseInt(idx, 10)] ?? ""; }); return withCode; } var CometChatTextBubble = ({ message, text, isSentByMe: isSentByMe4 = true, textFormatters = [], disableTruncation = false, className }) => { const contentRef = useRef(null); const [isTruncated, setIsTruncated] = useState(false); const [isExpanded, setIsExpanded] = useState(false); const { getLocalizedString: getLocalizedString4 } = useLocale(); const loggedInUser = useLoggedInUser(); const IframeContext = useCometChatFrameContext(); const getCurrentWindow = useCallback(() => { return IframeContext.iframeWindow ?? window; }, [IframeContext.iframeWindow]); const effectiveText = useMemo(() => { if (typeof text === "string") return text; try { return message?.getText() ?? ""; } catch { return ""; } }, [text, message]); useMemo(() => { for (const formatter of textFormatters) { if (formatter instanceof CometChatMentionsFormatter) { if (loggedInUser) formatter.setLoggedInUser(loggedInUser); formatter.setMessageBubbleAlignment(isSentByMe4 ? "right" : "left"); try { const mentioned = message?.getMentionedUsers() ?? []; if (mentioned.length > 0) formatter.setUsers(mentioned); } catch { } } } }, [textFormatters, loggedInUser, isSentByMe4, message]); useEffect(() => { setIsExpanded(false); }, [effectiveText]); const formattedHtml = useMemo(() => { return applyFormatters(effectiveText, textFormatters, message); }, [effectiveText, textFormatters, message]); const isSingleEmoji = useMemo(() => detectSingleEmoji(effectiveText), [effectiveText]); const linkPreviews = useMemo( () => extractLinkPreviews(message), [message] ); const translatedText = useMemo( () => extractTranslation(message), [message] ); const formattedTranslation = useMemo( () => translatedText ? applyFormatters(translatedText, textFormatters, message) : "", [translatedText, textFormatters, message] ); useEffect(() => { if (disableTruncation || !contentRef.current) { setIsTruncated(false); return; } requestAnimationFrame(() => { if (!contentRef.current) return; const tolerance = 8; const isOverflowing = contentRef.current.scrollHeight - contentRef.current.clientHeight > tolerance; setIsTruncated(isOverflowing); }); }, [formattedHtml, disableTruncation]); const toggleReadMore = useCallback(() => { setIsExpanded((prev) => !prev); }, []); const publish = usePublishEvent(); const mentionedUsers = useMemo(() => { try { return message?.getMentionedUsers() ?? []; } catch { return []; } }, [message]); const handleTextClick = useCallback( (event) => { const target = event.target; if (target.tagName === "A" && target.classList.contains("cometchat-link")) { event.preventDefault(); event.stopPropagation(); const url = target.getAttribute("href"); if (url) { getCurrentWindow().open(url, "_blank", "noopener,noreferrer"); } return; } if (target.classList.contains("cometchat-mentions")) { event.preventDefault(); event.stopPropagation(); const uid = target.getAttribute("data-uid"); const mentionType = target.getAttribute("data-mention-type"); if (!uid || uid === "all" || mentionType === "channel") return; const user = mentionedUsers.find((u) => u.getUid() === uid); if (user) { publish({ type: "ui:open-chat", user }); } } }, [publish, mentionedUsers, getCurrentWindow] ); const handleLinkPreviewClick = useCallback( (url) => { if (url) { getCurrentWindow().open(url, "_blank", "noopener,noreferrer"); } }, [getCurrentWindow] ); const handleLinkPreviewKeyDown = useCallback( (event, url) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); handleLinkPreviewClick(url); } }, [handleLinkPreviewClick] ); const rootClasses = [ "cometchat-text-bubble", isSentByMe4 ? "cometchat-text-bubble--outgoing" : "cometchat-text-bubble--incoming", isSingleEmoji ? "cometchat-text-bubble--single-emoji" : "", linkPreviews.length > 0 ? "cometchat-text-bubble--has-link-preview" : "", // Plain global classes for external CSS targeting (e.g. AI chat overrides) "cometchat-text-bubble", isSentByMe4 ? "cometchat-text-bubble-outgoing" : "cometchat-text-bubble-incoming", className ].filter(Boolean).join(" "); return /* @__PURE__ */ jsxs("div", { className: rootClasses, children: [ linkPreviews.length > 0 && /* @__PURE__ */ jsx("div", { className: "cometchat-text-bubble__link-preview-container", children: linkPreviews.map((preview) => /* @__PURE__ */ jsxs( "div", { className: "cometchat-text-bubble__link-preview", role: "article", "aria-label": getLocalizedString4("accessibility_link_preview_for").replace( "{domain}", getDomainFromUrl(preview.url) ), tabIndex: 0, onClick: () => { handleLinkPreviewClick(preview.url); }, onKeyDown: (e) => { handleLinkPreviewKeyDown(e, preview.url); }, children: [ preview.image && /* @__PURE__ */ jsx("div", { className: "cometchat-text-bubble__link-preview-image-container", children: /* @__PURE__ */ jsx( "img", { className: "cometchat-text-bubble__link-preview-image", src: preview.image, alt: preview.title ?? "Link preview image", loading: "lazy", decoding: "async" } ) }), /* @__PURE__ */ jsxs("div", { className: "cometchat-text-bubble__link-preview-content", children: [ /* @__PURE__ */ jsxs("div", { className: "cometchat-text-bubble__link-preview-text", children: [ preview.title && /* @__PURE__ */ jsx("span", { className: "cometchat-text-bubble__link-preview-title", children: preview.title }), preview.description && /* @__PURE__ */ jsx("span", { className: "cometchat-text-bubble__link-preview-description", children: preview.description }), /* @__PURE__ */ jsx("span", { className: "cometchat-text-bubble__link-preview-domain", children: getDomainFromUrl(preview.url) }) ] }), preview.favicon && !preview.image && /* @__PURE__ */ jsx( "img", { className: "cometchat-text-bubble__link-preview-favicon-large", src: preview.favicon, alt: "", loading: "lazy", decoding: "async" } ) ] }) ] }, preview.url )) }), translatedText ? /* @__PURE__ */ jsxs("div", { className: "cometchat-text-bubble__translation-container", children: [ /* @__PURE__ */ jsx( "div", { className: "cometchat-text-bubble__text", dangerouslySetInnerHTML: { __html: formattedHtml }, onClick: handleTextClick } ), /* @__PURE__ */ jsx("div", { className: "cometchat-text-bubble__translation-separator" }), /* @__PURE__ */ jsx("span", { className: "cometchat-text-bubble__translation-label", children: getLocalizedString4("message_translation_label") }), /* @__PURE__ */ jsx( "div", { className: "cometchat-text-bubble__translation-text", dangerouslySetInnerHTML: { __html: formattedTranslation }, onClick: handleTextClick } ) ] }) : /* @__PURE__ */ jsx("div", { className: "cometchat-text-bubble__content", children: /* @__PURE__ */ jsx( "div", { ref: contentRef, className: [ "cometchat-text-bubble__text", isExpanded ? "cometchat-text-bubble__text--expanded" : "", disableTruncation ? "cometchat-text-bubble__text--no-truncation" : "", // Plain global class for external CSS targeting "cometchat-text-bubble__body-text" ].filter(Boolean).join(" "), dangerouslySetInnerHTML: { __html: formattedHtml }, onClick: handleTextClick } ) }), isTruncated && !disableTruncation && /* @__PURE__ */ jsx("div", { className: "cometchat-text-bubble__read-more", children: /* @__PURE__ */ jsx( "button", { type: "button", className: "cometchat-text-bubble__read-more-button", "aria-label": isExpanded ? getLocalizedString4("text_message_show_less") : getLocalizedString4("text_message_read_more"), "aria-expanded": isExpanded, onClick: toggleReadMore, children: isExpanded ? getLocalizedString4("text_message_show_less") : getLocalizedString4("text_message_read_more") } ) }) ] }); }; CometChatTextBubble.displayName = "CometChatTextBubble"; var translationCache = /* @__PURE__ */ new Map(); async function translateMessage(message, language) { const messageId = message.getId(); const cacheKey = `${String(messageId)}_${language}`; const cached = translationCache.get(cacheKey); if (cached) return { translatedText: cached, isSameLanguage: false }; try { const originalText = message.getText(); const response = await CometChat.callExtension("message-translation", "POST", "v2/translate", { msgId: messageId, text: originalText, languages: [language] }); const languageOriginal = response.language_original ?? ""; const langParts = language.split("-"); const fallbackLang = langParts[0] ?? language; if (languageOriginal && (language.toLowerCase() === languageOriginal.toLowerCase() || fallbackLang.toLowerCase() === languageOriginal.toLowerCase())) { return { translatedText: "", isSameLanguage: true }; } const translations = response.translations ?? []; const translation = translations.find((t) => t.language_translated === language); if (!translation?.message_translated) { return { translatedText: "", isSameLanguage: false }; } const translatedText = translation.message_translated; translationCache.set(cacheKey, translatedText); return { translatedText, isSameLanguage: false }; } catch { return { translatedText: "", isSameLanguage: false }; } } function getCachedTranslation(messageId, language) { return translationCache.get(`${String(messageId)}_${language}`); } function clearTranslationCache() { translationCache.clear(); } // src/assets/add_reaction_icon.svg var add_reaction_icon_default = 'data:image/svg+xml,<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">%0A<g id="Base_Icon">%0A<path id="Vector" d="M8.00317 15.0591C7.09828 15.0591 6.24767 14.8864 5.45133 14.541C4.655 14.1954 3.96072 13.726 3.3685 13.1326C2.77617 12.5392 2.30733 11.845 1.962 11.05C1.61667 10.2549 1.444 9.40537 1.444 8.50148C1.444 7.59659 1.61711 6.74509 1.96333 5.94698C2.30956 5.14898 2.77817 4.45476 3.36917 3.86431C3.96006 3.27387 4.65483 2.80687 5.4535 2.46331C6.25217 2.11987 7.10083 1.94815 7.9995 1.94815C8.42072 1.94815 8.83039 1.98703 9.2285 2.06481C9.62661 2.14259 10.0114 2.25553 10.3828 2.40365C10.5809 2.48509 10.7041 2.59426 10.7523 2.73115C10.8004 2.86804 10.8013 2.99953 10.755 3.12565C10.7088 3.25176 10.6278 3.34953 10.512 3.41898C10.3962 3.48842 10.2689 3.48981 10.13 3.42315C9.793 3.28426 9.44883 3.17959 9.0975 3.10915C8.74617 3.03881 8.38017 3.00365 7.9995 3.00365C6.47683 3.00365 5.1795 3.53837 4.1075 4.60781C3.0355 5.67726 2.4995 6.97409 2.4995 8.49831C2.4995 10.0224 3.036 11.321 4.109 12.3941C5.18211 13.4671 6.48072 14.0036 8.00483 14.0036C9.52906 14.0036 10.8259 13.4676 11.8953 12.3956C12.9648 11.3236 13.4995 10.0263 13.4995 8.50365C13.4995 8.24876 13.4828 7.99659 13.4495 7.74715C13.4162 7.49781 13.3652 7.25181 13.2967 7.00915C13.2486 6.85592 13.2669 6.72492 13.3517 6.61615C13.4366 6.50748 13.5423 6.43609 13.669 6.40198C13.7958 6.36787 13.9247 6.38276 14.0557 6.44665C14.1868 6.51053 14.2801 6.63692 14.3357 6.82581C14.4079 7.09437 14.4625 7.36726 14.4995 7.64448C14.5365 7.92181 14.555 8.2082 14.555 8.50365C14.555 9.4032 14.3832 10.2527 14.0395 11.0521C13.6958 11.8516 13.2283 12.5463 12.6368 13.1361C12.0453 13.7259 11.3511 14.1939 10.5543 14.54C9.75756 14.8861 8.90717 15.0591 8.00317 15.0591ZM13.4745 3.69531H12.555C12.4069 3.69531 12.2824 3.64465 12.1815 3.54331C12.0805 3.44209 12.03 3.31709 12.03 3.16831C12.03 3.01965 12.0805 2.89437 12.1815 2.79248C12.2824 2.6907 12.4069 2.63981 12.555 2.63981H13.4745V1.72581C13.4745 1.5777 13.5252 1.45226 13.6265 1.34948C13.7277 1.2467 13.8527 1.19531 14.0015 1.19531C14.1502 1.19531 14.2754 1.2467 14.3773 1.34948C14.4791 1.45226 14.53 1.5777 14.53 1.72581V2.63981H15.444C15.5921 2.63981 15.7176 2.69137 15.