diff --git a/README.md b/README.md index 15489ca..bc2c77b 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,22 @@ Die Navigation beginnt mit dem [Dashboard](DASHBOARD.md). Es zeigt die Live-Telemetrie und die aus dem alten Athena-Dashboard übernommene History; die frühere separate Hardware-Seite wurde entfernt. +## Navigation und Detailansichten + +Die **Übersicht** enthält Dashboard und Steuerung (Endpunkt und GPU-Modi). +**KI-Werkzeuge** trennt Chat & Sprachmodelle, Bildgenerierung, Musikgenerierung, +Sprachausgabe, Spracherkennung, Stimmwerkzeuge und Videogenerierung. +Zusätzliche Oberflächen stehen unter **Anwendungen → Weitere Dienste**. + +Unter **Einstellungen → Laufzeiten** führt eine aufklappbare Liste zu den +Installations-, Versions- und Statusseiten. Die einzelnen Laufzeiten stehen +nicht mehr separat in der Seitenleiste. Bibliothek und Profile verwenden +kompakte, aufklappbare Zeilen: Details, API-Freigabe, Komponenten und Aktionen +stehen in der geöffneten Zeile. Downloads behalten Fortschritt, Geschwindigkeit, +Restzeit und Warteschlangenaktionen. Der laufende Betrieb wird zentral unter +**Steuerung** angezeigt; Testbereiche behalten ihre jeweiligen Formulare. +Bestehende URL-Fragmente bleiben gültig. + ## Vorläufige Testinstallation auf Debian Zielprodukt: nativer systemd-Dienst auf Debian. Der derzeitige Docker-Installer dient ausschließlich der isolierten Testphase. diff --git a/catalog-ui.js b/catalog-ui.js index f3759a4..269603c 100644 --- a/catalog-ui.js +++ b/catalog-ui.js @@ -14,7 +14,7 @@ window.CatalogUI = (() => { function html(library, downloads=false) { return `
HUGGING FACE · LIVE-KATALOG

${downloads ? 'Downloads' : library ? 'Deine Dateien auf Athena' : 'Das nächste Modell entdecken'}

${downloads ? 'Abgeschlossene Einträge ausblenden. Die Dateien bleiben in deiner Bibliothek.' : library ? 'Heruntergeladene Dateien mit festgehaltener Version und Integritätsprüfung.' : 'Modell suchen, Variante vergleichen und gezielt auf Athena herunterladen.'}

