${assets.length?'Try another name or category.':'Upload USD files and their dependencies into assets/ in your Space repository. They will appear here after the Space restarts.'}
`;$$('[data-asset]').forEach(b=>b.onclick=()=>select(b.dataset.asset));$('#empty-setup')?.addEventListener('click',()=>showTab('setup'));if(viewer)for(const a of list.slice(0,limit)){if(generation!==gridVersion)return;if(a.thumbnail||a.previewStatus!=='ready')continue;try{const url=await viewer.thumbnail(a);if(generation!==gridVersion)return;const img=$(`[data-asset="${a.id}"] img`);if(img){img.src=url;img.hidden=false}}catch{}}}
async function select(id){const a=assets.find(a=>a.id===id);if(!a)return;selected=a;const version=++viewVersion;$('#inspector').hidden=false;$('#selected-name').textContent=a.name;$('#selected-path').textContent='assets / '+a.path.replaceAll('/',' / ');$('#format').textContent=a.format;$('#asset-download').href=a.physics?`${a.packages.all}`:`${a.downloadUrl}`;$('#asset-download').firstChild.textContent=a.physics?'Download USD + MJCF ':'Download original USD ';$('#asset-source').hidden=!config.repoUrl;$('#asset-source').href=config.repoUrl?`${config.repoUrl}/blob/main/assets/${pathUrl(a.path)}`:'#';$('#asset-meta').innerHTML=`
`:''}${physicsCard(a)}`;$('#preview-message').hidden=false;$('#preview-message').textContent=a.previewStatus==='queued'?'Preparing this asset’s preview…':a.previewStatus==='unavailable'?(a.previewError||'Preview unavailable. The original USD can still be downloaded.'):'Loading 3D preview…';$('#preview-image').hidden=!a.thumbnail;if(a.thumbnail)$('#preview-image').src=`${a.thumbnailUrl}`;$('#viewer-state').textContent=a.previewStatus==='ready'?'Interactive 3D':'Original available';renderGrid();viewer?.clear();if(a.previewStatus==='ready'&&viewer){try{await viewer.show(a);if(version===viewVersion){$('#preview-message').hidden=true;$('#preview-image').hidden=true}}catch{if(version===viewVersion)$('#preview-message').textContent='3D preview could not load. Download the original USD to inspect it.'}}else if(!viewer&&a.previewStatus==='ready')$('#preview-message').textContent='WebGL preview unavailable in this browser. The original file is available below.'}
const g=kg=>kg==null?'—':kg<1?`${(kg*1000).toFixed(kg<0.1?1:0)} g`:`${kg.toFixed(2)} kg`;
function physicsCard(a){const p=a.physics;if(!p)return'';const d=p.dimensions_m||{},ref=p.reference,err=ref&&p.mass_kg!=null?Math.abs(p.mass_kg-ref.mass_kg)/ref.mass_kg*100:null;
const rows=[['Mass',`${g(p.mass_kg)}${p.low_kg!=null?` (${g(p.low_kg)}–${g(p.high_kg)})`:''}`],
ref?['YCB published mass',`${g(ref.mass_kg)} · ${err.toFixed(1)}% off`]:null,
p.confidence!=null?['Mass confidence',p.confidence.toFixed(2)]:null,
['Size (L × W × H)',['length_m','width_m','height_m'].map(k=>d[k]!=null?(d[k]*1000).toFixed(1):'?').join(' × ')+' mm'],
['Materials',escape((p.materials||[]).join(', ')||'—')],
['Friction · restitution',`${p.static_friction??'—'} · ${p.restitution??'—'}`],
['Collision',`${p.hulls} convex hulls (⬢ in the viewer)`],
p.com_m?['Centre of mass',p.com_m.map(v=>(v*1000).toFixed(1)).join(', ')+' mm (x, y, z; Z up · ⊕ in the viewer)']:null,
['SimReady profiles',escape((p.profiles||[]).map(x=>x.replace('Prop-Robotics-','')).join(' · ')||'—')],
p.mjcf?['MuJoCo',`MJCF package ↓ · USD only ↓`]:null].filter(Boolean);
return `
Physics
`+rows.map(([k,v])=>`
${k}${v}
`).join('')}
function renderFiles(){$('#files').innerHTML=`
Download the complete assets folder so referenced layers and textures keep their relative paths. To pin a release, replace main with a commit SHA from the Space’s history.
from huggingface_hub import snapshot_download\n\nfolder = snapshot_download(\n repo_id=${escape(JSON.stringify(config.repoId||'your-org/your-space'))},\n repo_type="space",\n revision="main",\n allow_patterns=["assets/**"],\n)
`}
function renderAbout(){$('#about').innerHTML=`COLLECTION CARD
${escape(config.title)}
${escape(config.about)}
Maintained by ${escape(config.owner)}
Files, revisions, membership, and access are managed in this organization’s Hugging Face Space. Visitors download originals directly from this instance.
Asset licensing
${escape(config.license)}. Check the license documents included with the collection before reuse. The bundled Examples folder contains an MIT-licensed calibration cube; uploaded assets retain their owners’ chosen licenses.
Preview fidelity
Previews show mesh geometry at the default time and selected USD variants, using simplified shading and authored display colors when available. Full MDL materials, texture rendering, animation, skeletal deformation, and procedural primitives are not reproduced. Files without a supported preview remain downloadable.
Complete projects
Upload referenced layers, textures, and licenses with the same folder layout. Remote references and dependencies outside assets/ are not bundled. The collection ZIP includes the assets/ folder and its relative contents.
`}
function renderSetup(){const repo=config.repoUrl,template=config.templateUrl;$('#setup').innerHTML=`
FOR ORGANIZATION MAINTAINERS
One Space. Your organization’s assets.
Use this template to publish a library under your own namespace, with your own files, permissions, and revision history.
01 / DUPLICATE
Make it yours
Open the template on Hugging Face, choose “Duplicate this Space” from its menu, and select your organization as owner.
hf auth login\nhf upload your-org/your-space ./your-usd-folder assets --repo-type space
Replace your-org/your-space with your duplicated Space. Hugging Face checks your write access. There are no public upload endpoints or stored access tokens in this app.
Remove the Examples folder when you add your own collection. USD, USDA, USDC, and USDZ files are indexed automatically. Supply a neighboring PNG or JPG for a custom thumbnail. Each preview has a 120-second conversion limit; unsupported or very complex assets remain available as downloads.
This is a CPU Basic Docker Space. Hugging Face currently requires a paid account or organization plan to create or duplicate Docker Spaces; CPU Basic has no hourly compute fee. No GPU, external database, or shared central asset service is required.
`}
async function refresh(){try{const r=await fetch('catalog.json');if(!r.ok)throw new Error();const data=await r.json();const old=selected;assets=data.assets;$('#total-count').textContent=assets.length;$('#index-status').textContent=data.error|| (data.indexing?`Preparing previews · ${assets.filter(a=>a.previewStatus==='ready').length} of ${assets.length} ready`:`${assets.length} source ${assets.length===1?'asset':'assets'} · ${assets.filter(a=>a.previewStatus==='ready').length} interactive previews`);$('#template-callout').hidden=assets.length>0&&!assets.every(a=>a.path.startsWith('Examples/'));categories();if(!selected&&assets.length)await select(assets[0].id);else if(selected){const next=assets.find(a=>a.id===selected.id);if(next&&next.previewStatus!==old.previewStatus)await select(next.id);else renderGrid()}else renderGrid();if(data.indexing)setTimeout(refresh,3000)}catch{$('#index-status').textContent='The library could not be loaded. Refresh the page to retry.'}}
try{config=await fetch('config.json').then(r=>{if(!r.ok)throw new Error();return r.json()});$('#owner').textContent=config.owner;$('#collection-title').textContent=config.title;$('#collection-description').textContent=config.description;$('#license').textContent=config.license;document.title=config.title;document.documentElement.style.setProperty('--accent',config.accent);for(const [id,suffix] of [['source-link','/tree/main'],['community-link','/discussions'],['manage-assets','/tree/main/assets']]){const a=$('#'+id);if(config.repoUrl)a.href=config.repoUrl+suffix;else{a.removeAttribute('target');a.href='#';a.onclick=e=>{e.preventDefault();showTab('setup')}}}try{viewer=await import('./viewer.js')}catch{}await refresh()}catch{$('#index-status').textContent='Collection settings could not be loaded. Check collection.json and the Space logs.'}