diff --git a/static/css/components.css b/static/css/components.css index da6f5b4..7584a16 100644 --- a/static/css/components.css +++ b/static/css/components.css @@ -186,6 +186,28 @@ button:disabled{opacity:.5;cursor:not-allowed} .modal-card p{margin:0 0 18px;color:var(--mut);font-size:13.5px;line-height:1.55} .modal-actions{display:flex;gap:10px;justify-content:flex-end} +/* ---- News (Magazin) ---- */ +.news-grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fill,minmax(280px,1fr))} +.news-card{display:flex;flex-direction:column;gap:10px;background:var(--panel);border:1px solid var(--line); + border-radius:var(--radius);padding:15px 17px;text-decoration:none;color:inherit;transition:.15s} +.news-card:hover{border-color:var(--accent);background:var(--bg2)} +.news-card.featured{grid-column:span 2} +.news-meta{display:flex;align-items:center;gap:8px;font-size:11.5px} +.news-src{color:var(--accent);font-weight:500} +.news-rel{color:#7ee29a;background:rgba(63,185,80,.13);border-radius:5px;padding:1px 7px} +.news-date{margin-left:auto;color:var(--dim);font-family:var(--mono)} +.news-title{font-size:14px;line-height:1.45;color:var(--tx)} +.news-card.featured .news-title{font-size:18px;font-weight:500;line-height:1.35} +@media(max-width:620px){.news-card.featured{grid-column:span 1}} + +/* ---- Guide (Konzept-Karten) ---- */ +.concept-grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fill,minmax(260px,1fr))} +.concept{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:15px 17px} +.concept h4{margin:0 0 6px;font-size:14px;font-weight:500;color:var(--tx)} +.concept p{margin:0;font-size:13px;color:var(--mut);line-height:1.55} +.tut-steps{padding-left:20px;line-height:1.85;margin:6px 0 0} +.tut-steps li{margin-bottom:4px} + /* ---- Toast ---- */ .toast{position:fixed;bottom:22px;left:50%;transform:translateX(-50%);background:var(--panel2); border:1px solid var(--line2);border-radius:10px;padding:11px 16px;font-size:13.5px; diff --git a/static/js/panels/guides.js b/static/js/panels/guides.js index 378042b..62b2d7b 100644 --- a/static/js/panels/guides.js +++ b/static/js/panels/guides.js @@ -1,49 +1,57 @@ -// guides.js — Anfänger-Guide (v5): erklärt die Local-LLM-Welt idiotensicher. -// Reine Erklär-Seite. Tool-Einrichtung/Configs liegen im Tab „Verbinden". +// guides.js — Anfänger-Guide (v6): praktische Tutorials/Workflows + kompakte Konzepte. import { $ } from "../core/ui.js"; -const SECTIONS = [ - ["Erste Schritte — in 3 Schritten loslegen", ` -
    -
  1. Modell holen: Im Cookbook auf „Wofür möchtest du es nutzen?" deinen Fall wählen - (z.B. „Coden") → „Komplettes Setup installieren". Der Download läuft im Tab Aktivität.
  2. -
  3. Verbinden: Im Tab Verbinden die Basis-URL kopieren und in deinem Tool (z.B. OpenCode) - als „OpenAI-kompatibel" eintragen. Mit „Verbindung testen" prüfen.
  4. -
  5. Loslegen: Im Tool das Modell (z.B. coder) auswählen und arbeiten. Die Engine - lädt das passende Modell automatisch.
  6. +const TUTORIALS = [ + ["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. +
    3. Verbinden: Tab Verbinden → Basis-URL kopieren → in deinem Tool (z.B. OpenCode) + als „OpenAI-kompatibel" einfügen. Verbindung testen bestätigt, dass es läuft.
    4. +
    5. Arbeiten: Im Tool das Modell (z.B. coder) wählen und loslegen. Das passende + Modell wird automatisch geladen — du musst nichts starten.
    `], - ["Was ist ein LLM bzw. ein Modell?", ` -

    Ein LLM (Large Language Model) ist ein KI-Sprachmodell — die „Intelligenz", die Text versteht - und schreibt (wie ChatGPT, nur lokal auf deinem Mini-PC, ohne Cloud). Ein Modell ist eine - konkrete Datei davon. Verschiedene Modelle sind unterschiedlich gut/schnell und auf Dinge spezialisiert - (Coden, Bilder, Chat).

    `], - ["Was ist GGUF?", ` -

    GGUF ist das Dateiformat, in dem lokale Modelle gespeichert werden — gemacht, damit sie - effizient auf normaler Hardware laufen. Unsere Engine (llama-swap/llama.cpp) lädt nur GGUF. - Steht im Cookbook „keine GGUF", hat dieses HuggingFace-Repo schlicht keine passende Datei — dann ein - anderes Repo nehmen (oft gibt's „…-GGUF"-Varianten).

    `], - ["Quantisierung (z.B. Q4_K_M)?", ` -

    Modelle sind riesig. Quantisierung verkleinert sie, indem die Zahlen gröber gespeichert werden - (16-Bit → 4-Bit) — sie passen dann in den Speicher und laufen schneller, bei kaum spürbarem - Qualitätsverlust. Q4_K_M ist der beliebte Sweetspot aus Größe und Qualität. Höhere Zahl (Q6, Q8) - = etwas besser, aber größer/langsamer.

    `], - ["Kontextfenster — das Gedächtnis", ` -

    Das Kontextfenster ist das Kurzzeitgedächtnis: wie viel Text (Tokens) sich das Modell - gleichzeitig merken kann. Größer = es überblickt längere Dateien/Gespräche, braucht aber - mehr Speicher und wird etwas langsamer. Fürs Coden (viele Dateien) hilft groß; für kurzen Chat - reicht klein. Mission Control rechnet dir das Optimum für deine Hardware aus.

    `], - ["Swapping — warum nur ein Modell läuft", ` -

    llama-swap lädt immer nur ein Modell gleichzeitig und tauscht es bei Bedarf automatisch aus. - Vorteil: Du kannst viele Modelle einrichten, ohne dass sie sich den Speicher teilen müssen. - Heißt auch: Der Speicherbedarf deines Setups = das größte Einzelmodell, nicht die Summe.

    `], - ["MCP, Skills & Agenten", ` -

    Agent: Ein Programm, das das Modell selbstständig Aufgaben erledigen lässt — Dateien - lesen/schreiben, Befehle ausführen, mehrere Schritte planen (z.B. OpenCode, Cline).

    -

    MCP (Model Context Protocol): Ein Standard, über den ein Agent dem Modell zusätzliche - Werkzeuge gibt — z.B. „durchsuche das Web", „lies meine Datenbank", „steuere Tool X". So wird aus - einem reinen Textmodell ein nützlicher Helfer mit Zugriff auf echte Tools.

    -

    Skills: Vorgefertigte Fähigkeiten/Anleitungen, die ein Agent für wiederkehrende Aufgaben nutzt.

    `], + ["Wie leere ich den Kontext? Machen die Tools das selbst?", ` +

    Kurz: meistens ja, automatisch. Der Kontext (das „Gedächtnis") gilt pro Unterhaltung. + Startest du im Tool einen neuen Chat / eine neue Session, ist der Kontext wieder leer — + das Tool verwaltet das Fenster für dich.

    +

    Du musst nur selten eingreifen: wird ein Gespräch sehr lang, fang einen neuen an. Wichtig: + „Grafikspeicher leeren" (Tab Server) ist etwas anderes — das wirft das Modell aus dem Speicher, + nicht den Gesprächsverlauf.

    `], + ["Brauche ich überhaupt einen Agenten?", ` +

    Kommt drauf an, was du willst:

    + +

    Das Gute: Diese Agenten laufen lokal auf deinem PC und reden mit deinem lokalen Modell. + Du brauchst keinen Cloud-Agenten — die „empfohlenen Tools" SIND die Agenten.

    `], + ["Auto-Swap — wie das Laden funktioniert", ` +

    llama-swap lädt automatisch das Modell, das gerade angefragt wird, und entlädt es nach einer + Leerlaufzeit wieder (oder wenn du ein anderes anfragst). Es läuft immer nur eines gleichzeitig.

    +

    Heißt für dich: Du richtest beliebig viele Modelle ein und wählst im Tool einfach das passende — + das Laden/Entladen passiert im Hintergrund, ohne dass du etwas tun musst.

    `], + ["Die empfohlenen Tools optimal nutzen", ` + +

    Tipp: starkes Modell (coder) für schwere Aufgaben, schnelles + (coder-fast) für simple — du wählst es einfach im Tool aus.

    `], +]; + +const CONCEPTS = [ + ["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), das die KI selbstständig Aufgaben erledigen lässt: Dateien lesen/schreiben, Befehle ausführen, planen."], + ["MCP", "Model Context Protocol — gibt einem Agenten zusätzliche Werkzeuge (Web durchsuchen, Dateien, Tools). Macht aus dem Textmodell einen echten Helfer."], + ["Skills", "Vorgefertigte Fähigkeiten/Anleitungen, die ein Agent für wiederkehrende Aufgaben nutzt."], ]; function mount() { @@ -51,13 +59,18 @@ function mount() { c.innerHTML = `

    Guide für Einsteiger

    -
    Die Local-LLM-Welt in Klartext — ohne Vorwissen verständlich.
    +
    Praktische Workflows + die Local-LLM-Welt in Klartext — ohne Vorwissen.
    + +

    Tutorials & Workflows

    - ${SECTIONS.map(([title, body], i) => ` -
    - ${title} -
    ${body}
    -
    `).join("")} + ${TUTORIALS.map(([t, b], i) => ` +
    ${t} +
    ${b}
    `).join("")} +
    + +

    Konzepte kurz erklärt

    +
    + ${CONCEPTS.map(([term, text]) => `

    ${term}

    ${text}

    `).join("")}
    `; } diff --git a/static/js/panels/news.js b/static/js/panels/news.js index 930fd50..f9d90aa 100644 --- a/static/js/panels/news.js +++ b/static/js/panels/news.js @@ -1,4 +1,4 @@ -// news.js — News-Board (v5): kuratierte News + „Für dich: Upgrades" (Modell-Tausch-Vorschläge). +// news.js — News-Board (v6): Magazin-Layout + „Für dich: Upgrades". import { api, getHfToken } from "../core/api.js"; import { $, esc, ago, toast } from "../core/ui.js"; @@ -10,13 +10,11 @@ function mount() { $(".view[data-view='news']").innerHTML = `

    News

    -
    Neuigkeiten rund um lokale LLMs — aus kuratierten Quellen (HuggingFace, r/LocalLLaMA, Latent Space, Ahead of AI …).
    +
    Aktuelles aus der Local-LLM-Welt — aus kuratierten Quellen (HuggingFace, r/LocalLLaMA, Latent Space, Ahead of AI …).
    -
    -
    Lade News…
    -
    `; +
    Lade News…
    `; $("#news-refresh").addEventListener("click", () => { load(); loadUpgrades(); }); load(); loadUpgrades(); @@ -34,21 +32,18 @@ function upRow(u, i) { `; } - async function loadUpgrades() { const el = $("#upgrades"); if (!el) return; try { - const d = await api("/api/cookbook/upgrades"); - UPS = d.upgrades || []; + UPS = (await api("/api/cookbook/upgrades")).upgrades || []; if (!UPS.length) { el.innerHTML = ""; return; } - el.innerHTML = `
    + el.innerHTML = `

    Für dich: Upgrades

    ${UPS.length}
    -
    Neuere Modelle, die ein installiertes ersetzen können — Begründung inklusive.
    +
    Neuere Modelle, die ein installiertes ersetzen können — mit Begründung.
    ${UPS.map(upRow).join("")}
    `; el.querySelectorAll("[data-up]").forEach(b => b.addEventListener("click", () => installUpgrade(+b.getAttribute("data-up"), b))); } catch { el.innerHTML = ""; } } - async function installUpgrade(i, btn) { const u = UPS[i]; if (!u) return; btn.disabled = true; btn.textContent = "Starte…"; @@ -60,32 +55,25 @@ async function installUpgrade(i, btn) { } catch (e) { toast("Fehler: " + e.message, true); btn.disabled = false; btn.textContent = "Installieren"; } } -// ---- News ---- -function card(n) { +// ---- News (Magazin-Layout) ---- +function newsCard(n, featured) { const date = n.ts ? ago(n.ts) : ""; - const rel = n.relevant ? ' · für deine Hardware' : ""; - return ` -
    -
    ${esc(n.title)}
    -
    ${esc(n.source)}${rel}
    -
    -
    ${esc(date)}
    + const rel = n.relevant ? `für dich` : ""; + return `
    +
    ${esc(n.source)}${rel}${esc(date)}
    +
    ${esc(n.title)}
    `; } - async function load() { const el = $("#news-list"); if (!el) return; el.innerHTML = `
    Lade News… (erstes Laden kann kurz dauern)
    `; try { - const d = await api("/api/news"); - const items = d.items || []; + const items = (await api("/api/news")).items || []; el.innerHTML = items.length - ? `
    ${items.map(card).join("")}
    ` + ? `
    ${items.map((n, i) => newsCard(n, i === 0)).join("")}
    ` : `
    Keine News geladen
    Die Quellen sind gerade nicht erreichbar — später nochmal probieren.
    `; - } catch (e) { - el.innerHTML = `
    ${esc(e.message)}
    `; - } + } catch (e) { el.innerHTML = `
    ${esc(e.message)}
    `; } } export default { id: "news", mount };