/** * Formatters - Data formatting utilities * Provides functions for formatting file names, tensor shapes, file sizes, * data types, and tensor info for display in the UI. * Requirements: 2.5, 5.6, 7.3 */ const Formatters = (function () { // ─── File Name Formatting ───────────────────────────────────────────────── /** * Format a file path into a human-readable display name. * Strips directory separators and returns only the base file name. * e.g. "models/jsl/model.onnx" → "model.onnx" * @param {string} filePath * @returns {string} */ function formatFileName(filePath) { if (!filePath) return 'Unknown'; const base = filePath.replace(/\\/g, '/').split('/').pop() || filePath; return base; } /** * Format a model name for display: strip extension and replace * underscores/hyphens with spaces, then title-case. * e.g. "my_model.onnx" → "My Model" * @param {string} filePath * @returns {string} */ function formatModelName(filePath) { if (!filePath) return 'Unknown'; const base = formatFileName(filePath); const noExt = base.replace(/\.onnx$/i, ''); return noExt .replace(/[_-]+/g, ' ') .replace(/\b\w/g, c => c.toUpperCase()); } // ─── Tensor Shape Formatting ────────────────────────────────────────────── /** * Format a tensor shape array into a readable string. * Dynamic dimensions (null, -1, 0, or string) are shown as "?". * e.g. [1, 3, 224, 224] → "[1, 3, 224, 224]" * e.g. [-1, 3, 224, 224] → "[?, 3, 224, 224]" * @param {Array} shape * @returns {string} */ function formatShape(shape) { if (!Array.isArray(shape) || shape.length === 0) return '[]'; const dims = shape.map(d => { if (d === null || d === undefined) return '?'; if (typeof d === 'string') return d || '?'; if (typeof d === 'number' && (d < 0 || d === 0)) return '?'; return String(d); }); return '[' + dims.join(', ') + ']'; } // ─── File Size Formatting ───────────────────────────────────────────────── /** * Format a byte count into a human-readable size string. * e.g. 1024 → "1.00 KB", 1048576 → "1.00 MB" * @param {number} bytes * @param {number} [decimals=2] * @returns {string} */ function formatBytes(bytes, decimals = 2) { if (bytes == null || isNaN(bytes)) return 'Unknown'; if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(Math.abs(bytes)) / Math.log(k)); const idx = Math.min(i, sizes.length - 1); const value = bytes / Math.pow(k, idx); return value.toFixed(decimals) + ' ' + sizes[idx]; } // ─── Data Type Formatting ───────────────────────────────────────────────── /** * ONNX data type code → human-readable name mapping. */ const DATA_TYPE_MAP = { 0: 'UNDEFINED', 1: 'FLOAT', 2: 'UINT8', 3: 'INT8', 4: 'UINT16', 5: 'INT16', 6: 'INT32', 7: 'INT64', 8: 'STRING', 9: 'BOOL', 10: 'FLOAT16', 11: 'DOUBLE', 12: 'UINT32', 13: 'UINT64', 14: 'COMPLEX64', 15: 'COMPLEX128', 16: 'BFLOAT16', }; /** * Format an ONNX data type code or string into a readable label. * Accepts numeric codes (1–16) or string names. * @param {number|string} code * @returns {string} */ function formatDataType(code) { if (code == null) return 'Unknown'; if (typeof code === 'number') { return DATA_TYPE_MAP[code] || `Type(${code})`; } // Already a string — normalise to upper-case const upper = String(code).toUpperCase(); // Check if it matches a known name const known = Object.values(DATA_TYPE_MAP); return known.includes(upper) ? upper : code; } // ─── Tensor Info Formatting ─────────────────────────────────────────────── /** * Format a TensorInfo object into a concise one-line description. * e.g. "input_0: FLOAT [1, 3, 224, 224]" * @param {{ name: string, shape: Array, dataType: number|string }} tensor * @returns {string} */ function formatTensorInfo(tensor) { if (!tensor) return 'Unknown'; const name = tensor.name || 'unnamed'; const dtype = formatDataType(tensor.dataType); const shape = formatShape(tensor.shape); return `${name}: ${dtype} ${shape}`; } /** * Format element count for an initializer. * @param {Array} shape * @returns {number} */ function calcElementCount(shape) { if (!Array.isArray(shape) || shape.length === 0) return 0; return shape.reduce((acc, d) => acc * (d > 0 ? d : 1), 1); } // ─── Number Formatting ──────────────────────────────────────────────────── /** * Format a large integer with locale-aware thousands separators. * @param {number} n * @returns {string} */ function formatNumber(n) { if (n == null || isNaN(n)) return '0'; return n.toLocaleString(); } // ─── Public API ─────────────────────────────────────────────────────────── return { formatFileName, formatModelName, formatShape, formatBytes, formatDataType, formatTensorInfo, calcElementCount, formatNumber, DATA_TYPE_MAP, }; })(); // Export for global access in vanilla JS context window.Formatters = Formatters;