/** * Format bytes to human readable string */ export const formatBytes = (bytes: number, decimals = 2): string => { if (bytes === 0) return '0 B'; const k = 1024; const dm = decimals < 0 ? 0 : decimals; const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; }; /** * Format duration in milliseconds to human readable string */ export const formatDuration = (ms: number): string => { if (ms < 1000) return `${ms}ms`; if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`; if (ms < 3600000) { const minutes = Math.floor(ms / 60000); const seconds = Math.floor((ms % 60000) / 1000); return `${minutes}m ${seconds}s`; } const hours = Math.floor(ms / 3600000); const minutes = Math.floor((ms % 3600000) / 60000); return `${hours}h ${minutes}m`; }; /** * Format number with commas */ export const formatNumber = (num: number): string => { return num.toLocaleString(); }; /** * Format currency */ export const formatCurrency = (amount: number, currency = 'USD', locale = 'en-US'): string => { return new Intl.NumberFormat(locale, { style: 'currency', currency, }).format(amount); }; /** * Format percentage */ export const formatPercentage = (value: number, decimals = 1): string => { return `${(value * 100).toFixed(decimals)}%`; }; /** * Format timestamp to relative time */ export const formatRelativeTime = (timestamp: number): string => { const now = Date.now(); const diff = now - timestamp; if (diff < 60000) return 'just now'; if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`; if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`; if (diff < 604800000) return `${Math.floor(diff / 86400000)}d ago`; return new Date(timestamp).toLocaleDateString(); }; /** * Truncate text with ellipsis */ export const truncateText = (text: string, maxLength: number, suffix = '...'): string => { if (text.length <= maxLength) return text; return text.substring(0, maxLength - suffix.length) + suffix; }; /** * Format model name for display */ export const formatModelName = (model: string): string => { // Remove common prefixes const cleaned = model .replace(/^(openai\/)/, '') .replace(/^(anthropic\/)/, '') .replace(/^(google\/)/, '') .replace(/^(deepseek\/)/, ''); // Capitalize words return cleaned .split('-') .map(word => word.charAt(0).toUpperCase() + word.slice(1)) .join(' '); }; /** * Format file size with appropriate unit */ export const formatFileSize = (bytes: number): string => { if (bytes === 0) return '0 Bytes'; const k = 1000; // Use 1000 for file sizes (not 1024) const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`; }; /** * Format date to ISO string for display */ export const formatISODate = (date: Date | string | number): string => { const d = new Date(date); return d.toISOString().split('T')[0]; };