diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 487afb7..22cb359 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -1,29 +1,28 @@ import { mount } from 'svelte' import { api, getToken, setToken, getHfToken, setHfToken } from '@core/api.js' import { initNav } from '@core/nav.js' +import { ICON } from '@core/ui.js' import { statusStore } from './stores/status.svelte.js' import { jobsStore } from './stores/jobs.svelte.js' -import GuidesPanel from './panels/GuidesPanel.svelte' +import { systemStore } from './stores/system.svelte.js' + +// Svelte panels — vollständig migriert +import OverviewPanel from './panels/OverviewPanel.svelte' +import ModelsPanel from './panels/ModelsPanel.svelte' +import JobsPanel from './panels/JobsPanel.svelte' +import ServerPanel from './panels/ServerPanel.svelte' +import CookbookPanel from './panels/CookbookPanel.svelte' +import ConnectPanel from './panels/ConnectPanel.svelte' import NewsPanel from './panels/NewsPanel.svelte' +import GuidesPanel from './panels/GuidesPanel.svelte' -// Legacy panels (bundled via Vite; guides + news sind durch Svelte ersetzt) -import overview from '@panels/overview.js' -import models from '@panels/models.js' -import server from '@panels/server.js' -import jobs from '@panels/jobs.js' -import cookbook from '@panels/cookbook.js' -import connect from '@panels/connect.js' - -const legacyPanels: any[] = [overview, models, server, jobs, cookbook, connect] let prevModelStates: Record = {} -// ---- Swap-Flash ---- function flashSwap() { const el = document.getElementById('top-active-text') if (el) { el.classList.add('swap-flash'); setTimeout(() => el.classList.remove('swap-flash'), 900) } } -// ---- Status verteilen ---- function applyStatus(s: any) { const dot = document.getElementById('swdot') const label = document.getElementById('swlabel') @@ -47,7 +46,6 @@ function applyStatus(s: any) { if (s.swap_ok) hideAlert() else showAlert(`LLM-Engine nicht erreichbar unter ${host} – läuft der llama-swap Dienst?`, true) - // Swap-Erkennung const newStates: Record = {} for (const m of (s.models || [])) newStates[m.name] = m.state for (const [name, state] of Object.entries(newStates)) { @@ -58,7 +56,6 @@ function applyStatus(s: any) { } setSecChip(s) statusStore.set(s) - for (const p of legacyPanels) p.onStatus?.(s) } function setSecChip(s: any) { @@ -71,11 +68,10 @@ function setSecChip(s: any) { function applyJobs(jobs: any[]) { jobsStore.set(jobs || []) - for (const p of legacyPanels) p.onJobs?.(jobs) } function applySystem(sys: any) { - for (const p of legacyPanels) p.onSystem?.(sys) + systemStore.set(sys) } function showAlert(html: string, warn: boolean) { @@ -89,7 +85,7 @@ function hideAlert() { if (a) a.style.display = 'none' } -// ---- Update-Badges ---- +// Update badges const goView = (v: string) => (document.querySelector(`.nav-item[data-view="${v}"]`) as HTMLElement)?.click() function mkBadge(text: string, cls: string, view: string, title?: string) { @@ -111,16 +107,14 @@ async function pollUpdates() { el.innerHTML = '' el.appendChild(mkBadge( u.os > 0 ? `OS: ${u.os} Updates` : stale ? 'OS: ?' : 'OS: aktuell', - u.os > 0 ? 'warn' : stale ? 'warn' : 'ok', - 'server', - u.os > 0 ? `${u.os} ausstehende Pakete${ageNote}` : stale ? `apt-Cache veraltet (${age}h)` : `Keine Updates${ageNote}` + u.os > 0 ? 'warn' : stale ? 'warn' : 'ok', 'server', + u.os > 0 ? `${u.os} Pakete${ageNote}` : stale ? `apt-Cache veraltet (${age}h)` : `Keine Updates${ageNote}` )) el.appendChild(mkBadge(u.engine > 0 ? 'Engine: Update' : 'Engine: aktuell', u.engine > 0 ? 'warn' : 'ok', 'server')) el.appendChild(mkBadge(u.models > 0 ? `Modelle: ${u.models}` : 'Modelle: aktuell', u.models > 0 ? 'warn' : 'ok', 'news')) } catch { /* noop */ } } -// ---- Polling ---- async function pollStatus() { try { applyStatus(await api('/api/status')) } catch { applyStatus(null) } @@ -140,15 +134,28 @@ function connectSystemStream() { // ---- Boot ---- -// Legacy panels mounten (overview, models, server, jobs, cookbook, connect) -for (const p of legacyPanels) p.mount?.() +// Icons in Nav/Logo injizieren +document.getElementById('logo')?.innerHTML !== undefined && + (document.getElementById('logo')!.innerHTML = ICON.logo) +document.querySelectorAll('[data-ic]').forEach(n => { + (n as HTMLElement).innerHTML = ICON[(n as HTMLElement).dataset.ic || ''] || '' +}) -// Svelte panels mounten (guides, news) -const guidesEl = document.querySelector('.view[data-view="guides"]') -if (guidesEl) mount(GuidesPanel, { target: guidesEl }) - -const newsEl = document.querySelector('.view[data-view="news"]') -if (newsEl) mount(NewsPanel, { target: newsEl }) +// Svelte panels mounten +const panels: [string, any][] = [ + ['overview', OverviewPanel], + ['models', ModelsPanel], + ['activity', JobsPanel], + ['server', ServerPanel], + ['cookbook', CookbookPanel], + ['connect', ConnectPanel], + ['news', NewsPanel], + ['guides', GuidesPanel], +] +for (const [view, Panel] of panels) { + const el = document.querySelector(`.view[data-view="${view}"]`) + if (el) mount(Panel, { target: el }) +} initNav('overview') diff --git a/frontend/src/panels/ConnectPanel.svelte b/frontend/src/panels/ConnectPanel.svelte new file mode 100644 index 0000000..12a2160 --- /dev/null +++ b/frontend/src/panels/ConnectPanel.svelte @@ -0,0 +1,222 @@ + + +
+
+

Verbinden

+
Schritt für Schritt: KI-Modelle in deine Tools einbinden — getestet, erklärt, mit Copy-Paste-Config.
+
+
+ + +
+

1 · Verbindung prüfen

