/* Visual prototype only. Drafts stay in this browser; no runtime/download API calls. */ const Studio=(()=>{ const key='athena-deck-studio-v1'; const seed={profiles:[{id:'alltag',name:'qwen-alltag',model:'qwen-iq4',context:76800,slots:1,gpu:'both',split:'85,15',cache:'q8_0',batch:2048,ubatch:128,threads:8,unified:true,flash:true,vision:false},{id:'parallel',name:'qwen-parallel',model:'qwen-iq4',context:160000,slots:2,gpu:'both',split:'85,15',cache:'q8_0',batch:2048,ubatch:128,threads:8,unified:true,flash:true,vision:false},{id:'lang',name:'qwen-langkontext',model:'qwen-iq4',context:262144,slots:1,gpu:'both',split:'80,20',cache:'q8_0',batch:2048,ubatch:128,threads:8,unified:true,flash:true,vision:false}],runtime:{backend:'CUDA',revision:'',architectures:'auto',custom:'',jobs:4,toolkit:'auto',reserve:2048}}; let state=structuredClone(seed);try{const saved=JSON.parse(localStorage.getItem(key));if(saved?.profiles&&Array.isArray(saved.profiles)&&saved.runtime)state=saved;}catch{} let section='discover',category='',editing=null; const models=[{id:'qwen-iq4',kind:'chat',name:'Qwen 27B',variant:'IQ4_XS · GGUF',source:'Hugging Face',description:'Sprachmodell · Beispiel für mehrere Profile mit einer Modelldatei.'},{id:'qwen-q4',kind:'chat',name:'Qwen 27B',variant:'Q4_K_M · GGUF',source:'Hugging Face',description:'Alternative Quantisierung · eigener Bibliothekseintrag.'},{id:'image-example',kind:'image',name:'Bildmodell',variant:'Modellpaket · Beispiel',source:'Modellkatalog',description:'Textencoder, Diffusionsmodell und VAE als zusammengehöriges Paket.'},{id:'tts-example',kind:'audio',name:'Sprachausgabe',variant:'TTS · Beispiel',source:'Modellkatalog',description:'Sprachmodell und Stimmen mit passender Audio-Laufzeit.'},{id:'asr-example',kind:'audio',name:'Spracherkennung',variant:'ASR · Beispiel',source:'Modellkatalog',description:'Audio in Text umwandeln · CPU-/GPU-Eignung später prüfen.'},{id:'video-example',kind:'video',name:'Videomodell',variant:'Modellpaket · Beispiel',source:'Modellkatalog',description:'Auflösung, Bildanzahl und Zusatzkomponenten gemeinsam planen.'}]; const labels={chat:'Sprachmodelle / Chat',image:'Bildgenerierung',audio:'Audio',video:'Video'}; const e=v=>String(v??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); const count=n=>Number(n).toLocaleString('de-DE'); const selected=(a,b)=>a===b?'selected':''; const check=v=>v?'checked':''; function save(){try{localStorage.setItem(key,JSON.stringify(state));return true;}catch{notice('Speichern im Browser nicht möglich. Entwurf bleibt nur für diese Ansicht erhalten.');return false;}} function notice(message){const node=document.querySelector('#studio-notice');if(node)node.textContent=message;} const banner=()=>'
GUI-VORSCHAUBeispiele und lokale Entwürfe. Keine Downloads, Builds oder Modellstarts.
'; function header(title,description){return `
ATHENA / MODELLVERWALTUNG

${title}

${description}

${banner()}`;} function render(page,force=false){ if(!force&&document.querySelector('#studio')?.dataset.page===page)return; if(category!==page){category=page;section='discover';editing=null;} const view=document.querySelector('#view'); view.innerHTML=`
${page==='runtime'?runtime():workspace(page)}

`; bind(page); } function workspace(page){ return header(labels[page], 'Modelle entdecken, als Bibliothek organisieren und über Profile bereitstellen.')+`
${[['discover','Entdecken'],['library','Bibliothek'],['profiles','Profile'],['running','Laufend']].map(([id,label])=>``).join('')}
${section==='discover'?discover(page):section==='library'?library(page):section==='profiles'?profiles(page):running(page)}
`; } function modelCard(m,library=false){return `
${m.kind==='chat'?'Aa':m.kind==='image'?'▧':m.kind==='audio'?'≈':'▷'}${library?'Bibliotheksbeispiel':'Katalogbeispiel'}

${e(m.name)}

${e(m.variant)}

${e(m.description)}

Quelle
${e(m.source)} · nicht verbunden
Downloadgröße
Noch nicht ermittelt
Eignung für Athena
Noch nicht geprüft
${library&&m.kind==='chat'?``:''}
`;} function discover(page){return `

Die Suche filtert nur diese Beispieldaten. Eine Live-Verbindung zu Hugging Face folgt später.

${models.filter(m=>m.kind===page).map(m=>modelCard(m)).join('')}
`;} function library(page){return `

Deine Modellbibliothek

Eine Modelldatei kann von mehreren Profilen verwendet werden.

0 verifizierte Installationen
${models.filter(m=>m.kind===page).slice(0,1).map(m=>modelCard(m,true)).join('')}

Die Einträge zeigen die geplante Darstellung. Es wurden weder lokale Modelldateien importiert noch Downloads ausgeführt.

`;} function profiles(page){ if(page!=='chat')return `
VORBEREITET

${page==='image'?'Auflösung und Generierung':page==='audio'?'Sprache, Stimmen und Verarbeitung':'Auflösung und Videolänge'}

${page==='image'?'':page==='audio'?'':''}

Eigene Worker-Adapter und Eignungsregeln folgen. LLM-Kontextparameter werden hier nicht übernommen.

`; return `

Ein Modell. Mehrere Möglichkeiten.

API-Namen und Startparameter als lokale Profilentwürfe.

${state.profiles.map(p=>`
PROFILENTWURF

${e(p.name)}

${e(models.find(m=>m.id===p.model)?.variant||'Modell nicht zugeordnet')}

Kontextbudget${count(p.context)}
Slots${e(p.slots)}
ZustandNicht bereitgestellt
`).join('')||'
Noch keine Profilentwürfe. Lege dein erstes Profil an.
'}
GEPLANTE API · NICHT AKTIV

Ein Endpunkt für deine Profile

GET /v1/models
${e(JSON.stringify({data:state.profiles.map(p=>({id:p.name,object:'model'}))},null,2))}

Später wählt der Client den Profilnamen im Feld model. Deck verwaltet den passenden llama-server-Prozess.

`; } function running(page){return `
○

Keine von Deck verwaltete Modelllaufzeit

${page==='chat'?'llama.cpp ist in dieser Vorschau nicht angebunden. Gespeicherte Profile starten keinen Prozess.':'Für diesen Bereich ist noch kein Worker angebunden.'}

Laufzeit-Einstellungen ansehen →

So soll der Profilwechsel ablaufen

  1. Anfrage zuordnenProfil anhand des API-Namens auswählen.
  2. Arbeit abschließenLaufende Anfragen beenden lassen; neue einreihen.
  3. Prozess wechselnllama-server beenden und mit dem Zielprofil starten.
  4. Bereitschaft prüfenErst danach wartende Anfragen weiterleiten.

Mehrere angebotene Profile sind nicht automatisch mehrere geladene Modelle. Produktive Athena-Prozesse werden in dieser Vorschau nicht gesteuert.

`;} function detail(id){const m=models.find(m=>m.id===id);document.querySelector('#model-detail').innerHTML=`

${e(m.name)} · Eignungsprüfung

NOCH NICHT BERECHNET
Unterstützt?

Format und Laufzeit-Kompatibilität noch nicht geprüft.

Passt grundsätzlich?

Gewichte, Zusatzkomponenten und Arbeitsspeicherbedarf noch nicht ermittelt.

Kann jetzt starten?

Keine Ressourcenreservierung oder Prozesssteuerung angebunden.

${m.kind==='chat'?'Kontextlänge, KV-Cache, Slots und GPU-Aufteilung beeinflussen den Speicherbedarf. Die beiden GPUs werden nicht pauschal als ein gemeinsamer VRAM-Pool gerechnet.':'Die spätere Einschätzung berücksichtigt die zur Aufgabe passenden Parameter und den jeweiligen Worker.'} Es gibt in dieser Vorschau keine erfundenen Speicherwerte.

`;document.querySelector('#model-detail').scrollIntoView({behavior:'smooth',block:'nearest'});} function field(label,name,value,type='number',attrs=''){return ``;} function editor(id,model){editing=id||null;const p=id?state.profiles.find(p=>p.id===id):{...seed.profiles[0],name:'qwen-neues-profil',model:model||'qwen-iq4'};if(!p)return; document.querySelector('#profile-editor').innerHTML=`

${id?'Profil bearbeiten':'Neues Profil'}

NUR ENTWURF
${field('API-Profilname','name',p.name,'text','required pattern="[a-zA-Z0-9][a-zA-Z0-9_-]{1,63}" maxlength="64"')}${field('Gesamtes Kontextbudget (Token)','context',p.context,'number','required min="512" max="2097152" step="1"')}${field('Parallele Slots','slots',p.slots,'number','required min="1" max="16" step="1"')}

Das Kontextbudget gilt für das Profil, nicht automatisch je Slot. Die genaue Slot-Zuteilung und Unterstützung werden später mit der gewählten llama.cpp-Version geprüft.

Erweiterte Startoptionen
${field('Gewichtung 5080 : 3060','split',p.split,'text','required pattern="[0-9]+(\\.[0-9]+)?,[0-9]+(\\.[0-9]+)?"')}${field('Batch-Größe','batch',p.batch,'number','required min="1" max="8192"')}${field('Microbatch-Größe','ubatch',p.ubatch,'number','required min="1" max="8192"')}${field('CPU-Threads','threads',p.threads,'number','required min="1" max="256"')}
${id?'':''}
`; document.querySelector('#profile-form').onsubmit=event=>{event.preventDefault();const data=new FormData(event.target),value=Object.fromEntries(data);for(const n of ['context','slots','batch','ubatch','threads'])value[n]=Number(value[n]);for(const n of ['unified','flash','vision'])value[n]=data.has(n);const out=document.querySelector('#profile-error');if(state.profiles.some(q=>q.id!==editing&&q.name===value.name)){out.textContent='Dieser API-Profilname ist bereits vergeben.';return;}if(value.ubatch>value.batch){out.textContent='Microbatch darf nicht größer als Batch sein.';return;}if(value.gpu==='both'&&!value.split.split(',').some(v=>Number(v)>0)){out.textContent='Mindestens eine GPU-Gewichtung muss größer als null sein.';return;}value.id=editing||crypto.randomUUID();const idx=state.profiles.findIndex(q=>q.id===editing);if(idx<0)state.profiles.push(value);else state.profiles[idx]=value;const stored=save();render(category,true);notice(stored?'Profilentwurf in diesem Browser gespeichert. Kein Modell gestartet.':'Entwurf nur im Speicher gehalten; Browser-Speicher nicht verfügbar.');}; document.querySelector('#cancel-edit').onclick=()=>document.querySelector('#profile-editor').replaceChildren(); if(id)document.querySelector('#delete-profile').onclick=()=>{state.profiles=state.profiles.filter(q=>q.id!==id);save();render(category,true);notice('Lokalen Profilentwurf entfernt. Modelldateien wurden nicht verändert.');}; document.querySelector('#profile-editor').scrollIntoView({behavior:'smooth',block:'start'}); } function runtime(){const r=state.runtime;return header('llama.cpp-Laufzeit','Installation, GPU-Unterstützung und Versionen an einem Ort vorbereiten.')+`
INSTALLIERTE VERSION

Nicht angebunden

Kein Build erkannt oder ausgeführt.

UPDATE-STATUS

Noch nicht geprüft

Keine Live-Abfrage von Releases.

ZIELSYSTEM

Athena

Geplant: RTX 5080 + RTX 3060

Installation & Build

LOKALER ENTWURF
${field('Gewünschtes Release / Commit','revision',r.revision,'text','placeholder="Noch keine Version ausgewählt" maxlength="80"')}${field('Explizite Architekturziele (optional)','custom',r.custom,'text','placeholder="Später anhand beider GPUs prüfen" maxlength="80"')}${field('Parallele Build-Jobs','jobs',r.jobs,'number','required min="1" max="64"')}${field('Geplante VRAM-Reserve je GPU (MiB)','reserve',r.reserve,'number','required min="256" max="16384" step="256"')}

Bei verschiedenen GPUs muss der CUDA-Build die Architektur beider Karten unterstützen. Die konkreten Build-Ziele und die passende Toolkit-Version werden später auf Athena geprüft. Diese Vorschau installiert keine Treiber oder Host-Pakete.

Updates & Änderungen

Vorgesehen: installierte Version mit verfügbaren Releases vergleichen, Fixes ansehen und einen neuen Build getrennt vorbereiten.

Geplanter Build-Ablauf

  1. Voraussetzungen prüfenCompiler, CUDA, beide GPUs und freier Speicher.
  2. Version beziehenAusgewähltes Release oder festen Commit herunterladen.
  3. Separat bauen & prüfenNeue Binärdatei prüfen; laufende Version beibehalten.
  4. Gezielt aktivierenNach laufenden Anfragen wechseln; vorigen Build behalten.

Kein Build aktiv · keine Warteschlange · keine Wiederherstellungsversion vorhanden.

`;} function bind(page){ document.querySelectorAll('[data-section]').forEach(b=>b.onclick=()=>{section=b.dataset.section;render(page,true);}); document.querySelectorAll('[data-detail]').forEach(b=>b.onclick=()=>detail(b.dataset.detail)); document.querySelectorAll('[data-create]').forEach(b=>b.onclick=()=>{const model=b.dataset.create;section='profiles';render(page,true);editor(null,model);}); document.querySelectorAll('[data-edit]').forEach(b=>b.onclick=()=>editor(b.dataset.edit)); document.querySelectorAll('[data-duplicate]').forEach(b=>b.onclick=()=>{const old=state.profiles.find(p=>p.id===b.dataset.duplicate);let n=2,name=old.name.slice(0,55)+'-kopie';while(state.profiles.some(p=>p.name===name))name=old.name.slice(0,50)+'-kopie-'+n++;state.profiles.push({...old,id:crypto.randomUUID(),name});save();render(page,true);notice('Profil lokal dupliziert. Kein zusätzlicher Prozess gestartet.');}); document.querySelector('#new-profile')?.addEventListener('click',()=>editor()); const filter=()=>{const q=document.querySelector('#model-search').value.toLowerCase(),source=document.querySelector('#source-filter').value;const filtered=models.filter(m=>m.kind===page&&(source==='all'||source===m.source)&&(m.name+' '+m.variant).toLowerCase().includes(q));document.querySelector('#model-results').innerHTML=filtered.map(m=>modelCard(m)).join('');document.querySelector('#no-models').hidden=!!filtered.length;document.querySelectorAll('[data-detail]').forEach(b=>b.onclick=()=>detail(b.dataset.detail));}; document.querySelector('#model-search')?.addEventListener('input',filter);document.querySelector('#source-filter')?.addEventListener('change',filter); document.querySelector('#release-preview')?.addEventListener('click',()=>{const p=document.querySelector('#release-panel');p.hidden=!p.hidden;}); document.querySelector('#build-form')?.addEventListener('submit',event=>{event.preventDefault();const value=Object.fromEntries(new FormData(event.target));value.jobs=Number(value.jobs);value.reserve=Number(value.reserve);if(value.architectures==='custom'&&!value.custom.trim()){notice('Bitte explizite Architekturziele eintragen oder automatische Erkennung wählen.');return;}state.runtime=value;notice(save()?'Build-Entwurf im Browser gespeichert. Kein Download oder Build ausgeführt.':'Entwurf nur für diese Sitzung gespeichert.');}); } return {render}; })();