export type BurstPoint = { x: number; y: number; maskRadius?: number; }; export function getBurstPointFromElement(element: Element): BurstPoint { const rect = element.getBoundingClientRect(); return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2, maskRadius: Math.max(rect.width, rect.height) * 0.5, }; } export function getElementCenter(element: Element): BurstPoint { return getBurstPointFromElement(element); } export function findEmojiOriginInMessage( targetEventId: string, emoji: string ): BurstPoint | undefined { const message = document.querySelector(`[data-message-id="${CSS.escape(targetEventId)}"]`); if (!message) return undefined; const matches = message.querySelectorAll('[data-emoticon]'); for (const element of matches) { if (element.getAttribute('data-emoticon') === emoji) { return getBurstPointFromElement(element); } } return undefined; } export function getMessageFallbackOrigin(targetEventId: string): BurstPoint | undefined { const message = document.querySelector(`[data-message-id="${CSS.escape(targetEventId)}"]`); if (!message) return undefined; const jumboBody = message.querySelector('[data-jumbo-emoji]'); if (jumboBody) { return getBurstPointFromElement(jumboBody); } const body = message.querySelector('[data-message-body]'); if (body) { return getBurstPointFromElement(body); } return getBurstPointFromElement(message); } export function resolveBurstOrigin( targetEventId: string | undefined, emojis: string[], explicitOrigin?: BurstPoint ): BurstPoint { if (explicitOrigin) return explicitOrigin; const primaryEmoji = emojis[0]; if (targetEventId && primaryEmoji) { const emojiOrigin = findEmojiOriginInMessage(targetEventId, primaryEmoji); if (emojiOrigin) return emojiOrigin; const messageOrigin = getMessageFallbackOrigin(targetEventId); if (messageOrigin) return messageOrigin; } return { x: window.innerWidth / 2, y: window.innerHeight * 0.35, }; }