Files
esh-pfi-infrastructure/stacks/news-digest/templates/digest.html.j2
T
vh 40f1e0ee00 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.
2026-04-28 11:29:15 -07:00

302 lines
13 KiB
Django/Jinja
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Daily Digest · {{ date_long }} · {{ edition_short }}</title>
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
{# Cache-bust on every digest run so frontend asset updates land
immediately — browsers were serving stale app.js / style.css after
rebuilds, which was hiding headline desks (old JS treated them as
empty because it only counted .item, not .headline). #}
<link rel="stylesheet" href="style.css?v={{ generated_at.strftime('%Y%m%d%H%M%S') }}" />
</head>
<body>
{# ────── header ────── #}
<header class="masthead" role="banner">
<div class="masthead-inner">
<a class="masthead-brand" href="index.html" title="Latest edition">
<span class="brand-glyph" aria-hidden="true">◢</span>
<span class="brand-mark">DAILY DIGEST</span>
</a>
<div class="masthead-edition">
<span class="edition-num">№ {{ generated_at.strftime("%j") }}</span>
<span class="edition-stamp">{{ edition|upper }} EDITION</span>
<a class="archive-link" href="archive.html">ARCHIVE →</a>
</div>
<div class="masthead-meta">
<div class="meta-row">
<span class="meta-label">FILED</span>
<span class="meta-value">{{ date_long|upper }} · {{ time_short }} {{ tz }}</span>
</div>
<div class="meta-row">
<span class="meta-label">DESK</span>
<span class="meta-value">WORLD · LOCAL · SUBREDDITS · TECH FEEDS</span>
</div>
<div class="meta-row">
<span class="meta-label">CURATED BY</span>
<span class="meta-value">{{ model }}</span>
</div>
</div>
</div>
{# A thin aurora-glow rule under the masthead — the only sanctioned
Australis gradient. #}
<div class="aurora-rule" aria-hidden="true"></div>
</header>
{# ────── nav strip ────── #}
<nav class="jumpnav" aria-label="sections">
<ol>
{% if world_headlines %}
<li><a href="#world"><span class="jump-num">01</span> WORLD</a></li>
{% endif %}
{% if local_headlines %}
<li><a href="#local"><span class="jump-num">02</span> LOCAL</a></li>
{% endif %}
{% if reddit_sources %}
<li><a href="#reddit"><span class="jump-num">03</span> REDDIT</a></li>
{% endif %}
{% if tech_sources %}
<li><a href="#tech"><span class="jump-num">04</span> TECH FEEDS</a></li>
{% endif %}
</ol>
</nav>
<main class="brief">
{# ────── WORLD HEADLINES ────── #}
{% if world_headlines %}
<section id="world" class="desk desk-headlines">
<header class="desk-head">
<span class="desk-num">01</span>
<h2 class="desk-title">World</h2>
<span class="desk-sub">top of last {{ generated_at.hour < 14 and "8" or "8" }} hours · headlines</span>
<span class="desk-count">{{ world_total }} items</span>
<button class="desk-collapse" type="button" aria-label="Collapse section" title="Collapse / expand">▾</button>
</header>
<ol class="headlines-list">
{% for h in world_headlines %}
<li class="headline" data-id="{{ h.id }}">
<button class="item-hide" type="button" title="Hide this headline" aria-label="Hide this headline">×</button>
<h3 class="headline-title">
<a href="{{ h.url }}" target="_blank" rel="noopener">{{ h.title }}</a>
</h3>
<span class="headline-source">{{ h.source }}</span>
<span class="headline-time">{{ h.posted_at|humanago }}</span>
{% if h.tldr %}
<p class="headline-tldr">{{ h.tldr }}</p>
{% endif %}
</li>
{% endfor %}
</ol>
</section>
{% endif %}
{# ────── LOCAL HEADLINES ────── #}
{% if local_headlines %}
<section id="local" class="desk desk-headlines">
<header class="desk-head">
<span class="desk-num">02</span>
<h2 class="desk-title">Local</h2>
<span class="desk-sub">orange county · greater LA · last 8 hours</span>
<span class="desk-count">{{ local_total }} items</span>
<button class="desk-collapse" type="button" aria-label="Collapse section" title="Collapse / expand">▾</button>
</header>
<ol class="headlines-list">
{% for h in local_headlines %}
<li class="headline" data-id="{{ h.id }}">
<button class="item-hide" type="button" title="Hide this headline" aria-label="Hide this headline">×</button>
<h3 class="headline-title">
<a href="{{ h.url }}" target="_blank" rel="noopener">{{ h.title }}</a>
</h3>
<span class="headline-source">{{ h.source }}</span>
<span class="headline-time">{{ h.posted_at|humanago }}</span>
{% if h.tldr %}
<p class="headline-tldr">{{ h.tldr }}</p>
{% endif %}
</li>
{% endfor %}
</ol>
</section>
{% endif %}
{# ────── REDDIT SECTION ────── #}
{% if reddit_sources %}
<section id="reddit" class="desk">
<header class="desk-head">
<span class="desk-num">03</span>
<h2 class="desk-title">Reddit</h2>
<span class="desk-sub">top of last {{ generated_at.hour < 14 and "12" or "12" }} hours · score-filtered</span>
<span class="desk-count">{{ reddit_total }} items</span>
<button class="desk-collapse" type="button" aria-label="Collapse section" title="Collapse / expand">▾</button>
</header>
{% for src in reddit_sources %}
<article class="source">
<header class="source-head">
<a class="source-name" href="{{ src.href }}" target="_blank" rel="noopener">
{{ src.name }}
</a>
<span class="source-count">{{ src.items|length }}</span>
</header>
<ol class="items">
{% for it in src.items %}
<li class="item" data-tag="{{ it.tag }}" data-id="{{ it.id }}">
<button class="item-hide" type="button" title="Hide this item" aria-label="Hide this item">×</button>
<div class="item-rail" aria-hidden="true">
{% if it.score %}
<span class="chip chip-score" title="upvotes">▲ {{ it.score }}</span>
{% endif %}
{% if it.comments %}
<span class="chip chip-comments" title="comments">{{ it.comments }} ⌥</span>
{% endif %}
{% if it.tag and it.tag != 'other' %}
<span class="chip chip-tag chip-tag-{{ it.tag }}">{{ it.tag }}</span>
{% endif %}
</div>
<div class="item-body">
<h3 class="item-title">
<a href="{{ it.url or it.permalink }}" target="_blank" rel="noopener">{{ it.title }}</a>
</h3>
{% if it.tldr %}
<p class="item-tldr">{{ it.tldr }}</p>
{% endif %}
<footer class="item-foot">
<div class="item-actions">
{% if it.url and it.url != it.permalink %}
<a class="action action-source" href="{{ it.url }}" target="_blank" rel="noopener">
<span class="action-arrow">↗</span>
<span class="action-label">SOURCE</span>
<span class="action-domain">{{ it.url|domain }}</span>
</a>
{% endif %}
<a class="action action-thread" href="{{ it.permalink }}" target="_blank" rel="noopener">
<span class="action-arrow">⌥</span>
<span class="action-label">REDDIT THREAD</span>
</a>
</div>
<div class="item-meta-row">
<span class="item-meta">{{ it.posted_at|humanago }} ago</span>
<span class="sep">·</span>
<span class="item-meta">u/{{ it.author }}</span>
</div>
</footer>
</div>
</li>
{% endfor %}
</ol>
</article>
{% endfor %}
</section>
{% endif %}
{# ────── TECH FEEDS SECTION ────── #}
{% if tech_sources %}
<section id="tech" class="desk">
<header class="desk-head">
<span class="desk-num">04</span>
<h2 class="desk-title">Tech Feeds</h2>
<span class="desk-sub">non-reddit · last {{ generated_at.hour < 14 and "12" or "12" }} hours</span>
<span class="desk-count">{{ tech_total }} items</span>
<button class="desk-collapse" type="button" aria-label="Collapse section" title="Collapse / expand">▾</button>
</header>
{% for src in tech_sources %}
<article class="source">
<header class="source-head">
<a class="source-name" href="{{ src.href }}" target="_blank" rel="noopener">
{{ src.name }}
</a>
<span class="source-count">{{ src.items|length }}</span>
</header>
<ol class="items">
{% for it in src.items %}
<li class="item" data-tag="{{ it.tag }}" data-id="{{ it.id }}">
<button class="item-hide" type="button" title="Hide this item" aria-label="Hide this item">×</button>
<div class="item-rail" aria-hidden="true">
{% if it.tag and it.tag != 'other' %}
<span class="chip chip-tag chip-tag-{{ it.tag }}">{{ it.tag }}</span>
{% endif %}
</div>
<div class="item-body">
<h3 class="item-title">
<a href="{{ it.url }}" target="_blank" rel="noopener">{{ it.title }}</a>
</h3>
{% if it.tldr %}
<p class="item-tldr">{{ it.tldr }}</p>
{% endif %}
<footer class="item-foot">
{% if it.url %}
<div class="item-actions">
<a class="action action-source" href="{{ it.url }}" target="_blank" rel="noopener">
<span class="action-arrow">↗</span>
<span class="action-label">SOURCE</span>
<span class="action-domain">{{ it.url|domain }}</span>
</a>
</div>
{% endif %}
<div class="item-meta-row">
<span class="item-meta">{{ it.posted_at|humanago }} ago</span>
{% if it.author %}
<span class="sep">·</span>
<span class="item-meta">{{ it.author }}</span>
{% endif %}
</div>
</footer>
</div>
</li>
{% endfor %}
</ol>
</article>
{% endfor %}
</section>
{% endif %}
{% if not reddit_sources and not tech_sources and not world_headlines and not local_headlines %}
<section class="desk empty">
<p class="empty-msg">
No items cleared the filters in the last window.<br>
Lower <code>DIGEST_MIN_SCORE</code> or widen
<code>DIGEST_REDDIT_HOURS</code> if this looks wrong.
</p>
</section>
{% endif %}
{# ────── HIDDEN TRAY ──────
Hidden by default. app.js unhides it once it has anything to show
(i.e. at least one .item on this page is in the user's hidden set).
Click the header to expand/collapse; click a row's restore button
to bring the item back into the desk it came from. #}
<section id="hidden-tray" class="hidden-tray" hidden>
<header class="hidden-tray-head">
<button class="hidden-tray-toggle" type="button" aria-expanded="false">
<span class="tray-glyph" aria-hidden="true">▾</span>
<span class="tray-label">Hidden</span>
<span class="tray-count" id="hidden-tray-count">0</span>
</button>
</header>
<ol class="hidden-tray-list" id="hidden-tray-list" hidden></ol>
</section>
</main>
<footer class="colophon">
<div class="colophon-inner">
<div class="colophon-block">
<span class="meta-label">FILED</span>
<span class="meta-value">{{ generated_at.isoformat(timespec="seconds") }}</span>
</div>
<div class="colophon-block">
<span class="meta-label">NEXT EDITION</span>
<span class="meta-value">{{ next_edition|upper }} · 12 H</span>
</div>
<div class="colophon-block">
<span class="meta-label">PIPELINE</span>
<span class="meta-value">REDDIT JSON + MINIFLUX → {{ model }} → JINJA2</span>
</div>
</div>
</footer>
<script src="app.js?v={{ generated_at.strftime('%Y%m%d%H%M%S') }}" defer></script>
</body>
</html>