/** Browser-safe workspace path normalization shared by Portal and Vite client imports. */ /** Normalize workspace HTML paths so `demo.html` and `public/demo.html` match. */ export function normalizeWorkspaceRelativePath(relativePath) { const parts = String(relativePath ?? '') .replace(/^\/+/, '') .split('/') .filter((part) => part && part !== '.' && part !== '..'); if (parts.length === 0) return ''; if (parts[0].toLowerCase() === 'public') return parts.join('/'); if (parts.length === 1 && parts[0].toLowerCase().endsWith('.html')) return `public/${parts[0]}`; return parts.join('/'); } export function resolvePageWorkspaceRelativePath(snapshot) { if (!snapshot || typeof snapshot !== 'object') return null; return normalizeWorkspaceRelativePath(snapshot.relative_path) || null; } export function resolveAssetWorkspaceRelativePath({ categoryCode = null, originalFilename = null, explicitPath = null, } = {}) { const explicit = normalizeWorkspaceRelativePath(explicitPath); if (explicit) return explicit; const filename = String(originalFilename ?? '') .replace(/\\/g, '/') .replace(/^\/+/, ''); if (!filename) return null; if (/^(?:public|oa)\//i.test(filename)) { return normalizeWorkspaceRelativePath(filename); } const category = String(categoryCode ?? '').trim(); if (category === 'public' || category === 'oa') { return normalizeWorkspaceRelativePath(`${category}/${filename}`); } return null; }