+
Stelle sicher, dass die LLM-Engine erreichbar ist, bevor du Tools konfigurierst.
+ +
+ + +
+ {#if behindProxy()} +
+ + Du öffnest Mission Control über einen Proxy/Namen. Die Engine läuft auf Port 8080 ohne HTTPS direkt am Bosgame. + Trag in deinen Tools die LAN-IP ein: http://192.168.178.151:8080/v1 — nicht den Proxy-Namen. +
+ {/if} +
+ + {#if testResult} + {testResult} + {/if} +
+
+ + +
+

Wie funktioniert das Auto-Swap?

+
+ {#each [ + ['⚡', 'Kein manuelles Starten nötig', 'Wenn du in Zed oder OpenCode „coder" auswählst und eine Anfrage sendest, lädt llama-swap das Modell automatisch — in ca. 2–5 Sekunden.'], + ['🔄', 'Modelle wechseln sich automatisch ab', 'Rufst du ein anderes Modell auf, entlädt llama-swap das aktuelle (nach TTL-Ablauf) und lädt das neue. Es läuft immer nur eines gleichzeitig.'], + ['🧠', 'Zwei Modelle gleichzeitig aktiv halten', 'Du hast 124 GB RAM — genug für z.B. Scout 8B (~5 GB) dauerhaft geladen + ein größeres Modell das swappt. Dafür beim Scout im Modelle-Tab → Konfigurieren die TTL auf 99999 setzen.'] + ] as [emoji, title, desc]} +
+ {emoji} +
+
{title}
+
{desc}
+
+
+ {/each} +
+
+ + +{#if allModels.length} +
+
+

2 · Modelle auswählen

+ {selectedModelIds.length ? `${selectedModelIds.length} von ${allModels.length} ausgewählt` : 'alle werden eingeschlossen'} +
+
Wähle, welche Modelle im Config-Snippet erscheinen sollen.
+
+ {#each allModels as m} + {@const active = selectedModelIds.includes(m.name)} + selectedModelIds = active ? selectedModelIds.filter(x => x !== m.name) : [...selectedModelIds, m.name]} + onkeydown={e => e.key === 'Enter' && (selectedModelIds = active ? selectedModelIds.filter(x => x !== m.name) : [...selectedModelIds, m.name])}> + {m.name} + + {/each} +
+ {#if selectedModelIds.length} +
+ { e.preventDefault(); selectedModelIds = [] }} style="color:var(--mut);font-size:12.5px">Auswahl aufheben (alle) +
+ {/if} +
+{/if} + + +
+

3 · Tool wählen

+
Für welches Werkzeug brauchst du die Konfiguration?
+
+ {#each [ + ['zed', 'Zed', 'Schneller nativer Editor — Empfohlen'], + ['opencode', 'OpenCode', 'Open-Source Coding-Agent (Terminal/Desktop)'], + ['cline', 'Cline / Cursor', 'VS Code Extension, Bring-Your-Own-Key'], + ['openwebui', 'OpenWebUI', 'Browser-Chat-Interface'] + ] as [id, label, desc]} + + {/each} +
+
+ + +
+

4 · Config-Snippet

+ {#if selectedTool === 'zed'} + {@const block = zedBlock(url, activeIds)} +

Öffne die Zed-Settings mit Cmd/Strg + , und füge diesen Block in deine bestehende settings.json ein. Falls bereits ein "bosgame"-Eintrag existiert: vorher löschen.

+
Nur diesen Block einfügen, nicht die ganze Datei ersetzen — sonst gehen andere Einstellungen verloren.
+
+ +
{block}
+
+

API-Key: Öffne den Agent-Panel → Anbieter bosgame → trag irgendeinen Wert ein (z.B. local).

+
Danach im Agent-Panel das Modell bosgame / {firstModel} auswählen.
+ {:else if selectedTool === 'opencode'} + {@const block = openCodeBlock(url, activeIds)} +

Datei anlegen oder öffnen:

+

Windows: %USERPROFILE%\.config\opencode\opencode.json

+
Nur den "provider"-Block einfügen. Falls bereits ein "llama-swap"-Eintrag vorhanden: vorher löschen, sonst erscheinen Modelle doppelt.
+
+ +
{block}
+
+

Danach in OpenCode /modelsllama-swap/{firstModel} wählen.

+ {:else if selectedTool === 'cline'} +

In Cline/Cursor den Provider „OpenAI Compatible" wählen:

+ +
+ + +
+ +
+ + +
+
API-Key: ein beliebiger Wert (z.B. local).
+ {:else if selectedTool === 'openwebui'} +

Settings → Admin Panel → Connections → neue OpenAI-Verbindung:

+ +
+ + +
+ +
+ + +
+ {/if} +
+ + +{#if visionModel} + {@const snippet = `curl ${url}/chat/completions \\\n -H "Content-Type: application/json" \\\n -d '{\n "model": "${visionModel}",\n "messages": [{"role":"user","content":[{"type":"text","text":"Was ist auf dem Bild falsch?"},{"type":"image_url","image_url":{"url":"data:image/png;base64,"}}]}]\n}'`} +
+
+

Bilder einbinden (Vision)

+ {@html icon('check')} „{visionModel}" bereit +
+
Mit Vision-Modellen kannst du Screenshots oder Fehlerbilder direkt mitschicken.
+
+ +
{snippet}
+
+
+{/if} diff --git a/frontend/src/panels/CookbookPanel.svelte b/frontend/src/panels/CookbookPanel.svelte new file mode 100644 index 0000000..50eeb61 --- /dev/null +++ b/frontend/src/panels/CookbookPanel.svelte @@ -0,0 +1,533 @@ + + + + +
+

Cookbook

Modelle finden, installieren und eigene Setups zusammenstellen.
+
+ +
+
+

Use-Case-Setups

+ +
+
Fertige Modell-Bundles für deinen Anwendungsfall. Hardware-Ampel zeigt, ob es auf deinen PC passt.
+ {#if recipeLoading} +
Lade Setups…
+ {:else} +
+ {#each recipes as r} + {@const best = r.id === recommended} + + {/each} +
+ {/if} +
+ + + +
+
+

Aktuell die besten Modelle für dein System

+ {discoverData ? 'aktuell' : ''} + +
+
Täglich aktualisierte Top-Modelle je Kategorie — Hardware-Fit berechnet für dein System. {discoverStale ? '⚠ Cache veraltet.' : ''}
+ {#if discoverLoading && !discoverData} +
Frage Quellen ab…
+ {:else if discoverData?.categories} + {#each discoverData.categories as cat} +
+
+

{cat.title}

+
+
+ {#each cat.models as m} + {@const best = cat.recommended && m.repo === cat.recommended} +
+ {#if best}
★ Beste Wahl für dein System
{/if} +
+
+

{m.name}

+
{m.author} · {m.params_b}B
+
+ {m.fit.text} +
+
+ {@html capChipHtml(ROLE_LABEL[m.role] || capLabel(m.repo))} + {@html moeChipHtml(m.repo)} +
+
{metricLine(m.fit)} · ⬇ {(m.downloads || 0).toLocaleString()}
+ {#each (m.requirements || []) as req} +
⚠ {req}
+ {/each} +
+
+ {#if m.installed} + ✓ installiert + {:else} + + {/if} +
+
+ {/each} +
+
+ {/each} + {/if} +
+ + + +
+

Profi-Suche (HuggingFace)

+
Direkt HuggingFace durchsuchen — oder eine Modell-URL einfügen für den direkten Sprung.
+
+ e.key === 'Enter' && doSearch()}> + + +
+ +
+ {#each ['', 'coder', 'vision', 'reasoning', 'chat', 'agent'] as f} + + {/each} +
+ + {#if searchResults.length} +
+ {#each searchResults as m, i} +
openModel(m.id, m.id.split('/').pop())}> + {#if i === bestResultIdx}
★ Beste Wahl für dein System
{/if} +
+
+

{m.id.split('/').pop()}

+
{m.author || ''}
+
+ {cardFits[i] ? fitWord(cardFits[i]!) : 'prüfe…'} +
+
+ {@html capChipHtml(capLabel(m.id))} + {@html moeChipHtml(m.id)} +
+
+
+ {cardFits[i] ? metricLine({ req_gb: 0, tps: 0 }) : 'Hardware-Fit…'} + ⬇ {(m.downloads || 0).toLocaleString()} +
+
+ {/each} +
+ {/if} +
+ + + +{#if recipeModal} + {@const r = recipeModal} + +{/if} + + + +{#if modelModal} + +{/if} + + + +{#if newRecipeOpen} + +{/if} diff --git a/frontend/src/panels/JobsPanel.svelte b/frontend/src/panels/JobsPanel.svelte new file mode 100644 index 0000000..8e7b74c --- /dev/null +++ b/frontend/src/panels/JobsPanel.svelte @@ -0,0 +1,182 @@ + + +
+
+

Aktivität

+
Live-Auslastung und laufende Aufgaben (Downloads, Updates) mit Protokoll.
+
+
+ + +{#if sys} + {@const gpu = gpuPct()} +
+
+
Prozessor (CPU)
+
{Math.round(sys.cpu?.percent ?? 0)} %
+
{sys.cpu?.temp != null ? `${Math.round(sys.cpu.temp)}° CPU-Temp` : 'Auslastung'}
+
+
+
Arbeitsspeicher
+
{Math.round(sys.ram.percent)} %
+
{fmtBytes(sys.ram.used)} / {fmtBytes(sys.ram.total)}
+
+
+
Grafikspeicher
+
{Math.round(gpu)} %
+
{sys.gpu_temp != null ? `${Math.round(sys.gpu_temp)}° GPU-Temp` : 'VRAM + GTT'}
+
+
+ + +
+

System-Metriken (Bosgame)

+
Live-Auslastung deines Mini-PCs, alle 0,5 Sekunden.
+
+
Arbeitsspeicher (RAM){fmtBytes(sys.ram.used)} / {fmtBytes(sys.ram.total)}
+
Grafikspeicher (VRAM + GTT) + {sys.gpu ? `${fmtBytes(sys.gpu.vram.used + sys.gpu.gtt.used)} / ${fmtBytes(sys.gpu.vram.total + sys.gpu.gtt.total)}` : '–'} +
+
Speicherplatz (Disk){Math.round(sys.disk?.percent ?? 0)} % belegt
+
Temperatur (GPU / CPU) + {sys.gpu_temp != null ? Math.round(sys.gpu_temp) + '°' : '–'} / {sys.cpu?.temp != null ? Math.round(sys.cpu.temp) + '°' : '–'} +
+
+
+
CPU-Verlauf
{@html sparkBars(hist.cpu, '--accent')}
+
RAM-Verlauf
{@html sparkBars(hist.ram, '--purple')}
+
VRAM-Verlauf
{@html sparkBars(hist.gpu, '--on')}
+
+
+{/if} + + +
+
+

Hintergrund-Aufgaben

+ {jobs.length ? (failedCount ? failedCount + ' Fehler' : jobs.length + ' gesamt') : ''} + {#if finishedCount} + + {/if} +
+
Downloads & Updates erscheinen hier mit Live-Protokoll — zum Aufklappen klicken.
+ + {#if !jobs.length} +
+
Gerade nichts los.
+
Alles ruhig — keine laufenden Aufgaben.
+
+ {:else} + {#each jobs as j} + {@const p = jobPct(j)} + {@const eta = j.state === 'running' ? fmtEta(j.eta_s) : ''} + {@const rate = j.state === 'running' && j.rate_bps ? fmtBytes(j.rate_bps) + '/s' : ''} + {@const extra = [eta, rate].filter(Boolean).join(' · ')} + {@const open = tracked.includes(j.id)} +
+
jobsStore.toggle(j.id)} + onkeydown={e => e.key === 'Enter' && jobsStore.toggle(j.id)}> + + {j.label} + {#if p != null} + {Math.round(p)} %{extra ? ` · ${extra}` : ''} + {/if} + {#if j.state === 'running'} + + {:else} + + {/if} + {@html statusBadge(j.state)} +
+ {#if p != null && j.state === 'running'} +
+ {/if} + {#if open} +
{(j.log || []).join('\n')}
+ {/if} +
+ {/each} + {/if} +
diff --git a/frontend/src/panels/ModelsPanel.svelte b/frontend/src/panels/ModelsPanel.svelte new file mode 100644 index 0000000..fe5a320 --- /dev/null +++ b/frontend/src/panels/ModelsPanel.svelte @@ -0,0 +1,248 @@ + + +
+
+
+

Modelle

+
Deine konfigurierten Modelle — testen, Kontext anpassen oder aus dem Speicher werfen.
+
+
+
+ + +
+

Schnelltest

+
Schreib eine Nachricht — das gewählte Modell wird automatisch geweckt.
+ + + + + + {#if chatReply} +
{chatReply}
+ {/if} +
+ + +
+

Modelle & Ports

{allModels.length ? allModels.length + ' konfiguriert' : ''}
+
Modelle laden automatisch, sobald eine Anfrage kommt — du musst nichts manuell starten.
+ + {#if !allModels.length} +
+
Noch keine Modelle konfiguriert
+
Hol dir unter „Cookbook" ein passendes Modell.
+
+ {:else} + + + + {#each allModels as m} + {#if m.incomplete} + + + + + + + + + {:else} + + + + + + + + + {/if} + {/each} + +
ModellKannDetailsStatusPortAktionen
{m.name}
kein Modell hinterlegt
leere Rolleleer + + +
+
+ {m.name} + {#if m.role}{m.role}{/if} +
+ {#if m.meta?.filename}
{m.meta.filename}
{/if} +
+ API-Name: + {#each (m.api_ids?.length ? m.api_ids : [m.name]) as id} + copyToClipboard(id)}>{id} + {/each} +
+
{@html capTags(m.meta?.caps)}{@html details(m.meta)}{@html badge(m.state, m.download_progress)}{m.port ?? 'auto'} + + + + +
+ {/if} +
+ + +{#if cfgOpen && cfgModel} + +{/if} + + +{#if roleOpen} + +{/if} diff --git a/frontend/src/panels/OverviewPanel.svelte b/frontend/src/panels/OverviewPanel.svelte new file mode 100644 index 0000000..331ea68 --- /dev/null +++ b/frontend/src/panels/OverviewPanel.svelte @@ -0,0 +1,209 @@ + + +
+
+

{heroTitle}

+
{heroSub}
+
+
+ +
+
+
LLM-Engine
+
{swapOk ? 'Online' : 'Offline'}
+
llama-swap
+
+
+
Aktives Modell
+
{activeModel ? activeModel.name : 'Keins'}
+
{activeModel ? 'im Grafikspeicher' : 'nichts geladen'}
+
+ {#if sys} + {@const t = sys.gpu_temp ?? sys.cpu?.temp ?? null} +
+
GPU-Temperatur
+
{t != null ? `${Math.round(t)}°` : '–'}
+
{tempWord(t)}
+
+ {@const free = sys.ram.total - sys.ram.used} +
+
Freier Speicher
+
{Math.round(free / 1024 ** 3)} GB
+
von {Math.round(sys.ram.total / 1024 ** 3)} GB
+
+ {:else} +
GPU-Temperatur
messe…
+
Freier Speicher
messe…
+ {/if} +
+ +
+
+

System-Gesundheit

+
So ausgelastet ist dein Mini-PC gerade.
+ {#if sys} + {@const gpu = gpuPct()} + {#each [ + ['Prozessor (CPU)', sys.cpu?.percent ?? 0], + ['Arbeitsspeicher (RAM)', sys.ram.percent], + ['Grafikspeicher (VRAM)', gpu] + ] as [label, pct]} + {@const p = Math.max(0, Math.min(100, (pct as number) || 0))} +
+
{label}{Math.round(p)} %
+
+
+ {/each} + {:else} +
Warte auf Messwerte…
+ {/if} +
+ +
+

Schnellstart

+
Die häufigsten Aufgaben — ein Klick.
+ + + +
+
+ +
+
+
+

Dein Stack

+ {allModels.length ? allModels.length + ' konfiguriert' : ''} +
+ {#if allModels.length} +
+ {#each allModels as m} + {@const downloading = m.state === 'downloading'} + {@const on = ['running','ready','loading','starting'].includes(m.state)} + {@const dot = (m.state === 'loading' || m.state === 'starting' || downloading) ? 'load' : on ? 'on' : ''} + {@const statusText = downloading + ? `↓ Download${m.download_progress != null ? ` ${Math.round(m.download_progress)}%` : ''}` + : on ? (m.state === 'loading' ? 'lädt…' : 'geladen') : 'bereit'} +
+ +
+
+ {m.name} + {#if m.meta?.caps?.length} + {capTagLabel(m.meta.caps)} + {/if} +
+ {#if m.meta?.filename} +
{m.meta.filename}
+ {/if} +
+ {statusText} +
+ {/each} +
+ {:else} +
+
Noch keine Modelle
+
Hol dir unter „Cookbook" ein Modell, das auf deine Hardware passt.
+
+ {/if} +
+ +
+
+

Top-News

+ go('news')} onkeydown={e => e.key === 'Enter' && go('news')}>Alle → +
+ {#if news.length} +
+ {#each news as n} + +
+
{n.title}
+
{n.source}
+
+
+ {/each} +
+ {:else} +
News gerade nicht ladbar.
+ {/if} +
+
diff --git a/frontend/src/panels/ServerPanel.svelte b/frontend/src/panels/ServerPanel.svelte new file mode 100644 index 0000000..febde21 --- /dev/null +++ b/frontend/src/panels/ServerPanel.svelte @@ -0,0 +1,259 @@ + + +
+
+

Server & Wartung

+
Dienste steuern, Updates einspielen und live mitlesen — ganz ohne SSH oder Terminal.
+
+
+ + +
+
+

LLM-Engine (llama-swap)

+ + {status?.swap_ok ? '● erreichbar' : '● offline'} + +
+
Das Herzstück: lädt bei jeder Anfrage automatisch das passende Modell.
+
+
Adresse (für deine Tools){(status?.swap_url || '—') + '/v1'}
+
Aktuell geladen + {loadedModel ? loadedModel.name + ` (${loadedModel.state})` : 'nichts geladen — lädt bei der nächsten Anfrage'} +
+
Konfigurierte Modelle + {swapModels.filter((m: any) => !m.incomplete).length || 'keine'} +
+
+
+ + {#if swapCheckResult} + {swapCheckResult} + {/if} +
+
+ + +
+

Dienste & Applikation

+
Diese Aktionen sind harmlos und passwortlos — es geht nichts dabei verloren.
+ + + + + +
+ + +
+

Betriebssystem (Bosgame)

+
Tiefe Eingriffe — das Dashboard fragt einmalig nach deinem sudo-Passwort.
+ + +
+ + +{#if activeJob} + {@const j = activeJob as any} + {@const done = j.state === 'done'} + {@const failed = j.state === 'failed'} + {@const canceled = j.state === 'canceled'} + {@const terminal = done || failed || canceled} + {@const p = jobPct(j)} +
+
+

Aktueller Vorgang

+ {#if !terminal} + + {/if} + + {#if done}✓ fertig + {:else if failed}✗ fehlgeschlagen + {:else if canceled}abgebrochen + {:else}läuft…{/if} + +
+
{activeJobLabel}{terminal ? (done ? ' — erfolgreich.' : canceled ? ' — abgebrochen.' : ' — fehlgeschlagen.') : ' läuft…'}
+ {#if p != null && !terminal} +
+ {/if} +
{(j.log || []).join('\n')}
+
+{/if} + + +
+
+

Live-Konsole

+ +
+
Live mitlesen, was der Dienst gerade tut.
+
{consoleLogs}
+
diff --git a/frontend/src/stores/jobs.svelte.ts b/frontend/src/stores/jobs.svelte.ts index f3f45c1..5e68ac9 100644 --- a/frontend/src/stores/jobs.svelte.ts +++ b/frontend/src/stores/jobs.svelte.ts @@ -1,6 +1,14 @@ let _value = $state([]) +let _tracked = $state([]) export const jobsStore = { get value() { return _value }, set(jobs: any[]) { _value = jobs || [] }, + get tracked() { return _tracked }, + track(id: string) { if (!_tracked.includes(id)) _tracked = [..._tracked, id] }, + toggle(id: string) { + _tracked = _tracked.includes(id) + ? _tracked.filter(x => x !== id) + : [..._tracked, id] + }, } diff --git a/frontend/svelte.config.js b/frontend/svelte.config.js index 64c5130..85e6cef 100644 --- a/frontend/svelte.config.js +++ b/frontend/svelte.config.js @@ -5,4 +5,9 @@ export default { compilerOptions: { runes: true, }, + onwarn: (warning, handler) => { + // a11y-Warnungen nicht als Build-Fehler behandeln + if (warning.code.startsWith('a11y')) return + handler(warning) + }, } diff --git a/static/dist/main.js b/static/dist/main.js index c3cdb1e..b7fa1c3 100644 --- a/static/dist/main.js +++ b/static/dist/main.js @@ -1,17 +1,51 @@ -var ir=Object.defineProperty;var ns=e=>{throw TypeError(e)};var sr=(e,t,n)=>t in e?ir(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var se=(e,t,n)=>sr(e,typeof t!="symbol"?t+"":t,n),oi=(e,t,n)=>t.has(e)||ns("Cannot "+n);var d=(e,t,n)=>(oi(e,t,"read from private field"),n?n.call(e):t.get(e)),_=(e,t,n)=>t.has(e)?ns("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,n),y=(e,t,n,i)=>(oi(e,t,"write to private field"),i?i.call(e,n):t.set(e,n),n),T=(e,t,n)=>(oi(e,t,"access private method"),n);var Is=Array.isArray,ar=Array.prototype.indexOf,Nn=Array.prototype.includes,Xn=Array.from,rr=Object.defineProperty,rn=Object.getOwnPropertyDescriptor,lr=Object.getOwnPropertyDescriptors,or=Object.prototype,dr=Array.prototype,Gs=Object.getPrototypeOf,is=Object.isExtensible;const cr=()=>{};function ur(e){for(var t=0;t{e=i,t=s});return{promise:n,resolve:e,reject:t}}function ss(e,t){if(Array.isArray(e))return e;if(!(Symbol.iterator in e))return Array.from(e);const n=[];for(const i of e)if(n.push(i),n.length===t)break;return n}const U=2,fn=4,ei=8,qs=1<<24,ke=16,$e=32,et=64,ki=128,ve=512,I=1024,q=2048,De=4096,Q=8192,me=16384,Ut=32768,as=1<<25,It=65536,In=1<<17,fr=1<<18,jt=1<<19,pr=1<<20,Ie=1<<25,yt=65536,Gn=1<<21,At=1<<22,Ze=1<<23,di=Symbol("$state"),Dn=Symbol("attributes"),Mi=Symbol("class"),hr=Symbol("style"),Yt=Symbol("text"),ti=new class extends Error{constructor(){super(...arguments);se(this,"name","StaleReactionError");se(this,"message","The reaction that called `getAbortSignal()` was re-run or destroyed")}};function vr(e){throw new Error("https://svelte.dev/e/lifecycle_outside_component")}function mr(){throw new Error("https://svelte.dev/e/async_derived_orphan")}function gr(e,t,n){throw new Error("https://svelte.dev/e/each_key_duplicate")}function br(e){throw new Error("https://svelte.dev/e/effect_in_teardown")}function wr(){throw new Error("https://svelte.dev/e/effect_in_unowned_derived")}function yr(e){throw new Error("https://svelte.dev/e/effect_orphan")}function xr(){throw new Error("https://svelte.dev/e/effect_update_depth_exceeded")}function _r(){throw new Error("https://svelte.dev/e/state_descriptors_fixed")}function kr(){throw new Error("https://svelte.dev/e/state_prototype_fixed")}function Mr(){throw new Error("https://svelte.dev/e/state_unsafe_mutation")}function Sr(){throw new Error("https://svelte.dev/e/svelte_boundary_reset_onerror")}const $r=1,Er=2,Tr=16,Cr=1,Lr=2,N=Symbol("uninitialized"),Us="http://www.w3.org/1999/xhtml",Ar="http://www.w3.org/2000/svg",zr="http://www.w3.org/1998/Math/MathML";function Dr(){console.warn("https://svelte.dev/e/derived_inert")}function Pr(){console.warn("https://svelte.dev/e/svelte_boundary_reset_noop")}function js(e){return e===this.v}function Or(e,t){return e!=e?t==t:e!==t||e!==null&&typeof e=="object"||typeof e=="function"}function Vs(e){return!Or(e,this.v)}let Y=null;function Gt(e){Y=e}function Ws(e,t=!1,n){Y={p:Y,i:!1,c:null,e:null,s:e,x:null,r:E,l:null}}function Js(e){var t=Y,n=t.e;if(n!==null){t.e=null;for(var i of n)ga(i)}return t.i=!0,Y=t.p,{}}function Qs(){return!0}let Et=[];function Br(){var e=Et;Et=[],ur(e)}function zt(e){if(Et.length===0){var t=Et;queueMicrotask(()=>{t===Et&&Br()})}Et.push(e)}function Ys(e){var t=E;if(t===null)return S.f|=Ze,e;if((t.f&Ut)===0&&(t.f&fn)===0)throw e;Je(e,t)}function Je(e,t){if(!(t!==null&&(t.f&me)!==0)){for(;t!==null;){if((t.f&ki)!==0){if((t.f&Ut)===0)throw e;try{t.b.error(e);return}catch(n){e=n}}t=t.parent}throw e}}const Hr=-7169;function F(e,t){e.f=e.f&Hr|t}function Hi(e){(e.f&ve)!==0||e.deps===null?F(e,I):F(e,De)}function Zs(e){if(e!==null)for(const t of e)(t.f&U)===0||(t.f&yt)===0||(t.f^=yt,Zs(t.deps))}function Xs(e,t,n){(e.f&q)!==0?t.add(e):(e.f&De)!==0&&n.add(e),Zs(e.deps),F(e,I)}function Rr(e){let t=0,n=_t(0),i;return()=>{Gi()&&(M(n),cl(()=>(t===0&&(i=La(()=>e(()=>on(n)))),t+=1,()=>{zt(()=>{t-=1,t===0&&(i==null||i(),i=void 0,on(n))})})))}}var Fr=It|jt;function Nr(e,t,n,i){new Ir(e,t,n,i)}var ue,Bi,fe,ut,Z,pe,W,re,He,ft,Ve,Pt,vn,mn,Re,Qn,H,Gr,Kr,qr,Si,Pn,On,$i,Ei;class Ir{constructor(t,n,i,s){_(this,H);se(this,"parent");se(this,"is_pending",!1);se(this,"transform_error");_(this,ue);_(this,Bi,null);_(this,fe);_(this,ut);_(this,Z);_(this,pe,null);_(this,W,null);_(this,re,null);_(this,He,null);_(this,ft,0);_(this,Ve,0);_(this,Pt,!1);_(this,vn,new Set);_(this,mn,new Set);_(this,Re,null);_(this,Qn,Rr(()=>(y(this,Re,_t(d(this,ft))),()=>{y(this,Re,null)})));var a;y(this,ue,t),y(this,fe,n),y(this,ut,r=>{var o=E;o.b=this,o.f|=ki,i(r)}),this.parent=E.b,this.transform_error=s??((a=this.parent)==null?void 0:a.transform_error)??(r=>r),y(this,Z,Ki(()=>{T(this,H,Si).call(this)},Fr))}defer_effect(t){Xs(t,d(this,vn),d(this,mn))}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!d(this,fe).pending}update_pending_count(t,n){T(this,H,$i).call(this,t,n),y(this,ft,d(this,ft)+t),!(!d(this,Re)||d(this,Pt))&&(y(this,Pt,!0),zt(()=>{y(this,Pt,!1),d(this,Re)&&Kt(d(this,Re),d(this,ft))}))}get_effect_pending(){return d(this,Qn).call(this),M(d(this,Re))}error(t){if(!d(this,fe).onerror&&!d(this,fe).failed)throw t;x!=null&&x.is_fork?(d(this,pe)&&x.skip_effect(d(this,pe)),d(this,W)&&x.skip_effect(d(this,W)),d(this,re)&&x.skip_effect(d(this,re)),x.oncommit(()=>{T(this,H,Ei).call(this,t)})):T(this,H,Ei).call(this,t)}}ue=new WeakMap,Bi=new WeakMap,fe=new WeakMap,ut=new WeakMap,Z=new WeakMap,pe=new WeakMap,W=new WeakMap,re=new WeakMap,He=new WeakMap,ft=new WeakMap,Ve=new WeakMap,Pt=new WeakMap,vn=new WeakMap,mn=new WeakMap,Re=new WeakMap,Qn=new WeakMap,H=new WeakSet,Gr=function(){try{y(this,pe,he(()=>d(this,ut).call(this,d(this,ue))))}catch(t){this.error(t)}},Kr=function(t){const n=d(this,fe).failed;n&&y(this,re,he(()=>{n(d(this,ue),()=>t,()=>()=>{})}))},qr=function(){const t=d(this,fe).pending;t&&(this.is_pending=!0,y(this,W,he(()=>t(d(this,ue)))),zt(()=>{var n=y(this,He,document.createDocumentFragment()),i=gt();n.append(i),y(this,pe,T(this,H,On).call(this,()=>he(()=>d(this,ut).call(this,i)))),d(this,Ve)===0&&(d(this,ue).before(n),y(this,He,null),bt(d(this,W),()=>{y(this,W,null)}),T(this,H,Pn).call(this,x))}))},Si=function(){try{if(this.is_pending=this.has_pending_snippet(),y(this,Ve,0),y(this,ft,0),y(this,pe,he(()=>{d(this,ut).call(this,d(this,ue))})),d(this,Ve)>0){var t=y(this,He,document.createDocumentFragment());Ui(d(this,pe),t);const n=d(this,fe).pending;y(this,W,he(()=>n(d(this,ue))))}else T(this,H,Pn).call(this,x)}catch(n){this.error(n)}},Pn=function(t){this.is_pending=!1,t.transfer_effects(d(this,vn),d(this,mn))},On=function(t){var n=E,i=S,s=Y;Pe(d(this,Z)),ge(d(this,Z)),Gt(d(this,Z).ctx);try{return xt.ensure(),t()}catch(a){return Ys(a),null}finally{Pe(n),ge(i),Gt(s)}},$i=function(t,n){var i;if(!this.has_pending_snippet()){this.parent&&T(i=this.parent,H,$i).call(i,t,n);return}y(this,Ve,d(this,Ve)+t),d(this,Ve)===0&&(T(this,H,Pn).call(this,n),d(this,W)&&bt(d(this,W),()=>{y(this,W,null)}),d(this,He)&&(d(this,ue).before(d(this,He)),y(this,He,null)))},Ei=function(t){d(this,pe)&&(ie(d(this,pe)),y(this,pe,null)),d(this,W)&&(ie(d(this,W)),y(this,W,null)),d(this,re)&&(ie(d(this,re)),y(this,re,null));var n=d(this,fe).onerror;let i=d(this,fe).failed;var s=!1,a=!1;const r=()=>{if(s){Pr();return}s=!0,a&&Sr(),d(this,re)!==null&&bt(d(this,re),()=>{y(this,re,null)}),T(this,H,On).call(this,()=>{T(this,H,Si).call(this)})},o=c=>{try{a=!0,n==null||n(c,r),a=!1}catch(u){Je(u,d(this,Z)&&d(this,Z).parent)}i&&y(this,re,T(this,H,On).call(this,()=>{try{return he(()=>{var u=E;u.b=this,u.f|=ki,i(d(this,ue),()=>c,()=>r)})}catch(u){return Je(u,d(this,Z).parent),null}}))};zt(()=>{var c;try{c=this.transform_error(t)}catch(u){Je(u,d(this,Z)&&d(this,Z).parent);return}c!==null&&typeof c=="object"&&typeof c.then=="function"?c.then(o,u=>Je(u,d(this,Z)&&d(this,Z).parent)):o(c)})};function Ur(e,t,n,i){const s=Ri;var a=e.filter(m=>!m.settled),r=t.map(s);if(n.length===0&&a.length===0){i(r);return}var o=E,c=jr(),u=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(m=>m.promise)):null;function p(m){if((o.f&me)===0){c();try{i([...r,...m])}catch(h){Je(h,o)}Kn()}}var v=ea();if(n.length===0){u.then(()=>p([])).finally(v);return}function f(){Promise.all(n.map(m=>Vr(m))).then(p).catch(m=>Je(m,o)).finally(v)}u?u.then(()=>{c(),f(),Kn()}):f()}function jr(){var e=E,t=S,n=Y,i=x;return function(a=!0){Pe(e),ge(t),Gt(n),a&&(e.f&me)===0&&(i==null||i.activate(),i==null||i.apply())}}function Kn(e=!0){Pe(null),ge(null),Gt(null),e&&(x==null||x.deactivate())}function ea(){var e=E,t=e.b,n=x,i=!!(t!=null&&t.is_rendered());return t==null||t.update_pending_count(1,n),n.increment(i,e),()=>{t==null||t.update_pending_count(-1,n),n.decrement(i,e)}}function Ri(e){var t=U|q;return E!==null&&(E.f|=jt),{ctx:Y,deps:null,effects:null,equals:js,f:t,fn:e,reactions:null,rv:0,v:N,wv:0,parent:E,ac:null}}const Zt=Symbol("obsolete");function Vr(e,t,n){let i=E;i===null&&mr();var s=void 0,a=_t(N),r=!S,o=new Set;return dl(()=>{var m,h;var c=E,u=Ks();s=u.promise;try{Promise.resolve(e()).then(u.resolve,b=>{b!==ti&&u.reject(b)}).finally(Kn)}catch(b){u.reject(b),Kn()}var p=x;if(r){if((c.f&Ut)!==0)var v=ea();if((m=i.b)!=null&&m.is_rendered())(h=p.async_deriveds.get(c))==null||h.reject(Zt);else for(const b of o.values())b.reject(Zt);o.add(u),p.async_deriveds.set(c,u)}const f=(b,C=void 0)=>{v==null||v(),o.delete(u),C!==Zt&&(p.activate(),C?(a.f|=Ze,Kt(a,C)):((a.f&Ze)!==0&&(a.f^=Ze),Kt(a,b)),p.deactivate())};u.promise.then(f,b=>f(null,b||"unknown"))}),rl(()=>{for(const c of o)c.reject(Zt)}),new Promise(c=>{function u(p){function v(){p===s?c(a):u(s)}p.then(v,v)}u(s)})}function rs(e){const t=Ri(e);return ka(t),t}function Wr(e){const t=Ri(e);return t.equals=Vs,t}function Jr(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;nthis.schedule(i)){var i=d(this,Te).get(t);if(i){d(this,Te).delete(t);for(var s of i.d)F(s,q),n(s);for(s of i.m)F(s,De),n(s)}d(this,Nt).add(t)}capture(t,n,i=!1){t.v!==N&&!this.previous.has(t)&&this.previous.set(t,t.v),(t.f&Ze)===0&&(this.current.set(t,[n,i]),K==null||K.set(t,n)),this.is_fork||(t.v=n)}activate(){x=this}deactivate(){x=null,K=null}flush(){try{ui=!0,x=this,T(this,P,Xt).call(this)}finally{ls=0,Ti=null,Tt=null,Bn=null,ui=!1,x=null,K=null,mt.clear()}}discard(){var t;for(const n of d(this,Ht))n(this);d(this,Ht).clear();for(const n of this.async_deriveds.values())n.reject(Zt);T(this,P,en).call(this),(t=d(this,Ft))==null||t.resolve()}register_created_effect(t){d(this,gn).push(t)}increment(t,n){if(y(this,Rt,d(this,Rt)+1),t){let i=d(this,Fe).get(n)??0;d(this,Fe).set(n,i+1)}}decrement(t,n){if(y(this,Rt,d(this,Rt)-1),t){let i=d(this,Fe).get(n)??0;i===1?d(this,Fe).delete(n):d(this,Fe).set(n,i-1)}d(this,ht)||(y(this,ht,!0),zt(()=>{y(this,ht,!1),this.linked&&this.flush()}))}transfer_effects(t,n){for(const i of t)d(this,Ne).add(i);for(const i of n)d(this,xe).add(i);t.clear(),n.clear()}oncommit(t){d(this,Bt).add(t)}ondiscard(t){d(this,Ht).add(t)}settled(){return(d(this,Ft)??y(this,Ft,Ks())).promise}static ensure(){if(x===null){const t=x=new Yn;ui||zt(()=>{d(t,Ot)||t.flush()})}return x}apply(){{K=null;return}}schedule(t){var s;if(Ti=t,(s=t.b)!=null&&s.is_pending&&(t.f&(fn|ei|qs))!==0&&(t.f&Ut)===0){t.b.defer_effect(t);return}for(var n=t;n.parent!==null;){n=n.parent;var i=n.f;if(Tt!==null&&n===E&&(S===null||(S.f&U)===0))return;if((i&(et|$e))!==0){if((i&I)===0)return;n.f^=I}}d(this,X).push(n)}};Ot=new WeakMap,We=new WeakMap,pt=new WeakMap,Bt=new WeakMap,Ht=new WeakMap,Rt=new WeakMap,Fe=new WeakMap,Ft=new WeakMap,X=new WeakMap,gn=new WeakMap,Ne=new WeakMap,xe=new WeakMap,Te=new WeakMap,Nt=new WeakMap,ht=new WeakMap,P=new WeakSet,Ci=function(){if(this.is_fork)return!0;for(const i of d(this,Fe).keys()){for(var t=i,n=!1;t.parent!==null;){if(d(this,Te).has(t)){n=!0;break}t=t.parent}if(!n)return!0}return!1},Xt=function(){var c,u,p,v;y(this,Ot,!0),ls++>1e3&&(T(this,P,en).call(this),Xr());for(const f of d(this,Ne))d(this,xe).delete(f),F(f,q),this.schedule(f);for(const f of d(this,xe))F(f,De),this.schedule(f);const t=d(this,X);y(this,X,[]),this.apply();var n=Tt=[],i=[],s=Bn=[];for(const f of t)try{T(this,P,Li).call(this,f,n,i)}catch(m){throw la(f),T(this,P,Ci).call(this)||this.discard(),m}if(x=null,s.length>0){var a=Yn.ensure();for(const f of s)a.schedule(f)}if(Tt=null,Bn=null,T(this,P,Ci).call(this)){T(this,P,$t).call(this,i),T(this,P,$t).call(this,n);for(const[f,m]of d(this,Te))ra(f,m);s.length>0&&T(c=x,P,Xt).call(c);return}const r=T(this,P,ia).call(this);if(r){T(this,P,$t).call(this,i),T(this,P,$t).call(this,n),T(u=r,P,sa).call(u,this);return}d(this,Ne).clear(),d(this,xe).clear();for(const f of d(this,Bt))f(this);d(this,Bt).clear(),ln=this,os(i),os(n),ln=null,(p=d(this,Ft))==null||p.resolve();var o=x;if(d(this,Rt)===0&&(d(this,X).length===0||o!==null)&&T(this,P,en).call(this),d(this,X).length>0)if(o!==null){const f=o;d(f,X).push(...d(this,X).filter(m=>!d(f,X).includes(m)))}else o=this;o!==null&&T(v=o,P,Xt).call(v)},Li=function(t,n,i){t.f^=I;for(var s=t.first;s!==null;){var a=s.f,r=(a&($e|et))!==0,o=r&&(a&I)!==0,c=o||(a&Q)!==0||d(this,Te).has(s);if(!c&&s.fn!==null){r?s.f^=I:(a&fn)!==0?n.push(s):xn(s)&&((a&ke)!==0&&d(this,xe).add(s),qt(s));var u=s.first;if(u!==null){s=u;continue}}for(;s!==null;){var p=s.next;if(p!==null){s=p;break}s=s.parent}}},ia=function(){for(var t=d(this,We);t!==null;){if(!t.is_fork){for(const[n,[,i]]of this.current)if(t.current.has(n)&&!i)return t}t=d(t,We)}return null},sa=function(t){var i;for(const[s,a]of t.current)!this.previous.has(s)&&t.previous.has(s)&&this.previous.set(s,t.previous.get(s)),this.current.set(s,a);for(const[s,a]of t.async_deriveds){const r=this.async_deriveds.get(s);r&&a.promise.then(r.resolve).catch(r.reject)}t.async_deriveds.clear(),this.transfer_effects(d(t,Ne),d(t,xe));const n=s=>{var a=s.reactions;if(a!==null)for(const c of a){var r=c.f;if((r&U)!==0)n(c);else{var o=c;r&(At|ke)&&!this.async_deriveds.has(o)&&(d(this,xe).delete(o),F(o,q),this.schedule(o))}}};for(const s of this.current.keys())n(s);this.oncommit(()=>t.discard()),T(i=t,P,en).call(i),x=this,T(this,P,Xt).call(this)},$t=function(t){for(var n=0;n!f.current.get(m)[1]);if(!(!d(f,Ot)||s.length===0)){var a=s.filter(m=>!this.current.has(m));if(a.length===0)t&&f.discard();else if(n.length>0){if(t)for(const m of d(this,Nt))f.unskip_effect(m,h=>{var b;(h.f&(ke|At))!==0?f.schedule(h):T(b=f,P,$t).call(b,[h])});f.activate();var r=new Set,o=new Map;for(var c of n)aa(c,a,r,o);o=new Map;var u=[...f.current].filter(([m,h])=>{const b=this.current.get(m);return b?b[0]!==h[0]||b[1]!==h[1]:!0}).map(([m])=>m);if(u.length>0)for(const m of d(this,gn))(m.f&(me|Q|In))===0&&Ni(m,u,o)&&((m.f&(At|ke))!==0?(F(m,q),f.schedule(m)):d(f,Ne).add(m));if(d(f,X).length>0&&!d(f,ht)){f.apply();for(var p of d(f,X))T(v=f,P,Li).call(v,p,[],[]);y(f,X,[])}f.deactivate()}}}},en=function(){if(this.linked){var t=d(this,We),n=d(this,pt);t===null?ci=n:y(t,pt,n),n===null?kt=t:y(n,We,t),this.linked=!1}};let xt=Yn;function Xr(){try{xr()}catch(e){Je(e,Ti)}}let we=null;function os(e){var t=e.length;if(t!==0){for(var n=0;n0)){mt.clear();for(const s of we){if((s.f&(me|Q))!==0)continue;const a=[s];let r=s.parent;for(;r!==null;)we.has(r)&&(we.delete(r),a.push(r)),r=r.parent;for(let o=a.length-1;o>=0;o--){const c=a[o];(c.f&(me|Q))===0&&qt(c)}}we.clear()}}we=null}}function aa(e,t,n,i){if(!n.has(e)&&(n.add(e),e.reactions!==null))for(const s of e.reactions){const a=s.f;(a&U)!==0?aa(s,t,n,i):(a&(At|ke))!==0&&(a&q)===0&&Ni(s,t,i)&&(F(s,q),Ii(s))}}function Ni(e,t,n){const i=n.get(e);if(i!==void 0)return i;if(e.deps!==null)for(const s of e.deps){if(Nn.call(t,s))return!0;if((s.f&U)!==0&&Ni(s,t,n))return n.set(s,!0),!0}return n.set(e,!1),!1}function Ii(e){x.schedule(e)}function ra(e,t){if(!((e.f&$e)!==0&&(e.f&I)!==0)){(e.f&q)!==0?t.d.push(e):(e.f&De)!==0&&t.m.push(e),F(e,I);for(var n=e.first;n!==null;)ra(n,t),n=n.next}}function la(e){F(e,I);for(var t=e.first;t!==null;)la(t),t=t.next}let qn=new Set;const mt=new Map;let oa=!1;function _t(e,t){var n={f:0,v:e,reactions:null,equals:js,rv:0,wv:0};return n}function ee(e,t){const n=_t(e);return ka(n),n}function el(e,t=!1,n=!0){const i=_t(e);return t||(i.equals=Vs),i}function V(e,t,n=!1){S!==null&&(!Me||(S.f&In)!==0)&&Qs()&&(S.f&(U|ke|At|In))!==0&&(ze===null||!ze.has(e))&&Mr();let i=n?Qe(t):t;return Kt(e,i,Bn)}function Kt(e,t,n=null){if(!e.equals(t)){mt.set(e,tt?t:e.v);var i=xt.ensure();if(i.capture(e,t),(e.f&U)!==0){const s=e;(e.f&q)!==0&&Fi(s),K===null&&Hi(s)}e.wv=Sa(),da(e,q,n),E!==null&&(E.f&I)!==0&&(E.f&($e|et))===0&&(de===null?fl([e]):de.push(e)),!i.is_fork&&qn.size>0&&!oa&&tl()}return t}function tl(){oa=!1;for(const e of qn){(e.f&I)!==0&&F(e,De);let t;try{t=xn(e)}catch{t=!0}t&&qt(e)}qn.clear()}function on(e){V(e,e.v+1)}function da(e,t,n){var i=e.reactions;if(i!==null)for(var s=i.length,a=0;a{if(wt===a)return o();var c=S,u=wt;ge(null),us(a);var p=o();return ge(c),us(u),p};return i&&n.set("length",ee(e.length)),new Proxy(e,{defineProperty(o,c,u){(!("value"in u)||u.configurable===!1||u.enumerable===!1||u.writable===!1)&&_r();var p=n.get(c);return p===void 0?r(()=>{var v=ee(u.value);return n.set(c,v),v}):V(p,u.value,!0),!0},deleteProperty(o,c){var u=n.get(c);if(u===void 0){if(c in o){const p=r(()=>ee(N));n.set(c,p),on(s)}}else V(u,N),on(s);return!0},get(o,c,u){var m;if(c===di)return e;var p=n.get(c),v=c in o;if(p===void 0&&(!v||(m=rn(o,c))!=null&&m.writable)&&(p=r(()=>{var h=Qe(v?o[c]:N),b=ee(h);return b}),n.set(c,p)),p!==void 0){var f=M(p);return f===N?void 0:f}return Reflect.get(o,c,u)},getOwnPropertyDescriptor(o,c){var u=Reflect.getOwnPropertyDescriptor(o,c);if(u&&"value"in u){var p=n.get(c);p&&(u.value=M(p))}else if(u===void 0){var v=n.get(c),f=v==null?void 0:v.v;if(v!==void 0&&f!==N)return{enumerable:!0,configurable:!0,value:f,writable:!0}}return u},has(o,c){var f;if(c===di)return!0;var u=n.get(c),p=u!==void 0&&u.v!==N||Reflect.has(o,c);if(u!==void 0||E!==null&&(!p||(f=rn(o,c))!=null&&f.writable)){u===void 0&&(u=r(()=>{var m=p?Qe(o[c]):N,h=ee(m);return h}),n.set(c,u));var v=M(u);if(v===N)return!1}return p},set(o,c,u,p){var j;var v=n.get(c),f=c in o;if(i&&c==="length")for(var m=u;mee(N)),n.set(m+"",h))}if(v===void 0)(!f||(j=rn(o,c))!=null&&j.writable)&&(v=r(()=>ee(void 0)),V(v,Qe(u)),n.set(c,v));else{f=v.v!==N;var b=r(()=>Qe(u));V(v,b)}var C=Reflect.getOwnPropertyDescriptor(o,c);if(C!=null&&C.set&&C.set.call(p,u),!f){if(i&&typeof c=="string"){var L=n.get("length"),z=Number(c);Number.isInteger(z)&&z>=L.v&&V(L,z+1)}on(s)}return!0},ownKeys(o){M(s);var c=Reflect.ownKeys(o).filter(v=>{var f=n.get(v);return f===void 0||f.v!==N});for(var[u,p]of n)p.v!==N&&!(u in o)&&c.push(u);return c},setPrototypeOf(){kr()}})}var ds,ca,ua,fa;function nl(){if(ds===void 0){ds=window,ca=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;ua=rn(t,"firstChild").get,fa=rn(t,"nextSibling").get,is(e)&&(e[Mi]=void 0,e[Dn]=null,e[hr]=void 0,e.__e=void 0),is(n)&&(n[Yt]=void 0)}}function gt(e=""){return document.createTextNode(e)}function Ye(e){return ua.call(e)}function yn(e){return fa.call(e)}function R(e,t){return Ye(e)}function pa(e,t=!1){{var n=Ye(e);return n instanceof Comment&&n.data===""?yn(n):n}}function J(e,t=1,n=!1){let i=e;for(;t--;)i=yn(i);return i}function il(e){e.textContent=""}function ha(){return!1}function va(e,t,n){return t==null||t===Us?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function ma(e){var t=S,n=E;ge(null),Pe(null);try{return e()}finally{ge(t),Pe(n)}}function sl(e){E===null&&(S===null&&yr(),wr()),tt&&br()}function al(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function it(e,t){var n=E;n!==null&&(n.f&Q)!==0&&(e|=Q);var i={ctx:Y,deps:null,nodes:null,f:e|q|ve,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};x==null||x.register_created_effect(i);var s=i;if((e&fn)!==0)Tt!==null?Tt.push(i):xt.ensure().schedule(i);else if(t!==null){try{qt(i)}catch(r){throw ie(i),r}s.deps===null&&s.teardown===null&&s.nodes===null&&s.first===s.last&&(s.f&jt)===0&&(s=s.first,(e&ke)!==0&&(e&It)!==0&&s!==null&&(s.f|=It))}if(s!==null&&(s.parent=n,n!==null&&al(s,n),S!==null&&(S.f&U)!==0&&(e&et)===0)){var a=S;(a.effects??(a.effects=[])).push(s)}return i}function Gi(){return S!==null&&!Me}function rl(e){const t=it(ei,null);return F(t,I),t.teardown=e,t}function ll(e){sl();var t=E.f,n=!S&&(t&$e)!==0&&Y!==null&&!Y.i;if(n){var i=Y;(i.e??(i.e=[])).push(e)}else return ga(e)}function ga(e){return it(fn|pr,e)}function ol(e){xt.ensure();const t=it(et|jt,e);return(n={})=>new Promise(i=>{n.outro?bt(t,()=>{ie(t),i(void 0)}):(ie(t),i(void 0))})}function dl(e){return it(At|jt,e)}function cl(e,t=0){return it(ei|t,e)}function ot(e,t=[],n=[],i=[]){Ur(i,t,n,s=>{it(ei,()=>{e(...s.map(M))})})}function Ki(e,t=0){var n=it(ke|t,e);return n}function he(e){return it($e|jt,e)}function ba(e){var t=e.teardown;if(t!==null){const n=tt,i=S;cs(!0),ge(null);try{t.call(null)}finally{cs(n),ge(i)}}}function qi(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){const s=n.ac;s!==null&&ma(()=>{s.abort(ti)});var i=n.next;(n.f&et)!==0?n.parent=null:ie(n,t),n=i}}function ul(e){for(var t=e.first;t!==null;){var n=t.next;(t.f&$e)===0&&ie(t),t=n}}function ie(e,t=!0){var n=!1;(t||(e.f&fr)!==0)&&e.nodes!==null&&e.nodes.end!==null&&(wa(e.nodes.start,e.nodes.end),n=!0),e.f|=as,qi(e,t&&!n),pn(e,0);var i=e.nodes&&e.nodes.t;if(i!==null)for(const a of i)a.stop();ba(e),e.f^=as,e.f|=me;var s=e.parent;s!==null&&s.first!==null&&ya(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function wa(e,t){for(;e!==null;){var n=e===t?null:yn(e);e.remove(),e=n}}function ya(e){var t=e.parent,n=e.prev,i=e.next;n!==null&&(n.next=i),i!==null&&(i.prev=n),t!==null&&(t.first===e&&(t.first=i),t.last===e&&(t.last=n))}function bt(e,t,n=!0){var i=[];xa(e,i,!0);var s=()=>{n&&ie(e),t&&t()},a=i.length;if(a>0){var r=()=>--a||s();for(var o of i)o.out(r)}else s()}function xa(e,t,n){if((e.f&Q)===0){e.f^=Q;var i=e.nodes&&e.nodes.t;if(i!==null)for(const o of i)(o.is_global||n)&&t.push(o);for(var s=e.first;s!==null;){var a=s.next;if((s.f&et)===0){var r=(s.f&It)!==0||(s.f&$e)!==0&&(e.f&ke)!==0;xa(s,t,r?n:!1)}s=a}}}function Un(e){_a(e,!0)}function _a(e,t){if((e.f&Q)!==0){e.f^=Q,(e.f&I)===0&&(F(e,q),xt.ensure().schedule(e));for(var n=e.first;n!==null;){var i=n.next,s=(n.f&It)!==0||(n.f&$e)!==0;_a(n,s?t:!1),n=i}var a=e.nodes&&e.nodes.t;if(a!==null)for(const r of a)(r.is_global||t)&&r.in()}}function Ui(e,t){if(e.nodes)for(var n=e.nodes.start,i=e.nodes.end;n!==null;){var s=n===i?null:yn(n);t.append(n),n=s}}let Hn=!1,tt=!1;function cs(e){tt=e}let S=null,Me=!1;function ge(e){S=e}let E=null;function Pe(e){E=e}let ze=null;function ka(e){S!==null&&(ze??(ze=new Set)).add(e)}let te=null,ae=0,de=null;function fl(e){de=e}let Ma=1,dt=0,wt=dt;function us(e){wt=e}function Sa(){return++Ma}function xn(e){var t=e.f;if((t&q)!==0)return!0;if(t&U&&(e.f&=~yt),(t&De)!==0){for(var n=e.deps,i=n.length,s=0;se.wv)return!0}(t&ve)!==0&&K===null&&F(e,I)}return!1}function $a(e,t,n=!0){var i=e.reactions;if(i!==null&&!(ze!==null&&ze.has(e)))for(var s=0;s{e.ac.abort(ti)}),e.ac=null);try{e.f|=Gn;var p=e.fn,v=p();e.f|=Ut;var f=e.deps,m=x==null?void 0:x.is_fork;if(te!==null){var h;if(m||pn(e,ae),f!==null&&ae>0)for(f.length=ae+te.length,h=0;h{throw L});throw f}}finally{e[ct]=t,delete e.currentTarget,ge(p),Pe(v)}}}var Rs;const fi=((Rs=globalThis==null?void 0:globalThis.window)==null?void 0:Rs.trustedTypes)&&globalThis.window.trustedTypes.createPolicy("svelte-trusted-html",{createHTML:e=>e});function gl(e){return(fi==null?void 0:fi.createHTML(e))??e}function bl(e){var t=va("template");return t.innerHTML=gl(e.replaceAll("","")),t.content}function jn(e,t){var n=E;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function be(e,t){var n=(t&Cr)!==0,i=(t&Lr)!==0,s,a=!e.startsWith("");return()=>{s===void 0&&(s=bl(a?e:""+e),n||(s=Ye(s)));var r=i||ca?document.importNode(s,!0):s.cloneNode(!0);if(n){var o=Ye(r),c=r.lastChild;jn(o,c)}else jn(r,r);return r}}function ce(e,t){e!==null&&e.before(t)}function ye(e,t){var n=t==null?"":typeof t=="object"?`${t}`:t;n!==(e[Yt]??(e[Yt]=e.nodeValue))&&(e[Yt]=n,e.nodeValue=`${n}`)}function za(e,t){return wl(e,t)}const An=new Map;function wl(e,{target:t,anchor:n,props:i={},events:s,context:a,intro:r=!0,transformError:o}){nl();var c=void 0,u=ol(()=>{var p=n??t.appendChild(gt());Nr(p,{pending:()=>{}},m=>{Ws({});var h=Y;a&&(h.c=a),s&&(i.$$events=s),c=e(m,i)||{},Js()},o);var v=new Set,f=m=>{for(var h=0;h{var C;for(var m of v)for(const L of[t,document]){var h=An.get(L),b=h.get(m);--b==0?(L.removeEventListener(m,hs),h.delete(m),h.size===0&&An.delete(L)):h.set(m,b)}Ai.delete(f),p!==n&&((C=p.parentNode)==null||C.removeChild(p))}});return yl.set(c,u),c}let yl=new WeakMap;var _e,Ce,le,vt,bn,wn,Zn;class xl{constructor(t,n=!0){se(this,"anchor");_(this,_e,new Map);_(this,Ce,new Map);_(this,le,new Map);_(this,vt,new Set);_(this,bn,!0);_(this,wn,t=>{if(d(this,_e).has(t)){var n=d(this,_e).get(t),i=d(this,Ce).get(n);if(i)Un(i),d(this,vt).delete(n);else{var s=d(this,le).get(n);s&&(Un(s.effect),d(this,Ce).set(n,s.effect),d(this,le).delete(n),s.fragment.lastChild.remove(),this.anchor.before(s.fragment),i=s.effect)}for(const[a,r]of d(this,_e)){if(d(this,_e).delete(a),a===t)break;const o=d(this,le).get(r);o&&(ie(o.effect),d(this,le).delete(r))}for(const[a,r]of d(this,Ce)){if(a===n||d(this,vt).has(a))continue;const o=()=>{if(Array.from(d(this,_e).values()).includes(a)){var u=document.createDocumentFragment();Ui(r,u),u.append(gt()),d(this,le).set(a,{effect:r,fragment:u})}else ie(r);d(this,vt).delete(a),d(this,Ce).delete(a)};d(this,bn)||!i?(d(this,vt).add(a),bt(r,o,!1)):o()}}});_(this,Zn,t=>{d(this,_e).delete(t);const n=Array.from(d(this,_e).values());for(const[i,s]of d(this,le))n.includes(i)||(ie(s.effect),d(this,le).delete(i))});this.anchor=t,y(this,bn,n)}ensure(t,n){var i=x,s=ha();if(n&&!d(this,Ce).has(t)&&!d(this,le).has(t))if(s){var a=document.createDocumentFragment(),r=gt();a.append(r),d(this,le).set(t,{effect:he(()=>n(r)),fragment:a})}else d(this,Ce).set(t,he(()=>n(this.anchor)));if(d(this,_e).set(i,t),s){for(const[o,c]of d(this,Ce))o===t?i.unskip_effect(c):i.skip_effect(c);for(const[o,c]of d(this,le))o===t?i.unskip_effect(c.effect):i.skip_effect(c.effect);i.oncommit(d(this,wn)),i.ondiscard(d(this,Zn))}else d(this,wn).call(this,i)}}_e=new WeakMap,Ce=new WeakMap,le=new WeakMap,vt=new WeakMap,bn=new WeakMap,wn=new WeakMap,Zn=new WeakMap;function pi(e,t,n=!1){var i=new xl(e),s=n?It:0;function a(r,o){i.ensure(r,o)}Ki(()=>{var r=!1;t((o,c=0)=>{r=!0,a(c,o)}),r||a(-1,null)},s)}function Vn(e,t){return t}function _l(e,t,n){for(var i=[],s=t.length,a,r=t.length,o=0;o{if(a){if(a.pending.delete(v),a.done.add(v),a.pending.size===0){var f=e.outrogroups;zi(e,Xn(a.done)),f.delete(a),f.size===0&&(e.outrogroups=null)}}else r-=1},!1)}if(r===0){var c=i.length===0&&n!==null;if(c){var u=n,p=u.parentNode;il(p),p.append(u),e.items.clear()}zi(e,t,!c)}else a={pending:new Set(t),done:new Set},(e.outrogroups??(e.outrogroups=new Set)).add(a)}function zi(e,t,n=!0){var i;if(e.pending.size>0){i=new Set;for(const r of e.pending.values())for(const o of r)i.add(e.items.get(o).e)}for(var s=0;s{var z=n();return Is(z)?z:z==null?[]:Xn(z)}),v,f=new Map,m=!0;function h(z){(L.effect.f&me)===0&&(L.pending.delete(z),L.fallback=u,kl(L,v,r,t,i),u!==null&&(v.length===0?(u.f&Ie)===0?Un(u):(u.f^=Ie,tn(u,null,r)):bt(u,()=>{u=null})))}function b(z){L.pending.delete(z)}var C=Ki(()=>{v=M(p);for(var z=v.length,j=new Set,oe=x,Oe=ha(),k=0;ka(r)):(u=he(()=>a(vs??(vs=gt()))),u.f|=Ie)),z>j.size&&gr(),!m)if(f.set(oe,j),Oe){for(const[Wt,Ke]of o)j.has(Wt)||oe.skip_effect(Ke.e);oe.oncommit(h),oe.ondiscard(b)}else h(oe);M(p)}),L={effect:C,items:o,pending:f,outrogroups:null,fallback:u};m=!1}function Qt(e){for(;e!==null&&(e.f&$e)===0;)e=e.next;return e}function kl(e,t,n,i,s){var G;var a=t.length,r=e.items,o=Qt(e.effect.first),c,u=null,p=[],v=[],f,m,h,b;for(b=0;b0){var A=a===0?n:null;_l(e,Oe,A)}}}function Ml(e,t,n,i,s,a,r,o){var c=(r&$r)!==0?(r&Tr)===0?el(n,!1,!1):_t(n):null,u=(r&Er)!==0?_t(s):null;return{v:c,i:u,e:he(()=>(a(t,c??n,u??s,o),()=>{e.delete(i)}))}}function tn(e,t,n){if(e.nodes)for(var i=e.nodes.start,s=e.nodes.end,a=t&&(t.f&Ie)===0?t.nodes.start:n;i!==null;){var r=yn(i);if(a.before(i),i===s)return;i=r}}function qe(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function Sl(e,t,n=!1,i=!1,s=!1,a=!1){var r=e,o="";if(n)var c=e;ot(()=>{var u=E;if(o!==(o=t()??"")){if(n){u.nodes=null,c.innerHTML=o,o!==""&&jn(Ye(c),c.lastChild);return}if(u.nodes!==null&&(wa(u.nodes.start,u.nodes.end),u.nodes=null),o!==""){var p=i?Ar:s?zr:void 0,v=va(i?"svg":s?"math":"template",p);v.innerHTML=o;var f=i||s?v:v.content;if(jn(Ye(f),f.lastChild),i||s)for(;Ye(f);)r.before(Ye(f));else r.before(f)}}})}function $l(e,t,n){var i=e==null?"":""+e;return i===""?null:i}function ms(e,t,n,i,s,a){var r=e[Mi];if(r!==n||r===void 0){var o=$l(n);o==null?e.removeAttribute("class"):e.className=o,e[Mi]=n}return a}const El=Symbol("is custom element"),Tl=Symbol("is html");function Cl(e,t,n,i){var s=Ll(e);s[t]!==(s[t]=n)&&(n==null?e.removeAttribute(t):typeof n!="string"&&Al(e).includes(t)?e[t]=n:e.setAttribute(t,n))}function Ll(e){return e[Dn]??(e[Dn]={[El]:e.nodeName.includes("-"),[Tl]:e.namespaceURI===Us})}var gs=new Map;function Al(e){var t=e.getAttribute("is")||e.nodeName,n=gs.get(t);if(n)return n;gs.set(t,n=[]);for(var i,s=e,a=Element.prototype;a!==s;){i=lr(s);for(var r in i)i[r].set&&r!=="innerHTML"&&r!=="textContent"&&r!=="innerText"&&n.push(r);s=Gs(s)}return n}function zl(e){Y===null&&vr(),ll(()=>{const t=La(e);if(typeof t=="function")return t})}let hn=localStorage.getItem("mc_token")||"";function Da(){return hn}function Dl(e){hn=(e||"").trim(),localStorage.setItem("mc_token",hn)}function Pl(){const e={"Content-Type":"application/json"};return hn&&(e["X-MC-Token"]=hn),e}function _n(){return localStorage.getItem("mc_hf_token")||""}function Ol(e){localStorage.setItem("mc_hf_token",(e||"").trim())}async function $(e,t={}){const n=await fetch(e,{headers:Pl(),...t}),i=await n.json().catch(()=>({}));if(!n.ok)throw new Error(i.error||"HTTP "+n.status);return i}const l=(e,t=document)=>t.querySelector(e),bs=(e,t=document)=>[...t.querySelectorAll(e)];function g(e){return String(e??"").replace(/[&<>]/g,t=>({"&":"&","<":"<",">":">"})[t])}let ws;function w(e,t=!1){const n=l("#toast");n&&(n.textContent=e,n.className="toast show"+(t?" err":""),clearTimeout(ws),ws=setTimeout(()=>n.className="toast",3200))}function Bl(e,t){return e==="running"||e==="ready"?'geladen':e==="loading"||e==="starting"?'lädt…':e==="downloading"?`↓ Download${t!=null?` ${Math.round(t)}%`:""}`:'bereit'}const D=e=>`${e}`,Pa={logo:D(''),grid:D(''),cpu:D(''),pulse:D(''),server:D(''),book:D(''),help:D(''),settings:D(''),swap:D(''),monitor:D(''),layers:D(''),alert:D(''),gauge:D(''),search:D(''),compass:D(''),code:D(''),eye:D(''),refresh:D(''),file:D(''),database:D(''),thermo:D(''),shield:D(''),info:D(''),chevron:D(''),bolt:D(''),x:D(''),check:D(''),download:D(''),clock:D('')};function nt(e){return Pa[e]||""}function Jn(e){return`${Pa.info}`}function rt(e){if(!e&&e!==0)return"–";const t=e/1024/1024/1024;return t>=1?t.toFixed(1)+" GB":Math.round(e/1024/1024)+" MB"}function Ee({title:e,body:t,confirmLabel:n="Bestätigen",cancelLabel:i="Abbrechen",danger:s=!1}){return new Promise(a=>{const r=document.createElement("div");r.className="modal-overlay",r.innerHTML=``;const c=u=>{d.remove(),o(u)};d.addEventListener("click",u=>{if(u.target===d)return c(!1);const f=u.target.closest("[data-x]");f&&c(f.getAttribute("data-x")==="1")}),document.body.appendChild(d),d.querySelector('[data-x="1"]').focus()})}function Ni({title:e,body:t="",placeholder:a="",password:r=!1,confirmLabel:i="Weiter",danger:o=!1}){return new Promise(d=>{const c=document.createElement("div");c.className="modal-overlay",c.innerHTML=``;const c=o.querySelector("input"),u=p=>{o.remove(),r(p)};o.addEventListener("click",p=>{if(p.target===o)return u(null);const v=p.target.closest("[data-x]");v&&u(v.getAttribute("data-x")==="1"?c.value||"":null)}),c.addEventListener("keydown",p=>{p.key==="Enter"&&u(c.value||""),p.key==="Escape"&&u(null)}),document.body.appendChild(o),c.focus()})}function Hl(e="overview"){const t=bs(".nav-item[data-view]:not(.disabled)"),n=bs(".view[data-view]"),i=new Set(n.map(a=>a.dataset.view));function s(a){i.has(a)||(a=e),n.forEach(r=>r.hidden=r.dataset.view!==a),t.forEach(r=>r.classList.toggle("active",r.dataset.view===a)),location.hash!=="#/"+a&&(location.hash="#/"+a)}return t.forEach(a=>a.addEventListener("click",r=>{r.preventDefault(),s(a.dataset.view)})),window.addEventListener("hashchange",()=>s(location.hash.replace("#/",""))),s(location.hash.replace("#/","")||e),{show:s}}let ys=ee(null);const Rl={get value(){return M(ys)},set(e){V(ys,e,!0)}};let xs=ee(Qe([]));const Fl={get value(){return M(xs)},set(e){V(xs,e||[],!0)}},Nl="5";var Fs;typeof window<"u"&&((Fs=window.__svelte??(window.__svelte={})).v??(Fs.v=new Set)).add(Nl);const Il=[["Dein erster Workflow — in 3 Schritten",` + + `;const u=c.querySelector("input"),f=g=>{c.remove(),d(g)};c.addEventListener("click",g=>{if(g.target===c)return f(null);const $=g.target.closest("[data-x]");$&&f($.getAttribute("data-x")==="1"?u.value||"":null)}),u.addEventListener("keydown",g=>{g.key==="Enter"&&f(u.value||""),g.key==="Escape"&&f(null)}),document.body.appendChild(c),u.focus()})}function Fo(e="overview"){const t=Bi(".nav-item[data-view]:not(.disabled)"),a=Bi(".view[data-view]"),r=new Set(a.map(o=>o.dataset.view));function i(o){r.has(o)||(o=e),a.forEach(d=>d.hidden=d.dataset.view!==o),t.forEach(d=>d.classList.toggle("active",d.dataset.view===o)),location.hash!=="#/"+o&&(location.hash="#/"+o)}return t.forEach(o=>o.addEventListener("click",d=>{d.preventDefault(),i(o.dataset.view)})),window.addEventListener("hashchange",()=>i(location.hash.replace("#/",""))),i(location.hash.replace("#/","")||e),{show:i}}let Ii=R(null);const dr={get value(){return n(Ii)},set(e){_(Ii,e,!0)}};let Fi=R(Bt([])),Fn=R(Bt([]));const ra={get value(){return n(Fi)},set(e){_(Fi,e||[],!0)},get tracked(){return n(Fn)},track(e){n(Fn).includes(e)||_(Fn,[...n(Fn),e],!0)},toggle(e){_(Fn,n(Fn).includes(e)?n(Fn).filter(t=>t!==e):[...n(Fn),e],!0)}};let Ki=R(null);const yi={get value(){return n(Ki)},set(e){_(Ki,e,!0)}},Ko="5";var Zi;typeof window<"u"&&((Zi=window.__svelte??(window.__svelte={})).v??(Zi.v=new Set)).add(Ko);var Go=M('
GPU-Temperatur
Freier Speicher
GB
',1),Uo=M('
GPU-Temperatur
messe…
Freier Speicher
messe…
',1),Ho=M('
'),Vo=M('
Warte auf Messwerte…
'),Wo=M(" "),jo=M('
'),Jo=M('
'),Gi=M('
'),Qo=M('
Noch keine Modelle
Hol dir unter „Cookbook" ein Modell, das auf deine Hardware passt.
'),Yo=M('
'),Zo=M('
News gerade nicht ladbar.
'),Xo=M('

LLM-Engine
llama-swap
Aktives Modell

System-Gesundheit

So ausgelastet ist dein Mini-PC gerade.

Schnellstart

Die häufigsten Aufgaben — ein Klick.

Dein Stack

Top-News

Alle →
',1);function ed(e,t){Yn(t,!0);const a=N(()=>dr.value),r=N(()=>yi.value),i=N(()=>{var p;return((p=n(a))==null?void 0:p.models)||[]}),o=N(()=>{var p;return((p=n(a))==null?void 0:p.swap_ok)??!1}),d=N(()=>n(i).find(p=>["running","ready","loading","starting"].includes(p.state))),c=N(()=>n(a)?n(o)?n(r)&&n(r).ram.percent>=90?"Achtung: Speicher wird knapp.":"Alles läuft rund.":"LLM-Engine offline":"Verbinde…"),u=N(()=>n(a)?n(o)?n(r)&&n(r).ram.percent>=90?`Arbeitsspeicher bei ${Math.round(n(r).ram.percent)} % — eventuell Speicher freigeben.`:`${n(i).length} ${n(i).length===1?"Modell":"Modelle"} bereit · ${n(d)?`„${n(d).name}" geladen`:"keins geladen"} · keine Warnungen.`:"Der llama-swap-Dienst antwortet gerade nicht.":""),f=N(()=>()=>{var D;const p=(D=n(r))==null?void 0:D.gpu;return p&&p.vram.total+p.gtt.total>0?(p.vram.used+p.gtt.used)/(p.vram.total+p.gtt.total)*100:0}),g=p=>p==null?"":p<55?"kühl & gesund":p<72?"normal":"läuft heiß";let $=R(Bt([]));or(async()=>{try{_($,((await fe("/api/news")).items||[]).slice(0,4),!0)}catch{}});async function h(){await _n({title:"Speicher freigeben?",body:"Alle aktuell geladenen Modelle werden aus dem Grafikspeicher geworfen. Sie laden beim nächsten Aufruf automatisch neu — es geht nichts verloren.",confirmLabel:"Speicher freigeben"})&&(await fe("/api/unload",{method:"POST"}),document.dispatchEvent(new Event("mc:refresh")))}function b(p){var D;(D=document.querySelector(`.nav-item[data-view="${p}"]`))==null||D.dispatchEvent(new MouseEvent("click"))}function S(p){return p!=null&&p.includes("Bild")?"img":p!=null&&p.includes("Code")?"code":"text"}function P(p){return p!=null&&p.includes("Bild")?"Bild":p!=null&&p.includes("Code")?"Code":"Text"}var E=Xo(),I=ft(E),de=s(I),Y=s(de),ce=s(Y),me=l(Y,2),U=s(me),F=l(I,2),ie=s(F),he=l(s(ie),2),_e=s(he),Me=l(ie,2),He=l(s(Me),2),T=s(He),J=l(He,2),se=s(J),$e=l(Me,2);{var Q=p=>{const D=N(()=>{var xe;return n(r).gpu_temp??((xe=n(r).cpu)==null?void 0:xe.temp)??null}),ge=N(()=>n(r).ram.total-n(r).ram.used);var re=Go(),Le=ft(re),we=l(s(Le),2),pt=s(we),dt=l(we,2),St=s(dt),rt=l(Le,2),Et=l(s(rt),2),Dt=s(Et),Ot=l(Et,2),Qt=s(Ot);z((xe,We,Ze,Rt)=>{m(pt,xe),st(dt,1,`t-s ${n(D)!=null&&n(D)<72?"ok":""}`),m(St,We),m(Dt,Ze),m(Qt,`von ${Rt??""} GB`)},[()=>n(D)!=null?`${Math.round(n(D))}°`:"–",()=>g(n(D)),()=>Math.round(n(ge)/1073741824),()=>Math.round(n(r).ram.total/1073741824)]),x(p,re)},be=p=>{var D=Uo();x(p,D)};V($e,p=>{n(r)?p(Q):p(be,-1)})}var X=l(F,2),ze=s(X),Se=l(s(ze),4);{var Ie=p=>{const D=N(()=>n(f)());var ge=Cr(),re=ft(ge);Ue(re,17,()=>{var Le;return[["Prozessor (CPU)",((Le=n(r).cpu)==null?void 0:Le.percent)??0],["Arbeitsspeicher (RAM)",n(r).ram.percent],["Grafikspeicher (VRAM)",n(D)]]},Ge,(Le,we)=>{var pt=N(()=>Qa(n(we),2));let dt=()=>n(pt)[0],St=()=>n(pt)[1];const rt=N(()=>Math.max(0,Math.min(100,St()||0)));var Et=Ho(),Dt=s(Et),Ot=s(Dt),Qt=s(Ot),xe=l(Ot),We=s(xe),Ze=l(Dt,2),Rt=s(Ze);z((Pn,yn)=>{m(Qt,dt()),m(We,`${Pn??""} %`),st(Ze,1,`bar ${n(rt)>=90?"bad":n(rt)>=75?"warn":""}`),jn(Rt,`width:${yn??""}%`)},[()=>Math.round(n(rt)),()=>Math.max(2,n(rt))]),x(Le,Et)}),x(p,ge)},Ve=p=>{var D=Vo();x(p,D)};V(Se,p=>{n(r)?p(Ie):p(Ve,-1)})}var Fe=l(ze,2),mt=l(s(Fe),4),bt=s(mt);Qe(bt,()=>ea("search"),!0);var Kt=l(bt,4);Qe(Kt,()=>ea("chevron"),!0);var wt=l(mt,2),Mt=s(wt);Qe(Mt,()=>ea("refresh"),!0);var L=l(Mt,4);Qe(L,()=>ea("chevron"),!0);var K=l(wt,2),w=s(K);Qe(w,()=>ea("file"),!0);var A=l(w,4);Qe(A,()=>ea("chevron"),!0);var q=l(X,2),ee=s(q),O=s(ee),ue=l(s(O),2),B=s(ue),j=l(O,2);{var te=p=>{var D=Gi();Ue(D,21,()=>n(i),Ge,(ge,re)=>{const Le=N(()=>n(re).state==="downloading"),we=N(()=>["running","ready","loading","starting"].includes(n(re).state)),pt=N(()=>n(re).state==="loading"||n(re).state==="starting"||n(Le)?"load":n(we)?"on":""),dt=N(()=>n(Le)?`↓ Download${n(re).download_progress!=null?` ${Math.round(n(re).download_progress)}%`:""}`:n(we)?n(re).state==="loading"?"lädt…":"geladen":"bereit");var St=Jo(),rt=s(St),Et=l(rt,2),Dt=s(Et),Ot=s(Dt),Qt=s(Ot),xe=l(Ot,2);{var We=rn=>{var yt=Wo(),xn=s(yt);z((Oe,Tt)=>{st(yt,1,`tag ${Oe??""}`),m(xn,Tt)},[()=>S(n(re).meta.caps),()=>P(n(re).meta.caps)]),x(rn,yt)};V(xe,rn=>{var yt,xn;(xn=(yt=n(re).meta)==null?void 0:yt.caps)!=null&&xn.length&&rn(We)})}var Ze=l(Dt,2);{var Rt=rn=>{var yt=jo(),xn=s(yt);z(()=>m(xn,n(re).meta.filename)),x(rn,yt)};V(Ze,rn=>{var yt;(yt=n(re).meta)!=null&&yt.filename&&rn(Rt)})}var Pn=l(Et,2),yn=s(Pn);z(()=>{st(rt,1,`li-dot ${n(pt)??""}`),m(Qt,n(re).name),m(yn,n(dt))}),x(ge,St)}),x(p,D)},ae=p=>{var D=Qo();x(p,D)};V(j,p=>{n(i).length?p(te):p(ae,-1)})}var De=l(ee,2),Ae=s(De),Ce=l(s(Ae),2),qe=l(Ae,2);{var Ye=p=>{var D=Gi();Ue(D,21,()=>n($),Ge,(ge,re)=>{var Le=Yo(),we=s(Le),pt=s(we),dt=s(pt),St=l(pt,2),rt=s(St);z(()=>{Lr(Le,"href",n(re).link),m(dt,n(re).title),m(rt,n(re).source)}),x(ge,Le)}),x(p,D)},ot=p=>{var D=Zo();x(p,D)};V(qe,p=>{n($).length?p(Ye):p(ot,-1)})}z(()=>{m(ce,n(c)),m(U,n(u)),st(he,1,`t-v ${n(o)?"ok":"bad"}`),m(_e,n(o)?"Online":"Offline"),m(T,n(d)?n(d).name:"Keins"),m(se,n(d)?"im Grafikspeicher":"nichts geladen"),m(B,n(i).length?n(i).length+" konfiguriert":"")}),C("click",mt,()=>b("cookbook")),C("click",wt,h),C("click",K,()=>b("server")),C("click",Ce,()=>b("news")),C("keydown",Ce,p=>p.key==="Enter"&&b("news")),x(e,E),Zn()}_a(["click","keydown"]);var td=M(""),nd=M('
'),ad=M('
Noch keine Modelle konfiguriert
Hol dir unter „Cookbook" ein passendes Modell.
'),rd=M('
kein Modell hinterlegt
leere Rolleleer '),id=M(' '),sd=M('
'),ld=M(' '),od=M('
API-Name:
'),dd=M('
ModellKannDetailsStatusPortAktionen
'),cd=M('
Empfohlen für deine Hardware

'),ud=M(''),vd=M(''),fd=M(''),pd=M('

Modelle

Deine konfigurierten Modelle — testen, Kontext anpassen oder aus dem Speicher werfen.

Schnelltest

Schreib eine Nachricht — das gewählte Modell wird automatisch geweckt.

Modelle & Ports

Modelle laden automatisch, sobald eine Anfrage kommt — du musst nichts manuell starten.
',1);function hd(e,t){Yn(t,!0);const a="Kontext = das Kurzzeitgedächtnis des Modells. Größer = merkt sich mehr (längere Dateien/Chats), braucht aber mehr Speicher und wird etwas langsamer.",r="Die Rolle ist ein zusätzlicher, sprechender Name (z.B. coder). Du kannst in deinen Tools entweder den echten Modellnamen ODER die Rolle angeben — beides führt zum selben Modell.",i=[{id:"coder",label:"Coder",desc:"Programmieren & Code"},{id:"vision",label:"Vision",desc:"Bilder verstehen"},{id:"scout",label:"Scout",desc:"Schneller Allrounder"},{id:"reviewer",label:"Reviewer",desc:"Code/Texte prüfen"},{id:"manager",label:"Manager",desc:"Planen & koordinieren"}],o=N(()=>{var w;return((w=dr.value)==null?void 0:w.models)||[]});function d(){document.dispatchEvent(new Event("mc:refresh"))}let c=R(""),u=R(""),f=R(""),g=R(!1);const $=N(()=>n(o).filter(w=>!w.incomplete));async function h(){var A;const w=n(c)||((A=n($)[0])==null?void 0:A.name);if(!(!w||!n(u).trim())){_(g,!0),_(f,"(wecke Modell, kann beim Laden kurz dauern…)");try{const q=await fe("/api/chat",{method:"POST",body:JSON.stringify({model:w,message:n(u)})});_(f,q.reply,!0)}catch(q){_(f,"Fehler: "+q.message)}_(g,!1),d()}}let b=R(!1),S=R(null),P=R(8192);function E(w){var A;_(S,n(o).find(q=>q.name===w),!0),n(S)&&(_(P,((A=n(S).meta)==null?void 0:A.ctx)||8192,!0),_(b,!0))}async function I(){try{await fe("/api/update_model",{method:"POST",body:JSON.stringify({alias:n(S).name,ctx:n(P)})}),Z("Gespeichert — aktiv beim nächsten Modell-Start."),_(b,!1),d()}catch(w){Z(w.message,!0)}}let de=R(!1),Y=R(""),ce=R(""),me=R("");function U(w){const A=n(o).find(q=>q.name===w);_(Y,w,!0),_(me,(A==null?void 0:A.role)||"",!0),_(ce,""),_(de,!0)}async function F(w){try{await fe("/api/set_role",{method:"POST",body:JSON.stringify({alias:n(Y),role:w})}),Z(w?`Rolle gesetzt: ${w}`:"Rolle entfernt."),_(de,!1),d()}catch(A){Z(A.message,!0)}}async function ie(w){if(await _n({title:`„${w}" entladen?`,body:"Das Modell wird aus dem Grafikspeicher geworfen und lädt beim nächsten Aufruf automatisch neu.",confirmLabel:"Entladen"}))try{await fe("/api/unload?model="+encodeURIComponent(w),{method:"POST"}),Z("Entladen: "+w),setTimeout(d,600)}catch(A){Z(A.message,!0)}}async function he(w){if(await _n({title:`„${w}" wirklich löschen?`,body:"Das Modell wird aus der Konfiguration ausgetragen und seine Dateien werden von der Festplatte gelöscht, um Speicher freizugeben. Das lässt sich nicht rückgängig machen — später brauchst du dafür einen neuen Download.",confirmLabel:"Endgültig löschen",danger:!0}))try{const A=await fe("/api/delete_model",{method:"POST",body:JSON.stringify({alias:w})});Z(A.note||"Gelöscht: "+w),setTimeout(d,600)}catch(A){Z(A.message,!0)}}function _e(w){return w!=null&&w.length?w.map(A=>A==="Code"?'Code':A==="Bild"?'Bild':'Text').join(" "):"–"}function Me(w){if(!w)return"–";const A=w.quant||"?",q=w.ctx?Math.round(w.ctx/1024)+"K":"?",ee=w.size_bytes?(w.size_bytes/1024**3).toFixed(1)+" GB":"?";return`${A} · ${q} · ${ee}`}function He(w){var A;(A=navigator.clipboard)==null||A.writeText(w),Z("Kopiert: "+w)}var T=pd(),J=l(ft(T),2),se=l(s(J),6);Ue(se,21,()=>n($),Ge,(w,A)=>{var q=td(),ee=s(q),O={};z(()=>{m(ee,n(A).name),O!==(O=n(A).name)&&(q.value=(q.__value=n(A).name)??"")}),x(w,q)});var $e=l(se,4),Q=l($e,2),be=s(Q),X=l(Q,2);{var ze=w=>{var A=nd(),q=s(A);z(()=>m(q,n(f))),x(w,A)};V(X,w=>{n(f)&&w(ze)})}var Se=l(J,2),Ie=s(Se),Ve=l(s(Ie)),Fe=s(Ve),mt=l(Ie,4);{var bt=w=>{var A=ad();x(w,A)},Kt=w=>{var A=dd(),q=l(s(A));Ue(q,21,()=>n(o),Ge,(ee,O)=>{var ue=Cr(),B=ft(ue);{var j=ae=>{var De=rd(),Ae=s(De),Ce=s(Ae),qe=l(Ae,5),Ye=s(qe),ot=l(Ye,2);z(()=>m(Ce,n(O).name)),C("click",Ye,()=>{var p;Z("Lade im Cookbook ein Modell und setze den Alias auf: "+n(O).name),(p=document.querySelector(".nav-item[data-view='cookbook']"))==null||p.dispatchEvent(new MouseEvent("click"))}),C("click",ot,()=>he(n(O).name)),x(ae,De)},te=ae=>{var De=od(),Ae=s(De),Ce=s(Ae),qe=s(Ce),Ye=l(qe);{var ot=xe=>{var We=id(),Ze=s(We);z(()=>m(Ze,n(O).role)),x(xe,We)};V(Ye,xe=>{n(O).role&&xe(ot)})}var p=l(Ce,2);{var D=xe=>{var We=sd(),Ze=s(We);z(()=>m(Ze,n(O).meta.filename)),x(xe,We)};V(p,xe=>{var We;(We=n(O).meta)!=null&&We.filename&&xe(D)})}var ge=l(p,2),re=l(s(ge),2);Ue(re,17,()=>{var xe;return(xe=n(O).api_ids)!=null&&xe.length?n(O).api_ids:[n(O).name]},Ge,(xe,We)=>{var Ze=ld(),Rt=s(Ze);z(()=>m(Rt,n(We))),C("click",Ze,()=>He(n(We))),x(xe,Ze)});var Le=l(Ae);Qe(Le,()=>{var xe;return _e((xe=n(O).meta)==null?void 0:xe.caps)},!0);var we=l(Le);Qe(we,()=>Me(n(O).meta),!0);var pt=l(we);Qe(pt,()=>Io(n(O).state,n(O).download_progress),!0);var dt=l(pt),St=s(dt),rt=l(dt),Et=s(rt),Dt=l(Et,2),Ot=l(Dt,2),Qt=l(Ot,2);z(()=>{m(qe,`${n(O).name??""} `),m(St,n(O).port??"auto")}),C("click",Et,()=>U(n(O).name)),C("click",Dt,()=>E(n(O).name)),C("click",Ot,()=>ie(n(O).name)),C("click",Qt,()=>he(n(O).name)),x(ae,De)};V(B,ae=>{n(O).incomplete?ae(j):ae(te,-1)})}x(ee,ue)}),x(w,A)};V(mt,w=>{n(o).length?w(Kt,-1):w(bt)})}var wt=l(Se,2);{var Mt=w=>{var A=ud(),q=s(A),ee=s(q),O=l(ee,4),ue=s(O),B=l(O,2),j=l(s(B));Qe(j,()=>si(a));var te=l(B,2),ae=l(te,2);{var De=Ce=>{var qe=cd(),Ye=s(qe),ot=l(s(Ye),2),p=s(ot),D=l(p,2),ge=l(Ye,2);z((re,Le)=>{m(p,`Optimal ~${re??""}k → ~${n(S).meta.peak_ram_optimal_gb??""} GB Spitzenbedarf.`),m(D,` Aktuell ~${Le??""}k → ~${n(S).meta.peak_ram_gb??""} GB.`)},[()=>Math.round(n(S).meta.optimal_ctx/1024),()=>Math.round((n(S).meta.ctx||0)/1024)]),C("click",ge,()=>_(P,n(S).meta.optimal_ctx,!0)),x(Ce,qe)};V(ae,Ce=>{var qe;(qe=n(S).meta)!=null&&qe.optimal_ctx&&Ce(De)})}var Ae=l(ae,4);z(()=>m(ue,n(S).name)),C("click",A,Ce=>{Ce.target.classList.contains("modal-overlay")&&_(b,!1)}),C("click",ee,()=>_(b,!1)),Mn(te,()=>n(P),Ce=>_(P,Ce)),C("click",Ae,I),x(w,A)};V(wt,w=>{n(b)&&n(S)&&w(Mt)})}var L=l(wt,2);{var K=w=>{var A=fd(),q=s(A),ee=s(q),O=l(ee,2),ue=l(s(O));Qe(ue,()=>si(r));var B=l(O,2),j=s(B),te=l(B,4);Ue(te,21,()=>i,Ge,(qe,Ye)=>{var ot=vd(),p=s(ot);z(()=>{st(ot,1,n(me)===n(Ye).id?"primary":"ghost"),Lr(ot,"title",n(Ye).desc),m(p,n(Ye).label)}),C("click",ot,()=>F(n(Ye).id)),x(qe,ot)});var ae=l(te,4),De=l(ae,2),Ae=s(De),Ce=l(Ae,2);z(()=>m(j,n(Y))),C("click",A,qe=>{qe.target.classList.contains("modal-overlay")&&_(de,!1)}),C("click",ee,()=>_(de,!1)),Mn(ae,()=>n(ce),qe=>_(ce,qe)),C("click",Ae,()=>F("")),C("click",Ce,()=>F(n(ce).trim().toLowerCase())),x(w,A)};V(L,w=>{n(de)&&w(K)})}z(()=>{Q.disabled=n(g),m(be,n(g)?"…":"Senden"),m(Fe,n(o).length?n(o).length+" konfiguriert":"")}),Wa(se,()=>n(c),w=>_(c,w)),Mn($e,()=>n(u),w=>_(u,w)),C("click",Q,h),x(e,T),Zn()}_a(["click"]);var gd=M('
Prozessor (CPU)
%
Arbeitsspeicher
%
Grafikspeicher
%

System-Metriken (Bosgame)

Live-Auslastung deines Mini-PCs, alle 0,5 Sekunden.
Arbeitsspeicher (RAM)
Grafikspeicher (VRAM + GTT)
Speicherplatz (Disk)
Temperatur (GPU / CPU)
CPU-Verlauf
RAM-Verlauf
VRAM-Verlauf
',1),_d=M(''),md=M('
Gerade nichts los.
Alles ruhig — keine laufenden Aufgaben.
'),bd=M(' '),wd=M(''),yd=M(''),xd=M('
'),kd=M('
'),Md=M('
'),Sd=M('

Aktivität

Live-Auslastung und laufende Aufgaben (Downloads, Updates) mit Protokoll.

Hintergrund-Aufgaben

Downloads & Updates erscheinen hier mit Live-Protokoll — zum Aufklappen klicken.
',1);function Ed(e,t){Yn(t,!0);const a=N(()=>ra.value),r=N(()=>ra.tracked),i=N(()=>yi.value);let o=Bt({cpu:[],ram:[],gpu:[]});gi(()=>{var se;if(!n(i))return;const T=n(i).gpu,J=T&&T.vram.total+T.gtt.total>0?(T.vram.used+T.gtt.used)/(T.vram.total+T.gtt.total)*100:0;o.cpu=[...o.cpu,((se=n(i).cpu)==null?void 0:se.percent)??0].slice(-60),o.ram=[...o.ram,n(i).ram.percent].slice(-60),o.gpu=[...o.gpu,J].slice(-60)});const d=N(()=>()=>{var J;const T=(J=n(i))==null?void 0:J.gpu;return T&&T.vram.total+T.gtt.total>0?(T.vram.used+T.gtt.used)/(T.vram.total+T.gtt.total)*100:0}),c=N(()=>n(a).filter(T=>T.state!=="running"&&T.state!=="queued").length),u=N(()=>n(a).filter(T=>T.state==="failed").length);function f(T){if(T.state!=="running")return null;if(typeof T.progress=="number")return Math.min(100,T.progress);const se=((T.log||[]).at(-1)||"").match(/(\d+(?:\.\d+)?)\s*%/);return se?Math.min(100,parseFloat(se[1])):null}function g(T){return T==null||T<0?"":T>=90?`noch ~${Math.round(T/60)} min`:`noch ~${Math.max(1,Math.round(T))} s`}function $(T){return T==="done"?'fertig':T==="failed"?'fehler':T==="canceled"?'abgebrochen':'läuft…'}function h(T){return T==="done"?"on":T==="failed"||T==="canceled"?"":"load"}async function b(T){try{await fe(`/api/jobs/${T}/cancel`,{method:"POST"}),Z("Abbruch angefordert…")}catch(J){Z(J.message,!0)}}async function S(T){try{await fe(`/api/jobs/${T}`,{method:"DELETE"})}catch(J){Z(J.message,!0)}}async function P(){try{const T=await fe("/api/jobs/clear",{method:"POST"});Z(`${T.removed} Einträge entfernt.`)}catch(T){Z(T.message,!0)}}function E(T,J){return T.map(se=>`
`).join("")}var I=Sd(),de=l(ft(I),2);{var Y=T=>{const J=N(()=>n(d)());var se=gd(),$e=ft(se),Q=s($e),be=l(s(Q),2),X=s(be),ze=l(be,2),Se=s(ze),Ie=l(Q,2),Ve=l(s(Ie),2),Fe=s(Ve),mt=l(Ve,2),bt=s(mt),Kt=l(Ie,2),wt=l(s(Kt),2),Mt=s(wt),L=l(wt,2),K=s(L),w=l($e,2),A=l(s(w),4),q=s(A),ee=l(s(q)),O=s(ee),ue=l(q,2),B=l(s(ue),2),j=s(B),te=l(ue,2),ae=l(s(te)),De=s(ae),Ae=l(te,2),Ce=l(s(Ae),2),qe=s(Ce),Ye=l(A,2),ot=s(Ye),p=l(s(ot));Qe(p,()=>E(o.cpu,"--accent"),!0);var D=l(ot,2),ge=l(s(D));Qe(ge,()=>E(o.ram,"--purple"),!0);var re=l(D,2),Le=l(s(re));Qe(Le,()=>E(o.gpu,"--on"),!0),z((we,pt,dt,St,rt,Et,Dt,Ot,Qt,xe,We,Ze,Rt)=>{m(X,we),m(Se,pt),m(Fe,dt),m(bt,`${St??""} / ${rt??""}`),m(Mt,Et),m(K,Dt),m(O,`${Ot??""} / ${Qt??""}`),m(j,xe),m(De,`${We??""} % belegt`),m(qe,`${Ze??""} / ${Rt??""}`)},[()=>{var we;return Math.round(((we=n(i).cpu)==null?void 0:we.percent)??0)},()=>{var we;return((we=n(i).cpu)==null?void 0:we.temp)!=null?`${Math.round(n(i).cpu.temp)}° CPU-Temp`:"Auslastung"},()=>Math.round(n(i).ram.percent),()=>Xn(n(i).ram.used),()=>Xn(n(i).ram.total),()=>Math.round(n(J)),()=>n(i).gpu_temp!=null?`${Math.round(n(i).gpu_temp)}° GPU-Temp`:"VRAM + GTT",()=>Xn(n(i).ram.used),()=>Xn(n(i).ram.total),()=>n(i).gpu?`${Xn(n(i).gpu.vram.used+n(i).gpu.gtt.used)} / ${Xn(n(i).gpu.vram.total+n(i).gpu.gtt.total)}`:"–",()=>{var we;return Math.round(((we=n(i).disk)==null?void 0:we.percent)??0)},()=>n(i).gpu_temp!=null?Math.round(n(i).gpu_temp)+"°":"–",()=>{var we;return((we=n(i).cpu)==null?void 0:we.temp)!=null?Math.round(n(i).cpu.temp)+"°":"–"}]),x(T,se)};V(de,T=>{n(i)&&T(Y)})}var ce=l(de,2),me=s(ce),U=l(s(me),2),F=s(U),ie=l(U,2);{var he=T=>{var J=_d();C("click",J,P),x(T,J)};V(ie,T=>{n(c)&&T(he)})}var _e=l(me,4);{var Me=T=>{var J=md();x(T,J)},He=T=>{var J=Cr(),se=ft(J);Ue(se,17,()=>n(a),Ge,($e,Q)=>{const be=N(()=>f(n(Q))),X=N(()=>n(Q).state==="running"?g(n(Q).eta_s):""),ze=N(()=>n(Q).state==="running"&&n(Q).rate_bps?Xn(n(Q).rate_bps)+"/s":""),Se=N(()=>[n(X),n(ze)].filter(Boolean).join(" · ")),Ie=N(()=>n(r).includes(n(Q).id));var Ve=Md(),Fe=s(Ve),mt=s(Fe),bt=l(mt,2),Kt=s(bt),wt=l(bt,2);{var Mt=B=>{var j=bd(),te=s(j);z(ae=>m(te,`${ae??""} %${n(Se)?` · ${n(Se)}`:""}`),[()=>Math.round(n(be))]),x(B,j)};V(wt,B=>{n(be)!=null&&B(Mt)})}var L=l(wt,2);{var K=B=>{var j=wd();z(()=>jn(j,`${n(be)==null?"margin-left:auto;":""}margin-right:2px;padding:2px 9px;font-size:12px`)),C("click",j,te=>{te.stopPropagation(),b(n(Q).id)}),x(B,j)},w=B=>{var j=yd();C("click",j,te=>{te.stopPropagation(),S(n(Q).id)}),x(B,j)};V(L,B=>{n(Q).state==="running"?B(K):B(w,-1)})}var A=l(L,2);Qe(A,()=>$(n(Q).state));var q=l(Fe,2);{var ee=B=>{var j=xd(),te=s(j);z(()=>jn(te,`width:${n(be)??""}%`)),x(B,j)};V(q,B=>{n(be)!=null&&n(Q).state==="running"&&B(ee)})}var O=l(q,2);{var ue=B=>{var j=kd(),te=s(j);z(ae=>m(te,ae),[()=>(n(Q).log||[]).join(` +`)]),x(B,j)};V(O,B=>{n(Ie)&&B(ue)})}z(B=>{st(mt,1,`li-dot ${B??""}`),m(Kt,n(Q).label)},[()=>h(n(Q).state)]),C("click",Fe,()=>ra.toggle(n(Q).id)),C("keydown",Fe,B=>B.key==="Enter"&&ra.toggle(n(Q).id)),x($e,Ve)}),x(T,J)};V(_e,T=>{n(a).length?T(He,-1):T(Me)})}z(()=>m(F,n(a).length?n(u)?n(u)+" Fehler":n(a).length+" gesamt":"")),x(e,I),Zn()}_a(["click","keydown"]);var Td=M(' '),$d=M(''),Ad=M('✓ fertig'),Cd=M('✗ fehlgeschlagen'),Ld=M('abgebrochen'),Pd=M('läuft…'),zd=M('
'),Dd=M('

Aktueller Vorgang

'),Od=M('

Server & Wartung

Dienste steuern, Updates einspielen und live mitlesen — ganz ohne SSH oder Terminal.

LLM-Engine (llama-swap)

Das Herzstück: lädt bei jeder Anfrage automatisch das passende Modell.
Adresse (für deine Tools)
Aktuell geladen
Konfigurierte Modelle

Dienste & Applikation

Diese Aktionen sind harmlos und passwortlos — es geht nichts dabei verloren.

Betriebssystem (Bosgame)

Tiefe Eingriffe — das Dashboard fragt einmalig nach deinem sudo-Passwort.

Live-Konsole

Live mitlesen, was der Dienst gerade tut.
',1);function Rd(e,t){Yn(t,!0);const a=N(()=>dr.value),r=N(()=>ra.value);let i=null,o=R(`Verbinde… +`),d=R("llama-swap"),c,u=R(null),f=R("");const g=new Set,$=N(()=>n(u)?n(r).find(p=>p.id===n(u)):null);gi(()=>{if(!n($))return;const p=n($),D=p.state==="done",ge=p.state==="failed",re=p.state==="canceled";(D||ge||re)&&!g.has(p.id)&&(g.add(p.id),Z(D?`✓ ${n(f)} abgeschlossen.`:re?`${n(f)} abgebrochen.`:`✗ ${n(f)} fehlgeschlagen.`,ge),D&&setTimeout(()=>document.dispatchEvent(new Event("mc:refresh")),800))});function h(p,D){_(u,p,!0),_(f,D,!0),ra.track(p)}or(()=>{b()}),Oo(()=>{i==null||i.close()});function b(){i==null||i.close();const p=n(d);_(o,`Verbinde mit ${p}… +`);const D=location.protocol==="https:"?"wss:":"ws:";i=new WebSocket(`${D}//${location.host}/api/logs/${p}?token=${encodeURIComponent(Ws())}`),i.onmessage=ge=>{_(o,n(o)+ge.data),c&&(c.scrollTop=c.scrollHeight)},i.onclose=()=>{_(o,n(o)+` +— Verbindung getrennt —`)},i.onerror=()=>i==null?void 0:i.close()}async function S(p,D,ge){if(await _n({title:D,body:ge,confirmLabel:"Neustarten"}))try{await fe(`/api/service/${p}/restart`,{method:"POST"}),Z("Neustart ausgelöst: "+p),setTimeout(()=>document.dispatchEvent(new Event("mc:refresh")),2e3)}catch(re){Z(re.message,!0)}}async function P(){if(await _n({title:"Mission Control aktualisieren?",body:"Holt die neueste Dashboard-Version aus Gitea und startet automatisch neu. Die Seite trennt kurz und verbindet von selbst wieder (~10 Sek)."}))try{const p=await fe("/api/self-update",{method:"POST"});Z("Update läuft — Dashboard startet gleich neu."),h(p.job_id,"Mission Control Update")}catch(p){Z(p.message,!0)}}async function E(){if(await _n({title:"LLM-Engine aktualisieren?",body:"Lädt die neueste llama.cpp-ROCm-Version und installiert sie. Die Engine ist während des Updates kurz nicht erreichbar."}))try{const p=await fe("/api/update",{method:"POST"});Z("LLM-Engine-Update läuft."),h(p.job_id,"LLM-Engine-Update")}catch(p){Z(p.message,!0)}}async function I(){if(await _n({title:"Grafikspeicher leeren?",body:"Alle geladenen Modelle werden entladen. Sie laden beim nächsten Aufruf automatisch neu — es geht nichts verloren.",confirmLabel:"Leeren"}))try{await fe("/api/unload",{method:"POST"}),Z("Grafikspeicher geleert."),setTimeout(()=>document.dispatchEvent(new Event("mc:refresh")),600)}catch(p){Z(p.message,!0)}}async function de(){if(!await _n({title:"OS-Updates installieren?",body:"Führt apt update & upgrade aus. Das kann ein paar Minuten dauern; der Fortschritt erscheint in der Aktivität."}))return;const p=await Ni({title:"sudo-Passwort",body:"Für die System-Updates wird einmalig dein sudo-Passwort gebraucht.",placeholder:"sudo-Passwort",password:!0,confirmLabel:"Installieren"});if(p)try{const D=await fe("/api/os-update",{method:"POST",body:JSON.stringify({password:p})});Z("OS-Update gestartet."),h(D.job_id,"OS-Update")}catch(D){Z(D.message,!0)}}async function Y(){if(!await _n({title:"Server wirklich neu starten?",body:"Der ganze Bosgame startet physisch neu. Alles ist für ~1 Minute offline — auch dieses Dashboard.",confirmLabel:"Reboot",danger:!0}))return;const p=await Ni({title:"sudo-Passwort",body:"Für den Reboot wird einmalig dein sudo-Passwort gebraucht.",placeholder:"sudo-Passwort",password:!0,confirmLabel:"Jetzt neustarten",danger:!0});if(p)try{await fe("/api/reboot",{method:"POST",body:JSON.stringify({password:p})}),Z("Reboot ausgelöst — bis gleich.")}catch(D){Z(D.message,!0)}}async function ce(){_(me,"prüfe…");try{const p=await fe("/api/integration/test");_(me,p.ok?`✓ Engine antwortet — ${p.models.length} Modell(e): ${p.models.join(", ")}`:`✗ keine Verbindung: ${p.error}`,!0),_(U,p.ok,!0)}catch(p){_(me,"✗ "+p.message),_(U,!1)}}let me=R(""),U=R(!1);const F=N(()=>{var p;return((p=n(a))==null?void 0:p.models)||[]}),ie=N(()=>n(F).find(p=>p.state==="running"||p.state==="loading"));function he(p){if(!p||p.state!=="running")return null;if(typeof p.progress=="number")return Math.min(100,p.progress);const ge=((p.log||[]).at(-1)||"").match(/(\d+(?:\.\d+)?)\s*%/);return ge?Math.min(100,parseFloat(ge[1])):null}var _e=Od(),Me=l(ft(_e),2),He=s(Me),T=l(s(He),2),J=s(T),se=l(He,4),$e=s(se),Q=l(s($e)),be=s(Q),X=l($e,2),ze=l(s(X),2),Se=s(ze),Ie=l(X,2),Ve=l(s(Ie),2),Fe=s(Ve),mt=l(se,2),bt=s(mt),Kt=l(bt,2);{var wt=p=>{var D=Td(),ge=s(D);z(()=>{jn(D,`color:${n(U)?"#7ee29a":"#ff9b95"}`),m(ge,n(me))}),x(p,D)};V(Kt,p=>{n(me)&&p(wt)})}var Mt=l(Me,2),L=l(s(Mt),4),K=l(L,2),w=l(K,2),A=l(w,2),q=l(A,2),ee=l(Mt,2),O=l(s(ee),4),ue=l(O,2),B=l(ee,2);{var j=p=>{const D=N(()=>n($)),ge=N(()=>n(D).state==="done"),re=N(()=>n(D).state==="failed"),Le=N(()=>n(D).state==="canceled"),we=N(()=>n(ge)||n(re)||n(Le)),pt=N(()=>he(n(D)));var dt=Dd(),St=s(dt),rt=l(s(St),2);{var Et=Oe=>{var Tt=$d();C("click",Tt,async()=>{await _n({title:n(f)+" abbrechen?",body:"Der laufende Vorgang wird gestoppt.",confirmLabel:"Abbrechen",danger:!0})&&fe(`/api/jobs/${n(D).id}/cancel`,{method:"POST"})}),x(Oe,Tt)};V(rt,Oe=>{n(we)||Oe(Et)})}var Dt=l(rt,2),Ot=s(Dt);{var Qt=Oe=>{var Tt=Ad();x(Oe,Tt)},xe=Oe=>{var Tt=Cd();x(Oe,Tt)},We=Oe=>{var Tt=Ld();x(Oe,Tt)},Ze=Oe=>{var Tt=Pd();x(Oe,Tt)};V(Ot,Oe=>{n(ge)?Oe(Qt):n(re)?Oe(xe,1):n(Le)?Oe(We,2):Oe(Ze,-1)})}var Rt=l(St,2),Pn=s(Rt),yn=l(Rt,2);{var rn=Oe=>{var Tt=zd(),Dr=s(Tt);z(()=>jn(Dr,`width:${n(pt)??""}%`)),x(Oe,Tt)};V(yn,Oe=>{n(pt)!=null&&!n(we)&&Oe(rn)})}var yt=l(yn,2),xn=s(yt);z(Oe=>{m(Pn,`${n(f)??""}${n(we)?n(ge)?" — erfolgreich.":n(Le)?" — abgebrochen.":" — fehlgeschlagen.":" läuft…"}`),m(xn,Oe)},[()=>(n(D).log||[]).join(` +`)]),x(p,dt)};V(B,p=>{n($)&&p(j)})}var te=l(B,2),ae=s(te),De=l(s(ae),2),Ae=s(De);Ae.value=Ae.__value="llama-swap";var Ce=l(Ae);Ce.value=Ce.__value="mission-control";var qe=l(Ce);qe.value=qe.__value="system";var Ye=l(ae,4),ot=s(Ye);Do(Ye,p=>c=p,()=>c),z(p=>{var D,ge,re;jn(T,`margin-left:auto;${(D=n(a))!=null&&D.swap_ok?"background:rgba(63,185,80,.12);border-color:rgba(63,185,80,.25);color:#7ee29a":"background:rgba(248,81,73,.12);border-color:rgba(248,81,73,.3);color:#ff9b95"}`),m(J,(ge=n(a))!=null&&ge.swap_ok?"● erreichbar":"● offline"),m(be,(((re=n(a))==null?void 0:re.swap_url)||"—")+"/v1"),m(Se,n(ie)?n(ie).name+` (${n(ie).state})`:"nichts geladen — lädt bei der nächsten Anfrage"),m(Fe,p),m(ot,n(o))},[()=>n(F).filter(p=>!p.incomplete).length||"keine"]),C("click",bt,ce),C("click",L,()=>S("llama-swap","LLM-Engine neustarten?","Die Engine startet neu. Geladene Modelle werden kurz entladen (~5 Sekunden), laden danach automatisch wieder.")),C("click",K,()=>S("mission-control","Dashboard neustarten?","Diese Oberfläche trennt sich kurz und verbindet automatisch wieder.")),C("click",w,P),C("click",A,E),C("click",q,I),C("click",O,de),C("click",ue,Y),C("change",De,b),Wa(De,()=>n(d),p=>_(d,p)),x(e,_e),Zn()}_a(["click","change"]);var Bd=M('
Lade Setups…
'),qd=M(' ×',1),Gr=M('
★ Beste Wahl für dein System
'),Nd=M('
Dein Setup
'),Id=M(''),Fd=M('
'),Kd=M('
Frage Quellen ab…
'),Gd=M('
'),Ud=M('✓ installiert'),Hd=M(''),Vd=M('

'),Wd=M('

'),jd=M(''),Jd=M('

'),Qd=M('
'),Yd=M('
'),Zd=M('
'),Xd=M(" "),ec=M('
'),tc=M('
Größter Spitzenbedarf: . Es läuft immer nur ein Modell gleichzeitig — das größte bestimmt, ob das Setup passt.
'),nc=M(''),ac=M('
Lade Dateien von HuggingFace…
'),Ui=M(""),rc=M('
Empfohlener Kontext für deine Hardware: übernehmen
'),ic=M('
Ressourcen-Check
'),sc=M('
',1),lc=M('
Keine GGUF-Dateien im Repo gefunden.
'),oc=M(''),dc=M('
'),cc=M(''),uc=M('

Cookbook

Modelle finden, installieren und eigene Setups zusammenstellen.

Use-Case-Setups

Fertige Modell-Bundles für deinen Anwendungsfall. Hardware-Ampel zeigt, ob es auf deinen PC passt.

Aktuell die besten Modelle für dein System

Profi-Suche (HuggingFace)

Direkt HuggingFace durchsuchen — oder eine Modell-URL einfügen für den direkten Sprung.
',1);function vc(e,t){Yn(t,!0);const a="Kontext = das Kurzzeitgedächtnis des Modells. Größer = merkt sich mehr, braucht aber mehr Speicher und wird etwas langsamer.",r="MoE (Mixture of Experts): viele Parameter fürs Wissen, aber pro Wort ist nur ein kleiner Teil aktiv → fast große-Modell-Qualität bei hohem Tempo. Ideal für deine APU.",i={perfect:0,marginal:1,too_tight:2},o={vision:"Bilder",coder:"Coden",reasoning:"Logik",agent:"Agenten & Tools",scout:"Allrounder",reviewer:"Review",manager:"Manager"},d=[{label:"Bilder",kw:["-vl-","-vl","vision","llava","multimodal","-mm-","pixtral"]},{label:"Coden",kw:["coder","-code-","code-","codestral","starcoder"]},{label:"Logik",kw:["-r1","deepseek-r1","reasoning","qwq","magistral","-think","thinking","-o1"]},{label:"Agenten & Tools",kw:["hermes","-tool","command-r","watt","-fc-","function"]}];function c(y){const k=(y||"").toLowerCase();for(const H of d)if(H.kw.some(G=>k.includes(G)))return H.label;return"Allrounder"}function u(y=""){const k=y.toLowerCase();return/\d+x\d+(\.\d+)?b/.test(k)||new RegExp("(?{bt(),Kt()});async function bt(){_(P,!0);try{const y=await fe("/api/cookbook/recipes");_(b,y.recipes||[],!0),_(S,y.recommended_id||null,!0)}catch(y){Z(y.message,!0)}_(P,!1)}async function Kt(y=!1){_(I,!0);try{const k=await fe("/api/cookbook/discover"+(y?"?force=true":""));_(E,k,!0),_(de,k.stale,!0)}catch(k){Z("Empfehlungen nicht ladbar: "+k.message,!0)}_(I,!1)}function wt(y){y=y.trim();const k=y.match(/huggingface\.co\/([^\s/]+\/[^\s/?#]+)/i);return k?k[1]:/^[\w.-]+\/[\w.-]+$/.test(y)?y:null}async function Mt(){const y=n(Y).trim(),k=wt(y);let H=y;if(n(U)&&!k&&(H=H?H+" "+n(U):n(U)),!H&&!k){_(ce,[],!0);return}_(me,!0),_(ie,[],!0),_(he,-1);try{if(k)_(ce,[{id:k,author:k.split("/")[0],downloads:0}],!0);else{const G=`https://huggingface.co/api/models?search=${encodeURIComponent(H)}&filter=gguf&sort=${encodeURIComponent(n(F))}&direction=-1&limit=40`,le=await fetch(G);_(ce,await le.json(),!0)}n(ce).forEach((G,le)=>L(le,G.id))}catch(G){Z(G.message,!0),_(ce,[],!0)}_(me,!1)}async function L(y,k){var H;try{const G=await fe("/api/cookbook/analyze",{method:"POST",body:JSON.stringify({repo_id:k,ctx:8192})});if(!((H=G.files)!=null&&H.length)){n(ie)[y]=null,K();return}const le=G.files.find(Je=>{var Xe;return(Xe=Je.quant)==null?void 0:Xe.includes("Q4_K_M")})||G.files[0];n(ie)[y]=le.fit.level,K()}catch{n(ie)[y]=null,K()}}function K(){let y=-1,k=99;n(ie).forEach((H,G)=>{if(H==null)return;const le=i[H]??99;le=0&&k<=1?y:-1,!0)}function w(y){_(_e,n(b).find(k=>k.id===y),!0)}async function A(y){const k=document.getElementById("cb-r-install-btn");k&&(k.disabled=!0,k.textContent="Starte…");try{const H=await fe("/api/cookbook/install-recipe",{method:"POST",body:JSON.stringify({recipe_id:y,hf_token:Ja()})});Z(`${H.count} Downloads gestartet — siehe Aktivität.`),_(_e,null),mt("activity")}catch(H){Z(H.message,!0),k&&(k.disabled=!1,k.textContent="Installieren")}}async function q(y,k){var H,G;_(Me,!0),_(He,k,!0),_(T,y,!0),_($e,""),_(Q,8192),_(J,null),_(X,null),_(se,""),_(be,!0);try{if(_(J,await fe("/api/cookbook/analyze",{method:"POST",body:JSON.stringify({repo_id:y,ctx:8192})}),!0),(G=(H=n(J))==null?void 0:H.files)!=null&&G.length){const le=n(J).files.find(Je=>{var Xe;return(Xe=Je.quant)==null?void 0:Xe.includes("Q4_K_M")})||n(J).files[0];_(se,le.filename,!0),le.optimal_ctx&&n(Q)===8192&&_(Q,le.optimal_ctx,!0),ee()}}catch(le){Z(le.message,!0)}_(be,!1)}function ee(){var y,k,H;!((k=(y=n(J))==null?void 0:y.files)!=null&&k.length)||!n(se)||(_(X,n(J).files.find(G=>G.filename===n(se))||null,!0),(H=n(X))!=null&&H.optimal_ctx&&n(Q)===8192&&_(Q,n(X).optimal_ctx,!0))}async function O(){if(!n(T)||!n(se))return Z("Bitte eine GGUF-Datei wählen.",!0);_(be,!0);try{const y=await fe("/api/download",{method:"POST",body:JSON.stringify({repo:n(T),file:n(se),hf_token:Ja()})});await fe("/api/register",{method:"POST",body:JSON.stringify({role:n($e),model_path:y.expected_path,ctx:n(Q)})}),Z("Download gestartet — siehe Aktivität."),_(Me,!1),mt("activity")}catch(y){Z("Fehler: "+y.message,!0)}_(be,!1)}async function ue(y,k,H,G){G.disabled=!0,G.textContent="Starte…";try{await fe("/api/cookbook/install-model",{method:"POST",body:JSON.stringify({repo:y,role:k,params_b:H,quant:"Q4_K_M",hf_token:Ja()})}),Z("Download gestartet — siehe Aktivität."),mt("activity")}catch(le){Z(le.message,!0),G.disabled=!1,G.textContent="Installieren"}}function B(){_(Fe,null),_(Se,""),_(Ie,""),_(Ve,[{repo:"",role:"",quant:"Q4_K_M"}],!0),_(ze,!0)}function j(y){const k=n(b).find(H=>H.id===y);k&&(_(Fe,y,!0),_(Se,k.title,!0),_(Ie,k.desc||"",!0),_(Ve,k.models.map(H=>({repo:H.repo,role:H.role,quant:H.quant||"Q4_K_M"})),!0),_(ze,!0))}async function te(){if(!n(Se).trim())return Z("Bitte einen Titel angeben.",!0);const y=n(Ve).filter(k=>k.repo.trim());if(!y.length)return Z("Mindestens ein Modell (Repo) angeben.",!0);try{n(Fe)?(await fe("/api/cookbook/user-recipe/"+encodeURIComponent(n(Fe)),{method:"PUT",body:JSON.stringify({title:n(Se),desc:n(Ie),models:y})}),Z("Setup aktualisiert.")):(await fe("/api/cookbook/user-recipe",{method:"POST",body:JSON.stringify({title:n(Se),desc:n(Ie),models:y})}),Z("Setup gespeichert.")),_(ze,!1),bt()}catch(k){Z(k.message,!0)}}async function ae(y){const k=n(b).find(H=>H.id===y);if(await _n({title:`„${(k==null?void 0:k.title)||y}" löschen?`,body:"Dein eigenes Setup wird aus dem Cookbook entfernt. Bereits installierte Modelle bleiben unangetastet.",confirmLabel:"Löschen",danger:!0}))try{await fe("/api/cookbook/user-recipe/"+encodeURIComponent(y),{method:"DELETE"}),Z("Setup gelöscht."),bt()}catch(H){Z(H.message,!0)}}function De(y){if(!u(y))return"";const k=f(y);return`MoE${k?` · ~${k}B aktiv`:""}`}function Ae(y){return y?`${An(y)}`:""}var Ce=uc(),qe=l(ft(Ce),2),Ye=s(qe),ot=l(s(Ye),2),p=l(Ye,4);{var D=y=>{var k=Bd();x(y,k)},ge=y=>{var k=Fd();Ue(k,21,()=>n(b),Ge,(H,G)=>{const le=N(()=>n(G).id===n(S));var Je=Id(),Xe=s(Je);{var Yt=Be=>{var tt=qd(),Zt=ft(tt),ct=l(Zt,2);C("click",Zt,ut=>{ut.stopPropagation(),j(n(G).id)}),C("keydown",Zt,ut=>{ut.key==="Enter"&&(ut.stopPropagation(),j(n(G).id))}),C("click",ct,ut=>{ut.stopPropagation(),ae(n(G).id)}),C("keydown",ct,ut=>{ut.key==="Enter"&&(ut.stopPropagation(),ae(n(G).id))}),x(Be,tt)};V(Xe,Be=>{n(G).user&&Be(Yt)})}var Gt=l(Xe,2);{var Ut=Be=>{var tt=Gr();x(Be,tt)},Ht=Be=>{var tt=Nd();x(Be,tt)};V(Gt,Be=>{n(le)?Be(Ut):n(G).user&&Be(Ht,1)})}var Pe=l(Gt,2),ke=s(Pe),Ne=s(ke),Ke=s(Ne),Re=l(ke,2),ht=s(Re),xt=l(Pe,2),$t=s(xt),At=l(xt,2),et=s(At);z((Be,tt)=>{st(Je,1,`card-btn${n(le)?" cb-best":""}`),m(Ke,n(G).title),st(Re,1,`fit-badge ${Be??""}`),m(ht,tt),m($t,n(G).desc),m(et,`${n(G).models.length??""} Modell${n(G).models.length>1?"e":""} im Setup`)},[()=>g(n(G).fit_level),()=>$(n(G).fit_level)]),C("click",Je,()=>w(n(G).id)),x(H,Je)}),x(y,k)};V(p,y=>{n(P)?y(D):y(ge,-1)})}var re=l(qe,2),Le=s(re),we=l(s(Le),2),pt=s(we),dt=l(we,2),St=s(dt),rt=l(Le,2),Et=s(rt),Dt=l(rt,2);{var Ot=y=>{var k=Kd();x(y,k)},Qt=y=>{var k=Cr(),H=ft(k);Ue(H,17,()=>n(E).categories,Ge,(G,le)=>{var Je=Wd(),Xe=s(Je),Yt=s(Xe),Gt=s(Yt),Ut=l(Xe,2);Ue(Ut,21,()=>n(le).models,Ge,(Ht,Pe)=>{const ke=N(()=>n(le).recommended&&n(Pe).repo===n(le).recommended);var Ne=Vd(),Ke=s(Ne);{var Re=kt=>{var Nt=Gr();x(kt,Nt)};V(Ke,kt=>{n(ke)&&kt(Re)})}var ht=l(Ke,2),xt=s(ht),$t=s(xt),At=s($t),et=l($t,2),Be=s(et),tt=l(xt,2),Zt=s(tt),ct=l(ht,2),ut=s(ct);Qe(ut,()=>Ae(o[n(Pe).role]||c(n(Pe).repo)));var kn=l(ut,2);Qe(kn,()=>De(n(Pe).repo));var qn=l(ct,2),je=s(qn),nt=l(qn,2);Ue(nt,17,()=>n(Pe).requirements||[],Ge,(kt,Nt)=>{var Nn=Gd(),Or=s(Nn);z(()=>m(Or,`⚠ ${n(Nt)??""}`)),x(kt,Nn)});var it=l(nt,4),vt=s(it);{var Xt=kt=>{var Nt=Ud();z(()=>Lr(Nt,"title",`bereits eingerichtet als ${n(Pe).installed??""}`)),x(kt,Nt)},qt=kt=>{var Nt=Hd();C("click",Nt,Nn=>ue(n(Pe).repo,n(Pe).role,n(Pe).params_b,Nn.currentTarget)),x(kt,Nt)};V(vt,kt=>{n(Pe).installed?kt(Xt):kt(qt,-1)})}z((kt,Nt,Nn)=>{st(Ne,1,`card${n(ke)?" res-best":""}`),m(At,n(Pe).name),m(Be,`${n(Pe).author??""} · ${n(Pe).params_b??""}B`),st(tt,1,`fit-badge ${kt??""}`),m(Zt,n(Pe).fit.text),m(je,`${Nt??""} · ⬇ ${Nn??""}`)},[()=>g(n(Pe).fit.level),()=>h(n(Pe).fit),()=>(n(Pe).downloads||0).toLocaleString()]),x(Ht,Ne)}),z(()=>m(Gt,n(le).title)),x(G,Je)}),x(y,k)};V(Dt,y=>{var k;n(I)&&!n(E)?y(Ot):(k=n(E))!=null&&k.categories&&y(Qt,1)})}var xe=l(re,2),We=l(s(xe),4),Ze=s(We),Rt=l(Ze,2),Pn=s(Rt);Pn.value=Pn.__value="downloads";var yn=l(Pn);yn.value=yn.__value="trending";var rn=l(yn);rn.value=rn.__value="created_at";var yt=l(Rt,2),xn=s(yt),Oe=l(We,2);Ue(Oe,20,()=>["","coder","vision","reasoning","chat","agent"],Ge,(y,k)=>{var H=jd(),G=s(H);z(()=>{st(H,1,n(U)===k?"primary":"ghost"),m(G,k||"Alle")}),C("click",H,()=>{_(U,k,!0),Mt()}),x(y,H)});var Tt=l(Oe,2);{var Dr=y=>{var k=Qd();Ue(k,21,()=>n(ce),Ge,(H,G,le)=>{var Je=Jd(),Xe=s(Je);{var Yt=ct=>{var ut=Gr();x(ct,ut)};V(Xe,ct=>{le===n(he)&&ct(Yt)})}var Gt=l(Xe,2),Ut=s(Gt),Ht=s(Ut),Pe=s(Ht),ke=l(Ht,2),Ne=s(ke),Ke=l(Ut,2),Re=s(Ke),ht=l(Gt,2),xt=s(ht);Qe(xt,()=>Ae(c(n(G).id)));var $t=l(xt,2);Qe($t,()=>De(n(G).id));var At=l(ht,4),et=s(At),Be=s(et),tt=l(et,2),Zt=s(tt);z((ct,ut,kn,qn,je)=>{st(Je,1,`card${le===n(he)?" res-best":""}`),m(Pe,ct),m(Ne,n(G).author||""),st(Ke,1,`fit-badge ${ut??""}`),m(Re,kn),m(Be,qn),m(Zt,`⬇ ${je??""}`)},[()=>n(G).id.split("/").pop(),()=>n(ie)[le]?g(n(ie)[le]):"warn",()=>n(ie)[le]?$(n(ie)[le]):"prüfe…",()=>n(ie)[le]?h({req_gb:0,tps:0}):"Hardware-Fit…",()=>(n(G).downloads||0).toLocaleString()]),C("click",Je,()=>q(n(G).id,n(G).id.split("/").pop())),x(H,Je)}),x(y,k)};V(Tt,y=>{n(ce).length&&y(Dr)})}var xi=l(xe,2);{var Ys=y=>{const k=N(()=>n(_e));var H=nc(),G=s(H),le=s(G),Je=l(le,2),Xe=s(Je),Yt=l(Je,2),Gt=s(Yt),Ut=l(Yt,2);Ue(Ut,21,()=>n(k).models,Ge,(Ke,Re)=>{var ht=ec(),xt=s(ht),$t=s(xt),At=s($t),et=s(At),Be=l(At,2),tt=s(Be),Zt=l(Be,2);Qe(Zt,()=>De(n(Re).repo));var ct=l($t,2);{var ut=it=>{var vt=Yd(),Xt=s(vt);z(()=>m(Xt,n(Re).why)),x(it,vt)};V(ct,it=>{n(Re).why&&it(ut)})}var kn=l(ct,2);{var qn=it=>{var vt=Zd(),Xt=s(vt);z((qt,kt,Nt)=>m(Xt,`~${qt??""} GB · ~${kt??""} Tok/s · optimal ~${Nt??""}k Kontext`),[()=>(n(Re).fit.req_gb??0).toFixed(1),()=>Math.round(n(Re).fit.tps??0),()=>Math.round((n(Re).optimal_ctx||8192)/1024)]),x(it,vt)};V(kn,it=>{n(Re).fit&&it(qn)})}var je=l(xt,2);{var nt=it=>{var vt=Xd(),Xt=s(vt);z(qt=>{st(vt,1,`fit-badge ${qt??""}`),m(Xt,n(Re).fit.text)},[()=>g(n(Re).fit.level)]),x(it,vt)};V(je,it=>{n(Re).fit&&it(nt)})}z(()=>{m(et,n(Re).name),m(tt,n(Re).role)}),x(Ke,ht)});var Ht=l(Ut,2);{var Pe=Ke=>{const Re=N(()=>Math.max(...n(k).models.map(At=>{var et;return((et=At.fit)==null?void 0:et.req_gb)??0})));var ht=tc(),xt=l(s(ht)),$t=s(xt);z(At=>m($t,`~${At??""} GB`),[()=>n(Re).toFixed(1)]),x(Ke,ht)};V(Ht,Ke=>{n(k).models.length&&n(k).models[0].fit&&Ke(Pe)})}var ke=l(Ht,2),Ne=s(ke);z(()=>{m(Xe,n(k).title),m(Gt,n(k).desc),st(ke,1,`primary${n(k).fit_level==="too_tight"?" warn":""}`),m(Ne,n(k).fit_level==="too_tight"?"Trotzdem installieren (zu groß)":"Komplettes Setup installieren")}),C("click",H,Ke=>{Ke.target.classList.contains("modal-overlay")&&_(_e,null)}),C("click",le,()=>_(_e,null)),C("click",ke,()=>A(n(k).id)),x(y,H)};V(xi,y=>{n(_e)&&y(Ys)})}var ki=l(xi,2);{var Zs=y=>{var k=oc(),H=s(k),G=s(H),le=l(G,2),Je=s(le),Xe=l(le,2),Yt=s(Xe),Gt=l(Xe,2);{var Ut=ke=>{var Ne=ac();x(ke,Ne)},Ht=ke=>{var Ne=sc(),Ke=l(ft(Ne),2);Ue(Ke,21,()=>n(J).files,Ge,(je,nt)=>{const it=N(()=>n(nt).fit.level==="perfect"?"●":n(nt).fit.level==="marginal"?"◐":"○");var vt=Ui(),Xt=s(vt),qt={};z(()=>{m(Xt,`${n(it)??""} ${n(nt).filename??""}`),qt!==(qt=n(nt).filename)&&(vt.value=(vt.__value=n(nt).filename)??"")}),x(je,vt)});var Re=l(Ke,2),ht=s(Re),xt=l(s(ht),2),$t=l(ht,2),At=s($t),et=l(s(At));Qe(et,()=>si(a));var Be=l(At,2),tt=l(Re,2);{var Zt=je=>{var nt=rc(),it=l(s(nt)),vt=s(it),Xt=l(it,2);z(qt=>m(vt,`~${qt??""}k`),[()=>Math.round(n(X).optimal_ctx/1024)]),C("click",Xt,qt=>{qt.preventDefault(),_(Q,n(X).optimal_ctx,!0)}),x(je,nt)};V(tt,je=>{var nt;(nt=n(X))!=null&&nt.optimal_ctx&&je(Zt)})}var ct=l(tt,2);{var ut=je=>{var nt=ic(),it=s(nt),vt=l(s(it),2),Xt=s(vt),qt=l(it,2),kt=s(qt);z((Nt,Nn,Or)=>{m(Xt,`~${Nt??""} GB · ~${Nn??""} Tok/s · ${(n(X).quant||"GGUF")??""}`),st(qt,1,`fit-badge ${Or??""}`),m(kt,n(X).fit.text)},[()=>(n(X).fit.req_gb??0).toFixed(1),()=>Math.round(n(X).fit.tps??0),()=>g(n(X).fit.level)]),x(je,nt)};V(ct,je=>{n(X)&&je(ut)})}var kn=l(ct,2),qn=s(kn);z(()=>{var je,nt;st(kn,1,`primary${((je=n(X))==null?void 0:je.fit.level)==="too_tight"?" warn":""}`),kn.disabled=n(be),m(qn,n(be)?"Starte…":((nt=n(X))==null?void 0:nt.fit.level)==="too_tight"?"Trotzdem holen (zu groß)":"Herunterladen & Einpflegen")}),C("change",Ke,ee),Wa(Ke,()=>n(se),je=>_(se,je)),Mn(xt,()=>n($e),je=>_($e,je)),Mn(Be,()=>n(Q),je=>_(Q,je)),C("click",kn,O),x(ke,Ne)},Pe=ke=>{var Ne=lc();x(ke,Ne)};V(Gt,ke=>{var Ne,Ke;n(be)&&!n(J)?ke(Ut):(Ke=(Ne=n(J))==null?void 0:Ne.files)!=null&&Ke.length?ke(Ht,1):n(be)||ke(Pe,2)})}z(()=>{m(Je,n(He)),m(Yt,n(T))}),C("click",k,ke=>{ke.target.classList.contains("modal-overlay")&&_(Me,!1)}),C("click",G,()=>_(Me,!1)),x(y,k)};V(ki,y=>{n(Me)&&y(Zs)})}var Xs=l(ki,2);{var el=y=>{var k=cc(),H=s(k),G=s(H),le=l(G,2),Je=s(le),Xe=l(le,6),Yt=l(Xe,4),Gt=l(Yt,4);Ue(Gt,17,()=>n(Ve),Ge,(ke,Ne,Ke)=>{var Re=dc(),ht=s(Re),xt=l(ht,2),$t=l(xt,2);Ue($t,20,()=>["Q4_K_M","Q5_K_M","Q6_K","Q8_0","Q3_K_M"],Ge,(et,Be)=>{var tt=Ui(),Zt=s(tt),ct={};z(()=>{m(Zt,Be),ct!==(ct=Be)&&(tt.__value=Be)}),x(et,tt)});var At=l($t,2);Mn(ht,()=>n(Ne).repo,et=>n(Ne).repo=et),Mn(xt,()=>n(Ne).role,et=>n(Ne).role=et),Wa($t,()=>n(Ne).quant,et=>n(Ne).quant=et),C("click",At,()=>_(Ve,n(Ve).filter((et,Be)=>Be!==Ke),!0)),x(ke,Re)});var Ut=l(Gt,2),Ht=l(Ut,4),Pe=s(Ht);z(()=>{m(Je,n(Fe)?"Setup bearbeiten":"Eigenes Setup erstellen"),m(Pe,n(Fe)?"Änderungen speichern":"Setup speichern")}),C("click",k,ke=>{ke.target.classList.contains("modal-overlay")&&_(ze,!1)}),C("click",G,()=>_(ze,!1)),Mn(Xe,()=>n(Se),ke=>_(Se,ke)),Mn(Yt,()=>n(Ie),ke=>_(Ie,ke)),C("click",Ut,()=>_(Ve,[...n(Ve),{repo:"",role:"",quant:"Q4_K_M"}],!0)),C("click",Ht,te),x(y,k)};V(Xs,y=>{n(ze)&&y(el)})}z(()=>{m(pt,n(E)?"aktuell":""),dt.disabled=n(I),m(St,n(I)?"Suche…":"Aktualisieren"),m(Et,`Täglich aktualisierte Top-Modelle je Kategorie — Hardware-Fit berechnet für dein System. ${n(de)?"⚠ Cache veraltet.":""}`),yt.disabled=n(me),m(xn,n(me)?"Lade…":"Suchen")}),C("click",ot,B),C("click",dt,()=>Kt(!0)),C("keydown",Ze,y=>y.key==="Enter"&&Mt()),Mn(Ze,()=>n(Y),y=>_(Y,y)),Wa(Rt,()=>n(F),y=>_(F,y)),C("click",yt,Mt),x(e,Ce),Zn()}_a(["click","keydown","change"]);var fc=M(`
Du öffnest Mission Control über einen Proxy/Namen. Die Engine läuft auf Port 8080 ohne HTTPS direkt am Bosgame. + Trag in deinen Tools die LAN-IP ein: http://192.168.178.151:8080/v1 — nicht den Proxy-Namen.
`),pc=M(' '),hc=M('
'),gc=M(' '),_c=M(''),mc=M('

2 · Modelle auswählen

Wähle, welche Modelle im Config-Snippet erscheinen sollen.
'),bc=M(''),wc=M('

Öffne die Zed-Settings mit Cmd/Strg + , und füge diesen Block in deine bestehende settings.json ein. Falls bereits ein "bosgame"-Eintrag existiert: vorher löschen.

Nur diesen Block einfügen, nicht die ganze Datei ersetzen — sonst gehen andere Einstellungen verloren.

API-Key: Öffne den Agent-Panel → Anbieter bosgame → trag irgendeinen Wert ein (z.B. local).

Danach im Agent-Panel das Modell auswählen.
',1),yc=M('

Datei anlegen oder öffnen:

Windows: %USERPROFILE%\\.config\\opencode\\opencode.json

Nur den "provider"-Block einfügen. Falls bereits ein "llama-swap"-Eintrag vorhanden: vorher löschen, sonst erscheinen Modelle doppelt.

Danach in OpenCode /models wählen.

',1),xc=M('

In Cline/Cursor den Provider „OpenAI Compatible" wählen:

API-Key: ein beliebiger Wert (z.B. local).
',1),kc=M('

Settings → Admin Panel → Connections → neue OpenAI-Verbindung:

',1),Mc=M('

Bilder einbinden (Vision)

Mit Vision-Modellen kannst du Screenshots oder Fehlerbilder direkt mitschicken.
'),Sc=M('

Verbinden

Schritt für Schritt: KI-Modelle in deine Tools einbinden — getestet, erklärt, mit Copy-Paste-Config.

1 · Verbindung prüfen

Stelle sicher, dass die LLM-Engine erreichbar ist, bevor du Tools konfigurierst.

Wie funktioniert das Auto-Swap?

3 · Tool wählen

Für welches Werkzeug brauchst du die Konfiguration?

4 · Config-Snippet

',1);function Ec(e,t){Yn(t,!0);const a=N(()=>dr.value),r=N(()=>{var L;return(((L=n(a))==null?void 0:L.models)||[]).filter(K=>!K.incomplete)}),i=N(f);let o=R("zed"),d=R(Bt([])),c=R(""),u=R(!1);function f(){return`http://${location.hostname}:8080/v1`}function g(L){return/^\d{1,3}(\.\d{1,3}){3}$/.test(L)}function $(){return location.protocol==="https:"||!g(location.hostname)}const h=N(()=>n(d).length?n(d):n(r).map(L=>L.name)),b=N(()=>n(h)[0]||"coder"),S=N(()=>{var L;return(L=n(r).find(K=>{var w,A;return(A=(w=K.meta)==null?void 0:w.caps)==null?void 0:A.includes("Bild")}))==null?void 0:L.name});function P(L,K){const A=(K.length?n(r).filter(q=>K.includes(q.name)):n(r).length?n(r):[{name:"coder",meta:{}}]).map(q=>{var O;const ee=(((O=q.meta)==null?void 0:O.caps)||[]).some(ue=>/Bild|Vision/i.test(ue));return` { "name": "${q.name}", "display_name": "${q.name}", "max_tokens": 32768${ee?', "capabilities": { "tools": true, "images": true }':""} }`}).join(`, +`);return`"language_models": { + "openai_compatible": { + "bosgame": { + "api_url": "${L}", + "available_models": [ +${A} + ] + } + } +}`}function E(L,K){const A=(K.length?K:n(r).length?n(r).map(q=>q.name):["coder"]).map(q=>` "${q}": { "name": "${q}" }`).join(`, +`);return`"provider": { + "llama-swap": { + "npm": "@ai-sdk/openai-compatible", + "name": "Bosgame (llama-swap)", + "options": { + "baseURL": "${L}", + "apiKey": "local" + }, + "models": { +${A} + } + } +}`}async function I(){_(c,"Teste…");try{const L=await fe("/api/integration/test");_(u,L.ok,!0),_(c,L.ok?`✓ Verbunden — ${L.models.length} Modell(e): ${L.models.join(", ")}`:`✗ Keine Verbindung: ${L.error}`,!0)}catch(L){_(u,!1),_(c,"✗ "+L.message)}}function de(L){var w;const K=L.querySelector("code");K&&((w=navigator.clipboard)==null||w.writeText(K.textContent||""))}function Y(L){var K;(K=navigator.clipboard)==null||K.writeText(L.value)}var ce=Sc(),me=l(ft(ce),2),U=l(s(me),6),F=s(U),ie=l(F,2),he=l(U,2);{var _e=L=>{var K=fc();x(L,K)},Me=N(()=>$());V(he,L=>{n(Me)&&L(_e)})}var He=l(he,2),T=s(He),J=l(T,2);{var se=L=>{var K=pc(),w=s(K);z(()=>{jn(K,`color:${n(u)?"#7ee29a":"#ff9b95"}`),m(w,n(c))}),x(L,K)};V(J,L=>{n(c)&&L(se)})}var $e=l(me,2),Q=l(s($e),2);Ue(Q,20,()=>[["⚡","Kein manuelles Starten nötig",'Wenn du in Zed oder OpenCode „coder" auswählst und eine Anfrage sendest, lädt llama-swap das Modell automatisch — in ca. 2–5 Sekunden.'],["🔄","Modelle wechseln sich automatisch ab","Rufst du ein anderes Modell auf, entlädt llama-swap das aktuelle (nach TTL-Ablauf) und lädt das neue. Es läuft immer nur eines gleichzeitig."],["🧠","Zwei Modelle gleichzeitig aktiv halten","Du hast 124 GB RAM — genug für z.B. Scout 8B (~5 GB) dauerhaft geladen + ein größeres Modell das swappt. Dafür beim Scout im Modelle-Tab → Konfigurieren die TTL auf 99999 setzen."]],Ge,(L,K)=>{var w=N(()=>Qa(K,3));let A=()=>n(w)[0],q=()=>n(w)[1],ee=()=>n(w)[2];var O=hc(),ue=s(O),B=s(ue),j=l(ue,2),te=s(j),ae=s(te),De=l(te,2),Ae=s(De);z(()=>{m(B,A()),m(ae,q()),m(Ae,ee())}),x(L,O)});var be=l($e,2);{var X=L=>{var K=mc(),w=s(K),A=l(s(w),2),q=s(A),ee=l(w,4);Ue(ee,21,()=>n(r),Ge,(B,j)=>{const te=N(()=>n(d).includes(n(j).name));var ae=gc(),De=s(ae);z(()=>{jn(ae,`cursor:pointer;${n(te)?"background:rgba(45,212,191,.2);border-color:var(--accent);color:var(--accent)":""}`),m(De,n(j).name)}),C("click",ae,()=>_(d,n(te)?n(d).filter(Ae=>Ae!==n(j).name):[...n(d),n(j).name],!0)),C("keydown",ae,Ae=>Ae.key==="Enter"&&_(d,n(te)?n(d).filter(Ce=>Ce!==n(j).name):[...n(d),n(j).name],!0)),x(B,ae)});var O=l(ee,2);{var ue=B=>{var j=_c(),te=s(j);C("click",te,ae=>{ae.preventDefault(),_(d,[],!0)}),x(B,j)};V(O,B=>{n(d).length&&B(ue)})}z(()=>m(q,n(d).length?`${n(d).length} von ${n(r).length} ausgewählt`:"alle werden eingeschlossen")),x(L,K)};V(be,L=>{n(r).length&&L(X)})}var ze=l(be,2),Se=l(s(ze),4);Ue(Se,20,()=>[["zed","Zed","Schneller nativer Editor — Empfohlen"],["opencode","OpenCode","Open-Source Coding-Agent (Terminal/Desktop)"],["cline","Cline / Cursor","VS Code Extension, Bring-Your-Own-Key"],["openwebui","OpenWebUI","Browser-Chat-Interface"]],Ge,(L,K)=>{var w=N(()=>Qa(K,3));let A=()=>n(w)[0],q=()=>n(w)[1],ee=()=>n(w)[2];var O=bc(),ue=s(O),B=s(ue),j=l(ue,2),te=s(j);z(()=>{st(O,1,`card-btn${n(o)===A()?" cb-best":""}`),m(B,q()),m(te,ee())}),C("click",O,()=>_(o,A(),!0)),x(L,O)});var Ie=l(ze,2),Ve=l(s(Ie),2);{var Fe=L=>{const K=N(()=>P(n(i),n(h)));var w=wc(),A=l(ft(w),4),q=s(A),ee=l(q,2),O=s(ee),ue=s(O),B=l(A,4),j=l(s(B)),te=s(j);z(()=>{m(ue,n(K)),m(te,`bosgame / ${n(b)??""}`)}),C("click",q,ae=>de(ae.currentTarget.parentElement)),x(L,w)},mt=L=>{const K=N(()=>E(n(i),n(h)));var w=yc(),A=l(ft(w),6),q=s(A),ee=l(q,2),O=s(ee),ue=s(O),B=l(A,2),j=l(s(B),3),te=s(j);z(()=>{m(ue,n(K)),m(te,`llama-swap/${n(b)??""}`)}),C("click",q,ae=>de(ae.currentTarget.parentElement)),x(L,w)},bt=L=>{var K=xc(),w=l(ft(K),4),A=s(w),q=l(A,2),ee=l(w,4),O=s(ee),ue=l(O,2);z(()=>{ur(A,n(i)),ur(O,n(b))}),C("click",q,B=>Y(B.currentTarget.previousElementSibling)),C("click",ue,B=>Y(B.currentTarget.previousElementSibling)),x(L,K)},Kt=L=>{var K=kc(),w=l(ft(K),4),A=s(w),q=l(A,2),ee=l(w,4),O=s(ee),ue=l(O,2);z(()=>ur(A,n(i))),C("click",q,B=>Y(B.currentTarget.previousElementSibling)),C("click",ue,B=>Y(B.currentTarget.previousElementSibling)),x(L,K)};V(Ve,L=>{n(o)==="zed"?L(Fe):n(o)==="opencode"?L(mt,1):n(o)==="cline"?L(bt,2):n(o)==="openwebui"&&L(Kt,3)})}var wt=l(Ie,2);{var Mt=L=>{const K=N(()=>`curl ${n(i)}/chat/completions \\ + -H "Content-Type: application/json" \\ + -d '{ + "model": "${n(S)}", + "messages": [{"role":"user","content":[{"type":"text","text":"Was ist auf dem Bild falsch?"},{"type":"image_url","image_url":{"url":"data:image/png;base64,"}}]}] +}'`);var w=Mc(),A=s(w),q=l(s(A),2),ee=s(q);Qe(ee,()=>ea("check"));var O=l(ee),ue=l(A,4),B=s(ue),j=l(B,2),te=s(j),ae=s(te);z(()=>{m(O,` „${n(S)??""}" bereit`),m(ae,n(K))}),C("click",B,De=>de(De.currentTarget.parentElement)),x(L,w)};V(wt,L=>{n(S)&&L(Mt)})}z(()=>ur(F,n(i))),C("click",ie,L=>{const K=L.currentTarget.previousElementSibling;Y(K)}),C("click",T,I),x(e,ce),Zn()}_a(["click","keydown"]);var Tc=M('
'),$c=M('

Für dich: Upgrades

Neuere Modelle, die ein installiertes ersetzen können.
'),Ac=M('
Lade News… (erstes Laden kann kurz dauern)
'),Cc=M('
'),Lc=M('
Keine News geladen
Die Quellen sind gerade nicht erreichbar — später nochmal probieren.
'),Pc=M('für dich'),zc=M('
'),Dc=M('
'),Oc=M('

News

Aktuelles aus der Local-LLM-Welt — aus kuratierten Quellen.
',1);function Rc(e,t){Yn(t,!0);let a=R(Bt([])),r=R(Bt([])),i=R(!0),o=R("");const d=U=>U==="perfect"?"ok":U==="marginal"?"warn":"bad";function c(U){const F=Math.max(0,Date.now()/1e3-U);return F<60?F+"s":F<3600?Math.floor(F/60)+"m":F<86400?Math.floor(F/3600)+"h":Math.floor(F/86400)+"d"}async function u(){_(i,!0),_(o,"");try{_(a,(await fe("/api/news")).items||[],!0)}catch(U){_(o,U.message||"Fehler beim Laden",!0)}finally{_(i,!1)}}async function f(){try{_(r,(await fe("/api/cookbook/upgrades")).upgrades||[],!0)}catch{_(r,[],!0)}}async function g(U,F){var ie;F.disabled=!0,F.textContent="Starte…";try{await fe("/api/cookbook/install-model",{method:"POST",body:JSON.stringify({repo:U.repo,role:U.role,params_b:U.params_b,quant:U.quant,hf_token:Ja()})}),(ie=document.querySelector(".nav-item[data-view='activity']"))==null||ie.dispatchEvent(new MouseEvent("click"))}catch(he){F.disabled=!1,F.textContent="Installieren",alert("Fehler: "+he.message)}}function $(){u(),f()}or(()=>{u(),f()});var h=Oc(),b=ft(h),S=l(s(b),2),P=l(b,2);{var E=U=>{var F=$c(),ie=s(F),he=l(s(ie),2),_e=s(he),Me=l(ie,4);Ue(Me,21,()=>n(r),Ge,(He,T)=>{var J=Tc(),se=s(J),$e=s(se),Q=s($e),be=s(Q),X=l(Q,2),ze=s(X),Se=l($e,2),Ie=s(Se),Ve=l(se,2);z(Fe=>{m(be,n(T).name),st(X,1,`fit-badge ${Fe??""}`),m(ze,n(T).fit.text),m(Ie,`Ersetzt dein „${n(T).old??""}". ${n(T).why??""}`)},[()=>d(n(T).fit.level)]),C("click",Ve,Fe=>g(n(T),Fe.currentTarget)),x(He,J)}),z(()=>m(_e,n(r).length)),x(U,F)};V(P,U=>{n(r).length&&U(E)})}var I=l(P,2);{var de=U=>{var F=Ac();x(U,F)},Y=U=>{var F=Cc(),ie=s(F);z(()=>m(ie,n(o))),x(U,F)},ce=U=>{var F=Lc();x(U,F)},me=U=>{var F=Dc();Ue(F,21,()=>n(a),Ge,(ie,he,_e)=>{var Me=zc();st(Me,1,`news-card${_e===0?" featured":""}`);var He=s(Me),T=s(He),J=s(T),se=l(T,2);{var $e=Se=>{var Ie=Pc();x(Se,Ie)};V(se,Se=>{n(he).relevant&&Se($e)})}var Q=l(se,2),be=s(Q),X=l(He,2),ze=s(X);z(Se=>{Lr(Me,"href",n(he).link),m(J,n(he).source),m(be,Se),m(ze,n(he).title)},[()=>n(he).ts?c(n(he).ts):""]),x(ie,Me)}),x(U,F)};V(I,U=>{n(i)?U(de):n(o)?U(Y,1):n(a).length?U(me,-1):U(ce,2)})}C("click",S,$),x(e,h),Zn()}_a(["click"]);const Bc=[["Dein erster Workflow — in 3 Schritten",`
  1. Modell holen: Tab Cookbook → wähle deinen Anwendungsfall (z.B. „Coden") → Komplettes Setup installieren. Der Download läuft im Tab Aktivität.
  2. @@ -75,483 +109,4 @@ var ir=Object.defineProperty;var ns=e=>{throw TypeError(e)};var sr=(e,t,n)=>t in
  3. OpenWebUI: Klassische Chat-Oberfläche für Fragen/Texte.
  4. Tipp: starkes Modell (coder) für schwere Aufgaben, schnelles - (scout) für einfache — du wählst es einfach im Tool aus.

    `]],Gl=[["LLM / Modell","Die KI selbst — versteht und schreibt Text (wie ChatGPT, nur lokal auf deinem PC). Ein Modell ist eine konkrete Datei davon, oft auf etwas spezialisiert (Code, Bilder, Chat)."],["GGUF","Das lokal lauffähige Dateiformat. Unsere Engine lädt nur GGUF — Repos ohne GGUF kann sie nicht nutzen."],["Quantisierung (Q4_K_M)","Verkleinert ein Modell (16-Bit → 4-Bit), damit es in den Speicher passt — bei kaum Qualitätsverlust. Q4_K_M ist der beliebte Sweetspot."],["Kontextfenster","Das Kurzzeitgedächtnis: wie viel Text sich das Modell gleichzeitig merkt. Größer = mehr Überblick, mehr Speicher, etwas langsamer."],["Auto-Swap","llama-swap lädt/entlädt Modelle automatisch — immer nur eins gleichzeitig. Du managst nichts."],["Agent","Ein Programm (OpenCode, Cline, Continue), das das Modell handeln lässt — Dateien lesen/ändern, Befehle ausführen, planen, im Loop bis die Aufgabe erledigt ist."],["MCP","Model Context Protocol — der Anschluss, über den ein Agent zusätzliche Werkzeuge bekommt (Web, Dateien, Datenbank). MCP = Werkzeuge."],["Skills","Verpackte Anleitung für wiederkehrende Aufgaben — der Agent liest sie und macht es jedes Mal wie du willst. Skills = Anleitungen."],["Rules / Commands","Die konkrete Form von Skills je Tool: AGENTS.md (OpenCode), .clinerules (Cline), Rules/Prompts (Continue)."]];var Kl=be('
    '),ql=be('

    '),Ul=be('

    Guide für Einsteiger

    Praktische Workflows + die Local-LLM-Welt in Klartext — ohne Vorwissen.

    Tutorials & Workflows

    Konzepte kurz erklärt

    ',1);function jl(e){var t=Ul(),n=J(pa(t),4);Wn(n,21,()=>Il,Vn,(s,a,r)=>{var o=rs(()=>ss(M(a),2));let c=()=>M(o)[0],u=()=>M(o)[1];var p=Kl();p.open=r===0;var v=R(p),f=R(v),m=J(v,2);Sl(m,u,!0),ot(()=>ye(f,c())),ce(s,p)});var i=J(n,4);Wn(i,21,()=>Gl,Vn,(s,a)=>{var r=rs(()=>ss(M(a),2));let o=()=>M(r)[0],c=()=>M(r)[1];var u=ql(),p=R(u),v=R(p),f=J(p,2),m=R(f);ot(()=>{ye(v,o()),ye(m,c())}),ce(s,u)}),ce(e,t)}var Vl=be('
    '),Wl=be('

    Für dich: Upgrades

    Neuere Modelle, die ein installiertes ersetzen können.
    '),Jl=be('
    Lade News… (erstes Laden kann kurz dauern)
    '),Ql=be('
    '),Yl=be('
    Keine News geladen
    Die Quellen sind gerade nicht erreichbar — später nochmal probieren.
    '),Zl=be('für dich'),Xl=be('
    '),eo=be('
    '),to=be('

    News

    Aktuelles aus der Local-LLM-Welt — aus kuratierten Quellen.
    ',1);function no(e,t){Ws(t,!0);let n=ee(Qe([])),i=ee(Qe([])),s=ee(!0),a=ee("");const r=k=>k==="perfect"?"ok":k==="marginal"?"warn":"bad";function o(k){const A=Math.max(0,Date.now()/1e3-k);return A<60?A+"s":A<3600?Math.floor(A/60)+"m":A<86400?Math.floor(A/3600)+"h":Math.floor(A/86400)+"d"}async function c(){V(s,!0),V(a,"");try{V(n,(await $("/api/news")).items||[],!0)}catch(k){V(a,k.message||"Fehler beim Laden",!0)}finally{V(s,!1)}}async function u(){try{V(i,(await $("/api/cookbook/upgrades")).upgrades||[],!0)}catch{V(i,[],!0)}}async function p(k,A){var G;A.disabled=!0,A.textContent="Starte…";try{await $("/api/cookbook/install-model",{method:"POST",body:JSON.stringify({repo:k.repo,role:k.role,params_b:k.params_b,quant:k.quant,hf_token:_n()})}),(G=document.querySelector(".nav-item[data-view='activity']"))==null||G.dispatchEvent(new MouseEvent("click"))}catch(O){A.disabled=!1,A.textContent="Installieren",alert("Fehler: "+O.message)}}function v(){c(),u()}zl(()=>{c(),u()});var f=to(),m=pa(f),h=J(R(m),2),b=J(m,2);{var C=k=>{var A=Wl(),G=R(A),O=J(R(G),2),Wt=R(O),Ke=J(G,4);Wn(Ke,21,()=>M(i),Vn,($n,Be)=>{var En=Vl(),Jt=R(En),Tn=R(Jt),Cn=R(Tn),si=R(Cn),Ln=J(Cn,2),ai=R(Ln),st=J(Tn,2),ri=R(st),nr=J(Jt,2);ot(li=>{ye(si,M(Be).name),ms(Ln,1,`fit-badge ${li??""}`),ye(ai,M(Be).fit.text),ye(ri,`Ersetzt dein „${M(Be).old??""}". ${M(Be).why??""}`)},[()=>r(M(Be).fit.level)]),fs("click",nr,li=>p(M(Be),li.currentTarget)),ce($n,En)}),ot(()=>ye(Wt,M(i).length)),ce(k,A)};pi(b,k=>{M(i).length&&k(C)})}var L=J(b,2);{var z=k=>{var A=Jl();ce(k,A)},j=k=>{var A=Ql(),G=R(A);ot(()=>ye(G,M(a))),ce(k,A)},oe=k=>{var A=Yl();ce(k,A)},Oe=k=>{var A=eo();Wn(A,21,()=>M(n),Vn,(G,O,Wt)=>{var Ke=Xl();ms(Ke,1,`news-card${Wt===0?" featured":""}`);var $n=R(Ke),Be=R($n),En=R(Be),Jt=J(Be,2);{var Tn=st=>{var ri=Zl();ce(st,ri)};pi(Jt,st=>{M(O).relevant&&st(Tn)})}var Cn=J(Jt,2),si=R(Cn),Ln=J($n,2),ai=R(Ln);ot(st=>{Cl(Ke,"href",M(O).link),ye(En,M(O).source),ye(si,st),ye(ai,M(O).title)},[()=>M(O).ts?o(M(O).ts):""]),ce(G,Ke)}),ce(k,A)};pi(L,k=>{M(s)?k(z):M(a)?k(j,1):M(n).length?k(Oe,-1):k(oe,2)})}fs("click",h,v),ce(e,f),Js()}ml(["click"]);let Ge=null,ne=null;const Ba=new Set(["running","ready","loading","starting"]);function ji(){return(Ge==null?void 0:Ge.models)||[]}function Ha(){return ji().find(e=>Ba.has(e.state))}function io(e){var t;(t=document.querySelector(`.nav-item[data-view="${e}"]`))==null||t.click()}async function so(){if(await Ee({title:"Speicher freigeben?",body:"Alle aktuell geladenen Modelle werden aus dem Grafikspeicher geworfen. Beim nächsten Aufruf lädt das jeweilige Modell einfach neu — es geht nichts verloren.",confirmLabel:"Speicher freigeben"}))try{await $("/api/unload",{method:"POST"}),w("Speicher freigegeben."),setTimeout(()=>document.dispatchEvent(new Event("mc:refresh")),600)}catch(t){w(t.message,!0)}}window.mcOv={go:io,freeMemory:so};function Vi(){let e="Verbinde…",t="";if(Ge){const n=ji().length,i=Ha();Ge.swap_ok?ne&&ne.ram.percent>=90?(e="Achtung: Speicher wird knapp.",t=`Arbeitsspeicher bei ${Math.round(ne.ram.percent)} % — eventuell Speicher freigeben.`):(e="Alles läuft rund.",t=`${n} ${n===1?"Modell":"Modelle"} bereit · ${i?`„${i.name}" geladen`:"keins geladen"} · keine Warnungen.`):(e="LLM-Engine offline",t="Der llama-swap-Dienst antwortet gerade nicht.")}l("#ov-hero").innerHTML=`
    -

    ${g(e)}

    ${g(t)}
    `}function Mt(e,t,n,i="",s=""){return`
    ${e}
    -
    ${t}
    ${n}
    `}function ao(e){return e==null?"":e<55?"kühl & gesund":e<72?"normal":"läuft heiß"}function Wi(){const e=Ha(),t=Ge==null?void 0:Ge.swap_ok;let n=Mt("LLM-Engine",t?"Online":"Offline","llama-swap",t?"ok":"bad"),i=Mt("Aktives Modell",e?g(e.name):"Keins",e?"im Grafikspeicher":"nichts geladen"),s,a;if(ne){const r=ne.gpu_temp??ne.cpu.temp;s=Mt("GPU-Temperatur",r!=null?`${Math.round(r)}°`:"–",ao(r),"",r!=null&&r<72?"ok":"");const o=ne.ram.total-ne.ram.used;a=Mt("Freier Speicher",`${(o/1024**3).toFixed(0)} GB`,`von ${(ne.ram.total/1024**3).toFixed(0)} GB`)}else s=Mt("GPU-Temperatur","–","messe…"),a=Mt("Freier Speicher","–","messe…");l("#ov-tiles").innerHTML=n+i+s+a}function hi(e,t){const n=Math.max(0,Math.min(100,t||0)),i=n>=90?"bad":n>=75?"warn":"";return`
    ${e} - ${Math.round(n)} %
    -
    `}function Ra(){const e=`

    System-Gesundheit

    -
    So ausgelastet ist dein Mini-PC gerade.
    `;if(!ne){l("#ov-health").innerHTML=e+'
    Warte auf Messwerte…
    ';return}let t=0;const n=ne.gpu;n&&n.vram.total+n.gtt.total>0&&(t=(n.vram.used+n.gtt.used)/(n.vram.total+n.gtt.total)*100),l("#ov-health").innerHTML=e+hi("Prozessor (CPU)",ne.cpu.percent)+hi("Arbeitsspeicher (RAM)",ne.ram.percent)+hi("Grafikspeicher (VRAM)",t)}function vi(e,t,n,i,s){return``}function Fa(){l("#ov-quickstart").innerHTML=`

    Schnellstart

    -
    Die häufigsten Aufgaben — ein Klick.
    `+vi("search","teal","Modell finden","Passend zu deiner Hardware","window.mcOv.go('cookbook')")+vi("refresh","amber","Speicher freigeben","Modelle entladen · fragt vorher nach","window.mcOv.freeMemory()")+vi("file","blue","Logs ansehen","Live mitlesen, was läuft","window.mcOv.go('server')")}function ro(e){return e?e.includes("Bild")?'Bild':e.includes("Code")?'Code':'Text':""}function lo(e){var r,o;const t=e.state==="downloading",n=Ba.has(e.state),i=e.state==="loading"||e.state==="starting"||t?"load":n?"on":"";let s;t?s=`↓ Download${e.download_progress!=null?` ${Math.round(e.download_progress)}%`:""}`:s=n?e.state==="loading"?"lädt…":"geladen":"bereit";const a=(r=e.meta)!=null&&r.filename?`
    ${g(e.meta.filename)}
    `:"";return`
    -
    -
    ${g(e.name)}${ro((o=e.meta)==null?void 0:o.caps)}
    ${a}
    - ${g(s)}
    `}async function oo(){const e=l("#ov-news");if(!e)return;const t=`

    Top-News

    Alle →
    `;e.innerHTML=t+'
    Lade…
    ';try{const n=((await $("/api/news")).items||[]).slice(0,4);e.innerHTML=t+(n.length?`
    ${n.map(i=>` -
    ${g(i.title)}
    -
    ${g(i.source)}
    `).join("")}
    `:'
    Keine News.
    ')}catch{e.innerHTML=t+'
    News gerade nicht ladbar.
    '}}function Na(){const e=ji();l("#ov-stack").innerHTML=`

    Dein Stack

    ${e.length?e.length+" konfiguriert":""}
    `+(e.length?`
    ${e.map(lo).join("")}
    `:`
    Noch keine Modelle
    -
    Hol dir unter „Cookbook" ein Modell, das auf deine Hardware passt.
    `)}function co(){Vi(),Wi(),Ra(),Fa(),Na()}function uo(){Fa(),co(),oo()}function fo(e){Ge=e,Vi(),Wi(),Na()}function po(e){ne=e,Vi(),Wi(),Ra()}const ho={id:"overview",mount:uo,onStatus:fo,onSystem:po};let _s=[];const vo="Kontext = das Kurzzeitgedächtnis des Modells. Größer = merkt sich mehr (längere Dateien/Chats), braucht aber mehr Speicher und wird etwas langsamer.",mo="Die Rolle ist ein zusätzlicher, sprechender Name (z.B. coder). Du kannst in deinen Tools entweder den echten Modellnamen ODER die Rolle angeben — beides führt zum selben Modell.",go=[{id:"coder",label:"Coder",desc:"Programmieren & Code"},{id:"vision",label:"Vision",desc:"Bilder verstehen"},{id:"scout",label:"Scout",desc:"Schneller Allrounder"},{id:"reviewer",label:"Reviewer",desc:"Code/Texte prüfen"},{id:"manager",label:"Manager",desc:"Planen & koordinieren"}];let je=[];function kn(){document.dispatchEvent(new Event("mc:refresh"))}function bo(e){return Bl(e.state,e.download_progress)}function wo(e){return e?`${g(e)}`:""}function yo(e){return`
    - API-Name: - ${(e.api_ids&&e.api_ids.length?e.api_ids:[e.name]).map(n=>`${g(n)}`).join('·')} -
    `}function xo(e){return e!=null&&e.length?e.map(t=>t==="Code"?'Code':t==="Bild"?'Bild':'Text').join(" "):"–"}function _o(e){if(!e)return"–";const t=e.quant||"?",n=e.ctx?Math.round(e.ctx/1024)+"K":"?",i=e.size_bytes?(e.size_bytes/1024**3).toFixed(1)+" GB":"?";return`${g(t)} · ${n} · ${i}`}function ko(){l("#m-head").innerHTML=`
    -

    Modelle

    -
    Deine konfigurierten Modelle — testen, Kontext anpassen oder aus dem Speicher werfen.
    `,l("#m-chat").innerHTML=` -

    Schnelltest

    -
    Schreib eine Nachricht — das gewählte Modell wird automatisch geweckt.
    - - - - - - `,l("#m-table").innerHTML=` -

    Modelle & Ports

    -
    Modelle laden automatisch, sobald eine Anfrage kommt — du musst nichts manuell starten.
    - - - -
    ModellKannDetailsStatusPortAktionen
    - - - - - `,l("#chat-btn").addEventListener("click",Ao),l("#cfg-close").addEventListener("click",()=>l("#cfg-modal").style.display="none"),l("#cfg-modal").addEventListener("click",e=>{e.target.id==="cfg-modal"&&(l("#cfg-modal").style.display="none")}),l("#cfg-save").addEventListener("click",Co),l("#role-close").addEventListener("click",()=>l("#role-modal").style.display="none"),l("#role-modal").addEventListener("click",e=>{e.target.id==="role-modal"&&(l("#role-modal").style.display="none")}),l("#role-save").addEventListener("click",()=>Pi(l("#role-custom").value.trim().toLowerCase())),l("#role-clear").addEventListener("click",()=>Pi("")),l("#m-table").addEventListener("click",e=>{var s,a;const t=e.target.closest("[data-action]");if(!t)return;const n=t.getAttribute("data-name"),i=t.getAttribute("data-action");n&&(i==="cfg"?To(n):i==="role"?Mo(n):i==="unload"?Lo(n):i==="del"?Eo(n):i==="copy"?((s=navigator.clipboard)==null||s.writeText(n),w("Kopiert: "+n)):i==="assign"&&(w("Lade im Cookbook ein Modell und setze den Alias auf: "+n),(a=document.querySelector(".nav-item[data-view='cookbook']"))==null||a.click()))})}let Di=null;function Mo(e){Di=e;const t=je.find(n=>n.name===e);l("#role-model-name").textContent=e,l("#role-custom").value="",l("#role-chips").innerHTML=go.map(n=>``).join(""),l("#role-chips").querySelectorAll("[data-rolepick]").forEach(n=>n.addEventListener("click",()=>Pi(n.getAttribute("data-rolepick")))),l("#role-modal").style.display="flex"}async function Pi(e){if(Di)try{await $("/api/set_role",{method:"POST",body:JSON.stringify({alias:Di,role:e})}),w(e?`Rolle gesetzt: ${e}`:"Rolle entfernt."),l("#role-modal").style.display="none",kn()}catch(t){w(t.message,!0)}}function Ia(){const e=l("#models");if(!e)return;l("#models-empty").style.display=je.length?"none":"flex",l("#m-count").textContent=je.length?je.length+" konfiguriert":"";const t=l("#chat-model"),n=t.value;t.innerHTML="",e.innerHTML=je.map(i=>{var a,r;if(i.incomplete)return` - ${g(i.name)}
    kein Modell hinterlegt
    - — - leere Rolle - leer - — - - - - `;const s=(a=i.meta)!=null&&a.filename?`
    ${g(i.meta.filename)}
    `:"";return` - -
    ${g(i.name)}${wo(i.role)}
    - ${s}${yo(i)} - ${xo((r=i.meta)==null?void 0:r.caps)} - ${_o(i.meta)} - ${bo(i)} - ${i.port??"auto"} - - - - - - `}).join("");for(const i of je)i.incomplete||t.insertAdjacentHTML("beforeend",``);n&&(t.value=n)}function So(e){je=(e==null?void 0:e.models)||[],Ia()}function $o(e){_s=e||[],_s.some(n=>(n.state==="running"||n.state==="queued")&&/^download\b/i.test(n.label))&&Ia()}async function Eo(e){if(await Ee({title:`„${e}" wirklich löschen?`,body:"Das Modell wird aus der Konfiguration ausgetragen und seine Dateien werden von der Festplatte gelöscht, um Speicher freizugeben. Das lässt sich nicht rückgängig machen — später brauchst du dafür einen neuen Download.",confirmLabel:"Endgültig löschen",danger:!0}))try{const n=await $("/api/delete_model",{method:"POST",body:JSON.stringify({alias:e})});w(n.note||"Gelöscht: "+e),setTimeout(kn,600)}catch(n){w(n.message,!0)}}function To(e){var s,a,r,o,c;const t=je.find(u=>u.name===e);if(!t)return;l("#cfg-model-name").textContent=t.name,l("#cfg-ctx").value=((s=t.meta)==null?void 0:s.ctx)||8192;const n=(a=t.meta)==null?void 0:a.optimal_ctx,i=l("#cfg-rec");if(n){const u=((r=t.meta)==null?void 0:r.ctx)||0,p=(o=t.meta)==null?void 0:o.peak_ram_gb,v=(c=t.meta)==null?void 0:c.peak_ram_optimal_gb;l("#cfg-rec-note").innerHTML=`Optimal ~${Math.round(n/1024)}k → ~${v} GB Spitzenbedarf.
    Aktuell ~${Math.round(u/1024)}k → ~${p} GB.`,l("#cfg-rec-apply").onclick=()=>{l("#cfg-ctx").value=n},i.style.display="flex"}else i.style.display="none";l("#cfg-modal").style.display="flex"}async function Co(){const e=l("#cfg-model-name").textContent,t=parseInt(l("#cfg-ctx").value)||8192;l("#cfg-save").disabled=!0;try{await $("/api/update_model",{method:"POST",body:JSON.stringify({alias:e,ctx:t})}),w("Gespeichert — aktiv beim nächsten Modell-Start."),l("#cfg-modal").style.display="none",kn()}catch(n){w(n.message,!0)}l("#cfg-save").disabled=!1}async function Lo(e){if(await Ee({title:`„${e}" entladen?`,body:"Das Modell wird aus dem Grafikspeicher geworfen und lädt beim nächsten Aufruf automatisch neu.",confirmLabel:"Entladen"}))try{await $("/api/unload?model="+encodeURIComponent(e),{method:"POST"}),w("Entladen: "+e),setTimeout(kn,600)}catch(t){w(t.message,!0)}}async function Ao(){const e=l("#chat-model").value,t=l("#chat-msg").value.trim();if(!e)return w("Kein Modell vorhanden.",!0);if(!t)return;const n=l("#chat-btn");n.disabled=!0,n.textContent="…";const i=l("#chat-reply");i.style.display="block",i.textContent="(wecke Modell, kann beim Laden kurz dauern…)";try{const s=await $("/api/chat",{method:"POST",body:JSON.stringify({model:e,message:t})});i.textContent=s.reply}catch(s){i.textContent="Fehler: "+s.message}n.disabled=!1,n.textContent="Senden",kn()}const zo={id:"models",mount:ko,onStatus:So,onJobs:$o},dn=new Set;let lt=[],B=null;function Ji(e){dn.add(e),Vt()}const Ue={cpu:[],ram:[],gpu:[]},Do=60;function Po(e){return e==="done"?'fertig':e==="failed"?'fehler':e==="canceled"?'abgebrochen':'läuft…'}function Oo(e){return e==="done"?"on":e==="failed"||e==="canceled"?"":"load"}function Bo(e){if(e.state!=="running")return null;if(typeof e.progress=="number")return Math.min(100,e.progress);const n=((e.log||[])[e.log.length-1]||"").match(/(\d+(?:\.\d+)?)\s*%/);return n?Math.min(100,parseFloat(n[1])):null}function Ho(e){return e==null||e<0?"":e>=90?`noch ~${Math.round(e/60)} min`:`noch ~${Math.max(1,Math.round(e))} s`}async function Ro(e){try{await $(`/api/jobs/${e}/cancel`,{method:"POST"}),w("Abbruch angefordert…"),Vt()}catch(t){w(t.message,!0)}}async function Fo(e){try{await $(`/api/jobs/${e}`,{method:"DELETE"}),Vt()}catch(t){w(t.message,!0)}}async function No(){try{const e=await $("/api/jobs/clear",{method:"POST"});w(`${e.removed} Einträge entfernt.`),Vt()}catch(e){w(e.message,!0)}}function mi(e,t,n){return`
    ${e}
    ${t}
    ${n}
    `}function gi(e,t){return'
    '+e.map(n=>`
    `).join("")+"
    "}function ks(){const e=B==null?void 0:B.gpu;return e&&e.vram.total+e.gtt.total>0?(e.vram.used+e.gtt.used)/(e.vram.total+e.gtt.total)*100:0}function Ga(){if(!B)return;Ue.cpu.push(B.cpu.percent),Ue.ram.push(B.ram.percent),Ue.gpu.push(ks());for(const s of["cpu","ram","gpu"])Ue[s].length>Do&&Ue[s].shift();const e=l("#act-kpis");e&&(e.innerHTML=mi("Prozessor (CPU)",`${Math.round(B.cpu.percent)} %`,B.cpu.temp!=null?`${Math.round(B.cpu.temp)}° CPU-Temp`:"Auslastung")+mi("Arbeitsspeicher",`${Math.round(B.ram.percent)} %`,`${rt(B.ram.used)} / ${rt(B.ram.total)}`)+mi("Grafikspeicher",`${Math.round(ks())} %`,B.gpu_temp!=null?`${Math.round(B.gpu_temp)}° GPU-Temp`:"VRAM + GTT"));const t=B.gpu,n=t&&t.vram.total+t.gtt.total>0?`${rt(t.vram.used+t.gtt.used)} / ${rt(t.vram.total+t.gtt.total)}`:"–",i=l("#act-sys");i&&(i.innerHTML=` -

    System-Metriken (Bosgame)

    -
    Live-Auslastung deines Mini-PCs, alle 0,5 Sekunden.
    -
    -
    Arbeitsspeicher (RAM)${rt(B.ram.used)} / ${rt(B.ram.total)}
    -
    Grafikspeicher (VRAM + GTT)${n}
    -
    Speicherplatz (Disk)${Math.round(B.disk.percent)} % belegt
    -
    Temperatur (GPU / CPU)${B.gpu_temp!=null?Math.round(B.gpu_temp)+"°":"–"} / ${B.cpu.temp!=null?Math.round(B.cpu.temp)+"°":"–"}
    -
    -
    -
    CPU-Verlauf
    ${gi(Ue.cpu,"--accent")}
    -
    RAM-Verlauf
    ${gi(Ue.ram,"--purple")}
    -
    VRAM-Verlauf
    ${gi(Ue.gpu,"--on")}
    -
    `)}function Io(){l("#act-head").innerHTML=`
    -

    Aktivität

    -
    Live-Auslastung und laufende Aufgaben (Downloads, Updates) mit Protokoll.
    `,l("#v-activity").innerHTML=` -

    Hintergrund-Aufgaben

    -
    -
    Downloads & Updates erscheinen hier mit Live-Protokoll — zum Aufklappen klicken.
    -
    -
    Gerade nichts los.
    Alles ruhig — keine laufenden Aufgaben.
    `,l("#v-activity").addEventListener("click",e=>{if(e.target.closest("#jobs-clear")){No();return}const t=e.target.closest("[data-cancel]");if(t){e.stopPropagation(),Ro(t.getAttribute("data-cancel"));return}const n=e.target.closest("[data-del]");if(n){e.stopPropagation(),Fo(n.getAttribute("data-del"));return}const i=e.target.closest(".job-h");if(!i)return;const s=i.getAttribute("data-id");dn.has(s)?dn.delete(s):dn.add(s),Vt()}),B&&Ga()}function Vt(){const e=l("#jobs");if(!e)return;l("#jobs-empty").style.display=lt.length?"none":"flex";const t=lt.filter(s=>s.state==="failed").length,n=lt.filter(s=>s.state!=="running"&&s.state!=="queued").length;l("#job-count").textContent=lt.length?t?t+" Fehler":lt.length+" gesamt":"";const i=l("#jobs-clear");i&&(i.style.display=n?"":"none"),e.innerHTML=lt.map(s=>{const a=dn.has(s.id)?`
    ${g((s.log||[]).join(` -`))}
    `:"",r=Bo(s),o=r!=null?`
    `:"",c=s.state==="running"?Ho(s.eta_s):"",u=s.state==="running"&&s.rate_bps?rt(s.rate_bps)+"/s":"",p=[c,u].filter(Boolean).join(" · "),v=r!=null?`${Math.round(r)} %${p?` · ${p}`:""}`:"",f=s.state==="running"?``:``;return`
    - ${g(s.label)}${v}${f}${Po(s.state)}
    ${o}${a}
    `}).join("")}function Go(e){lt=e||[],Vt()}function Ko(e){B=e,l("#act-sys")&&Ga()}const qo={id:"jobs",mount:Io,onJobs:Go,onSystem:Ko};function Qi(){document.dispatchEvent(new Event("mc:refresh"))}function at(e,t,n,i,s){return``}function Uo(){l("#wartung").outerHTML=`
    -
    -

    Server & Wartung

    -
    Dienste steuern, Updates einspielen und live mitlesen — ganz ohne SSH oder Terminal.
    -
    - -
    -

    LLM-Engine (llama-swap)

    - prüfe…
    -
    Das Herzstück: llama-swap lädt bei jeder Anfrage automatisch das passende Modell und entlädt die anderen — es läuft immer nur eines gleichzeitig.
    -
    -
    Adresse (für deine Tools)
    -
    Aktuell geladen
    -
    Konfigurierte Modelle
    -
    -
    Wie ansteuern? In deinem Tool (Zed/OpenCode) die obige Adresse als OpenAI-Basis-URL eintragen und einen Modellnamen oder eine Rolle angeben (siehe „Verbinden"). Eine einzige Anfrage genügt — das Modell wird automatisch geweckt.
    -
    -
    - -
    -

    Dienste & Applikation

    -
    Diese Aktionen sind harmlos und passwortlos — es geht nichts dabei verloren.
    - ${at("w-restart-swap","refresh","teal","LLM-Engine neustarten","Startet llama-swap neu. Geladene Modelle werden kurz entladen (~5 Sek), laden danach automatisch wieder.")} - ${at("w-restart-mc","refresh","blue","Dashboard neustarten","Startet diese Oberfläche neu. Sie trennt kurz und verbindet automatisch wieder.")} - ${at("w-self-update","download","teal","Mission Control aktualisieren","Holt die neueste Dashboard-Version aus Gitea und startet automatisch neu (~10 Sek).")} - ${at("w-update","bolt","blue","LLM-Engine aktualisieren","Lädt die neueste llama.cpp-Version (ROCm) für deine GPU und installiert sie.")} - ${at("w-unload","database","amber","Grafikspeicher leeren","Wirft alle aktuell geladenen Modelle aus dem VRAM. Fragt vorher nach.")} -
    - -
    -

    Betriebssystem (Bosgame)

    -
    Tiefe Eingriffe — das Dashboard fragt einmalig nach deinem sudo-Passwort.
    - ${at("w-os-update","refresh","amber","OS-Updates installieren","Führt apt update & upgrade aus. Kann ein paar Minuten dauern — Fortschritt in der Aktivität.")} - ${at("w-reboot","bolt","red","Server neustarten (Reboot)","Bootet den ganzen Bosgame neu. Alles ist für ~1 Minute offline, auch dieses Dashboard.")} -
    - - - -
    -

    Live-Konsole

    - -
    -
    Live mitlesen, was der Dienst gerade tut.
    -
    Verbinde…
    -
    -
    `,l("#w-restart-swap").addEventListener("click",()=>Ss("llama-swap","LLM-Engine neustarten?","Die Engine startet neu. Geladene Modelle werden kurz entladen (~5 Sekunden), laden danach automatisch wieder.")),l("#w-restart-mc").addEventListener("click",()=>Ss("mission-control","Dashboard neustarten?","Diese Oberfläche trennt sich kurz und verbindet automatisch wieder. Laufende Downloads laufen weiter.")),l("#w-self-update").addEventListener("click",Qo),l("#w-update").addEventListener("click",Zo),l("#w-unload").addEventListener("click",Jo),l("#w-os-update").addEventListener("click",Xo),l("#w-reboot").addEventListener("click",ed),l("#w-console-sel").addEventListener("change",Ms),l("#w-update-cancel").addEventListener("click",Wo),l("#w-swap-check").addEventListener("click",jo),Oi&&Ka(Oi),Ms()}let Oi=null;function Ka(e){const t=l("#w-swap-state");if(!t)return;const n=!!(e!=null&&e.swap_ok);t.textContent=n?"● erreichbar":"● offline",t.style.cssText="margin-left:auto;"+(n?"background:rgba(63,185,80,.12);border-color:rgba(63,185,80,.25);color:#7ee29a":"background:rgba(248,81,73,.12);border-color:rgba(248,81,73,.3);color:#ff9b95"),l("#w-swap-url").textContent=((e==null?void 0:e.swap_url)||"—")+"/v1";const i=(e==null?void 0:e.models)||[],s=i.find(r=>r.state==="running"||r.state==="loading");l("#w-swap-loaded").innerHTML=s?`${g(s.name)} (${g(s.state)})`:'nichts geladen — lädt bei der nächsten Anfrage';const a=i.filter(r=>!r.incomplete).length;l("#w-swap-count").textContent=a?`${a} bereit`:"keine"}async function jo(){const e=l("#w-swap-check-res");e.textContent="prüfe…",e.style.color="var(--mut)";try{const t=await $("/api/integration/test");t.ok?(e.style.color="#7ee29a",e.textContent=`✓ Engine antwortet — ${t.models.length} Modell(e): ${t.models.join(", ")}`):(e.style.color="#ff9b95",e.textContent="✗ keine Verbindung: "+t.error)}catch(t){e.style.color="#ff9b95",e.textContent="✗ "+t.message}}function Vo(e){Oi=e,Ka(e)}async function Wo(){if(Le&&await Ee({title:Le.label+" abbrechen?",body:"Der laufende Vorgang wird gestoppt. Bei einem Download bleibt eine unvollständige Datei zurück (kannst du später neu starten).",confirmLabel:"Abbrechen",danger:!0}))try{await $(`/api/jobs/${Le.id}/cancel`,{method:"POST"}),w("Abbruch angefordert…")}catch(e){w(e.message,!0)}}let St=null;function Ms(){St&&(St.close(),St=null);const e=l("#w-console-sel").value,t=l("#w-console");t.textContent="Verbinde mit "+e+`… -`;const n=location.protocol==="https:"?"wss:":"ws:";St=new WebSocket(`${n}//${location.host}/api/logs/${e}?token=${encodeURIComponent(Da())}`),St.onmessage=i=>{t.textContent+=i.data,t.scrollTop=t.scrollHeight},St.onclose=()=>{t.textContent+=` -— Verbindung getrennt —`}}async function Ss(e,t,n){if(await Ee({title:t,body:n,confirmLabel:"Neustarten"}))try{await $(`/api/service/${e}/restart`,{method:"POST"}),w("Neustart ausgelöst: "+e),setTimeout(Qi,2e3)}catch(i){w(i.message,!0)}}async function Jo(){if(await Ee({title:"Grafikspeicher leeren?",body:"Alle geladenen Modelle werden entladen. Sie laden beim nächsten Aufruf automatisch neu — es geht nichts verloren.",confirmLabel:"Leeren"}))try{await $("/api/unload",{method:"POST"}),w("Grafikspeicher geleert."),setTimeout(Qi,600)}catch(e){w(e.message,!0)}}async function Qo(){if(await Ee({title:"Mission Control aktualisieren?",body:"Holt die neueste Dashboard-Version aus Gitea und startet automatisch neu. Die Seite trennt kurz und verbindet von selbst wieder (~10 Sek)."}))try{const e=await $("/api/self-update",{method:"POST"});w("Update läuft — Dashboard startet gleich neu."),Ji(e.job_id)}catch(e){w(e.message,!0)}}let Le=null;const $s=new Set;function qa(e,t){Le={id:e,label:t};const n=l("#w-update-card");n&&(n.style.display="",l("#w-update-label").textContent=t+" läuft…",l("#w-update-badge").innerHTML='läuft…',l("#w-update-log").textContent="Starte…",n.scrollIntoView({behavior:"smooth",block:"nearest"}))}function Yo(e){if(!Le)return;const t=(e||[]).find(v=>v.id===Le.id),n=l("#w-update-card");if(!t||!n)return;n.style.display="";const i=l("#w-update-log");i&&(i.textContent=(t.log||[]).join(` -`),i.scrollTop=i.scrollHeight);const s=t.state==="done",a=t.state==="failed",r=t.state==="canceled",o=s||a||r,c=l("#w-update-badge");c&&(c.innerHTML=s?'✓ fertig':a?'✗ fehlgeschlagen':r?'abgebrochen':'läuft…');let u=null;if(!o)if(typeof t.progress=="number")u=t.progress;else{const f=((t.log||[])[t.log.length-1]||"").match(/(\d+(?:\.\d+)?)\s*%/);f&&(u=parseFloat(f[1]))}const p=l("#w-update-bar");p&&(u!=null?(p.style.display="",p.querySelector("i").style.width=Math.min(100,u)+"%"):p.style.display="none"),l("#w-update-cancel").style.display=o?"none":"",o&&(l("#w-update-label").textContent=Le.label+(s?" — erfolgreich abgeschlossen.":r?" — abgebrochen.":" — fehlgeschlagen (siehe Protokoll)."),$s.has(t.id)||($s.add(t.id),w(s?"✓ "+Le.label+" abgeschlossen.":r?Le.label+" abgebrochen.":"✗ "+Le.label+" fehlgeschlagen.",a),s&&setTimeout(Qi,800)))}async function Zo(){if(await Ee({title:"LLM-Engine aktualisieren?",body:"Lädt die neueste llama.cpp-ROCm-Version und installiert sie. Die Engine ist während des Updates kurz nicht erreichbar."}))try{const e=await $("/api/update",{method:"POST"});w("LLM-Engine-Update läuft."),Ji(e.job_id),qa(e.job_id,"LLM-Engine-Update")}catch(e){w(e.message,!0)}}async function Xo(){if(!await Ee({title:"OS-Updates installieren?",body:"Führt apt update & upgrade aus. Das kann ein paar Minuten dauern; der Fortschritt erscheint in der Aktivität."}))return;const e=await Oa({title:"sudo-Passwort",body:"Für die System-Updates wird einmalig dein sudo-Passwort gebraucht.",placeholder:"sudo-Passwort",password:!0,confirmLabel:"Installieren"});if(e)try{const t=await $("/api/os-update",{method:"POST",body:JSON.stringify({password:e})});w("OS-Update gestartet."),Ji(t.job_id),qa(t.job_id,"OS-Update")}catch(t){w(t.message,!0)}}async function ed(){if(!await Ee({title:"Server wirklich neu starten?",body:"Der ganze Bosgame startet physisch neu. Alles ist für ~1 Minute offline — auch dieses Dashboard.",confirmLabel:"Reboot",danger:!0}))return;const e=await Oa({title:"sudo-Passwort",body:"Für den Reboot wird einmalig dein sudo-Passwort gebraucht.",placeholder:"sudo-Passwort",password:!0,confirmLabel:"Jetzt neustarten",danger:!0});if(e)try{await $("/api/reboot",{method:"POST",body:JSON.stringify({password:e})}),w("Reboot ausgelöst — bis gleich.")}catch(t){w(t.message,!0)}}const td={id:"server",mount:Uo,onJobs:Yo,onStatus:Vo},nd="Kontext = das Kurzzeitgedächtnis des Modells. Größer = merkt sich mehr, braucht aber mehr Speicher und wird etwas langsamer.",Ua="GGUF ist das lokal lauffähige Dateiformat. Unsere Engine lädt nur GGUF — Repos ohne GGUF-Datei kann sie nicht nutzen.",id="MoE (Mixture of Experts): viele Parameter fürs Wissen, aber pro Wort ist nur ein kleiner Teil aktiv → fast große-Modell-Qualität bei hohem Tempo. Ideal für deine APU.",sd={vision:"Bilder",coder:"Coden",reasoning:"Logik",agent:"Agenten & Tools",scout:"Allrounder",reviewer:"Review",manager:"Manager"},ad=[{label:"Bilder",kw:["-vl-","-vl","vision","llava","multimodal","-mm-","pixtral"]},{label:"Coden",kw:["coder","-code-","code-","codestral","starcoder"]},{label:"Logik",kw:["-r1","deepseek-r1","reasoning","qwq","magistral","-think","thinking","-o1"]},{label:"Agenten & Tools",kw:["hermes","-tool","command-r","watt","-fc-","function"]}];function ja(e){const t=(e||"").toLowerCase();for(const n of ad)if(n.kw.some(i=>t.includes(i)))return n.label;return"Allrounder"}function rd(e=""){const t=e.toLowerCase();return/\d+x\d+(\.\d+)?b/.test(t)||new RegExp("(?MoE${t?` · ~${t}B aktiv`:""}`}function Va(e){return e?`${g(e)}`:""}const od=[{id:"",label:"Alle"},{id:"coder",label:"Coder"},{id:"scout",label:"Scout"},{id:"vision",label:"Vision"},{id:"manager",label:"Manager"},{id:"reviewer",label:"Reviewer"}];let nn=null,sn=[],Ae=null,Dt="",Mn=[],Wa=null,cn=[];const dd={perfect:0,marginal:1,too_tight:2},Sn=e=>e==="perfect"?"ok":e==="marginal"?"warn":"bad",cd=e=>e==="perfect"?"Passt locker":e==="marginal"?"Wird knapp":"Zu groß";function ud(){const e=l(".view[data-view='cookbook']");e.innerHTML=` -
    -

    Modell-Cookbook

    -
    Wofür willst du es nutzen? Wir empfehlen das passende Setup für deine Hardware.
    -
    - -

    Wofür möchtest du es nutzen?

    - deine Hardware -
    -
    -
    Lade Setups…
    -
    - -

    Aktuell beste Modelle für dein System

    - -
    -
    Automatisch aus vertrauenswürdigen Quellen (HuggingFace) — laufend aktuell, gefiltert auf das, was auf deine Hardware passt.
    -
    Lade aktuelle Empfehlungen…
    - -
    - Profi-Modus: HuggingFace direkt durchsuchen -
    -
    - - -
    -
    -
    - -
    -
    - passt locker - läuft, aber knapp - zu groß für deinen Speicher -
    -
    Es werden nur Modelle im GGUF-Format ${Jn(Ua)} gezeigt — nur die laufen lokal.
    -
    -
    -
    - - - - - - `,Ja(),l("#cb-btn-search").addEventListener("click",Rn),l("#cb-search").addEventListener("keydown",t=>{t.key==="Enter"&&Rn()}),l("#cb-sort").addEventListener("change",()=>{(l("#cb-search").value.trim()||Dt)&&Rn()}),l("#cb-modal-close").addEventListener("click",()=>l("#cb-modal").style.display="none"),l("#cb-modal").addEventListener("click",t=>{t.target.id==="cb-modal"&&(l("#cb-modal").style.display="none")}),l("#cb-m-download").addEventListener("click",Td),l("#cb-m-files").addEventListener("change",Sd),l("#cb-m-ctx").addEventListener("change",Cs),window.cbSetCtx=t=>{l("#cb-m-ctx").value=t,Cs()},l("#cb-r-close").addEventListener("click",()=>l("#cb-recipe-modal").style.display="none"),l("#cb-recipe-modal").addEventListener("click",t=>{t.target.id==="cb-recipe-modal"&&(l("#cb-recipe-modal").style.display="none")}),l("#cb-new-open").addEventListener("click",gd),l("#cb-new-close").addEventListener("click",()=>l("#cb-new-modal").style.display="none"),l("#cb-new-modal").addEventListener("click",t=>{t.target.id==="cb-new-modal"&&(l("#cb-new-modal").style.display="none")}),l("#cb-new-add").addEventListener("click",()=>l("#cb-new-models").insertAdjacentHTML("beforeend",Xi())),l("#cb-new-models").addEventListener("click",t=>{const n=t.target.closest(".cb-nm-del");n&&n.closest(".cb-nm-row").remove()}),l("#cb-new-save").addEventListener("click",wd),l("#cb-disc-refresh").addEventListener("click",()=>Es(!0)),l("#cb-discover").addEventListener("click",t=>{const n=t.target.closest("[data-inst]");n&&vd(n.getAttribute("data-inst"),n.getAttribute("data-role"),n.getAttribute("data-pb"),n)}),Qa(),Zi(),Es()}function fd(e){const t=Date.now()/1e3-e;return t<90?"gerade eben":t<3600?`vor ${Math.round(t/60)} min`:t<86400?`vor ${Math.round(t/3600)} h`:`vor ${Math.round(t/86400)} Tg.`}async function Es(e=!1){const t=l("#cb-discover"),n=l("#cb-disc-when"),i=l("#cb-disc-refresh");e&&(i.disabled=!0,i.textContent="Suche…",t.innerHTML='
    Frage Quellen ab…
    ');try{const s=await $("/api/cookbook/discover"+(e?"?force=true":""));n&&(n.textContent=s.updated?`aktualisiert ${fd(s.updated)}`:""),pd(s)}catch(s){t.innerHTML=`
    Empfehlungen nicht ladbar: ${g(s.message)}
    `}i.disabled=!1,i.textContent="Aktualisieren"}function pd(e){const t=l("#cb-discover"),n=e.categories||[];if(!n.length){t.innerHTML='
    Keine passenden Modelle gefunden.
    ';return}t.innerHTML=n.map(i=>` -
    -
    ${nt(i.icon)} -

    ${g(i.title)}

    -
    ${i.models.map(s=>hd(s,i.recommended)).join("")}
    -
    `).join("")}function hd(e,t){const n=(e.requirements||[]).map(o=>`
    ⚠ ${g(o)}
    `).join(""),i=t&&e.repo===t,s=i?'
    ★ Beste Wahl für dein System
    ':"",a=`
    ${Va(sd[e.role]||ja(e.repo))}${Yi(e.repo)}
    `,r=e.installed?`✓ installiert`:``;return`
    - ${s} -
    -

    ${g(e.name)}

    -
    ${g(e.author)} · ${e.params_b}B
    - ${g(e.fit.text)} -
    - ${a} -
    ${Ya(e.fit)} · ⬇ ${(e.downloads||0).toLocaleString()}
    - ${n} -
    -
    ${r}
    -
    `}async function vd(e,t,n,i){var s;i.disabled=!0,i.textContent="Starte…";try{await $("/api/cookbook/install-model",{method:"POST",body:JSON.stringify({repo:e,role:t,params_b:parseFloat(n)||7,quant:"Q4_K_M",hf_token:_n()})}),w("Download gestartet — siehe Aktivität."),(s=document.querySelector(".nav-item[data-view='activity']"))==null||s.click()}catch(a){w("Fehler: "+a.message,!0),i.disabled=!1,i.textContent="Installieren"}}async function Zi(){try{const e=await $("/api/cookbook/recipes");Mn=e.recipes||[],Wa=e.recommended_id||null,md()}catch(e){l("#cb-recipes").innerHTML=`
    Setups nicht ladbar: ${g(e.message)}
    `}}function md(){l("#cb-recipes").innerHTML=Mn.map(e=>{const t=e.id===Wa,n=t?'
    ★ Beste Wahl für dein System
    ':e.user?'
    Dein Setup
    ':"",i=e.user?` - - ×`:"";return``}).join(""),l("#cb-recipes").querySelectorAll("[data-recipe]").forEach(e=>e.addEventListener("click",()=>xd(e.getAttribute("data-recipe")))),l("#cb-recipes").querySelectorAll("[data-delr]").forEach(e=>e.addEventListener("click",t=>{t.stopPropagation(),yd(e.getAttribute("data-delr"))})),l("#cb-recipes").querySelectorAll("[data-editr]").forEach(e=>e.addEventListener("click",t=>{t.stopPropagation(),bd(e.getAttribute("data-editr"))}))}function Xi(){return`
    - - - - -
    `}let Ct=null;function gd(){Ct=null,l("#cb-new-title").value="",l("#cb-new-desc").value="",l("#cb-new-models").innerHTML=Xi(),l("#cb-new-modal-title").textContent="Eigenes Setup erstellen",l("#cb-new-save").textContent="Setup speichern",l("#cb-new-modal").style.display="flex"}function bd(e){const t=Mn.find(n=>n.id===e);t&&(Ct=e,l("#cb-new-title").value=t.title,l("#cb-new-desc").value=t.desc||"",l("#cb-new-models").innerHTML=t.models.map(n=>{const i=document.createElement("div");i.innerHTML=Xi();const s=i.firstElementChild;s.querySelector(".cb-nm-repo").value=n.repo,s.querySelector(".cb-nm-role").value=n.role;const a=s.querySelector(".cb-nm-quant");return a&&[...a.options].forEach(r=>{r.value===n.quant&&(r.selected=!0)}),s.outerHTML}).join(""),l("#cb-new-modal-title").textContent="Setup bearbeiten",l("#cb-new-save").textContent="Änderungen speichern",l("#cb-new-modal").style.display="flex")}async function wd(){const e=l("#cb-new-title").value.trim();if(!e)return w("Bitte einen Titel angeben.",!0);const t=[...l("#cb-new-models").querySelectorAll(".cb-nm-row")].map(i=>({repo:i.querySelector(".cb-nm-repo").value.trim(),role:i.querySelector(".cb-nm-role").value.trim()||"modell",quant:i.querySelector(".cb-nm-quant").value})).filter(i=>i.repo);if(!t.length)return w("Mindestens ein Modell (Repo) angeben.",!0);const n=l("#cb-new-save");n.disabled=!0,n.textContent="Speichere…";try{const i=JSON.stringify({title:e,desc:l("#cb-new-desc").value.trim(),models:t});Ct?(await $("/api/cookbook/user-recipe/"+encodeURIComponent(Ct),{method:"PUT",body:i}),w("Setup aktualisiert.")):(await $("/api/cookbook/user-recipe",{method:"POST",body:i}),w("Setup gespeichert.")),Ct=null,l("#cb-new-modal").style.display="none",Zi()}catch(i){w("Fehler: "+i.message,!0)}n.disabled=!1,n.textContent=Ct?"Änderungen speichern":"Setup speichern"}async function yd(e){const t=Mn.find(n=>n.id===e);if(await Ee({title:`„${t?t.title:e}" löschen?`,body:"Dein eigenes Setup wird aus dem Cookbook entfernt. Bereits installierte Modelle bleiben unangetastet.",confirmLabel:"Löschen",danger:!0}))try{await $("/api/cookbook/user-recipe/"+encodeURIComponent(e),{method:"DELETE"}),w("Setup gelöscht."),Zi()}catch(n){w(n.message,!0)}}function xd(e){const t=Mn.find(s=>s.id===e);if(!t)return;l("#cb-r-title").textContent=t.title,l("#cb-r-desc").textContent=t.desc,l("#cb-r-models").innerHTML=`
    ${t.models.map(s=>` -
    -
    -
    - ${g(s.name)}${g(s.role)}${Yi(s.repo)}
    -
    ${g(s.why)}
    -
    ~${s.fit.req_gb.toFixed(1)} GB · ~${Math.round(s.fit.tps)} Tok/s · optimal ~${Math.round(s.optimal_ctx/1024)}k Kontext
    -
    - ${g(s.fit.text)} -
    `).join("")}
    `;const n=Math.max(...t.models.map(s=>s.fit.req_gb));l("#cb-r-note").innerHTML=`Größter Spitzenbedarf: ~${n.toFixed(1)} GB. Es läuft immer nur ein Modell gleichzeitig — das größte bestimmt, ob das Setup passt (kein Summieren).`;const i=l("#cb-r-install");i.className=t.fit_level==="too_tight"?"primary warn":"primary",i.textContent=t.fit_level==="too_tight"?"Trotzdem installieren (zu groß)":"Komplettes Setup installieren",i.onclick=()=>_d(t.id),l("#cb-recipe-modal").style.display="flex"}async function _d(e){var n;const t=l("#cb-r-install");t.disabled=!0,t.textContent="Starte…";try{const i=await $("/api/cookbook/install-recipe",{method:"POST",body:JSON.stringify({recipe_id:e,hf_token:_n()})});w(`Setup wird installiert (${i.count} Modelle) — siehe Aktivität.`),l("#cb-recipe-modal").style.display="none",(n=document.querySelector(".nav-item[data-view='activity']"))==null||n.click()}catch(i){w("Fehler: "+i.message,!0)}t.disabled=!1,t.textContent="Komplettes Setup installieren"}function Ja(){l("#cb-filters").innerHTML=od.map(e=>``).join(""),l("#cb-filters").querySelectorAll("[data-f]").forEach(e=>e.addEventListener("click",()=>{Dt=e.getAttribute("data-f"),Ja(),Rn()}))}function Qa(){var t;const e=l("#cb-hw");e&&(t=nn==null?void 0:nn.ram)!=null&&t.total&&(e.innerHTML=`${nt("cpu")}${Math.round(nn.ram.total/1024**3)} GB Speicher`)}function Ya(e){return`~${e.req_gb.toFixed(1)} GB · ~${Math.round(e.tps)} Tok/s`}function kd(e){e=e.trim();const t=e.match(/huggingface\.co\/([^\s/]+\/[^\s/?#]+)/i);return t?t[1]:/^[\w.-]+\/[\w.-]+$/.test(e)?e:null}async function Rn(){var s;const e=l("#cb-search").value.trim(),t=kd(e);let n=e;if(Dt&&!t&&(n=n?n+" "+Dt:Dt),!n&&!t){l("#cb-grid").innerHTML="";return}const i=l("#cb-btn-search");i.disabled=!0,i.textContent="Lade…",l("#cb-grid").innerHTML='
    Suche auf HuggingFace…
    ';try{if(t)sn=[{id:t,author:t.split("/")[0],downloads:0}],Ts(sn);else{const a=((s=l("#cb-sort"))==null?void 0:s.value)||"downloads",r=`https://huggingface.co/api/models?search=${encodeURIComponent(n)}&filter=gguf&sort=${encodeURIComponent(a)}&direction=-1&limit=40`;sn=await(await fetch(r)).json(),Ts(sn)}}catch(a){l("#cb-grid").innerHTML=`
    ${g(a.message)}
    `}i.disabled=!1,i.textContent="Suchen"}function Ts(e){const t=l("#cb-grid");if(cn=[],!(e!=null&&e.length)){t.innerHTML='
    Keine GGUF-Modelle gefunden.
    ';return}t.innerHTML=e.map((n,i)=>` -
    -
    -

    ${g(n.id.split("/").pop())}

    -
    ${g(n.author||"")}
    - prüfe… -
    -
    ${Va(ja(n.id))}${Yi(n.id)}
    -
    -
    - Hardware-Fit… - ⬇ ${(n.downloads||0).toLocaleString()}
    -
    `).join(""),t.querySelectorAll("[data-res]").forEach(n=>n.addEventListener("click",()=>Ed(+n.getAttribute("data-res")))),e.forEach((n,i)=>Md(i,n.id))}async function Md(e,t){var n;try{const i=await $("/api/cookbook/analyze",{method:"POST",body:JSON.stringify({repo_id:t,ctx:8192})}),s=l("#cb-b-"+e),a=l("#cb-m-"+e);if(!s||!a)return;if(!((n=i.files)!=null&&n.length)){s.className="fit-badge",s.style.cssText="background:rgba(139,151,165,.14);color:var(--mut)",s.title=Ua,s.textContent="kein GGUF",a.textContent="—",cn[e]=null,bi();return}let r=i.files.find(o=>{var c;return(c=o.quant)==null?void 0:c.includes("Q4_K_M")})||i.files[0];s.className="fit-badge "+Sn(r.fit.level),s.textContent=r.fit.text,a.textContent=Ya(r.fit)+" · "+(r.quant||"GGUF"),cn[e]=r.fit.level,bi()}catch{const i=l("#cb-b-"+e);i&&(i.className="fit-badge bad",i.textContent="Fehler"),cn[e]=null,bi()}}function bi(){const e=l("#cb-grid");if(!e)return;let t=-1,n=99;if(cn.forEach((s,a)=>{if(s==null)return;const r=dd[s]??99;r{var a;s.classList.remove("res-best"),(a=s.querySelector(".cb-best-tag"))==null||a.remove()}),t<0||n>1)return;const i=e.querySelector(`[data-res="${t}"]`);i&&(i.classList.add("res-best"),i.insertAdjacentHTML("afterbegin",'
    ★ Beste Wahl für dein System
    '))}function es(){const e=l("#cb-m-files").value,t=Ae==null?void 0:Ae.files.find(a=>a.filename===e);if(!t){l("#cb-m-fit").style.display="none";return}l("#cb-m-fit").style.display="flex",l("#cb-m-fit-text").innerHTML=`Bedarf: ~${t.fit.req_gb.toFixed(1)} GB · ${Ae.params_b}B · ${g(t.quant)} · ~${Math.round(t.fit.tps)} Tok/s`,l("#cb-m-fit-badge").innerHTML=`${g(t.fit.text)}`;const n=t.optimal_ctx,i=l("#cb-m-ctx-rec");if(n){const a=l("#cb-m-ctx");a&&(a.value==="8192"||a.value==="")&&(a.value=n)}i&&(i.innerHTML=n?`Empfohlener Kontext für deine Hardware: ~${Math.round(n/1024)}kübernehmen`:"");const s=l("#cb-m-download");t.fit.level==="too_tight"?(s.className="primary warn",s.textContent="Trotzdem holen (zu groß)"):(s.className="primary",s.textContent="Herunterladen & Einpflegen")}function Sd(){es()}async function Cs(){if(!Ae)return;const e=parseInt(l("#cb-m-ctx").value)||8192,t=l("#cb-m-files").value;try{Ae=await $("/api/cookbook/analyze",{method:"POST",body:JSON.stringify({repo_id:Ae.repo,ctx:e})}),l("#cb-m-files").value=t,es()}catch{}}function $d(e,t){l("#cb-modal").style.display="flex",l("#cb-m-title").textContent=e,l("#cb-m-repo").textContent=t}async function Ed(e){var n;const t=sn[e];if(t){$d(t.id.split("/").pop(),t.id),l("#cb-m-alias").value="",l("#cb-m-files").style.display="none",l("#cb-m-loading").style.display="block",l("#cb-m-download").disabled=!0;try{const i=parseInt(l("#cb-m-ctx").value)||8192;if(Ae=await $("/api/cookbook/analyze",{method:"POST",body:JSON.stringify({repo_id:t.id,ctx:i})}),l("#cb-m-loading").style.display="none",l("#cb-m-files").style.display="block",!((n=Ae.files)!=null&&n.length)){l("#cb-m-files").innerHTML="",l("#cb-m-fit").style.display="none";return}l("#cb-m-files").innerHTML=Ae.files.map(s=>{const a=s.fit.level==="perfect"?"●":s.fit.level==="marginal"?"◐":"○";return``}).join(""),l("#cb-m-download").disabled=!1,es()}catch(i){l("#cb-m-loading").textContent="Fehler: "+i.message}}}async function Td(){var a;const e=l("#cb-m-repo").textContent,t=l("#cb-m-files").value,n=l("#cb-m-alias").value.trim().toLowerCase(),i=parseInt(l("#cb-m-ctx").value)||8192;if(!e||!t)return w("Bitte eine GGUF-Datei wählen.",!0);const s=l("#cb-m-download");s.disabled=!0,s.textContent="Starte…";try{const r=await $("/api/download",{method:"POST",body:JSON.stringify({repo:e,file:t,hf_token:_n()})});await $("/api/register",{method:"POST",body:JSON.stringify({role:n,model_path:r.expected_path,ctx:i})}),w("Download gestartet — siehe Aktivität."),l("#cb-modal").style.display="none",(a=document.querySelector(".nav-item[data-view='activity']"))==null||a.click()}catch(r){w("Fehler: "+r.message,!0)}s.disabled=!1,s.textContent="Herunterladen & Einpflegen"}function Cd(e){nn=e,Qa()}const Ld={id:"cookbook",mount:ud,onSystem:Cd};let Fn=null,Ls="",Lt="zed",Se=[];window.mcCopyCode=e=>{var i;const t=e.parentElement.querySelector("code,pre");(i=navigator.clipboard)==null||i.writeText((t==null?void 0:t.textContent)||"");const n=e.textContent;e.textContent="Kopiert!",setTimeout(()=>e.textContent=n,1400)};window.mcCopyVal=e=>{var i;const t=e.previousElementSibling;(i=navigator.clipboard)==null||i.writeText(t.value);const n=e.textContent;e.textContent="Kopiert!",setTimeout(()=>e.textContent=n,1400)};window.cnGo=e=>{var t;return(t=document.querySelector(`.nav-item[data-view="${e}"]`))==null?void 0:t.click()};window.cnSelectTool=e=>{Lt=e,ts()};window.cnToggleModel=e=>{Se.includes(e)?Se=Se.filter(t=>t!==e):Se.push(e),ts()};function Ad(){return`http://${location.hostname}:8080/v1`}function zd(e){return/^\d{1,3}(\.\d{1,3}){3}$/.test(e)}function Dd(){return location.protocol==="https:"||!zd(location.hostname)}function Xe(){return((Fn==null?void 0:Fn.models)||[]).filter(e=>!e.incomplete)}function Pd(){return Se.length?Se:Xe().map(e=>e.name)}function an(e,t){return` -
    - - -
    `}const Za=e=>`${nt("check")}${g(e)}`;function Od(e,t){const i=(t.length?Xe().filter(s=>t.includes(s.name)):Xe().length?Xe():[{name:"coder",meta:{}}]).map(s=>{var o;const r=(((o=s.meta)==null?void 0:o.caps)||[]).some(c=>/Bild|Vision/i.test(c))?', "capabilities": { "tools": true, "images": true }':"";return` { "name": "${s.name}", "display_name": "${s.name}", "max_tokens": 32768${r} }`}).join(`, -`);return`"language_models": { - "openai_compatible": { - "bosgame": { - "api_url": "${e}", - "available_models": [ -${i} - ] - } - } -}`}function Bd(e,t){const i=(t.length?t:Xe().length?Xe().map(s=>s.name):["coder"]).map(s=>` "${s}": { "name": "${s}" }`).join(`, -`);return`"provider": { - "llama-swap": { - "npm": "@ai-sdk/openai-compatible", - "name": "Bosgame (llama-swap)", - "options": { - "baseURL": "${e}", - "apiKey": "local" - }, - "models": { -${i} - } - } -}`}function Hd(e){return`
    -

    1 · Verbindung prüfen

    -
    Stelle sicher, dass die LLM-Engine erreichbar ist, bevor du Tools konfigurierst.
    - ${an("Engine-Adresse (für alle Tools)",e)} - ${Dd()?`
    - Du öffnest Mission Control über einen Proxy/Namen (HTTPS o.ä.). Die Engine läuft auf Port 8080 ohne HTTPS direkt am Bosgame. - Trag in deinen Tools die LAN-IP ein: http://192.168.178.151:8080/v1 — nicht den Proxy-Namen.
    `:""} -
    - - -
    -
    `}function Rd(){return`
    -

    Wie funktioniert das Auto-Swap?

    -
    -
    - -
    -
    Kein manuelles Starten nötig
    -
    Wenn du in Zed oder OpenCode „coder" auswählst und eine Anfrage sendest, - lädt llama-swap das Modell automatisch — in ca. 2–5 Sekunden. Du siehst es hier im Dashboard unter „Aktives Modell".
    -
    -
    -
    - 🔄 -
    -
    Modelle wechseln sich automatisch ab
    -
    Rufst du ein anderes Modell auf, entlädt llama-swap das aktuelle (nach TTL-Ablauf) - und lädt das neue. Es läuft immer nur eines gleichzeitig — llama-swap verwaltet das komplett selbst.
    -
    -
    -
    - 🧠 -
    -
    Zwei Modelle gleichzeitig aktiv halten
    -
    Du hast 124 GB RAM — genug für z.B. Scout 8B (~5 GB) dauerhaft geladen + ein - größeres Modell das swappt. Dafür beim Scout im Modelle-Tab → Konfigurieren die TTL auf 99999 setzen - (= nie automatisch entladen). Das kleine Modell beantwortet schnelle Fragen sofort, ohne jede Wartezeit.
    -
    -
    -
    -
    `}function Fd(){const e=Xe();if(!e.length)return"";const t=e.map(i=>`${g(i.name)}`).join(" ");return`
    -

    2 · Modelle auswählen

    ${Se.length?`${Se.length} von ${e.length} ausgewählt`:"alle werden eingeschlossen"}
    -
    Wähle, welche Modelle im Config-Snippet erscheinen sollen. Standardmäßig alle.
    -
    ${t}
    - ${Se.length?'':""} -
    `}window.cnClearModels=()=>{Se=[],ts()};function zn(e,t,n){return``}function Nd(){return`
    -

    3 · Tool wählen

    -
    Für welches Werkzeug brauchst du die Konfiguration?
    -
    - ${zn("zed","Zed","Schneller nativer Editor — Empfohlen")} - ${zn("opencode","OpenCode","Open-Source Coding-Agent (Terminal/Desktop)")} - ${zn("cline","Cline / Cursor","VS Code Extension, Bring-Your-Own-Key")} - ${zn("openwebui","OpenWebUI","Browser-Chat-Interface")} -
    -
    `}function Id(e){const t=Pd(),n=t[0]||"coder";let i="";if(Lt==="zed"){const s=Od(e,t);i=` -

    Öffne die Zed-Settings mit Cmd/Strg + , und füge diesen Block in deine bestehende - settings.json ein. Falls bereits ein "bosgame"-Eintrag existiert: vorher löschen.

    -
    - Nur diesen Block einfügen, nicht die ganze Datei ersetzen — sonst gehen andere Einstellungen verloren. -
    -
    - -
    ${g(s)}
    -
    -

    API-Key: Öffne den Agent-Panel → Anbieter bosgame → trag irgendeinen Wert ein (z.B. local). - Alternativ BOSGAME_API_KEY=local als Umgebungsvariable.

    -
    Danach im Agent-Panel das Modell bosgame / ${g(n)} auswählen. Das Auto-Swap lädt es beim ersten Request.
    `}else if(Lt==="opencode"){const s=Bd(e,t);i=` -

    Datei anlegen oder öffnen:

    -

    Windows: %USERPROFILE%\\.config\\opencode\\opencode.json
    - Linux/Mac: ~/.config/opencode/opencode.json
    - Oder im Projektordner: opencode.json

    -
    - Nur den "provider"-Block einfügen oder mergen. - Falls bereits ein "llama-swap"-Eintrag vorhanden ist: vorher löschen, sonst erscheinen Modelle doppelt. -
    -
    - -
    ${g(s)}
    -
    -

    Danach in OpenCode /modelsllama-swap/${g(n)} wählen.

    -
    Statt alles zu ersetzen: nur den provider-Block in deine bestehende Config einfügen — so bleiben andere Anbieter erhalten.
    `}else Lt==="cline"?i=` -

    In Cline/Cursor den Provider „OpenAI Compatible" wählen und folgende Felder eintragen:

    - ${an("Basis-URL (OpenAI Compatible)",e)} - ${an("Modell-ID",n)} -
    API-Key: ein beliebiger Wert (z.B. local) — llama-swap prüft ihn nicht.
    `:Lt==="openwebui"&&(i=` -

    Settings → Admin Panel → Connections → neue OpenAI-Verbindung:

    - ${an("Basis-URL",e)} - ${an("API-Key","dummy-key")} -
    Nach dem Speichern erscheinen die Modelle automatisch in der Modell-Auswahl.
    `);return`
    -

    4 · Config-Snippet

    - ${i} -
    `}function Gd(e){var i;const t=(i=Xe().find(s=>{var a,r;return(r=(a=s.meta)==null?void 0:a.caps)==null?void 0:r.includes("Bild")}))==null?void 0:i.name;if(!t)return"";const n=`curl ${e}/chat/completions \\ - -H "Content-Type: application/json" \\ - -d '{ - "model": "${t}", - "messages": [{"role":"user","content":[ - {"type":"text","text":"Was ist auf dem Bild falsch?"}, - {"type":"image_url","image_url":{"url":"data:image/png;base64,"}} - ]}] -}'`;return`
    -

    Bilder einbinden (Vision)

    ${Za(`„${t}" bereit`)}
    -
    Mit Vision-Modellen kannst du Screenshots oder Fehlerbilder direkt mitschicken.
    -
    - -
    ${g(n)}
    -
    -
    `}function un(){var n;const e=l(".view[data-view='connect']");if(!e)return;const t=Ad();e.innerHTML=` -
    -

    Verbinden

    -
    Schritt für Schritt: KI-Modelle in deine Tools einbinden — getestet, erklärt, mit Copy-Paste-Config.
    -
    - - ${Hd(t)} - ${Rd()} - ${Fd()} - ${Nd()} - ${Id(t)} - ${Gd(t)}`,(n=l("#cn-test"))==null||n.addEventListener("click",Kd)}function ts(){if(!l(".view[data-view='connect'] .card:nth-of-type(3)")){un();return}const t=l(".view[data-view='connect']"),n=t==null?void 0:t.querySelectorAll(".card");if(!n||n.length<3){un();return}un()}async function Kd(){const e=l("#cn-test-result");e.innerHTML='Teste…';try{const t=await $("/api/integration/test");e.innerHTML=t.ok?Za(`Verbunden — ${t.models.length} Modell(e): ${t.models.join(", ")}`):`✗ Keine Verbindung: ${g(t.error)}`}catch(t){e.innerHTML=`✗ ${g(t.message)}`}}function qd(){un()}function Ud(e){Fn=e;const t=((e==null?void 0:e.models)||[]).map(n=>n.name).join(",")+"|"+!!e;t!==Ls&&(Ls=t,un())}const jd={id:"connect",mount:qd,onStatus:Ud},ni=[ho,zo,td,qo,Ld,jd];let As={};function Vd(){const e=document.getElementById("top-active-text");e&&(e.classList.add("swap-flash"),setTimeout(()=>e.classList.remove("swap-flash"),900))}function zs(e){var s;const t=document.getElementById("swdot"),n=document.getElementById("swlabel"),i=document.getElementById("top-active-text");if(!e)t&&(t.className="dot off"),n&&(n.textContent="Backend nicht erreichbar"),i&&(i.textContent="Backend offline"),Ds("Backend nicht erreichbar – läuft uvicorn?",!1);else{const a=(e.swap_url||"").replace(/^https?:\/\//,"");t&&(t.className="dot "+(e.swap_ok?"on":"off")),n&&(n.textContent=(e.swap_ok?"LLM-Engine: Online – ":"LLM-Engine: Offline – ")+a);const r=(e.models||[]).find(c=>c.state==="running");i&&(i.innerHTML=r?`Geladen: ${r.name}`:"Kein Modell im VRAM"),e.swap_ok?Yd():Ds(`LLM-Engine nicht erreichbar unter ${a} – läuft der llama-swap Dienst?`,!0);const o={};for(const c of e.models||[])o[c.name]=c.state;for(const[c,u]of Object.entries(o)){const p=As[c];if(p&&p!==u&&(u==="running"||u==="loading")){Vd();break}}As=o}Wd(e),Rl.set(e);for(const a of ni)(s=a.onStatus)==null||s.call(a,e)}function Wd(e){const t=document.getElementById("sec-chip"),n=document.getElementById("sec-chip-tx");!t||!n||(t.classList.toggle("ok",!!(e!=null&&e.secured)),n.textContent=e!=null&&e.secured?"Geschützt · Heimnetz":"Nur im Heimnetz (offen)")}function Jd(e){var t;Fl.set(e||[]);for(const n of ni)(t=n.onJobs)==null||t.call(n,e)}function Qd(e){var t;for(const n of ni)(t=n.onSystem)==null||t.call(n,e)}function Ds(e,t){const n=document.getElementById("alert");n.className="alert"+(t?" warn":""),n.innerHTML=`${e}`,n.style.display="flex"}function Yd(){const e=document.getElementById("alert");e&&(e.style.display="none")}const Zd=e=>{var t;return(t=document.querySelector(`.nav-item[data-view="${e}"]`))==null?void 0:t.click()};function wi(e,t,n,i){const s=document.createElement("span");return s.className="upd-badge"+(t?" "+t:""),s.textContent=e,i&&(s.title=i),s.onclick=()=>Zd(n),s}async function Xa(){try{const e=await $("/api/updates"),t=document.getElementById("update-badges");if(!t)return;const n=e.apt_cache_age_h,i=n!=null&&n>24,s=n!=null?` · apt-Cache: vor ${n}h`:"";t.innerHTML="",t.appendChild(wi(e.os>0?`OS: ${e.os} Updates`:i?"OS: ?":"OS: aktuell",e.os>0||i?"warn":"ok","server",e.os>0?`${e.os} ausstehende Pakete${s}`:i?`apt-Cache veraltet (${n}h)`:`Keine Updates${s}`)),t.appendChild(wi(e.engine>0?"Engine: Update":"Engine: aktuell",e.engine>0?"warn":"ok","server")),t.appendChild(wi(e.models>0?`Modelle: ${e.models}`:"Modelle: aktuell",e.models>0?"warn":"ok","news"))}catch{}}async function ii(){try{zs(await $("/api/status"))}catch{zs(null)}}async function er(){try{Jd(await $("/api/jobs"))}catch{}}function tr(){const e=window.location.protocol==="https:"?"wss:":"ws:",t=new WebSocket(`${e}//${window.location.host}/api/system/stream`);t.onmessage=n=>{try{Qd(JSON.parse(n.data))}catch{}},t.onclose=()=>{setTimeout(tr,3e3)},t.onerror=()=>t.close()}var Ns;for(const e of ni)(Ns=e.mount)==null||Ns.call(e);const Ps=document.querySelector('.view[data-view="guides"]');Ps&&za(jl,{target:Ps});const Os=document.querySelector('.view[data-view="news"]');Os&&za(no,{target:Os});Hl("overview");const yi=document.getElementById("token");yi&&(yi.value=Da(),yi.addEventListener("change",e=>{Dl(e.target.value),ii()}));const xi=document.getElementById("hf-token");xi&&(xi.value=_n(),xi.addEventListener("change",e=>Ol(e.target.value)));const Bs=document.getElementById("nav-settings"),_i=document.getElementById("settings-modal"),Hs=document.getElementById("sm-close");Bs&&_i&&Hs&&(Bs.addEventListener("click",()=>_i.style.display="flex"),Hs.addEventListener("click",()=>_i.style.display="none"));function Xd(){const e=document.getElementById("clock");e&&(e.textContent=new Date().toTimeString().slice(0,5))}document.addEventListener("mc:refresh",ii);ii();er();tr();setInterval(Xd,1e3);setInterval(ii,3e3);setInterval(er,1500);Xa();setInterval(Xa,6e4); + (scout) für einfache — du wählst es einfach im Tool aus.

    `]],qc=[["LLM / Modell","Die KI selbst — versteht und schreibt Text (wie ChatGPT, nur lokal auf deinem PC). Ein Modell ist eine konkrete Datei davon, oft auf etwas spezialisiert (Code, Bilder, Chat)."],["GGUF","Das lokal lauffähige Dateiformat. Unsere Engine lädt nur GGUF — Repos ohne GGUF kann sie nicht nutzen."],["Quantisierung (Q4_K_M)","Verkleinert ein Modell (16-Bit → 4-Bit), damit es in den Speicher passt — bei kaum Qualitätsverlust. Q4_K_M ist der beliebte Sweetspot."],["Kontextfenster","Das Kurzzeitgedächtnis: wie viel Text sich das Modell gleichzeitig merkt. Größer = mehr Überblick, mehr Speicher, etwas langsamer."],["Auto-Swap","llama-swap lädt/entlädt Modelle automatisch — immer nur eins gleichzeitig. Du managst nichts."],["Agent","Ein Programm (OpenCode, Cline, Continue), das das Modell handeln lässt — Dateien lesen/ändern, Befehle ausführen, planen, im Loop bis die Aufgabe erledigt ist."],["MCP","Model Context Protocol — der Anschluss, über den ein Agent zusätzliche Werkzeuge bekommt (Web, Dateien, Datenbank). MCP = Werkzeuge."],["Skills","Verpackte Anleitung für wiederkehrende Aufgaben — der Agent liest sie und macht es jedes Mal wie du willst. Skills = Anleitungen."],["Rules / Commands","Die konkrete Form von Skills je Tool: AGENTS.md (OpenCode), .clinerules (Cline), Rules/Prompts (Continue)."]];var Nc=M('
    '),Ic=M('

    '),Fc=M('

    Guide für Einsteiger

    Praktische Workflows + die Local-LLM-Welt in Klartext — ohne Vorwissen.

    Tutorials & Workflows

    Konzepte kurz erklärt

    ',1);function Kc(e){var t=Fc(),a=l(ft(t),4);Ue(a,21,()=>Bc,Ge,(i,o,d)=>{var c=N(()=>Qa(n(o),2));let u=()=>n(c)[0],f=()=>n(c)[1];var g=Nc();g.open=d===0;var $=s(g),h=s($),b=l($,2);Qe(b,f,!0),z(()=>m(h,u())),x(i,g)});var r=l(a,4);Ue(r,21,()=>qc,Ge,(i,o)=>{var d=N(()=>Qa(n(o),2));let c=()=>n(d)[0],u=()=>n(d)[1];var f=Ic(),g=s(f),$=s(g),h=l(g,2),b=s(h);z(()=>{m($,c()),m(b,u())}),x(i,f)}),x(e,t)}let Hi={};function Gc(){const e=document.getElementById("top-active-text");e&&(e.classList.add("swap-flash"),setTimeout(()=>e.classList.remove("swap-flash"),900))}function Vi(e){const t=document.getElementById("swdot"),a=document.getElementById("swlabel"),r=document.getElementById("top-active-text");if(!e)t&&(t.className="dot off"),a&&(a.textContent="Backend nicht erreichbar"),r&&(r.textContent="Backend offline"),Wi("Backend nicht erreichbar – läuft uvicorn?",!1);else{const i=(e.swap_url||"").replace(/^https?:\/\//,"");t&&(t.className="dot "+(e.swap_ok?"on":"off")),a&&(a.textContent=(e.swap_ok?"LLM-Engine: Online – ":"LLM-Engine: Offline – ")+i);const o=(e.models||[]).find(c=>c.state==="running");r&&(r.innerHTML=o?`Geladen: ${o.name}`:"Kein Modell im VRAM"),e.swap_ok?Wc():Wi(`LLM-Engine nicht erreichbar unter ${i} – läuft der llama-swap Dienst?`,!0);const d={};for(const c of e.models||[])d[c.name]=c.state;for(const[c,u]of Object.entries(d)){const f=Hi[c];if(f&&f!==u&&(u==="running"||u==="loading")){Gc();break}}Hi=d}Uc(e),dr.set(e)}function Uc(e){const t=document.getElementById("sec-chip"),a=document.getElementById("sec-chip-tx");!t||!a||(t.classList.toggle("ok",!!(e!=null&&e.secured)),a.textContent=e!=null&&e.secured?"Geschützt · Heimnetz":"Nur im Heimnetz (offen)")}function Hc(e){ra.set(e||[])}function Vc(e){yi.set(e)}function Wi(e,t){const a=document.getElementById("alert");a.className="alert"+(t?" warn":""),a.innerHTML=`${e}`,a.style.display="flex"}function Wc(){const e=document.getElementById("alert");e&&(e.style.display="none")}const jc=e=>{var t;return(t=document.querySelector(`.nav-item[data-view="${e}"]`))==null?void 0:t.click()};function Ur(e,t,a,r){const i=document.createElement("span");return i.className="upd-badge"+(t?" "+t:""),i.textContent=e,r&&(i.title=r),i.onclick=()=>jc(a),i}async function js(){try{const e=await fe("/api/updates"),t=document.getElementById("update-badges");if(!t)return;const a=e.apt_cache_age_h,r=a!=null&&a>24,i=a!=null?` · apt-Cache: vor ${a}h`:"";t.innerHTML="",t.appendChild(Ur(e.os>0?`OS: ${e.os} Updates`:r?"OS: ?":"OS: aktuell",e.os>0||r?"warn":"ok","server",e.os>0?`${e.os} Pakete${i}`:r?`apt-Cache veraltet (${a}h)`:`Keine Updates${i}`)),t.appendChild(Ur(e.engine>0?"Engine: Update":"Engine: aktuell",e.engine>0?"warn":"ok","server")),t.appendChild(Ur(e.models>0?`Modelle: ${e.models}`:"Modelle: aktuell",e.models>0?"warn":"ok","news"))}catch{}}async function zr(){try{Vi(await fe("/api/status"))}catch{Vi(null)}}async function Js(){try{Hc(await fe("/api/jobs"))}catch{}}function Qs(){const e=window.location.protocol==="https:"?"wss:":"ws:",t=new WebSocket(`${e}//${window.location.host}/api/system/stream`);t.onmessage=a=>{try{Vc(JSON.parse(a.data))}catch{}},t.onclose=()=>{setTimeout(Qs,3e3)},t.onerror=()=>t.close()}var Xi;((Xi=document.getElementById("logo"))==null?void 0:Xi.innerHTML)!==void 0&&(document.getElementById("logo").innerHTML=Pr.logo);document.querySelectorAll("[data-ic]").forEach(e=>{e.innerHTML=Pr[e.dataset.ic||""]||""});const Jc=[["overview",ed],["models",hd],["activity",Ed],["server",Rd],["cookbook",vc],["connect",Ec],["news",Rc],["guides",Kc]];for(const[e,t]of Jc){const a=document.querySelector(`.view[data-view="${e}"]`);a&&wo(t,{target:a})}Fo("overview");const Hr=document.getElementById("token");Hr&&(Hr.value=Ws(),Hr.addEventListener("change",e=>{Ro(e.target.value),zr()}));const Vr=document.getElementById("hf-token");Vr&&(Vr.value=Ja(),Vr.addEventListener("change",e=>qo(e.target.value)));const ji=document.getElementById("nav-settings"),Wr=document.getElementById("settings-modal"),Ji=document.getElementById("sm-close");ji&&Wr&&Ji&&(ji.addEventListener("click",()=>Wr.style.display="flex"),Ji.addEventListener("click",()=>Wr.style.display="none"));function Qc(){const e=document.getElementById("clock");e&&(e.textContent=new Date().toTimeString().slice(0,5))}document.addEventListener("mc:refresh",zr);zr();Js();Qs();setInterval(Qc,1e3);setInterval(zr,3e3);setInterval(Js,1500);js();setInterval(js,6e4); diff --git a/static/index.html b/static/index.html index 11ae286..a09b333 100644 --- a/static/index.html +++ b/static/index.html @@ -11,7 +11,6 @@
    -
    -
    verbinde… @@ -39,63 +37,21 @@ Nur im Heimnetz
    - -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - - - - - - +
    + + + + + + +
    -
    - - - diff --git a/wird b/wird new file mode 100644 index 0000000..e69de29