diff --git a/STUDIO.md b/STUDIO.md
index 0537140..7355fba 100644
--- a/STUDIO.md
+++ b/STUDIO.md
@@ -264,3 +264,16 @@ aktuellen Browserseite. Beim erneuten Öffnen einer Bibliothek erscheinen deren
zuletzt geladene Dateien sofort mit Aktualisierungshinweis. Aktionen bleiben bis
zur frischen Serverantwort gesperrt; Löschschutz und Profilzuordnungen werden
weiterhin serverseitig geprüft. Ein Neuladen der Browserseite leert diesen Cache.
+
+### Vereinfachte Steuerung
+
+Unter Steuerung zuerst LLM, Video oder Musik auswählen und anschließend starten.
+Die Auswahl allein verändert den laufenden Betrieb nicht. Start wechselt bei
+Bedarf die Betriebsart und öffnet den gemeinsamen API-Listener. Stoppen schließt
+den Listener und beendet bei Video/Musik den exklusiven Betrieb durch Rückkehr
+auf LLM; die bisherigen Schutzprüfungen und Wartebedingungen bleiben bestehen.
+Während eines Wechsels erscheint ein unbestimmter Ladebalken mit der echten
+Wechselphase, ohne erfundene Prozentangabe. Bereit bedeutet API-erreichbar; der
+Hinweis zu Gewichten unterscheidet geladene Modelle vom Laden bei Anfrage.
+API-Adressen und der Port werden für die ausgewählte Betriebsart angezeigt.
+Technische Konfiguration und Profilfreigaben bleiben unter Einstellungen → Endpunkt.
diff --git a/endpoint-ui.js b/endpoint-ui.js
index 2a46b1a..772e080 100644
--- a/endpoint-ui.js
+++ b/endpoint-ui.js
@@ -1,22 +1,61 @@
window.EndpointUI=(()=>{
const e=v=>String(v??'—').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
const stateName={running:'Läuft',stopped:'Gestoppt',stopping:'Wird gestoppt · laufende Aufträge werden beendet',loading:'Modell lädt',ready:'Modell bereit',failed:'Fehlgeschlagen'};
- let pending=false,sequence=0;
+ let pending=false,sequence=0,selectedMode=null,controlTimer=null;
async function api(path='',data){const r=await fetch('/api/v1/endpoint'+path,data!==undefined?{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||'Endpunkt nicht erreichbar');return v;}
function put(el,html){if(el._deckHtml!==html){el.innerHTML=html;el._deckHtml=html;}}
function summary(s){return `
Aktivierte Profile werden angeboten. Erst eine Anfrage lädt das Modell. Wechsel warten auf laufende Antworten; Bildaufträge entladen vorher das eigene Sprachmodell.
${[['llm','Sprachmodelle'],['image','Bildgenerierung'],['tts','Text → Sprache (TTS)'],['stt','Sprache → Text (STT)'],['music','Musik']].map(([key,label])=>{const c=s.counts[key];return `
${label}
${c.supported?`${c.enabled} Profile aktiviert · ${c.available} ausführbar`:'Noch keine eigene Laufzeit eingerichtet'}
${c.loaded?'AKTIV IM SPEICHER':c.available?'BEI ANFRAGE LADEN':'NICHT AKTIV'}`}).join('')}
${e(switching?v.switch_phase:loading?s.worker.phase||'Modell wird geladen':s.state==='stopping'?'Laufende Aufträge werden beendet':'Start wird angefordert')}
Eigener Router für Deck-Profile. Bestehende Athena-Dienste bleiben unabhängig.
${settings?'
Lokaler API-Port
Der Zugriff verwendet den API-Token aus Zugang & API-Token. Verwaltungsoberfläche und API haben getrennte Ports.
Am Endpunkt angebotene Profile
Nur explizit aktivierte, ausführbare Profile erscheinen in GET /v1/models. Aktivieren lädt noch kein Modell.
API-Routen
POST /v1/chat/completions · Text und Streaming POST /v1/images/generations · Qwen-Image-Rezept, ein Bild als b64_json POST /v1/audio/speech und POST /v1/audio/transcriptions · noch nicht eingerichtet (501)
Alle Routen nutzen denselben Port und Bearer-Token. Bei Bildaufträgen bestimmt das Profil Auflösung, Schritte, Guidance und Seed. Noch keine Bildbearbeitung oder Vision-Eingaben.
Der Zugriff verwendet den API-Token aus Zugang & API-Token. Verwaltungsoberfläche und API haben getrennte Ports.
Am Endpunkt angebotene Profile
Nur explizit aktivierte, ausführbare Profile erscheinen in GET /v1/models. Aktivieren lädt noch kein Modell.
API-Routen
POST /v1/chat/completions · Text und Streaming POST /v1/images/generations · Qwen-Image-Rezept, ein Bild als b64_json POST /v1/audio/speech und POST /v1/audio/transcriptions · noch nicht eingerichtet (501)
Alle Routen nutzen denselben Port und Bearer-Token. Bei Bildaufträgen bestimmt das Profil Auflösung, Schritte, Guidance und Seed. Noch keine Bildbearbeitung oder Vision-Eingaben.
':''}`;
root=view.querySelector('#endpoint-page');
- for(const action of ['start','stop'])root.querySelector('#endpoint-'+action).onclick=()=>mutate('/'+action,{});
+ if(settings)for(const action of ['start','stop'])root.querySelector('#endpoint-'+action).onclick=()=>mutate('/'+action,{});
root.querySelector('#endpoint-port-form')?.addEventListener('submit',event=>{event.preventDefault();mutate('/config',{port:Number(root.querySelector('#endpoint-port').value)});});
}
if(pending)return;const id=sequence;
try{const s=await api();if(!root.isConnected||id!==sequence)return;
+ if(!settings){control(s,root);clearTimeout(controlTimer);if(s.video?.state==='switching'||s.worker.state==='loading'||s.state==='stopping')controlTimer=setTimeout(()=>{if(root.isConnected)render(false);},1000);return;}
put(root.querySelector('#endpoint-summary'),summary(s));
- if(s.video)VideoUI.mode(root.querySelector('#video-mode-panel'),s.video,s.profiles,s.port);
root.querySelector('#endpoint-start').disabled=s.state!=='stopped';root.querySelector('#endpoint-stop').disabled=s.state!=='running';
if(settings){const port=root.querySelector('#endpoint-port');if(document.activeElement!==port)port.value=s.port;port.disabled=s.state!=='stopped';root.querySelector('#endpoint-port-save').disabled=s.state!=='stopped';
root.querySelector('#endpoint-port-help').textContent=s.allowed_ports.length?'Docker-Testinstallation: veröffentlichte Loopback-Ports '+s.allowed_ports.join(', ')+'. Portwechsel nur bei gestopptem Endpunkt. Der SSH-Tunnel muss denselben Port weiterleiten.':'Nativer Dienst: freie Ports zwischen 1024 und 65535, nur bei gestopptem Endpunkt.';
diff --git a/style.css b/style.css
index 451ea74..616d982 100644
--- a/style.css
+++ b/style.css
@@ -42,3 +42,6 @@ nav a.nav-sub{padding-left:28px;font-size:12px;color:var(--deck-tone-a4bda8, #a4
/* Compact, expandable detail lists share one interaction across model categories. */
.detail-list{display:grid;gap:8px;margin-top:20px}.detail-row{border:1px solid var(--deck-tone-303a37,#303a37);border-radius:9px;background:var(--deck-tone-181f21,#181f21);overflow:hidden}.detail-summary{display:grid;grid-template-columns:minmax(0,1.3fr) minmax(0,1.3fr) minmax(110px,.7fr) minmax(140px,1fr);gap:18px;align-items:center;padding:18px 18px 18px 38px;position:relative;list-style:none;line-height:1.5}.detail-summary::-webkit-details-marker{display:none}.detail-summary:before{content:'▸';position:absolute;left:16px;top:19px;color:var(--deck-tone-c5edaf,#c5edaf)}.detail-row[open]>.detail-summary:before{content:'▾'}.detail-summary:hover{background:var(--deck-tone-20292a,#20292a)}.detail-summary:focus-visible{outline:2px solid var(--deck-tone-c5edaf,#c5edaf);outline-offset:-3px}.detail-summary strong{font-size:14px;overflow-wrap:anywhere}.detail-summary span{font-size:12px;color:var(--deck-tone-97a79e,#97a79e);overflow-wrap:anywhere}.detail-summary small{display:block;margin-top:4px}.detail-body{padding:8px 22px 22px;border-top:1px solid var(--deck-tone-303a37,#303a37)}.detail-body>.card-actions{margin-top:16px}.detail-body .execution-setup{margin-top:12px;padding-top:12px}.detail-row .endpoint-choice{margin:0 8px 8px 0;font-size:12px}#studio[data-page=runtimes] .detail-summary{grid-template-columns:1fr 1fr 1fr}.download-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px 20px;padding:18px;margin-top:8px;border-radius:9px}.download-row .card-top,.download-row progress,.download-row .warning{grid-column:1/-1}.download-row h3,.download-row p{margin:0}.download-row h3{font-size:14px;overflow-wrap:anywhere}.download-row .hub-filename{grid-column:1/-1}.download-row button{margin:0;font-size:12px}.download-row .card-top{display:flex;justify-content:space-between}.download-row .card-top strong{font-size:14px}.nav-heading{margin:16px 12px 4px}.eyebrow{margin-top:24px}aside{padding-top:24px}nav a{padding:9px 12px}nav .nav-heading:first-child{margin-top:0}.studio-tabs{flex-wrap:wrap}
@media(max-width:1000px){.detail-summary{grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:8px 18px}}@media(max-width:600px){.detail-summary,#studio[data-page=runtimes] .detail-summary{grid-template-columns:1fr;gap:5px}.detail-body{padding:8px 16px 16px}.download-row{grid-template-columns:1fr}.download-row>*{grid-column:1!important}nav{grid-template-columns:1fr 1fr}nav .nav-heading{grid-column:1/-1}.card-actions{gap:6px}}
+
+/* Control is a focused mode chooser; detailed API configuration lives in settings. */
+.control-modes{display:flex;gap:12px;margin:24px 0 20px}.control-modes button{min-width:110px;margin:0;font-size:15px}.control-card{max-width:900px}.control-heading{display:flex;align-items:center;justify-content:space-between;gap:16px}.control-heading h2{margin:0;font-size:25px}.control-card>.card-actions{margin-top:18px}.control-api{border-top:1px solid var(--deck-tone-303a37,#303a37);margin-top:24px;padding-top:8px}.control-api h3{font-size:14px}.control-api>div{display:grid;grid-template-columns:130px minmax(0,1fr);gap:14px;margin:12px 0;font-size:13px}.control-api span{color:var(--deck-tone-97a79e,#97a79e)}.control-api code{overflow-wrap:anywhere}.control-details{border-top:1px solid var(--deck-tone-303a37,#303a37);padding-top:16px;margin-top:20px}.control-card .small{font-size:12px}@media(max-width:600px){.control-modes{gap:8px}.control-modes button{min-width:0;flex:1;padding:12px}.control-api>div{grid-template-columns:1fr;gap:4px}}