diff --git a/dfetch_hub/site/index.html b/dfetch_hub/site/index.html index deef0a6..d5e1f82 100644 --- a/dfetch_hub/site/index.html +++ b/dfetch_hub/site/index.html @@ -290,6 +290,22 @@ .vbadge-branch{background:#e8f4f8;color:#1a5276;border:1px solid #aed6f1} .vtable tbody tr{cursor:pointer} .vtable tbody tr.ver-selected td{background:rgba(0,113,188,.1)!important} + /* Hierarchical tag tree */ + .vtree-group-hdr td{ + background:var(--bg)!important; + cursor:pointer; + font-size:.8rem; + font-weight:700; + padding:.45rem .85rem; + color:var(--muted); + user-select:none; + border-bottom:1px solid var(--border) + } + .vtree-group-hdr:hover td{background:var(--border)!important} + .vtree-chevron{display:inline-block;width:1rem;font-size:.7rem} + .vtree-prefix{color:var(--text)} + .vtree-count{margin-left:.35rem;font-size:.72rem;font-weight:400;color:var(--subtle)} + .vtree-item td:first-child{padding-left:1.8rem} /* Version pill */ .action-card{margin-bottom:.65rem} .ver-pill{display:flex;align-items:center;gap:.4rem;margin-top:.45rem;padding:.3rem .7rem;background:var(--bg);border:1px solid var(--border);border-radius:var(--rm)} @@ -848,14 +864,15 @@ allRefsCache=allRefs.slice(0,50); const firstTag=sortedTags.length?{name:sortedTags[0].name,kind:'tag'}:null; curVersion=firstTag||{name:c.default_branch||'main',kind:'branch'}; - document.getElementById('vtbody').innerHTML=allRefsCache.length?allRefsCache.map((r,i)=>{ + const hasHierTags=allRefsCache.some(r=>r.kind==='tag'&&r.name.includes('/')); + document.getElementById('vtbody').innerHTML=allRefsCache.length?(hasHierTags?_renderVerTree(allRefsCache):allRefsCache.map((r,i)=>{ const sel=curVersion&&curVersion.name===r.name&&curVersion.kind===r.kind; - return ` + return ` ${esc(r.name)}${i===0&&r.kind==='tag'?'latest':''} ${r.kind} ${r.commit_sha?r.commit_sha.slice(0,10):'—'} ${r.date?r.date.slice(0,10):'—'} - `;}).join(''):'No version info'; + `;}).join('')):'No version info'; // Snippet document.getElementById('dSnip').innerHTML=buildSnipHtml(c,curVersion); // Aside @@ -884,7 +901,7 @@ const r=allRefsCache[i];if(!r)return; curVersion={name:r.name,kind:r.kind}; _replaceURL({pkg:curPkg,v:r.name,vk:r.kind}); - document.querySelectorAll('#vtbody tr').forEach((tr,idx)=>tr.classList.toggle('ver-selected',idx===i)); + document.querySelectorAll('#vtbody tr[data-vidx]').forEach(tr=>tr.classList.toggle('ver-selected',+tr.dataset.vidx===i)); const c=curPkg?RAW[curPkg]:null; if(c)document.getElementById('dSnip').innerHTML=buildSnipHtml(c,curVersion); updSelVerInfo(); @@ -896,6 +913,42 @@ document.getElementById('selVerKind').textContent=curVersion.kind; el.style.display=''; } +function _renderVerTree(refs){ + // Group tags whose names contain '/' by their prefix (part before first /) + const groups=new Map(); + refs.forEach((r,i)=>{ + if(r.kind!=='tag'||!r.name.includes('/'))return; + const pfx=r.name.slice(0,r.name.indexOf('/')); + if(!groups.has(pfx))groups.set(pfx,[]); + groups.get(pfx).push(i); + }); + let html=''; + // Render grouped tags — collapsed by default, open if selected version is inside + groups.forEach((idxs,pfx)=>{ + const open=idxs.some(i=>curVersion&&refs[i].name===curVersion.name&&refs[i].kind===curVersion.kind); + html+=`${open?'▾':'▸'}${esc(pfx)}/(${idxs.length})`; + idxs.forEach(i=>{ + const r=refs[i];const sel=curVersion&&r.name===curVersion.name&&r.kind===curVersion.kind; + const isLatest=i===0; + html+=`${esc(r.name.slice(pfx.length+1))}${isLatest?'latest':''}tag${r.commit_sha?r.commit_sha.slice(0,10):'—'}${r.date?r.date.slice(0,10):'—'}`; + }); + }); + // Render ungrouped tags and branches + refs.forEach((r,i)=>{ + if(r.kind==='tag'&&r.name.includes('/'))return; + const sel=curVersion&&r.name===curVersion.name&&r.kind===curVersion.kind; + const isLatest=i===0&&r.kind==='tag'; + html+=`${esc(r.name)}${isLatest?'latest':''}${r.kind}${r.commit_sha?r.commit_sha.slice(0,10):'—'}${r.date?r.date.slice(0,10):'—'}`; + }); + return html||'No version info'; +} +function toggleTreeGroup(hdr){ + const chevron=hdr.querySelector('.vtree-chevron'); + const open=chevron&&chevron.textContent==='▾'; + if(chevron)chevron.textContent=open?'▸':'▾'; + let sib=hdr.nextElementSibling; + while(sib&&sib.classList.contains('vtree-item')){sib.style.display=open?'none':'';sib=sib.nextElementSibling;} +} function switchTab(name){ document.querySelectorAll('.tabbtn').forEach((b,i)=>{const ns=['readme','versions'];b.classList.toggle('active',ns[i]===name)}); document.querySelectorAll('.tabcontent').forEach(el=>el.classList.remove('active'));