- ${downloads ? '' : library ? '
' : `

Blendet anhand der Hub-Metadaten Finetunes, LoRAs, Merges und separate Quantisierungen aus. Unvollständig gekennzeichnete Ableitungen können sichtbar bleiben.

Beliebte Modelle
01 ENTDECKEN → 02 AUSWÄHLEN → 03 LADEN

Welches Modell interessiert dich?

Wähle ein Ergebnis, um Dateien, Varianten und Lizenz zu prüfen.

Downloads belegen Festplattenspeicher. Sie starten kein Modell. Für Bild, Audio und Video sind häufig mehrere Dateien und ein passender Worker erforderlich.

`}
`; + ${downloads ? '' : library ? '
' : `

Blendet anhand der Hub-Metadaten Finetunes, LoRAs, Merges und separate Quantisierungen aus. Unvollständig gekennzeichnete Ableitungen können sichtbar bleiben.

Beliebte Modelle
01 ENTDECKEN → 02 AUSWÄHLEN → 03 LADEN

Welches Modell interessiert dich?

Wähle ein Ergebnis, um Dateien, Varianten und Lizenz zu prüfen.

Downloads belegen Festplattenspeicher. Sie starten kein Modell. Für Bild, Audio und Video sind häufig mehrere Dateien und ein passender Worker erforderlich.

`}`; } function bind(kind, library, downloads=false) { const root = document.querySelector('#live-catalog'); if (!root) return; @@ -30,7 +30,8 @@ window.CatalogUI = (() => { root.querySelectorAll('[data-repo]').forEach(b => b.onclick = () => loadFiles(b.dataset.repo)); } function renderLibrary() { - el('hub-results').innerHTML = state.entries.filter(x => x.kind === kind).map(x => `
${escape(x.role_label)} · HERUNTERGELADEN

${escape(x.repo)}

${escape(x.file)}

${size(x.size)}

${x.profile_eligible?'Modelldatei · über ein Profil verwenden':'Zusatzdatei · wird einem Modellprofil unter Komponenten zugeordnet'}

Version und Integrität

Revision ${escape(x.revision)}

${x.upstream_hash_verified ? 'SHA-256 mit Quelle geprüft' : 'SHA-256 lokal erfasst; Größe mit Quelle geprüft'}

${executionHTML(x.execution)}${x.used_by_profiles?.length?`

Verwendet von Profil: ${x.used_by_profiles.map(p=>escape(p.name)).join(", ")}. Bitte zuerst das Profil löschen.

`:""}
${x.profile_eligible===true?``:""}
`).join('') || '

Deine Bibliothek ist noch leer

Unter „Entdecken“ kannst du Dateien für diesen Bereich auswählen und herunterladen.

'; + const openRows=new Set([...root.querySelectorAll('.detail-row[open]')].map(x=>x.dataset.rowId)); + el('hub-results').innerHTML = state.entries.filter(x => x.kind === kind).map(x => `
${escape(x.file)}${escape(x.role_label)}${size(x.size)}${x.used_by_profiles?.length?`${x.used_by_profiles.length} Profil(e)`:'Nicht zugeordnet'}

Quelle: ${escape(x.repo)}

${x.profile_eligible?'Modelldatei · über ein Profil verwenden':'Zusatzdatei · wird einem Modellprofil unter Komponenten zugeordnet'}

Version und Integrität

Revision ${escape(x.revision)}

${x.upstream_hash_verified ? 'SHA-256 mit Quelle geprüft' : 'SHA-256 lokal erfasst; Größe mit Quelle geprüft'}

${executionHTML(x.execution)}${x.used_by_profiles?.length?`

Verwendet von Profil: ${x.used_by_profiles.map(p=>escape(p.name)).join(", ")}. Bitte zuerst das Profil löschen.

`:""}
${x.profile_eligible===true?``:""}
`).join('') || '

Deine Bibliothek ist noch leer

Unter „Entdecken“ kannst du Dateien für diesen Bereich auswählen und herunterladen.

'; root.querySelectorAll('[data-delete-model]').forEach(b=>b.onclick=async()=>{const entry=state.entries.find(x=>x.id===b.dataset.deleteModel);if(!entry||!confirm(`Datei „${entry.file}“ (${size(entry.size)}) dauerhaft von Athena löschen?`))return;b.disabled=true;try{await api('/delete',{id:entry.id});message('Datei aus der Bibliothek gelöscht.');await refresh();}catch(error){message(error.message);b.disabled=false;}}); } function renderJob() { @@ -38,7 +39,7 @@ window.CatalogUI = (() => { const jobs=downloads ? (state.downloads||[]).filter(j=>j.kind===kind) : state.job?.state==='downloading' ? [state.job] : []; const signature=JSON.stringify(jobs); if(signature===lastJobs)return; lastJobs=signature; const labels={queued:'Warteschlange',downloading:'Wird heruntergeladen',complete:'Download abgeschlossen',cancelled:'Download abgebrochen',failed:'Download fehlgeschlagen',interrupted:'Download unterbrochen'}; - el('hub-job').innerHTML=jobs.map(j=>{const percent=j.total>0?Math.min(100,Math.max(0,j.bytes/j.total*100)):0;return `
${escape(labels[j.state]||j.state)}${percent.toFixed(1)} %

${escape(j.repo)}

${escape(j.file)}

${size(j.bytes)} von ${size(j.total)}${j.state==='downloading'?` · ${Number.isFinite(j.bytes_per_second)?size(j.bytes_per_second)+'/s':'Geschwindigkeit wird ermittelt …'} · Restzeit: ${Number.isFinite(j.eta_seconds)?Math.ceil(j.eta_seconds/60)+' Min.':'–'}`:''}

${j.error?`

${escape(j.error)}

`:''}${j.state==='downloading'?'':`${j.state==='queued'?'Download noch nicht gestartet':'Datei bleibt erhalten'}`}
`;}).join('')||(downloads?'

Keine Downloads in dieser Liste

Heruntergeladene Modelldateien findest du weiterhin in der Bibliothek.

':''); + el('hub-job').innerHTML=jobs.map(j=>{const percent=j.total>0?Math.min(100,Math.max(0,j.bytes/j.total*100)):0;return `
${escape(labels[j.state]||j.state)}${percent.toFixed(1)} %

${escape(j.repo)}

${escape(j.file)}

${size(j.bytes)} von ${size(j.total)}${j.state==='downloading'?` · ${Number.isFinite(j.bytes_per_second)?size(j.bytes_per_second)+'/s':'Geschwindigkeit wird ermittelt …'} · Restzeit: ${Number.isFinite(j.eta_seconds)?Math.ceil(j.eta_seconds/60)+' Min.':'–'}`:''}

${j.error?`

${escape(j.error)}

`:''}${j.state==='downloading'?'':`${j.state==='queued'?'Download noch nicht gestartet':'Datei bleibt erhalten'}`}
`;}).join('')||(downloads?'

Keine Downloads in dieser Liste

Heruntergeladene Modelldateien findest du weiterhin in der Bibliothek.

':''); root.querySelectorAll('[data-cancel]').forEach(b=>b.onclick=async()=>{b.disabled=true;try{await api('/cancel',{});message('Abbruch angefordert; die Netzwerkabfrage kann bis zu 20 Sekunden dauern.');}catch(error){message(error.message);b.disabled=false;}}); root.querySelectorAll('[data-dismiss]').forEach(b=>b.onclick=async()=>{b.disabled=true;try{await api('/dismiss',{id:b.dataset.dismiss});message('Eintrag ausgeblendet. Die Modelldatei bleibt in der Bibliothek.');await refresh();}catch(error){message(error.message);b.disabled=false;}}); } diff --git a/endpoint-ui.js b/endpoint-ui.js index 73eedb4..2a46b1a 100644 --- a/endpoint-ui.js +++ b/endpoint-ui.js @@ -8,7 +8,7 @@ window.EndpointUI=(()=>{ async function render(settings=false){ const view=document.querySelector('#view');let root=view.querySelector('#endpoint-page'); if(!root||root.dataset.settings!==String(settings)){ - sequence++;view.innerHTML=`
${settings?'EINSTELLUNGEN / API':'ATHENA DECK / ROUTER'}

${settings?'Endpunkt & Profile':'Dein Modell-Endpunkt'}

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. 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.

':''}
`; + sequence++;view.innerHTML=`
${settings?'EINSTELLUNGEN / API':'ATHENA DECK / ROUTER'}

${settings?'Endpunkt':'Steuerung'}

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.

':''}
`; root=view.querySelector('#endpoint-page'); 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)});}); diff --git a/index.html b/index.html index 6f75ff0..a86d788 100644 --- a/index.html +++ b/index.html @@ -1 +1 @@ -Athena Deck
ATHENA CONTROL SURFACEv0.7 · Router
+Athena Deck
ATHENA CONTROL SURFACEv0.7 · Router
diff --git a/profiles-ui.js b/profiles-ui.js index 2ec140d..7194f7f 100644 --- a/profiles-ui.js +++ b/profiles-ui.js @@ -7,9 +7,10 @@ window.ProfilesUI=(()=>{ const root=document.querySelector('#live-profiles'),el=id=>root.querySelector('#'+id);let rows=[],models=[],schema={},enabled=[],gpus=[],projectors=[],entries=[],panelSequence=0; const message=t=>{if(root.isConnected)el('profile-message').textContent=t;}; async function load(){try{const [p,c,h,ep]=await Promise.all([api('profiles'),api('catalog'),['chat','video'].includes(kind)?api('hardware').catch(()=>({gpus:[]})):Promise.resolve({gpus:[]}),api('endpoint')]);entries=c.entries;projectors=c.entries.filter(x=>x.role==='vision_projector'&&x.file.endsWith('.gguf'));enabled=ep.profiles.filter(x=>x.enabled).map(x=>x.id);gpus=h.gpus||[];if(!root.isConnected)return;rows=p.profiles.filter(x=>x.kind===kind);models=c.entries.filter(x=>x.kind===kind&&x.profile_eligible===true);schema=p.schemas[kind];el('profile-new').disabled=!models.length; - el('profile-list').innerHTML=rows.map(p=>`
GESPEICHERT AUF ATHENA

${e(p.name)}

${e(p.model?.file||'Modelldatei nicht verfügbar')}

${Object.entries(p.parameters).map(([k,v])=>`${e(labels[k]||k)}: ${e(['video_device','text_encoder_device'].includes(k)?({auto:'Automatisch · RTX 5080 bevorzugt',same:'Wie Videopipeline'}[v]||gpus.find(g=>g.uuid===v)?.name||v):k==='gpu_reserve_mode'?({auto:'Automatisch',manual:'Manuell',none:'Keine zusätzliche Reserve'}[v]||v):k==='gpu_reserve_mib'?(Object.entries(v).map(([id,n])=>`${gpus.find(g=>g.uuid===id)?.name||id}: ${n} MiB`).join(', ')||'gemäß Reservemodus'):k==='gpu_devices'?(v.length?v.map((id,i)=>`CUDA${i}: ${gpus.find(g=>g.uuid===id)?.name||id}`).join(', '):'Automatisch'):Array.isArray(v)?(v.join(', ')||'Automatisch'):v)}`).join(' · ')}

${kind==='image'?`

Prompt-Aufwerter: Text-zu-Bild ${p.prompt_enhancer?.t2i?'an':'aus'} · Bildbearbeitung ${p.prompt_enhancer?.i2i?'an':'aus'} · Gerät ${e(p.prompt_enhancer?.device||'auto')}

`:''}${CatalogUI.executionHTML(p.execution)}
${p.runnable?(kind==='video'?'Laufzeit bereit · Video-Modus in Übersicht aktivieren':'Laufzeit bereit · lädt bei Anfrage'):'Was fehlt zur Ausführung?'}${p.blockers.map(t=>`

${e(t)}

`).join('')}
${kind==='image'?``:''}
`).join('')||`

Noch keine Profile

${models.length?'Wähle eine heruntergeladene Modelldatei und lege dein erstes Profil an.':'Lade zuerst eine Gewichtsdatei unter Entdecken herunter.'}

`; - el('profile-list').insertAdjacentHTML('afterbegin',`

${kind==='image'?'Der feste API-Name athena-image verwendet das hier freigegebene Bildprofil und dessen Auflösung. Höchstens ein Bildprofil ist am API-Endpunkt freigegeben. Ein neuer Haken ersetzt die bisherige Auswahl.':['audio','stt'].includes(kind)?'Eingerichtete Sprachprofile können am API-Endpunkt freigegeben werden.':kind==='chat'?'Mehrere Sprachmodellprofile können gleichzeitig am API-Endpunkt freigegeben sein.':kind==='video'?'Genau ein Videoprofil kann am API-Endpunkt freigegeben sein. Ein neuer Haken ersetzt die bisherige Auswahl. Die Übersicht lädt dieses Profil beim Wechsel auf Video; API-Name: athena-video. Zum Ändern der Freigabe zuerst auf LLM wechseln.':'Für diesen Bereich ist noch keine ausführbare Laufzeit am Endpunkt angebunden.'} Freigegeben bedeutet nicht bereits in den GPU-Speicher geladen.

`); - root.querySelectorAll('[data-publish]').forEach(b=>b.onchange=async()=>{const checked=b.checked;root.querySelectorAll('[data-publish]').forEach(x=>x.disabled=true);try{await api('endpoint/profile',{id:b.dataset.publish,enabled:checked});await load();message(kind==='video'?'Video-Freigabe gespeichert. Die Übersicht lädt dieses Profil beim Wechsel auf Video.':'Endpunkt-Freigabe gespeichert. Geladen wird erst bei einer API-Anfrage.');}catch(error){await load();message(error.message);}}); + const openRows=new Set([...root.querySelectorAll('.detail-row[open]')].map(x=>x.dataset.rowId)); + el('profile-list').innerHTML=rows.map(p=>`
${e(p.name)}${e(p.model?.file||'Modelldatei nicht verfügbar')}${p.parameters.context?`${e(p.parameters.context)} Token · ${e(p.parameters.slots||1)} Slots`:e(p.execution?.runtime||'Laufzeit prüfen')}${enabled.includes(p.id)?'API: freigegeben':'API: nicht freigegeben'}${p.runnable?'Bereit · lädt bei Anfrage':'Einrichtung fehlt'}

${Object.entries(p.parameters).map(([k,v])=>`${e(labels[k]||k)}: ${e(['video_device','text_encoder_device'].includes(k)?({auto:'Automatisch · RTX 5080 bevorzugt',same:'Wie Videopipeline'}[v]||gpus.find(g=>g.uuid===v)?.name||v):k==='gpu_reserve_mode'?({auto:'Automatisch',manual:'Manuell',none:'Keine zusätzliche Reserve'}[v]||v):k==='gpu_reserve_mib'?(Object.entries(v).map(([id,n])=>`${gpus.find(g=>g.uuid===id)?.name||id}: ${n} MiB`).join(', ')||'gemäß Reservemodus'):k==='gpu_devices'?(v.length?v.map((id,i)=>`CUDA${i}: ${gpus.find(g=>g.uuid===id)?.name||id}`).join(', '):'Automatisch'):Array.isArray(v)?(v.join(', ')||'Automatisch'):v)}`).join(' · ')}

${kind==='image'?`

Prompt-Aufwerter: Text-zu-Bild ${p.prompt_enhancer?.t2i?'an':'aus'} · Bildbearbeitung ${p.prompt_enhancer?.i2i?'an':'aus'} · Gerät ${e(p.prompt_enhancer?.device||'auto')}

`:''}${CatalogUI.executionHTML(p.execution)}
${p.runnable?(kind==='video'?'Laufzeit bereit · Video-Modus in Steuerung aktivieren':'Laufzeit bereit · lädt bei Anfrage'):'Was fehlt zur Ausführung?'}${p.blockers.map(t=>`

${e(t)}

`).join('')}
${kind==='image'?``:''}
`).join('')||`

Noch keine Profile

${models.length?'Wähle eine heruntergeladene Modelldatei und lege dein erstes Profil an.':'Lade zuerst eine Gewichtsdatei unter Entdecken herunter.'}

`; + el('profile-list').insertAdjacentHTML('afterbegin',`

${kind==='image'?'Der feste API-Name athena-image verwendet das hier freigegebene Bildprofil und dessen Auflösung. Höchstens ein Bildprofil ist am API-Endpunkt freigegeben. Ein neuer Haken ersetzt die bisherige Auswahl.':['audio','stt'].includes(kind)?'Eingerichtete Sprachprofile können am API-Endpunkt freigegeben werden.':kind==='chat'?'Mehrere Sprachmodellprofile können gleichzeitig am API-Endpunkt freigegeben sein.':kind==='video'?'Genau ein Videoprofil kann am API-Endpunkt freigegeben sein. Ein neuer Haken ersetzt die bisherige Auswahl. Die Steuerung lädt dieses Profil beim Wechsel auf Video; API-Name: athena-video. Zum Ändern der Freigabe zuerst auf LLM wechseln.':kind==='music'?'Gib ein eingerichtetes Musikprofil frei, um es in der Steuerung verwenden zu können.':'Für diesen Bereich ist noch keine ausführbare Laufzeit am Endpunkt angebunden.'} Freigegeben bedeutet nicht bereits in den GPU-Speicher geladen.

`); + root.querySelectorAll('[data-publish]').forEach(b=>b.onchange=async()=>{const checked=b.checked;root.querySelectorAll('[data-publish]').forEach(x=>x.disabled=true);try{await api('endpoint/profile',{id:b.dataset.publish,enabled:checked});await load();message(kind==='video'?'Video-Freigabe gespeichert. Die Steuerung lädt dieses Profil beim Wechsel auf Video.':'Endpunkt-Freigabe gespeichert. Geladen wird erst bei einer API-Anfrage.');}catch(error){await load();message(error.message);}}); root.querySelectorAll('[data-delete]').forEach(b=>b.onclick=async()=>{const p=rows.find(p=>p.id===b.dataset.delete);if(!confirm(`Profil „${p.name}“ löschen? Modelldateien und Komponenten bleiben in der Bibliothek.`))return;b.disabled=true;try{await api('profiles/delete',{id:p.id,revision:p.revision});panelSequence++;el('profile-editor').replaceChildren();await load();message('Profil gelöscht. Modelldateien und Komponenten bleiben erhalten.');}catch(error){message(error.message);b.disabled=false;}}); root.querySelectorAll('[data-components]').forEach(b=>b.onclick=()=>components(rows.find(p=>p.id===b.dataset.components))); root.querySelectorAll('[data-enhancers]').forEach(b=>b.onclick=()=>enhancers(rows.find(p=>p.id===b.dataset.enhancers))); diff --git a/studio.js b/studio.js index fb5fb5f..0f26ef8 100644 --- a/studio.js +++ b/studio.js @@ -1,14 +1,14 @@ /* Server-backed model management. Legacy browser drafts remain untouched. */ const Studio=(()=>{ let section='discover',category=''; - const labels={chat:'Sprachmodelle / Chat',image:'Bildgenerierung',audio:'Text-to-Speech (TTS)',stt:'Spracherkennung (STT)',music:'Musik',voice:'Stimme / Voice',video:'Video'}; + const labels={chat:'Chat & Sprachmodelle',image:'Bildgenerierung',audio:'Sprachausgabe (TTS)',stt:'Spracherkennung (STT)',music:'Musikgenerierung',voice:'Stimmwerkzeuge',video:'Videogenerierung'}; const audioDescriptions={audio:'Geschriebenen Text vorlesen lassen. Die Suche zeigt Text-to-Speech-Modelle.',stt:'Gesprochene Sprache in Text umwandeln. Die Suche zeigt Spracherkennungsmodelle.',music:'Musik und Klänge aus Text erzeugen. Die Hub-Kategorie Text-to-Audio umfasst neben Musik auch Geräusche und andere Audioerzeugung.',voice:'Vorhandenes Audio bearbeiten: Stimmumwandlung, Audiotrennung und weitere Audio-to-Audio-Modelle. Eigenständige Studio-Oberflächen gehören unter Weitere Dienste.'}; function render(page,force=false,modelId=null){ if(page==='video'){VideoUI.runtime();return;} if(!force&&document.querySelector('#studio')?.dataset.page===page)return; if(category!==page){category=page;section='discover';} if(modelId)section='profiles'; - const body=page==='stt-runtime'?STTUI.html(true):page==='tts-runtime'?TTSUI.html(true):['docker-runtime','services'].includes(page)?DockerUI.html(page==='services'):page==='runtimes'?runtimeOverview():page==='image-runtime'?imageRuntime():page==='runtime'?RuntimeUI.html():`
ATHENA / MODELLVERWALTUNG

${labels[page]}

${audioDescriptions[page]||'Modelle entdecken, herunterladen und mit gespeicherten Profilen konfigurieren.'}

${audioDescriptions[page]&&!['audio','stt','music'].includes(page)?'

Entdecken und Downloads sind verfügbar. Für diese Audio-Bereiche ist noch keine ausführbare Laufzeit in Deck angebunden. Gespeicherte Profile sind vorbereitend.

':''}
${[['discover','Entdecken'],['library','Bibliothek'],['downloads','Downloads'],['profiles','Profile'],...(['audio','stt'].includes(page)?[['setup','Einrichten']]:[]),...(['image','chat','audio','stt','music'].includes(page)?[['test','Testen'],...(page==='chat'?[['auto','Auto-Test']]:[])]:[]),['running','Laufend']].map(([id,label])=>``).join('')}
${section==='setup'?(page==='stt'?STTUI.html(true):TTSUI.html(true)):['test','running'].includes(section)&&page==='music'?MusicUI.html():['test','running'].includes(section)&&page==='stt'?STTUI.html():['test','running'].includes(section)&&page==='audio'?TTSUI.html():section==='auto'?AutoTestUI.html():section==='test'?(page==='chat'?ChatTestUI.html():ImageTestUI.html()):section==='running'&&page==='image'?ImageTestUI.html(true):section==='profiles'?ProfilesUI.html():section==='running'&&page==='chat'?EndpointUI.runningHTML():section==='running'?'

Keine von Deck gestarteten Modelle

Profile werden auf Athena gespeichert. Für diesen Bereich ist noch kein Modellworker angebunden. Fehlende Komponenten stehen beim jeweiligen Profil.

llama.cpp-Builds verwalten →
':CatalogUI.html(section==='library',section==='downloads')}
`; + const body=page==='stt-runtime'?STTUI.html(true):page==='tts-runtime'?TTSUI.html(true):['docker-runtime','services'].includes(page)?DockerUI.html(page==='services'):page==='runtimes'?runtimeOverview():page==='image-runtime'?imageRuntime():page==='runtime'?RuntimeUI.html():`
ATHENA / MODELLVERWALTUNG

${labels[page]}

${audioDescriptions[page]||'Modelle entdecken, herunterladen und mit gespeicherten Profilen konfigurieren.'}

${audioDescriptions[page]&&!['audio','stt','music'].includes(page)?'

Entdecken und Downloads sind verfügbar. Für diese Audio-Bereiche ist noch keine ausführbare Laufzeit in Deck angebunden. Gespeicherte Profile sind vorbereitend.

':''}
${[['discover','Entdecken'],['library','Bibliothek'],['downloads','Downloads'],['profiles','Profile'],...(['audio','stt'].includes(page)?[['setup','Einrichten']]:[]),...(['image','chat','audio','stt','music'].includes(page)?[['test','Testen'],...(page==='chat'?[['auto','Auto-Test']]:[])]:[])].map(([id,label])=>``).join('')}
${section==='setup'?(page==='stt'?STTUI.html(true):TTSUI.html(true)):['test','running'].includes(section)&&page==='music'?MusicUI.html():['test','running'].includes(section)&&page==='stt'?STTUI.html():['test','running'].includes(section)&&page==='audio'?TTSUI.html():section==='auto'?AutoTestUI.html():section==='test'?(page==='chat'?ChatTestUI.html():ImageTestUI.html()):section==='running'&&page==='image'?ImageTestUI.html(true):section==='profiles'?ProfilesUI.html():section==='running'&&page==='chat'?EndpointUI.runningHTML():section==='running'?'

Keine von Deck gestarteten Modelle

Profile werden auf Athena gespeichert. Für diesen Bereich ist noch kein Modellworker angebunden. Fehlende Komponenten stehen beim jeweiligen Profil.

llama.cpp-Builds verwalten →
':CatalogUI.html(section==='library',section==='downloads')}
`; document.querySelector('#view').innerHTML=`
${body}
`; if(['docker-runtime','services'].includes(page)){DockerUI.bind(page==='services');return;} if(page==='stt-runtime'){STTUI.bind(true);return;} @@ -28,7 +28,11 @@ const Studio=(()=>{ else if(section!=='running')CatalogUI.bind(page,section==='library',section==='downloads'); document.querySelectorAll('[data-section]').forEach(b=>b.onclick=()=>{section=b.dataset.section;render(page,true);}); } - function runtimeOverview(){return `
EINSTELLUNGEN

Laufzeiten

Laufzeiten werden einmal eingerichtet und für kompatible Modelle wiederverwendet. Entdecken, Bibliothek und Profile zeigen den passenden Einrichtungsweg; unbekannte Modelle werden als noch nicht unterstützt gekennzeichnet. Textencoder und VAE werden separat in der Modellbibliothek verwaltet und einem Profil zugeordnet.

Spracherkennung

Qwen3-ASR mit llama.cpp und Audio-Projektor auf der CPU.

STT einrichten →

Text-to-Speech

Qwen3-TTS mit CUDA und eingebauten Stimmen einrichten.

TTS einrichten →

llama.cpp

Sprachmodelle · CPU / CUDA. Versionen herunterladen, bauen und verwalten.

llama.cpp öffnen →

Bildgenerierung

Eigene Bildlaufzeit und modellabhängige Zusatzkomponenten.

Bildlaufzeit öffnen →

audio.cpp

Gemeinsame Audio-Laufzeit; Deck-Worker werden separat angebunden.

audio.cpp herunterladen / bereitmachen →

LTX Original

Originales LTX-Desktop-Backend mit nativer LTX-API für LTX DeskWEB.

LTX Original einrichten →

Docker

Optionale Container-Umgebung für zusätzliche Oberflächen und Studios.

Docker öffnen →
`;} + function runtimeOverview(){ + const rows=[['llama.cpp','Chat & Sprachmodelle','CPU / CUDA · Versionen herunterladen, bauen und verwalten.','runtime'],['Bildlaufzeit','Bildgenerierung','Vorhandene Laufzeiten für kompatible Modelle wiederverwenden.','image-runtime'],['Sprachausgabe','TTS','Qwen3-TTS mit CUDA und eingebauten Stimmen.','tts-runtime'],['Spracherkennung','STT','Qwen3-ASR mit llama.cpp und Audio-Projektor.','stt-runtime'],['audio.cpp','Musik & Audio','Gemeinsame Audio-Laufzeit und modellabhängige Worker.','audio-cpp-runtime'],['LTX Original','Video','Originales LTX-Backend mit nativer API für LTX DeskWEB.','ltx-original-runtime'],['Docker','Weitere Dienste','Optionale Container-Umgebung für zusätzliche Oberflächen.','docker-runtime']]; + return `
EINSTELLUNGEN

Laufzeiten

Einmal einrichten, für passende Modelle wiederverwenden. Öffne eine Laufzeit, um ihren Installationsstatus, Versionen und Optionen zu sehen. Zusatzdateien verwaltest du beim Profil unter Einrichtung & Komponenten.

${rows.map(([name,category,description,route])=>`
${name}${category}Einrichtung & Versionen
`).join('')}
`; + } + function imageRuntime(){return `
EINSTELLUNGEN / LAUFZEITEN

Bildgenerierung

ComfyUI + ComfyUI-GGUF

Eigene Bildlaufzeit für Qwen-Image-2.1 GGUF und das unterstützte FLUX.2-Klein-9B-Modell. Die Umgebung kann über den Deck-Installer mitgeliefert oder hier separat installiert werden. Modelle laden erst bei einem Testauftrag.

Textencoder läuft auf der RTX 3060, Diffusionsmodell und VAE auf der RTX 5080. Bei großen FLUX-Gewichten wird zusätzlich System-RAM genutzt. Beide GPUs müssen frei sein. Pro Test wird ein eigener Prozess gestartet und anschließend beendet.

Installationsstatus wird geprüft …

Bildgenerierung → Testen öffnen
${ImageTestUI.html(true)}`;} function openProfile(page,modelId){render(page,true,modelId);} return {render,openProfile}; diff --git a/style.css b/style.css index 4eddc79..451ea74 100644 --- a/style.css +++ b/style.css @@ -38,3 +38,7 @@ nav a.nav-sub{padding-left:28px;font-size:12px;color:var(--deck-tone-a4bda8, #a4 .deck-preview{background:#101416}.deck-preview i{background:#181f21;border:1px solid #303a37}.deck-preview i:nth-child(2){border-top:5px solid #c5edaf} .classic-preview{background:radial-gradient(circle at 15% -10%,#183049 0,transparent 70%),#070b11}.classic-preview i{background:linear-gradient(145deg,#111b28,#0a1018);border:1px solid #213044}.classic-preview i:nth-child(2){border-top:5px solid #45d7ff} @media(max-width:700px){.theme-options{grid-template-columns:1fr}} + +/* 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}} diff --git a/video-ui.js b/video-ui.js index 0180261..4c4496a 100644 --- a/video-ui.js +++ b/video-ui.js @@ -12,12 +12,12 @@ window.VideoUI=(()=>{ root.innerHTML=`

Video-Laufzeit

${s.runtime?.installed?'Vorhanden · bereit':s.runtime_kind==='original'?'Nicht installiert · unter Einstellungen → LTX Original einrichten':'Nicht installiert · unter Einstellungen → Bildgenerierung einrichten'}

5080: Diffusionsmodell und VAE. 3060: Textencoder. Große BF16-Dateien nutzen zusätzlich CPU-Auslagerung. Gewichte laden erst bei einer Anfrage.

LTX Original installieren / Status →
${(s.models||[]).map(x=>`

${e(x.model.file)}

${e(x.label)}${x.blockers.map(b=>`

${e(b)}

`).join('')}

${Object.keys(x.components).length} Komponenten geprüft · gleiche Quellversion

${x.model.id===s.selected&&s.mode==='llm'?'':''}
`).join('')||'

Kein unterstütztes LTX-Modell in der Bibliothek. Unter Entdecken herunterladen.

'}

`; root.querySelector('#video-runtime-choice').onchange=async event=>{root.dataset.busy='1';try{await api('video/runtime',{runtime:event.target.value});}catch(x){root.querySelector('.video-message').textContent=x.message;}finally{root.dataset.busy='0';poll();}}; root.querySelectorAll('[data-components-model]').forEach(b=>b.onclick=async()=>{root.dataset.busy='1';try{const data=await api('profiles/components?'+new URLSearchParams({model_id:b.dataset.componentsModel}));root.innerHTML=`

LTX-Komponenten

${e(data.message)}

${data.requirements.map((r,i)=>`

${e(r.label)} · ${r.available.length?'Bereits heruntergeladen':r.candidates.length?'Fehlt':'In dieser Quellversion nicht gefunden'}

${!r.available.length?r.candidates.map((c,j)=>``).join(''):''}`).join('')}

`;root.querySelector('[data-close-components]').onclick=()=>{root.dataset.busy='0';poll();};root.querySelectorAll('[data-download-component]').forEach(d=>d.onclick=async()=>{const [i,j]=d.dataset.downloadComponent.split(':');const f=data.requirements[i].candidates[j];d.disabled=true;try{await api('catalog/download',{repo:f.repo,filename:f.name,revision:f.revision,kind:'video'});root.querySelector('.video-message').textContent='Download eingeplant. Fortschritt unter Downloads. Nach Abschluss werden die Komponenten automatisch erkannt.';}catch(x){root.querySelector('.video-message').textContent=x.message;d.disabled=false;}});}catch(x){root.dataset.busy='0';root.querySelector('.video-message').textContent=x.message;}}); - root.querySelectorAll('[data-select]').forEach(b=>b.onclick=async()=>{root.dataset.busy='1';try{await api('video/service',{id:b.dataset.select});root.querySelector('.video-message').textContent='Videomodell ausgewählt. Start über die Übersicht.';}catch(x){root.querySelector('.video-message').textContent=x.message;}finally{root.dataset.busy='0';}}); + root.querySelectorAll('[data-select]').forEach(b=>b.onclick=async()=>{root.dataset.busy='1';try{await api('video/service',{id:b.dataset.select});root.querySelector('.video-message').textContent='Videomodell ausgewählt. Start über die Steuerung.';}catch(x){root.querySelector('.video-message').textContent=x.message;}finally{root.dataset.busy='0';}}); }}catch(x){root.textContent=x.message;}if(root.isConnected)setTimeout(poll,5000);}poll(); } function runtime(){ const view=document.querySelector('#view');if(view.querySelector('#video-service-page'))return; - view.innerHTML='

Video

LTX Original mit LTX DeskWEB oder die vorhandene ComfyUI-Laufzeit auswählen. Deck verwaltet Modelle, Komponenten und den GPU-Modus; Generierungsparameter gehören in die jeweilige Video-Oberfläche.

'; + view.innerHTML='

Videogenerierung

LTX Original mit LTX DeskWEB oder die vorhandene ComfyUI-Laufzeit auswählen. Deck verwaltet Modelle, Komponenten und den GPU-Modus; Generierungsparameter gehören in die jeweilige Video-Oberfläche.

'; function show(tab){const root=view.querySelector('#video-content');if(tab==='runtime'){root.innerHTML='
';services(root.firstChild);}else{root.innerHTML=CatalogUI.html(tab==='library',tab==='downloads');CatalogUI.bind('video',tab==='library',tab==='downloads');}} view.querySelectorAll('[data-video-tab]').forEach(b=>b.onclick=()=>show(b.dataset.videoTab));show('runtime'); }