/* ===================================================================== Vignette — 포맷 유틸 (시간/타이머/숫자). 전부 tabular-nums 전제. ===================================================================== */ /** 초 → "M:SS" (세션 타이머용, 0~59분). 1시간 넘으면 "H:MM:SS". */ export function formatElapsed(totalSeconds: number): string { const s = Math.max(0, Math.floor(totalSeconds)); const hours = Math.floor(s / 3600); const minutes = Math.floor((s % 3600) / 60); const seconds = s % 60; const pad = (n: number) => String(n).padStart(2, "0"); if (hours > 0) return `${hours}:${pad(minutes)}:${pad(seconds)}`; return `${minutes}:${pad(seconds)}`; } /** 초 → "MM:SS" (항상 2자리 분). 자막 타임코드용. */ export function formatTimecode(totalSeconds: number): string { const s = Math.max(0, Math.floor(totalSeconds)); const minutes = Math.floor(s / 60); const seconds = s % 60; const pad = (n: number) => String(n).padStart(2, "0"); return `${pad(minutes)}:${pad(seconds)}`; } /** 초 → "12분", "1시간 5분" 등 한국어 사람친화 표기. */ export function formatDurationKo(totalSeconds: number): string { const s = Math.max(0, Math.floor(totalSeconds)); const hours = Math.floor(s / 3600); const minutes = Math.floor((s % 3600) / 60); if (hours > 0 && minutes > 0) return `${hours}시간 ${minutes}분`; if (hours > 0) return `${hours}시간`; if (minutes > 0) return `${minutes}분`; return `${s}초`; } /** 정수에 천단위 콤마 (tabular 표기 전제). */ export function formatNumber(n: number): string { return new Intl.NumberFormat("ko-KR").format(n); } /** 0~1 비율 → 백분율 정수 문자열 "72%". */ export function formatPercent(ratio: number, fractionDigits = 0): string { const pct = clamp01(ratio) * 100; return `${pct.toFixed(fractionDigits)}%`; } /** 변화량 표기: +6 / -3 / 0 (부호 명시). */ export function formatDelta(delta: number): string { if (delta > 0) return `+${delta}`; return String(delta); } /** 추세 방향 (화살표는 컴포넌트가 토큰 색으로 렌더). 이모지 아님. */ export type Trend = "up" | "down" | "flat"; export function trendOf(delta: number, deadband = 0): Trend { if (delta > deadband) return "up"; if (delta < -deadband) return "down"; return "flat"; } /** ISO 날짜/Date → "6/24", "2026-06-24" 등. */ export function formatDateShort(input: string | Date): string { const d = typeof input === "string" ? new Date(input) : input; if (Number.isNaN(d.getTime())) return ""; return `${d.getMonth() + 1}/${d.getDate()}`; } export function formatDateISO(input: string | Date): string { const d = typeof input === "string" ? new Date(input) : input; if (Number.isNaN(d.getTime())) return ""; const pad = (n: number) => String(n).padStart(2, "0"); return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`; } /** 0~1 클램프. */ export function clamp01(v: number): number { if (v < 0) return 0; if (v > 1) return 1; return v; } /** 임의 범위 클램프. */ export function clamp(v: number, min: number, max: number): number { return Math.min(Math.max(v, min), max); }