const WARNING_KINDS = ['alpha_error_exceeds_policy', 'transparency_presence_changed', 'quality_below_policy'];
const BROWSER_FORMATS = ['png', 'jpeg', 'webp', 'gif'];
function formatNumber(value, locale) { return new Intl.NumberFormat(locale).format(value); }
function formatSize(value, locale = 'en-US') {
if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) return '—';
if (value < 1024) return `${formatNumber(value, locale)} B`;
const units = ['KiB', 'MiB', 'GiB', 'TiB', 'PiB'];
let amount = value / 1024, unit = 0;
while (amount >= 1024 && unit < units.length - 1) { amount /= 1024; unit++; }
return `${new Intl.NumberFormat(locale, { minimumFractionDigits: 1, maximumFractionDigits: 2 }).format(amount)} ${units[unit]}`;
}
function formatPercent(ratio) { return Number.isFinite(ratio) ? `${(ratio * 100).toFixed(1)}%` : '—'; }
function assetUrl(path) {
if (typeof path !== 'string') return null;
const parts = path.replaceAll('\\', '/').split('/');
if (!['previews', 'originals', 'candidates'].includes(parts[0]) || parts.length < 2
|| parts.some(p => !p || p === '.' || p === '..' || [...p].some(ch => ch.charCodeAt(0) < 32 || ch === ':'))) return null;
return parts.map(encodeURIComponent).join('/');
}
function basename(path) { return String(path || '').split(/[\\/]/).pop(); }
function warningKind(candidate) {
return candidate && candidate.lossy && !candidate.valid && candidate.artifact
&& WARNING_KINDS.includes(candidate.rejection) ? candidate.rejection : null;
}
function warningKinds(candidate) {
if (!warningKind(candidate)) return [];
const all = Array.isArray(candidate.warnings) ? candidate.warnings.filter(w => WARNING_KINDS.includes(w)) : [];
return all.length ? all : [candidate.rejection];
}
function recommended(resource) {
const index = resource && resource.smallest_candidate;
const candidate = Number.isInteger(index) && index >= 0 ? resource.candidates && resource.candidates[index] : null;
return candidate && candidate.valid && candidate.artifact ? candidate : null;
}
function recommendedSavings(resource) {
const candidate = recommended(resource);
return candidate ? Math.max(0, candidate.savings_bytes || 0) : 0;
}
function hasWarningCandidate(resource) {
return !!(resource && resource.candidates && resource.candidates.some(c => warningKind(c)));
}
function blockedReason(resource, candidate) {
if (!candidate || !candidate.artifact) return 'no_smaller_candidate';
if (!candidate.valid && !warningKind(candidate)) return 'failed_verification';
if (resource.resource && resource.resource.conversion_exclusion) return resource.resource.conversion_exclusion;
const crossing = candidate.format !== resource.resource.format || resource.resource.extension_mismatch;
if (crossing && resource.resource.format_lock) return resource.resource.format_lock;
return '';
}
function displayableInBrowser(format) { return BROWSER_FORMATS.includes(format); }