// AKAI Utils — shared utility functions function esc(s) { if (!s) return '' return s.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, ''') } function sanitize(s) { return s.replace(/[^a-zA-Z0-9_-]/g, '_') } function formatBytes(bytes) { if (bytes === 0) return '0 B' const k = 1024 const sizes = ['B', 'KB', 'MB', 'GB'] const i = Math.floor(Math.log(bytes) / Math.log(k)) return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i] } function formatTime(secs) { const m = Math.floor(secs / 60) const s = Math.floor(secs % 60) return m + ':' + s.toString().padStart(2, '0') } function groupWavs(paths) { const libs = {} for (const p of paths) { const parts = p.split('/') let lib, prog, file if (parts.length === 1) { lib = ''; prog = parts[0]; file = parts[0] } else if (parts.length === 2) { lib = parts[0]; prog = parts[0]; file = parts[1] } else { lib = parts[0]; prog = parts[1]; file = parts[parts.length - 1] } if (!libs[lib]) libs[lib] = {} if (!libs[lib][prog]) libs[lib][prog] = [] libs[lib][prog].push({ fullPath: p, name: file.replace(/\.wav$/i, '') }) } return libs } function cleanName(name) { return name.replace(/_+/g, ' ').trim() } if (typeof module !== 'undefined' && module.exports) { module.exports = { esc, sanitize, formatBytes, formatTime, groupWavs, cleanName } }