import { encodeHTML } from "./html.js"; import { splitWrappedText } from "./render.js"; /** * Retrieves num with suffix k(thousands) precise to given decimal places. * * @param num The number to format. * @param precision The number of decimal places to include. * @returns The formatted number. */ const kFormatter = (num: number, precision?: number): string | number => { const abs = Math.abs(num); const sign = Math.sign(num); if (typeof precision === "number" && !isNaN(precision)) { return (sign * (abs / 1000)).toFixed(precision) + "k"; } if (abs < 1000) { return sign * abs; } return `${sign * parseFloat((abs / 1000).toFixed(1))}k`; }; /** * Convert bytes to a human-readable string representation. * * @param bytes The number of bytes to convert. * @returns The human-readable representation of bytes. * @throws {Error} If bytes is negative or too large. */ const formatBytes = (bytes: number): string => { if (bytes < 0) { throw new Error("Bytes must be a non-negative number"); } if (bytes === 0) { return "0 B"; } const sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB"]; const base = 1024; const i = Math.floor(Math.log(bytes) / Math.log(base)); const unit = sizes[i]; if (unit === undefined) { throw new Error("Bytes is too large to convert to a human-readable string"); } return `${(bytes / Math.pow(base, i)).toFixed(1)} ${unit}`; }; /** * Split text over multiple lines based on the card width. * * @param text Text to split. * @param width Available wrap width in px. * @param fontSize Font size in px. * @param maxLines Maximum number of lines. * @returns Array of lines. */ const wrapTextMultiline = ( text: string, width: number, fontSize: number, maxLines = 3, ): Array => { const wrapped = splitWrappedText(text, fontSize, width); const lines = wrapped .map((line) => encodeHTML(line.trim())) .slice(0, maxLines); // Only consider maxLines lines // Add "..." to the last line if the text exceeds maxLines if (wrapped.length > maxLines) { const lastIndex = maxLines - 1; const lastLine = lines[lastIndex]; if (lastLine !== undefined) { lines[lastIndex] = `${lastLine}...`; } } // Remove empty lines if text fits in less than maxLines lines const multiLineText = lines.filter(Boolean); return multiLineText; }; export { kFormatter, formatBytes, wrapTextMultiline };