constplaceholders={chat:['Sprachmodelle / Chat','Modellprofile, Laufzeit und Chat','Hier werden später Modellprofile und native llama.cpp-Prozesse verwaltet.'],image:['Bildgenerierung','Worker und Aufträge','Hier entsteht später die Übersicht für Bild-Worker und deren Zustand.'],audio:['Audio','Spracheingabe und Sprachausgabe','Hier werden später getrennte Worker für Erkennung und Ausgabe eingebunden.'],services:['Weitere Dienste','Erweiterbare Dienste','Hier finden später zusätzliche Worker und ihre Schnittstellen Platz.']};
if(page==='home'){consts=awaitapi('status'),d=s.demo;html=heading('DEIN KONTROLLZENTRUM','Alles beginnt mit einer klaren Basis.','Eine kleine Oberfläche für Status, Hardware und den ersten steuerbaren Dienst.')+`<div class="grid"><section class="card"><div class="label">Anwendung</div><div class="value"><i></i> Bereit</div><p>Athena Deck läuft auf ${esc(s.location)}.</p><div class="note">Laufzeit ${fmt(s.uptime_seconds,' s')} · API v1</div></section><section class="card"><div class="label">Betriebsmodus</div><div class="value">Isoliert</div><p>Ein eigenständiger Prototyp mit lesendem Hardware-Zugriff auf Athena.</p><div class="note">Keine Modell- oder Router-Steuerung angebunden</div></section><section class="card wide"><div class="card-top"><h2>Demo-Dienst</h2><span class="pill">LOKAL / CPU</span></div><p>Ein minimaler HTTP-Prozess zum Testen von Start, Stopp und Erreichbarkeit.</p><div class="metrics">${metric('Prozess',d.state==='running'?'Läuft':'Gestoppt')}${metric('Health-Prüfung',d.reachable?'Erreichbar':'Nicht erreichbar')}${metric('Lokaler Port',d.port??'—')}</div><button id="start" ${busy||d.state==='running'?'disabled':''}>Dienst starten</button><button id="stop" class="secondary" ${busy||d.state!=='running'?'disabled':''}>Dienst stoppen</button><div class="note">Lädt keine Modelle · endet beim Beenden von Athena Deck</div></section></div>`;
<!doctype html><htmllang="de"><head><metacharset="utf-8"><metaname="viewport"content="width=device-width,initial-scale=1"><title>Athena Deck</title><linkrel="stylesheet"href="/style.css"><scriptsrc="/network-ui.js"defer></script><scriptsrc="/access-ui.js"defer></script><scriptsrc="/app.js"defer></script></head><body><aside><aclass="brand"href="#home"><span>Α</span> ATHENA <b>DECK</b></a><pclass="eyebrow">WORKSPACE / PROTOTYP 01</p><navaria-label="Hauptnavigation"><ahref="#home">Übersicht <span>01</span></a><ahref="#chat">Sprachmodelle / Chat <span>02</span></a><ahref="#image">Bildgenerierung <span>03</span></a><ahref="#audio">Audio <span>04</span></a><ahref="#services">Weitere Dienste <span>05</span></a><ahref="#hardware">Hardware <span>06</span></a><ahref="#network">Netzwerk <span>07</span></a><ahref="#access">Zugang & API <span>08</span></a></nav><footer><i></i> Isolierte Entwicklungsumgebung<p>Steuerung auf dem Mac<br>Hardware von Athena · nur lesend</p></footer></aside><main><header><span>ATHENA CONTROL SURFACE</span><spanclass="pill">v0.3 · Prototyp</span></header><divid="view"></div><pid="error"role="alert"></p></main></body></html>
<!doctype html><htmllang="de"><head><metacharset="utf-8"><metaname="viewport"content="width=device-width,initial-scale=1"><title>Athena Deck</title><linkrel="stylesheet"href="/style.css"><scriptsrc="/network-ui.js"defer></script><scriptsrc="/access-ui.js"defer></script><scriptsrc="/studio.js"defer></script><scriptsrc="/app.js"defer></script></head><body><aside><aclass="brand"href="#home"><span>Α</span> ATHENA <b>DECK</b></a><pclass="eyebrow">WORKSPACE / PROTOTYP 01</p><navaria-label="Hauptnavigation"><ahref="#home">Übersicht <span>01</span></a><ahref="#chat">Sprachmodelle / Chat <span>02</span></a><ahref="#image">Bildgenerierung <span>03</span></a><ahref="#audio">Audio <span>04</span></a><ahref="#video">Video <span>05</span></a><ahref="#services">Weitere Dienste <span>06</span></a><ahref="#hardware">Hardware <span>07</span></a><pclass="nav-heading">EINSTELLUNGEN</p><ahref="#runtime">llama.cpp & Updates <span>↗</span></a><ahref="#network">Netzwerk <span>↗</span></a><ahref="#access">Zugang & API <span>↗</span></a></nav><footer><i></i> Isolierte Entwicklungsumgebung<p>Steuerung auf dem Mac<br>Hardware von Athena · nur lesend</p></footer></aside><main><header><span>ATHENA CONTROL SURFACE</span><spanclass="pill">v0.4 · GUI-Prototyp</span></header><divid="view"></div><pid="error"role="alert"></p></main></body></html>
constmodels=[{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.'}];
functionsave(){try{localStorage.setItem(key,JSON.stringify(state));returntrue;}catch{notice('Speichern im Browser nicht möglich. Entwurf bleibt nur für diese Ansicht erhalten.');returnfalse;}}
constbanner=()=>'<div class="prototype-banner"><span class="pill">GUI-VORSCHAU</span><span>Beispiele und lokale Entwürfe. Keine Downloads, Builds oder Modellstarts.</span></div>';
returnheader(labels[page],'Modelle entdecken, als Bibliothek organisieren und über Profile bereitstellen.')+`<div class="studio-tabs" role="tablist" aria-label="Modellverwaltung">${[['discover','Entdecken'],['library','Bibliothek'],['profiles','Profile'],['running','Laufend']].map(([id,label])=>`<button role="tab" aria-selected="${section===id}" class="${section===id?'chosen':'secondary'}" data-section="${id}">${label}${id==='profiles'&&page==='chat'?` <span>${state.profiles.length}</span>`:''}</button>`).join('')}</div><div role="tabpanel">${section==='discover'?discover(page):section==='library'?library(page):section==='profiles'?profiles(page):running(page)}</div>`;
}
functionmodelCard(m,library=false){return`<article class="card model-card"><div class="card-top"><span class="model-icon">${m.kind==='chat'?'Aa':m.kind==='image'?'▧':m.kind==='audio'?'≈':'▷'}</span><span class="pill">${library?'Bibliotheksbeispiel':'Katalogbeispiel'}</span></div><h2>${e(m.name)}</h2><div class="tag">${e(m.variant)}</div><p>${e(m.description)}</p><dl class="model-facts"><div><dt>Quelle</dt><dd>${e(m.source)} · nicht verbunden</dd></div><div><dt>Downloadgröße</dt><dd>Noch nicht ermittelt</dd></div><div><dt>Eignung für Athena</dt><dd><span class="unknown-dot"></span> Noch nicht geprüft</dd></div></dl><div class="card-actions"><button class="secondary" data-detail="${m.id}">Details & Eignung</button>${library&&m.kind==='chat'?`<button data-create="${m.id}">Profil entwerfen</button>`:'<button disabled>Herunterladen</button>'}</div></article>`;}
functiondiscover(page){return`<div class="studio-toolbar"><div><label for="model-search">Modell suchen</label><input id="model-search" type="search" placeholder="Beispiele nach Name oder Variante filtern …"></div><div><label for="source-filter">Quelle</label><select id="source-filter"><option value="all">Alle Quellen</option><option>Hugging Face</option><option>Modellkatalog</option></select></div></div><p class="small muted">Die Suche filtert nur diese Beispieldaten. Eine Live-Verbindung zu Hugging Face folgt später.</p><div class="model-grid" id="model-results">${models.filter(m=>m.kind===page).map(m=>modelCard(m)).join('')}</div><p id="no-models" hidden>Keine passenden Beispiele gefunden.</p><section id="model-detail"></section>`;}
functionlibrary(page){return`<div class="section-heading"><div><h2>Deine Modellbibliothek</h2><p>Eine Modelldatei kann von mehreren Profilen verwendet werden.</p></div><span class="pill">0 verifizierte Installationen</span></div><div class="model-grid">${models.filter(m=>m.kind===page).slice(0,1).map(m=>modelCard(m,true)).join('')}</div><p class="note">Die Einträge zeigen die geplante Darstellung. Es wurden weder lokale Modelldateien importiert noch Downloads ausgeführt.</p><section id="model-detail"></section>`;}
functionprofiles(page){
if(page!=='chat')return`<section class="card"><span class="pill">VORBEREITET</span><h2>${page==='image'?'Auflösung und Generierung':page==='audio'?'Sprache, Stimmen und Verarbeitung':'Auflösung und Videolänge'}</h2><div class="form-grid">${page==='image'?'<label>Breite / Höhe<input disabled value="1024 × 1024 · Beispiel"></label><label>Schritte<input disabled value="30 · Beispiel"></label>':page==='audio'?'<label>Aufgabe<select disabled><option>Sprachausgabe / Spracherkennung</option></select></label><label>Sprache<select disabled><option>Automatisch</option></select></label>':'<label>Auflösung<input disabled value="1280 × 720 · Beispiel"></label><label>Bildanzahl<input disabled value="Noch nicht festgelegt"></label>'}</div><p>Eigene Worker-Adapter und Eignungsregeln folgen. LLM-Kontextparameter werden hier nicht übernommen.</p><button disabled>Profil speichern</button></section>`;
return`<div class="section-heading"><div><h2>Ein Modell. Mehrere Möglichkeiten.</h2><p>API-Namen und Startparameter als lokale Profilentwürfe.</p></div><button id="new-profile">+ Neues Profil</button></div><div class="profile-list">${state.profiles.map(p=>`<article class="card profile-row"><div><span class="label">PROFILENTWURF</span><h2>${e(p.name)}</h2><p>${e(models.find(m=>m.id===p.model)?.variant||'Modell nicht zugeordnet')}</p></div><div><span class="label">Kontextbudget</span><strong>${count(p.context)}</strong></div><div><span class="label">Slots</span><strong>${e(p.slots)}</strong></div><div><span class="label">Zustand</span><strong class="muted">Nicht bereitgestellt</strong></div><div class="profile-actions"><button class="secondary" data-edit="${e(p.id)}">Bearbeiten</button><button class="secondary" data-duplicate="${e(p.id)}">Duplizieren</button></div></article>`).join('')||'<section class="empty">Noch keine Profilentwürfe. Lege dein erstes Profil an.</section>'}</div><section class="card endpoint-preview"><span class="label">GEPLANTE API · NICHT AKTIV</span><h2>Ein Endpunkt für deine Profile</h2><code>GET /v1/models</code><pre>${e(JSON.stringify({data:state.profiles.map(p=>({id:p.name,object:'model'}))},null,2))}</pre><p>Später wählt der Client den Profilnamen im Feld <code>model</code>. Deck verwaltet den passenden llama-server-Prozess.</p></section><div id="profile-editor"></div>`;
}
functionrunning(page){return`<section class="card runtime-empty"><span class="model-icon">○</span><h2>Keine von Deck verwaltete Modelllaufzeit</h2><p>${page==='chat'?'llama.cpp ist in dieser Vorschau nicht angebunden. Gespeicherte Profile starten keinen Prozess.':'Für diesen Bereich ist noch kein Worker angebunden.'}</p><button disabled>Profil starten</button><a class="link" href="#runtime">Laufzeit-Einstellungen ansehen →</a></section><section class="card"><h2>So soll der Profilwechsel ablaufen</h2><ol class="flow"><li><b>Anfrage zuordnen</b><span>Profil anhand des API-Namens auswählen.</span></li><li><b>Arbeit abschließen</b><span>Laufende Anfragen beenden lassen; neue einreihen.</span></li><li><b>Prozess wechseln</b><span>llama-server beenden und mit dem Zielprofil starten.</span></li><li><b>Bereitschaft prüfen</b><span>Erst danach wartende Anfragen weiterleiten.</span></li></ol><p class="note">Mehrere angebotene Profile sind nicht automatisch mehrere geladene Modelle. Produktive Athena-Prozesse werden in dieser Vorschau nicht gesteuert.</p></section>`;}
functiondetail(id){constm=models.find(m=>m.id===id);document.querySelector('#model-detail').innerHTML=`<article class="card detail-card"><div class="card-top"><h2>${e(m.name)} · Eignungsprüfung</h2><span class="pill">NOCH NICHT BERECHNET</span></div><div class="fit-grid"><div><b>Unterstützt?</b><p>Format und Laufzeit-Kompatibilität noch nicht geprüft.</p></div><div><b>Passt grundsätzlich?</b><p>Gewichte, Zusatzkomponenten und Arbeitsspeicherbedarf noch nicht ermittelt.</p></div><div><b>Kann jetzt starten?</b><p>Keine Ressourcenreservierung oder Prozesssteuerung angebunden.</p></div></div><p class="note">${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.</p></article>`;document.querySelector('#model-detail').scrollIntoView({behavior:'smooth',block:'nearest'});}
document.querySelector('#profile-editor').innerHTML=`<section class="card editor"><div class="card-top"><h2>${id?'Profil bearbeiten':'Neues Profil'}</h2><span class="pill">NUR ENTWURF</span></div><form id="profile-form"><div class="form-grid">${field('API-Profilname','name',p.name,'text','required pattern="[a-zA-Z0-9][a-zA-Z0-9_-]{1,63}" maxlength="64"')}<label>Modelldatei / Quantisierung<select name="model">${models.filter(m=>m.kind==='chat').map(m=>`<option value="${m.id}" ${selected(m.id,p.model)}>${e(m.name)} · ${e(m.variant)} (Beispiel)</option>`).join('')}</select></label>${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"')}<label>GPU-Zuordnung<select name="gpu"><option value="both" ${selected(p.gpu,'both')}>RTX 5080 + RTX 3060</option><option value="5080" ${selected(p.gpu,'5080')}>Nur RTX 5080</option><option value="3060" ${selected(p.gpu,'3060')}>Nur RTX 3060</option><option value="cpu" ${selected(p.gpu,'cpu')}>CPU</option></select></label><label>KV-Cache-Format<select name="cache">${['f16','q8_0','q4_0'].map(v=>`<option ${selected(v,p.cache)}>${v}</option>`).join('')}</select></label></div><label class="checkbox"><input type="checkbox" name="unified" ${check(p.unified)}> Gemeinsamen KV-Pool vorsehen</label><p class="small">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.</p><details><summary>Erweiterte Startoptionen</summary><div class="form-grid">${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"')}</div><label class="checkbox"><input type="checkbox" name="flash" ${check(p.flash)}> Flash Attention vorsehen</label><label class="checkbox"><input type="checkbox" name="vision" ${check(p.vision)}> Vision-Projektor vorsehen (noch nicht zugeordnet)</label></details><div class="card-actions"><button>Entwurf speichern</button><button type="button" class="secondary" id="cancel-edit">Schließen</button>${id?'<button type="button" class="secondary danger" id="delete-profile">Entwurf entfernen</button>':''}</div><p id="profile-error" role="alert"></p></form></section>`;
document.querySelector('#profile-form').onsubmit=event=>{event.preventDefault();constdata=newFormData(event.target),value=Object.fromEntries(data);for(constnof['context','slots','batch','ubatch','threads'])value[n]=Number(value[n]);for(constnof['unified','flash','vision'])value[n]=data.has(n);constout=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();constidx=state.profiles.findIndex(q=>q.id===editing);if(idx<0)state.profiles.push(value);elsestate.profiles[idx]=value;conststored=save();render(category,true);notice(stored?'Profilentwurf in diesem Browser gespeichert. Kein Modell gestartet.':'Entwurf nur im Speicher gehalten; Browser-Speicher nicht verfügbar.');};
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.');};
functionruntime(){constr=state.runtime;returnheader('llama.cpp-Laufzeit','Installation, GPU-Unterstützung und Versionen an einem Ort vorbereiten.')+`<div class="runtime-stats"><section class="card"><span class="label">INSTALLIERTE VERSION</span><h2>Nicht angebunden</h2><p>Kein Build erkannt oder ausgeführt.</p></section><section class="card"><span class="label">UPDATE-STATUS</span><h2>Noch nicht geprüft</h2><p>Keine Live-Abfrage von Releases.</p></section><section class="card"><span class="label">ZIELSYSTEM</span><h2>Athena</h2><p>Geplant: RTX 5080 + RTX 3060</p></section></div><section class="card"><div class="card-top"><h2>Installation & Build</h2><span class="pill">LOKALER ENTWURF</span></div><form id="build-form"><div class="form-grid"><label>Rechen-Backend<select name="backend">${['CUDA','CPU','Vulkan'].map(v=>`<option ${selected(v,r.backend)}>${v}</option>`).join('')}</select></label>${field('Gewünschtes Release / Commit','revision',r.revision,'text','placeholder="Noch keine Version ausgewählt" maxlength="80"')}<label>GPU-Architekturen<select name="architectures"><option value="auto" ${selected('auto',r.architectures)}>Auf dem Zielserver automatisch erkennen</option><option value="custom" ${selected('custom',r.architectures)}>Explizite Build-Ziele vorgeben</option></select></label>${field('Explizite Architekturziele (optional)','custom',r.custom,'text','placeholder="Später anhand beider GPUs prüfen" maxlength="80"')}<label>CUDA-Toolkit<select name="toolkit"><option value="auto">Verfügbarkeit und Kompatibilität prüfen</option></select></label>${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"')}</div><p class="note">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.</p><button>Build-Entwurf speichern</button><button disabled>llama.cpp herunterladen & bauen</button></form></section><section class="card"><div class="card-top"><h2>Updates & Änderungen</h2><button id="release-preview" class="secondary">Update-Ansicht öffnen</button></div><p>Vorgesehen: installierte Version mit verfügbaren Releases vergleichen, Fixes ansehen und einen neuen Build getrennt vorbereiten.</p><div id="release-panel" hidden><div class="release-comparison"><div><span class="label">AKTUELL</span><h3>Nicht ermittelt</h3></div><span>→</span><div><span class="label">VERFÜGBAR</span><h3>Nicht abgefragt</h3></div></div><h3>Release-Notes & Fixes</h3><p>Hier erscheinen später die tatsächlichen Änderungen mit Quelle und Veröffentlichungsdatum. Keine Beispieldaten werden als neue Version oder behobener Fehler ausgegeben.</p><a class="link" href="https://github.com/ggml-org/llama.cpp/releases" target="_blank" rel="noopener noreferrer">Offizielle llama.cpp-Releases öffnen ↗</a><div class="card-actions"><button disabled>Nach Updates suchen</button><button disabled>Neue Version bauen</button></div></div></section><section class="card"><h2>Geplanter Build-Ablauf</h2><ol class="flow"><li><b>Voraussetzungen prüfen</b><span>Compiler, CUDA, beide GPUs und freier Speicher.</span></li><li><b>Version beziehen</b><span>Ausgewähltes Release oder festen Commit herunterladen.</span></li><li><b>Separat bauen & prüfen</b><span>Neue Binärdatei prüfen; laufende Version beibehalten.</span></li><li><b>Gezielt aktivieren</b><span>Nach laufenden Anfragen wechseln; vorigen Build behalten.</span></li></ol><button disabled>Vorherigen Build wiederherstellen</button><p class="note">Kein Build aktiv · keine Warteschlange · keine Wiederherstellungsversion vorhanden.</p></section>`;}
document.querySelectorAll('[data-duplicate]').forEach(b=>b.onclick=()=>{constold=state.profiles.find(p=>p.id===b.dataset.duplicate);letn=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('#build-form')?.addEventListener('submit',event=>{event.preventDefault();constvalue=Object.fromEntries(newFormData(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.');});
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.