@linktr.ee/messaging-react 2.3.0 → 2.3.3-rc-1779777764

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 (35) hide show
  1. package/dist/{Card-DdpdnSh_.js → Card-Bz1cMPwJ.js} +16 -16
  2. package/dist/{Card-DdpdnSh_.js.map → Card-Bz1cMPwJ.js.map} +1 -1
  3. package/dist/{Card-ot16XqS2.cjs → Card-CBWof9at.cjs} +2 -2
  4. package/dist/{Card-ot16XqS2.cjs.map → Card-CBWof9at.cjs.map} +1 -1
  5. package/dist/{Card-CexShqpK.cjs → Card-D2KIDqPs.cjs} +2 -2
  6. package/dist/{Card-CexShqpK.cjs.map → Card-D2KIDqPs.cjs.map} +1 -1
  7. package/dist/{Card-4takoN_-.js → Card-D9iWkHKh.js} +6 -6
  8. package/dist/{Card-4takoN_-.js.map → Card-D9iWkHKh.js.map} +1 -1
  9. package/dist/{Card-BuROm0u7.js → Card-DFG3rvzF.js} +19 -19
  10. package/dist/{Card-BuROm0u7.js.map → Card-DFG3rvzF.js.map} +1 -1
  11. package/dist/{Card-CgpHBx-W.cjs → Card-DgXoTo6c.cjs} +2 -2
  12. package/dist/{Card-CgpHBx-W.cjs.map → Card-DgXoTo6c.cjs.map} +1 -1
  13. package/dist/{LockedThumbnail-Drsh4B5o.js → LockedThumbnail-BkeCwTCw.js} +8 -8
  14. package/dist/{LockedThumbnail-Drsh4B5o.js.map → LockedThumbnail-BkeCwTCw.js.map} +1 -1
  15. package/dist/{LockedThumbnail-CydtYOSA.cjs → LockedThumbnail-CD9YTQ0r.cjs} +2 -2
  16. package/dist/{LockedThumbnail-CydtYOSA.cjs.map → LockedThumbnail-CD9YTQ0r.cjs.map} +1 -1
  17. package/dist/assets/index.css +1 -1
  18. package/dist/index-DuGzAVyy.cjs +18 -0
  19. package/dist/index-DuGzAVyy.cjs.map +1 -0
  20. package/dist/index-v6yofqub.js +8128 -0
  21. package/dist/index-v6yofqub.js.map +1 -0
  22. package/dist/index.cjs +1 -1
  23. package/dist/index.js +1 -1
  24. package/package.json +1 -1
  25. package/src/components/ChannelView.tsx +8 -2
  26. package/src/components/CustomMessage/CustomMessage.stories.tsx +140 -0
  27. package/src/components/CustomMessage/CustomMessageActions.tsx +35 -0
  28. package/src/components/CustomMessage/index.tsx +20 -15
  29. package/src/components/CustomMessageInput/CustomMessageInput.test.tsx +12 -11
  30. package/src/components/CustomMessageInput/index.tsx +7 -3
  31. package/src/styles.css +129 -19
  32. package/dist/index-BCbVXFHI.js +0 -4698
  33. package/dist/index-BCbVXFHI.js.map +0 -1
  34. package/dist/index-CQ913euH.cjs +0 -2
  35. package/dist/index-CQ913euH.cjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-BCbVXFHI.js","sources":["../src/providers/MessagingProvider.tsx","../src/hooks/useMessaging.ts","../src/hooks/useRestorePendingMessages.ts","../src/components/ChannelList/ChannelListContext.tsx","../src/hooks/useChannelStar.ts","../src/utils/formatRelativeTime.ts","../src/utils/getMessageDisplayText.ts","../src/utils/resolveParticipantDisplayName.ts","../src/components/Avatar/getAvatarEmoji.ts","../src/components/Avatar/index.tsx","../src/components/CustomMessage/MessageTag.tsx","../src/components/ChannelList/CustomChannelPreview.tsx","../src/components/ChannelList/index.tsx","../src/components/ActionButton/index.tsx","../src/components/IconButton/index.tsx","../src/components/CloseButton/index.tsx","../src/components/ChannelInfoDialog/index.tsx","../src/components/CustomDateSeparator/index.tsx","../src/hooks/useMessageVote.ts","../src/components/LockedAttachment/index.tsx","../src/components/AttachmentCard/utils/mimeType.ts","../src/components/AttachmentCard/utils/icons.ts","../src/components/AttachmentCard/MediaPlayer.tsx","../src/components/AttachmentCard/Thumbnail.tsx","../src/components/AttachmentCard/index.tsx","../src/components/MediaMessage/index.tsx","../src/components/LockedAttachment/types.ts","../src/components/CustomMessage/context.tsx","../src/components/CustomMessage/MessageVoteButtons.tsx","../src/components/CustomMessage/index.tsx","../src/components/CustomLinkPreviewList/index.tsx","../src/components/CustomMessageInput/index.tsx","../src/components/CustomSystemMessage/index.tsx","../src/components/CustomTypingIndicator/DmAgentContext.ts","../src/components/CustomTypingIndicator/index.tsx","../src/components/MessagingShell/ChannelEmptyState.tsx","../src/components/Loading/index.tsx","../src/components/MessagingShell/LoadingState.tsx","../src/components/ChannelView.tsx","../src/components/MessagingShell/EmptyState.tsx","../src/components/MessagingShell/ErrorState.tsx","../src/components/MessagingShell/index.tsx","../src/components/LinkAttachment/components/_shared/normalizeExternalHref.ts","../src/components/LinkAttachment/components/_shared/CardCta.tsx","../src/components/LinkAttachment/components/_shared/CardBody.tsx","../src/components/LinkAttachment/components/_shared/CardShell.tsx","../src/components/LinkAttachment/components/_shared/CardThumbnail.tsx","../src/components/LinkAttachment/components/Composer/Card.tsx","../src/components/LinkAttachment/components/Received/Card.tsx","../src/components/LinkAttachment/components/Sent/Card.tsx","../src/components/LinkAttachment/index.tsx","../src/components/MessageAttachment/_shared/Bubble.tsx","../src/components/MessageAttachment/_shared/DismissButton.tsx","../src/components/MessageAttachment/types.ts","../src/components/MessageAttachment/Audio/index.tsx","../src/components/MessageAttachment/_shared/fileMeta.ts","../src/components/MessageAttachment/_shared/CompactDocumentRow.tsx","../src/components/MessageAttachment/_shared/triggerDownload.ts","../src/components/MessageAttachment/File/index.tsx","../src/components/MessageAttachment/_shared/CarouselNav.tsx","../src/components/MessageAttachment/_shared/DownloadAction.tsx","../src/components/MessageAttachment/_shared/useCarousel.ts","../src/components/MessageAttachment/_shared/ViewerShell.tsx","../src/components/MessageAttachment/_shared/ImageViewer.tsx","../src/components/MessageAttachment/_shared/MediaStackGrid.tsx","../src/components/MessageAttachment/_shared/useViewer.ts","../src/components/MessageAttachment/Image/index.tsx","../src/components/MessageAttachment/_shared/PdfViewer.tsx","../src/components/MessageAttachment/Pdf/index.tsx","../src/components/MessageAttachment/_shared/VideoViewer.tsx","../src/components/MessageAttachment/Video/index.tsx","../src/components/MessageAttachment/index.tsx","../src/components/FaqList/FaqListItem.tsx","../src/components/FaqList/index.tsx"],"sourcesContent":["import { StreamChatService } from '@linktr.ee/messaging-core'\nimport React, {\n createContext,\n useContext,\n useEffect,\n useState,\n useRef,\n useCallback,\n} from 'react'\nimport type { StreamChat } from 'stream-chat'\nimport { Chat } from 'stream-chat-react'\n\nimport type { MessagingProviderProps, MessagingCapabilities } from '../types'\n\n/**\n * Context value for messaging state and service\n */\nexport interface MessagingContextValue {\n service: StreamChatService | null\n client: StreamChat | null // Stream Chat client\n isConnected: boolean\n isLoading: boolean\n error: string | null\n capabilities: MessagingCapabilities\n refreshConnection: () => Promise<void>\n debug: boolean\n}\n\nconst MessagingContext = createContext<MessagingContextValue>({\n service: null,\n client: null,\n isConnected: false,\n isLoading: false,\n error: null,\n capabilities: {},\n refreshConnection: async () => {},\n debug: false,\n})\n\n/**\n * Hook to access messaging context\n */\nexport const useMessagingContext = () => useContext(MessagingContext)\n\n/**\n * Provider component that wraps messaging-core with React state management\n */\nexport const MessagingProvider: React.FC<MessagingProviderProps> = ({\n children,\n user,\n serviceConfig,\n apiKey,\n capabilities = {},\n debug = false,\n}) => {\n // Create debug logger that respects the debug prop\n const debugLog = useCallback(\n (message: string, ...args: unknown[]) => {\n if (debug) {\n console.log(`🔥 [MessagingProvider] ${message}`, ...args)\n }\n },\n [debug]\n )\n\n debugLog('🔄 RENDER START', {\n userId: user?.id,\n apiKey: apiKey?.substring(0, 8) + '...',\n serviceConfig: !!serviceConfig,\n capabilities: Object.keys(capabilities),\n })\n\n const [service, setService] = useState<StreamChatService | null>(null)\n const [client, setClient] = useState<StreamChat | null>(null)\n const [isConnected, setIsConnected] = useState(false)\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n // Prevent multiple concurrent connection attempts\n const connectingRef = useRef(false)\n\n // Track renders and prop changes\n const prevPropsRef = useRef({\n userId: user?.id,\n apiKey,\n serviceConfig,\n capabilities,\n })\n const renderCountRef = useRef(0)\n renderCountRef.current++\n\n debugLog('📊 RENDER INFO', {\n renderCount: renderCountRef.current,\n currentProps: { userId: user?.id, apiKey: apiKey?.substring(0, 8) + '...' },\n propChanges: {\n userChanged: prevPropsRef.current.userId !== user?.id,\n apiKeyChanged: prevPropsRef.current.apiKey !== apiKey,\n serviceConfigChanged:\n prevPropsRef.current.serviceConfig !== serviceConfig,\n capabilitiesChanged: prevPropsRef.current.capabilities !== capabilities,\n },\n })\n\n prevPropsRef.current = {\n userId: user?.id,\n apiKey,\n serviceConfig,\n capabilities,\n }\n\n // Initialize service when config changes\n useEffect(() => {\n const currentRender = renderCountRef.current\n debugLog('🔧 SERVICE INIT EFFECT TRIGGERED', {\n renderCount: currentRender,\n apiKey: !!apiKey,\n serviceConfig: !!serviceConfig,\n dependencies: {\n apiKey: apiKey?.substring(0, 8) + '...',\n serviceConfigRef: serviceConfig,\n serviceConfigStable:\n prevPropsRef.current.serviceConfig === serviceConfig,\n apiKeyStable: prevPropsRef.current.apiKey === apiKey,\n },\n })\n\n if (!apiKey || !serviceConfig) {\n debugLog('⚠️ SERVICE INIT SKIPPED', {\n renderCount: currentRender,\n reason: 'Missing apiKey or serviceConfig',\n })\n return\n }\n\n debugLog('🚀 CREATING NEW SERVICE', {\n renderCount: currentRender,\n apiKey: apiKey?.substring(0, 8) + '...',\n serviceConfigChanged:\n prevPropsRef.current.serviceConfig !== serviceConfig,\n })\n\n const newService = new StreamChatService({\n ...serviceConfig,\n apiKey,\n debug,\n })\n\n setService(newService)\n debugLog('✅ SERVICE SET', {\n renderCount: currentRender,\n serviceInstance: !!newService,\n })\n\n return () => {\n debugLog('🧹 SERVICE CLEANUP', {\n renderCount: currentRender,\n reason: 'Effect cleanup',\n })\n newService.disconnectUser().catch(console.error)\n }\n }, [apiKey, serviceConfig, debug, debugLog]) // Use serviceConfig object directly, not individual properties\n\n // Track if we've already connected this user with this service to prevent duplicate connections\n const connectedUserRef = useRef<{\n serviceId: StreamChatService\n userId: string\n } | null>(null)\n\n // Connect user when service and user are available\n useEffect(() => {\n debugLog('🔗 USER CONNECTION EFFECT TRIGGERED', {\n hasService: !!service,\n hasUser: !!user,\n userId: user?.id,\n isConnecting: connectingRef.current,\n isConnected: isConnected,\n dependencies: { service: !!service, userId: user?.id },\n })\n\n if (!service || !user) {\n debugLog('⚠️ USER CONNECTION SKIPPED', 'Missing service or user')\n return\n }\n\n if (connectingRef.current) {\n debugLog('⚠️ USER CONNECTION SKIPPED', 'Already connecting')\n return\n }\n\n // Check if we've already connected this exact user with this exact service instance\n if (\n connectedUserRef.current?.serviceId === service &&\n connectedUserRef.current?.userId === user.id\n ) {\n debugLog(\n '⚠️ USER CONNECTION SKIPPED',\n 'Already connected this user with this service'\n )\n return\n }\n\n const connectUser = async () => {\n debugLog('🚀 STARTING USER CONNECTION', { userId: user.id })\n connectingRef.current = true\n setIsLoading(true)\n setError(null)\n\n try {\n debugLog('📞 CALLING SERVICE.CONNECTUSER', { userId: user.id })\n const streamClient = await service.connectUser(user)\n setClient(streamClient)\n setIsConnected(true)\n connectedUserRef.current = { serviceId: service, userId: user.id } // Mark as connected\n debugLog('✅ USER CONNECTION SUCCESS', {\n userId: user.id,\n clientId: streamClient.userID,\n })\n } catch (err) {\n const errorMessage =\n err instanceof Error ? err.message : 'Connection failed'\n setError(errorMessage)\n debugLog('❌ USER CONNECTION ERROR', {\n userId: user.id,\n error: errorMessage,\n })\n } finally {\n setIsLoading(false)\n connectingRef.current = false\n debugLog('🔄 USER CONNECTION FINISHED', {\n userId: user.id,\n isConnected,\n })\n }\n }\n\n connectUser()\n }, [service, user, debugLog, isConnected]) // Remove isConnected to prevent circular dependency\n\n // Disconnect when user is removed (cleanup effect)\n useEffect(() => {\n debugLog('🔌 CLEANUP EFFECT REGISTERED', {\n hasService: !!service,\n isConnected,\n })\n return () => {\n if (service && isConnected) {\n debugLog(\n '🧹 CLEANUP EFFECT TRIGGERED',\n 'Cleaning up connection on unmount'\n )\n connectedUserRef.current = null // Reset connection tracking\n service.disconnectUser().catch(console.error)\n } else {\n debugLog('🔇 CLEANUP EFFECT SKIPPED', {\n hasService: !!service,\n isConnected,\n })\n }\n }\n }, [service, isConnected, debugLog])\n\n const refreshConnection = useCallback(async () => {\n debugLog('🔄 REFRESH CONNECTION CALLED', {\n hasService: !!service,\n hasUser: !!user,\n })\n\n if (!service || !user) {\n debugLog('⚠️ REFRESH CONNECTION SKIPPED', 'Missing service or user')\n return\n }\n\n debugLog('🚀 STARTING CONNECTION REFRESH', { userId: user.id })\n setIsLoading(true)\n try {\n debugLog('🔌 DISCONNECTING FOR REFRESH')\n await service.disconnectUser()\n debugLog('📞 RECONNECTING FOR REFRESH')\n const streamClient = await service.connectUser(user)\n setClient(streamClient)\n setIsConnected(true)\n setError(null)\n debugLog('✅ CONNECTION REFRESH SUCCESS', { userId: user.id })\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Refresh failed'\n setError(errorMessage)\n debugLog('❌ CONNECTION REFRESH ERROR', {\n userId: user.id,\n error: errorMessage,\n })\n } finally {\n setIsLoading(false)\n debugLog('🔄 CONNECTION REFRESH FINISHED', { userId: user.id })\n }\n }, [service, user, debugLog])\n\n // Memoize context value to prevent unnecessary re-renders\n const contextValue: MessagingContextValue = React.useMemo(() => {\n debugLog('💫 CONTEXT VALUE MEMOIZATION', {\n hasService: !!service,\n hasClient: !!client,\n isConnected,\n isLoading,\n hasError: !!error,\n capabilitiesKeys: Object.keys(capabilities),\n })\n\n return {\n service,\n client,\n isConnected,\n isLoading,\n error,\n capabilities,\n refreshConnection,\n debug,\n }\n }, [\n service,\n client,\n isConnected,\n isLoading,\n error,\n capabilities,\n refreshConnection,\n debug,\n debugLog,\n ])\n\n debugLog('🔄 RENDER END', {\n renderCount: renderCountRef.current,\n willRenderChat: !!(client && isConnected),\n contextValueReady: !!contextValue,\n })\n\n return (\n <MessagingContext.Provider value={contextValue}>\n {client && isConnected ? (\n <Chat\n client={client}\n customClasses={{\n channelList:\n 'str-chat__channel-list str-chat__channel-list-react bg-transparent lg:border-r-2 border-r-0 border-[#0000000A]',\n }}\n >\n {children}\n </Chat>\n ) : (\n children\n )}\n </MessagingContext.Provider>\n )\n}\n","import { useMessagingContext } from '../providers/MessagingProvider';\nimport type { MessagingContextValue } from '../providers/MessagingProvider';\n\n/**\n * Hook to access messaging service and state\n */\nexport const useMessaging = (): MessagingContextValue => {\n return useMessagingContext();\n};\n","import type { Channel } from 'stream-chat'\n\n/**\n * Restores pending messages into the channel's visible message list so they\n * appear as if they were already sent.\n *\n * Stream's pending-messages feature removes messages from the channel view\n * once client state is lost (e.g. page refresh). This function works around\n * that limitation by reading `channel.state.pending_messages` and\n * re-inserting them via `channel.state.addMessageSorted`.\n */\nexport function restorePendingMessages(channel: Channel) {\n const pendingMessages = channel.state.pending_messages\n if (!pendingMessages?.length) return\n\n for (const pending of pendingMessages) {\n channel.state.addMessageSorted(pending.message)\n }\n}\n","import React from 'react'\nimport type { Channel, LocalMessage } from 'stream-chat'\n\ntype ChannelListContextValue = {\n selectedChannel?: Channel | null\n onChannelSelect: (channel: Channel) => void\n debug: boolean\n renderMessagePreview?: (\n message: LocalMessage | undefined,\n defaultPreview?: string\n ) => React.ReactNode\n viewerLanguage?: string\n}\n\nconst ChannelListContext = React.createContext<ChannelListContextValue>({\n selectedChannel: undefined,\n onChannelSelect: () => {},\n debug: false,\n renderMessagePreview: undefined,\n viewerLanguage: undefined,\n})\n\nexport const ChannelListProvider = ChannelListContext.Provider\n\nexport const useChannelListContext = () => React.useContext(ChannelListContext)\n","import { useEffect, useState } from 'react'\nimport { Channel, Event } from 'stream-chat'\n\nexport const useChannelStar = (channel?: Channel) => {\n const [isChannelStarred, setIsChannelStarred] = useState(\n !!channel?.state?.membership?.pinned_at\n )\n\n useEffect(() => {\n if (!channel) {\n setIsChannelStarred(false)\n return\n }\n\n setIsChannelStarred(!!channel.state.membership?.pinned_at)\n\n const handleMemberUpdate = (event: Event) => {\n setIsChannelStarred(\n event?.member ? !!event.member.pinned_at : !!channel.state.membership?.pinned_at\n )\n }\n\n channel.on('member.updated', handleMemberUpdate)\n\n return () => {\n channel.off('member.updated', handleMemberUpdate)\n }\n }, [channel])\n\n return isChannelStarred\n}\n","/**\n * Get the number of days between two dates (calendar days in UTC, not 24-hour periods)\n * Uses UTC to ensure consistent day calculations globally since messages are stored in UTC\n */\nconst getDaysDifference = (date1: Date, date2: Date): number => {\n const d1 = new Date(\n Date.UTC(date1.getUTCFullYear(), date1.getUTCMonth(), date1.getUTCDate())\n )\n const d2 = new Date(\n Date.UTC(date2.getUTCFullYear(), date2.getUTCMonth(), date2.getUTCDate())\n )\n const diffTime = d2.getTime() - d1.getTime()\n return Math.floor(diffTime / (1000 * 60 * 60 * 24))\n}\n\n/**\n * Format a date - shows time for today, relative time for older messages\n * (e.g., \"Just now\", \"2:08 PM\" for today, \"Yesterday\" for yesterday, \"2d\" for 2 days ago)\n */\nexport const formatRelativeTime = (date: Date): string => {\n const now = new Date()\n const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000)\n\n // Less than 1 minute\n if (diffInSeconds < 60) {\n return 'Just now'\n }\n\n // Check if it's today (same calendar day)\n const daysDiff = getDaysDifference(date, now)\n\n // If today, show time in 12-hour format (e.g., \"8:40 PM\")\n if (daysDiff === 0) {\n return date.toLocaleTimeString([], {\n hour: 'numeric',\n minute: '2-digit',\n hour12: true,\n })\n }\n\n // Yesterday\n if (daysDiff === 1) {\n return 'Yesterday'\n }\n\n // Less than 7 days - show days\n if (daysDiff < 7) {\n return `${daysDiff}d`\n }\n\n // Less than 4 weeks - show weeks\n if (daysDiff < 28) {\n const weeks = Math.floor(daysDiff / 7)\n return `${weeks}w`\n }\n\n // More than 4 weeks - show date as MM/DD/YY\n return date.toLocaleDateString('en-US', {\n month: 'numeric',\n day: 'numeric',\n year: '2-digit',\n })\n}\n","import type { LocalMessage } from 'stream-chat'\n\ntype MessageWithI18n = Pick<LocalMessage, 'text'> & {\n i18n?: Record<string, string> | null\n}\n\nexport function normalizeLanguageCode(language?: string): string | undefined {\n const normalized = language?.trim().toLowerCase().split(/[-_]/)[0]\n return normalized || undefined\n}\n\nexport function getMessageDisplayText({\n message,\n viewerLanguage,\n}: {\n message?: MessageWithI18n | null\n viewerLanguage?: string\n}): string | undefined {\n const fallbackText = message?.text\n const normalizedLanguage = normalizeLanguageCode(viewerLanguage)\n\n if (!normalizedLanguage) {\n return fallbackText\n }\n\n return message?.i18n?.[`${normalizedLanguage}_text`] ?? fallbackText\n}\n","const UUID_REGEX =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i\n\nexport type ParticipantDisplayUser = {\n id?: string\n name?: string | null\n username?: string | null\n}\n\nexport function isUuidLike(value: string): boolean {\n return UUID_REGEX.test(value.trim())\n}\n\nfunction isUsableDisplayValue(\n value: string | null | undefined,\n userId?: string\n): value is string {\n const trimmed = value?.trim()\n if (!trimmed) return false\n if (userId && trimmed === userId) return false\n return !isUuidLike(trimmed)\n}\n\n/**\n * Resolves a human-readable participant label from Stream user fields.\n * Never falls back to user.id. UUID-like names are treated as missing.\n */\nexport function resolveParticipantDisplayName(\n user?: ParticipantDisplayUser | null\n): string {\n if (isUsableDisplayValue(user?.name, user?.id)) {\n return user.name.trim()\n }\n\n if (isUsableDisplayValue(user?.username, user?.id)) {\n return user.username.trim()\n }\n\n return 'Unknown member'\n}\n","/**\n * Generate a fruit emoji based on a string id\n * Returns a consistent fruit emoji for the same id\n */\nconst EMOJIS = [\n '🍎', // Apple\n '🍌', // Banana\n '🍇', // Grape\n '🍊', // Orange\n '🍓', // Strawberry\n '🥥', // Coconut\n '🍒', // Cherry\n '🥭', // Mango\n '🍉', // Watermelon\n '🍋', // Lemon\n '🥝', // Kiwi\n '🫒', // Olive\n '🍈', // Melon\n]\n\n/**\n * Simple hash function to convert string to number\n */\nfunction hashString(str: string): number {\n let hash = 0\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i)\n hash = (hash << 5) - hash + char\n hash = hash & hash // Convert to 32-bit integer\n }\n return Math.abs(hash)\n}\n\n/**\n * Get a fruit emoji based on an id string\n * @param id - The string id to generate emoji from\n * @returns A fruit emoji string\n */\nexport function getAvatarEmoji(id: string): string {\n const hash = hashString(id)\n const index = hash % EMOJIS.length\n return EMOJIS[index]\n}\n\n","import { StarIcon } from '@phosphor-icons/react'\nimport classNames from 'classnames'\nimport React from 'react'\n\nimport { getAvatarEmoji } from './getAvatarEmoji'\n\nexport interface AvatarProps {\n id: string\n name: string\n image?: string\n size?: number\n className?: string\n starred?: boolean\n shape?: 'squircle' | 'circle'\n dmAgentEnabled?: boolean\n}\n\n/**\n * Avatar component that displays a user image or colored initial fallback\n */\nexport const Avatar = ({\n id,\n image,\n size = 40,\n className,\n starred = false,\n shape = 'squircle',\n dmAgentEnabled = false,\n}: AvatarProps) => {\n const emoji = getAvatarEmoji(id)\n\n const getFontSizeClass = () => {\n if (size < 32) return 'text-xs'\n if (size < 56) return 'text-sm'\n if (size < 120) return 'text-lg'\n return 'text-4xl'\n }\n\n const fontSizeClass = getFontSizeClass()\n // Match design treatment: 2px AI border at 40px avatar size.\n const aiBorderWidth = size >= 40 ? 2 : 1\n\n const borderStyle =\n shape === 'circle'\n ? { borderRadius: '50%' }\n : {\n borderRadius: '33%',\n cornerShape: 'superellipse(1.3)',\n }\n\n const avatarInner = (\n <div className=\"h-full w-full overflow-hidden\" style={borderStyle}>\n {image ? (\n <img\n src={image}\n alt=\"\"\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div\n aria-hidden=\"true\"\n className={classNames(\n 'avatar-fallback flex h-full w-full items-center justify-center bg-[#E6E5E3] font-semibold select-none transition-colors',\n fontSizeClass\n )}\n >\n {emoji}\n </div>\n )}\n </div>\n )\n\n return (\n <div\n className={classNames(\n 'relative flex-shrink-0 !bg-transparent',\n className\n )}\n style={{\n '--str-chat__avatar-size': `${size}px`,\n width: `${size}px`,\n height: `${size}px`,\n } as React.CSSProperties}\n >\n {starred && (\n <div\n aria-hidden=\"true\"\n className=\"absolute -left-1.5 -top-1.5 z-10 flex size-5 items-center justify-center rounded-full bg-white shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_4px_8px_rgba(0,0,0,0.06)]\"\n >\n <StarIcon className=\"size-3 text-yellow-600\" weight=\"duotone\" />\n </div>\n )}\n <div\n data-testid=\"avatar-ring\"\n className={classNames(\n 'h-full w-full',\n 'bg-transparent'\n )}\n style={{\n ...borderStyle,\n ...(dmAgentEnabled && {\n borderWidth: `${aiBorderWidth}px`,\n borderStyle: 'solid',\n borderColor: 'var(--AI-Gradient, #7F22FE)',\n boxShadow: 'inset 0 1px 2px 0 rgba(255, 255, 255, 0.5)',\n }),\n }}\n >\n {avatarInner}\n </div>\n </div>\n )\n}\n","import { GiftIcon } from '@phosphor-icons/react'\nimport { LocalMessage } from 'stream-chat'\n\ninterface MessageTagProps {\n message: LocalMessage\n /** When true, renders as a standalone bubble instead of a small tag */\n standalone?: boolean\n /** When true, renders sender-side chatbot variants */\n isMyMessage?: boolean\n /** Whether the message includes an attachment */\n hasAttachment?: boolean\n}\n\nconst SparkleIcon = ({ size = 15 }: { size?: number }) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 15 15\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M12.003 9a.985.985 0 0 1-.652.934l-3.223 1.191-1.188 3.226a.995.995 0 0 1-1.867 0l-1.195-3.226L.65 9.937a.995.995 0 0 1 0-1.867l3.227-1.195 1.187-3.226a.995.995 0 0 1 1.868 0l1.195 3.226 3.226 1.187a.99.99 0 0 1 .649.938m3-5.83a.52.52 0 0 1-.344.492l-1.702.63-.627 1.703a.525.525 0 0 1-.986 0l-.63-1.704-1.704-.627a.525.525 0 0 1 0-.986l1.703-.63.627-1.704a.526.526 0 0 1 .986 0l.631 1.703 1.704.627a.52.52 0 0 1 .342.495\"\n fill=\"currentColor\"\n fillOpacity={0.55}\n />\n </svg>\n)\n\n/** Check if a message is a tip based on metadata */\nexport const isTipMessage = (message: LocalMessage): boolean => {\n return message.metadata?.custom_type === 'MESSAGE_TIP'\n}\n\n/** Check if a message is a paid message based on metadata */\nexport const isPaidMessage = (message: LocalMessage): boolean => {\n return message.metadata?.custom_type === 'MESSAGE_PAID'\n}\n\n/** Check if a message is a chatbot message based on metadata */\nexport const isChatbotMessage = (message: LocalMessage): boolean => {\n return message.metadata?.custom_type === 'MESSAGE_CHATBOT'\n}\n\n/** Check if a message is a locked attachment */\nexport const isAttachmentMessage = (message: LocalMessage): boolean => {\n return message.metadata?.custom_type === 'MESSAGE_ATTACHMENT'\n}\n\n/** Check if a message has a tip/paid tag (both render the same) */\nexport const isTipOrPaidMessage = (message: LocalMessage): boolean => {\n return isTipMessage(message) || isPaidMessage(message)\n}\n\n/** Check if a message is a tip/paid-only message (no text) */\nexport const isTipOnlyMessage = (message: LocalMessage): boolean => {\n return isTipOrPaidMessage(message) && !message.text?.trim()\n}\n\nexport const MessageTag = ({\n message,\n standalone = false,\n isMyMessage = false,\n hasAttachment = false,\n}: MessageTagProps) => {\n const isTipOrPaid = isTipOrPaidMessage(message)\n const isChatbot = isChatbotMessage(message)\n\n if (!isTipOrPaid && !isChatbot) {\n return null\n }\n\n if (isTipOrPaid) {\n const amountText = message.metadata?.amount_text\n if (!amountText) return null\n\n const className = standalone\n ? 'message-tip-standalone'\n : 'message-tag message-tag--tip'\n\n const label = standalone\n ? `${amountText} tip`\n : `Delivered with ${amountText} tip`\n\n return (\n <div className={className}>\n <GiftIcon size={standalone ? 14 : 12} />\n <span>{label}</span>\n </div>\n )\n }\n\n const isSenderAttachmentVariant = isMyMessage && hasAttachment\n const chatbotLabel = isSenderAttachmentVariant\n ? 'Sent with AI'\n : 'Sent with DM Agent'\n\n const chatbotClassName = [\n 'message-chatbot-indicator',\n isMyMessage\n ? 'message-chatbot-indicator--sender'\n : 'message-chatbot-indicator--receiver',\n isSenderAttachmentVariant\n ? 'message-chatbot-indicator--attachment'\n : 'message-chatbot-indicator--text',\n ].join(' ')\n\n const label = (\n <span className=\"message-chatbot-indicator__label\">{chatbotLabel}</span>\n )\n const icon = (\n <span className=\"message-chatbot-indicator__icon\">\n <SparkleIcon size={isSenderAttachmentVariant ? 12 : 15} />\n </span>\n )\n\n // Chatbot indicator variant\n return (\n <div className={chatbotClassName} data-testid=\"message-chatbot-indicator\">\n {isMyMessage && !isSenderAttachmentVariant ? (\n <>\n {label}\n {icon}\n </>\n ) : (\n <>\n {icon}\n {label}\n </>\n )}\n </div>\n )\n}\n","import classNames from 'classnames'\nimport React from 'react'\nimport { ChannelPreviewUIComponentProps } from 'stream-chat-react'\n\nimport { useChannelStar } from '../../hooks/useChannelStar'\nimport { formatRelativeTime } from '../../utils/formatRelativeTime'\nimport { getMessageDisplayText } from '../../utils/getMessageDisplayText'\nimport { resolveParticipantDisplayName } from '../../utils/resolveParticipantDisplayName'\nimport { Avatar } from '../Avatar'\nimport { isChatbotMessage } from '../CustomMessage/MessageTag'\n\nimport { useChannelListContext } from './ChannelListContext'\n\n/**\n * Custom channel preview that handles selection\n */\nconst CustomChannelPreview = React.memo<ChannelPreviewUIComponentProps>(\n ({ channel, unread }) => {\n const {\n selectedChannel,\n onChannelSelect,\n debug,\n renderMessagePreview,\n viewerLanguage,\n } = useChannelListContext()\n\n const isSelected = selectedChannel?.id === channel?.id\n\n const handleClick = () => {\n if (channel) {\n onChannelSelect(channel)\n }\n }\n const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n const isActivationKey = event.key === 'Enter' || event.key === ' '\n const isRepeatedKeydown = event.repeat\n\n if (!isActivationKey || isRepeatedKeydown) return\n\n event.preventDefault()\n handleClick()\n }\n\n // Get participant info\n const members = Object.values(channel?.state?.members || {})\n const participant = members.find(\n (member) => member.user?.id && member.user.id !== channel?._client?.userID\n )\n const participantName = resolveParticipantDisplayName(participant?.user)\n const participantImage = participant?.user?.image\n\n // Get last non-system message for preview\n const lastMessage = (() => {\n const messages = channel?.state?.messages\n if (!messages?.length) return undefined\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].type !== 'system') return messages[i]\n }\n return undefined\n })()\n\n const getLastMessageText = () => {\n const displayText = getMessageDisplayText({\n message: lastMessage,\n viewerLanguage,\n })\n if (displayText) return displayText\n\n const isTip = lastMessage?.metadata?.custom_type === 'MESSAGE_TIP'\n if (isTip) return '💵 Sent a tip'\n\n const attachment = lastMessage?.attachments?.[0]\n if (attachment) {\n // Link previews - show the actual URL\n if (attachment.og_scrape_url) return attachment.og_scrape_url\n\n // Type-based detection\n if (attachment.type === 'image') return '📷 Sent an image'\n if (attachment.type === 'video') return '🎥 Sent a video'\n if (attachment.type === 'audio') return '🎵 Sent audio'\n if (attachment.type === 'file') return '📎 Sent a file'\n\n // Fallback\n return '📎 Sent an attachment'\n }\n\n return 'No messages yet'\n }\n\n const lastMessageText = getLastMessageText()\n const lastMessageTime = lastMessage?.created_at\n ? formatRelativeTime(new Date(lastMessage.created_at))\n : ''\n const isLastMessageFromChatbot = lastMessage\n ? isChatbotMessage(lastMessage)\n : false\n\n const messagePreview = renderMessagePreview\n ? renderMessagePreview(lastMessage, lastMessageText)\n : `${isLastMessageFromChatbot ? '✨ ' : ''}${lastMessageText}`\n const isChannelStarred = useChannelStar(channel)\n\n // Use the unread prop passed by Stream Chat (reactive and updates automatically)\n const unreadCount = unread ?? 0\n\n if (debug) {\n console.log('📺 [ChannelList] 📋 CHANNEL PREVIEW RENDER', {\n channelId: channel?.id,\n isSelected,\n participantName,\n unreadCount,\n hasTimestamp: !!lastMessageTime,\n })\n }\n\n return (\n <div\n role=\"button\"\n tabIndex={0}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n className={classNames(\n 'group w-full px-4 py-3 transition-colors text-left max-w-full overflow-hidden focus-ring',\n {\n 'bg-primary-alt/10 border-l-4 border-l-primary': isSelected,\n 'hover:bg-sand': !isSelected,\n }\n )}\n >\n <div className=\"flex items-start gap-3\">\n {/* Avatar */}\n <Avatar\n id={participant?.user?.id || channel.id || 'unknown'}\n name={participantName}\n image={participantImage}\n size={44}\n starred={isChannelStarred}\n className=\"[&_.avatar-fallback]:group-hover:bg-[#eeeeee]\"\n />\n\n {/* Content column */}\n <div className=\"flex-1 min-w-0 flex flex-col gap-1\">\n {/* Name and timestamp row */}\n <div className=\"flex items-center justify-between gap-2\">\n <h3\n className={classNames(\n 'text-sm font-medium truncate',\n isSelected ? 'text-primary' : 'text-charcoal'\n )}\n >\n {isChannelStarred && (\n <span className=\"sr-only\">Starred conversation. </span>\n )}\n {participantName}\n </h3>\n {lastMessageTime && (\n <span className=\"text-xs text-stone flex-shrink-0\">\n {lastMessageTime}\n </span>\n )}\n </div>\n\n {/* Message and unread badge row */}\n <div className=\"flex items-center justify-between gap-2 min-w-0\">\n <p className=\"text-xs text-stone flex-1 line-clamp-1\">\n {messagePreview}\n </p>\n {unreadCount > 0 && (\n <span className=\"bg-[#7f22fe] text-white text-xs px-2 py-0.5 rounded-full min-w-[20px] text-center flex-shrink-0\">\n {unreadCount > 99 ? '99+' : unreadCount}\n </span>\n )}\n </div>\n </div>\n </div>\n </div>\n )\n }\n)\n\nexport default CustomChannelPreview\nCustomChannelPreview.displayName = 'CustomChannelPreview'\n","import classNames from 'classnames'\nimport React from 'react'\nimport type { Channel } from 'stream-chat'\nimport { ChannelList as StreamChannelList } from 'stream-chat-react'\n\nimport { restorePendingMessages } from '../../hooks/useRestorePendingMessages'\nimport { useMessagingContext } from '../../providers/MessagingProvider'\nimport type { ChannelListProps } from '../../types'\n\nimport { ChannelListProvider } from './ChannelListContext'\nimport CustomChannelPreview from './CustomChannelPreview'\n\nconst DEFAULT_SORT = { last_message_at: -1 } as const\n\n/**\n * Channel list component with customizable header and actions\n */\nexport const ChannelList = React.memo<ChannelListProps>(\n ({\n onChannelSelect,\n selectedChannel,\n filters,\n allowNewMessagesFromUnfilteredChannels = false,\n onMessageNew,\n onAddedToChannel,\n channelRenderFilterFn,\n sort = DEFAULT_SORT,\n className,\n customEmptyStateIndicator,\n renderMessagePreview,\n viewerLanguage,\n }) => {\n // Track renders\n const renderCountRef = React.useRef(0)\n renderCountRef.current++\n\n // Get debug flag from context\n const { debug = false } = useMessagingContext()\n\n // Wrap channelRenderFilterFn to restore pending messages for all channels\n // as soon as they are loaded, without waiting for the user to click into each one.\n const wrappedChannelRenderFilterFn = React.useCallback(\n (channels: Channel[]) => {\n for (const channel of channels) {\n restorePendingMessages(channel)\n }\n return channelRenderFilterFn\n ? channelRenderFilterFn(channels)\n : channels\n },\n [channelRenderFilterFn]\n )\n\n if (debug) {\n console.log('📺 [ChannelList] 🔄 RENDER START', {\n renderCount: renderCountRef.current,\n selectedChannelId: selectedChannel?.id,\n filters,\n })\n }\n\n const contextValue = React.useMemo(\n () => ({\n selectedChannel,\n onChannelSelect,\n debug,\n renderMessagePreview,\n viewerLanguage,\n }),\n [\n selectedChannel,\n onChannelSelect,\n debug,\n renderMessagePreview,\n viewerLanguage,\n ]\n )\n\n return (\n <div\n className={classNames(\n 'messaging-channel-list h-full flex flex-col min-w-0 overflow-hidden',\n className\n )}\n >\n {/* Channel List */}\n <div className=\"flex-1 overflow-hidden min-w-0\">\n <ChannelListProvider value={contextValue}>\n <StreamChannelList\n key={`${JSON.stringify(filters)}:${JSON.stringify(sort)}`}\n filters={filters}\n sort={sort}\n options={{ limit: 30 }}\n allowNewMessagesFromUnfilteredChannels={\n allowNewMessagesFromUnfilteredChannels\n }\n onMessageNew={onMessageNew}\n onAddedToChannel={onAddedToChannel}\n channelRenderFilterFn={wrappedChannelRenderFilterFn}\n Preview={CustomChannelPreview}\n EmptyStateIndicator={customEmptyStateIndicator}\n />\n </ChannelListProvider>\n </div>\n </div>\n )\n }\n)\nChannelList.displayName = 'ChannelList'\n","import classNames from \"classnames\";\nimport React from \"react\";\n\nexport interface ActionButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: \"default\" | \"danger\";\n}\n\nconst ActionButton = ({\n variant = \"default\",\n className,\n children,\n ...rest\n}: ActionButtonProps) => {\n const isDanger = variant === \"danger\";\n return (\n <button\n type=\"button\"\n className={classNames(\n \"flex w-full items-center gap-3 rounded-lg px-4 py-3 text-left text-sm transition-colors focus-ring disabled:cursor-not-allowed disabled:opacity-60\",\n isDanger\n ? \"text-danger hover:bg-danger/50\"\n : \"text-charcoal hover:bg-sand\",\n className,\n )}\n {...rest}\n >\n {children}\n </button>\n );\n};\n\nexport default ActionButton;\n","import classNames from \"classnames\";\nimport React from \"react\";\n\ninterface IconButtonProps\n extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n \"type\" | \"children\"\n > {\n label: string;\n children: React.ReactNode;\n className?: string;\n}\n\nexport function IconButton({ label, className, children, ...rest }: IconButtonProps) {\n return (\n <button\n type=\"button\"\n className={classNames(\n \"inline-flex items-center justify-center rounded-full transition-colors focus-ring p-2\",\n {\n \"cursor-not-allowed opacity-50\": rest.disabled,\n \"hover:bg-sand\": !rest.disabled,\n },\n className,\n )}\n {...rest}\n >\n <span className=\"sr-only\">{label}</span>\n {children}\n </button>\n );\n}","import { XIcon } from '@phosphor-icons/react'\n\nimport { IconButton } from '../IconButton'\n\ninterface CloseButtonProps {\n onClick: () => void\n}\n\nexport function CloseButton({ onClick }: CloseButtonProps) {\n return (\n <IconButton label=\"Close\" onClick={onClick} className=\"p-1\">\n <XIcon className=\"h-5 w-5 text-stone\" weight=\"bold\" />\n </IconButton>\n )\n}\n","import {\n FlagIcon,\n ProhibitInsetIcon,\n SignOutIcon,\n SpinnerGapIcon,\n} from '@phosphor-icons/react'\nimport React, { useState, useCallback, useEffect } from 'react'\nimport type { Channel as ChannelType, ChannelMemberResponse } from 'stream-chat'\n\nimport { useMessagingContext } from '../../providers/MessagingProvider'\nimport { resolveParticipantDisplayName } from '../../utils/resolveParticipantDisplayName'\nimport ActionButton from '../ActionButton'\nimport { Avatar } from '../Avatar'\nimport { CloseButton } from '../CloseButton'\n\n// Custom user type with username\ntype CustomUser = {\n username?: string\n}\n\n// Blocked user from Stream Chat API\ntype BlockedUser = {\n blocked_user_id: string\n}\n\nexport interface ChannelInfoDialogProps {\n dialogRef: React.RefObject<HTMLDialogElement>\n onClose: () => void\n participant: ChannelMemberResponse | undefined\n participantDisplayName?: string\n channel: ChannelType\n followerStatusLabel?: string\n onLeaveConversation?: (channel: ChannelType) => void\n onBlockParticipant?: (participantId?: string) => void\n showDeleteConversation?: boolean\n onDeleteConversationClick?: () => void\n onBlockParticipantClick?: () => void\n onReportParticipantClick?: () => void\n customProfileContent?: React.ReactNode\n customChannelActions?: React.ReactNode\n}\n\n/**\n * Channel info dialog (matching original implementation)\n */\nexport const ChannelInfoDialog: React.FC<ChannelInfoDialogProps> = ({\n dialogRef,\n onClose,\n participant,\n participantDisplayName,\n channel,\n followerStatusLabel,\n onLeaveConversation,\n onBlockParticipant,\n showDeleteConversation = true,\n onDeleteConversationClick,\n onBlockParticipantClick,\n onReportParticipantClick,\n customProfileContent,\n customChannelActions,\n}) => {\n const { service, debug } = useMessagingContext()\n const [isParticipantBlocked, setIsParticipantBlocked] = useState(false)\n const [isLeaving, setIsLeaving] = useState(false)\n const [isUpdatingBlockStatus, setIsUpdatingBlockStatus] = useState(false)\n\n // Check if participant is blocked when participant changes\n const checkIsParticipantBlocked = useCallback(async () => {\n if (!service || !participant?.user?.id) return\n\n try {\n const blockedUsers = await service.getBlockedUsers()\n const isBlocked = blockedUsers.some(\n (user: BlockedUser) => user.blocked_user_id === participant?.user?.id\n )\n setIsParticipantBlocked(isBlocked)\n } catch (error) {\n console.error(\n '[ChannelInfoDialog] Failed to check blocked status:',\n error\n )\n }\n }, [service, participant?.user?.id])\n\n useEffect(() => {\n checkIsParticipantBlocked()\n }, [checkIsParticipantBlocked])\n\n const handleLeaveConversation = async () => {\n if (isLeaving) return\n\n // Fire analytics callback before action\n onDeleteConversationClick?.()\n\n if (debug) {\n console.log('[ChannelInfoDialog] Leave conversation', channel.cid)\n }\n setIsLeaving(true)\n\n try {\n const actingUserId = channel._client?.userID ?? null\n await channel.hide(actingUserId, false)\n\n if (onLeaveConversation) {\n await onLeaveConversation(channel)\n }\n\n onClose()\n } catch (error) {\n console.error('[ChannelInfoDialog] Failed to leave conversation', error)\n } finally {\n setIsLeaving(false)\n }\n }\n\n const handleBlockUser = async () => {\n if (isUpdatingBlockStatus || !service) return\n\n // Fire analytics callback before action\n onBlockParticipantClick?.()\n\n if (debug) {\n console.log('[ChannelInfoDialog] Block member', participant?.user?.id)\n }\n setIsUpdatingBlockStatus(true)\n\n try {\n await service.blockUser(participant?.user?.id)\n\n if (onBlockParticipant) {\n await onBlockParticipant(participant?.user?.id)\n }\n\n onClose()\n } catch (error) {\n console.error('[ChannelInfoDialog] Failed to block member', error)\n } finally {\n setIsUpdatingBlockStatus(false)\n }\n }\n\n const handleUnblockUser = async () => {\n if (isUpdatingBlockStatus || !service) return\n\n // Fire analytics callback before action\n onBlockParticipantClick?.()\n\n if (debug) {\n console.log('[ChannelInfoDialog] Unblock member', participant?.user?.id)\n }\n setIsUpdatingBlockStatus(true)\n\n try {\n await service.unBlockUser(participant?.user?.id)\n\n if (onBlockParticipant) {\n await onBlockParticipant(participant?.user?.id)\n }\n\n onClose()\n } catch (error) {\n console.error('[ChannelInfoDialog] Failed to unblock member', error)\n } finally {\n setIsUpdatingBlockStatus(false)\n }\n }\n\n const handleReportUser = () => {\n // Fire analytics callback before action\n onReportParticipantClick?.()\n\n onClose()\n window.open(\n 'https://linktr.ee/s/about/trust-center/report',\n '_blank',\n 'noopener,noreferrer'\n )\n }\n\n if (!participant) return null\n\n const participantName =\n participantDisplayName ??\n resolveParticipantDisplayName(participant.user)\n const participantImage = participant.user?.image\n const participantUsername = (participant.user as CustomUser)?.username\n const participantSecondary = participantUsername\n ? `linktr.ee/${participantUsername}`\n : undefined\n const participantId = participant.user?.id || 'unknown'\n\n return (\n // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions\n <dialog\n ref={dialogRef}\n className=\"mes-dialog group\"\n onClose={onClose}\n onClick={(e) => {\n if (e.target === dialogRef.current) {\n onClose()\n }\n }}\n >\n <div className=\"ml-auto flex h-full w-full flex-col bg-white shadow-none transition-shadow duration-200 group-open:shadow-max-elevation-light\">\n <div className=\"flex items-center justify-between border-b border-sand px-4 py-3\">\n <h2 className=\"text-base font-semibold text-charcoal\">Chat info</h2>\n <CloseButton onClick={onClose} />\n </div>\n\n <div className=\"flex-1 px-2 overflow-y-auto w-full\">\n <div\n className=\"flex flex-col items-center gap-3 self-stretch px-4 py-2 mt-6 rounded-lg border border-black/[0.04]\"\n style={{ backgroundColor: '#FBFAF9' }}\n >\n <div className=\"flex items-center gap-3 w-full\">\n <Avatar\n id={participantId}\n name={participantName}\n image={participantImage}\n size={88}\n shape=\"circle\"\n />\n <div className=\"flex flex-col min-w-0 flex-1\">\n <p className=\"truncate text-base font-semibold text-charcoal\">\n {participantName}\n </p>\n {participantSecondary && (\n <p className=\"truncate text-sm text-[#00000055]\">\n {participantSecondary}\n </p>\n )}\n {/* Deprecated: use customProfileContent instead. followerStatusLabel will be removed in a future release. */}\n {followerStatusLabel && !customProfileContent && (\n <span\n className=\"mt-1 rounded-full text-xs font-normal w-fit\"\n style={{\n padding: '4px 8px',\n backgroundColor:\n followerStatusLabel === 'Subscribed to you'\n ? '#DCFCE7'\n : '#F5F5F4',\n color:\n followerStatusLabel === 'Subscribed to you'\n ? '#008236'\n : '#78716C',\n lineHeight: '133.333%',\n letterSpacing: '0.21px',\n }}\n >\n {followerStatusLabel}\n </span>\n )}\n </div>\n </div>\n {customProfileContent && (\n <div className=\"w-full\">{customProfileContent}</div>\n )}\n </div>\n\n <ul className=\"flex flex-col gap-2 mt-2\">\n {showDeleteConversation && (\n <li>\n <ActionButton\n onClick={handleLeaveConversation}\n disabled={isLeaving}\n aria-busy={isLeaving}\n >\n {isLeaving ? (\n <SpinnerGapIcon className=\"h-5 w-5 animate-spin\" />\n ) : (\n <SignOutIcon className=\"h-5 w-5\" />\n )}\n <span>Delete Conversation</span>\n </ActionButton>\n </li>\n )}\n <li>\n {isParticipantBlocked ? (\n <ActionButton\n onClick={handleUnblockUser}\n disabled={isUpdatingBlockStatus}\n aria-busy={isUpdatingBlockStatus}\n >\n {isUpdatingBlockStatus ? (\n <SpinnerGapIcon className=\"h-5 w-5 animate-spin\" />\n ) : (\n <ProhibitInsetIcon className=\"h-5 w-5\" />\n )}\n <span>Unblock</span>\n </ActionButton>\n ) : (\n <ActionButton\n onClick={handleBlockUser}\n disabled={isUpdatingBlockStatus}\n aria-busy={isUpdatingBlockStatus}\n >\n {isUpdatingBlockStatus ? (\n <SpinnerGapIcon className=\"h-5 w-5 animate-spin\" />\n ) : (\n <ProhibitInsetIcon className=\"h-5 w-5\" />\n )}\n <span>Block</span>\n </ActionButton>\n )}\n </li>\n <li>\n <ActionButton variant=\"danger\" onClick={handleReportUser}>\n <FlagIcon className=\"h-5 w-5\" />\n <span>Report</span>\n </ActionButton>\n </li>\n {customChannelActions}\n </ul>\n </div>\n </div>\n </dialog>\n )\n}\n","import { DateSeparator, type DateSeparatorProps } from 'stream-chat-react'\n\nexport const CustomDateSeparator = (props: DateSeparatorProps) => (\n <DateSeparator {...props} position=\"center\" />\n)\n","import { useCallback, useMemo } from 'react'\nimport type { LocalMessage } from 'stream-chat'\nimport { useChannelStateContext, useChatContext } from 'stream-chat-react'\n\nexport type VoteSelection = 'up' | 'down' | null\n\nconst VOTE_UP = 'vote_up'\nconst VOTE_DOWN = 'vote_down'\n\nfunction getVoteFromReactions(\n ownReactions: LocalMessage['own_reactions']\n): VoteSelection {\n if (!ownReactions?.length) return null\n if (ownReactions.some((r) => r.type === VOTE_DOWN)) return 'down'\n if (ownReactions.some((r) => r.type === VOTE_UP)) return 'up'\n return null\n}\n\ninterface UseMessageVoteResult {\n selected: VoteSelection\n voteUp: () => void\n voteDown: () => void\n}\n\n/**\n * Hook that wraps Stream Chat reactions to provide toggle-style\n * upvote/downvote behavior on a message.\n *\n * Uses enforce_unique so sending a new reaction type automatically\n * removes the previous one. Uses skip_push to avoid notifying the\n * Linker on every vote.\n */\nexport function useMessageVote(message: LocalMessage): UseMessageVoteResult {\n const { channel } = useChannelStateContext()\n const { client } = useChatContext('useMessageVote')\n\n const selected = useMemo(\n () => getVoteFromReactions(message.own_reactions),\n [message.own_reactions]\n )\n\n const voteUp = useCallback(async () => {\n if (!client?.userID) return\n try {\n if (selected === 'up') {\n await channel.deleteReaction(message.id, VOTE_UP)\n } else {\n await channel.sendReaction(\n message.id,\n { type: VOTE_UP },\n { enforce_unique: true, skip_push: true }\n )\n }\n } catch {\n // Silently fail — voting is non-critical\n }\n }, [channel, client?.userID, message.id, selected])\n\n const voteDown = useCallback(async () => {\n if (!client?.userID) return\n try {\n if (selected === 'down') {\n await channel.deleteReaction(message.id, VOTE_DOWN)\n } else {\n await channel.sendReaction(\n message.id,\n { type: VOTE_DOWN },\n { enforce_unique: true, skip_push: true }\n )\n }\n } catch {\n // Silently fail — voting is non-critical\n }\n }, [channel, client?.userID, message.id, selected])\n\n return { selected, voteUp, voteDown }\n}\n","import React, { Suspense } from 'react'\n\nimport type { ComposerCardProps } from './components/Composer/Card'\nimport type { ReceivedCardProps } from './components/Received/Card'\nimport type { SentCardProps } from './components/Sent/Card'\n\nconst ComposerCardLazy = React.lazy(() => import('./components/Composer/Card'))\nconst SentCardLazy = React.lazy(() => import('./components/Sent/Card'))\nconst ReceivedCardLazy = React.lazy(() => import('./components/Received/Card'))\n\nconst LockedAttachmentFallback = () => (\n <div\n className=\"w-[280px] min-h-[200px] animate-pulse rounded-md bg-black/[0.06] shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_1px_2px_rgba(0,0,0,0.04),0_8px_32px_rgba(0,0,0,0.1)]\"\n aria-hidden\n />\n)\n\nconst Composer = (props: ComposerCardProps) => (\n <Suspense fallback={<LockedAttachmentFallback />}>\n <ComposerCardLazy {...props} />\n </Suspense>\n)\n\nconst Sent = (props: SentCardProps) => (\n <Suspense fallback={<LockedAttachmentFallback />}>\n <SentCardLazy {...props} />\n </Suspense>\n)\n\nconst Received = (props: ReceivedCardProps) => (\n <Suspense fallback={<LockedAttachmentFallback />}>\n <ReceivedCardLazy {...props} />\n </Suspense>\n)\n\n/**\n * @deprecated Renamed to `LockedAttachment.Sent` (post-send sender card) or\n * `LockedAttachment.Composer` (drafting state). This alias maps to `Sent`\n * because that matches the previous in-thread usage; pre-send / drafting\n * usages (e.g. with `onDismiss`) should migrate to `LockedAttachment.Composer`.\n */\nconst Creator = Sent\n\n/** @deprecated Renamed to `LockedAttachment.Received`. */\nconst Visitor = Received\n\nconst LockedAttachment = { Composer, Sent, Received, Creator, Visitor }\n\nexport default LockedAttachment\nexport type { ComposerCardProps, SentCardProps, ReceivedCardProps }\n/**\n * @deprecated Renamed to `SentCardProps`. Drafting usages (with `onDismiss`)\n * should migrate to `ComposerCardProps`.\n */\nexport type CreatorCardProps = SentCardProps\n/** @deprecated Renamed to `ReceivedCardProps`. */\nexport type VisitorCardProps = ReceivedCardProps\nexport type {\n PaymentStatus,\n LockedAttachmentSource,\n LockedAttachmentGalleryItem,\n LockedAttachmentContextValue,\n} from './types'\n","export type AttachmentSourceType = 'image' | 'audio' | 'video' | 'document'\n\nexport type DocumentIconType =\n | 'pdf'\n | 'doc'\n | 'xls'\n | 'csv'\n | 'ppt'\n | 'zip'\n | 'text'\n | 'markdown'\n | 'generic'\n\nconst DOCUMENT_ICON_PATTERNS: Array<[RegExp, DocumentIconType]> = [\n [/pdf/, 'pdf'],\n [/wordprocessingml|msword|\\.doc/, 'doc'],\n [/spreadsheetml|ms-excel|\\.xls/, 'xls'],\n [/csv/, 'csv'],\n [/presentationml|ms-powerpoint|\\.ppt/, 'ppt'],\n [/zip|x-rar|x-7z|x-tar|x-gzip/, 'zip'],\n [/plain|rtf/, 'text'],\n [/markdown/, 'markdown'],\n]\n\nexport function getSourceType(mimeType: string): AttachmentSourceType {\n if (mimeType.startsWith('video/')) return 'video'\n if (mimeType.startsWith('audio/')) return 'audio'\n if (mimeType.startsWith('image/')) return 'image'\n return 'document'\n}\n\nexport function getDocumentIconType(mimeType: string): DocumentIconType {\n const match = DOCUMENT_ICON_PATTERNS.find(([pattern]) =>\n pattern.test(mimeType)\n )\n return match ? match[1] : 'generic'\n}\n","import {\n FileIcon,\n FileCsvIcon,\n FileDocIcon,\n FileMdIcon,\n FilePdfIcon,\n FilePptIcon,\n FileTextIcon,\n FileXlsIcon,\n FileZipIcon,\n ImageIcon,\n SpeakerHighIcon,\n VideoCameraIcon,\n IconProps,\n} from '@phosphor-icons/react'\nimport React from 'react'\n\nimport { getDocumentIconType, getSourceType } from './mimeType'\nimport type { AttachmentSourceType } from './mimeType'\n\nexport const MEDIA_TYPE_ICON: Record<AttachmentSourceType, React.ElementType> =\n {\n video: VideoCameraIcon,\n audio: SpeakerHighIcon,\n image: ImageIcon,\n document: FileIcon,\n }\n\nconst DOCUMENT_ICON_COMPONENT = {\n pdf: FilePdfIcon,\n doc: FileDocIcon,\n xls: FileXlsIcon,\n csv: FileCsvIcon,\n ppt: FilePptIcon,\n zip: FileZipIcon,\n text: FileTextIcon,\n markdown: FileMdIcon,\n generic: FileIcon,\n} as const\n\nexport function getTypeIcon(mimeType: string): React.ElementType {\n const sourceType = getSourceType(mimeType)\n if (sourceType !== 'document') return MEDIA_TYPE_ICON[sourceType]\n return DOCUMENT_ICON_COMPONENT[getDocumentIconType(mimeType)]\n}\n\n/** Use instead of `<TypeIcon />` where TypeIcon = getTypeIcon(mime) to satisfy react-hooks/static-components. */\nexport function renderTypeIcon(\n mimeType: string,\n props: IconProps\n): React.ReactElement {\n return React.createElement(getTypeIcon(mimeType), props)\n}\n","import { CircleNotchIcon, PauseIcon, PlayIcon } from '@phosphor-icons/react'\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\n\nimport { isDevBuild } from '../../utils/isDevBuild'\n\nimport { renderTypeIcon } from './utils/icons'\nimport { getSourceType } from './utils/mimeType'\n\ntype TouchEventUnion =\n | MouseEvent\n | TouchEvent\n | React.MouseEvent\n | React.TouchEvent\n\n\nconst getClientXFromEvent = (e: TouchEventUnion): number => {\n if ('touches' in e) {\n return e.touches[0]?.clientX ?? e.changedTouches[0]?.clientX ?? 0\n }\n return e.clientX\n}\n\nexport interface MediaPlayerProps {\n source: string\n mimeType: string\n poster?: string\n autoPlay?: boolean\n /** Controlled playing state. When provided, syncs to internal play/pause. */\n playing?: boolean\n loop?: boolean\n controls?: boolean\n showProgress?: boolean\n /** When true, requests muted playback (helps autoplay policies on video). */\n muted?: boolean\n /**\n * When provided, overrides the default click-to-play-toggle behaviour on the\n * player container. The play/pause button (which calls stopPropagation) is\n * unaffected, so inline playback still works.\n */\n onContainerClick?: (e: React.MouseEvent) => void\n}\n\nconst MediaPlayer: React.FC<MediaPlayerProps> = ({\n source,\n mimeType,\n poster,\n autoPlay = false,\n playing: playingProp,\n loop = false,\n controls = true,\n showProgress = false,\n muted = false,\n onContainerClick,\n}) => {\n // --- Derived ---\n const sourceType = getSourceType(mimeType)\n\n // --- Refs ---\n const playerRef = useRef<HTMLMediaElement>(null)\n const trackRef = useRef<HTMLDivElement>(null)\n const rafRef = useRef<number | null>(null)\n const prevPlayingPropRef = useRef(playingProp)\n\n // --- State: playback ---\n const [playing, setPlaying] = useState(autoPlay)\n const [played, setPlayed] = useState(0)\n const [seeking, setSeeking] = useState(false)\n\n // --- State: UI ---\n const [scrubberHovered, setScrubberHovered] = useState(false)\n /** Set when autoplay/play() was rejected so user can start via gesture (no controls UI). */\n const [manualPlayRequired, setManualPlayRequired] = useState(false)\n\n // --- State: loading ---\n const [buffering, setBuffering] = useState(false)\n /** True until the first canPlay fires for the current source — hides controls/spinner behind poster. */\n const [initialLoad, setInitialLoad] = useState(true)\n const [videoAspect, setVideoAspect] = useState<number | null>(null)\n\n // --- Callbacks ---\n const startPlaybackFromGesture = useCallback(() => {\n setManualPlayRequired(false)\n setPlaying(true)\n }, [])\n\n const getFraction = useCallback((e: TouchEventUnion) => {\n const track = trackRef.current\n if (!track) return 0\n const rect = track.getBoundingClientRect()\n return Math.max(\n 0,\n Math.min(1, (getClientXFromEvent(e) - rect.left) / rect.width)\n )\n }, [])\n\n const seekTo = useCallback((fraction: number) => {\n const el = playerRef.current\n if (el && el.duration) el.currentTime = fraction * el.duration\n }, [])\n\n const handleTrackPointerDown = (\n e: React.MouseEvent<HTMLDivElement> | React.TouchEvent<HTMLDivElement>\n ) => {\n e.stopPropagation()\n setSeeking(true)\n const fraction = getFraction(e)\n setPlayed(fraction)\n seekTo(fraction)\n }\n\n // --- Effects ---\n\n // Sync controlled playing prop to internal state\n useEffect(() => {\n if (\n playingProp !== undefined &&\n playingProp !== prevPlayingPropRef.current\n ) {\n prevPlayingPropRef.current = playingProp\n setPlaying(playingProp)\n }\n }, [playingProp])\n\n // RAF-driven progress updates\n useEffect(() => {\n if (!playing) {\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current)\n rafRef.current = null\n }\n return\n }\n const tick = () => {\n const el = playerRef.current\n if (el && el.duration && !seeking) setPlayed(el.currentTime / el.duration)\n rafRef.current = requestAnimationFrame(tick)\n }\n rafRef.current = requestAnimationFrame(tick)\n return () => {\n if (rafRef.current !== null) cancelAnimationFrame(rafRef.current)\n }\n }, [playing, seeking])\n\n // ReactPlayer v3 uses native HTML media elements and does not support a\n // declarative `playing` prop — playback must be driven imperatively.\n useEffect(() => {\n const el = playerRef.current\n if (!el) return\n if (playing) {\n void el.play().catch((err) => {\n setPlaying(false)\n setManualPlayRequired(true)\n if (isDevBuild()) {\n console.debug('[MediaPlayer] play() failed', err)\n }\n })\n } else {\n el.pause()\n }\n }, [playing])\n\n // Global seeking listeners\n useEffect(() => {\n if (!seeking) return\n const onMove = (e: MouseEvent | TouchEvent) => setPlayed(getFraction(e))\n const onUp = (e: MouseEvent | TouchEvent) => {\n setSeeking(false)\n seekTo(getFraction(e))\n }\n window.addEventListener('mousemove', onMove)\n window.addEventListener('mouseup', onUp)\n window.addEventListener('touchmove', onMove, { passive: true })\n window.addEventListener('touchend', onUp)\n return () => {\n window.removeEventListener('mousemove', onMove)\n window.removeEventListener('mouseup', onUp)\n window.removeEventListener('touchmove', onMove)\n window.removeEventListener('touchend', onUp)\n }\n }, [seeking, getFraction, seekTo])\n\n // --- Derived render values ---\n // Use natural aspect ratio once metadata loads, fall back to 16:9 before then.\n const aspectStyle = videoAspect\n ? { aspectRatio: String(videoAspect) }\n : undefined\n const aspectClass = !videoAspect ? ' aspect-video' : ''\n const scrubberPercent = Math.round(played * 100)\n\n return (\n <div\n role=\"button\"\n tabIndex={0}\n className={`relative cursor-pointer overflow-hidden bg-black ${aspectClass}`}\n style={aspectStyle}\n onClick={(e) => {\n if (onContainerClick) { onContainerClick(e); return }\n if (manualPlayRequired) return\n if (controls) setPlaying((p) => !p)\n }}\n onKeyDown={(e) => {\n if (e.key !== 'Enter' && e.key !== ' ') return\n e.preventDefault()\n if (onContainerClick) { onContainerClick(e as unknown as React.MouseEvent); return }\n if (manualPlayRequired) return\n if (controls) setPlaying((p) => !p)\n }}\n >\n {/* For audio, poster persists as a visual background. For video, hide once loaded. */}\n {poster && (sourceType === 'audio' || initialLoad) && (\n <img\n src={poster}\n alt=\"\"\n className=\"absolute inset-0 h-full w-full object-cover\"\n />\n )}\n {!poster && (sourceType === 'audio' || initialLoad) && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n {renderTypeIcon(mimeType, {\n className: 'size-12 text-black/20',\n weight: 'regular',\n })}\n </div>\n )}\n <div className=\"absolute inset-0\">\n {sourceType === 'audio' ? (\n <audio\n ref={playerRef as React.RefObject<HTMLAudioElement>}\n src={source}\n loop={loop}\n muted={muted}\n style={{ width: '100%', height: '100%' }}\n onLoadStart={() => setBuffering(true)}\n onCanPlay={() => {\n setBuffering(false)\n setInitialLoad(false)\n }}\n onWaiting={() => setBuffering(true)}\n onPlay={() => setManualPlayRequired(false)}\n onEnded={() => {\n if (!loop) {\n setPlaying(false)\n setPlayed(0)\n }\n }}\n >\n <track kind=\"captions\" />\n </audio>\n ) : (\n <video\n ref={playerRef as React.RefObject<HTMLVideoElement>}\n src={source}\n loop={loop}\n muted={muted}\n playsInline\n style={{ width: '100%', height: '100%' }}\n onLoadStart={() => setBuffering(true)}\n onCanPlay={() => {\n setBuffering(false)\n setInitialLoad(false)\n }}\n onWaiting={() => setBuffering(true)}\n onPlay={() => setManualPlayRequired(false)}\n onLoadedMetadata={() => {\n const el = playerRef.current\n if (\n el instanceof HTMLVideoElement &&\n el.videoWidth &&\n el.videoHeight\n ) {\n setVideoAspect(el.videoWidth / el.videoHeight)\n }\n }}\n onEnded={() => {\n if (!loop) {\n setPlaying(false)\n setPlayed(0)\n }\n }}\n >\n <track kind=\"captions\" />\n </video>\n )}\n </div>\n\n {buffering && !manualPlayRequired && (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center\">\n <CircleNotchIcon\n className=\"size-8 animate-spin text-white/80\"\n weight=\"bold\"\n />\n </div>\n )}\n\n {manualPlayRequired && !controls && (\n <div\n className=\"absolute inset-0 z-30 flex cursor-pointer items-center justify-center bg-black/35\"\n role=\"button\"\n tabIndex={0}\n aria-label=\"Play preview\"\n onClick={(e) => {\n e.stopPropagation()\n startPlaybackFromGesture()\n }}\n onKeyDown={(e) => {\n if (e.key !== 'Enter' && e.key !== ' ') return\n e.preventDefault()\n e.stopPropagation()\n startPlaybackFromGesture()\n }}\n >\n <span className=\"flex size-16 items-center justify-center rounded-full bg-white/20 text-white backdrop-blur-sm\">\n <PlayIcon className=\"size-9 translate-x-0.5\" weight=\"fill\" />\n </span>\n </div>\n )}\n\n {showProgress && !controls && (\n <div className=\"absolute inset-x-0 bottom-0 px-3 pb-2.5 pt-6 bg-gradient-to-t from-black/40 to-transparent\">\n <div\n role=\"slider\"\n aria-label=\"Playback position\"\n aria-valuenow={scrubberPercent}\n aria-valuemin={0}\n aria-valuemax={100}\n tabIndex={0}\n ref={trackRef}\n className=\"relative flex h-4 w-full cursor-pointer items-center\"\n onMouseDown={handleTrackPointerDown}\n onTouchStart={handleTrackPointerDown}\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => {\n if (e.key === 'ArrowRight') seekTo(Math.min(1, played + 0.05))\n if (e.key === 'ArrowLeft') seekTo(Math.max(0, played - 0.05))\n }}\n >\n <div className=\"w-full overflow-hidden rounded-full bg-white/30 h-1\">\n <div\n className=\"h-full rounded-full bg-white\"\n style={{ width: `${scrubberPercent}%` }}\n />\n </div>\n </div>\n </div>\n )}\n\n {controls && (\n <div className=\"absolute inset-x-0 bottom-0 flex items-center gap-2 bg-gradient-to-t from-black/60 to-transparent px-3 pb-2.5 pt-6 transition-all duration-200\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n setPlaying((p) => !p)\n }}\n className=\"shrink-0 text-white\"\n aria-label={playing ? 'Pause' : 'Play'}\n >\n {playing ? (\n <PauseIcon className=\"size-5\" weight=\"fill\" />\n ) : (\n <PlayIcon className=\"size-5 translate-x-px\" weight=\"fill\" />\n )}\n </button>\n\n <div\n role=\"slider\"\n aria-label=\"Playback position\"\n aria-valuenow={scrubberPercent}\n aria-valuemin={0}\n aria-valuemax={100}\n tabIndex={0}\n ref={trackRef}\n className=\"relative flex h-4 w-full cursor-pointer items-center\"\n onMouseDown={handleTrackPointerDown}\n onTouchStart={handleTrackPointerDown}\n onClick={(e) => e.stopPropagation()}\n onMouseEnter={() => setScrubberHovered(true)}\n onMouseLeave={() => setScrubberHovered(false)}\n onKeyDown={(e) => {\n if (e.key === 'ArrowRight') seekTo(Math.min(1, played + 0.05))\n if (e.key === 'ArrowLeft') seekTo(Math.max(0, played - 0.05))\n }}\n >\n <div\n className={`w-full overflow-hidden rounded-full bg-white/30 transition-all duration-200 ${scrubberHovered || seeking ? 'h-1.5' : 'h-1'}`}\n >\n <div\n className=\"h-full rounded-full bg-white\"\n style={{ width: `${scrubberPercent}%` }}\n />\n </div>\n <div\n className={`absolute size-3 -translate-x-1/2 rounded-full bg-white shadow transition-[opacity,transform] duration-200 ${scrubberHovered || seeking ? 'scale-100 opacity-100' : 'scale-0 opacity-0'}`}\n style={{ left: `${scrubberPercent}%` }}\n />\n </div>\n </div>\n )}\n </div>\n )\n}\n\nexport default MediaPlayer\n","import React, { useState } from 'react'\n\nimport MediaPlayer, { type MediaPlayerProps } from './MediaPlayer'\nimport { renderTypeIcon } from './utils/icons'\nimport { getSourceType } from './utils/mimeType'\n\nexport type AttachmentThumbnailVariant = 'light' | 'dark'\n\nexport interface AttachmentThumbnailProps {\n mimeType: string\n sourceUrl?: string\n thumbnailUrl?: string\n title?: string\n variant: AttachmentThumbnailVariant\n /** Extra props passed to MediaPlayer when source is video or audio. */\n mediaPlayerProps?: Partial<\n Pick<\n MediaPlayerProps,\n 'autoPlay' | 'loop' | 'muted' | 'controls' | 'onContainerClick'\n >\n >\n /**\n * When true (Visitor unlocked image/document), use aspect-video + object-contain fade-in.\n */\n containedImage?: boolean\n}\n\nconst placeholderIconClass = (variant: AttachmentThumbnailVariant) =>\n variant === 'dark' ? 'size-12 text-white/20' : 'size-12 text-black/20'\n\nconst posterShellClass = (variant: AttachmentThumbnailVariant) =>\n variant === 'dark'\n ? 'aspect-video overflow-hidden bg-white/10'\n : 'aspect-video overflow-hidden bg-black/5'\n\n/**\n * Renders the media preview area for attachment cards (LockedAttachment, MediaMessage).\n * Overlays (dim, lock, eye toggle) are composed by the parent.\n */\nconst AttachmentThumbnail: React.FC<AttachmentThumbnailProps> = ({\n mimeType,\n sourceUrl,\n thumbnailUrl,\n title,\n variant,\n mediaPlayerProps,\n containedImage = false,\n}) => {\n const sourceType = getSourceType(mimeType)\n const [sourceReady, setSourceReady] = useState(false)\n\n if (sourceUrl && (sourceType === 'video' || sourceType === 'audio')) {\n return (\n <MediaPlayer\n source={sourceUrl}\n mimeType={mimeType}\n poster={thumbnailUrl}\n controls\n {...mediaPlayerProps}\n />\n )\n }\n\n if (sourceUrl && sourceType === 'image') {\n if (containedImage) {\n return (\n <div className=\"relative aspect-video overflow-hidden bg-black/5\">\n <img\n src={sourceUrl}\n alt={title ?? ''}\n className={`absolute inset-0 h-full w-full object-contain transition-opacity duration-300 ${sourceReady ? 'opacity-100' : 'opacity-0'}`}\n draggable={false}\n onLoad={() => setSourceReady(true)}\n />\n </div>\n )\n }\n return (\n <img\n src={sourceUrl}\n alt={title ?? ''}\n className=\"block w-full\"\n draggable={false}\n />\n )\n }\n\n if (sourceUrl && sourceType === 'document') {\n if (thumbnailUrl) {\n if (containedImage) {\n return (\n <div className=\"relative aspect-video overflow-hidden bg-black/5\">\n <img\n src={thumbnailUrl}\n alt={title ?? ''}\n className={`absolute inset-0 h-full w-full object-contain transition-opacity duration-300 ${sourceReady ? 'opacity-100' : 'opacity-0'}`}\n draggable={false}\n onLoad={() => setSourceReady(true)}\n />\n </div>\n )\n }\n return (\n <img\n src={thumbnailUrl}\n alt=\"\"\n className=\"block w-full\"\n draggable={false}\n />\n )\n }\n return (\n <div\n className={`flex aspect-video w-full items-center justify-center ${variant === 'dark' ? 'bg-white/10' : 'bg-black/5'}`}\n >\n {renderTypeIcon(mimeType, {\n className: placeholderIconClass(variant),\n weight: 'regular',\n })}\n </div>\n )\n }\n\n // Poster-only or empty (no sourceUrl)\n if (thumbnailUrl) {\n return (\n <div className={`relative ${posterShellClass(variant)}`}>\n <img\n src={thumbnailUrl}\n alt={title ?? ''}\n draggable={false}\n className=\"absolute inset-0 h-full w-full object-cover\"\n />\n </div>\n )\n }\n\n return (\n <div\n className={`flex aspect-video w-full items-center justify-center ${variant === 'dark' ? 'bg-white/10' : 'bg-black/5'}`}\n >\n {renderTypeIcon(mimeType, {\n className: placeholderIconClass(variant),\n weight: 'regular',\n })}\n </div>\n )\n}\n\nexport default AttachmentThumbnail\n","import classNames from 'classnames'\nimport React from 'react'\n\nimport { renderTypeIcon } from './utils/icons'\n\nexport { default as AttachmentThumbnail } from './Thumbnail'\nexport type {\n AttachmentThumbnailProps,\n AttachmentThumbnailVariant,\n} from './Thumbnail'\nexport { default as MediaPlayer } from './MediaPlayer'\nexport type { MediaPlayerProps } from './MediaPlayer'\nexport { renderTypeIcon, getTypeIcon, MEDIA_TYPE_ICON } from './utils/icons'\nexport {\n getSourceType,\n getDocumentIconType,\n type AttachmentSourceType,\n type DocumentIconType,\n} from './utils/mimeType'\n\nexport interface AttachmentCardProps {\n variant: 'light' | 'dark'\n thumbnail: React.ReactNode\n title?: string\n placeholderTitle?: string\n mimeType: string\n detail?: string\n statusBadge?: React.ReactNode\n action?: React.ReactNode\n topLeft?: React.ReactNode\n topRight?: React.ReactNode\n rootRef?: React.Ref<HTMLDivElement>\n 'data-testid'?: string\n}\n\nconst AttachmentCard: React.FC<AttachmentCardProps> = ({\n variant,\n thumbnail,\n title,\n placeholderTitle = 'Attachment title',\n mimeType,\n detail,\n statusBadge,\n action,\n topLeft,\n topRight,\n rootRef,\n 'data-testid': dataTestId,\n}) => {\n const isDark = variant === 'dark'\n const displayTitle = isDark ? (title ?? placeholderTitle) : (title ?? '')\n const titleDimmed = isDark && !title\n\n return (\n <div\n ref={rootRef}\n data-testid={dataTestId}\n className={classNames(\n 'relative w-[280px] select-none overflow-hidden rounded-[24px] shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_4px_8px_rgba(0,0,0,0.06)]',\n isDark ? 'bg-[#121110]' : 'bg-white'\n )}\n >\n {topLeft ? (\n <div className=\"pointer-events-auto absolute left-3 top-3 z-50\">{topLeft}</div>\n ) : null}\n {topRight ? (\n <div className=\"pointer-events-auto absolute right-3 top-3 z-50\">{topRight}</div>\n ) : null}\n\n {thumbnail}\n\n <div className=\"px-4 pb-3 pt-3\">\n {displayTitle.trim() !== '' && (\n <p\n className={classNames('mb-0.5 truncate text-base font-medium', {\n 'text-black': !isDark,\n 'text-white/30': isDark && titleDimmed,\n 'text-white': isDark && !titleDimmed,\n })}\n >\n {displayTitle}\n </p>\n )}\n\n <div className=\"flex flex-wrap items-center gap-1\">\n {renderTypeIcon(mimeType, {\n className: classNames(\n 'size-5 shrink-0',\n isDark ? 'text-white/55' : 'text-black/55'\n ),\n weight: 'regular',\n })}\n\n {detail != null && detail !== '' && (\n <span\n className={classNames(\n 'text-xs font-medium',\n isDark ? 'text-white/55' : 'text-black/55'\n )}\n >\n {detail}\n </span>\n )}\n\n {statusBadge}\n </div>\n\n {action}\n </div>\n </div>\n )\n}\n\nexport default AttachmentCard\n","import { CircleNotchIcon, DownloadSimpleIcon, LinkIcon } from '@phosphor-icons/react'\nimport React, { useState } from 'react'\nimport type { Attachment, LocalMessage } from 'stream-chat'\n\nimport AttachmentCard, {\n AttachmentThumbnail,\n getSourceType,\n} from '../AttachmentCard'\nimport { Avatar } from '../Avatar'\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n\nfunction linkCardShellClass(isMyMessage: boolean) {\n const bg = isMyMessage ? 'bg-[#121110]' : 'bg-[#F3F3F1]'\n return `w-[280px] select-none overflow-hidden rounded-[24px] ${bg} shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_4px_8px_rgba(0,0,0,0.06)]`\n}\n\nfunction linkThumbnailBg(isMyMessage: boolean) {\n return isMyMessage ? 'bg-white/10' : 'bg-black/5'\n}\n\nfunction linkPrimaryText(isMyMessage: boolean) {\n return isMyMessage ? 'text-white' : 'text-black'\n}\n\nfunction linkSecondaryText(isMyMessage: boolean) {\n return isMyMessage ? 'text-white/55' : 'text-black/55'\n}\n\nfunction linkTertiaryText(isMyMessage: boolean) {\n return isMyMessage ? 'text-white/40' : 'text-black/40'\n}\n\nfunction linkIconColor(isMyMessage: boolean) {\n return isMyMessage ? 'text-white/20' : 'text-black/20'\n}\n\n/** Link preview (OG) — no download; opens in a new tab. */\nconst LinkCard: React.FC<{\n attachment: Attachment\n isMyMessage: boolean\n}> = ({ attachment, isMyMessage }) => {\n const { title, text, image_url, og_scrape_url, title_link } = attachment\n const rawUrl = og_scrape_url ?? title_link\n const url =\n typeof rawUrl === 'string' && rawUrl.trim() !== '' ? rawUrl : undefined\n\n const body = (\n <React.Fragment>\n <div className=\"p-2\">\n {image_url ? (\n <img\n src={image_url}\n alt={title ?? ''}\n className=\"aspect-video w-full rounded-[20px] object-cover\"\n />\n ) : (\n <div\n className={`aspect-video w-full rounded-[20px] ${linkThumbnailBg(isMyMessage)} flex items-center justify-center`}\n >\n <LinkIcon className={`size-12 ${linkIconColor(isMyMessage)}`} />\n </div>\n )}\n </div>\n <div className=\"px-3 pb-3\">\n {title && (\n <p className={`truncate text-[14px] font-medium leading-5 ${linkPrimaryText(isMyMessage)}`}>\n {title}\n </p>\n )}\n {text && (\n <p className={`truncate text-[12px] leading-4 ${linkSecondaryText(isMyMessage)}`}>{text}</p>\n )}\n {url && (\n <p className={`mt-1 truncate text-[12px] leading-4 ${linkTertiaryText(isMyMessage)}`}>\n {url}\n </p>\n )}\n </div>\n </React.Fragment>\n )\n\n if (url) {\n return (\n <a href={url} target=\"_blank\" rel=\"noopener noreferrer\" className=\"block no-underline\">\n {body}\n </a>\n )\n }\n\n return <div className=\"block\">{body}</div>\n}\n\nexport function isLinkAttachment(a: Attachment): boolean {\n return a.type === 'link' || (!!a.og_scrape_url && !a.asset_url)\n}\n\nexport function resolveLinkAttachment(\n message: LocalMessage\n): Attachment | undefined {\n return message.attachments?.find(isLinkAttachment)\n}\n\nasync function triggerDownload(url: string, filename?: string): Promise<void> {\n let name: string\n try { name = filename ?? new URL(url).pathname.split('/').pop() ?? 'download' }\n catch { name = filename ?? 'download' }\n const res = await fetch(url, { mode: 'cors' })\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n const blob = await res.blob()\n const objectUrl = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = objectUrl\n a.download = name\n a.style.display = 'none'\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n URL.revokeObjectURL(objectUrl)\n}\n\nconst DownloadAction: React.FC<{ url: string; filename?: string }> = ({\n url,\n filename,\n}) => {\n const [busy, setBusy] = useState(false)\n\n const handleClick = (e: React.MouseEvent) => {\n e.stopPropagation()\n const fallback = window.open('', '_blank', 'noopener,noreferrer')\n setBusy(true)\n triggerDownload(url, filename)\n .then(() => { fallback?.close() })\n .catch(() => { if (fallback) fallback.location.href = url })\n .finally(() => setBusy(false))\n }\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n disabled={busy}\n className=\"mt-3 inline-flex h-10 w-full items-center justify-center gap-2 rounded-full bg-[#121110] px-4 text-sm font-medium leading-none text-white hover:bg-[#2a2928] disabled:opacity-70\"\n >\n {busy ? (\n <CircleNotchIcon className=\"size-4 animate-spin text-white\" weight=\"bold\" />\n ) : (\n <React.Fragment>\n <DownloadSimpleIcon className=\"size-4 text-white\" weight=\"bold\" />\n Download\n </React.Fragment>\n )}\n </button>\n )\n}\n\nexport interface MediaMessageResolved {\n resolvedUrl: string\n resolvedType: string\n title?: string\n fileSize?: number\n thumbnailUrl?: string\n}\n\nexport function resolveMediaFromMessage(\n message: LocalMessage\n): MediaMessageResolved | null {\n const videoAttachment = message.attachments?.find(\n (a) => a.type === 'video' && a.asset_url\n )\n const imageAttachment = message.attachments?.find(\n (a) => a.type === 'image' && (a as { image_url?: string }).image_url\n )\n const audioAttachment = message.attachments?.find(\n (a) => a.type === 'audio' && a.asset_url\n )\n const fileAttachment = message.attachments?.find(\n (a) => a.type === 'file' && a.asset_url\n )\n\n const activeAttachment =\n videoAttachment ?? imageAttachment ?? audioAttachment ?? fileAttachment\n\n const resolvedUrl =\n videoAttachment?.asset_url ??\n (imageAttachment as { image_url?: string } | undefined)?.image_url ??\n audioAttachment?.asset_url ??\n fileAttachment?.asset_url\n\n if (!resolvedUrl) return null\n\n const resolvedType =\n activeAttachment?.mime_type ??\n (activeAttachment?.type === 'image'\n ? 'image/jpeg'\n : activeAttachment?.type === 'video'\n ? 'video/mp4'\n : activeAttachment?.type === 'audio'\n ? 'audio/mpeg'\n : 'application/octet-stream')\n\n const title = (activeAttachment as { title?: string } | undefined)?.title\n const fileSize = (activeAttachment as { file_size?: number } | undefined)?.file_size\n const thumbnailUrl = (videoAttachment as { thumb_url?: string } | undefined)?.thumb_url\n\n return {\n resolvedUrl,\n resolvedType,\n title,\n fileSize,\n thumbnailUrl,\n }\n}\n\n/** Dark card (sent / own message) — matches LockedAttachment.Sent preview without toggle. */\nconst Creator: React.FC<MediaMessageResolved> = ({\n resolvedUrl,\n resolvedType,\n title,\n fileSize,\n thumbnailUrl,\n}) => {\n const detail = fileSize !== undefined ? formatBytes(fileSize) : undefined\n\n return (\n <AttachmentCard\n variant=\"dark\"\n title={title}\n placeholderTitle=\"\"\n mimeType={resolvedType}\n detail={detail}\n thumbnail={\n <AttachmentThumbnail\n mimeType={resolvedType}\n sourceUrl={resolvedUrl}\n thumbnailUrl={thumbnailUrl}\n title={title}\n variant=\"dark\"\n />\n }\n />\n )\n}\n\n/** Light card (received) — matches LockedAttachment.Received unlocked without Purchased copy. */\nconst Visitor: React.FC<MediaMessageResolved> = ({\n resolvedUrl,\n resolvedType,\n title,\n fileSize,\n thumbnailUrl,\n}) => {\n const sourceType = getSourceType(resolvedType)\n const detail = fileSize !== undefined ? formatBytes(fileSize) : undefined\n\n return (\n <AttachmentCard\n variant=\"light\"\n title={title}\n mimeType={resolvedType}\n detail={detail}\n thumbnail={\n <AttachmentThumbnail\n mimeType={resolvedType}\n sourceUrl={resolvedUrl}\n thumbnailUrl={thumbnailUrl}\n title={title}\n variant=\"light\"\n containedImage={sourceType === 'image' || sourceType === 'document'}\n />\n }\n action={<DownloadAction url={resolvedUrl} filename={title} />}\n />\n )\n}\n\nexport interface MediaMessageProps {\n message: LocalMessage\n isMyMessage?: boolean\n}\n\n/**\n * @deprecated Slated for removal once the profile repo is migrated to\n * `LinkAttachment` (and its forthcoming media-only Attachment variants).\n * New consumers should reach for `LinkAttachment.Composer`,\n * `LinkAttachment.Sent`, or `LinkAttachment.Received` instead.\n */\nconst MediaMessageRoot: React.FC<MediaMessageProps> = ({\n message,\n isMyMessage = false,\n}) => {\n const linkAttachment = resolveLinkAttachment(message)\n const resolved = resolveMediaFromMessage(message)\n if (!linkAttachment && !resolved) return null\n\n const messageClass = isMyMessage\n ? 'str-chat__message str-chat__message-simple str-chat__message--me str-chat__message-simple--me'\n : 'str-chat__message str-chat__message-simple str-chat__message--other'\n\n return (\n <div className={messageClass}>\n {!isMyMessage && message.user && (\n <Avatar\n className=\"str-chat__avatar str-chat__message-sender-avatar\"\n id={message.user.id}\n image={message.user.image}\n name={message.user.name ?? message.user.id}\n />\n )}\n <div\n className=\"str-chat__message-inner\"\n style={{ marginInlineEnd: 0, marginInlineStart: 0 }}\n >\n <div className=\"str-chat__message-bubble-wrapper\">\n <div\n className=\"str-chat__message-bubble\"\n style={{ padding: 0, borderRadius: 0, overflow: 'visible', background: 'transparent' }}\n >\n {linkAttachment ? (\n <div className={linkCardShellClass(isMyMessage)}>\n <LinkCard attachment={linkAttachment} isMyMessage={isMyMessage} />\n </div>\n ) : isMyMessage ? (\n <Creator {...resolved!} />\n ) : (\n <Visitor {...resolved!} />\n )}\n </div>\n </div>\n </div>\n </div>\n )\n}\n\nconst MediaMessageCreatorEntry: React.FC<{ message: LocalMessage }> = ({\n message,\n}) => {\n const linkAttachment = resolveLinkAttachment(message)\n if (linkAttachment) {\n return (\n <div className={linkCardShellClass(true)}>\n <LinkCard attachment={linkAttachment} isMyMessage={true} />\n </div>\n )\n }\n const resolved = resolveMediaFromMessage(message)\n if (!resolved) return null\n return <Creator {...resolved} />\n}\n\nconst MediaMessageVisitorEntry: React.FC<{ message: LocalMessage }> = ({\n message,\n}) => {\n const linkAttachment = resolveLinkAttachment(message)\n if (linkAttachment) {\n return (\n <div className={linkCardShellClass(false)}>\n <LinkCard attachment={linkAttachment} isMyMessage={false} />\n </div>\n )\n }\n const resolved = resolveMediaFromMessage(message)\n if (!resolved) return null\n return <Visitor {...resolved} />\n}\n\nexport const MediaMessage = Object.assign(MediaMessageRoot, {\n Creator: MediaMessageCreatorEntry,\n Visitor: MediaMessageVisitorEntry,\n})\n","import type { Channel, LocalMessage } from 'stream-chat'\n\nimport type { PaymentStatus } from '../../stream-custom-data'\n\nexport interface LockedAttachmentBaseProps {\n title?: string\n mimeType?: string\n thumbnailUrl?: string\n detail?: string\n amountText?: string\n paymentStatus?: PaymentStatus\n /**\n * When provided with 2+ items, the card renders as a mixed-media carousel\n * (e.g. a couple of photos + a video) instead of a single thumbnail. Each\n * item brings its own thumbnail and optional source so that\n * `LockedAttachment.Composer` / `.Sent` / `.Received` can all share the\n * same carousel chrome.\n */\n gallery?: LockedAttachmentGalleryItem[]\n}\n\nexport interface LockedAttachmentGalleryItem {\n /** MIME type of this carousel item — drives the per-item play / lock affordance. */\n mimeType: string\n /** Poster image used for both the locked (blurred) and unlocked preview state. */\n thumbnailUrl?: string\n /** Underlying source (image or video URL) — shown only when unlocked. */\n sourceUrl?: string\n}\n\nexport interface LockedAttachmentSource {\n /** Proxied URL used by the media player for in-app playback. */\n sourceUrl: string\n /** URL opened when the visitor clicks Download — may be a file or a web destination. */\n redeemUrl?: string\n /** Thumbnail URL from the fetched asset — overrides the metadata thumbnail when present. */\n thumbnailUrl?: string\n}\n\nexport type { PaymentStatus }\n\nexport interface LockedAttachmentContextValue {\n isUnlocking: (id: string) => boolean\n onUnlockClick?: (message: LocalMessage, channel: Channel) => void\n onDownloadClick?: (message: LocalMessage, channel: Channel) => void\n onFetchSource?: (message: LocalMessage, channel: Channel) => Promise<LockedAttachmentSource | void>\n}\n\nexport const defaultLockedAttachmentContextValue: LockedAttachmentContextValue = {\n isUnlocking: () => false,\n}\n","import { createContext, useContext } from 'react'\n\nimport { defaultLockedAttachmentContextValue, type LockedAttachmentContextValue } from '../LockedAttachment/types'\n\nexport interface CustomMessageRegistry {\n LockedAttachment: LockedAttachmentContextValue\n}\n\nconst customMessageDefaults: CustomMessageRegistry = {\n LockedAttachment: defaultLockedAttachmentContextValue,\n}\n\nconst CustomMessageContext = createContext<Partial<CustomMessageRegistry>>({})\n\nexport const CustomMessageProvider = CustomMessageContext.Provider\n\nexport function useCustomMessage<K extends keyof CustomMessageRegistry>(key: K): CustomMessageRegistry[K] {\n const ctx = useContext(CustomMessageContext)\n return (ctx[key] ?? customMessageDefaults[key]) as CustomMessageRegistry[K]\n}\n","import { ThumbsUpIcon, ThumbsDownIcon } from '@phosphor-icons/react'\nimport classNames from 'classnames'\nimport React from 'react'\n\nimport type { VoteSelection } from '../../hooks/useMessageVote'\n\ninterface MessageVoteButtonsProps {\n selected: VoteSelection\n onVoteUp: () => void\n onVoteDown: () => void\n}\n\nexport const MessageVoteButtons: React.FC<MessageVoteButtonsProps> = ({\n selected,\n onVoteUp,\n onVoteDown,\n}) => (\n <div className=\"message-vote-buttons\">\n <button\n type=\"button\"\n className={classNames('message-vote-button focus-ring', {\n 'message-vote-button--selected': selected === 'up',\n })}\n onClick={onVoteUp}\n aria-label=\"Good response\"\n aria-pressed={selected === 'up'}\n data-tooltip=\"Good response\"\n >\n <ThumbsUpIcon size={16} weight={selected === 'up' ? 'fill' : 'regular'} />\n </button>\n <button\n type=\"button\"\n className={classNames('message-vote-button focus-ring', {\n 'message-vote-button--selected': selected === 'down',\n })}\n onClick={onVoteDown}\n aria-label=\"Bad response\"\n aria-pressed={selected === 'down'}\n data-tooltip=\"Bad response\"\n >\n <ThumbsDownIcon size={16} weight={selected === 'down' ? 'fill' : 'regular'} />\n </button>\n </div>\n)\n","import classNames from 'classnames'\nimport React, { useMemo, useState } from 'react'\nimport {\n Attachment as DefaultAttachment,\n EditMessageModal as DefaultEditMessageModal,\n MessageBounceModal,\n MessageBouncePrompt as DefaultMessageBouncePrompt,\n MessageBlocked as DefaultMessageBlocked,\n MessageDeleted as DefaultMessageDeleted,\n MessageErrorIcon,\n MessageIsThreadReplyInChannelButtonIndicator as DefaultMessageIsThreadReplyInChannelButtonIndicator,\n MessageRepliesCountButton as DefaultMessageRepliesCountButton,\n MessageText,\n Poll,\n ReminderNotification as DefaultReminderNotification,\n StreamedMessageText as DefaultStreamedMessageText,\n areMessageUIPropsEqual,\n isDateSeparatorMessage,\n isMessageBlocked,\n isMessageBounced,\n messageHasAttachments,\n messageHasReactions,\n useChannelStateContext,\n useComponentContext,\n useChatContext,\n useMessageContext,\n useMessageReminder,\n type MessageContextValue,\n type MessageUIComponentProps,\n} from 'stream-chat-react'\n\nimport { useMessageVote } from '../../hooks/useMessageVote'\nimport { getMessageDisplayText } from '../../utils/getMessageDisplayText'\nimport { Avatar } from '../Avatar'\nimport LockedAttachment from '../LockedAttachment'\nimport { isLinkAttachment } from '../MediaMessage'\n\nimport { useCustomMessage } from './context'\nimport {\n MessageTag,\n isAttachmentMessage,\n isChatbotMessage,\n isTipOnlyMessage,\n} from './MessageTag'\nimport { MessageVoteButtons } from './MessageVoteButtons'\n\ntype CustomMessageUIComponentProps = MessageUIComponentProps & {\n chatbotVotingEnabled?: boolean\n viewerLanguage?: string\n}\n\ntype CustomMessageWithContextProps = MessageContextValue & {\n chatbotVotingEnabled?: boolean\n viewerLanguage?: string\n}\n\nconst CustomMessageWithContext = (props: CustomMessageWithContextProps) => {\n const {\n additionalMessageInputProps,\n chatbotVotingEnabled,\n editing,\n endOfGroup,\n firstOfGroup,\n groupedByUser,\n handleAction,\n handleOpenThread,\n handleRetry,\n highlighted,\n isMessageAIGenerated,\n isMyMessage,\n message,\n renderText,\n threadList,\n viewerLanguage,\n } = props\n\n const { client } = useChatContext('CustomMessage')\n const { channel } = useChannelStateContext('CustomMessage')\n const { isUnlocking, onUnlockClick, onFetchSource, onDownloadClick } =\n useCustomMessage('LockedAttachment')\n const [isBounceDialogOpen, setIsBounceDialogOpen] = useState(false)\n const reminder = useMessageReminder(message.id)\n const { selected: voteState, voteUp, voteDown } = useMessageVote(message)\n\n const {\n Attachment = DefaultAttachment,\n EditMessageModal = DefaultEditMessageModal,\n MessageBlocked = DefaultMessageBlocked,\n MessageBouncePrompt = DefaultMessageBouncePrompt,\n MessageDeleted = DefaultMessageDeleted,\n MessageIsThreadReplyInChannelButtonIndicator = DefaultMessageIsThreadReplyInChannelButtonIndicator,\n MessageRepliesCountButton = DefaultMessageRepliesCountButton,\n ReminderNotification = DefaultReminderNotification,\n StreamedMessageText = DefaultStreamedMessageText,\n PinIndicator,\n } = useComponentContext('CustomMessage')\n\n const hasAttachment = messageHasAttachments(message)\n const hasReactions = messageHasReactions(message)\n const isAIGenerated = useMemo(\n () => isMessageAIGenerated?.(message),\n [isMessageAIGenerated, message]\n )\n const finalAttachments = useMemo(() => {\n const attachments = message.attachments ?? []\n const raw = message.shared_location\n ? [message.shared_location, ...attachments]\n : attachments\n\n if (!isChatbotMessage(message)) return raw\n\n const filtered = raw.filter((a) => !('type' in a) || !isLinkAttachment(a))\n return filtered.length === raw.length ? raw : filtered\n }, [message])\n const displayMessage = useMemo(() => {\n const displayText = getMessageDisplayText({ message, viewerLanguage })\n return displayText === message.text\n ? message\n : { ...message, text: displayText }\n }, [message, viewerLanguage])\n\n if (isDateSeparatorMessage(message)) {\n return null\n }\n\n if (message.deleted_at || message.type === 'deleted') {\n return <MessageDeleted message={message} />\n }\n\n if (isMessageBlocked(message)) {\n return <MessageBlocked />\n }\n\n const showReplyCountButton = !threadList && !!message.reply_count\n const showIsReplyInChannel =\n !threadList && message.show_in_channel && message.parent_id\n const allowRetry =\n message.status === 'failed' && message.error?.status !== 403\n const isBounced = isMessageBounced(message)\n let handleClick: (() => void) | undefined = undefined\n\n if (allowRetry) {\n handleClick = () => handleRetry(message)\n } else if (isBounced) {\n handleClick = () => setIsBounceDialogOpen(true)\n }\n\n const isMine = isMyMessage()\n const rootClassName = classNames(\n 'str-chat__message str-chat__message-simple',\n `str-chat__message--${message.type}`,\n `str-chat__message--${message.status}`,\n isMine\n ? 'str-chat__message--me str-chat__message-simple--me'\n : 'str-chat__message--other',\n message.text ? 'str-chat__message--has-text' : 'has-no-text',\n {\n 'str-chat__message--has-attachment': hasAttachment,\n 'str-chat__message--highlighted': highlighted,\n 'str-chat__message--pinned pinned-message': message.pinned,\n 'str-chat__message--with-reactions': hasReactions,\n 'str-chat__message-send-can-be-retried':\n message?.status === 'failed' && message?.error?.status !== 403,\n 'str-chat__message-with-thread-link':\n showReplyCountButton || showIsReplyInChannel,\n 'str-chat__virtual-message__wrapper--end': endOfGroup,\n 'str-chat__virtual-message__wrapper--first': firstOfGroup,\n 'str-chat__virtual-message__wrapper--group': groupedByUser,\n }\n )\n\n const poll = message.poll_id && client.polls.fromState(message.poll_id)\n const isTipOnly = isTipOnlyMessage(message)\n const isChatbot = isChatbotMessage(message)\n const isAttachment = isAttachmentMessage(message)\n const hasRenderableAttachments = !!(\n finalAttachments?.length && !message.quoted_message\n )\n const useAttachmentFooterChatbotTag =\n isChatbot && isMine && hasRenderableAttachments\n\n return (\n <>\n {editing && (\n <EditMessageModal\n additionalMessageInputProps={additionalMessageInputProps}\n />\n )}\n {isBounceDialogOpen && (\n <MessageBounceModal\n MessageBouncePrompt={MessageBouncePrompt}\n onClose={() => setIsBounceDialogOpen(false)}\n open={isBounceDialogOpen}\n />\n )}\n <div className={rootClassName} key={message.id}>\n {PinIndicator && <PinIndicator />}\n {!!reminder && <ReminderNotification reminder={reminder} />}\n {message.user && (\n <Avatar\n className=\"str-chat__avatar str-chat__message-sender-avatar\"\n id={message.user.id}\n image={message.user.image}\n name={message.user.name || message.user.id}\n size={isChatbot ? 24 : 28}\n shape=\"circle\"\n dmAgentEnabled={isChatbot}\n />\n )}\n {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}\n <div\n className={classNames('str-chat__message-inner', {\n 'str-chat__simple-message--error-failed': allowRetry || isBounced,\n })}\n data-testid=\"message-inner\"\n onClick={handleClick}\n onKeyDown={handleClick}\n role={handleClick ? 'button' : undefined}\n tabIndex={handleClick ? 0 : undefined}\n style={{\n // Force margins to 0 to prevent hover layout shift\n // Stream Chat CSS sets margin-inline-end/start to 78px, then 0 on hover\n marginInlineEnd: 0,\n marginInlineStart: 0,\n }}\n >\n {isAttachment ? (\n <div className=\"str-chat__message-bubble-wrapper\">\n {isMine ? (\n <LockedAttachment.Sent\n title={message.metadata?.attachment_title}\n mimeType={message.metadata?.attachment_mime_type}\n thumbnailUrl={message.metadata?.attachment_thumbnail}\n amountText={message.metadata?.amount_text}\n detail={message.metadata?.attachment_detail}\n paymentStatus={message.metadata?.payment_status}\n onPreviewClick={() => onUnlockClick?.(message, channel)}\n onFetchSource={async () =>\n await onFetchSource?.(message, channel)\n }\n />\n ) : (\n <LockedAttachment.Received\n title={message.metadata?.attachment_title}\n mimeType={message.metadata?.attachment_mime_type}\n thumbnailUrl={message.metadata?.attachment_thumbnail}\n amountText={message.metadata?.amount_text}\n detail={message.metadata?.attachment_detail}\n paymentStatus={message.metadata?.payment_status}\n isUnlocking={isUnlocking(message.id)}\n onUnlockClick={() => onUnlockClick?.(message, channel)}\n onFetchSource={async () =>\n await onFetchSource?.(message, channel)\n }\n onDownloadClick={() => onDownloadClick?.(message, channel)}\n />\n )}\n {message.text && (\n <div className=\"str-chat__message-bubble\">\n <MessageText\n message={displayMessage}\n renderText={renderText}\n />\n </div>\n )}\n </div>\n ) : isTipOnly ? (\n /* Tip-only messages render as a standalone bubble */\n <MessageTag message={message} standalone />\n ) : (\n <div className=\"str-chat__message-bubble-wrapper\">\n <div className=\"str-chat__message-bubble\">\n {isChatbot && !useAttachmentFooterChatbotTag && (\n <MessageTag\n message={message}\n hasAttachment={hasRenderableAttachments}\n isMyMessage={isMine}\n />\n )}\n {poll && <Poll poll={poll} />}\n {finalAttachments?.length && !message.quoted_message ? (\n <Attachment\n actionHandler={handleAction}\n attachments={finalAttachments}\n />\n ) : null}\n {isAIGenerated ? (\n <StreamedMessageText\n message={displayMessage}\n renderText={renderText}\n />\n ) : (\n <MessageText\n message={displayMessage}\n renderText={renderText}\n />\n )}\n <MessageErrorIcon />\n </div>\n {/* Tip/paid tags stay outside; chatbot attachment indicator stays outside too */}\n {(!isChatbot || useAttachmentFooterChatbotTag) && (\n <MessageTag\n message={message}\n hasAttachment={hasRenderableAttachments}\n isMyMessage={isMine}\n />\n )}\n {chatbotVotingEnabled && isChatbot && (\n <MessageVoteButtons\n selected={voteState}\n onVoteUp={voteUp}\n onVoteDown={voteDown}\n />\n )}\n </div>\n )}\n </div>\n {showReplyCountButton && (\n <MessageRepliesCountButton\n onClick={handleOpenThread}\n reply_count={message.reply_count}\n />\n )}\n {showIsReplyInChannel && (\n <MessageIsThreadReplyInChannelButtonIndicator />\n )}\n </div>\n </>\n )\n}\n\nconst MemoizedCustomMessage = React.memo(\n CustomMessageWithContext,\n (prev, next) => {\n if (prev.chatbotVotingEnabled !== next.chatbotVotingEnabled) return false\n if (prev.viewerLanguage !== next.viewerLanguage) return false\n return areMessageUIPropsEqual(prev, next)\n }\n) as typeof CustomMessageWithContext\n\nexport const CustomMessage = (props: CustomMessageUIComponentProps) => {\n const messageContext = useMessageContext('CustomMessage')\n return <MemoizedCustomMessage {...messageContext} {...props} />\n}\n","import { XIcon } from '@phosphor-icons/react'\nimport React from 'react'\nimport {\n LinkPreview,\n LinkPreviewsManager,\n LinkPreviewsManagerState,\n} from 'stream-chat'\nimport { useMessageComposer, useStateStore } from 'stream-chat-react'\n\nconst linkPreviewsManagerStateSelector = (state: LinkPreviewsManagerState) => ({\n linkPreviews: Array.from(state.previews.values()).filter(\n (preview) =>\n LinkPreviewsManager.previewIsLoaded(preview) ||\n LinkPreviewsManager.previewIsLoading(preview)\n ),\n})\n\ninterface CustomLinkPreviewCardProps {\n link: LinkPreview\n onDismiss: (url: string) => void\n}\n\nconst CustomLinkPreviewCard: React.FC<CustomLinkPreviewCardProps> = ({\n link,\n onDismiss,\n}) => {\n const { og_scrape_url, title, image_url } = link\n\n const handleDismissLink = (e: React.MouseEvent) => {\n e.preventDefault()\n onDismiss(og_scrape_url)\n }\n\n return (\n <a\n href={og_scrape_url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"relative w-full block rounded-[24px] bg-[#121110] p-2 no-underline transition-opacity hover:opacity-90\"\n >\n {image_url && (\n <img\n src={image_url}\n alt={title || ''}\n className=\"h-[148px] w-full rounded-[20px] object-cover\"\n />\n )}\n <button\n type=\"button\"\n onClick={handleDismissLink}\n className=\"absolute right-4 top-4 flex size-6 items-center justify-center rounded-full border border-white/40 bg-white/70 backdrop-blur-2xl focus-ring\"\n aria-label=\"Close link preview\"\n >\n <XIcon className=\"size-4 text-black/90\" />\n </button>\n <div className=\"p-2\">\n {title && (\n <div className=\"text-[14px] font-medium leading-5 text-white\">\n {title}\n </div>\n )}\n <div className=\"text-[12px] leading-4 text-white/55\">\n {og_scrape_url}\n </div>\n </div>\n </a>\n )\n}\n\nexport const CustomLinkPreviewList = () => {\n const { linkPreviewsManager } = useMessageComposer()\n\n const { linkPreviews: stateLinkPreviews } = useStateStore(\n linkPreviewsManager.state,\n linkPreviewsManagerStateSelector\n )\n\n const handleDismiss = (url: string) => {\n linkPreviewsManager.dismissPreview(url)\n }\n\n const showLinkPreviews = stateLinkPreviews.length > 0\n\n if (!showLinkPreviews) return null\n\n return (\n <div className=\"flex flex-col items-center w-full gap-2 mb-4\">\n {stateLinkPreviews.map((linkPreview) => (\n <CustomLinkPreviewCard\n key={linkPreview.og_scrape_url}\n link={linkPreview}\n onDismiss={handleDismiss}\n />\n ))}\n </div>\n )\n}\n","import { ArrowUpIcon } from '@phosphor-icons/react'\nimport React from 'react'\nimport {\n AttachmentPreviewList,\n MessageInput,\n QuotedMessagePreview,\n SimpleAttachmentSelector,\n TextareaComposer,\n useChannelStateContext,\n useComponentContext,\n useMessageComposerHasSendableData,\n useMessageInputContext,\n} from 'stream-chat-react'\n\nimport { CustomLinkPreviewList } from '../CustomLinkPreviewList'\n\nconst DefaultSendButton: React.FC<{\n sendMessage: () => void\n disabled?: boolean\n [key: string]: unknown\n}> = ({ sendMessage, disabled, ...rest }) => (\n <button\n {...rest}\n type=\"button\"\n aria-label=\"Send\"\n disabled={disabled}\n onClick={sendMessage}\n >\n <ArrowUpIcon weight=\"bold\" className=\"size-4\" />\n </button>\n)\n\nconst CustomMessageInputInner: React.FC = () => {\n const { channel } = useChannelStateContext()\n const isFrozen = channel?.data?.frozen === true\n const { handleSubmit } = useMessageInputContext()\n const { SendButton: SendButtonFromContext } = useComponentContext(\n 'CustomMessageInput',\n )\n const SendButton = SendButtonFromContext ?? DefaultSendButton\n const hasSendableData = useMessageComposerHasSendableData()\n const isSendDisabled = isFrozen || !hasSendableData\n\n return (\n <>\n <div className=\"left-container\">\n <SimpleAttachmentSelector />\n </div>\n <div className=\"central-container min-w-0 w-full p-2 bg-white rounded-[1.5rem] shadow-[0_4px_16px_0_rgba(0,0,0,0.08),0_1px_2px_0_rgba(0,0,0,0.04),0_0_0_1px_rgba(0,0,0,0.04)]\">\n <QuotedMessagePreview />\n <CustomLinkPreviewList />\n <AttachmentPreviewList />\n <div className=\"flex\">\n <div className=\"w-full ml-2 mr-4 self-center leading-[0]\">\n <TextareaComposer\n aria-disabled={isFrozen || undefined}\n className=\"w-full resize-none outline-none leading-6\"\n maxRows={4}\n readOnly={isFrozen}\n tabIndex={isFrozen ? -1 : undefined}\n />\n </div>\n <SendButton\n sendMessage={handleSubmit}\n aria-label=\"Send\"\n className=\"str-chat__send-button mt-auto flex justify-center items-center flex-shrink-0 rounded-full size-8 bg-[#121110] disabled:bg-[#F1F0EE] disabled:text-black/20 text-white focus-ring\"\n data-testid=\"send-button\"\n disabled={isSendDisabled}\n type=\"button\"\n />\n </div>\n </div>\n </>\n )\n}\n\nexport interface CustomMessageInputProps {\n renderActions?: () => React.ReactNode\n}\n\nexport const CustomMessageInput: React.FC<CustomMessageInputProps> = ({\n renderActions,\n}) => {\n const { channel } = useChannelStateContext()\n const isFrozen = channel?.data?.frozen === true\n\n return (\n <div\n // @ts-expect-error Only React 19 onwards has `inert` in its types.\n inert={isFrozen ? '' : undefined}\n aria-disabled={isFrozen || undefined}\n className=\"message-input flex items-center gap-2 p-4 aria-disabled:opacity-40\"\n >\n {renderActions?.()}\n <MessageInput Input={CustomMessageInputInner} />\n </div>\n )\n}\n","import { ProhibitIcon, SparkleIcon } from '@phosphor-icons/react'\nimport { MessageTimestamp, type EventComponentProps } from 'stream-chat-react'\n\nimport type {\n AgeSafetySystemType,\n DmAgentSystemType,\n} from '../../stream-custom-data'\n\nconst DM_AGENT_SYSTEM_TYPES: readonly DmAgentSystemType[] = [\n 'SYSTEM_DM_AGENT_PAUSED',\n 'SYSTEM_DM_AGENT_RESUMED',\n]\n\nconst DM_AGENT_SYSTEM_MESSAGE_FALLBACK_TEXT: Record<DmAgentSystemType, string> =\n {\n SYSTEM_DM_AGENT_PAUSED: 'DM Agent has left the conversation',\n SYSTEM_DM_AGENT_RESUMED: 'DM Agent has rejoined the conversation',\n }\n\nconst AGE_SAFETY_SYSTEM_TYPES: readonly AgeSafetySystemType[] = [\n 'SYSTEM_AGE_SAFETY_BLOCKED',\n]\n\nconst AGE_SAFETY_SYSTEM_MESSAGE_FALLBACK_TEXT: Record<\n AgeSafetySystemType,\n string\n> = {\n SYSTEM_AGE_SAFETY_BLOCKED:\n 'This user isn’t able to reply because they don’t meet our age safety guidelines.',\n}\n\nconst AGE_SAFETY_SYSTEM_MESSAGE_EMPHASIS = 'age safety guidelines.'\nconst AGE_SAFETY_SYSTEM_MESSAGE_URL = 'https://linktr.ee/s/about/contact'\n\nconst isDmAgentSystemType = (\n value: string | undefined\n): value is DmAgentSystemType => {\n return DM_AGENT_SYSTEM_TYPES.includes(value as DmAgentSystemType)\n}\n\nconst isAgeSafetySystemType = (\n value: string | undefined\n): value is AgeSafetySystemType => {\n return AGE_SAFETY_SYSTEM_TYPES.includes(value as AgeSafetySystemType)\n}\n\ntype CustomSystemMessageVariant =\n | {\n kind: 'dm-agent'\n type: DmAgentSystemType\n }\n | {\n kind: 'age-safety'\n type: AgeSafetySystemType\n }\n\nconst getCustomSystemMessageVariant = (\n message: EventComponentProps['message']\n): CustomSystemMessageVariant | undefined => {\n const metadataCustomType = message.metadata?.custom_type\n if (isDmAgentSystemType(metadataCustomType)) {\n return {\n kind: 'dm-agent',\n type: metadataCustomType,\n }\n }\n\n if (isAgeSafetySystemType(metadataCustomType)) {\n return {\n kind: 'age-safety',\n type: metadataCustomType,\n }\n }\n\n const fallbackType = message.dm_agent_system_type\n if (isDmAgentSystemType(fallbackType)) {\n return {\n kind: 'dm-agent',\n type: fallbackType,\n }\n }\n\n return undefined\n}\n\nconst renderAgeSafetyMessageText = (messageText: string): React.ReactNode => {\n const emphasisIndex = messageText.indexOf(AGE_SAFETY_SYSTEM_MESSAGE_EMPHASIS)\n if (emphasisIndex === -1) {\n return messageText\n }\n\n const emphasisEndIndex =\n emphasisIndex + AGE_SAFETY_SYSTEM_MESSAGE_EMPHASIS.length\n\n return (\n <>\n {messageText.slice(0, emphasisIndex)}\n <a\n href={AGE_SAFETY_SYSTEM_MESSAGE_URL}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"mes-age-safety-system-message__emphasis font-medium text-inherit underline\"\n >\n {AGE_SAFETY_SYSTEM_MESSAGE_EMPHASIS}\n </a>\n {messageText.slice(emphasisEndIndex)}\n </>\n )\n}\n\nexport const CustomSystemMessage: React.FC<EventComponentProps> = (props) => {\n const isDateHidden = props.message.hide_date === true\n const customSystemMessageVariant = getCustomSystemMessageVariant(\n props.message\n )\n\n if (customSystemMessageVariant?.kind === 'dm-agent') {\n const messageText =\n props.message.text?.trim() ||\n DM_AGENT_SYSTEM_MESSAGE_FALLBACK_TEXT[customSystemMessageVariant.type]\n\n return (\n <div className=\"str-chat__message--system\" data-testid=\"message-system\">\n <div\n className=\"mes-dm-agent-system-message mx-auto mb-2 inline-flex w-fit max-w-[min(100%,480px)] items-center justify-center gap-[10px] rounded-[12px] border border-[rgba(0,0,0,0.08)] p-3 text-[rgba(0,0,0,0.55)]\"\n data-testid=\"dm-agent-system-message\"\n data-dm-agent-system-type={customSystemMessageVariant.type}\n >\n <SparkleIcon\n size={16}\n weight=\"regular\"\n aria-hidden\n className=\"mes-dm-agent-system-message__sparkle shrink-0\"\n />\n <p className=\"mes-dm-agent-system-message__text m-0 text-center text-[14px] font-normal leading-5 tracking-[0.21px]\">\n {messageText}\n </p>\n </div>\n {!isDateHidden && <MessageTimestamp message={props.message} />}\n </div>\n )\n }\n\n if (customSystemMessageVariant?.kind === 'age-safety') {\n const messageText =\n props.message.text?.trim() ||\n AGE_SAFETY_SYSTEM_MESSAGE_FALLBACK_TEXT[customSystemMessageVariant.type]\n\n return (\n <div className=\"str-chat__message--system\" data-testid=\"message-system\">\n <div\n className=\"mes-age-safety-system-message box-border mx-auto mb-2 flex w-full max-w-[329px] items-start justify-center gap-3 rounded-[12px] border border-[var(--border-secondary,rgba(0,0,0,0.08))] bg-[var(--bg-warning-subtle,#fef3c6)] px-2 py-4 pl-5 text-[color:var(--text-warning-on-warning,#894b00)]\"\n data-testid=\"age-safety-system-message\"\n data-age-safety-system-type={customSystemMessageVariant.type}\n >\n <ProhibitIcon\n size={24}\n weight=\"duotone\"\n aria-hidden\n className=\"mes-age-safety-system-message__icon shrink-0 text-[color:var(--text-warning-on-warning,#894b00)]\"\n data-testid=\"age-safety-system-message-icon\"\n />\n <div className=\"mes-age-safety-system-message__content min-w-0 flex-[1_0_0]\">\n <p className=\"m-0 text-balance text-left text-[12px] font-normal leading-4 tracking-[0.21px] text-[color:var(--text-warning-on-warning,#894b00)]\">\n {renderAgeSafetyMessageText(messageText)}\n </p>\n </div>\n </div>\n {!isDateHidden && <MessageTimestamp message={props.message} />}\n </div>\n )\n }\n\n return (\n <div className=\"str-chat__message--system\" data-testid=\"message-system\">\n <div className=\"str-chat__message--system__text\">\n <div className=\"str-chat__message--system__line\"></div>\n <p>{props.message.text}</p>\n <div className=\"str-chat__message--system__line\"></div>\n </div>\n {!isDateHidden && <MessageTimestamp message={props.message} />}\n </div>\n )\n}\n","import { createContext } from 'react'\n\nexport const DmAgentEnabledContext = createContext<boolean>(false)\n","import React, { useContext } from 'react'\nimport type { Event, UserResponse } from 'stream-chat'\nimport {\n AIStates,\n useAIState,\n useChannelStateContext,\n useChatContext,\n useTypingContext,\n} from 'stream-chat-react'\n\nimport { Avatar } from '../Avatar'\n\nimport { DmAgentEnabledContext } from './DmAgentContext'\n\ninterface CustomTypingIndicatorProps {\n threadList?: boolean\n}\n\nconst Circle = ({ cx, index }: { cx: string; index: number }) => (\n <circle cx={cx} cy=\"4\" r=\"3.9\" fill=\"#A0A0A0\">\n <animateTransform\n attributeName=\"transform\"\n type=\"translate\"\n values=\"0 0; 0 -2.25; 0 0;\"\n dur=\"900ms\"\n begin={`${120 * index}ms`} // 0ms, 120ms, 240ms\n repeatCount=\"indefinite\"\n />\n </circle>\n)\n\nconst AI_ACTIVE_STATES = new Set<string>([\n AIStates.Thinking,\n AIStates.Generating,\n AIStates.ExternalSources,\n])\n\nconst CustomTypingIndicator = ({ threadList }: CustomTypingIndicatorProps) => {\n const { channel, channelConfig, thread } = useChannelStateContext()\n const { client } = useChatContext()\n const { typing = {} } = useTypingContext()\n const { aiState } = useAIState(channel)\n const dmAgentEnabled = useContext(DmAgentEnabledContext)\n\n // Show the AI indicator whenever the consumer agent is producing a reply.\n // This event stream is independent of `typing.start`/`typing.stop`, so it is\n // intentionally NOT gated by `channelConfig.typing_events`. Gate strictly on\n // `dmAgentEnabled` so stale or off-surface ai_indicator events never surface\n // the bubble on channels where the agent is not active.\n const isAiActive =\n !threadList && dmAgentEnabled && AI_ACTIVE_STATES.has(aiState)\n\n if (isAiActive) {\n const agentUser = findOtherChannelUser(channel, client.user?.id)\n return (\n <TypingBubble\n avatarId={agentUser?.id ?? 'ai-agent'}\n avatarName={agentUser?.name ?? agentUser?.id ?? 'Agent'}\n avatarImage={agentUser?.image}\n testId=\"typing-indicator-ai\"\n />\n )\n }\n\n if (channelConfig?.typing_events === false) {\n return null\n }\n\n const typingInChannel = !threadList\n ? Object.values(typing).filter(\n ({ parent_id, user }: Event) =>\n user?.id !== client.user?.id && !parent_id\n )\n : []\n\n const typingInThread = threadList\n ? Object.values(typing).filter(\n ({ parent_id, user }: Event) =>\n user?.id !== client.user?.id && parent_id === thread?.id\n )\n : []\n\n const typingUsers = threadList ? typingInThread : typingInChannel\n if (!typingUsers.length) {\n return null\n }\n\n const typingUser = typingUsers[0]?.user\n const memberUser =\n typingUser?.id && channel.state.members[typingUser.id]\n ? channel.state.members[typingUser.id].user\n : undefined\n\n return (\n <TypingBubble\n avatarId={typingUser?.id ?? memberUser?.id ?? 'typing-user'}\n avatarName={\n typingUser?.name ?? memberUser?.name ?? typingUser?.id ?? 'Typing user'\n }\n avatarImage={typingUser?.image ?? memberUser?.image}\n testId=\"typing-indicator\"\n />\n )\n}\n\nconst TypingBubble = ({\n avatarId,\n avatarName,\n avatarImage,\n testId,\n}: {\n avatarId: string\n avatarName: string\n avatarImage?: string | null\n testId: string\n}) => (\n <div\n className=\"str-chat__typing-indicator !items-end !bg-transparent\"\n data-testid={testId}\n style={{ insetInlineStart: 0, insetInlineEnd: 'auto' }}\n >\n <div className=\"shrink-0\" aria-hidden=\"true\">\n <Avatar\n id={avatarId}\n name={avatarName}\n image={avatarImage ?? undefined}\n size={24}\n shape=\"circle\"\n />\n </div>\n\n <div className=\"px-4 py-3 rounded-lg bg-[#E9EAED] h-12 flex flex-col justify-end\">\n <svg\n aria-hidden=\"true\"\n className=\"block overflow-visible mb-[0.2rem]\"\n viewBox=\"0 0 32 8\"\n width=\"32\"\n height=\"8\"\n overflow=\"visible\"\n >\n <Circle cx=\"4\" index={0} />\n <Circle cx=\"16\" index={1} />\n <Circle cx=\"28\" index={2} />\n </svg>\n </div>\n </div>\n)\n\ntype ChannelLike = ReturnType<typeof useChannelStateContext>['channel']\n\nfunction findOtherChannelUser(\n channel: ChannelLike,\n selfId: string | undefined\n): UserResponse | undefined {\n const members = channel?.state?.members ?? {}\n for (const member of Object.values(members)) {\n const memberUser = member?.user\n if (memberUser && memberUser.id !== selfId) {\n return memberUser\n }\n }\n return undefined\n}\n\nexport default CustomTypingIndicator\n","import React from 'react'\n\n/**\n * Empty state component shown when a channel has no messages\n * Returns null to show nothing - the LoadingIndicator handles the loading state\n */\nexport const ChannelEmptyState: React.FC = () => null\n","import classNames from 'classnames'\n\ntype LoadingProps = {\n className?: string\n message?: string\n}\n\nconst Loading = ({ className, message }: LoadingProps) => (\n <div\n className={classNames('flex items-center justify-center h-full', className)}\n >\n <svg viewBox=\"0 0 100 100\" className=\"size-8 fill-pebble\" stroke=\"none\">\n <circle cx=\"6\" cy=\"50\" r=\"6\">\n <animateTransform\n attributeName=\"transform\"\n dur=\"1s\"\n type=\"translate\"\n values=\"0 15 ; 0 -15; 0 15\"\n repeatCount=\"indefinite\"\n begin=\"0.1\"\n />\n </circle>\n <circle cx=\"30\" cy=\"50\" r=\"6\">\n <animateTransform\n attributeName=\"transform\"\n dur=\"1s\"\n type=\"translate\"\n values=\"0 10 ; 0 -10; 0 10\"\n repeatCount=\"indefinite\"\n begin=\"0.2\"\n />\n </circle>\n <circle cx=\"54\" cy=\"50\" r=\"6\">\n <animateTransform\n attributeName=\"transform\"\n dur=\"1s\"\n type=\"translate\"\n values=\"0 5 ; 0 -5; 0 5\"\n repeatCount=\"indefinite\"\n begin=\"0.3\"\n />\n </circle>\n </svg>\n {message && <span className=\"text-stone\">{message}</span>}\n </div>\n)\n\nexport default Loading\n","import React from 'react'\n\nimport Loading from '../Loading'\n\n/**\n * Loading state component\n */\nexport const LoadingState = React.memo(() => (\n <div className=\"messaging-loading-state flex items-center justify-center h-full\">\n <div className=\"flex items-center\">\n <Loading className=\"w-6 h-6\" />\n <span className=\"text-sm text-stone\">Loading messages</span>\n </div>\n </div>\n))\nLoadingState.displayName = 'LoadingState'\n","import {\n ArrowLeftIcon,\n CaretRightIcon,\n DotsThreeIcon,\n SparkleIcon,\n StarIcon,\n} from '@phosphor-icons/react'\nimport classNames from 'classnames'\nimport React, { useCallback, useRef } from 'react'\nimport { Channel as ChannelType, ChannelMemberResponse } from 'stream-chat'\nimport {\n Channel,\n Window,\n MessageList,\n useMessageContext,\n useChannelStateContext,\n WithComponents,\n MessageUIComponentProps,\n} from 'stream-chat-react'\n\nimport { useChannelStar } from '../hooks/useChannelStar'\nimport type { ChannelViewProps } from '../types'\nimport { resolveParticipantDisplayName } from '../utils/resolveParticipantDisplayName'\n\nimport { Avatar } from './Avatar'\nimport { ChannelInfoDialog } from './ChannelInfoDialog'\nimport { CustomDateSeparator } from './CustomDateSeparator'\nimport { CustomMessage } from './CustomMessage'\nimport { CustomMessageInput } from './CustomMessageInput'\nimport { CustomSystemMessage } from './CustomSystemMessage'\nimport CustomTypingIndicator from './CustomTypingIndicator'\nimport { DmAgentEnabledContext } from './CustomTypingIndicator/DmAgentContext'\nimport { ChannelEmptyState } from './MessagingShell/ChannelEmptyState'\nimport { LoadingState } from './MessagingShell/LoadingState'\n\nconst ICON_BTN_CLASS =\n 'size-10 rounded-full bg-[#F1F0EE] hover:bg-[#E5E4E1] flex items-center justify-center transition-colors duration-150 focus-ring'\nconst DM_AGENT_HEADER_HELPER_TEXT = 'Replies instantly with AI assistant'\n\n/**\n * Custom channel header component\n */\nconst CustomChannelHeader: React.FC<{\n onBack?: () => void\n showBackButton: boolean\n onShowInfo: () => void\n canShowInfo: boolean\n showStarButton?: boolean\n dmAgentEnabled?: boolean\n getParticipantDisplayName: (\n participant: ChannelMemberResponse | undefined\n ) => string\n}> = ({\n onBack,\n showBackButton,\n onShowInfo,\n canShowInfo,\n showStarButton = false,\n dmAgentEnabled = false,\n getParticipantDisplayName,\n}) => {\n const { channel } = useChannelStateContext()\n\n // Get participant info (excluding current user)\n const participant = React.useMemo(() => {\n const myUserId = channel._client?.userID\n if (!myUserId) return undefined\n const members = Object.values(channel.state?.members || {})\n return members.find(\n (member) => member.user?.id && member.user.id !== myUserId\n )\n }, [channel._client?.userID, channel.state?.members])\n\n const participantName = getParticipantDisplayName(participant)\n const participantImage = participant?.user?.image\n const isStarred = useChannelStar(channel)\n\n const handleStarClick = async () => {\n try {\n if (isStarred) {\n await channel.unpin()\n } else {\n await channel.pin()\n }\n } catch (error) {\n console.error(\n '[CustomChannelHeader] Failed to update pinned status:',\n error\n )\n }\n }\n\n return (\n <div className=\"@container\">\n <div className=\"grid grid-cols-[1fr_auto_1fr] w-full items-center @lg:hidden\">\n <div className=\"flex items-center gap-2\">\n {showBackButton && (\n <button\n className={ICON_BTN_CLASS}\n onClick={onBack || (() => {})}\n type=\"button\"\n aria-label=\"Back to conversations\"\n >\n <ArrowLeftIcon className=\"size-5 text-black/90\" />\n </button>\n )}\n </div>\n <div className=\"flex flex-col gap-1 items-center\">\n <Avatar\n id={participant?.user?.id || channel.id || 'unknown'}\n name={participantName}\n image={participantImage}\n starred={showStarButton && isStarred}\n dmAgentEnabled={dmAgentEnabled}\n size={40}\n />\n <button\n type=\"button\"\n onClick={onShowInfo}\n className=\"flex items-center gap-0.5 rounded-full bg-black/[0.05] px-3 py-1 text-xs font-medium text-black/90 hover:bg-black/[0.08] transition-colors\"\n aria-label={`View info for ${participantName}`}\n >\n {participantName}\n <CaretRightIcon className=\"size-3 shrink-0\" />\n </button>\n {dmAgentEnabled && (\n <div className=\"flex items-center gap-1 text-[10px] leading-3 text-black/55\">\n <SparkleIcon className=\"size-3 shrink-0 text-black/55\" />\n <span>{DM_AGENT_HEADER_HELPER_TEXT}</span>\n </div>\n )}\n </div>\n <div className=\"flex justify-end items-center gap-2\">\n {showStarButton && (\n <button\n className={ICON_BTN_CLASS}\n onClick={handleStarClick}\n type=\"button\"\n aria-label={\n isStarred ? 'Unstar conversation' : 'Star conversation'\n }\n >\n <StarIcon\n className={classNames('size-5', {\n 'text-yellow-600': isStarred,\n 'text-black/90': !isStarred,\n })}\n weight={isStarred ? 'duotone' : 'regular'}\n />\n </button>\n )}\n <button\n className={ICON_BTN_CLASS}\n onClick={onShowInfo}\n type=\"button\"\n aria-label=\"Show info\"\n >\n <DotsThreeIcon className=\"size-5 text-black/90\" />\n </button>\n </div>\n </div>\n <div className=\"hidden @lg:flex items-center justify-between gap-3 min-h-12\">\n <div className=\"flex items-center gap-4 min-w-0\">\n {showBackButton && onBack && (\n <button\n type=\"button\"\n onClick={onBack}\n className={ICON_BTN_CLASS}\n aria-label=\"Back to conversations\"\n >\n <ArrowLeftIcon className=\"size-5 text-black/90\" />\n </button>\n )}\n {/* Avatar */}\n <Avatar\n id={participant?.user?.id || channel.id || 'unknown'}\n name={participantName}\n image={participantImage}\n starred={showStarButton && isStarred}\n dmAgentEnabled={dmAgentEnabled}\n size={40}\n />\n <div className=\"min-w-0\">\n {canShowInfo ? (\n <button\n type=\"button\"\n onClick={onShowInfo}\n className=\"flex items-center gap-1 font-medium text-black/90 truncate hover:text-black/70 transition-colors\"\n aria-label={`View info for ${participantName}`}\n >\n <span className=\"truncate\">{participantName}</span>\n <CaretRightIcon className=\"size-4 shrink-0\" />\n </button>\n ) : (\n <h1 className=\"font-medium text-black/90 truncate\">\n {participantName}\n </h1>\n )}\n {dmAgentEnabled && (\n <div className=\"mt-0.5 flex items-center gap-1 text-[10px] leading-3 text-black/55\">\n <SparkleIcon className=\"size-3 shrink-0 text-black/55\" />\n <span className=\"truncate\">{DM_AGENT_HEADER_HELPER_TEXT}</span>\n </div>\n )}\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n {showStarButton && (\n <button\n className={ICON_BTN_CLASS}\n onClick={handleStarClick}\n type=\"button\"\n aria-label={\n isStarred ? 'Unstar conversation' : 'Star conversation'\n }\n >\n <StarIcon\n className={classNames('size-5', {\n 'text-yellow-600': isStarred,\n 'text-black/90': !isStarred,\n })}\n weight={isStarred ? 'duotone' : 'regular'}\n />\n </button>\n )}\n {canShowInfo && onShowInfo && (\n <button\n className={ICON_BTN_CLASS}\n onClick={onShowInfo}\n type=\"button\"\n aria-label=\"Show info\"\n >\n <DotsThreeIcon className=\"size-5 text-black/90\" />\n </button>\n )}\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * Inner component that has access to channel context\n */\nconst ChannelViewInner: React.FC<{\n onBack?: () => void\n showBackButton: boolean\n renderMessageInputActions?: (channel: ChannelType) => React.ReactNode\n renderConversationFooter?: (channel: ChannelType) => React.ReactNode\n onLeaveConversation?: (channel: ChannelType) => void\n onBlockParticipant?: (participantId?: string) => void\n CustomChannelEmptyState?: React.ComponentType\n showDeleteConversation?: boolean\n onDeleteConversationClick?: () => void\n onBlockParticipantClick?: () => void\n onReportParticipantClick?: () => void\n showStarButton?: boolean\n chatbotVotingEnabled?: boolean\n renderChannelBanner?: () => React.ReactNode\n customProfileContent?: React.ReactNode\n customChannelActions?: React.ReactNode\n renderMessage?: (\n messageNode: React.ReactElement,\n message: NonNullable<MessageUIComponentProps['message']>\n ) => React.ReactNode\n dmAgentEnabled?: boolean\n viewerLanguage?: string\n getParticipantDisplayName: (\n participant: ChannelMemberResponse | undefined\n ) => string\n}> = ({\n onBack,\n showBackButton,\n renderMessageInputActions,\n renderConversationFooter,\n onLeaveConversation,\n onBlockParticipant,\n showDeleteConversation = true,\n onDeleteConversationClick,\n onBlockParticipantClick,\n onReportParticipantClick,\n showStarButton = false,\n chatbotVotingEnabled = false,\n renderChannelBanner,\n customProfileContent,\n customChannelActions,\n renderMessage,\n dmAgentEnabled = false,\n viewerLanguage,\n getParticipantDisplayName,\n}) => {\n const { channel } = useChannelStateContext()\n const infoDialogRef = useRef<HTMLDialogElement>(null)\n\n // Get participant info for info dialog\n const participant = React.useMemo(() => {\n const myUserId = channel._client?.userID\n if (!myUserId) return undefined\n const members = Object.values(channel.state?.members || {})\n return members.find(\n (member) => member.user?.id && member.user.id !== myUserId\n )\n }, [channel._client?.userID, channel.state?.members])\n\n const currentMember = React.useMemo(() => {\n const myUserId = channel._client?.userID\n if (!myUserId) return undefined\n const members = Object.values(channel.state?.members || {})\n return members.find((member) => member.user?.id === myUserId)\n }, [channel._client?.userID, channel.state?.members])\n\n const currentUserIsAccount =\n (currentMember?.user as { is_account?: boolean } | undefined)?.is_account ??\n (currentMember as { is_account?: boolean } | undefined)?.is_account\n const participantIsAccount =\n (participant?.user as { is_account?: boolean } | undefined)?.is_account ??\n (participant as { is_account?: boolean } | undefined)?.is_account\n\n const showDmAgentHeader =\n dmAgentEnabled &&\n currentUserIsAccount === false &&\n participantIsAccount === true\n\n // Get follower status label from channel data\n const followerStatusLabel = React.useMemo(() => {\n const channelExtraData = (channel.data ?? {}) as {\n followerStatus?: string\n isFollower?: boolean\n }\n\n // If explicit followerStatus is provided, use it\n if (channelExtraData.followerStatus) {\n return String(channelExtraData.followerStatus)\n }\n // If isFollower is explicitly defined, use it to determine status\n if (channelExtraData.isFollower !== undefined) {\n return channelExtraData.isFollower\n ? 'Subscribed to you'\n : 'Not subscribed'\n }\n // Otherwise, don't show any status\n return undefined\n }, [channel.data])\n\n const handleShowInfo = useCallback(() => {\n infoDialogRef.current?.showModal()\n }, [])\n\n const handleCloseInfo = useCallback(() => {\n infoDialogRef.current?.close()\n }, [])\n\n // Prevents all message instances from unmounting when ChannelViewInner re-renders\n const MessageOverride = useCallback(\n (props: MessageUIComponentProps) => {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const { message } = useMessageContext('ChannelView')\n const messageNode = (\n <CustomMessage\n {...props}\n chatbotVotingEnabled={chatbotVotingEnabled}\n viewerLanguage={viewerLanguage}\n />\n )\n\n if (!renderMessage || !message) {\n return messageNode\n }\n\n return renderMessage(messageNode, message)\n },\n [chatbotVotingEnabled, renderMessage, viewerLanguage]\n )\n\n return (\n <>\n <WithComponents overrides={{ Message: MessageOverride }}>\n <Window>\n {/* Custom Channel Header */}\n <div key=\"lt-channel-header\" className=\"p-4\">\n <CustomChannelHeader\n onBack={onBack}\n showBackButton={showBackButton}\n onShowInfo={handleShowInfo}\n canShowInfo={Boolean(participant)}\n showStarButton={showStarButton}\n dmAgentEnabled={showDmAgentHeader}\n getParticipantDisplayName={getParticipantDisplayName}\n />\n </div>\n\n {/* Custom Banner/Summary */}\n {renderChannelBanner ? (\n <React.Fragment key=\"lt-channel-banner\">\n {renderChannelBanner()}\n </React.Fragment>\n ) : null}\n\n {/* Message List */}\n <div\n key=\"lt-channel-message-list\"\n className=\"flex-1 overflow-hidden relative\"\n >\n <MessageList\n hideDeletedMessages\n hideNewMessageSeparator={false}\n messageActions={undefined}\n />\n </div>\n\n {renderConversationFooter ? (\n <React.Fragment key=\"lt-channel-conversation-footer\">\n {renderConversationFooter(channel)}\n </React.Fragment>\n ) : null}\n\n {/* Message Input */}\n <CustomMessageInput\n key=\"lt-channel-message-input\"\n renderActions={() => renderMessageInputActions?.(channel)}\n />\n </Window>\n </WithComponents>\n\n {/* Channel Info Dialog */}\n <ChannelInfoDialog\n dialogRef={infoDialogRef}\n onClose={handleCloseInfo}\n participant={participant}\n participantDisplayName={getParticipantDisplayName(participant)}\n channel={channel}\n followerStatusLabel={followerStatusLabel}\n onLeaveConversation={onLeaveConversation}\n onBlockParticipant={onBlockParticipant}\n showDeleteConversation={showDeleteConversation}\n onDeleteConversationClick={onDeleteConversationClick}\n onBlockParticipantClick={onBlockParticipantClick}\n onReportParticipantClick={onReportParticipantClick}\n customProfileContent={customProfileContent}\n customChannelActions={customChannelActions}\n />\n </>\n )\n}\n\n/**\n * Channel view component with message list and input\n */\nexport const ChannelView = React.memo<ChannelViewProps>(\n ({\n channel,\n onBack,\n showBackButton = false,\n renderMessageInputActions,\n renderConversationFooter,\n onLeaveConversation,\n onBlockParticipant,\n className,\n CustomChannelEmptyState = ChannelEmptyState,\n showDeleteConversation = true,\n onDeleteConversationClick,\n onBlockParticipantClick,\n onReportParticipantClick,\n dmAgentEnabled,\n messageMetadata,\n onMessageSent,\n showStarButton = false,\n chatbotVotingEnabled = false,\n renderChannelBanner,\n customProfileContent,\n customChannelActions,\n renderMessage,\n sendButton,\n viewerLanguage,\n getParticipantDisplayName: getParticipantDisplayNameProp,\n }) => {\n const getParticipantDisplayName = useCallback(\n (participant: ChannelMemberResponse | undefined) =>\n getParticipantDisplayNameProp?.(participant) ??\n resolveParticipantDisplayName(participant?.user),\n [getParticipantDisplayNameProp]\n )\n\n // Custom send message handler that:\n // 1. Applies messageMetadata if provided\n // 2. Adds skip_push and silent when DM agent is active\n // 3. Calls onMessageSent callback with full response\n // Read chatbot_paused inside callback to get current value at send time (not stale closure)\n const doSendMessageRequest = useCallback(\n async (\n _channel: ChannelType,\n message: Parameters<ChannelType['sendMessage']>[0],\n options?: Parameters<ChannelType['sendMessage']>[1]\n ) => {\n const agentPaused =\n (channel.data as { chatbot_paused?: boolean })?.chatbot_paused ===\n true\n const shouldSuppressNotifications = dmAgentEnabled && !agentPaused\n\n // Build final message with nested metadata\n const finalMessage = {\n ...message,\n ...(shouldSuppressNotifications && { silent: true }),\n ...(messageMetadata && {\n metadata: {\n ...(message.metadata ?? {}),\n ...messageMetadata,\n },\n }),\n }\n\n // Build final options\n const finalOptions = {\n ...options,\n ...(shouldSuppressNotifications && { skip_push: true }),\n }\n\n const response = await channel.sendMessage(finalMessage, finalOptions)\n\n // Fire callback with full response (includes message.id)\n onMessageSent?.(response)\n\n return response\n },\n [channel, dmAgentEnabled, messageMetadata, onMessageSent]\n )\n\n return (\n <div\n className={classNames(\n 'messaging-channel-view h-full flex flex-col',\n className\n )}\n >\n <DmAgentEnabledContext.Provider value={dmAgentEnabled ?? false}>\n <Channel\n channel={channel}\n MessageSystem={CustomSystemMessage}\n EmptyStateIndicator={CustomChannelEmptyState}\n LoadingIndicator={LoadingState}\n DateSeparator={CustomDateSeparator}\n TypingIndicator={CustomTypingIndicator}\n doSendMessageRequest={doSendMessageRequest}\n {...(sendButton ? { SendButton: sendButton } : {})}\n >\n <ChannelViewInner\n onBack={onBack}\n showBackButton={showBackButton}\n renderMessageInputActions={renderMessageInputActions}\n renderConversationFooter={renderConversationFooter}\n onLeaveConversation={onLeaveConversation}\n onBlockParticipant={onBlockParticipant}\n CustomChannelEmptyState={CustomChannelEmptyState}\n showDeleteConversation={showDeleteConversation}\n onDeleteConversationClick={onDeleteConversationClick}\n onBlockParticipantClick={onBlockParticipantClick}\n onReportParticipantClick={onReportParticipantClick}\n showStarButton={showStarButton}\n dmAgentEnabled={dmAgentEnabled}\n chatbotVotingEnabled={chatbotVotingEnabled}\n renderChannelBanner={renderChannelBanner}\n customProfileContent={customProfileContent}\n customChannelActions={customChannelActions}\n renderMessage={renderMessage}\n viewerLanguage={viewerLanguage}\n getParticipantDisplayName={getParticipantDisplayName}\n />\n </Channel>\n </DmAgentEnabledContext.Provider>\n </div>\n )\n }\n)\nChannelView.displayName = 'ChannelView'\n","import React from 'react'\n\nconst ChatBubblesIllustration = ({ className }: { className?: string }) => (\n <svg\n width=\"140\"\n height=\"120\"\n viewBox=\"44 -2 144 126\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n >\n <g clipPath=\"url(#clip0_empty_state)\">\n <path\n d=\"M123.68 82.1932C123.383 103.675 105.839 121 84.2417 121C77.4724 121 71.0986 119.297 65.5327 116.299L52.5873 119.687L53.8036 106.673C48.1776 99.8701 44.7994 91.1453 44.7994 81.6356C44.7994 59.8965 62.4554 42.2754 84.2374 42.2754C89.1328 42.2754 93.8175 43.1633 98.1413 44.789\"\n fill=\"#D7D4CE\"\n />\n <path\n d=\"M84.2458 86.0364C82.2851 86.0364 80.6957 84.4501 80.6957 82.4933C80.6957 80.5365 82.2851 78.9502 84.2458 78.9502C86.2065 78.9502 87.7959 80.5365 87.7959 82.4933C87.7959 84.4501 86.2065 86.0364 84.2458 86.0364Z\"\n fill=\"white\"\n />\n <path\n d=\"M68.3044 86.0364C66.3437 86.0364 64.7543 84.4501 64.7543 82.4933C64.7543 80.5365 66.3437 78.9502 68.3044 78.9502C70.2651 78.9502 71.8545 80.5365 71.8545 82.4933C71.8545 84.4501 70.2651 86.0364 68.3044 86.0364Z\"\n fill=\"white\"\n />\n <path\n d=\"M100.183 86.0364C98.2226 86.0364 96.6332 84.4501 96.6332 82.4933C96.6332 80.5365 98.2226 78.9502 100.183 78.9502C102.144 78.9502 103.733 80.5365 103.733 82.4933C103.733 84.4501 102.144 86.0364 100.183 86.0364Z\"\n fill=\"white\"\n />\n <g filter=\"url(#filter0_empty_state)\">\n <path\n d=\"M171.522 68.7154C177.443 61.4539 181 52.1488 181 42C181 18.8027 162.421 0 139.5 0C116.579 0 98 18.8027 98 42C98 65.1973 116.579 84 139.5 84C146.622 84 153.328 82.1857 159.184 78.9829L172.801 82.5993L171.522 68.7154Z\"\n fill=\"white\"\n />\n <path\n d=\"M171.522 68.7154C177.443 61.4539 181 52.1488 181 42C181 18.8027 162.421 0 139.5 0C116.579 0 98 18.8027 98 42C98 65.1973 116.579 84 139.5 84C146.622 84 153.328 82.1857 159.184 78.9829L172.801 82.5993L171.522 68.7154Z\"\n stroke=\"#D7D4CE\"\n strokeWidth=\"2\"\n strokeMiterlimit=\"10\"\n />\n </g>\n <path\n d=\"M139.502 45.5431C137.541 45.5431 135.952 43.9568 135.952 42C135.952 40.0432 137.541 38.4569 139.502 38.4569C141.462 38.4569 143.052 40.0432 143.052 42C143.052 43.9568 141.462 45.5431 139.502 45.5431Z\"\n fill=\"#D7D4CE\"\n />\n <path\n d=\"M123.561 45.5431C121.601 45.5431 120.011 43.9568 120.011 42C120.011 40.0432 121.601 38.4569 123.561 38.4569C125.522 38.4569 127.111 40.0432 127.111 42C127.111 43.9568 125.522 45.5431 123.561 45.5431Z\"\n fill=\"#D7D4CE\"\n />\n <path\n d=\"M155.439 45.5431C153.478 45.5431 151.889 43.9568 151.889 42C151.889 40.0432 153.478 38.4569 155.439 38.4569C157.4 38.4569 158.989 40.0432 158.989 42C158.989 43.9568 157.4 45.5431 155.439 45.5431Z\"\n fill=\"#D7D4CE\"\n />\n </g>\n <defs>\n <filter\n id=\"filter0_empty_state\"\n x=\"97\"\n y=\"-1\"\n width=\"89\"\n height=\"90\"\n filterUnits=\"userSpaceOnUse\"\n colorInterpolationFilters=\"sRGB\"\n >\n <feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n <feColorMatrix\n in=\"SourceAlpha\"\n type=\"matrix\"\n values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0\"\n result=\"hardAlpha\"\n />\n <feOffset dx=\"4\" dy=\"4\" />\n <feComposite in2=\"hardAlpha\" operator=\"out\" />\n <feColorMatrix\n type=\"matrix\"\n values=\"0 0 0 0 0.8428 0 0 0 0 0.830064 0 0 0 0 0.8095 0 0 0 1 0\"\n />\n <feBlend\n mode=\"normal\"\n in2=\"BackgroundImageFix\"\n result=\"effect1_dropShadow\"\n />\n <feBlend\n mode=\"normal\"\n in=\"SourceGraphic\"\n in2=\"effect1_dropShadow\"\n result=\"shape\"\n />\n </filter>\n <clipPath id=\"clip0_empty_state\">\n <rect width=\"233\" height=\"233\" fill=\"white\" />\n </clipPath>\n </defs>\n </svg>\n)\n\n/**\n * Empty state component shown when no channel is selected\n */\nexport const EmptyState = React.memo<{ hasChannels: boolean; channelsLoaded: boolean }>(\n ({ hasChannels, channelsLoaded }) => (\n <div className=\"messaging-empty-state flex items-center justify-center h-full p-8 text-balance\">\n <div className=\"flex flex-col items-center max-w-sm text-center\">\n <ChatBubblesIllustration />\n {channelsLoaded && !hasChannels && (\n <div className=\"mt-8\">\n <h2 className=\"font-medium text-black text-[18px] mb-2\">\n Your inbox is empty\n </h2>\n <p className=\"text-[#676B5F] text-sm mb-6\">\n Share with your followers to start receiving messages\n </p>\n </div>\n )}\n </div>\n </div>\n))\nEmptyState.displayName = 'EmptyState'\n","import React from 'react'\n\ntype ErrorStateProps = {\n message: string\n onBack?: () => void\n}\n\n/**\n * Error state component shown when something goes wrong\n */\nexport const ErrorState = React.memo<ErrorStateProps>(({ message, onBack }) => (\n <div className=\"messaging-error-state flex items-center justify-center h-full p-8\">\n <div className=\"text-center max-w-sm\">\n <div className=\"w-24 h-24 bg-danger-alt/20 rounded-full flex items-center justify-center mx-auto mb-6\">\n <span className=\"text-4xl\">⚠️</span>\n </div>\n\n <h2 className=\"font-semibold text-charcoal mb-2\">Oops!</h2>\n\n <p className=\"text-stone text-sm mb-6\">{message}</p>\n\n {onBack && (\n <button\n type=\"button\"\n onClick={onBack}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-[#7f22fe] hover:bg-primary-alt rounded-lg transition-colors focus-ring\"\n >\n Go Back\n </button>\n )}\n </div>\n </div>\n))\nErrorState.displayName = 'ErrorState'\n","import classNames from 'classnames'\nimport React, { useState, useCallback, useRef, useEffect } from 'react'\nimport type { Channel } from 'stream-chat'\n\nimport { useMessaging } from '../../hooks/useMessaging'\nimport type { MessagingShellProps } from '../../types'\nimport { ChannelList } from '../ChannelList'\nimport { ChannelView } from '../ChannelView'\n\nimport { EmptyState } from './EmptyState'\nimport { ErrorState } from './ErrorState'\nimport { LoadingState } from './LoadingState'\n\n/**\n * Main messaging interface component that combines channel list and channel view\n */\nexport const MessagingShell: React.FC<MessagingShellProps> = ({\n capabilities = {},\n className,\n renderMessageInputActions,\n renderConversationFooter,\n onChannelSelect,\n initialParticipantFilter,\n initialParticipantData,\n CustomChannelEmptyState,\n showChannelList = true,\n filters,\n channelRenderFilterFn,\n channelListCustomEmptyStateIndicator,\n onDeleteConversationClick,\n onBlockParticipantClick,\n onReportParticipantClick,\n dmAgentEnabled,\n messageMetadata,\n onMessageSent,\n showStarButton = false,\n chatbotVotingEnabled = false,\n viewerLanguage,\n renderMessagePreview,\n renderChannelBanner,\n customProfileContent,\n customChannelActions,\n renderMessage,\n sendButton,\n}) => {\n const {\n service,\n client,\n isConnected,\n isLoading,\n error,\n refreshConnection,\n debug,\n } = useMessaging()\n\n const [selectedChannel, setSelectedChannel] = useState<Channel | null>(null)\n const [hasChannels, setHasChannels] = useState(false)\n const [channelsLoaded, setChannelsLoaded] = useState(false)\n const [directConversationMode, setDirectConversationMode] = useState(false)\n const [directConversationError, setDirectConversationError] = useState<\n string | null\n >(null)\n\n const { showDeleteConversation = true } = capabilities\n\n // Create default filters and merge with provided filters\n const channelFilters = React.useMemo(() => {\n const userId = client?.userID\n\n // Base filters that should always be present\n const baseFilters = {\n type: 'messaging',\n last_message_at: { $exists: true },\n ...(userId && {\n members: { $in: [userId] },\n hidden: false,\n }),\n }\n\n // Merge provided filters with base filters\n // Provided filters can override base filters if needed\n return {\n ...baseFilters,\n ...filters,\n }\n }, [filters, client?.userID])\n\n // Track if we've already synced channels to prevent repeated API calls\n const syncedRef = useRef<string | null>(null)\n\n // Function to sync channels (extracted for reuse)\n const syncChannels = useCallback(async () => {\n if (!client || !isConnected) return\n\n const userId = client.userID\n if (!userId) return\n\n try {\n if (debug) {\n console.log('[MessagingShell] Syncing channels for user:', userId)\n }\n\n const channels = await client.queryChannels(\n {\n type: 'messaging',\n members: { $in: [userId] },\n },\n {},\n { limit: 100 }\n )\n\n setHasChannels(channels.length > 0)\n setChannelsLoaded(true)\n syncedRef.current = userId // Mark as synced for this user\n\n if (debug) {\n console.log('[MessagingShell] Channels synced successfully:', {\n channelCount: channels.length,\n })\n }\n } catch (error) {\n console.error('[MessagingShell] Failed to sync channels:', error)\n // Don't mark as synced on error, allow retry\n }\n }, [client, isConnected, debug])\n\n // Sync existing channels to drive empty-state behavior.\n useEffect(() => {\n if (!client || !isConnected) return\n\n const userId = client.userID\n if (!userId) return\n\n // Prevent repeated sync for the same user\n if (syncedRef.current === userId) return\n\n syncChannels()\n }, [client, isConnected, syncChannels])\n\n // Load initial channel for direct conversation mode\n useEffect(() => {\n if (!initialParticipantFilter || !client || !isConnected) return\n\n const loadInitialChannel = async () => {\n const userId = client.userID\n if (!userId) return\n\n try {\n if (debug) {\n console.log(\n '[MessagingShell] Loading initial conversation with:',\n initialParticipantFilter\n )\n }\n\n const channels = await client.queryChannels(\n {\n type: 'messaging',\n members: { $eq: [userId, initialParticipantFilter] },\n },\n {},\n { limit: 1 }\n )\n\n if (channels.length > 0) {\n setSelectedChannel(channels[0])\n setDirectConversationMode(true)\n setDirectConversationError(null)\n\n // Notify parent component of channel selection\n if (onChannelSelect) {\n onChannelSelect(channels[0])\n }\n\n if (debug) {\n console.log(\n '[MessagingShell] Initial conversation loaded:',\n channels[0].id\n )\n }\n } else {\n // No channel found - try to create one if participant data is provided\n if (initialParticipantData && service) {\n if (debug) {\n console.log(\n '[MessagingShell] No conversation found, creating one for:',\n initialParticipantData\n )\n }\n\n try {\n // Use the existing service method to create the channel\n const channel = await service.startChannelWithParticipant({\n id: initialParticipantData.id,\n name: initialParticipantData.name,\n phone: initialParticipantData.phone,\n })\n\n setSelectedChannel(channel)\n setDirectConversationMode(true)\n setDirectConversationError(null)\n\n // Notify parent component of channel selection\n if (onChannelSelect) {\n onChannelSelect(channel)\n }\n\n if (debug) {\n console.log(\n '[MessagingShell] Channel created and loaded:',\n channel.id\n )\n }\n } catch (createErr) {\n console.error(\n '[MessagingShell] Failed to create conversation:',\n createErr\n )\n setDirectConversationError('Failed to create conversation')\n }\n } else {\n // No participant data provided, show error\n setDirectConversationError(\n 'No conversation found with this account'\n )\n\n if (debug) {\n console.log(\n '[MessagingShell] No conversation found for:',\n initialParticipantFilter\n )\n }\n }\n }\n } catch (err) {\n console.error(\n '[MessagingShell] Failed to load initial conversation:',\n err\n )\n setDirectConversationError('Failed to load conversation')\n }\n }\n\n loadInitialChannel()\n }, [\n initialParticipantFilter,\n initialParticipantData,\n client,\n isConnected,\n service,\n debug,\n onChannelSelect,\n ])\n\n const handleChannelSelect = useCallback(\n (channel: Channel) => {\n setSelectedChannel(channel)\n onChannelSelect?.(channel)\n },\n [onChannelSelect]\n )\n\n const handleBackToChannelList = useCallback(() => {\n // In direct conversation mode, don't allow going back to channel list\n // The parent component should handle navigation\n if (directConversationMode) return\n\n setSelectedChannel(null)\n }, [directConversationMode])\n\n const handleLeaveConversation = useCallback(\n async (channel: Channel) => {\n if (debug) {\n console.log('[MessagingShell] Leaving conversation:', channel.id)\n }\n setSelectedChannel(null)\n setDirectConversationMode(false) // Exit direct conversation mode\n\n // Force re-sync to update the existing participants list\n syncedRef.current = null\n await syncChannels()\n },\n [syncChannels, debug]\n )\n\n const handleBlockParticipant = useCallback(\n async (participantId?: string) => {\n if (debug) {\n console.log('[MessagingShell] Blocking participant:', participantId)\n }\n setSelectedChannel(null)\n setDirectConversationMode(false) // Exit direct conversation mode\n\n // Force re-sync to update the existing participants list\n syncedRef.current = null\n await syncChannels()\n },\n [syncChannels, debug]\n )\n\n const isChannelSelected = Boolean(selectedChannel)\n\n // Show loading state\n if (isLoading) {\n return (\n <div className={classNames('h-full', className)}>\n <LoadingState />\n </div>\n )\n }\n\n // Show error state\n if (error) {\n return (\n <div className={classNames('h-full', className)}>\n <ErrorState message={error} onBack={refreshConnection} />\n </div>\n )\n }\n\n // Show not connected state\n if (!isConnected || !client) {\n return (\n <div className={classNames('h-full', className)}>\n <ErrorState\n message=\"Not connected to messaging service\"\n onBack={refreshConnection}\n />\n </div>\n )\n }\n\n // Show direct conversation error state\n if (directConversationError) {\n return (\n <div className={classNames('h-full', className)}>\n <ErrorState message={directConversationError} />\n </div>\n )\n }\n\n return (\n <div\n className={classNames(\n 'messaging-shell h-full bg-background-primary overflow-hidden',\n className\n )}\n >\n <div className=\"flex h-full min-h-0\">\n {/* Channel List Sidebar */}\n <div\n className={classNames(\n 'messaging-channel-list-sidebar min-h-0 min-w-0 lg:flex lg:flex-col',\n {\n '!hidden': showChannelList === false || directConversationMode,\n // Hide on mobile when channel selected, show on desktop with consistent wide width\n 'hidden lg:flex lg:flex-1 lg:max-w-2xl':\n showChannelList !== false &&\n !directConversationMode &&\n isChannelSelected,\n // Show on mobile when no channel selected, use same wide width on desktop\n 'flex flex-col w-full lg:flex-1 lg:max-w-2xl':\n showChannelList !== false &&\n !directConversationMode &&\n !isChannelSelected,\n }\n )}\n >\n <ChannelList\n onChannelSelect={handleChannelSelect}\n selectedChannel={selectedChannel || undefined}\n filters={channelFilters}\n channelRenderFilterFn={channelRenderFilterFn}\n customEmptyStateIndicator={channelListCustomEmptyStateIndicator}\n renderMessagePreview={renderMessagePreview}\n viewerLanguage={viewerLanguage}\n />\n </div>\n\n {/* Channel View */}\n <div\n className={classNames(\n 'messaging-conversation-view flex-1 flex-col min-w-0 min-h-0',\n {\n // In direct conversation mode (or waiting for it), always show (full width)\n flex:\n directConversationMode ||\n isChannelSelected ||\n initialParticipantFilter,\n // Normal mode: hide on mobile when no channel selected\n 'hidden lg:flex':\n !directConversationMode &&\n !isChannelSelected &&\n !initialParticipantFilter,\n }\n )}\n >\n {selectedChannel ? (\n <div className=\"flex-1 min-h-0 flex flex-col\">\n <ChannelView\n channel={selectedChannel}\n key={selectedChannel.id}\n onBack={handleBackToChannelList}\n showBackButton={!directConversationMode}\n renderMessageInputActions={renderMessageInputActions}\n renderConversationFooter={renderConversationFooter}\n renderChannelBanner={renderChannelBanner}\n onLeaveConversation={handleLeaveConversation}\n onBlockParticipant={handleBlockParticipant}\n CustomChannelEmptyState={CustomChannelEmptyState}\n showDeleteConversation={showDeleteConversation}\n onDeleteConversationClick={onDeleteConversationClick}\n onBlockParticipantClick={onBlockParticipantClick}\n onReportParticipantClick={onReportParticipantClick}\n dmAgentEnabled={dmAgentEnabled}\n messageMetadata={messageMetadata}\n onMessageSent={onMessageSent}\n showStarButton={showStarButton}\n chatbotVotingEnabled={chatbotVotingEnabled}\n viewerLanguage={viewerLanguage}\n customProfileContent={customProfileContent}\n customChannelActions={customChannelActions}\n renderMessage={renderMessage}\n sendButton={sendButton}\n />\n </div>\n ) : initialParticipantFilter ? (\n // Show loading while creating/loading direct conversation channel\n <LoadingState />\n ) : (\n <EmptyState\n hasChannels={hasChannels}\n channelsLoaded={channelsLoaded}\n />\n )}\n </div>\n </div>\n </div>\n )\n}\n","/**\n * Scheme detector: `protocol:` per RFC 3986 — a letter followed by any\n * combination of letters, digits, `+`, `.`, or `-` then `:`.\n */\nconst SCHEME_PATTERN = /^([a-z][a-z0-9+.-]*):/i\n\n/**\n * Allowlist of schemes that are safe to forward into `<a href>` for\n * external navigation. `javascript:` / `data:` / `vbscript:` etc. are\n * intentionally **not** on this list — link-attachment data is\n * effectively user-controlled, so passing them through would let a\n * recipient click execute attacker-supplied code or markup.\n */\nconst SAFE_SCHEMES = new Set(['http', 'https', 'mailto', 'tel', 'sms'])\n\n/**\n * Normalize a user-supplied URL into something safe to assign to\n * `<a href>` for external navigation.\n *\n * Link attachments / link apps always point at external destinations\n * (Spotify, TikTok, FAQ links, bare-hostname Linktree URLs like\n * `tr.ee/briemix`, etc.). Without normalization, a bare hostname is\n * treated as a relative path by the browser and clicks navigate within\n * the host site (e.g. `https://linktr.ee/admin/tr.ee/briemix`) instead\n * of opening the intended destination.\n *\n * Rules:\n * - Empty / whitespace-only → returns `undefined` (no href).\n * - Explicit scheme in the safe allowlist (`http`, `https`, `mailto`,\n * `tel`, `sms`) → returned trimmed.\n * - Explicit scheme **not** on the allowlist (`javascript:`, `data:`,\n * `vbscript:`, custom protocols, …) → returns `undefined` so the\n * shell falls back to a non-navigational chrome instead of letting\n * an attacker-controlled URL execute on click.\n * - Protocol-relative (`//example.com/…`) → returned as-is; browsers\n * resolve these against the current page's scheme.\n * - Site-relative path (`/admin/…`) → returned as-is so consumers can\n * still opt into in-app navigation if they really want to.\n * - Bare hostname or anything else → `https://` is prepended so the\n * browser treats it as an external URL.\n */\nexport function normalizeExternalHref(value?: string): string | undefined {\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n if (trimmed === '') return undefined\n\n const schemeMatch = SCHEME_PATTERN.exec(trimmed)\n if (schemeMatch) {\n const scheme = schemeMatch[1].toLowerCase()\n return SAFE_SCHEMES.has(scheme) ? trimmed : undefined\n }\n\n if (trimmed.startsWith('//')) return trimmed\n if (trimmed.startsWith('/')) return trimmed\n return `https://${trimmed}`\n}\n","import classNames from 'classnames'\nimport React from 'react'\n\nimport type { LinkAttachmentCta } from '../../types'\n\nimport type { LinkAttachmentVariant } from './CardShell'\nimport { normalizeExternalHref } from './normalizeExternalHref'\n\nexport interface CardCtaProps {\n variant: LinkAttachmentVariant\n cta: LinkAttachmentCta\n}\n\nconst BUTTON_CLASS_BY_VARIANT: Record<LinkAttachmentVariant, string> = {\n dark: 'bg-white text-[#121110] hover:bg-white/90',\n light: 'bg-[#121110] text-white hover:bg-[#2a2928]',\n}\n\n/**\n * Pill-shaped CTA rendered below the description on Link App cards that\n * surface an action instead of a URL (e.g. FAQ \"View FAQs\", Form \"Complete form\").\n * Renders as `<a target=\"_blank\">` when `cta.href` is set, otherwise as a\n * plain `<button>`.\n */\nconst CardCta: React.FC<CardCtaProps> = ({ variant, cta }) => {\n const className = classNames(\n 'mt-2 inline-flex h-10 w-full items-center justify-center rounded-full px-4 text-sm font-medium leading-none transition-colors',\n BUTTON_CLASS_BY_VARIANT[variant]\n )\n\n // Mirror the URL normalization used by the shell anchor so bare\n // hostnames (e.g. `tr.ee/foo`) open as external links rather than\n // resolving against the current host.\n const normalizedHref = normalizeExternalHref(cta.href)\n\n if (normalizedHref) {\n return (\n <a\n href={normalizedHref}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={(e) => {\n // Stop the click from bubbling up to the card's anchor wrapper\n // (Received variant) so we don't navigate twice.\n e.stopPropagation()\n cta.onClick?.()\n }}\n className={`${className} no-underline`}\n >\n {cta.label}\n </a>\n )\n }\n\n return (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n cta.onClick?.()\n }}\n className={className}\n >\n {cta.label}\n </button>\n )\n}\n\nexport default CardCta\n","import classNames from 'classnames'\nimport React from 'react'\n\nimport type { LinkAttachmentCta } from '../../types'\n\nimport CardCta from './CardCta'\nimport type { LinkAttachmentVariant } from './CardShell'\n\nexport interface CardBodyProps {\n variant: LinkAttachmentVariant\n title?: string\n /** Placeholder shown in the title slot when no title is set (dark variants only). */\n placeholderTitle?: string\n description?: string\n /** Footer URL shown below the description. Ignored when `cta` is set. */\n url?: string\n /**\n * Optional 16x16 brand badge rendered before the title (used by Link Apps:\n * Spotify, TikTok, FAQ, Form, etc.).\n */\n appIcon?: React.ReactNode\n /** Optional CTA rendered in place of the URL footer. */\n cta?: LinkAttachmentCta\n /** Trailing action rendered on the right of the title/description block. */\n trailingAction?: React.ReactNode\n}\n\nconst TITLE_CLASS_BY_VARIANT: Record<LinkAttachmentVariant, string> = {\n dark: 'text-white',\n light: 'text-black/90',\n}\n\nconst TITLE_DIMMED_CLASS = 'text-white/30'\n\nconst SECONDARY_CLASS_BY_VARIANT: Record<LinkAttachmentVariant, string> = {\n dark: 'text-white/55',\n light: 'text-black/55',\n}\n\n/**\n * Body of a `LinkAttachment.*` card. Matches the Figma `Container > Labels`\n * group: 16px horizontal padding, 12px vertical padding, 8px gap between\n * the title/description group and the URL/CTA footer, 4px gap within the\n * title group.\n *\n * Returns `null` when there's nothing to render so plain image / file\n * attachments collapse to a thumbnail-only card.\n */\nconst CardBody: React.FC<CardBodyProps> = ({\n variant,\n title,\n placeholderTitle,\n description,\n url,\n appIcon,\n cta,\n trailingAction,\n}) => {\n const isDark = variant === 'dark'\n const displayTitle = title ?? (isDark ? placeholderTitle : undefined) ?? ''\n const hasTitle = displayTitle.trim() !== ''\n const hasDescription =\n description != null && description.trim() !== ''\n // Mirror the trimming applied by `ReceivedCard` so a whitespace-only\n // `url` collapses the body footer (and the whole body, for media-only\n // cards) instead of rendering an empty line.\n const trimmedUrl = typeof url === 'string' ? url.trim() : ''\n const hasUrl = trimmedUrl !== ''\n const hasCta = cta != null\n\n if (!hasTitle && !hasDescription && !hasUrl && !hasCta) return null\n\n const titleDimmed = isDark && !title\n\n const titleClass = classNames(\n 'truncate text-base font-medium leading-6',\n titleDimmed ? TITLE_DIMMED_CLASS : TITLE_CLASS_BY_VARIANT[variant]\n )\n\n const secondaryClass = classNames(\n 'truncate text-xs leading-4',\n SECONDARY_CLASS_BY_VARIANT[variant]\n )\n\n return (\n <div className=\"px-4 py-3\">\n <div className=\"flex items-end gap-3\">\n <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n <div className=\"flex min-w-0 flex-col gap-1\">\n {hasTitle && (\n <div className=\"flex min-w-0 items-center gap-2\">\n {appIcon ? <span className=\"shrink-0\">{appIcon}</span> : null}\n <p className={classNames('min-w-0', titleClass)}>\n {displayTitle}\n </p>\n </div>\n )}\n\n {hasDescription && (\n <p className={secondaryClass}>{description}</p>\n )}\n </div>\n\n {!hasCta && hasUrl && (\n <p className={secondaryClass}>{trimmedUrl}</p>\n )}\n </div>\n\n {trailingAction && <div className=\"shrink-0\">{trailingAction}</div>}\n </div>\n\n {cta && <CardCta variant={variant} cta={cta} />}\n </div>\n )\n}\n\nexport default CardBody\n","import classNames from 'classnames'\nimport React from 'react'\n\nexport type LinkAttachmentVariant = 'dark' | 'light'\n\nexport interface CardShellProps {\n variant: LinkAttachmentVariant\n children: React.ReactNode\n /**\n * When provided, the entire card chrome is rendered as an anchor (used by\n * the Received card to open the link target on click). Falls back to a\n * `<div>` when omitted so Composer / Sent cards stay non-navigational.\n */\n href?: string\n /**\n * Click handler for the card chrome. When `href` is set the shell is an\n * anchor and `onClick` is invoked in addition to navigation. When `href`\n * is omitted but `onClick` is set, the shell renders as a clickable\n * button (used by media-only Received cards to open an image preview).\n */\n onClick?: () => void\n /** Accessible label for the clickable variant (when `onClick` is set without `href`). */\n ariaLabel?: string\n rootRef?: React.Ref<HTMLElement>\n /**\n * Absolutely-positioned slot rendered in the top-right corner of the\n * shell. Used by the Composer card to surface its dismiss affordance\n * when there's no hero thumbnail to anchor it to.\n */\n topRight?: React.ReactNode\n /**\n * Overrides the variant-derived background colour (e.g. audio cards\n * use `bg-[#F2F3F4]` regardless of the dark/light variant).\n */\n bgClassName?: string\n 'data-testid'?: string\n}\n\nconst SHELL_CLASS = classNames(\n 'relative block w-[280px] select-none overflow-hidden rounded-md',\n // 1px hairline border that sits flush with the card chrome — matches\n // the messaging design system's \"small border around link attachments\"\n // treatment from the mobile spec. The drop shadow remains for depth.\n 'border border-black/[0.08]',\n 'shadow-[0_1px_2px_rgba(0,0,0,0.04),0_8px_32px_rgba(0,0,0,0.1)]'\n)\n\n/**\n * Outer chrome for every `LinkAttachment.*` card. Matches the 280px width,\n * 16px corner radius, and shadow-400 treatment from the Figma design system.\n */\nconst CardShell: React.FC<CardShellProps> = ({\n variant,\n children,\n href,\n onClick,\n ariaLabel,\n rootRef,\n topRight,\n bgClassName,\n 'data-testid': dataTestId,\n}) => {\n const isInteractive = href != null || onClick != null\n const className = classNames(\n SHELL_CLASS,\n bgClassName ?? (variant === 'dark' ? 'bg-[#121110]' : 'bg-white'),\n // `focus-ring` is a design-system utility from the component-library\n // tailwind preset — outline-none + a black 2px focus-visible ring\n // with offset, so keyboard users can see the focused card.\n isInteractive ? 'cursor-pointer no-underline focus-ring' : null\n )\n\n const corner = topRight ? (\n <div className=\"pointer-events-auto absolute right-3 top-3 z-10\">\n {topRight}\n </div>\n ) : null\n\n if (href) {\n return (\n <a\n ref={rootRef as React.Ref<HTMLAnchorElement>}\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={onClick}\n data-testid={dataTestId}\n className={className}\n >\n {children}\n {corner}\n </a>\n )\n }\n\n if (onClick) {\n return (\n <button\n ref={rootRef as React.Ref<HTMLButtonElement>}\n type=\"button\"\n onClick={onClick}\n aria-label={ariaLabel}\n data-testid={dataTestId}\n className={classNames(className, 'text-left')}\n >\n {children}\n {corner}\n </button>\n )\n }\n\n return (\n <div\n ref={rootRef as React.Ref<HTMLDivElement>}\n data-testid={dataTestId}\n className={className}\n >\n {children}\n {corner}\n </div>\n )\n}\n\nexport default CardShell\n","import classNames from 'classnames'\nimport React from 'react'\n\nimport { renderTypeIcon } from '../../../AttachmentCard'\nimport { getSourceType } from '../../../AttachmentCard/utils/mimeType'\n\nimport type { LinkAttachmentVariant } from './CardShell'\n\nexport interface CardThumbnailProps {\n variant: LinkAttachmentVariant\n /** Source URL of the hero image (or poster for video). */\n thumbnailUrl?: string\n /**\n * Playable media URL. When provided alongside a video / audio `mimeType`,\n * the hero region renders a native HTML5 player with controls instead of\n * the static thumbnail / placeholder.\n */\n sourceUrl?: string\n /** Alt text — typically the card's title. */\n title?: string\n /**\n * Drives the placeholder type icon when no `thumbnailUrl` is provided,\n * and selects between image / video / audio rendering when `sourceUrl`\n * is set. Defaults to a generic image icon when unset.\n */\n mimeType?: string\n /** Optional decorations layered into the top corners of the thumbnail. */\n topLeft?: React.ReactNode\n topRight?: React.ReactNode\n}\n\nconst PLACEHOLDER_BG: Record<LinkAttachmentVariant, string> = {\n dark: 'bg-white/10',\n light: 'bg-black/5',\n}\n\nconst PLACEHOLDER_ICON: Record<LinkAttachmentVariant, string> = {\n dark: 'size-16 text-white/25',\n light: 'size-16 text-black/25',\n}\n\n/**\n * 180px hero region shown above the card body. Renders, in priority order:\n * 1. A native `<video controls>` when `sourceUrl` is set and the mime is\n * video — `thumbnailUrl` acts as the poster.\n * 2. A native `<audio controls>` when `sourceUrl` is set and the mime is\n * audio — laid over the audio type-icon backdrop.\n * 3. The supplied `thumbnailUrl` image.\n * 4. A placeholder type-icon derived from `mimeType`.\n */\n/** Mime + sourceUrl gives us a playable audio attachment. */\nexport const isPlayableAudio = (mimeType?: string, sourceUrl?: string) =>\n !!sourceUrl && !!mimeType && getSourceType(mimeType) === 'audio'\n\n/**\n * Mime + sourceUrl gives us a playable video or audio attachment. Used by\n * Received to skip wrapping the shell in an interactive `<button>` so the\n * native media controls remain operable.\n */\nexport const isPlayableMedia = (mimeType?: string, sourceUrl?: string) => {\n if (!sourceUrl || !mimeType) return false\n const source = getSourceType(mimeType)\n return source === 'video' || source === 'audio'\n}\n\n/**\n * Background colour the LinkAttachment cards switch to when the source is\n * audio — flat neutral around the native `<audio>` chrome regardless of\n * the dark / light variant.\n */\nexport const AUDIO_BG_CLASS = 'bg-[#F2F3F4]'\n\nconst CardThumbnail: React.FC<CardThumbnailProps> = ({\n variant,\n thumbnailUrl,\n sourceUrl,\n title,\n mimeType = 'image/*',\n topLeft,\n topRight,\n}) => {\n const sourceType = getSourceType(mimeType)\n const isPlayableVideo = !!sourceUrl && sourceType === 'video'\n\n if (isPlayableAudio(mimeType, sourceUrl)) {\n // Audio collapses the hero entirely — the native player sits inside\n // the card chrome with a bit of padding so the card background\n // (typically `bg-[#F2F3F4]`) is visible around it.\n return (\n <div className=\"p-3\">\n <audio\n src={sourceUrl}\n controls\n preload=\"metadata\"\n className=\"block w-full\"\n >\n <track kind=\"captions\" />\n </audio>\n </div>\n )\n }\n\n return (\n <div\n className={classNames(\n 'relative h-[180px] w-full overflow-hidden',\n isPlayableVideo && 'bg-black'\n )}\n >\n {isPlayableVideo ? (\n <video\n src={sourceUrl}\n poster={thumbnailUrl}\n controls\n playsInline\n preload=\"metadata\"\n className=\"absolute inset-0 h-full w-full object-contain\"\n >\n <track kind=\"captions\" />\n </video>\n ) : thumbnailUrl ? (\n <img\n src={thumbnailUrl}\n alt={title ?? ''}\n draggable={false}\n className=\"absolute inset-0 h-full w-full object-cover\"\n />\n ) : (\n <div\n className={classNames(\n 'flex h-full w-full items-center justify-center',\n PLACEHOLDER_BG[variant]\n )}\n >\n {renderTypeIcon(mimeType, {\n className: PLACEHOLDER_ICON[variant],\n weight: 'regular',\n })}\n </div>\n )}\n\n {topLeft ? (\n <div className=\"pointer-events-auto absolute left-3 top-3 z-10\">\n {topLeft}\n </div>\n ) : null}\n {topRight ? (\n <div className=\"pointer-events-auto absolute right-3 top-3 z-10\">\n {topRight}\n </div>\n ) : null}\n </div>\n )\n}\n\nexport default CardThumbnail\n","import { PencilSimpleIcon, XIcon } from '@phosphor-icons/react'\nimport React from 'react'\n\nimport type { LinkAttachmentBaseProps } from '../../types'\nimport CardBody from '../_shared/CardBody'\nimport CardShell from '../_shared/CardShell'\nimport CardThumbnail, {\n AUDIO_BG_CLASS,\n isPlayableAudio,\n} from '../_shared/CardThumbnail'\n\nexport interface ComposerCardProps extends LinkAttachmentBaseProps {\n /**\n * When provided, renders a dismiss X in the thumbnail corner. Called when\n * the composer clicks it to remove the attachment.\n */\n onDismiss?: () => void\n /**\n * When provided, renders a pencil button to the right of the description\n * that the composer can use to edit the attachment metadata.\n */\n onEditClick?: () => void\n}\n\n/**\n * The card the composer sees while drafting a link attachment.\n * Matches the Composer column of the messaging design system in Figma.\n */\nconst ComposerCard: React.FC<ComposerCardProps> = ({\n title,\n placeholderTitle,\n description,\n url,\n mimeType,\n thumbnailUrl,\n sourceUrl,\n layout = 'featured',\n appIcon,\n cta,\n onDismiss,\n onEditClick,\n}) => {\n const isClassic = layout === 'classic'\n const isAudio = isPlayableAudio(mimeType, sourceUrl)\n const dismissButton = onDismiss ? (\n <button\n type=\"button\"\n onClick={onDismiss}\n aria-label=\"Dismiss attachment\"\n className=\"flex size-6 items-center justify-center rounded-full bg-[#121110] text-white\"\n >\n <XIcon className=\"size-3\" weight=\"bold\" />\n </button>\n ) : undefined\n\n const editButton = onEditClick ? (\n <button\n type=\"button\"\n onClick={onEditClick}\n aria-label=\"Edit attachment\"\n className=\"flex size-10 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/15\"\n >\n <PencilSimpleIcon className=\"size-5\" weight=\"regular\" />\n </button>\n ) : undefined\n\n // Audio cards collapse to just the native player — render the dismiss\n // button as an inline sibling so it always has its own space and never\n // overlaps the audio control's volume/menu buttons.\n if (isAudio) {\n return (\n <CardShell variant=\"dark\" bgClassName={AUDIO_BG_CLASS}>\n <div className=\"flex items-center gap-2 pr-3\">\n <div className=\"min-w-0 flex-1\">\n <CardThumbnail\n variant=\"dark\"\n sourceUrl={sourceUrl}\n title={title}\n mimeType={mimeType}\n />\n </div>\n {dismissButton && <div className=\"shrink-0\">{dismissButton}</div>}\n </div>\n </CardShell>\n )\n }\n\n return (\n <CardShell\n variant=\"dark\"\n topRight={isClassic ? dismissButton : undefined}\n >\n {!isClassic && (\n <CardThumbnail\n variant=\"dark\"\n thumbnailUrl={thumbnailUrl}\n sourceUrl={sourceUrl}\n title={title}\n mimeType={mimeType}\n topRight={dismissButton}\n />\n )}\n <CardBody\n variant=\"dark\"\n title={title}\n placeholderTitle={placeholderTitle}\n description={description}\n url={url}\n appIcon={appIcon}\n cta={cta}\n trailingAction={editButton}\n />\n </CardShell>\n )\n}\n\nexport default ComposerCard\n","import React from 'react'\n\nimport type { LinkAttachmentBaseProps } from '../../types'\nimport CardBody from '../_shared/CardBody'\nimport CardShell from '../_shared/CardShell'\nimport CardThumbnail, {\n AUDIO_BG_CLASS,\n isPlayableAudio,\n isPlayableMedia,\n} from '../_shared/CardThumbnail'\nimport { normalizeExternalHref } from '../_shared/normalizeExternalHref'\n\nexport interface ReceivedCardProps extends LinkAttachmentBaseProps {\n /**\n * Fired when the recipient activates the card. Behavior depends on how\n * the card is configured:\n * - **Link app with a CTA** (FAQ / Form): the CTA owns navigation;\n * `onClick` fires when the recipient taps the CTA itself, alongside\n * `cta.onClick` (use for analytics).\n * - **Link app with a URL** (Spotify / TikTok / generic link): the card\n * chrome is an `<a target=\"_blank\">` opening `url` — `onClick` fires\n * alongside the navigation (use for analytics).\n * - **Hero-image only card**: the card has no URL, so it renders as\n * a button. `onClick` is the consumer's hook for opening a preview.\n * - **Video / audio link previews**: the shell stays non-interactive\n * so the native media controls remain operable — `onClick` is\n * ignored in this configuration.\n */\n onClick?: () => void\n}\n\n/**\n * The card the recipient sees in chat for a link attachment. Matches the\n * Received column of the messaging design system in Figma.\n */\nconst ReceivedCard: React.FC<ReceivedCardProps> = ({\n title,\n description,\n url,\n mimeType,\n thumbnailUrl,\n sourceUrl,\n layout = 'featured',\n appIcon,\n cta,\n onClick,\n}) => {\n // Video / audio link previews wrap the native media element — render a\n // plain non-interactive shell and let the media controls own clicks so\n // taps on play/pause/scrubber don't fire the outer card action.\n const isPlayingMedia = isPlayableMedia(mimeType, sourceUrl)\n // Normalize the URL so a bare hostname like `tr.ee/briemix` (used in\n // our own docs / stories) is treated as an external link instead of a\n // relative path. Returns `undefined` for empty / whitespace-only\n // values, so those fall through to the preview path instead of\n // producing an empty `href` on the shell anchor.\n const normalizedUrl = normalizeExternalHref(url)\n const shellHref =\n cta == null && normalizedUrl != null && !isPlayingMedia\n ? normalizedUrl\n : undefined\n const shellOnClick = cta == null && !isPlayingMedia ? onClick : undefined\n const audioBg = isPlayableAudio(mimeType, sourceUrl)\n ? AUDIO_BG_CLASS\n : undefined\n\n // When a CTA is set the shell isn't interactive — the CTA owns the\n // click target. Forward the card-level `onClick` to the CTA so\n // analytics / side-effect consumers still fire on activation while\n // preserving the CTA's own `onClick` callback.\n const wrappedCta =\n cta && onClick\n ? {\n ...cta,\n onClick: () => {\n onClick()\n cta.onClick?.()\n },\n }\n : cta\n\n return (\n <CardShell\n variant=\"light\"\n href={shellHref}\n onClick={shellOnClick}\n ariaLabel={title ?? 'Open attachment preview'}\n bgClassName={audioBg}\n data-testid=\"link-attachment\"\n >\n {layout === 'featured' && (\n <CardThumbnail\n variant=\"light\"\n thumbnailUrl={thumbnailUrl}\n sourceUrl={sourceUrl}\n title={title}\n mimeType={mimeType}\n />\n )}\n <CardBody\n variant=\"light\"\n title={title}\n description={description}\n url={url}\n appIcon={appIcon}\n cta={wrappedCta}\n />\n </CardShell>\n )\n}\n\nexport default ReceivedCard\n","import React from 'react'\n\nimport type { LinkAttachmentBaseProps } from '../../types'\nimport CardBody from '../_shared/CardBody'\nimport CardShell from '../_shared/CardShell'\nimport CardThumbnail, {\n AUDIO_BG_CLASS,\n isPlayableAudio,\n} from '../_shared/CardThumbnail'\n\nexport interface SentCardProps extends LinkAttachmentBaseProps {}\n\n/**\n * The card the sender sees in chat after a link attachment has been posted.\n * Matches the Sent column of the messaging design system in Figma — same\n * dark chrome as the Composer card minus the dismiss / edit affordances.\n */\nconst SentCard: React.FC<SentCardProps> = ({\n title,\n placeholderTitle,\n description,\n url,\n mimeType,\n thumbnailUrl,\n sourceUrl,\n layout = 'featured',\n appIcon,\n cta,\n}) => (\n <CardShell\n variant=\"dark\"\n bgClassName={\n isPlayableAudio(mimeType, sourceUrl) ? AUDIO_BG_CLASS : undefined\n }\n >\n {layout === 'featured' && (\n <CardThumbnail\n variant=\"dark\"\n thumbnailUrl={thumbnailUrl}\n sourceUrl={sourceUrl}\n title={title}\n mimeType={mimeType}\n />\n )}\n <CardBody\n variant=\"dark\"\n title={title}\n placeholderTitle={placeholderTitle}\n description={description}\n url={url}\n appIcon={appIcon}\n cta={cta}\n />\n </CardShell>\n)\n\nexport default SentCard\n","import ComposerCard, {\n type ComposerCardProps,\n} from './components/Composer/Card'\nimport ReceivedCard, {\n type ReceivedCardProps,\n} from './components/Received/Card'\nimport SentCard, { type SentCardProps } from './components/Sent/Card'\n\n/**\n * Link previews (1P / 3P Link Apps) shown in the chat thread. Mirrors\n * the `LockedAttachment` API — render `LinkAttachment.Composer` while\n * drafting, `LinkAttachment.Sent` after posting, and\n * `LinkAttachment.Received` in the recipient's thread. Maps to the\n * \"LinkApps\" section of the messaging design system.\n *\n * Two visual layouts via the `layout` prop:\n * - **Featured** (default) — 180px hero thumbnail above the body. Used\n * by hero-image LinkApps (Spotify with cover art, TikTok with a\n * frame, etc.).\n * - **Classic** — compact card with no hero thumbnail; title /\n * description / URL / CTA only. Used for LinkApp embeds without\n * artwork (FAQ, Form) and any link preview that lacks OG imagery.\n *\n * For chat **document / image / video / audio attachments**, reach for\n * `MessageAttachment.{Image,Video,Audio,Pdf,File}` instead — those\n * render as bubbles with built-in viewers (zoom-capable image\n * lightbox, native PDF viewer, video / audio with download) and a\n * caption slot for accompanying text.\n */\nconst LinkAttachment = {\n Composer: ComposerCard,\n Sent: SentCard,\n Received: ReceivedCard,\n}\n\nexport default LinkAttachment\nexport type { ComposerCardProps, SentCardProps, ReceivedCardProps }\nexport type {\n LinkAttachmentBaseProps,\n LinkAttachmentCta,\n LinkAttachmentLayout,\n} from './types'\n","import classNames from 'classnames'\nimport React from 'react'\n\nimport type { BubbleGroupPosition, BubbleVariant } from '../types'\n\nexport interface BubbleProps {\n variant: BubbleVariant\n /** Optional message text rendered below the attachment slot. */\n text?: React.ReactNode\n /**\n * Renders a hairline border around the bubble. Defaults to `true` —\n * matches the design system's \"small border around link / message\n * attachments\" treatment from the mobile spec.\n */\n bordered?: boolean\n /**\n * Position of this bubble inside a same-author message run. Drives\n * the corner-flattening that visually merges consecutive bubbles\n * (matches the grouping stream-chat-react applies to text bubbles).\n * Defaults to `'single'` so standalone usage keeps every corner\n * fully rounded.\n */\n groupPosition?: BubbleGroupPosition\n className?: string\n children: React.ReactNode\n 'data-testid'?: string\n}\n\n// Colors and metrics tracked from `stream-chat-react`'s default\n// `.str-chat__message-bubble` token set so attachments visually drop\n// into a normal `CustomMessage` thread without any seams:\n// - light → `--str-chat__secondary-surface-color` = grey200 (#e9eaed)\n// - dark → `.str-chat__message--me` override in `styles.css` (#121110)\n// - text padding → `--str-chat__spacing-2 --str-chat__spacing-4` = 8px 16px\n// - border-radius → `--str-chat__border-radius-md` = 18px\n//\n// TODO: migrate to Tailwind theme tokens once they exist for the\n// `linktree.dark` / `linktree.surface.secondary` palette — these hex\n// literals are repeated across LinkAttachment / LockedAttachment /\n// MediaMessage / CustomMessageInput / DownloadAction etc., so a\n// rebrand would need to touch every site. A central token would\n// collapse the migration to one definition.\nconst BUBBLE_BG_BY_VARIANT: Record<BubbleVariant, string> = {\n dark: 'bg-[#121110]',\n light: 'bg-[#e9eaed]',\n}\n\nconst BUBBLE_TEXT_BY_VARIANT: Record<BubbleVariant, string> = {\n dark: 'text-white',\n light: 'text-[#080707]',\n}\n\nconst BUBBLE_BORDER_BY_VARIANT: Record<BubbleVariant, string> = {\n dark: 'border-white/[0.08]',\n light: 'border-black/[0.08]',\n}\n\n/**\n * Per-corner rounding tables that mirror stream-chat-react's default\n * bubble grouping. Sender bubbles cluster against the right edge so\n * the right corners get flattened in a run; receiver bubbles cluster\n * against the left edge so the left corners get flattened.\n *\n * Rounded corners use `--str-chat__border-radius-md` (18px) and\n * flattened corners use `--str-chat__border-radius-sm` (4px) — same\n * tokens stream's stylesheet uses for `.str-chat__message-bubble`\n * inside a `--first` / `--group` / `--end` wrapper.\n */\ntype BubbleSide = 'sender' | 'receiver'\n\nconst sideForVariant = (variant: BubbleVariant): BubbleSide =>\n variant === 'dark' ? 'sender' : 'receiver'\n\nconst CORNER_CLASSES_BY_SIDE_AND_POSITION: Record<\n BubbleSide,\n Record<BubbleGroupPosition, string>\n> = {\n sender: {\n single:\n 'rounded-tl-[18px] rounded-tr-[18px] rounded-bl-[18px] rounded-br-[18px]',\n first:\n 'rounded-tl-[18px] rounded-tr-[18px] rounded-bl-[18px] rounded-br-[4px]',\n middle:\n 'rounded-tl-[18px] rounded-tr-[4px] rounded-bl-[18px] rounded-br-[4px]',\n end: 'rounded-tl-[18px] rounded-tr-[4px] rounded-bl-[18px] rounded-br-[18px]',\n },\n receiver: {\n single:\n 'rounded-tl-[18px] rounded-tr-[18px] rounded-bl-[18px] rounded-br-[18px]',\n first:\n 'rounded-tl-[18px] rounded-tr-[18px] rounded-bl-[4px] rounded-br-[18px]',\n middle:\n 'rounded-tl-[4px] rounded-tr-[18px] rounded-bl-[4px] rounded-br-[18px]',\n end: 'rounded-tl-[4px] rounded-tr-[18px] rounded-bl-[18px] rounded-br-[18px]',\n },\n}\n\n/**\n * Chat-bubble container shared by every `MessageAttachment.*` card.\n *\n * Holds the attachment slot (image / video / row) and an optional\n * `text` caption rendered below it — mirrors the mobile chat layout\n * where a sender can attach a caption alongside an attachment ('Here\n * is the file', 'Here is the image').\n *\n * Two visual variants:\n * - `dark` — sender-side (Composer / Sent), white text on `#121110`.\n * - `light` — recipient-side (Received), dark text on `#e9eaed`.\n *\n * Background colors, padding, border-radius, and inherited font come\n * from the stream-chat-react `.str-chat__message-bubble` token set so\n * attachments line up with the `CustomMessage` text bubbles in a real\n * conversation. Padding is uniform across every variant — text rows,\n * audio rows, document rows, and media bubbles all get the same\n * 8px / 16px inset, with media inside getting its own rounded corners.\n */\nconst Bubble: React.FC<BubbleProps> = ({\n variant,\n text,\n bordered = true,\n groupPosition = 'single',\n className,\n children,\n 'data-testid': dataTestId,\n}) => {\n const hasText = text != null && text !== ''\n const cornerClasses =\n CORNER_CLASSES_BY_SIDE_AND_POSITION[sideForVariant(variant)][groupPosition]\n\n return (\n <div\n data-testid={dataTestId}\n data-group-position={groupPosition}\n className={classNames(\n // 280px-wide bubble — matches the mobile chat attachment width\n // and keeps the document / image / audio bubbles visually\n // consistent inside the conversation timeline. The 8px / 16px\n // inset matches `--str-chat__spacing-2 --str-chat__spacing-4`\n // so attachments share the same hit / negative-space rhythm\n // as the surrounding `CustomMessage` text bubbles.\n 'relative w-[280px] overflow-hidden px-2 py-2',\n cornerClasses,\n BUBBLE_BG_BY_VARIANT[variant],\n BUBBLE_TEXT_BY_VARIANT[variant],\n bordered && 'border',\n bordered && BUBBLE_BORDER_BY_VARIANT[variant],\n className\n )}\n >\n {children}\n\n {hasText ? (\n <p\n className={classNames(\n // No `text-*` / `font-*` overrides here — caption inherits\n // the same font family + size as `.str-chat__message-text`\n // so it matches the surrounding `CustomMessage` bubbles.\n 'whitespace-pre-wrap break-words leading-snug',\n // Top gutter only — bubble's `py-2` already supplies the\n // bottom inset, and the children above already render\n // flush against their own bottom edge.\n 'pt-2',\n 'px-2'\n )}\n >\n {text}\n </p>\n ) : null}\n </div>\n )\n}\n\nexport default Bubble\n","import { XIcon } from '@phosphor-icons/react'\nimport classNames from 'classnames'\nimport React from 'react'\n\nexport interface DismissButtonProps {\n onClick: () => void\n /**\n * `'overlay'` — translucent button sitting on top of media (image /\n * video corner). `'inline'` — opaque button placed in the row of a\n * compact attachment alongside other content.\n */\n variant?: 'overlay' | 'inline'\n ariaLabel?: string\n}\n\nconst DismissButton: React.FC<DismissButtonProps> = ({\n onClick,\n variant = 'overlay',\n ariaLabel = 'Dismiss attachment',\n}) => (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onClick()\n }}\n aria-label={ariaLabel}\n className={classNames(\n 'flex size-6 items-center justify-center rounded-full text-white',\n variant === 'overlay'\n ? 'bg-[#121110]/85 backdrop-blur'\n : 'bg-white/15 hover:bg-white/25'\n )}\n >\n <XIcon className=\"size-3\" weight=\"bold\" />\n </button>\n)\n\nexport default DismissButton\n","import type React from 'react'\n\n/**\n * Three messaging states a `MessageAttachment.*` card can render in,\n * mirroring the LinkAttachment / LockedAttachment families.\n */\nexport type MessageAttachmentState = 'composer' | 'sent' | 'received'\n\n/**\n * Visual variant of the surrounding `Bubble` chrome. Sender-side bubbles\n * (`composer`, `sent`) get the dark treatment; recipient-side bubbles\n * (`received`) get the light treatment.\n */\nexport type BubbleVariant = 'dark' | 'light'\n\n/** Maps a state to the bubble chrome it should render with. */\nexport const bubbleVariantForState = (\n state: MessageAttachmentState\n): BubbleVariant => (state === 'received' ? 'light' : 'dark')\n\n/**\n * Position of a bubble inside a same-author run, mirroring the grouping\n * stream-chat-react applies to consecutive messages from the same user:\n *\n * - `'single'` — standalone message, every corner fully rounded.\n * - `'first'` — first in a 2+ message run; the corner facing the\n * next bubble in the run is flattened.\n * - `'middle'` — both the corner facing the previous bubble and the\n * corner facing the next bubble are flattened.\n * - `'end'` — last in a 2+ message run; the corner facing the\n * previous bubble is flattened (this is also the\n * bubble the avatar attaches to on the receiver side).\n *\n * \"Facing\" is determined by the bubble's side: sender-aligned bubbles\n * cluster against their right edge, receiver-aligned bubbles against\n * their left edge.\n */\nexport type BubbleGroupPosition = 'single' | 'first' | 'middle' | 'end'\n\n/**\n * Convenience adapter that derives the `BubbleGroupPosition` from the\n * three booleans `stream-chat-react` forwards into the `Message` HOC.\n * Drop-in for `useMessageContext()` consumers:\n *\n * ```tsx\n * const { firstOfGroup, endOfGroup, groupedByUser } = useMessageContext()\n * <MessageAttachment.Image.Sent\n * {...props}\n * groupPosition={bubbleGroupPositionFromStream({\n * firstOfGroup, endOfGroup, groupedByUser,\n * })}\n * />\n * ```\n */\nexport const bubbleGroupPositionFromStream = ({\n firstOfGroup,\n endOfGroup,\n groupedByUser,\n}: {\n firstOfGroup?: boolean\n endOfGroup?: boolean\n groupedByUser?: boolean\n}): BubbleGroupPosition => {\n if (!groupedByUser) return 'single'\n if (firstOfGroup && endOfGroup) return 'single'\n if (firstOfGroup) return 'first'\n if (endOfGroup) return 'end'\n return 'middle'\n}\n\n/** Loading hint forwarded to the underlying `<img>` element. */\nexport type ImageLoadingMode = 'lazy' | 'eager'\n\n/** Preload hint forwarded to the underlying `<video>` / `<audio>` element. */\nexport type MediaPreloadMode = 'none' | 'metadata' | 'auto'\n\n/** A single image inside an Image attachment (single or stacked). */\nexport interface ImageItem {\n src: string\n alt?: string\n /** Optional width/height hint — preserved aspect on cover-fit. */\n width?: number\n height?: number\n /**\n * Per-tile native lazy-load override. Defaults to the parent\n * attachment's `loading` value (which itself defaults to `'lazy'`).\n * Pass `'eager'` for above-the-fold hero tiles.\n */\n loading?: ImageLoadingMode\n}\n\n/** A single video inside a Video attachment (single or stacked). */\nexport interface VideoItem {\n src: string\n /** Poster / thumbnail rendered before playback starts. */\n poster?: string\n /** Optional MIME type (e.g. `video/mp4`). */\n mimeType?: string\n /**\n * Per-item override for the `<video preload>` hint. Defaults to the\n * parent attachment's `preload` value (which itself defaults to\n * `'none'` so the poster carries the visual weight and no video\n * bytes are fetched until playback).\n */\n preload?: MediaPreloadMode\n}\n\n/** A single PDF inside a Pdf attachment (single or stacked). */\nexport interface PdfItem {\n src: string\n /** Filename — drives the row title + the meta line + the viewer dialog's accessible name. */\n filename?: string\n /** File size in bytes — formatted into the `EXT · SIZE` meta line. */\n fileSize?: number\n /** Override displayed title. Defaults to `filename`. */\n title?: string\n}\n\n/** A single audio file inside an Audio attachment (single or stacked). */\nexport interface AudioItem {\n src: string\n /** MIME type hint — typed onto the inline `<source>` element. */\n mimeType?: string\n /**\n * Filename — used as the download default name (consumed by the\n * native player's kebab menu).\n */\n filename?: string\n /**\n * Per-track override for the `<audio preload>` hint. Defaults to\n * the parent attachment's `preload` value, which itself defaults\n * to `'metadata'` for a single audio (so the native player can\n * surface duration immediately) and `'none'` for a stacked thread\n * (so a fan-out of metadata requests doesn't happen on first paint).\n */\n preload?: MediaPreloadMode\n}\n\n/** A single generic file inside a File attachment (single or stacked). */\nexport interface FileItem {\n src: string\n /** Filename — drives the title + the meta line + the download default name. */\n filename?: string\n /** File size in bytes — formatted into the `EXT · SIZE` meta line. */\n fileSize?: number\n /** MIME type — drives the type icon. Defaults to `application/octet-stream`. */\n mimeType?: string\n /** Override displayed title. Defaults to `filename`. */\n title?: string\n}\n\n/** Shared props every `MessageAttachment.*.{Composer|Sent|Received}` accepts. */\nexport interface MessageAttachmentBaseProps {\n /**\n * Optional text rendered below the attachment inside the same bubble.\n * Mirrors the chat behavior in the mobile screenshots — `'Here is the\n * file'`, `'Here is the image'`, etc.\n */\n text?: React.ReactNode\n /**\n * Position of this attachment inside a same-author message run —\n * mirrors the corner-flattening stream-chat-react applies to text\n * bubbles in a clustered conversation. Defaults to `'single'`, so\n * standalone usage keeps every corner rounded. Pass the value\n * derived from `bubbleGroupPositionFromStream({ firstOfGroup,\n * endOfGroup, groupedByUser })` when rendering inside a real\n * `CustomMessage` thread to make the attachment visually merge\n * with adjacent bubbles. Image / Video Composer ignore this — the\n * draft preview renders bare without the surrounding bubble.\n */\n groupPosition?: BubbleGroupPosition\n}\n\n/** Shared props for the `Composer` state of every attachment type. */\nexport interface ComposerExtras {\n /** Renders a dismiss `×` button overlaid on the attachment. */\n onDismiss?: () => void\n}\n","import React from 'react'\n\nimport Bubble from '../_shared/Bubble'\nimport DismissButton from '../_shared/DismissButton'\nimport {\n bubbleVariantForState,\n type AudioItem,\n type ComposerExtras,\n type MediaPreloadMode,\n type MessageAttachmentBaseProps,\n type MessageAttachmentState,\n} from '../types'\n\nexport interface AudioAttachmentSharedProps extends MessageAttachmentBaseProps {\n /** Audio source URL (`mp3`, `aac`, `wav`, …). */\n src?: string\n /** MIME type hint — typed onto the inline `<source>` element. */\n mimeType?: string\n /**\n * Filename — used as the download default name (consumed by the\n * native player's kebab menu). The HTML `<audio>` element doesn't\n * expose a built-in title slot, so we don't render the filename\n * inside the bubble itself.\n */\n filename?: string\n /**\n * Stacked audio. Takes precedence over `src` when set. Each item\n * renders its own native `<audio controls>` player, vertically\n * stacked inside the same bubble with an 8px gap between players.\n * Sent + Received only — the composer surface accepts a single\n * attachment at a time.\n */\n items?: AudioItem[]\n /**\n * `<audio preload>` hint applied to every player on the bubble.\n * When omitted, the default depends on the rendered shape:\n *\n * - Single audio (no `items`, or `items.length === 1`) →\n * `'metadata'` so the native player surfaces duration\n * immediately.\n * - Stacked audio (`items.length > 1`) → `'none'` so a thread\n * of voice memos doesn't fan out N parallel metadata requests\n * on first paint.\n *\n * Per-track overrides live on `AudioItem.preload`.\n */\n preload?: MediaPreloadMode\n}\n\nconst resolveItems = ({\n src,\n mimeType,\n filename,\n items,\n}: {\n src?: string\n mimeType?: string\n filename?: string\n items?: AudioItem[]\n}): AudioItem[] => {\n if (items && items.length > 0) return items\n if (src) return [{ src, mimeType, filename }]\n return []\n}\n\nconst NativeAudioPlayer: React.FC<{\n item: AudioItem\n preload: MediaPreloadMode\n trailingAction?: React.ReactNode\n}> = ({ item, preload, trailingAction }) => (\n <div className=\"flex items-center gap-2\">\n {/* No `<track>` is rendered — we don't author caption sidecars\n for chat attachments, and an empty `<track kind=\"captions\" />`\n emits a runtime warning. Re-add a real track (with `src`,\n `srcLang`, and `default`) once caption support actually ships.\n The `jsx-a11y/media-has-caption` rule is suppressed for the\n same reason. */}\n {/* eslint-disable-next-line jsx-a11y/media-has-caption */}\n <audio\n src={item.src}\n controls\n preload={item.preload ?? preload}\n className=\"block min-w-0 flex-1\"\n >\n {item.mimeType ? <source src={item.src} type={item.mimeType} /> : null}\n </audio>\n {trailingAction ?? null}\n </div>\n)\n\ninterface InternalAudioRowProps extends AudioAttachmentSharedProps {\n state: MessageAttachmentState\n onDismiss?: () => void\n}\n\nconst AudioAttachmentRow: React.FC<InternalAudioRowProps> = ({\n state,\n src,\n mimeType,\n filename,\n items,\n text,\n groupPosition,\n preload,\n onDismiss,\n}) => {\n const variant = bubbleVariantForState(state)\n const showDismiss = state === 'composer' && !!onDismiss\n const resolvedItems = resolveItems({ src, mimeType, filename, items })\n\n if (resolvedItems.length === 0) {\n return null\n }\n\n // Default rule: surface duration immediately for a single player,\n // but avoid fanning out N metadata requests when the bubble is a\n // thread of voice memos. An explicit `preload` prop overrides;\n // per-track overrides live on `AudioItem.preload`.\n const resolvedPreload: MediaPreloadMode =\n preload ?? (resolvedItems.length > 1 ? 'none' : 'metadata')\n\n return (\n <Bubble\n variant={variant}\n text={text}\n groupPosition={groupPosition}\n data-testid=\"audio-attachment\"\n >\n {/* Native `<audio controls>` already exposes a download in its\n kebab menu, so we don't render a separate Download button.\n The element also has no built-in title slot — the dismiss\n button (Composer only) sits inline next to the player.\n Stacked players get an 8px vertical gap so each track reads\n as a discrete attachment. */}\n <div className=\"flex flex-col gap-2\">\n {resolvedItems.map((item, index) => (\n <NativeAudioPlayer\n key={`${item.src}-${index}`}\n item={item}\n preload={resolvedPreload}\n trailingAction={\n // Composer only supports a single attachment, so the\n // dismiss control sits on the only player.\n showDismiss && index === 0 ? (\n <DismissButton onClick={onDismiss!} variant=\"inline\" />\n ) : undefined\n }\n />\n ))}\n </div>\n </Bubble>\n )\n}\n\n/**\n * Composer-only props. Single audio (`src`) only — the composer\n * surface accepts a single attachment at a time, so `items` is not\n * supported. Captions (`text`) and `groupPosition` are also dropped:\n * the composer renders a standalone draft, not part of a same-author\n * run, and captions live in the message-input textarea, not inside\n * the draft attachment preview.\n */\nexport interface AudioComposerProps extends ComposerExtras {\n src?: string\n mimeType?: string\n filename?: string\n /** See `AudioAttachmentSharedProps.preload`. */\n preload?: MediaPreloadMode\n}\nexport type AudioSentProps = AudioAttachmentSharedProps\nexport type AudioReceivedProps = AudioAttachmentSharedProps\n\nconst AudioComposer: React.FC<AudioComposerProps> = (props) => (\n <AudioAttachmentRow {...props} state=\"composer\" />\n)\nconst AudioSent: React.FC<AudioSentProps> = (props) => (\n <AudioAttachmentRow {...props} state=\"sent\" />\n)\nconst AudioReceived: React.FC<AudioReceivedProps> = (props) => (\n <AudioAttachmentRow {...props} state=\"received\" />\n)\n\nconst AudioAttachment = {\n Composer: AudioComposer,\n Sent: AudioSent,\n Received: AudioReceived,\n}\n\nexport default AudioAttachment\n","import {\n getDocumentIconType,\n getSourceType,\n} from '../../AttachmentCard/utils/mimeType'\n\n/**\n * Format a byte count as a short human-readable string (`'379.5 KB'`).\n * Mirrors the meta line shown next to file attachments in the mobile chat\n * design — `EXT · SIZE`, e.g. `PDF · 379.5 KB`.\n */\nexport function formatFileSize(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes < 0) return ''\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n if (bytes < 1024 * 1024 * 1024)\n return `${(bytes / (1024 * 1024)).toFixed(2)} MB`\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`\n}\n\nconst EXTENSION_LABEL_BY_DOCUMENT_TYPE: Record<string, string> = {\n pdf: 'PDF',\n doc: 'DOC',\n xls: 'XLS',\n csv: 'CSV',\n ppt: 'PPT',\n zip: 'ZIP',\n text: 'TXT',\n markdown: 'MD',\n}\n\n/**\n * Returns the short uppercase extension label shown in the meta line of\n * compact document / file attachments. Prefers the filename extension\n * when present (matches the mobile design which trusts the filename),\n * and falls back to a label derived from the MIME type.\n */\nexport function getFileExtensionLabel(\n mimeType?: string,\n filename?: string\n): string | undefined {\n if (filename) {\n const lastDot = filename.lastIndexOf('.')\n if (lastDot > 0 && lastDot < filename.length - 1) {\n const ext = filename.slice(lastDot + 1)\n if (ext && ext.length <= 5) return ext.toUpperCase()\n }\n }\n\n if (!mimeType) return undefined\n\n const sourceType = getSourceType(mimeType)\n if (sourceType === 'document') {\n const docType = getDocumentIconType(mimeType)\n const docLabel = EXTENSION_LABEL_BY_DOCUMENT_TYPE[docType]\n if (docLabel) return docLabel\n if (mimeType === 'application/octet-stream') return undefined\n }\n\n const subtype = mimeType.split('/')[1]\n if (!subtype || subtype === '*') return undefined\n return subtype.toUpperCase()\n}\n\n/**\n * Build the meta line shown under the title in compact document / file\n * attachments — `EXT · SIZE` (`PDF · 379.5 KB`). Either part is dropped\n * when not available so audio / generic files still get a useful label.\n */\nexport function buildCompactMetaLabel(\n mimeType?: string,\n filename?: string,\n fileSize?: number\n): string | undefined {\n const ext = getFileExtensionLabel(mimeType, filename)\n const size =\n typeof fileSize === 'number' && fileSize > 0\n ? formatFileSize(fileSize)\n : undefined\n return [ext, size].filter(Boolean).join(' · ') || undefined\n}\n\n/**\n * Derive a sensible filename from a URL — used as the fallback save name\n * when the attachment has no `filename` set. Returns `'download'` for\n * URLs we can't parse.\n */\nexport function filenameFromUrl(url: string): string {\n try {\n const parsed = new URL(url)\n const last = parsed.pathname.split('/').pop()\n return last && last.length > 0 ? decodeURIComponent(last) : 'download'\n } catch {\n return 'download'\n }\n}\n","import classNames from 'classnames'\nimport React from 'react'\n\nimport { renderTypeIcon } from '../../AttachmentCard'\nimport type { BubbleVariant } from '../types'\n\nimport { buildCompactMetaLabel } from './fileMeta'\n\nexport interface CompactDocumentRowProps {\n variant: BubbleVariant\n /** Filename — drives the title (when no `title` set) and the `EXT` label. */\n filename?: string\n /** Override displayed title. Defaults to `filename`. */\n title?: string\n mimeType?: string\n fileSize?: number\n /**\n * When set, the icon + filename area becomes a `<button>` that fires\n * this handler. Lets the row's primary activation (e.g. opening the\n * PDF viewer) live separately from the trailing slot's own button\n * (e.g. a download icon) without nesting `<button>` inside `<button>`.\n * When omitted, the row renders as a plain presentational `<div>`\n * and the caller is responsible for any outer activation wrapper.\n */\n onActivate?: () => void\n /**\n * Required when `onActivate` is set. Visible to assistive tech as\n * the inner button's label (e.g. `'Open ESOP-summary.pdf'`).\n */\n activateLabel?: string\n /**\n * Optional trailing slot — sits outside the activation button so it\n * can host its own interactive controls (e.g. a download `<button>`,\n * the Composer's `DismissButton`) without nesting one button inside\n * another.\n */\n trailingAction?: React.ReactNode\n}\n\n// Meta line + icon tile are tinted relative to the bubble background,\n// so they stay variant-aware. The title inherits the bubble's text\n// color (set on the parent `Bubble`).\n//\n// `text-black/55` on the `#e9eaed` light bubble lands right at the\n// WCAG AA 4.5:1 small-text threshold (≈4.5:1) — bump to `/65` so\n// the meta line has a comfortable margin on the light bubble. The\n// dark variant (`text-white/55` on `#121110`) already passes AA at\n// ~6:1, so we leave it alone.\nconst META_CLASS_BY_VARIANT: Record<BubbleVariant, string> = {\n dark: 'text-white/55',\n light: 'text-black/65',\n}\n\nconst ICON_BG_BY_VARIANT: Record<BubbleVariant, string> = {\n dark: 'bg-white/10',\n light: 'bg-black/5',\n}\n\nconst ICON_COLOR_BY_VARIANT: Record<BubbleVariant, string> = {\n dark: 'text-white/85',\n light: 'text-black/85',\n}\n\n/**\n * Compact icon-row used by `Pdf` / `File` `MessageAttachment` variants.\n * Mirrors the mobile chat document attachment from\n * `EARN-8448/pr-2-chat-bubbles-and-file-icons` — a 40x40 typed icon\n * tile on the left, filename + `EXT · SIZE` meta on the right.\n *\n * The row is always wrapped by `Bubble`, which contributes the bubble\n * background, padding, border, and (when the parent wires it up) the\n * click target.\n */\nconst CompactDocumentRow: React.FC<CompactDocumentRowProps> = ({\n variant,\n filename,\n title,\n mimeType = 'application/octet-stream',\n fileSize,\n onActivate,\n activateLabel,\n trailingAction,\n}) => {\n const resolvedTitle = title ?? filename ?? 'File'\n const metaLabel = buildCompactMetaLabel(mimeType, filename, fileSize)\n\n const iconTile = (\n <div\n className={classNames(\n 'flex size-10 shrink-0 items-center justify-center rounded-sm',\n ICON_BG_BY_VARIANT[variant]\n )}\n aria-hidden\n >\n {renderTypeIcon(mimeType, {\n className: classNames('size-6', ICON_COLOR_BY_VARIANT[variant]),\n weight: 'regular',\n })}\n </div>\n )\n\n const textBlock = (\n <div className=\"flex min-w-0 flex-1 flex-col text-left\">\n <p className=\"truncate font-medium leading-snug\">{resolvedTitle}</p>\n {metaLabel ? (\n <p\n className={classNames(\n 'truncate text-xs leading-4',\n META_CLASS_BY_VARIANT[variant]\n )}\n >\n {metaLabel}\n </p>\n ) : null}\n </div>\n )\n\n // When the row is activatable, its body (icon + text) becomes the\n // click target — wrapping it in a `<button>` rather than wrapping\n // the entire row preserves the trailing slot for its own buttons\n // (download / dismiss) instead of nesting them inside one.\n const body = onActivate ? (\n <button\n type=\"button\"\n onClick={onActivate}\n aria-label={activateLabel}\n className={classNames(\n 'flex min-w-0 flex-1 items-center gap-3 rounded-sm text-left transition-colors',\n variant === 'dark'\n ? 'hover:bg-white/[0.04]'\n : 'hover:bg-black/[0.04]'\n )}\n >\n {iconTile}\n {textBlock}\n </button>\n ) : (\n <>\n {iconTile}\n {textBlock}\n </>\n )\n\n return (\n <div className=\"flex items-center gap-3 px-3 py-2\">\n {body}\n {trailingAction ? <div className=\"shrink-0\">{trailingAction}</div> : null}\n </div>\n )\n}\n\nexport default CompactDocumentRow\n","import { filenameFromUrl } from './fileMeta'\n\n/**\n * Download a remote asset with the desired filename.\n *\n * Tries the same-origin `fetch` → `Blob` → invisible-`<a>.download`\n * route first so the file lands on disk under `filename` instead of\n * navigating the tab to it. When CORS / network errors trip us up we\n * fall back to `window.open` and then to a no-window anchor — the\n * popup-blocker fallback ensures the download still fires even when\n * a browser blocks the `_blank` window.\n *\n * Shared by `DownloadAction` (rendered as an explicit Download button\n * in PDF / image / video viewers) and `MessageAttachment.File` rows\n * (where the entire row is the download trigger). Centralizing here\n * keeps one source of truth for the fallback chain.\n */\nexport async function triggerDownload(\n url: string,\n filename?: string\n): Promise<void> {\n const name = filename ?? filenameFromUrl(url)\n\n try {\n const res = await fetch(url, { mode: 'cors' })\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n const blob = await res.blob()\n const objectUrl = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = objectUrl\n a.download = name\n a.style.display = 'none'\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n URL.revokeObjectURL(objectUrl)\n } catch {\n const fallback = window.open(url, '_blank', 'noopener,noreferrer')\n if (!fallback) {\n // popup blocked — fall back to a non-windowed anchor\n const a = document.createElement('a')\n a.href = url\n a.download = name\n a.target = '_blank'\n a.rel = 'noopener noreferrer'\n a.style.display = 'none'\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n }\n }\n}\n\nexport default triggerDownload\n","import { DownloadSimpleIcon } from '@phosphor-icons/react'\nimport classNames from 'classnames'\nimport React from 'react'\n\nimport Bubble from '../_shared/Bubble'\nimport CompactDocumentRow from '../_shared/CompactDocumentRow'\nimport DismissButton from '../_shared/DismissButton'\nimport { filenameFromUrl } from '../_shared/fileMeta'\nimport { triggerDownload } from '../_shared/triggerDownload'\nimport {\n bubbleVariantForState,\n type BubbleVariant,\n type ComposerExtras,\n type FileItem,\n type MessageAttachmentBaseProps,\n type MessageAttachmentState,\n} from '../types'\n\nexport interface FileAttachmentSharedProps extends MessageAttachmentBaseProps {\n /** Source URL of the file (used as the download target). */\n src?: string\n /** Filename — drives the title + the meta line + the download default name. */\n filename?: string\n fileSize?: number\n /** MIME type — drives the type icon. Defaults to `application/octet-stream`. */\n mimeType?: string\n /**\n * Override displayed title (defaults to `filename`). Useful when a\n * sender renames the attachment before sending.\n */\n title?: string\n /**\n * Stacked files. Takes precedence over `src` when set. Each item\n * renders as its own row inside the bubble; clicking a row downloads\n * that file. Sent + Received only — the composer surface accepts a\n * single attachment at a time.\n */\n items?: FileItem[]\n /**\n * Forwarded to the row trigger. When omitted the click still\n * downloads the file. Supply this for analytics, or return `false`\n * to fully intercept the download (e.g. show a confirmation modal,\n * route to a custom preview, throttle large files). Any other\n * return value — including `void`/`undefined` — lets the built-in\n * download proceed. For stacked attachments the `index` argument\n * identifies the row.\n */\n onClick?: (index: number) => boolean | void\n}\n\nconst resolveItems = ({\n src,\n filename,\n fileSize,\n mimeType,\n title,\n items,\n}: {\n src?: string\n filename?: string\n fileSize?: number\n mimeType?: string\n title?: string\n items?: FileItem[]\n}): FileItem[] => {\n if (items && items.length > 0) return items\n if (src) return [{ src, filename, fileSize, mimeType, title }]\n return []\n}\n\ninterface FileRowButtonProps {\n variant: BubbleVariant\n item: FileItem\n index: number\n onActivate: (index: number) => void\n trailingAction: React.ReactNode\n}\n\n/**\n * Single tappable file row — used both for the lone file case and\n * for each child of a stacked attachment. The icon + filename area\n * is the click target (downloads the asset); the trailing slot is\n * decorative on Sent / Received and hosts the dismiss control on\n * Composer.\n */\nconst FileRowButton: React.FC<FileRowButtonProps> = ({\n variant,\n item,\n index,\n onActivate,\n trailingAction,\n}) => {\n const resolvedFilename = item.filename ?? filenameFromUrl(item.src)\n return (\n <CompactDocumentRow\n variant={variant}\n filename={resolvedFilename}\n title={item.title}\n mimeType={item.mimeType ?? 'application/octet-stream'}\n fileSize={item.fileSize}\n onActivate={() => onActivate(index)}\n activateLabel={`Download ${resolvedFilename}`}\n trailingAction={trailingAction}\n />\n )\n}\n\ninterface InternalFileRowProps extends FileAttachmentSharedProps {\n state: MessageAttachmentState\n onDismiss?: () => void\n}\n\nconst FileAttachmentRow: React.FC<InternalFileRowProps> = ({\n state,\n src,\n filename,\n fileSize,\n mimeType,\n title,\n items,\n text,\n groupPosition,\n onClick,\n onDismiss,\n}) => {\n const variant = bubbleVariantForState(state)\n const showDismiss = state === 'composer' && !!onDismiss\n const resolvedItems = resolveItems({\n src,\n filename,\n fileSize,\n mimeType,\n title,\n items,\n })\n\n // `useCallback` would be pointless here — `resolvedItems` is rebuilt\n // every render, so memoizing on it produces a fresh function every\n // render anyway. Keep this as a plain function.\n const handleActivate = (index: number) => {\n // `onClick` returning `false` cancels the built-in download so\n // consumers can route the click to a confirmation modal, custom\n // preview, etc. Any other return value (including void) lets the\n // default download proceed.\n if (onClick?.(index) === false) return\n const item = resolvedItems[index]\n if (!item) return\n const resolvedFilename = item.filename ?? filenameFromUrl(item.src)\n void triggerDownload(item.src, resolvedFilename)\n }\n\n if (resolvedItems.length === 0) {\n return null\n }\n\n const downloadIcon = (\n <span\n className={classNames(\n 'flex size-8 items-center justify-center rounded-full',\n variant === 'dark' ? 'text-white/70' : 'text-black/70'\n )}\n aria-hidden\n >\n <DownloadSimpleIcon className=\"size-5\" weight=\"bold\" />\n </span>\n )\n\n return (\n <Bubble\n variant={variant}\n text={text}\n groupPosition={groupPosition}\n data-testid=\"file-attachment\"\n >\n {/* Stacked rows get a small vertical gap so each file reads as\n a discrete attachment rather than one merged blob. Single\n row falls through to a no-op `gap-0`. */}\n <div className=\"flex flex-col gap-2\">\n {resolvedItems.map((item, index) => {\n // Composer only supports a single attachment so the dismiss\n // control sits on the only row; otherwise every row gets\n // the trailing download icon hint.\n const trailingAction =\n showDismiss && index === 0 ? (\n <DismissButton onClick={onDismiss!} variant=\"inline\" />\n ) : (\n downloadIcon\n )\n return (\n <FileRowButton\n key={`${item.src}-${index}`}\n variant={variant}\n item={item}\n index={index}\n onActivate={handleActivate}\n trailingAction={trailingAction}\n />\n )\n })}\n </div>\n </Bubble>\n )\n}\n\n/**\n * Composer-only props. Single file (`src`) only — the composer surface\n * accepts a single attachment at a time, so `items` is not supported.\n * Captions (`text`) and `groupPosition` are also dropped: the composer\n * renders a standalone draft, not part of a same-author run, and\n * captions live in the message-input textarea, not inside the draft\n * attachment preview.\n */\nexport interface FileComposerProps extends ComposerExtras {\n src?: string\n filename?: string\n fileSize?: number\n mimeType?: string\n title?: string\n onClick?: (index: number) => boolean | void\n}\nexport type FileSentProps = FileAttachmentSharedProps\nexport type FileReceivedProps = FileAttachmentSharedProps\n\nconst FileComposer: React.FC<FileComposerProps> = (props) => (\n <FileAttachmentRow {...props} state=\"composer\" />\n)\nconst FileSent: React.FC<FileSentProps> = (props) => (\n <FileAttachmentRow {...props} state=\"sent\" />\n)\nconst FileReceived: React.FC<FileReceivedProps> = (props) => (\n <FileAttachmentRow {...props} state=\"received\" />\n)\n\nconst FileAttachment = {\n Composer: FileComposer,\n Sent: FileSent,\n Received: FileReceived,\n}\n\nexport default FileAttachment\n","import { CaretLeftIcon, CaretRightIcon } from '@phosphor-icons/react'\nimport React from 'react'\n\nexport interface CarouselNavProps {\n onPrev: () => void\n onNext: () => void\n /** Accessible label for the previous-item button. */\n prevLabel?: string\n /** Accessible label for the next-item button. */\n nextLabel?: string\n}\n\n/**\n * Prev / next chrome shared by every carousel-aware viewer. Rendered\n * inside the dialog body so the buttons sit absolutely positioned\n * against the viewer (`.mes-media-viewer__nav--{prev,next}`).\n *\n * Styling lives as plain CSS in `styles.css` so the buttons render\n * correctly regardless of whether the consumer has Tailwind wired up.\n */\nconst CarouselNav: React.FC<CarouselNavProps> = ({\n onPrev,\n onNext,\n prevLabel = 'Previous',\n nextLabel = 'Next',\n}) => (\n <>\n <button\n type=\"button\"\n onClick={onPrev}\n aria-label={prevLabel}\n className=\"mes-media-viewer__nav mes-media-viewer__nav--prev\"\n >\n <CaretLeftIcon size={20} weight=\"bold\" aria-hidden />\n </button>\n <button\n type=\"button\"\n onClick={onNext}\n aria-label={nextLabel}\n className=\"mes-media-viewer__nav mes-media-viewer__nav--next\"\n >\n <CaretRightIcon size={20} weight=\"bold\" aria-hidden />\n </button>\n </>\n)\n\nexport default CarouselNav\n","import {\n CircleNotchIcon,\n DownloadSimpleIcon,\n IconProps,\n} from '@phosphor-icons/react'\nimport classNames from 'classnames'\nimport React, { useCallback, useState } from 'react'\n\nimport { triggerDownload } from './triggerDownload'\n\nexport type DownloadActionVariant =\n /** Solid pill button used inside compact / file rows. */\n | 'pill'\n /**\n * Compact round icon button sized for the trailing slot of a\n * `CompactDocumentRow` (PDF / File rows). Adopts the row's tone so\n * it sits inline next to the filename without competing with it.\n */\n | 'inline'\n /**\n * Round icon button sized to match the close action in the\n * `ViewerShell` chrome — used by `ImageViewer` for in-viewer\n * downloads. Styled with plain CSS (`.mes-media-viewer__action`) so\n * it renders correctly without a Tailwind dependency in the\n * consumer's app.\n */\n | 'viewer'\n\nexport interface DownloadActionProps {\n url: string\n filename?: string\n variant?: DownloadActionVariant\n /**\n * Override the visible label on `pill` variants. Defaults to\n * `'Download'`. On `inline` / `viewer` variants the label is\n * hidden visually and surfaced as the button's `aria-label`.\n */\n label?: string\n /** Hide the label, keeping just the icon. Defaults to `true` for non-pill variants. */\n iconOnly?: boolean\n /** Tone of the surface. Used by the `pill` variant. */\n tone?: 'dark' | 'light'\n /**\n * Triggered after the download starts so consumers can fire analytics\n * or close a viewer. Errors during download don't suppress the call.\n */\n onTriggered?: () => void\n}\n\nconst DownloadAction: React.FC<DownloadActionProps> = ({\n url,\n filename,\n variant = 'pill',\n label = 'Download',\n iconOnly,\n tone = 'dark',\n onTriggered,\n}) => {\n const [busy, setBusy] = useState(false)\n\n const handleClick = useCallback(\n (e: React.MouseEvent) => {\n e.stopPropagation()\n if (busy) return\n setBusy(true)\n triggerDownload(url, filename)\n .catch(() => {\n /* swallowed — fallback path inside `triggerDownload` */\n })\n .finally(() => {\n setBusy(false)\n onTriggered?.()\n })\n },\n [busy, url, filename, onTriggered]\n )\n\n const showIconOnly = iconOnly ?? variant !== 'pill'\n\n const iconClass = classNames(\n variant === 'pill' ? 'size-4' : 'size-5',\n 'shrink-0'\n )\n const iconProps: IconProps = { className: iconClass, weight: 'bold' }\n\n if (variant === 'inline') {\n // Sized to match the existing trailing slot used by `DismissButton`\n // and the decorative download span in `FileAttachment`. Tone keys\n // off the surrounding `Bubble` variant so a sender (dark) bubble\n // gets a lighter icon and a receiver (light) bubble gets a darker\n // one — same approach `CompactDocumentRow` already uses for the\n // type icon.\n const inlineToneClasses: Record<'dark' | 'light', string> = {\n dark: 'text-white/70 hover:bg-white/[0.08] hover:text-white',\n light: 'text-black/70 hover:bg-black/[0.08] hover:text-black',\n }\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n disabled={busy}\n aria-label={label}\n className={classNames(\n 'flex size-8 shrink-0 items-center justify-center rounded-full transition-colors disabled:opacity-70',\n inlineToneClasses[tone]\n )}\n >\n {busy ? (\n <CircleNotchIcon\n className=\"size-4 animate-spin\"\n weight=\"bold\"\n aria-hidden\n />\n ) : (\n <DownloadSimpleIcon\n className=\"size-5 shrink-0\"\n weight=\"bold\"\n aria-hidden\n />\n )}\n </button>\n )\n }\n\n if (variant === 'viewer') {\n // Styled entirely through `.mes-media-viewer__action` (plain CSS\n // in `styles.css`) so the button renders correctly when consumed\n // outside this repo without a Tailwind setup. Sits next to the\n // close action inside `ViewerShell`'s top-right chrome.\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n disabled={busy}\n aria-label={label}\n className=\"mes-media-viewer__action\"\n >\n {busy ? (\n <CircleNotchIcon size={20} weight=\"bold\" aria-hidden />\n ) : (\n <DownloadSimpleIcon size={20} weight=\"bold\" aria-hidden />\n )}\n </button>\n )\n }\n\n // pill — only remaining variant\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n disabled={busy}\n aria-label={showIconOnly ? label : undefined}\n className={classNames(\n 'mt-3 inline-flex h-10 w-full items-center justify-center gap-2 rounded-full px-4 text-sm font-medium leading-none transition-colors disabled:opacity-70',\n tone === 'dark'\n ? 'bg-[#121110] text-white hover:bg-[#2a2928]'\n : 'bg-white text-[#121110] hover:bg-white/90'\n )}\n >\n {busy ? (\n <CircleNotchIcon\n className=\"size-4 animate-spin\"\n weight=\"bold\"\n aria-hidden\n />\n ) : (\n <DownloadSimpleIcon {...iconProps} aria-hidden />\n )}\n {showIconOnly ? null : label}\n </button>\n )\n}\n\nexport default DownloadAction\n","import { useCallback, useEffect, useState } from 'react'\n\nconst clamp = (value: number, min: number, max: number) =>\n Math.min(Math.max(value, min), max)\n\nexport interface UseCarouselOptions {\n /** Total number of items in the carousel. */\n length: number\n /** Index to display when the carousel first opens / re-opens. */\n initialIndex: number\n /** When `false`, the keyboard listener is detached. */\n open: boolean\n}\n\nexport interface UseCarouselResult {\n /** Currently displayed index (clamped to `[0, length - 1]`). */\n index: number\n /** Step one item back, wrapping at the start. */\n prev: () => void\n /** Step one item forward, wrapping at the end. */\n next: () => void\n}\n\n/**\n * Tiny carousel state used by `ImageViewer`, `VideoViewer`, and\n * `PdfViewer` when their items array has more than one entry.\n *\n * - Resets to `initialIndex` whenever the viewer (re-)opens, so\n * clicking a different tile on the bubble surface lands on that\n * item rather than restoring the last-viewed position.\n * - Clamps defensively when `length` shrinks below the active index\n * (server push / optimistic update / stack edit) so the viewer\n * gracefully falls back instead of dereferencing `undefined`.\n * - Listens for `ArrowLeft` / `ArrowRight` while the viewer is open,\n * but yields when the focused element is a `<video>` / `<audio>`\n * element so the native media controls can use the same keys for\n * seek / volume.\n */\nexport const useCarousel = ({\n length,\n initialIndex,\n open,\n}: UseCarouselOptions): UseCarouselResult => {\n const safeInitial = clamp(initialIndex, 0, Math.max(length - 1, 0))\n const [index, setIndex] = useState(safeInitial)\n\n // Snap the active index back to whatever the caller asked for every\n // time the viewer (re)opens or its initial target / length changes.\n // Without this the viewer would remember the last index across\n // open/close cycles, which is wrong for a chat lightbox where each\n // tile click should land on that tile.\n useEffect(() => {\n if (!open) return\n setIndex(clamp(initialIndex, 0, Math.max(length - 1, 0)))\n }, [open, initialIndex, length])\n\n // Re-clamp on length change so a stale index doesn't dereference\n // past the new array bounds (e.g. items shrank while the viewer is\n // open). The `Math.max(..., 0)` keeps the index at `0` when the\n // array empties out so callers can render an empty-state instead of\n // a runtime crash.\n useEffect(() => {\n setIndex((i) => clamp(i, 0, Math.max(length - 1, 0)))\n }, [length])\n\n const prev = useCallback(() => {\n if (length <= 1) return\n setIndex((i) => (i <= 0 ? length - 1 : i - 1))\n }, [length])\n\n const next = useCallback(() => {\n if (length <= 1) return\n setIndex((i) => (i >= length - 1 ? 0 : i + 1))\n }, [length])\n\n useEffect(() => {\n if (!open || length <= 1) return undefined\n\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return\n // Don't hijack arrow keys when a native media element is\n // focused — the browser's own controls already use them for\n // seek / volume, and stealing them would feel broken.\n const active = document.activeElement\n if (\n active &&\n (active.tagName === 'VIDEO' || active.tagName === 'AUDIO')\n ) {\n return\n }\n e.preventDefault()\n if (e.key === 'ArrowLeft') prev()\n else next()\n }\n\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n }, [open, length, prev, next])\n\n return { index, prev, next }\n}\n\nexport default useCarousel\n","import { XIcon } from '@phosphor-icons/react'\nimport React, { useEffect, useRef } from 'react'\n\nexport interface ViewerShellProps {\n open: boolean\n onClose: () => void\n /**\n * Accessible label for the dialog — usually the media's filename. We\n * intentionally don't render a visible title bar (the chrome is a\n * single close button + optional actions so the media stays the\n * focus), so this is the only way assistive tech gets a name for\n * the modal.\n */\n ariaLabel?: string\n /**\n * Optional counter rendered in the top-left chrome (e.g. `2 / 6`).\n * Carousel-aware viewers set this when their items array has more\n * than one entry; omit it for single-item viewers.\n */\n counter?: React.ReactNode\n /**\n * Extra buttons rendered alongside the close button in the top-right\n * chrome (e.g. a download action). They share `.mes-media-viewer__action`\n * styling so the row reads as one consistent button group.\n */\n actions?: React.ReactNode\n children: React.ReactNode\n 'data-testid'?: string\n}\n\n/**\n * Full-viewport lightbox shell shared by `ImageViewer`, `VideoViewer`,\n * and `PdfViewer`. Built on a native `<dialog>` so the package ships\n * without a Tailwind dependency for the load-bearing chrome:\n *\n * - Top-layer + body-scroll lock come for free from `showModal()`.\n * - `Escape` to close is wired by the platform.\n * - Focus management (move-in on open, restore on close) is handled\n * by the browser; we explicitly focus the close button as a\n * fallback for environments where `showModal` is unavailable\n * (jsdom under tests).\n *\n * The structural styling lives as plain CSS under `.mes-media-viewer*`\n * in `styles.css` so a consumer who imports\n * `@linktr.ee/messaging-react/styles.css` gets a working lightbox even\n * if their Tailwind pipeline isn't scanning our `dist`.\n */\nconst ViewerShell: React.FC<ViewerShellProps> = ({\n open,\n onClose,\n ariaLabel,\n counter,\n actions,\n children,\n 'data-testid': dataTestId,\n}) => {\n const dialogRef = useRef<HTMLDialogElement | null>(null)\n const closeButtonRef = useRef<HTMLButtonElement | null>(null)\n\n // Drive `<dialog>` open/close imperatively from the React `open` prop.\n // We guard `showModal` / `close` because jsdom 23 ships the element\n // without those methods — falling back to the `[open]` attribute\n // keeps tests (and any other non-DOM environment) functional even if\n // the true top-layer modal behavior isn't available there.\n useEffect(() => {\n const dialog = dialogRef.current\n if (!dialog) return undefined\n\n if (open) {\n if (!dialog.open) {\n if (typeof dialog.showModal === 'function') {\n try {\n dialog.showModal()\n } catch {\n dialog.setAttribute('open', '')\n }\n } else {\n dialog.setAttribute('open', '')\n }\n }\n\n const previouslyFocused =\n typeof document !== 'undefined'\n ? (document.activeElement as HTMLElement | null)\n : null\n\n // Explicitly focus the close button. In a real browser\n // `showModal()` would already focus the first sequentially\n // focusable child (which is the close button), but doing it\n // ourselves guarantees the same behavior in jsdom and in any\n // browser where focus heuristics differ.\n closeButtonRef.current?.focus()\n\n return () => {\n if (dialog.open) {\n if (typeof dialog.close === 'function') {\n try {\n dialog.close()\n } catch {\n dialog.removeAttribute('open')\n }\n } else {\n dialog.removeAttribute('open')\n }\n }\n if (previouslyFocused && document.body.contains(previouslyFocused)) {\n previouslyFocused.focus()\n }\n }\n }\n\n if (dialog.open) {\n if (typeof dialog.close === 'function') {\n try {\n dialog.close()\n } catch {\n dialog.removeAttribute('open')\n }\n } else {\n dialog.removeAttribute('open')\n }\n }\n return undefined\n }, [open])\n\n // The platform fires `close` when the user dismisses via Escape or\n // any other native path. Mirror that into React state so the parent's\n // `open` flag stays in sync — otherwise the dialog would close\n // visually but our effect would re-open it on the next render.\n const handleDialogClose = () => {\n onClose()\n }\n\n // Backdrop click closes the viewer. The click lands on the `<dialog>`\n // itself (not its children) because the body sits inside an inner\n // wrapper — the easiest reliable check.\n const handleDialogClick = (e: React.MouseEvent<HTMLDialogElement>) => {\n if (e.target === dialogRef.current) onClose()\n }\n\n return (\n // The native `<dialog>` element ships its own keyboard dismissal\n // (Escape, handled by the platform and surfaced through `onClose`),\n // so the `onClick` on the backdrop doesn't need a paired keyboard\n // listener — that's the whole point of using `<dialog>`. The\n // `jsx-a11y` rules below don't model `<dialog>` as interactive, so\n // we silence them locally with the rationale captured here.\n // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions\n <dialog\n ref={dialogRef}\n className=\"mes-media-viewer\"\n aria-label={ariaLabel ?? 'Attachment viewer'}\n data-testid={dataTestId}\n onClose={handleDialogClose}\n onClick={handleDialogClick}\n >\n <div className=\"mes-media-viewer__chrome\">\n {counter ? (\n <span className=\"mes-media-viewer__counter\">{counter}</span>\n ) : null}\n <div className=\"mes-media-viewer__actions\">\n {actions}\n <button\n ref={closeButtonRef}\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close viewer\"\n className=\"mes-media-viewer__action\"\n >\n <XIcon size={20} weight=\"bold\" aria-hidden />\n </button>\n </div>\n </div>\n <div className=\"mes-media-viewer__body\">{children}</div>\n </dialog>\n )\n}\n\nexport default ViewerShell\n","import React, { useMemo } from 'react'\n\nimport CarouselNav from './CarouselNav'\nimport DownloadAction from './DownloadAction'\nimport { filenameFromUrl } from './fileMeta'\nimport { useCarousel } from './useCarousel'\nimport ViewerShell from './ViewerShell'\n\nexport interface ImageViewerItem {\n src: string\n alt?: string\n /**\n * Filename used by the download action and as the dialog's\n * accessible name. Falls back to the last pathname segment of\n * `src` when omitted.\n */\n filename?: string\n}\n\nexport interface ImageViewerProps {\n open: boolean\n items: ImageViewerItem[]\n /** Index to display when the viewer first opens. Defaults to `0`. */\n initialIndex?: number\n onClose: () => void\n}\n\n/**\n * Native lightbox-style image viewer — full-viewport `<dialog>` with\n * the image centered at reasonable max dimensions, a single close\n * button, and a download action in the chrome.\n *\n * When `items.length > 1` the viewer becomes a carousel:\n *\n * - Prev / next on-screen controls hug the viewport sides.\n * - `ArrowLeft` / `ArrowRight` step between siblings (the keyboard\n * listener is parked while a `<video>` / `<audio>` element is\n * focused so native media seek still works in mixed contexts).\n * - A `current / total` counter sits in the top-left chrome.\n *\n * Used by every `MessageAttachment.Image.*` variant\n * (Composer / Sent / Received) so admins can preview attachments\n * before sending and recipients can preview after receipt.\n */\nconst ImageViewer: React.FC<ImageViewerProps> = ({\n open,\n items,\n initialIndex = 0,\n onClose,\n}) => {\n const { index, prev, next } = useCarousel({\n length: items.length,\n initialIndex,\n open,\n })\n\n const item = items[index]\n const filename = useMemo(\n () => item?.filename ?? (item ? filenameFromUrl(item.src) : 'image'),\n [item]\n )\n\n if (!item) return null\n\n return (\n <ViewerShell\n open={open}\n onClose={onClose}\n ariaLabel={filename}\n counter={\n items.length > 1 ? `${index + 1} / ${items.length}` : undefined\n }\n actions={\n <DownloadAction\n url={item.src}\n filename={filename}\n variant=\"viewer\"\n label={`Download ${filename}`}\n />\n }\n data-testid=\"image-viewer\"\n >\n <img\n // Forcing a key swap on item change ensures React replaces the\n // `<img>` cleanly between siblings — otherwise the previous\n // image stays painted for a frame while the new `src`\n // decodes, which reads as a stutter at carousel pace.\n key={`${index}:${item.src}`}\n src={item.src}\n alt={item.alt ?? filename}\n draggable={false}\n // The user has explicitly opened the viewer, so we want the\n // active image to appear immediately rather than fall to the\n // browser's lazy-load heuristics.\n loading=\"eager\"\n decoding=\"async\"\n className=\"mes-media-viewer__image\"\n />\n\n {items.length > 1 ? (\n <CarouselNav\n onPrev={prev}\n onNext={next}\n prevLabel=\"Previous image\"\n nextLabel=\"Next image\"\n />\n ) : null}\n </ViewerShell>\n )\n}\n\nexport default ImageViewer\n","import classNames from 'classnames'\nimport React from 'react'\n\nexport interface MediaStackTile {\n /** Renderable tile content (image / video / poster). */\n content: React.ReactNode\n /** Pure-tile aria label (e.g. `'Photo 1'`). Used by `Pressable` mode. */\n ariaLabel?: string\n}\n\nexport interface MediaStackGridProps {\n tiles: MediaStackTile[]\n /**\n * When set, every tile is wrapped in a `<button>` and forwards the\n * tile index to `onTileActivate`. Used by Image / Video so any tile\n * opens the lightbox / video viewer at the right index.\n */\n onTileActivate?: (index: number) => void\n /**\n * Maximum tiles to render before the last one collapses into an\n * \"+N more\" overflow indicator. Defaults to `4`.\n */\n maxVisible?: number\n className?: string\n}\n\nconst TILE_SHELL =\n 'relative block size-full overflow-hidden bg-black/5 outline-none focus-visible:ring-2 focus-visible:ring-white/80 focus-visible:ring-offset-2 focus-visible:ring-offset-black'\n\n/**\n * Adaptive grid used by stacked image / video attachments. Layouts:\n *\n * - 1 tile — full-bleed\n * - 2 tiles — equal-width side-by-side row\n * - 3 tiles — one large left tile + two stacked right tiles\n * - 4 tiles — 2×2 grid\n * - 5+ — 2×2 grid with the bottom-right tile showing \"+N more\"\n *\n * The grid is square-ish overall (1:1 for 1, 16:9 for 2, 4:3 for 3+) so\n * stacks fit comfortably inside the bubble width without dominating the\n * conversation.\n */\nconst MediaStackGrid: React.FC<MediaStackGridProps> = ({\n tiles,\n onTileActivate,\n maxVisible = 4,\n className,\n}) => {\n const total = tiles.length\n if (total === 0) return null\n\n const visible = tiles.slice(0, Math.min(total, maxVisible))\n const overflow = total - visible.length\n\n const renderTile = (tile: MediaStackTile, index: number, extra?: React.ReactNode) => {\n const sharedClass = classNames(TILE_SHELL, 'h-full w-full')\n if (onTileActivate) {\n return (\n <button\n type=\"button\"\n key={index}\n onClick={() => onTileActivate(index)}\n aria-label={tile.ariaLabel ?? `Open media ${index + 1}`}\n className={classNames(sharedClass, 'cursor-zoom-in')}\n >\n {tile.content}\n {extra}\n </button>\n )\n }\n return (\n <div key={index} className={sharedClass}>\n {tile.content}\n {extra}\n </div>\n )\n }\n\n if (visible.length === 1) {\n return (\n <div className={classNames('aspect-square w-full', className)}>\n {renderTile(visible[0], 0)}\n </div>\n )\n }\n\n if (visible.length === 2) {\n return (\n <div\n className={classNames(\n 'grid aspect-[16/9] w-full grid-cols-2 gap-0.5',\n className\n )}\n >\n {visible.map((tile, index) => renderTile(tile, index))}\n </div>\n )\n }\n\n if (visible.length === 3) {\n return (\n <div\n className={classNames(\n 'grid aspect-[4/3] w-full grid-cols-2 grid-rows-2 gap-0.5',\n className\n )}\n >\n <div className=\"row-span-2\">{renderTile(visible[0], 0)}</div>\n {renderTile(visible[1], 1)}\n {renderTile(visible[2], 2)}\n </div>\n )\n }\n\n // 4 tiles (or 4 visible with overflow on the last)\n return (\n <div\n className={classNames(\n 'grid aspect-[4/3] w-full grid-cols-2 grid-rows-2 gap-0.5',\n className\n )}\n >\n {visible.map((tile, index) => {\n const isLastWithOverflow = overflow > 0 && index === visible.length - 1\n return renderTile(\n tile,\n index,\n isLastWithOverflow ? (\n <div className=\"absolute inset-0 flex items-center justify-center bg-black/55 text-2xl font-semibold text-white\">\n +{overflow}\n </div>\n ) : null\n )\n })}\n </div>\n )\n}\n\nexport default MediaStackGrid\n","import { useCallback, useState } from 'react'\n\n/**\n * Optional click handler used by every viewer-backed attachment.\n *\n * Return `false` to cancel the default open behavior — useful for\n * route-based viewers or confirmation modals. Any other return value\n * (including `void`/`undefined`) lets the built-in viewer open as\n * normal. The `index` argument identifies the row in stacked\n * attachments and is `0` for the single-attachment shape.\n */\nexport type ViewerClickHandler = (index: number) => boolean | void\n\nexport interface UseViewerResult {\n viewerOpen: boolean\n viewerIndex: number\n /**\n * Open the viewer at the given index. Forwards to the caller's\n * `onClick` first so analytics + intercept logic can fire before\n * the viewer mounts; if `onClick` returns `false` the open is\n * skipped entirely.\n */\n handleActivate: (index: number) => void\n closeViewer: () => void\n}\n\n/**\n * Tiny piece of state shared by every `MessageAttachment` that opens\n * a fullscreen viewer (`Image`, `Video`, `Pdf`). Tracks the open flag\n * + the active index, and threads the caller's `onClick` callback\n * through `handleActivate` so consumers can intercept the open\n * (e.g. analytics) or cancel it (return `false` to swap in a\n * route-based viewer / confirmation modal).\n */\nexport const useViewer = (onClick?: ViewerClickHandler): UseViewerResult => {\n const [viewerOpen, setViewerOpen] = useState(false)\n const [viewerIndex, setViewerIndex] = useState(0)\n\n const handleActivate = useCallback(\n (index: number) => {\n if (onClick?.(index) === false) return\n setViewerIndex(index)\n setViewerOpen(true)\n },\n [onClick]\n )\n\n const closeViewer = useCallback(() => setViewerOpen(false), [])\n\n return { viewerOpen, viewerIndex, handleActivate, closeViewer }\n}\n\nexport default useViewer\n","import React from 'react'\n\nimport Bubble from '../_shared/Bubble'\nimport DismissButton from '../_shared/DismissButton'\nimport ImageViewer, {\n type ImageViewerItem,\n} from '../_shared/ImageViewer'\nimport MediaStackGrid, {\n type MediaStackTile,\n} from '../_shared/MediaStackGrid'\nimport { useViewer } from '../_shared/useViewer'\nimport {\n bubbleVariantForState,\n type ImageItem,\n type ImageLoadingMode,\n type MessageAttachmentBaseProps,\n type MessageAttachmentState,\n} from '../types'\n\nexport interface ImageAttachmentSharedProps extends MessageAttachmentBaseProps {\n /** Single image — convenience for the most common case. */\n src?: string\n alt?: string\n /** Filename used as the viewer dialog's accessible name. */\n filename?: string\n /**\n * Stacked images. Takes precedence over `src` when set. Renders a\n * 1 / 2 / 3 / 4-tile grid (5+ collapse into a `+N` overflow tile).\n * Sent + Received only — the composer surface intentionally accepts\n * a single attachment at a time.\n */\n items?: ImageItem[]\n /**\n * Native lazy-load hint forwarded to every `<img>` rendered on the\n * bubble surface (Composer thumbnail, single + stacked tiles, and\n * the `+N` overflow tile). Defaults to `'lazy'` — chat surfaces\n * usually scroll a long history, and lazy loading prevents every\n * historical image from being fetched on mount. Set `'eager'` for\n * above-the-fold hero attachments. Per-tile overrides live on\n * `ImageItem.loading`. The opened `ImageViewer` always eager-loads\n * the active image regardless of this value.\n */\n loading?: ImageLoadingMode\n /**\n * Forwarded to the Image viewer trigger. When omitted the click\n * still opens the built-in viewer — supply this for analytics, or\n * return `false` to intercept the open (e.g. switch to a route-\n * based gallery / confirmation modal). Any other return value\n * (including `void`/`undefined`) lets the built-in viewer open.\n * For stacked attachments the `index` argument identifies the tile.\n */\n onClick?: (index: number) => boolean | void\n}\n\nconst tileFromItem = (\n item: ImageItem,\n index: number,\n totalCount: number,\n fallbackLoading: ImageLoadingMode\n): MediaStackTile => ({\n ariaLabel: `Open image ${index + 1} of ${totalCount}`,\n content: (\n <img\n src={item.src}\n alt={item.alt ?? ''}\n width={item.width}\n height={item.height}\n draggable={false}\n loading={item.loading ?? fallbackLoading}\n decoding=\"async\"\n className=\"absolute inset-0 size-full rounded-md object-cover\"\n />\n ),\n})\n\nconst resolveItems = ({\n src,\n alt,\n items,\n}: {\n src?: string\n alt?: string\n items?: ImageItem[]\n}): ImageItem[] => {\n if (items && items.length > 0) return items\n if (src) return [{ src, alt }]\n return []\n}\n\ninterface InternalImageRowProps extends ImageAttachmentSharedProps {\n state: MessageAttachmentState\n /**\n * Renders a dismiss button on each tile (Composer only). When the\n * stack has multiple tiles, the dismiss applies to the whole stack\n * — that mirrors the mobile composer where attachments are removed\n * as a single unit.\n */\n onDismiss?: () => void\n}\n\n/**\n * Project the bubble's `ImageItem`s onto the carousel-aware viewer's\n * `ImageViewerItem` shape. When a shared outer `filename` is supplied\n * for a stacked bubble, suffix `(N)` per sibling so each carousel page\n * still gets a distinct accessible name + download default.\n */\nconst buildViewerItems = (\n resolvedItems: ImageItem[],\n filename?: string\n): ImageViewerItem[] =>\n resolvedItems.map((item, index) => ({\n src: item.src,\n alt: item.alt,\n filename:\n filename && resolvedItems.length === 1\n ? filename\n : filename\n ? `${filename} (${index + 1})`\n : undefined,\n }))\n\n/**\n * Composer rendering — bare 280px-square image with a dismiss `×`\n * overlay and `rounded-md` corners. Intentionally renders without the\n * shared `Bubble` chrome (no border / no background / no padding) so\n * the in-progress attachment looks like a draft preview, not a sent\n * message. The composer surface only supports a single attachment at\n * a time — `items` and `text` are ignored here.\n */\nconst ImageComposerInner: React.FC<{\n src: string\n alt?: string\n filename?: string\n loading?: ImageLoadingMode\n onClick?: (index: number) => boolean | void\n onDismiss?: () => void\n}> = ({ src, alt, filename, loading = 'lazy', onClick, onDismiss }) => {\n const { viewerOpen, viewerIndex, handleActivate, closeViewer } = useViewer(\n onClick\n )\n\n return (\n <div className=\"relative w-fit\">\n <button\n type=\"button\"\n onClick={() => handleActivate(0)}\n aria-label=\"Open image\"\n className=\"block size-[280px] cursor-zoom-in overflow-hidden rounded-md outline-none focus-visible:ring-2 focus-visible:ring-black/40\"\n >\n <img\n src={src}\n alt={alt ?? ''}\n draggable={false}\n loading={loading}\n decoding=\"async\"\n className=\"size-full object-cover\"\n />\n </button>\n {onDismiss ? (\n <div className=\"absolute right-2 top-2 z-10\">\n <DismissButton onClick={onDismiss} />\n </div>\n ) : null}\n\n <ImageViewer\n open={viewerOpen}\n items={buildViewerItems([{ src, alt }], filename)}\n initialIndex={viewerIndex}\n onClose={closeViewer}\n />\n </div>\n )\n}\n\n/**\n * Sent / Received rendering — wrapped in the shared `Bubble` chrome,\n * supports single or stacked items, and renders an optional caption\n * below the media.\n */\nconst ImageBubbleRow: React.FC<InternalImageRowProps> = ({\n state,\n src,\n alt,\n filename,\n items,\n text,\n groupPosition,\n loading = 'lazy',\n onClick,\n}) => {\n const resolvedItems = resolveItems({ src, alt, items })\n const variant = bubbleVariantForState(state)\n const { viewerOpen, viewerIndex, handleActivate, closeViewer } = useViewer(\n onClick\n )\n\n if (resolvedItems.length === 0) {\n return null\n }\n\n const tiles: MediaStackTile[] = resolvedItems.map((item, index) =>\n tileFromItem(item, index, resolvedItems.length, loading)\n )\n\n return (\n <Bubble\n variant={variant}\n text={text}\n groupPosition={groupPosition}\n data-testid=\"image-attachment\"\n >\n <div className=\"relative\">\n <MediaStackGrid tiles={tiles} onTileActivate={handleActivate} />\n </div>\n\n <ImageViewer\n open={viewerOpen}\n items={buildViewerItems(resolvedItems, filename)}\n initialIndex={viewerIndex}\n onClose={closeViewer}\n />\n </Bubble>\n )\n}\n\n/**\n * Composer-only props. Single image (`src`) is required; stacked\n * `items` and `text` captions are not supported in the composer state.\n */\nexport interface ImageComposerProps {\n src: string\n alt?: string\n filename?: string\n /**\n * Native lazy-load hint forwarded to the composer thumbnail `<img>`.\n * Defaults to `'lazy'`. See `ImageAttachmentSharedProps.loading` for\n * the rationale.\n */\n loading?: ImageLoadingMode\n onClick?: (index: number) => boolean | void\n onDismiss?: () => void\n}\n\nexport type ImageSentProps = ImageAttachmentSharedProps\nexport type ImageReceivedProps = ImageAttachmentSharedProps\n\nconst ImageComposer: React.FC<ImageComposerProps> = (props) => (\n <ImageComposerInner {...props} />\n)\nconst ImageSent: React.FC<ImageSentProps> = (props) => (\n <ImageBubbleRow {...props} state=\"sent\" />\n)\nconst ImageReceived: React.FC<ImageReceivedProps> = (props) => (\n <ImageBubbleRow {...props} state=\"received\" />\n)\n\nconst ImageAttachment = {\n Composer: ImageComposer,\n Sent: ImageSent,\n Received: ImageReceived,\n}\n\nexport default ImageAttachment\n","import React, { useMemo } from 'react'\n\nimport CarouselNav from './CarouselNav'\nimport { filenameFromUrl } from './fileMeta'\nimport { useCarousel } from './useCarousel'\nimport ViewerShell from './ViewerShell'\n\nexport interface PdfViewerItem {\n /** Source URL of the PDF document. */\n src: string\n /** Filename used as the dialog's accessible name. */\n filename?: string\n}\n\nexport interface PdfViewerProps {\n open: boolean\n items: PdfViewerItem[]\n /** Index to display when the viewer first opens. Defaults to `0`. */\n initialIndex?: number\n onClose: () => void\n}\n\n/**\n * Modal PDF viewer that hosts the active document inside a sandboxed\n * `<iframe>`, leaving native rendering, scroll, search, and the\n * browser's built-in download controls to the browser. The lightbox\n * chrome is intentionally just a close button plus, for stacked\n * attachments, carousel prev/next + counter.\n */\nconst PdfViewer: React.FC<PdfViewerProps> = ({\n open,\n items,\n initialIndex = 0,\n onClose,\n}) => {\n const { index, prev, next } = useCarousel({\n length: items.length,\n initialIndex,\n open,\n })\n\n const item = items[index]\n const filename = useMemo(\n () => item?.filename ?? (item ? filenameFromUrl(item.src) : 'document'),\n [item]\n )\n\n // `#toolbar=0` is honored by Chromium-family browsers and hides the\n // built-in PDF toolbar's redundant controls. Firefox / Safari ignore\n // it and keep their native toolbars, which is fine — the user gets a\n // familiar experience either way.\n //\n // Preserve any existing fragment the caller supplied (e.g.\n // `#page=3`) by merging our params into it rather than naively\n // appending a second `#…` segment.\n const iframeSrc = useMemo(\n () => (item ? withPdfViewerParams(item.src) : undefined),\n [item]\n )\n\n if (!item || !iframeSrc) return null\n\n return (\n <ViewerShell\n open={open}\n onClose={onClose}\n ariaLabel={filename}\n counter={\n items.length > 1 ? `${index + 1} / ${items.length}` : undefined\n }\n data-testid=\"pdf-viewer\"\n >\n <iframe\n // Force a key swap on item change so the iframe remounts a\n // fresh document rather than re-using the previous scroll\n // position / search state when the user navigates between\n // siblings.\n key={`${index}:${item.src}`}\n src={iframeSrc}\n title={filename}\n className=\"mes-media-viewer__iframe\"\n // Sandbox the iframe to stop the embedded document from\n // reaching parent context. We intentionally omit\n // `allow-same-origin`: even when the PDF host happens to share\n // our origin, granting it defeats the rest of the sandbox.\n // `allow-scripts` keeps the native PDF viewer's interactive\n // affordances (search, page nav, form filling) working,\n // `allow-downloads` lets the user save via the built-in\n // toolbar where it's still visible (Firefox / Safari), and\n // `allow-popups` lets external links inside the PDF open in a\n // new tab.\n sandbox=\"allow-scripts allow-forms allow-popups allow-downloads\"\n />\n\n {items.length > 1 ? (\n <CarouselNav\n onPrev={prev}\n onNext={next}\n prevLabel=\"Previous document\"\n nextLabel=\"Next document\"\n />\n ) : null}\n </ViewerShell>\n )\n}\n\n/**\n * Merge our PDF-viewer params (`toolbar=0`, `navpanes=0`) into the\n * URL's existing fragment so callers can still pass things like\n * `#page=3` without us clobbering them — naively appending\n * `#toolbar=0` to a URL that already has a fragment produces\n * `…#page=3#toolbar=0` which most readers ignore.\n */\nconst withPdfViewerParams = (src: string): string => {\n const hashIndex = src.indexOf('#')\n const base = hashIndex === -1 ? src : src.slice(0, hashIndex)\n const existing = hashIndex === -1 ? '' : src.slice(hashIndex + 1)\n\n const params = new URLSearchParams(existing)\n if (!params.has('toolbar')) params.set('toolbar', '0')\n if (!params.has('navpanes')) params.set('navpanes', '0')\n\n return `${base}#${params.toString()}`\n}\n\nexport default PdfViewer\n","import React from 'react'\n\nimport Bubble from '../_shared/Bubble'\nimport CompactDocumentRow from '../_shared/CompactDocumentRow'\nimport DismissButton from '../_shared/DismissButton'\nimport DownloadAction from '../_shared/DownloadAction'\nimport { filenameFromUrl } from '../_shared/fileMeta'\nimport PdfViewer, { type PdfViewerItem } from '../_shared/PdfViewer'\nimport { useViewer } from '../_shared/useViewer'\nimport {\n bubbleVariantForState,\n type BubbleVariant,\n type ComposerExtras,\n type MessageAttachmentBaseProps,\n type MessageAttachmentState,\n type PdfItem,\n} from '../types'\n\nexport interface PdfAttachmentSharedProps extends MessageAttachmentBaseProps {\n /** Single PDF — convenience for the most common case. */\n src?: string\n /** Filename — drives the row title + the meta line + the viewer dialog's accessible name. */\n filename?: string\n /** File size in bytes — formatted into the `EXT · SIZE` meta line. */\n fileSize?: number\n /**\n * Override displayed title (defaults to `filename`). Useful when a\n * sender re-titles a generated PDF before sending.\n */\n title?: string\n /**\n * Stacked PDFs. Takes precedence over `src` when set. Each item\n * renders as its own row inside the bubble; clicking a row opens\n * that PDF in the viewer. Sent + Received only — the composer\n * surface accepts a single attachment at a time.\n */\n items?: PdfItem[]\n /**\n * Forwarded to the viewer trigger. When omitted the click still\n * opens the built-in `PdfViewer`. Supply this to fire analytics, or\n * return `false` to intercept the open (e.g. swap to a route-based\n * viewer / confirmation modal). Any other return value (including\n * `void`/`undefined`) lets the built-in viewer open. For stacked\n * attachments the `index` argument identifies the row.\n */\n onClick?: (index: number) => boolean | void\n}\n\nconst resolveItems = ({\n src,\n filename,\n fileSize,\n title,\n items,\n}: {\n src?: string\n filename?: string\n fileSize?: number\n title?: string\n items?: PdfItem[]\n}): PdfItem[] => {\n if (items && items.length > 0) return items\n if (src) return [{ src, filename, fileSize, title }]\n return []\n}\n\ninterface PdfRowButtonProps {\n variant: BubbleVariant\n item: PdfItem\n index: number\n onActivate: (index: number) => void\n trailingAction?: React.ReactNode\n}\n\n/**\n * Single tappable PDF row — used both for the lone PDF case and for\n * each child of a stacked attachment. The icon + filename area opens\n * the viewer; the trailing slot hosts a separate, sibling button\n * (download or dismiss) so each affordance owns its own click target.\n */\nconst PdfRowButton: React.FC<PdfRowButtonProps> = ({\n variant,\n item,\n index,\n onActivate,\n trailingAction,\n}) => {\n const resolvedFilename = item.filename ?? filenameFromUrl(item.src)\n return (\n <CompactDocumentRow\n variant={variant}\n filename={resolvedFilename}\n title={item.title}\n mimeType=\"application/pdf\"\n fileSize={item.fileSize}\n onActivate={() => onActivate(index)}\n activateLabel={`Open ${resolvedFilename}`}\n trailingAction={trailingAction}\n />\n )\n}\n\ninterface InternalPdfRowProps extends PdfAttachmentSharedProps {\n state: MessageAttachmentState\n onDismiss?: () => void\n}\n\nconst PdfAttachmentRow: React.FC<InternalPdfRowProps> = ({\n state,\n src,\n filename,\n fileSize,\n title,\n items,\n text,\n groupPosition,\n onClick,\n onDismiss,\n}) => {\n const variant = bubbleVariantForState(state)\n const resolvedItems = resolveItems({ src, filename, fileSize, title, items })\n const { viewerOpen, viewerIndex, handleActivate, closeViewer } = useViewer(\n onClick\n )\n const showDismiss = state === 'composer' && !!onDismiss\n\n if (resolvedItems.length === 0) {\n return null\n }\n\n // Project the bubble's `PdfItem`s onto the viewer's carousel-aware\n // shape. The viewer clamps its own active index defensively, so we\n // don't need to second-guess `viewerIndex` here.\n const viewerItems: PdfViewerItem[] = resolvedItems.map((item) => ({\n src: item.src,\n filename: item.filename ?? filenameFromUrl(item.src),\n }))\n\n return (\n <Bubble\n variant={variant}\n text={text}\n groupPosition={groupPosition}\n data-testid=\"pdf-attachment\"\n >\n {/* Stacked rows get a small vertical gap so each PDF reads as\n a discrete attachment rather than one merged blob. Single\n row falls through to a no-op `gap-0`. */}\n <div className=\"flex flex-col gap-2\">\n {resolvedItems.map((item, index) => {\n const itemFilename = item.filename ?? filenameFromUrl(item.src)\n // Composer only supports a single attachment so the dismiss\n // control sits on the only row. Sent / Received rows expose\n // a download icon button so the user can grab the PDF\n // without opening the viewer first — the viewer dialog is\n // intentionally just the document + a close button, so this\n // sibling action is the only one-click download surface.\n const trailingAction =\n showDismiss && index === 0 ? (\n <DismissButton onClick={onDismiss!} variant=\"inline\" />\n ) : state === 'composer' ? undefined : (\n <DownloadAction\n url={item.src}\n filename={itemFilename}\n variant=\"inline\"\n label={`Download ${itemFilename}`}\n tone={variant}\n />\n )\n return (\n <PdfRowButton\n key={`${item.src}-${index}`}\n variant={variant}\n item={item}\n index={index}\n onActivate={handleActivate}\n trailingAction={trailingAction}\n />\n )\n })}\n </div>\n\n <PdfViewer\n open={viewerOpen}\n items={viewerItems}\n initialIndex={viewerIndex}\n onClose={closeViewer}\n />\n </Bubble>\n )\n}\n\n/**\n * Composer-only props. Single PDF (`src`) only — the composer surface\n * accepts a single attachment at a time, so `items` is not supported.\n * Captions (`text`) and `groupPosition` are also dropped: the composer\n * renders a standalone draft, not part of a same-author run, and\n * captions live in the message-input textarea, not inside the draft\n * attachment preview.\n */\nexport interface PdfComposerProps extends ComposerExtras {\n src?: string\n filename?: string\n fileSize?: number\n title?: string\n onClick?: (index: number) => boolean | void\n}\nexport type PdfSentProps = PdfAttachmentSharedProps\nexport type PdfReceivedProps = PdfAttachmentSharedProps\n\nconst PdfComposer: React.FC<PdfComposerProps> = (props) => (\n <PdfAttachmentRow {...props} state=\"composer\" />\n)\nconst PdfSent: React.FC<PdfSentProps> = (props) => (\n <PdfAttachmentRow {...props} state=\"sent\" />\n)\nconst PdfReceived: React.FC<PdfReceivedProps> = (props) => (\n <PdfAttachmentRow {...props} state=\"received\" />\n)\n\nconst PdfAttachment = {\n Composer: PdfComposer,\n Sent: PdfSent,\n Received: PdfReceived,\n}\n\nexport default PdfAttachment\n","import React, { useMemo } from 'react'\n\nimport type { MediaPreloadMode } from '../types'\n\nimport CarouselNav from './CarouselNav'\nimport { filenameFromUrl } from './fileMeta'\nimport { useCarousel } from './useCarousel'\nimport ViewerShell from './ViewerShell'\n\nexport interface VideoViewerItem {\n src: string\n poster?: string\n mimeType?: string\n filename?: string\n /**\n * Per-item `<video preload>` override. Defaults to `'metadata'`\n * for the active item so duration / first-frame are available\n * immediately. Non-active siblings aren't mounted here (we render\n * only `items[index]` at a time via a `key` swap), so they\n * naturally stay unloaded.\n */\n preload?: MediaPreloadMode\n}\n\nexport interface VideoViewerProps {\n open: boolean\n items: VideoViewerItem[]\n /** Index to display when the viewer first opens. Defaults to `0`. */\n initialIndex?: number\n onClose: () => void\n}\n\n/**\n * Full-viewport video viewer used by every `MessageAttachment.Video.*`\n * variant. Renders a single `<video controls>` element with the\n * browser's native chrome (play / pause / seek / fullscreen / volume /\n * download).\n *\n * When `items.length > 1` the viewer becomes a carousel — see\n * `ImageViewer` for the same behavior; arrow-key navigation yields to\n * the focused `<video>` so the native seek shortcuts still work.\n */\nconst VideoViewer: React.FC<VideoViewerProps> = ({\n open,\n items,\n initialIndex = 0,\n onClose,\n}) => {\n const { index, prev, next } = useCarousel({\n length: items.length,\n initialIndex,\n open,\n })\n\n const item = items[index]\n const filename = useMemo(\n () => item?.filename ?? (item ? filenameFromUrl(item.src) : 'video'),\n [item]\n )\n\n if (!item) return null\n\n return (\n <ViewerShell\n open={open}\n onClose={onClose}\n ariaLabel={filename}\n counter={\n items.length > 1 ? `${index + 1} / ${items.length}` : undefined\n }\n data-testid=\"video-viewer\"\n >\n {/* No `<track>` is rendered — we don't author caption sidecars\n for chat attachments, and an empty `<track kind=\"captions\" />`\n emits a runtime warning. Re-add a real track (with `src`,\n `srcLang`, and `default`) once caption support ships. */}\n {/* eslint-disable-next-line jsx-a11y/media-has-caption */}\n <video\n // Forcing a key swap on item change ensures the new source\n // mounts a fresh element instead of reusing the previous\n // playback state — keeps the video paused-at-start when\n // navigating between stacked items.\n key={`${index}:${item.src}`}\n src={item.src}\n poster={item.poster}\n controls\n // `autoPlay` without `muted` is blocked by Chrome / Safari /\n // Firefox autoplay policies — the click that opens the viewer\n // doesn't count as a user-gesture for the freshly-mounted\n // `<video>` element. Defaulting to muted means playback\n // actually starts (matches how IG / Slack handle their video\n // lightboxes); the user can unmute via the native controls if\n // they want sound.\n autoPlay\n muted\n playsInline\n preload={item.preload ?? 'metadata'}\n className=\"mes-media-viewer__video\"\n >\n {item.mimeType ? <source src={item.src} type={item.mimeType} /> : null}\n </video>\n\n {items.length > 1 ? (\n <CarouselNav\n onPrev={prev}\n onNext={next}\n prevLabel=\"Previous video\"\n nextLabel=\"Next video\"\n />\n ) : null}\n </ViewerShell>\n )\n}\n\nexport default VideoViewer\n","import { PlayIcon, VideoCameraIcon } from '@phosphor-icons/react'\nimport React from 'react'\n\nimport Bubble from '../_shared/Bubble'\nimport DismissButton from '../_shared/DismissButton'\nimport MediaStackGrid, {\n type MediaStackTile,\n} from '../_shared/MediaStackGrid'\nimport { useViewer } from '../_shared/useViewer'\nimport VideoViewer, { type VideoViewerItem } from '../_shared/VideoViewer'\nimport {\n bubbleVariantForState,\n type MediaPreloadMode,\n type MessageAttachmentBaseProps,\n type MessageAttachmentState,\n type VideoItem,\n} from '../types'\n\nexport interface VideoAttachmentSharedProps extends MessageAttachmentBaseProps {\n /** Single video — convenience for the most common case. */\n src?: string\n /** Poster image — preview shown before playback starts. */\n poster?: string\n /** MIME type hint — typed onto the inline `<source>` element. */\n mimeType?: string\n /** Filename used as the viewer dialog's accessible name. */\n filename?: string\n /**\n * Stacked videos. Takes precedence over `src` when set. Renders a\n * 1 / 2 / 3 / 4-tile grid (5+ collapse into a `+N` overflow tile).\n * Sent + Received only — the composer surface intentionally accepts\n * a single attachment at a time.\n */\n items?: VideoItem[]\n /**\n * `<video preload>` hint forwarded into the viewer. Defaults to\n * `'none'` — the poster `<img>` carries the visual weight on the\n * bubble surface, and we shouldn't fetch any video bytes until the\n * user actually opens the viewer. Per-item overrides live on\n * `VideoItem.preload`. The opened `VideoViewer` always preloads\n * metadata for the active item (so duration / first-frame appear\n * immediately) regardless of this value.\n */\n preload?: MediaPreloadMode\n /**\n * Forwarded to the viewer trigger. When omitted the click still\n * opens the built-in `VideoViewer` — supply this for analytics, or\n * return `false` to intercept the open (e.g. switch to a route-\n * based player / confirmation modal). Any other return value\n * (including `void`/`undefined`) lets the built-in viewer open.\n * For stacked attachments the `index` argument identifies the tile.\n */\n onClick?: (index: number) => boolean | void\n}\n\nconst PlayBadge: React.FC = () => (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\n <span className=\"flex size-12 items-center justify-center rounded-full bg-black/55 text-white backdrop-blur\">\n <PlayIcon className=\"size-6\" weight=\"fill\" aria-hidden />\n </span>\n </div>\n)\n\nconst PosterTile: React.FC<{ item: VideoItem; index: number }> = ({\n item,\n index,\n}) => (\n <div className=\"absolute inset-0 size-full bg-[#0d0d0d]\">\n {item.poster ? (\n <img\n src={item.poster}\n alt={`Video ${index + 1} thumbnail`}\n draggable={false}\n loading=\"lazy\"\n decoding=\"async\"\n className=\"absolute inset-0 size-full rounded-md object-cover\"\n />\n ) : (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <VideoCameraIcon\n className=\"size-16 rounded-md text-white/30\"\n weight=\"regular\"\n aria-hidden\n />\n </div>\n )}\n <PlayBadge />\n </div>\n)\n\nconst tileFromItem = (\n item: VideoItem,\n index: number,\n totalCount: number\n): MediaStackTile => ({\n ariaLabel: `Play video ${index + 1} of ${totalCount}`,\n content: <PosterTile item={item} index={index} />,\n})\n\nconst resolveItems = ({\n src,\n poster,\n mimeType,\n preload,\n items,\n}: {\n src?: string\n poster?: string\n mimeType?: string\n preload?: MediaPreloadMode\n items?: VideoItem[]\n}): VideoItem[] => {\n if (items && items.length > 0) {\n return preload ? items.map((it) => ({ ...it, preload: it.preload ?? preload })) : items\n }\n if (src) return [{ src, poster, mimeType, preload }]\n return []\n}\n\ninterface InternalVideoRowProps extends VideoAttachmentSharedProps {\n state: MessageAttachmentState\n}\n\n/**\n * Project the bubble's `VideoItem`s onto the carousel-aware viewer's\n * `VideoViewerItem` shape. When a shared outer `filename` is supplied\n * for a stacked bubble, suffix `(N)` per sibling so each carousel page\n * still gets a distinct accessible name.\n */\nconst buildViewerItems = (\n resolvedItems: VideoItem[],\n filename?: string\n): VideoViewerItem[] =>\n resolvedItems.map((item, index) => ({\n src: item.src,\n poster: item.poster,\n mimeType: item.mimeType,\n preload: item.preload,\n filename:\n filename && resolvedItems.length === 1\n ? filename\n : filename\n ? `${filename} (${index + 1})`\n : undefined,\n }))\n\n/**\n * Composer rendering — bare 280px-square poster with a play badge,\n * a dismiss `×` overlay, and `rounded-md` corners. Renders without the\n * shared `Bubble` chrome (no border / no background / no padding) so\n * the in-progress attachment looks like a draft preview rather than a\n * sent message. Single attachment only — `items` and `text` are not\n * supported on the composer surface.\n */\nconst VideoComposerInner: React.FC<{\n src: string\n poster?: string\n mimeType?: string\n filename?: string\n preload?: MediaPreloadMode\n onClick?: (index: number) => boolean | void\n onDismiss?: () => void\n}> = ({ src, poster, mimeType, filename, preload, onClick, onDismiss }) => {\n const { viewerOpen, viewerIndex, handleActivate, closeViewer } = useViewer(\n onClick\n )\n\n return (\n <div className=\"relative w-fit\">\n <button\n type=\"button\"\n onClick={() => handleActivate(0)}\n aria-label=\"Play video\"\n className=\"relative block size-[280px] cursor-pointer overflow-hidden rounded-md outline-none focus-visible:ring-2 focus-visible:ring-black/40\"\n >\n <PosterTile item={{ src, poster, mimeType }} index={0} />\n </button>\n {onDismiss ? (\n <div className=\"absolute right-2 top-2 z-10\">\n <DismissButton onClick={onDismiss} />\n </div>\n ) : null}\n\n <VideoViewer\n open={viewerOpen}\n items={buildViewerItems(\n [{ src, poster, mimeType, preload }],\n filename\n )}\n initialIndex={viewerIndex}\n onClose={closeViewer}\n />\n </div>\n )\n}\n\n/**\n * Sent / Received rendering — wrapped in the shared `Bubble` chrome,\n * supports single or stacked items, and renders an optional caption\n * below the media.\n */\nconst VideoBubbleRow: React.FC<InternalVideoRowProps> = ({\n state,\n src,\n poster,\n mimeType,\n filename,\n items,\n text,\n groupPosition,\n preload,\n onClick,\n}) => {\n const resolvedItems = resolveItems({ src, poster, mimeType, preload, items })\n const variant = bubbleVariantForState(state)\n const { viewerOpen, viewerIndex, handleActivate, closeViewer } = useViewer(\n onClick\n )\n\n if (resolvedItems.length === 0) {\n return null\n }\n\n const tiles: MediaStackTile[] = resolvedItems.map((item, index) =>\n tileFromItem(item, index, resolvedItems.length)\n )\n\n return (\n <Bubble\n variant={variant}\n text={text}\n groupPosition={groupPosition}\n data-testid=\"video-attachment\"\n >\n <div className=\"relative\">\n <MediaStackGrid\n tiles={tiles}\n onTileActivate={handleActivate}\n className=\"overflow-hidden rounded-md\"\n />\n </div>\n\n <VideoViewer\n open={viewerOpen}\n items={buildViewerItems(resolvedItems, filename)}\n initialIndex={viewerIndex}\n onClose={closeViewer}\n />\n </Bubble>\n )\n}\n\n/**\n * Composer-only props. Single video (`src`) is required; stacked\n * `items` and `text` captions are not supported in the composer state.\n */\nexport interface VideoComposerProps {\n src: string\n poster?: string\n mimeType?: string\n filename?: string\n /**\n * `<video preload>` hint forwarded into the viewer. Defaults to\n * `'none'`. See `VideoAttachmentSharedProps.preload`.\n */\n preload?: MediaPreloadMode\n onClick?: (index: number) => boolean | void\n onDismiss?: () => void\n}\n\nexport type VideoSentProps = VideoAttachmentSharedProps\nexport type VideoReceivedProps = VideoAttachmentSharedProps\n\nconst VideoComposer: React.FC<VideoComposerProps> = (props) => (\n <VideoComposerInner {...props} />\n)\nconst VideoSent: React.FC<VideoSentProps> = (props) => (\n <VideoBubbleRow {...props} state=\"sent\" />\n)\nconst VideoReceived: React.FC<VideoReceivedProps> = (props) => (\n <VideoBubbleRow {...props} state=\"received\" />\n)\n\nconst VideoAttachment = {\n Composer: VideoComposer,\n Sent: VideoSent,\n Received: VideoReceived,\n}\n\nexport default VideoAttachment\n","import AudioAttachment, {\n type AudioComposerProps,\n type AudioReceivedProps,\n type AudioSentProps,\n} from './Audio'\nimport FileAttachment, {\n type FileComposerProps,\n type FileReceivedProps,\n type FileSentProps,\n} from './File'\nimport ImageAttachment, {\n type ImageComposerProps,\n type ImageReceivedProps,\n type ImageSentProps,\n} from './Image'\nimport PdfAttachment, {\n type PdfComposerProps,\n type PdfReceivedProps,\n type PdfSentProps,\n} from './Pdf'\nimport VideoAttachment, {\n type VideoComposerProps,\n type VideoReceivedProps,\n type VideoSentProps,\n} from './Video'\n\n/**\n * Per-MIME chat attachment cards used inside the messaging stream.\n *\n * Each subnamespace (`Image`, `Video`, `Audio`, `Pdf`, `File`) exposes\n * three states (`Composer`, `Sent`, `Received`) that map to the\n * sender / recipient sides of a chat:\n *\n * - `Composer` — sender draft state with a dismiss `×` affordance.\n * - `Sent` — sender-side state shown after posting.\n * - `Received` — recipient-side state.\n *\n * Sent / Received variants accept an optional `text` prop that renders\n * inside the same bubble as the attachment — mirroring the mobile chat\n * where a sender can pair a caption with an attachment ('Here is the\n * file', 'Here is the image'). Composer prop types intentionally omit\n * `text` (and `items` / `groupPosition`) so the type system enforces\n * the same contract: captions live in the message-input textarea, not\n * inside the draft attachment preview.\n *\n * Stacked attachments — every type except `Composer` accepts an\n * `items={[…]}` prop on Sent / Received that takes precedence over\n * the singular `src`/`filename`/etc. inputs:\n *\n * - Image / Video render a 1 / 2 / 3 / 4-tile grid (5+ collapse\n * into a `+N` overflow tile). Activating any tile opens the\n * built-in `ImageViewer` / `VideoViewer` at that index; when\n * `items.length > 1` the lightbox becomes a carousel (prev / next\n * chrome, ArrowLeft / ArrowRight, current / total counter) so the\n * user can browse siblings without closing the modal.\n * - Pdf / Audio / File render their compact rows / players in a\n * vertical stack with an 8px gap between rows. Each row keeps\n * its own activation target — Pdf opens the carousel-aware\n * `PdfViewer` at that index, Audio gets its own native player,\n * and File downloads the asset for that row.\n *\n * The Composer surface accepts only a single attachment at a time,\n * so its props omit `items` / `text`.\n *\n * Image / Video Composer renders bare — just the media with rounded\n * corners and a dismiss overlay, no surrounding bubble — so the draft\n * preview reads as an in-progress attachment instead of a sent\n * message. Sent / Received wrap the media in the shared bubble chrome.\n *\n * Image / Video / Pdf are interactive in every state (Composer, Sent,\n * Received) — clicks open the corresponding native lightbox `<dialog>`,\n * which centers the media at reasonable max dimensions (Escape and\n * backdrop click also dismiss). The Image viewer adds an explicit\n * download button to the chrome; the Video viewer relies on the\n * native `<video controls>` download menu, and the Pdf viewer\n * inherits the browser's built-in PDF download. Sibling-row Download\n * buttons on Pdf / File and the native `<audio>` controls expose\n * downloads outside the viewer too. The lightbox chrome is styled\n * with plain CSS (`.mes-media-viewer*`) so the package's shipped\n * stylesheet is enough to render the viewer correctly without a\n * Tailwind dependency in the consumer.\n *\n * # Lazy-loading defaults\n *\n * Media attachments bake in network-friendly defaults for long chat\n * histories and expose explicit escape hatches when a caller knows\n * better (e.g. a hero attachment that's already on-screen):\n *\n * - **Image** — every `<img>` rendered on the bubble surface\n * (Composer thumbnail, single + stacked tiles, `+N` overflow)\n * ships with `loading=\"lazy\" decoding=\"async\"`. Override via the\n * attachment's `loading` prop (or `ImageItem.loading` for per-tile\n * control). The opened `ImageViewer` always eager-loads the\n * active image so it appears immediately.\n * - **Video** — the `<video>` rendered inside the `VideoViewer`\n * defaults to `preload=\"none\"`, so no video bytes are fetched\n * until the user opens the viewer. Poster `<img>` thumbnails on\n * the bubble surface are `loading=\"lazy\" decoding=\"async\"`.\n * Override via the attachment's `preload` prop (or\n * `VideoItem.preload` per item). Once the viewer is open the\n * active item bumps to `preload=\"metadata\"` so duration / the\n * first frame appear immediately.\n * - **Audio** — a single audio bubble defaults to\n * `preload=\"metadata\"` so the native player surfaces duration\n * immediately. A stacked audio bubble (`items.length > 1`)\n * defaults to `preload=\"none\"` for every player so a thread of\n * voice memos doesn't fan out N parallel metadata requests on\n * first paint. Override via the attachment's `preload` prop\n * (or `AudioItem.preload` per track).\n * - **Pdf** / **File** — no inline media element to throttle; the\n * bubble renders compact rows that download / open lazily on\n * activation. No `loading` / `preload` props on these surfaces.\n */\nconst MessageAttachment = {\n Image: ImageAttachment,\n Video: VideoAttachment,\n Audio: AudioAttachment,\n Pdf: PdfAttachment,\n File: FileAttachment,\n}\n\nexport default MessageAttachment\n\nexport type {\n ImageComposerProps,\n ImageSentProps,\n ImageReceivedProps,\n VideoComposerProps,\n VideoSentProps,\n VideoReceivedProps,\n AudioComposerProps,\n AudioSentProps,\n AudioReceivedProps,\n PdfComposerProps,\n PdfSentProps,\n PdfReceivedProps,\n FileComposerProps,\n FileSentProps,\n FileReceivedProps,\n}\nexport type {\n ImageItem,\n ImageLoadingMode,\n VideoItem,\n PdfItem,\n AudioItem,\n FileItem,\n BubbleGroupPosition,\n MediaPreloadMode,\n MessageAttachmentBaseProps,\n MessageAttachmentState,\n} from './types'\nexport { bubbleGroupPositionFromStream } from './types'\nexport {\n formatFileSize,\n getFileExtensionLabel,\n buildCompactMetaLabel,\n} from './_shared/fileMeta'\n","import classNames from 'classnames'\nimport React from 'react'\nexport interface FaqListItemProps {\n question: string\n onClick: () => void\n loading?: boolean\n className?: string\n}\n\nexport const FaqListItem: React.FC<FaqListItemProps> = ({\n question,\n onClick,\n loading = false,\n className,\n}) => {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={loading}\n style={{ backgroundColor: '#E6E5E3' }}\n className={classNames(\n 'w-full text-center p-4 rounded-xl text-charcoal font-medium transition-colors focus-ring',\n {\n 'hover:brightness-95 active:brightness-90': !loading,\n 'opacity-50 cursor-not-allowed': loading,\n },\n className\n )}\n >\n {question}\n </button>\n )\n}\n","import React from 'react'\n\nimport { Avatar } from '../Avatar'\n\nimport { FaqListItem } from './FaqListItem'\n\nexport interface Faq {\n id: string\n question: string\n answer: string\n enabled: boolean\n order?: number | null\n}\n\nexport interface FaqListProps {\n faqs: Faq[]\n onFaqClick: (faqId: string) => void\n loadingFaqId?: string | null\n headerText?: string\n className?: string\n avatarImage?: string\n avatarName?: string\n}\n\nexport const FaqList: React.FC<FaqListProps> = ({\n faqs,\n onFaqClick,\n loadingFaqId,\n headerText,\n className,\n avatarImage,\n avatarName,\n}) => {\n const enabledFaqs = faqs\n .filter((faq) => faq.enabled)\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0))\n\n if (enabledFaqs.length === 0) {\n return null\n }\n\n return (\n <div className={className}>\n <div className=\"flex gap-3 items-end\">\n {/* Avatar at bottom-left, outside grey background */}\n {(avatarImage || avatarName) && (\n <div className=\"flex-none\">\n <Avatar\n id={avatarName || 'account'}\n name={avatarName || 'Account'}\n image={avatarImage}\n size={24}\n shape=\"circle\"\n />\n </div>\n )}\n\n {/* FAQs with grey background */}\n <div\n className=\"flex-1 flex flex-col gap-3 rounded-lg p-4\"\n style={{ backgroundColor: '#F1F0EE' }}\n >\n {headerText && (\n <p className=\"text-md text-charcoal mb-4\">{headerText}</p>\n )}\n {enabledFaqs.map((faq) => (\n <FaqListItem\n key={faq.id}\n question={faq.question}\n onClick={() => onFaqClick(faq.id)}\n loading={loadingFaqId === faq.id}\n />\n ))}\n </div>\n </div>\n </div>\n )\n}\n"],"names":["MessagingContext","createContext","useMessagingContext","useContext","MessagingProvider","children","user","serviceConfig","apiKey","capabilities","debug","debugLog","useCallback","message","args","service","setService","useState","client","setClient","isConnected","setIsConnected","isLoading","setIsLoading","error","setError","connectingRef","useRef","prevPropsRef","renderCountRef","useEffect","currentRender","newService","StreamChatService","connectedUserRef","_a","_b","streamClient","err","errorMessage","refreshConnection","contextValue","React","jsx","Chat","useMessaging","restorePendingMessages","channel","pendingMessages","pending","ChannelListContext","ChannelListProvider","useChannelListContext","useChannelStar","isChannelStarred","setIsChannelStarred","handleMemberUpdate","event","getDaysDifference","date1","date2","d1","diffTime","formatRelativeTime","date","now","daysDiff","normalizeLanguageCode","language","getMessageDisplayText","viewerLanguage","fallbackText","normalizedLanguage","UUID_REGEX","isUuidLike","value","isUsableDisplayValue","userId","trimmed","resolveParticipantDisplayName","EMOJIS","hashString","str","hash","i","char","getAvatarEmoji","id","index","Avatar","image","size","className","starred","shape","dmAgentEnabled","emoji","fontSizeClass","aiBorderWidth","borderStyle","avatarInner","classNames","jsxs","StarIcon","SparkleIcon","isTipMessage","isPaidMessage","isChatbotMessage","isAttachmentMessage","isTipOrPaidMessage","isTipOnlyMessage","MessageTag","standalone","isMyMessage","hasAttachment","isTipOrPaid","isChatbot","amountText","label","GiftIcon","isSenderAttachmentVariant","chatbotLabel","chatbotClassName","icon","Fragment","CustomChannelPreview","unread","selectedChannel","onChannelSelect","renderMessagePreview","isSelected","handleClick","handleKeyDown","isActivationKey","isRepeatedKeydown","participant","member","participantName","participantImage","lastMessage","messages","lastMessageText","displayText","attachment","lastMessageTime","isLastMessageFromChatbot","messagePreview","unreadCount","_c","DEFAULT_SORT","ChannelList","filters","allowNewMessagesFromUnfilteredChannels","onMessageNew","onAddedToChannel","channelRenderFilterFn","sort","customEmptyStateIndicator","wrappedChannelRenderFilterFn","channels","StreamChannelList","ActionButton","variant","rest","IconButton","CloseButton","onClick","XIcon","ChannelInfoDialog","dialogRef","onClose","participantDisplayName","followerStatusLabel","onLeaveConversation","onBlockParticipant","showDeleteConversation","onDeleteConversationClick","onBlockParticipantClick","onReportParticipantClick","customProfileContent","customChannelActions","isParticipantBlocked","setIsParticipantBlocked","isLeaving","setIsLeaving","isUpdatingBlockStatus","setIsUpdatingBlockStatus","checkIsParticipantBlocked","isBlocked","handleLeaveConversation","actingUserId","handleBlockUser","handleUnblockUser","handleReportUser","participantUsername","participantSecondary","participantId","_d","e","SpinnerGapIcon","SignOutIcon","ProhibitInsetIcon","FlagIcon","CustomDateSeparator","props","DateSeparator","VOTE_UP","VOTE_DOWN","getVoteFromReactions","ownReactions","r","useMessageVote","useChannelStateContext","useChatContext","selected","useMemo","voteUp","voteDown","ComposerCardLazy","SentCardLazy","ReceivedCardLazy","LockedAttachmentFallback","Composer","Suspense","Sent","Received","Creator","Visitor","LockedAttachment","DOCUMENT_ICON_PATTERNS","getSourceType","mimeType","getDocumentIconType","match","pattern","MEDIA_TYPE_ICON","VideoCameraIcon","SpeakerHighIcon","ImageIcon","FileIcon","DOCUMENT_ICON_COMPONENT","FilePdfIcon","FileDocIcon","FileXlsIcon","FileCsvIcon","FilePptIcon","FileZipIcon","FileTextIcon","FileMdIcon","getTypeIcon","sourceType","renderTypeIcon","getClientXFromEvent","MediaPlayer","source","poster","autoPlay","playingProp","loop","controls","showProgress","muted","onContainerClick","playerRef","trackRef","rafRef","prevPlayingPropRef","playing","setPlaying","played","setPlayed","seeking","setSeeking","scrubberHovered","setScrubberHovered","manualPlayRequired","setManualPlayRequired","buffering","setBuffering","initialLoad","setInitialLoad","videoAspect","setVideoAspect","startPlaybackFromGesture","getFraction","track","rect","seekTo","fraction","el","handleTrackPointerDown","tick","onMove","onUp","aspectStyle","aspectClass","scrubberPercent","p","CircleNotchIcon","PlayIcon","PauseIcon","placeholderIconClass","posterShellClass","AttachmentThumbnail","sourceUrl","thumbnailUrl","title","mediaPlayerProps","containedImage","sourceReady","setSourceReady","AttachmentCard","thumbnail","placeholderTitle","detail","statusBadge","action","topLeft","topRight","rootRef","dataTestId","isDark","displayTitle","titleDimmed","formatBytes","bytes","linkCardShellClass","linkThumbnailBg","linkPrimaryText","linkSecondaryText","linkTertiaryText","linkIconColor","LinkCard","text","image_url","og_scrape_url","title_link","rawUrl","url","body","LinkIcon","isLinkAttachment","a","resolveLinkAttachment","triggerDownload","filename","name","res","blob","objectUrl","DownloadAction","busy","setBusy","fallback","DownloadSimpleIcon","resolveMediaFromMessage","videoAttachment","imageAttachment","audioAttachment","fileAttachment","activeAttachment","resolvedUrl","resolvedType","fileSize","MediaMessageRoot","linkAttachment","resolved","MediaMessageCreatorEntry","MediaMessageVisitorEntry","MediaMessage","defaultLockedAttachmentContextValue","customMessageDefaults","CustomMessageContext","CustomMessageProvider","useCustomMessage","key","MessageVoteButtons","onVoteUp","onVoteDown","ThumbsUpIcon","ThumbsDownIcon","CustomMessageWithContext","additionalMessageInputProps","chatbotVotingEnabled","editing","endOfGroup","firstOfGroup","groupedByUser","handleAction","handleOpenThread","handleRetry","highlighted","isMessageAIGenerated","renderText","threadList","isUnlocking","onUnlockClick","onFetchSource","onDownloadClick","isBounceDialogOpen","setIsBounceDialogOpen","reminder","useMessageReminder","voteState","Attachment","DefaultAttachment","EditMessageModal","DefaultEditMessageModal","MessageBlocked","DefaultMessageBlocked","MessageBouncePrompt","DefaultMessageBouncePrompt","MessageDeleted","DefaultMessageDeleted","MessageIsThreadReplyInChannelButtonIndicator","DefaultMessageIsThreadReplyInChannelButtonIndicator","MessageRepliesCountButton","DefaultMessageRepliesCountButton","ReminderNotification","DefaultReminderNotification","StreamedMessageText","DefaultStreamedMessageText","PinIndicator","useComponentContext","messageHasAttachments","hasReactions","messageHasReactions","isAIGenerated","finalAttachments","attachments","raw","filtered","displayMessage","isDateSeparatorMessage","isMessageBlocked","showReplyCountButton","showIsReplyInChannel","allowRetry","isBounced","isMessageBounced","isMine","rootClassName","poll","isTipOnly","isAttachment","hasRenderableAttachments","useAttachmentFooterChatbotTag","MessageBounceModal","_e","_f","_g","_h","_i","_j","_k","_l","_m","_n","MessageText","Poll","MessageErrorIcon","MemoizedCustomMessage","prev","next","areMessageUIPropsEqual","CustomMessage","messageContext","useMessageContext","linkPreviewsManagerStateSelector","state","preview","LinkPreviewsManager","CustomLinkPreviewCard","link","onDismiss","CustomLinkPreviewList","linkPreviewsManager","useMessageComposer","stateLinkPreviews","useStateStore","handleDismiss","linkPreview","DefaultSendButton","sendMessage","disabled","ArrowUpIcon","CustomMessageInputInner","isFrozen","handleSubmit","useMessageInputContext","SendButtonFromContext","SendButton","hasSendableData","useMessageComposerHasSendableData","SimpleAttachmentSelector","QuotedMessagePreview","AttachmentPreviewList","TextareaComposer","CustomMessageInput","renderActions","MessageInput","DM_AGENT_SYSTEM_TYPES","DM_AGENT_SYSTEM_MESSAGE_FALLBACK_TEXT","AGE_SAFETY_SYSTEM_TYPES","AGE_SAFETY_SYSTEM_MESSAGE_FALLBACK_TEXT","AGE_SAFETY_SYSTEM_MESSAGE_EMPHASIS","AGE_SAFETY_SYSTEM_MESSAGE_URL","isDmAgentSystemType","isAgeSafetySystemType","getCustomSystemMessageVariant","metadataCustomType","fallbackType","renderAgeSafetyMessageText","messageText","emphasisIndex","emphasisEndIndex","CustomSystemMessage","isDateHidden","customSystemMessageVariant","MessageTimestamp","ProhibitIcon","DmAgentEnabledContext","Circle","cx","AI_ACTIVE_STATES","AIStates","CustomTypingIndicator","channelConfig","thread","typing","useTypingContext","aiState","useAIState","agentUser","findOtherChannelUser","TypingBubble","typingInChannel","parent_id","typingInThread","typingUsers","typingUser","memberUser","avatarId","avatarName","avatarImage","testId","selfId","members","ChannelEmptyState","Loading","LoadingState","ICON_BTN_CLASS","DM_AGENT_HEADER_HELPER_TEXT","CustomChannelHeader","onBack","showBackButton","onShowInfo","canShowInfo","showStarButton","getParticipantDisplayName","myUserId","isStarred","handleStarClick","ArrowLeftIcon","CaretRightIcon","DotsThreeIcon","ChannelViewInner","renderMessageInputActions","renderConversationFooter","renderChannelBanner","renderMessage","infoDialogRef","currentMember","currentUserIsAccount","participantIsAccount","showDmAgentHeader","channelExtraData","handleShowInfo","handleCloseInfo","MessageOverride","messageNode","WithComponents","Window","MessageList","ChannelView","CustomChannelEmptyState","messageMetadata","onMessageSent","sendButton","getParticipantDisplayNameProp","doSendMessageRequest","_channel","options","agentPaused","shouldSuppressNotifications","finalMessage","finalOptions","response","Channel","ChatBubblesIllustration","EmptyState","hasChannels","channelsLoaded","ErrorState","MessagingShell","initialParticipantFilter","initialParticipantData","showChannelList","channelListCustomEmptyStateIndicator","setSelectedChannel","setHasChannels","setChannelsLoaded","directConversationMode","setDirectConversationMode","directConversationError","setDirectConversationError","channelFilters","syncedRef","syncChannels","createErr","handleChannelSelect","handleBackToChannelList","handleBlockParticipant","isChannelSelected","SCHEME_PATTERN","SAFE_SCHEMES","normalizeExternalHref","schemeMatch","scheme","BUTTON_CLASS_BY_VARIANT","CardCta","cta","normalizedHref","TITLE_CLASS_BY_VARIANT","TITLE_DIMMED_CLASS","SECONDARY_CLASS_BY_VARIANT","CardBody","description","appIcon","trailingAction","hasTitle","hasDescription","trimmedUrl","hasUrl","hasCta","titleClass","secondaryClass","SHELL_CLASS","CardShell","href","ariaLabel","bgClassName","isInteractive","corner","PLACEHOLDER_BG","PLACEHOLDER_ICON","isPlayableAudio","isPlayableMedia","AUDIO_BG_CLASS","CardThumbnail","isPlayableVideo","ComposerCard","layout","onEditClick","isClassic","isAudio","dismissButton","editButton","PencilSimpleIcon","ReceivedCard","isPlayingMedia","normalizedUrl","shellHref","shellOnClick","audioBg","wrappedCta","SentCard","LinkAttachment","BUBBLE_BG_BY_VARIANT","BUBBLE_TEXT_BY_VARIANT","BUBBLE_BORDER_BY_VARIANT","sideForVariant","CORNER_CLASSES_BY_SIDE_AND_POSITION","Bubble","bordered","groupPosition","hasText","cornerClasses","DismissButton","bubbleVariantForState","bubbleGroupPositionFromStream","resolveItems","src","items","NativeAudioPlayer","item","preload","AudioAttachmentRow","showDismiss","resolvedItems","resolvedPreload","AudioComposer","AudioSent","AudioReceived","AudioAttachment","formatFileSize","EXTENSION_LABEL_BY_DOCUMENT_TYPE","getFileExtensionLabel","lastDot","ext","docType","docLabel","subtype","buildCompactMetaLabel","filenameFromUrl","last","META_CLASS_BY_VARIANT","ICON_BG_BY_VARIANT","ICON_COLOR_BY_VARIANT","CompactDocumentRow","onActivate","activateLabel","resolvedTitle","metaLabel","iconTile","textBlock","FileRowButton","resolvedFilename","FileAttachmentRow","handleActivate","downloadIcon","FileComposer","FileSent","FileReceived","FileAttachment","CarouselNav","onPrev","onNext","prevLabel","nextLabel","CaretLeftIcon","iconOnly","tone","onTriggered","showIconOnly","iconProps","clamp","min","max","useCarousel","length","initialIndex","open","safeInitial","setIndex","onKey","active","ViewerShell","counter","actions","closeButtonRef","dialog","previouslyFocused","ImageViewer","TILE_SHELL","MediaStackGrid","tiles","onTileActivate","maxVisible","total","visible","overflow","renderTile","tile","extra","sharedClass","isLastWithOverflow","useViewer","viewerOpen","setViewerOpen","viewerIndex","setViewerIndex","closeViewer","tileFromItem","totalCount","fallbackLoading","alt","buildViewerItems","ImageComposerInner","loading","ImageBubbleRow","ImageComposer","ImageSent","ImageReceived","ImageAttachment","PdfViewer","iframeSrc","withPdfViewerParams","hashIndex","base","existing","params","PdfRowButton","PdfAttachmentRow","viewerItems","itemFilename","PdfComposer","PdfSent","PdfReceived","PdfAttachment","VideoViewer","PlayBadge","PosterTile","it","VideoComposerInner","VideoBubbleRow","VideoComposer","VideoSent","VideoReceived","VideoAttachment","MessageAttachment","FaqListItem","question","FaqList","faqs","onFaqClick","loadingFaqId","headerText","enabledFaqs","faq","b"],"mappings":"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"}