news-digest: real article summaries + per-desk collapse
Two upgrades to make the digest actually readable:
1) Article-grounded 2-3 sentence summaries (everywhere)
The old prompt got just the title + miniflux's content excerpt,
which for HN/Lobsters/wire feeds is barely more than the title
itself — so summaries paraphrased the title and added nothing.
Now every URL gets fetched and main-content-extracted via
trafilatura on a parallel pre-pass (10 workers, ~15s for ~50
URLs). Extracted text caches to /output/.article-cache.json with
a 7-day TTL so repeat runs in the same window don't re-pull.
Headlines also get summarized now — one batched LLM call per
category (world / local). Rendered as a paragraph below the
title with source + time on the right rail.
Prompt rewrites tell the model to pull names/numbers/places
from the body and explicitly forbid restating the title.
Result: real specifics ("71% saw no pay increase globally",
"third time in less than two weeks", "Islamabad and Moscow
intermediaries") instead of title paraphrase.
2) Per-desk collapse buttons
Chevron next to .desk-count toggles a .is-collapsed class.
Collapsed state is per-device (localStorage by section id) since
collapse is a viewing preference, not content state.
This commit is contained in:
@@ -201,6 +201,38 @@
|
||||
});
|
||||
}
|
||||
|
||||
/* ── desk collapse (per-device, localStorage) ───────────────────── */
|
||||
// Stored as a JSON object {deskId: true} of collapsed desks. Per
|
||||
// device by design — collapse is a viewing preference, not content
|
||||
// state, so no server roundtrip.
|
||||
const COLLAPSE_KEY = "digest:collapsed-desks";
|
||||
function readCollapsed() {
|
||||
try { return JSON.parse(localStorage.getItem(COLLAPSE_KEY) || "{}"); }
|
||||
catch (_) { return {}; }
|
||||
}
|
||||
function writeCollapsed(obj) {
|
||||
try { localStorage.setItem(COLLAPSE_KEY, JSON.stringify(obj)); } catch (_) {}
|
||||
}
|
||||
function applyCollapseState() {
|
||||
const state = readCollapsed();
|
||||
document.querySelectorAll(".desk[id]").forEach((desk) => {
|
||||
desk.classList.toggle("is-collapsed", !!state[desk.id]);
|
||||
});
|
||||
}
|
||||
applyCollapseState();
|
||||
document.addEventListener("click", (ev) => {
|
||||
const btn = ev.target.closest(".desk-collapse");
|
||||
if (!btn) return;
|
||||
const desk = btn.closest(".desk[id]");
|
||||
if (!desk) return;
|
||||
ev.preventDefault();
|
||||
desk.classList.toggle("is-collapsed");
|
||||
const state = readCollapsed();
|
||||
if (desk.classList.contains("is-collapsed")) state[desk.id] = true;
|
||||
else delete state[desk.id];
|
||||
writeCollapsed(state);
|
||||
});
|
||||
|
||||
/* ── initial paint ──────────────────────────────────────────────── */
|
||||
|
||||
apiGetHidden().then((hidden) => {
|
||||
|
||||
Reference in New Issue
Block a user