Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width,initial-scale=1"> | |
| <title>Library — Zero-000</title> | |
| <style> | |
| *{box-sizing:border-box;margin:0;padding:0} | |
| body{background:#1a1a1a;color:#e8e8e8;font-family:Consolas,'Courier New',monospace;min-height:100vh} | |
| header{background:#242424;border-bottom:1px solid #606060;padding:12px 20px;display:flex;align-items:center;gap:16px;flex-wrap:wrap} | |
| header h1{font-size:15px;font-weight:bold} | |
| #search{margin-left:auto;background:#2e2e2e;border:1px solid #606060;border-radius:4px;color:#e8e8e8;padding:6px 12px;font-family:inherit;font-size:12px;width:220px;outline:none} | |
| #search:focus{border-color:#378ADD} | |
| .section{padding:12px 16px 0} | |
| .section-hdr{font-size:13px;font-weight:bold;color:#e8e8e8;margin-bottom:6px;display:flex;align-items:baseline;gap:10px} | |
| .section-count{font-size:11px;color:#a0a0a0;font-weight:normal} | |
| .grid{display:flex;flex-wrap:wrap;gap:10px;padding:8px 0 20px} | |
| .sep{height:1px;background:#333;margin:0 16px} | |
| .card{width:178px;background:#2e2e2e;border:2px solid #2e2e2e;border-radius:6px;cursor:pointer;overflow:hidden;text-decoration:none;color:inherit;display:block;transition:border-color .15s} | |
| .card:hover{border-color:#378ADD} | |
| .img-wrap{width:170px;height:210px;margin:4px;background:#242424;overflow:hidden;border-radius:3px;display:flex;align-items:center;justify-content:center} | |
| .img-wrap img{width:100%;height:100%;object-fit:cover} | |
| .no-img{color:#606060;font-size:11px} | |
| .card-body{padding:4px 6px 6px} | |
| .card-name{font-size:12px;font-weight:bold;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-bottom:3px} | |
| .card-row{display:flex;align-items:center;justify-content:space-between;margin-bottom:3px} | |
| .card-base{font-size:10px;border-radius:3px;padding:1px 5px;color:#e8e8e8;display:inline-block} | |
| .card-dl{font-size:10px;color:#a0a0a0} | |
| .trig-row{display:flex;align-items:center;gap:4px} | |
| .card-triggers{flex:1;font-size:10px;color:#a0a0a0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;user-select:text;cursor:text} | |
| .copy-btn{flex-shrink:0;display:inline-block;background:#2a2a2a;border:1px solid #555;border-radius:3px;color:#909090;cursor:pointer;font-size:9px;padding:1px 5px;line-height:14px;white-space:nowrap} | |
| .copy-btn:hover{background:#378ADD;color:#fff;border-color:#378ADD} | |
| #status{padding:60px 20px;color:#606060;text-align:center;font-size:13px} | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <h1>🎨 Library — Zero-000</h1> | |
| <input id="search" type="text" placeholder="Search name, trigger, model…" oninput="doFilter()"> | |
| </header> | |
| <div id="status">Loading…</div> | |
| <div id="main" style="display:none"> | |
| <div class="section"> | |
| <div class="section-hdr">🔗 LoRA Library <span id="lora-count" class="section-count"></span></div> | |
| <div id="lora-grid" class="grid"></div> | |
| </div> | |
| <div class="sep"></div> | |
| <div class="section"> | |
| <div class="section-hdr">🧠 Models Library <span id="model-count" class="section-count"></span></div> | |
| <div id="model-grid" class="grid"></div> | |
| </div> | |
| </div> | |
| <script> | |
| const AUTHOR='Zero-000'; | |
| const BASE_COLORS={ | |
| 'SDXL':'#378ADD','Pony':'#639922','Illustrious':'#E24B4A', | |
| 'SD1.5':'#8855cc','SD2.0':'#cc8800','SD2.1':'#338866', | |
| 'SD3':'#cc3355','Flux':'#cc6622' | |
| }; | |
| let loraCards=[], modelCards=[]; | |
| function cpyTxt(text,btn){ | |
| try{ | |
| const ta=document.createElement('textarea'); | |
| ta.value=text;ta.style.cssText='position:fixed;opacity:0;top:0;left:0;width:1px;height:1px'; | |
| document.body.appendChild(ta);ta.select();document.execCommand('copy'); | |
| document.body.removeChild(ta); | |
| if(btn){btn.textContent='✓';setTimeout(function(){btn.textContent='Copy';},1500);} | |
| }catch(err){console.warn('copy failed',err);} | |
| } | |
| async function fetchModels(){ | |
| const r=await fetch(`https://huggingface.co/api/models?author=${AUTHOR}&limit=500&sort=lastModified&cardData=true`); | |
| if(!r.ok) throw new Error('API '+r.status); | |
| return r.json(); | |
| } | |
| function getBase(m){ | |
| const cd=m.cardData||{}; | |
| if(cd.base_model_tag) return cd.base_model_tag; | |
| const tags=m.tags||[]; | |
| if(tags.includes('illustrious')) return 'Illustrious'; | |
| if(tags.some(t=>t.toLowerCase().includes('pony'))) return 'Pony'; | |
| if(tags.includes('stable-diffusion-xl')) return 'SDXL'; | |
| if(tags.includes('stable-diffusion')) return 'SD1.5'; | |
| if(tags.includes('flux')) return 'Flux'; | |
| return ''; | |
| } | |
| function fmtDl(n){ | |
| if(!n) return '0'; | |
| if(n>=1e6) return (n/1e6).toFixed(1).replace(/\.0$/,'')+'M'; | |
| if(n>=1e3) return (n/1e3).toFixed(1).replace(/\.0$/,'')+'k'; | |
| return ''+n; | |
| } | |
| function makeCard(m){ | |
| const repoId=m.modelId||m.id; | |
| const cd=m.cardData||{}; | |
| const name=cd.lora_name||(repoId.split('/').pop().replace(/-/g,' ')); | |
| const base=getBase(m); | |
| const triggers=(cd.trigger_words||'').replace(/^"|"$/g,''); | |
| const color=BASE_COLORS[base]||'#606060'; | |
| const img=`https://huggingface.co/${repoId}/resolve/main/preview_01.jpg`; | |
| const dl=fmtDl(m.downloads||0); | |
| const a=document.createElement('a'); | |
| a.className='card'; | |
| a.href=`https://huggingface.co/${repoId}`; | |
| a.target='_blank'; | |
| a.dataset.name=name.toLowerCase(); | |
| a.dataset.triggers=triggers.toLowerCase(); | |
| a.dataset.base=base.toLowerCase(); | |
| a.innerHTML=` | |
| <div class="img-wrap"> | |
| <img src="${img}" alt="${name}" | |
| onerror="this.parentNode.innerHTML='<span class=no-img>No Preview</span>'"> | |
| </div> | |
| <div class="card-body"> | |
| <div class="card-name" title="${name}">${name}</div> | |
| <div class="card-row"> | |
| <span class="card-base" style="background:${color}">${base||'Unknown'}</span> | |
| <span class="card-dl">⬇ ${dl}</span> | |
| </div> | |
| <div class="trig-row"> | |
| <div class="card-triggers" title="${triggers}">${triggers||'—'}</div> | |
| </div> | |
| </div>`; | |
| if(triggers){ | |
| const btn=document.createElement('button'); | |
| btn.className='copy-btn'; | |
| btn.textContent='Copy'; | |
| btn.title='Copy trigger words'; | |
| btn.addEventListener('click',function(e){ | |
| e.preventDefault();e.stopPropagation(); | |
| cpyTxt(triggers,btn); | |
| }); | |
| a.querySelector('.trig-row').appendChild(btn); | |
| } | |
| return a; | |
| } | |
| function filterSection(cards, countId, q){ | |
| let n=0; | |
| for(const el of cards){ | |
| const show=!q||el.dataset.name.includes(q)||el.dataset.triggers.includes(q)||el.dataset.base.includes(q); | |
| el.style.display=show?'':'none'; | |
| if(show)n++; | |
| } | |
| document.getElementById(countId).textContent=q?`${n} / ${cards.length}`:`${cards.length}`; | |
| } | |
| function doFilter(){ | |
| const q=document.getElementById('search').value.toLowerCase().trim(); | |
| filterSection(loraCards,'lora-count',q); | |
| filterSection(modelCards,'model-count',q); | |
| } | |
| async function main(){ | |
| try{ | |
| const models=await fetchModels(); | |
| const loraGrid=document.getElementById('lora-grid'); | |
| const modelGrid=document.getElementById('model-grid'); | |
| document.getElementById('status').style.display='none'; | |
| document.getElementById('main').style.display=''; | |
| for(const m of models){ | |
| const tags=m.tags||[]; | |
| if(tags.includes('lora')){ | |
| const c=makeCard(m); loraCards.push(c); loraGrid.appendChild(c); | |
| } else if(tags.includes('checkpoint')){ | |
| const c=makeCard(m); modelCards.push(c); modelGrid.appendChild(c); | |
| } | |
| } | |
| document.getElementById('lora-count').textContent=loraCards.length; | |
| document.getElementById('model-count').textContent=modelCards.length; | |
| }catch(e){ | |
| document.getElementById('status').textContent='Error: '+e.message; | |
| } | |
| } | |
| main(); | |
| </script> | |
| </body> | |
| </html> |