76 lines
2.5 KiB
TypeScript
76 lines
2.5 KiB
TypeScript
import { type ClassValue, clsx } from "clsx";
|
|
import { twMerge } from "tailwind-merge";
|
|
|
|
export function cn(...inputs: ClassValue[]) {
|
|
return twMerge(clsx(inputs));
|
|
}
|
|
|
|
export function formatDuration(seconds: number): string {
|
|
const h = Math.floor(seconds / 3600);
|
|
const m = Math.floor((seconds % 3600) / 60);
|
|
const s = Math.floor(seconds % 60);
|
|
const frames = Math.floor((seconds % 1) * 24); // rough
|
|
if (h > 0) return `${h}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
|
|
return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
|
|
}
|
|
|
|
export function formatTimecode(seconds: number, fps: number = 24): string {
|
|
const h = Math.floor(seconds / 3600);
|
|
const m = Math.floor((seconds % 3600) / 60);
|
|
const s = Math.floor(seconds % 60);
|
|
const f = Math.floor((seconds % 1) * fps);
|
|
if (h > 0) {
|
|
return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}:${String(f).padStart(2, "0")}`;
|
|
}
|
|
return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}:${String(f).padStart(2, "0")}`;
|
|
}
|
|
|
|
export function frameToTimecode(frame: number, fps: number): string {
|
|
return formatTimecode(frame / fps, fps);
|
|
}
|
|
|
|
export function formatFileSize(bytes: number | bigint): string {
|
|
const b = typeof bytes === "bigint" ? Number(bytes) : bytes;
|
|
if (b < 1024) return `${b} B`;
|
|
if (b < 1024 * 1024) return `${(b / 1024).toFixed(1)} KB`;
|
|
if (b < 1024 * 1024 * 1024) return `${(b / 1024 / 1024).toFixed(1)} MB`;
|
|
return `${(b / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
}
|
|
|
|
export function formatRelativeDate(date: Date | string): string {
|
|
const d = typeof date === "string" ? new Date(date) : date;
|
|
const now = new Date();
|
|
const diff = now.getTime() - d.getTime();
|
|
const minutes = Math.floor(diff / 60000);
|
|
const hours = Math.floor(diff / 3600000);
|
|
const days = Math.floor(diff / 86400000);
|
|
|
|
if (minutes < 1) return "just now";
|
|
if (minutes < 60) return `${minutes}m ago`;
|
|
if (hours < 24) return `${hours}h ago`;
|
|
if (days < 7) return `${days}d ago`;
|
|
return d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
}
|
|
|
|
export function getInitials(name: string | null | undefined): string {
|
|
if (!name) return "?";
|
|
return name
|
|
.split(" ")
|
|
.map((n) => n[0])
|
|
.join("")
|
|
.toUpperCase()
|
|
.slice(0, 2);
|
|
}
|
|
|
|
export function slugify(text: string): string {
|
|
return text
|
|
.toLowerCase()
|
|
.replace(/[^\w\s-]/g, "")
|
|
.replace(/[\s_-]+/g, "-")
|
|
.replace(/^-+|-+$/g, "");
|
|
}
|
|
|
|
export function versionLabel(n: number): string {
|
|
return `v${String(n).padStart(3, "0")}`;
|
|
}
|