v6 Phase B: News-Magazin-Layout + Guide-Neubau (Tutorials)
- news.js: Magazin-Karten-Layout (Quell-Chip, Featured-Beitrag, 'fuer dich'-Marker) statt Liste. - guides.js: praktische Tutorials/Workflows (erster Workflow, Kontext leeren, Agent noetig?, Auto-Swap, Tools optimal nutzen) + kompakte Konzept-Karten statt klobiger Akkordeons. - components.css: .news-grid/.news-card + .concept-grid. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+15
-27
@@ -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 = `
|
||||
<div class="pagehead">
|
||||
<div><h1>News</h1>
|
||||
<div class="sub">Neuigkeiten rund um lokale LLMs — aus kuratierten Quellen (HuggingFace, r/LocalLLaMA, Latent Space, Ahead of AI …).</div></div>
|
||||
<div class="sub">Aktuelles aus der Local-LLM-Welt — aus kuratierten Quellen (HuggingFace, r/LocalLLaMA, Latent Space, Ahead of AI …).</div></div>
|
||||
<button class="ghost" id="news-refresh">Aktualisieren</button>
|
||||
</div>
|
||||
<div id="upgrades"></div>
|
||||
<div class="card"><div id="news-list">
|
||||
<div class="empty" style="text-align:center;padding:40px">Lade News…</div>
|
||||
</div></div>`;
|
||||
<div id="news-list"><div class="empty" style="text-align:center;padding:40px">Lade News…</div></div>`;
|
||||
$("#news-refresh").addEventListener("click", () => { load(); loadUpgrades(); });
|
||||
load();
|
||||
loadUpgrades();
|
||||
@@ -34,21 +32,18 @@ function upRow(u, i) {
|
||||
<button class="primary" data-up="${i}">Installieren</button>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
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 = `<div class="card" style="margin-bottom:16px">
|
||||
el.innerHTML = `<div class="card" style="margin-bottom:18px;border-color:rgba(45,212,191,.25)">
|
||||
<div class="card-h"><h3>Für dich: Upgrades</h3><span class="meta">${UPS.length}</span></div>
|
||||
<div class="card-sub">Neuere Modelle, die ein installiertes ersetzen können — Begründung inklusive.</div>
|
||||
<div class="card-sub">Neuere Modelle, die ein installiertes ersetzen können — mit Begründung.</div>
|
||||
${UPS.map(upRow).join("")}</div>`;
|
||||
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 ? ' · <span class="text-accent">für deine Hardware</span>' : "";
|
||||
return `<a class="li" href="${esc(n.link)}" target="_blank" rel="noopener" style="text-decoration:none;color:inherit">
|
||||
<div class="li-main">
|
||||
<div style="font-size:14px;line-height:1.4">${esc(n.title)}</div>
|
||||
<div class="li-sub">${esc(n.source)}${rel}</div>
|
||||
</div>
|
||||
<div class="li-right"><div class="li-time">${esc(date)}</div></div>
|
||||
const rel = n.relevant ? `<span class="news-rel">für dich</span>` : "";
|
||||
return `<a href="${esc(n.link)}" target="_blank" rel="noopener" class="news-card${featured ? " featured" : ""}">
|
||||
<div class="news-meta"><span class="news-src">${esc(n.source)}</span>${rel}<span class="news-date">${esc(date)}</span></div>
|
||||
<div class="news-title">${esc(n.title)}</div>
|
||||
</a>`;
|
||||
}
|
||||
|
||||
async function load() {
|
||||
const el = $("#news-list"); if (!el) return;
|
||||
el.innerHTML = `<div class="empty" style="text-align:center;padding:40px">Lade News… (erstes Laden kann kurz dauern)</div>`;
|
||||
try {
|
||||
const d = await api("/api/news");
|
||||
const items = d.items || [];
|
||||
const items = (await api("/api/news")).items || [];
|
||||
el.innerHTML = items.length
|
||||
? `<div class="list">${items.map(card).join("")}</div>`
|
||||
? `<div class="news-grid">${items.map((n, i) => newsCard(n, i === 0)).join("")}</div>`
|
||||
: `<div class="empty-c"><div class="e-t">Keine News geladen</div>
|
||||
<div class="e-s">Die Quellen sind gerade nicht erreichbar — später nochmal probieren.</div></div>`;
|
||||
} catch (e) {
|
||||
el.innerHTML = `<div class="alert err" style="margin:0">${esc(e.message)}</div>`;
|
||||
}
|
||||
} catch (e) { el.innerHTML = `<div class="alert err" style="margin:0">${esc(e.message)}</div>`; }
|
||||
}
|
||||
|
||||
export default { id: "news", mount };
|
||||
|
||||
Reference in New Issue
Block a user