import { type ClassValue, clsx } from 'clsx' import { twMerge } from 'tailwind-merge' export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } export function formatDate(date: string | Date): string { const d = new Date(date) return d.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', }) } export function formatNumber(n: number): string { if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M` if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K` return n.toLocaleString() } export function maskApiKey(key?: string): string { if (!key) return '-' if (key.length <= 8) return '****' return `${key.slice(0, 4)}${'*'.repeat(key.length - 8)}${key.slice(-4)}` } export function sleep(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)) } /** 从 SWR error 中提取用户可见消息,过滤 abort 错误 */ export function getSwrErrorMessage(err: unknown): string | undefined { if (!err) return undefined if (err instanceof DOMException && err.name === 'AbortError') return undefined if (err instanceof Error) { if (err.name === 'AbortError' || err.message?.includes('aborted')) return undefined return err.message } return String(err) }