import { type ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } export function formatFileSize(bytes: number): string { if (bytes === 0) return "0 Bytes"; const k = 1024; const sizes = ["Bytes", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; } export function formatTimestamp(timestamp: string): string { return new Date(timestamp).toLocaleString(); } export function formatRelativeTime(dateString: string): string { const date = new Date(dateString); const now = new Date(); const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000); if (diffInSeconds < 60) { return "just now"; } const diffInMinutes = Math.floor(diffInSeconds / 60); if (diffInMinutes < 60) { return `${diffInMinutes} minute${diffInMinutes > 1 ? "s" : ""} ago`; } const diffInHours = Math.floor(diffInMinutes / 60); if (diffInHours < 24) { return `${diffInHours} hour${diffInHours > 1 ? "s" : ""} ago`; } const diffInDays = Math.floor(diffInHours / 24); if (diffInDays < 7) { return `${diffInDays} day${diffInDays > 1 ? "s" : ""} ago`; } // For older dates, show the actual date return date.toLocaleDateString(); } export function generateId(): string { return Math.random().toString(36).substr(2, 9); } export function debounce any>( func: T, delay: number ): (...args: Parameters) => void { let timeoutId: NodeJS.Timeout; return (...args: Parameters) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => func(...args), delay); }; } /** * Limit JSON object depth for preview performance * @param obj - Object to limit depth * @param maxDepth - Maximum depth to traverse (default: 1) * @returns Depth-limited object */ export function limitJsonDepth(obj: any, maxDepth: number = 1): any { if (maxDepth <= 0) { if (typeof obj === 'object' && obj !== null) { if (Array.isArray(obj)) { return `[Array(${obj.length})]`; } return `{Object with ${Object.keys(obj).length} keys}`; } return obj; } if (typeof obj !== 'object' || obj === null) { return obj; } if (Array.isArray(obj)) { return obj.slice(0, 3).map(item => limitJsonDepth(item, maxDepth - 1)) .concat(obj.length > 3 ? [`... ${obj.length - 3} more items`] : []); } const limited: any = {}; const keys = Object.keys(obj); const maxKeys = 5; for (let i = 0; i < Math.min(keys.length, maxKeys); i++) { const key = keys[i]; if (key !== undefined) { limited[key] = limitJsonDepth(obj[key], maxDepth - 1); } } if (keys.length > maxKeys) { limited[`_truncated_${keys.length - maxKeys}_more_keys`] = '...'; } return limited; }