Enable public model catalogue and isolated file downloads; define native target

This commit is contained in:
Mikei386
2026-09-28 15:50:31 +02:00
parent 6d5671f75b
commit 5fb732469a
12 changed files with 247 additions and 17 deletions
+16
View File
@@ -0,0 +1,16 @@
const CatalogUI=(()=>{
const e=v=>String(v??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
const size=n=>(n/1024**3).toFixed(2)+' GiB';
async function api(path,data){const r=await fetch('/api/v1/catalog'+path,data?{method:'POST',headers:{'Content-Type':'application/json','X-Athena-Deck':'1'},body:JSON.stringify(data)}:{});const v=await r.json();if(!r.ok)throw Error(v.error||'Anfrage fehlgeschlagen');return v;}
function html(library){return `<section id="live-catalog" class="card"><span class="pill">LIVE · HUGGING FACE</span><h2>${library?'Dateien auf Athena':'Modelle suchen'}</h2><p>Öffentliche Repositories. Downloads laden kein Modell in RAM oder VRAM. Für Bild, Audio und Video können mehrere Dateien und ein eigener Worker nötig sein.</p>${library?'':'<form id="hub-search"><label>Suchbegriff<input name="q" maxlength="120" placeholder="Qwen, FLUX, Whisper …"></label><button>Suchen</button></form>'}<button class="secondary" id="hub-refresh">Downloadstatus aktualisieren</button><p id="hub-message" role="status"></p><div id="hub-job"></div><div id="hub-results"></div><div id="hub-files"></div></section>`;}
function bind(kind,library){
const root=document.querySelector('#live-catalog');if(!root)return;
const message=t=>{if(root.isConnected)root.querySelector('#hub-message').textContent=t;};
async function refresh(){try{const v=await api('');if(!root.isConnected)return;const j=v.job;root.querySelector('#hub-job').innerHTML=`<p>Freier Speicher: ${size(v.free_bytes)} · Reserve: 10 GiB</p>`+(j?`<p>${e(j.repo)} / ${e(j.file)} · ${e(j.state)} · ${size(j.bytes)} / ${size(j.total)}</p>${j.error?`<p>${e(j.error)}</p>`:''}${j.state==='downloading'?'<button id="hub-cancel" class="secondary">Download abbrechen</button>':''}`:'');root.querySelector('#hub-cancel')?.addEventListener('click',async()=>{await api('/cancel',{});message('Abbruch angefordert; laufende Netzwerkabfrage kann noch bis zu 20 Sekunden dauern.');});if(library)root.querySelector('#hub-results').innerHTML=v.entries.filter(x=>x.kind===kind).map(x=>`<article class="card"><h3>${e(x.repo)}</h3><p>${e(x.file)} · ${size(x.size)}</p><p>Heruntergeladen · Laufzeit noch nicht angebunden</p><small>Revision ${e(x.revision)} · ${x.upstream_hash_verified?'SHA-256 mit Quelle geprüft':'SHA-256 lokal erfasst; Größe mit Quelle geprüft'}</small></article>`).join('')||'<p>Noch keine Dateien für diesen Bereich heruntergeladen.</p>';if(j?.state==='downloading')setTimeout(()=>{if(root.isConnected)refresh();},2000);}catch(err){message(err.message);}}
root.querySelector('#hub-refresh').onclick=refresh;
root.querySelector('#hub-search')?.addEventListener('submit',async event=>{event.preventDefault();message('Suche …');try{const v=await api('/search?'+new URLSearchParams({q:new FormData(event.target).get('q'),kind}));if(!root.isConnected)return;root.querySelector('#hub-results').innerHTML=v.models.map(m=>`<article class="card"><h3>${e(m.repo)}</h3><p>${m.gated?'Zugangsbeschränkt':'Öffentlich'} · ${e(m.downloads)} Downloads</p><button data-repo="${e(m.repo)}">Dateien auswählen</button></article>`).join('');message(v.models.length+' Ergebnisse');root.querySelectorAll('[data-repo]').forEach(b=>b.onclick=()=>files(b.dataset.repo));}catch(err){message(err.message);}});
async function files(repo){message('Dateien werden gelesen …');try{const v=await api('/files?'+new URLSearchParams({repo}));if(!root.isConnected)return;root.querySelector('#hub-files').innerHTML=`<h3>${e(repo)}</h3><p>Lizenz: ${e(v.license||'Nicht angegeben')} · <a target="_blank" rel="noopener noreferrer" href="${e(v.url)}">Modellkarte und Bedingungen lesen</a></p><p>VRAM- und Kontext-Eignung noch nicht berechnet. Dateigröße allein belegt keine Lauffähigkeit.</p>`+v.files.map((f,i)=>`<article class="card"><p>${e(f.name)} · ${size(f.size)}</p><button data-file="${i}" ${v.gated?'disabled':''}>Datei auf Athena herunterladen</button></article>`).join('');message(v.files.length+' unterstützte Dateien (GGUF, Safetensors, JSON).');root.querySelectorAll('[data-file]').forEach(b=>b.onclick=async()=>{b.disabled=true;try{await api('/download',{repo,filename:v.files[Number(b.dataset.file)].name,revision:v.revision,kind});message('Download gestartet. Kein Modell wird ausgeführt.');refresh();}catch(err){message(err.message);}finally{b.disabled=false;}});}catch(err){message(err.message);}}
refresh();
}
return {html,bind};
})();