import { TEmoji } from '@/types' import { clsx, type ClassValue } from 'clsx' import { twMerge } from 'tailwind-merge' function parseNativeEmoji(unified: string): string { return String.fromCodePoint(...unified.split('-').map((h) => parseInt(h, 16))) } export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } export function truncateText(text: string, maxWords: number): string { if (!text) return '' const words = text.trim().split(/\s+/) if (words.length <= maxWords) return text return words.slice(0, maxWords).join(' ') + '...' } /** * Remove emoji characters from a string * This regex covers most emoji ranges including: * - Emoticons (😀-🙏) * - Misc Symbols & Pictographs (🚀-đŸ—ŋ) * - Transport & Map Symbols (🚁-đŸ›ŋ) * - Enclosed characters (â“‚ī¸, ÂŠī¸, etc.) * - Regional indicator symbols (flags) * - And other emoji ranges */ export function removeEmojis(text: string): string { if (!text) return '' // Comprehensive emoji regex pattern covering major emoji Unicode ranges const emojiRegex = /[\u{1F600}-\u{1F64F}]|[\u{1F300}-\u{1F5FF}]|[\u{1F680}-\u{1F6FF}]|[\u{1F1E0}-\u{1F1FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]|[\u{1F900}-\u{1F9FF}]|[\u{1F018}-\u{1F270}]|[\u{238C}-\u{2454}]|[\u{20D0}-\u{20FF}]|[\u{FE00}-\u{FE0F}]|[\u{FE20}-\u{FE2F}]|[\u{E0020}-\u{E007F}]/gu return text.replace(emojiRegex, '').trim().replace(/\s+/g, ' ') } export function isSafari() { if (typeof window === 'undefined' || !window.navigator) return false const ua = window.navigator.userAgent const vendor = window.navigator.vendor return /Safari/.test(ua) && /Apple Computer/.test(vendor) && !/Chrome/.test(ua) } export function isAndroid() { if (typeof window === 'undefined' || !window.navigator) return false const ua = window.navigator.userAgent return /android/i.test(ua) } export function isTorBrowser() { if (typeof window === 'undefined' || !window.navigator) return false const ua = window.navigator.userAgent return /torbrowser/i.test(ua) } export function isTouchDevice() { if (typeof window === 'undefined' || !window.navigator) return false return 'ontouchstart' in window || navigator.maxTouchPoints > 0 } export function isInViewport(el: HTMLElement) { const rect = el.getBoundingClientRect() return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ) } export function isPartiallyInViewport(el: HTMLElement) { const rect = el.getBoundingClientRect() return ( rect.top < (window.innerHeight || document.documentElement.clientHeight) && rect.bottom > 0 && rect.left < (window.innerWidth || document.documentElement.clientWidth) && rect.right > 0 ) } export function isSupportCheckConnectionType() { if (typeof window === 'undefined' || !(navigator as any).connection) return false return typeof (navigator as any).connection.type === 'string' } export function isEmail(email: string) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) } export function isDevEnv() { return process.env.NODE_ENV === 'development' } export function parseEmojiPickerUnified(unified: string): string | TEmoji | undefined { if (unified.startsWith(':')) { const secondColonIndex = unified.indexOf(':', 1) if (secondColonIndex < 0) return undefined const shortcode = unified.slice(1, secondColonIndex) const url = unified.slice(secondColonIndex + 1) return { shortcode, url } } else { return parseNativeEmoji(unified) } }