UNPKG

@cometchat/chat-uikit-react

Version:

Ready-to-use Chat UI Components for React

1,171 lines (1,161 loc) 617 kB
'use strict'; var chunkX3OXE7RC_cjs = require('./chunk-X3OXE7RC.cjs'); var chunkIA2QFTAP_cjs = require('./chunk-IA2QFTAP.cjs'); var chunk6HPSLDQJ_cjs = require('./chunk-6HPSLDQJ.cjs'); var chunkVNTHS4P6_cjs = require('./chunk-VNTHS4P6.cjs'); var chunkWIRM7U5E_cjs = require('./chunk-WIRM7U5E.cjs'); require('./chunk-UXP7MHY2.cjs'); var chunkPGPXHXQO_cjs = require('./chunk-PGPXHXQO.cjs'); var chunkXBXSZ3KO_cjs = require('./chunk-XBXSZ3KO.cjs'); require('./chunk-WWKWZZGO.cjs'); var chunk56NKJ4EF_cjs = require('./chunk-56NKJ4EF.cjs'); var chunkNVYHKPPB_cjs = require('./chunk-NVYHKPPB.cjs'); var chunkUY4YZAGJ_cjs = require('./chunk-UY4YZAGJ.cjs'); var chunk3IC45R4O_cjs = require('./chunk-3IC45R4O.cjs'); var chunk5FCXD6IS_cjs = require('./chunk-5FCXD6IS.cjs'); var chunkMGBDQDQV_cjs = require('./chunk-MGBDQDQV.cjs'); var chunkQS63TAWE_cjs = require('./chunk-QS63TAWE.cjs'); var chunk5EJG2R2X_cjs = require('./chunk-5EJG2R2X.cjs'); var chunkFMFL2BKY_cjs = require('./chunk-FMFL2BKY.cjs'); require('./chunk-KN3IJCL2.cjs'); var chunkFP33MEDB_cjs = require('./chunk-FP33MEDB.cjs'); var chunk4ZJVM6IL_cjs = require('./chunk-4ZJVM6IL.cjs'); require('./chunk-UTHARQAT.cjs'); var chunkLYSZZEJS_cjs = require('./chunk-LYSZZEJS.cjs'); var chunkVHGCXRFL_cjs = require('./chunk-VHGCXRFL.cjs'); var chunk6NWUZE5T_cjs = require('./chunk-6NWUZE5T.cjs'); var chunkNOCEWAE7_cjs = require('./chunk-NOCEWAE7.cjs'); var chunkZI2EZV3Q_cjs = require('./chunk-ZI2EZV3Q.cjs'); var chunkC7J4AZRB_cjs = require('./chunk-C7J4AZRB.cjs'); var chunkBKUPUS7G_cjs = require('./chunk-BKUPUS7G.cjs'); var chunkKMTY4OFY_cjs = require('./chunk-KMTY4OFY.cjs'); require('./chunk-DQDZRMTV.cjs'); var chunkUTXCFY2H_cjs = require('./chunk-UTXCFY2H.cjs'); var React27 = require('react'); var jsxRuntime = require('react/jsx-runtime'); var chatSdkJavascript = require('@cometchat/chat-sdk-javascript'); var cardsReact = require('@cometchat/cards-react'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var React27__default = /*#__PURE__*/_interopDefault(React27); require('./index.css'); var CometChatThemeProvider = ({ theme: themeProp = "light", children }) => { const [internalTheme, setInternalTheme] = React27.useState(themeProp); React27.useEffect(() => { setInternalTheme(themeProp); }, [themeProp]); const setTheme = React27.useCallback((newTheme) => { setInternalTheme(newTheme); }, []); const contextValue = React27.useMemo( () => ({ theme: internalTheme, setTheme }), [internalTheme, setTheme] ); return /* @__PURE__ */ jsxRuntime.jsx(chunkZI2EZV3Q_cjs.CometChatThemeContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx("div", { "data-theme": internalTheme, className: "cometchat", children }) }); }; CometChatThemeProvider.displayName = "CometChatThemeProvider"; function LocaleProvider({ locale, localizeInstance, children }) { const language = locale ?? localizeInstance?.getDefaultLanguage() ?? new chunk4ZJVM6IL_cjs.CometChatLocalize().getDefaultLanguage(); const localize = React27.useMemo(() => { if (localizeInstance) { chunk4ZJVM6IL_cjs.CometChatLocalize.setSharedInstance(localizeInstance); return localizeInstance; } const existingShared = chunk4ZJVM6IL_cjs.CometChatLocalize.getSharedInstance(); if (existingShared) { if (existingShared.currentLanguage !== language) { existingShared.setCurrentLanguage(language); } return existingShared; } const instance = new chunk4ZJVM6IL_cjs.CometChatLocalize({ language }); chunk4ZJVM6IL_cjs.CometChatLocalize.setSharedInstance(instance); return instance; }, [localizeInstance, language]); const [translators, setTranslators] = React27.useState(() => { const translator = localize.getTranslators(); return { getLocalizedString: translator.t, tDateTimeParser: translator.tDateTimeParser, language: localize.currentLanguage, timezone: localize.getTimezone(), calendarObject: localize.getCalendarObject(), dateLocaleLanguage: localize.getDateLocaleLanguage() }; }); React27.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__ */ jsxRuntime.jsx(chunkKMTY4OFY_cjs.LocaleContext.Provider, { value: translators, children }); } var CometChatEventsProvider = ({ children }) => { const instanceId = React27.useId(); const subscribersRef = React27.useRef(/* @__PURE__ */ new Set()); const emit = React27.useCallback((event) => { subscribersRef.current.forEach((handler) => { try { handler(event); } catch (error) { chunkWIRM7U5E_cjs.CometChatLogger.error("CometChatEventsProvider", "Subscriber error", error); } }); }, []); const subscribe = React27.useCallback((handler) => { subscribersRef.current.add(handler); return () => { subscribersRef.current.delete(handler); }; }, []); const publish = React27.useCallback( (event) => { emit(event); }, [emit] ); React27.useEffect(() => { chunk4ZJVM6IL_cjs.CometChatUIKit._setEmit(emit); return () => { chunk4ZJVM6IL_cjs.CometChatUIKit._setEmit(null); }; }, [emit]); React27.useEffect(() => { const id = `CometChatEvents_msg_${instanceId}`; chatSdkJavascript.CometChat.addMessageListener( id, new chatSdkJavascript.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 () => { chatSdkJavascript.CometChat.removeMessageListener(id); }; }, [instanceId, emit]); React27.useEffect(() => { const id = `CometChatEvents_user_${instanceId}`; chatSdkJavascript.CometChat.addUserListener( id, new chatSdkJavascript.CometChat.UserListener({ onUserOnline: (user) => { emit({ type: "user/online", user }); }, onUserOffline: (user) => { emit({ type: "user/offline", user }); } }) ); return () => { chatSdkJavascript.CometChat.removeUserListener(id); }; }, [instanceId, emit]); React27.useEffect(() => { const id = `CometChatEvents_group_${instanceId}`; chatSdkJavascript.CometChat.addGroupListener( id, new chatSdkJavascript.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 () => { chatSdkJavascript.CometChat.removeGroupListener(id); }; }, [instanceId, emit]); React27.useEffect(() => { const id = `CometChatEvents_call_${instanceId}`; chatSdkJavascript.CometChat.addCallListener( id, new chatSdkJavascript.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 () => { chatSdkJavascript.CometChat.removeCallListener(id); }; }, [instanceId, emit]); React27.useEffect(() => { const id = `CometChatEvents_conn_${instanceId}`; chatSdkJavascript.CometChat.addConnectionListener( id, new chatSdkJavascript.CometChat.ConnectionListener({ onConnected: () => { emit({ type: "connection/connected" }); }, onDisconnected: () => { emit({ type: "connection/disconnected" }); } }) ); return () => { chatSdkJavascript.CometChat.removeConnectionListener(id); }; }, [instanceId, emit]); const contextValue = React27.useMemo(() => ({ subscribe, publish }), [subscribe, publish]); return /* @__PURE__ */ jsxRuntime.jsx(chunkVHGCXRFL_cjs.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 chunkNOCEWAE7_cjs.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 = chunkVHGCXRFL_cjs.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 = chunkVHGCXRFL_cjs.stripInvalidMentionFormats(result); return chunkVHGCXRFL_cjs.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 = chunkVHGCXRFL_cjs.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 = React27.useRef(null); const [isTruncated, setIsTruncated] = React27.useState(false); const [isExpanded, setIsExpanded] = React27.useState(false); const { getLocalizedString: getLocalizedString4 } = chunkKMTY4OFY_cjs.useLocale(); const loggedInUser = chunk4ZJVM6IL_cjs.useLoggedInUser(); const IframeContext = chunkC7J4AZRB_cjs.useCometChatFrameContext(); const getCurrentWindow = React27.useCallback(() => { return IframeContext.iframeWindow ?? window; }, [IframeContext.iframeWindow]); const effectiveText = React27.useMemo(() => { if (typeof text === "string") return text; try { return message?.getText() ?? ""; } catch { return ""; } }, [text, message]); React27.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]); React27.useEffect(() => { setIsExpanded(false); }, [effectiveText]); const formattedHtml = React27.useMemo(() => { return applyFormatters(effectiveText, textFormatters, message); }, [effectiveText, textFormatters, message]); const isSingleEmoji = React27.useMemo(() => detectSingleEmoji(effectiveText), [effectiveText]); const linkPreviews = React27.useMemo( () => extractLinkPreviews(message), [message] ); const translatedText = React27.useMemo( () => extractTranslation(message), [message] ); const formattedTranslation = React27.useMemo( () => translatedText ? applyFormatters(translatedText, textFormatters, message) : "", [translatedText, textFormatters, message] ); React27.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 = React27.useCallback(() => { setIsExpanded((prev) => !prev); }, []); const publish = chunkVHGCXRFL_cjs.usePublishEvent(); const mentionedUsers = React27.useMemo(() => { try { return message?.getMentionedUsers() ?? []; } catch { return []; } }, [message]); const handleTextClick = React27.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 = React27.useCallback( (url) => { if (url) { getCurrentWindow().open(url, "_blank", "noopener,noreferrer"); } }, [getCurrentWindow] ); const handleLinkPreviewKeyDown = React27.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__ */ jsxRuntime.jsxs("div", { className: rootClasses, children: [ linkPreviews.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "cometchat-text-bubble__link-preview-container", children: linkPreviews.map((preview) => /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("div", { className: "cometchat-text-bubble__link-preview-image-container", children: /* @__PURE__ */ jsxRuntime.jsx( "img", { className: "cometchat-text-bubble__link-preview-image", src: preview.image, alt: preview.title ?? "Link preview image", loading: "lazy", decoding: "async" } ) }), /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cometchat-text-bubble__link-preview-content", children: [ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cometchat-text-bubble__link-preview-text", children: [ preview.title && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "cometchat-text-bubble__link-preview-title", children: preview.title }), preview.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "cometchat-text-bubble__link-preview-description", children: preview.description }), /* @__PURE__ */ jsxRuntime.jsx("span", { className: "cometchat-text-bubble__link-preview-domain", children: getDomainFromUrl(preview.url) }) ] }), preview.favicon && !preview.image && /* @__PURE__ */ jsxRuntime.jsx( "img", { className: "cometchat-text-bubble__link-preview-favicon-large", src: preview.favicon, alt: "", loading: "lazy", decoding: "async" } ) ] }) ] }, preview.url )) }), translatedText ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cometchat-text-bubble__translation-container", children: [ /* @__PURE__ */ jsxRuntime.jsx( "div", { className: "cometchat-text-bubble__text", dangerouslySetInnerHTML: { __html: formattedHtml }, onClick: handleTextClick } ), /* @__PURE__ */ jsxRuntime.jsx("div", { className: "cometchat-text-bubble__translation-separator" }), /* @__PURE__ */ jsxRuntime.jsx("span", { className: "cometchat-text-bubble__translation-label", children: getLocalizedString4("message_translation_label") }), /* @__PURE__ */ jsxRuntime.jsx( "div", { className: "cometchat-text-bubble__translation-text", dangerouslySetInnerHTML: { __html: formattedTranslation }, onClick: handleTextClick } ) ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "cometchat-text-bubble__content", children: /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("div", { className: "cometchat-text-bubble__read-more", children: /* @__PURE__ */ jsxRuntime.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 chatSdkJavascript.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.8203 2.79448C15.9231 2.89759 15.9745 3.02353 15.9745 3.17231C15.9745 3.32098 15.9231 3.44531 15.8203 3.54531C15.7176 3.64531 15.5921 3.69531 15.444 3.69531H14.53V4.61481C14.53 4.76292 14.4784 4.88742 14.3753 4.98831C14.2722 5.08931 14.1463 5.13981 13.9975 5.13981C13.8488 5.13981 13.7245 5.08931 13.6245 4.98831C13.5245 4.88742 13.4745 4.76292 13.4745 4.61481V3.69531ZM10.3648 7.68148C10.6176 7.68148 10.8329 7.59298 11.0107 7.41598C11.1884 7.23909 11.2773 7.02431 11.2773 6.77165C11.2773 6.51887 11.1889 6.30359 11.012 6.12581C10.835 5.94803 10.6202 5.85915 10.3675 5.85915C10.1147 5.85915 9.89945 5.94759 9.72167 6.12448C9.54389 6.30148 9.455 6.51631 9.455 6.76898C9.455 7.02176 9.5435 7.23703 9.7205 7.41481C9.89739 7.59259 10.1122 7.68148 10.3648 7.68148ZM5.6315 7.68148C5.88428 7.68148 6.09956 7.59298 6.27733 7.41598C6.45511 7.23909 6.544 7.02431 6.544 6.77165C6.544 6.51887 6.4555 6.30359 6.2785 6.12581C6.10161 5.94803 5.88683 5.85915 5.63417 5.85915C5.38139 5.85915 5.16611 5.94759 4.98833 6.12448C4.81056 6.30148 4.72167 6.51631 4.72167 6.76898C4.72167 7.02176 4.81011 7.23703 4.987 7.41481C5.164 7.59259 5.37883 7.68148 5.6315 7.68148ZM7.99267 12.0925C8.61933 12.0925 9.19745 11.9481 9.727 11.6593C10.2566 11.3706 10.6873 10.9714 11.0193 10.4615C11.0858 10.334 11.0817 10.2092 11.007 10.087C10.9323 9.96476 10.8203 9.90365 10.671 9.90365H5.33083C5.17661 9.90365 5.06295 9.96476 4.98983 10.087C4.91661 10.2092 4.91522 10.3327 4.98567 10.4575C5.31345 10.9697 5.74261 11.3703 6.27317 11.6591C6.80372 11.948 7.37689 12.0925 7.99267 12.0925Z" fill="%23A1A1A1"/>%0A</g>%0A</svg>%0A'; // src/assets/reply.svg var reply_default = 'data:image/svg+xml,<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">%0A<mask id="mask0_740_8296" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="24" height="24">%0A<rect width="24" height="24" fill="%23D9D9D9"/>%0A</mask>%0A<g mask="url(%23mask0_740_8296)">%0A<path d="M6.37283 11.75L9.69608 15.073C9.84475 15.2218 9.91808 15.3959 9.91608 15.5953C9.91424 15.7946 9.83574 15.9718 9.68058 16.127C9.52541 16.2718 9.34974 16.3468 9.15358 16.352C8.95758 16.357 8.78199 16.282 8.62683 16.127L4.13258 11.6328C3.95174 11.4519 3.86133 11.241 3.86133 11C3.86133 10.759 3.95174 10.5481 4.13258 10.3673L8.62683 5.873C8.76533 5.73467 8.93674 5.66384 9.14108 5.6605C9.34558 5.65734 9.52541 5.72817 9.68058 5.873C9.83574 6.02817 9.91333 6.20642 9.91333 6.40775C9.91333 6.60892 9.83574 6.78709 9.68058 6.94225L6.37283 10.25H15.7498C17.0627 10.25 18.1825 10.7135 19.1093 11.6405C20.0363 12.5673 20.4998 13.6872 20.4998 15V17.75C20.4998 17.9628 20.428 18.141 20.2843 18.2845C20.1408 18.4282 19.9627 18.5 19.7498 18.5C19.537 18.5 19.3588 18.4282 19.2153 18.2845C19.0717 18.141 18.9998 17.9628 18.9998 17.75V15C18.9998 14.1025 18.6825 13.3365 18.0478 12.702C17.4133 12.0673 16.6473 11.75 15.7498 11.75H6.37283Z" fill="%23141414"/>%0A</g>%0A</svg>%0A'; // src/assets/reply_in_thread.svg var reply_in_thread_default = 'data:image/svg+xml,<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">%0A<g id="Base_Icon">%0A<mask id="mask0_3985_363515" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="25" height="24">%0A<rect id="Bounding box" x="0.00390625" width="24" height="24" fill="%23D9D9D9"/>%0A</mask>%0A<g mask="url(