Spaces:
Running
Running
| /** | |
| * 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<number|string|null>} 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<number>} 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; | |