Show the publication after the feed name, and link feeds to Miniflux for admins

Readers see "Feed · Publication" on the issue, article, public and RSS
pages and in the EPUB index and chapter headers; the publication is the
page's site name, else its domain, and is dropped when it would repeat
the feed's title. Admins get the feed name linked to its Miniflux page.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YWmCpUojfHXhSZ2129Z7Nv
This commit is contained in:
2026-09-09 06:01:29 +00:00
co-authored by Claude Fable 5.1
parent 87f3a5dbf1
commit f2a44507d4
8 changed files with 138 additions and 16 deletions
+1
View File
@@ -0,0 +1 @@
{% match source.feed_href %}{% when Some with (href) %}<a class="text-muted hover:text-accent" href="{{ href }}" target="_blank" rel="noopener">{{ source.feed_title }}</a>{% when None %}{{ source.feed_title }}{% endmatch %}{% match source.publication %}{% when Some with (publication) %} · {{ publication }}{% when None %}{% endmatch %}
+1 -1
View File
@@ -1,6 +1,6 @@
{% extends "layout.html" %}{% block ears %}<span class="sm:hidden">{{ toc.short_date }}</span><span class="hidden sm:inline">{{ toc.display_date }} · No. {{ toc.issue_number }}</span>{% endblock %}{% block content %}<div class="mx-auto max-w-7xl lg:grid lg:grid-cols-[15rem_minmax(0,1fr)] lg:gap-x-8 lg:px-6 xl:grid-cols-[18rem_minmax(0,1fr)] xl:gap-x-14">{% include "_toc.html" %}
<article class="reader-page mx-auto mt-10 w-full max-w-[68ch] px-4 sm:px-6 lg:mt-12 lg:px-0" data-toc-scroll>
<header><p class="font-sans text-[0.72rem] uppercase tracking-[0.12em] text-muted">Article</p><h1 class="mt-3 text-4xl font-semibold leading-[1.1] tracking-[-0.01em] sm:text-5xl"><a class="text-ink no-underline hover:text-accent" href="{{ source_url }}">{{ title }}</a></h1>{% match byline %}{% when Some with (byline) %}<p class="mt-5 font-sans text-sm font-medium text-ink-2">{{ byline }}</p>{% when None %}{% endmatch %}<p class="mt-1 font-sans text-sm text-muted">{{ meta_line }}</p>{% match why %}{% when Some with (why) %}<p class="mt-6 border-l-2 border-accent pl-3 italic text-ink-2">Why it's here: {{ why }}</p>{% when None %}{% endmatch %}{% match understanding %}{% when Some with (line) %}<p class="mt-4 font-sans text-sm text-muted">{{ line }}</p>{% when None %}{% endmatch %}{% match social_line %}{% when Some with (social) %}<p class="mt-4 font-sans text-sm text-muted">{{ social }}</p>{% when None %}{% endmatch %}{% match summary %}{% when Some with (summary) %}<p class="mt-7 text-xl italic leading-relaxed text-ink-2">{{ summary }}</p>{% when None %}{% endmatch %}{% if excerpt_only %}<p class="notice mt-6">Excerpt only — continue reading at the original site.</p>{% endif %}</header>
<header><p class="font-sans text-[0.72rem] uppercase tracking-[0.12em] text-muted">Article</p><h1 class="mt-3 text-4xl font-semibold leading-[1.1] tracking-[-0.01em] sm:text-5xl"><a class="text-ink no-underline hover:text-accent" href="{{ source_url }}">{{ title }}</a></h1>{% match byline %}{% when Some with (byline) %}<p class="mt-5 font-sans text-sm font-medium text-ink-2">{{ byline }}</p>{% when None %}{% endmatch %}<p class="mt-1 font-sans text-sm text-muted">{% include "_source.html" %} · {{ meta_line }}</p>{% match why %}{% when Some with (why) %}<p class="mt-6 border-l-2 border-accent pl-3 italic text-ink-2">Why it's here: {{ why }}</p>{% when None %}{% endmatch %}{% match understanding %}{% when Some with (line) %}<p class="mt-4 font-sans text-sm text-muted">{{ line }}</p>{% when None %}{% endmatch %}{% match social_line %}{% when Some with (social) %}<p class="mt-4 font-sans text-sm text-muted">{{ social }}</p>{% when None %}{% endmatch %}{% match summary %}{% when Some with (summary) %}<p class="mt-7 text-xl italic leading-relaxed text-ink-2">{{ summary }}</p>{% when None %}{% endmatch %}{% if excerpt_only %}<p class="notice mt-6">Excerpt only — continue reading at the original site.</p>{% endif %}</header>
<div class="prose-body mt-10 border-t border-rule pt-8">{{ body_html|safe }}</div>
{% match discussion_html %}{% when Some with (discussion) %}<section class="discussion prose-body mt-12 border-t border-rule pt-6 [&_.comment-meta]:font-sans [&_.comment-meta]:text-sm [&_.comment-meta]:text-muted [&_blockquote.reply]:ml-6"><h2 class="mb-6 border-0 pt-0 text-2xl">Discussion</h2>{{ discussion|safe }}</section>{% when None %}{% endmatch %}
<footer class="mt-12 border-t border-rule pt-6">{% match rating %}{% when Some with (widget) %}{% include "_rating_widget.html" %}{% when None %}{% endmatch %}<p class="mt-6 flex flex-wrap gap-2"><a class="btn" href="{{ read_online_url }}">Read online ↗</a>{% if page.is_admin() %}<a class="btn" href="{{ dashboard_href }}">Dashboard</a>{% endif %}</p><nav class="mt-10 grid grid-cols-1 gap-3 font-sans sm:grid-cols-2" aria-label="Adjacent articles">{% match previous %}{% when Some with (previous) %}<a class="group min-h-24 border border-rule p-4 text-ink no-underline hover:border-accent" rel="prev" href="{{ previous.href }}"><span class="block text-[0.68rem] uppercase tracking-[0.12em] text-muted">Previous</span><span class="mt-2 block leading-snug group-hover:text-accent">← {{ previous.title }}</span></a>{% when None %}{% endmatch %}{% match next %}{% when Some with (next) %}<a class="group min-h-24 border border-rule p-4 text-right text-ink no-underline hover:border-accent sm:col-start-2" rel="next" href="{{ next.href }}"><span class="block text-[0.68rem] uppercase tracking-[0.12em] text-muted">Next</span><span class="mt-2 block leading-snug group-hover:text-accent">{{ next.title }} →</span></a>{% when None %}{% endmatch %}</nav><p class="mt-8 font-sans text-sm"><a href="{{ issue_href }}">← Back to this issue</a></p></footer>
+1 -1
View File
@@ -1 +1 @@
{% for section in issue.sections %}<h2>{{ section.name }}</h2><ul>{% for entry in section.entries %}<li><a href="{{ entry.url }}">{{ entry.title }}</a> — {{ entry.source }} ({{ entry.domain }}){% match entry.summary %}{% when Some with (summary) %}<p class="summary">{{ summary }}</p>{% when None %}{% endmatch %}{% match entry.why %}{% when Some with (why) %}<p class="why"><em>Why it's here: {{ why }}</em></p>{% when None %}{% endmatch %}{% if !entry.comment_links.is_empty() %} · {% for link in entry.comment_links %}<a href="{{ link.url }}">{{ link.label }}</a>{% endfor %}{% endif %}</li>{% endfor %}</ul>{% endfor %}
{% for section in issue.sections %}<h2>{{ section.name }}</h2><ul>{% for entry in section.entries %}<li><a href="{{ entry.url }}">{{ entry.title }}</a> — {{ entry.source }}{% match entry.publication %}{% when Some with (publication) %} · {{ publication }}{% when None %}{% endmatch %}{% match entry.summary %}{% when Some with (summary) %}<p class="summary">{{ summary }}</p>{% when None %}{% endmatch %}{% match entry.why %}{% when Some with (why) %}<p class="why"><em>Why it's here: {{ why }}</em></p>{% when None %}{% endmatch %}{% if !entry.comment_links.is_empty() %} · {% for link in entry.comment_links %}<a href="{{ link.url }}">{{ link.label }}</a>{% endfor %}{% endif %}</li>{% endfor %}</ul>{% endfor %}
+1 -1
View File
@@ -5,7 +5,7 @@
<section aria-labelledby="brief-heading" data-toc-entry="/issues/{{ date }}"><h1 id="brief-heading" class="reader-section-heading">The Brief</h1><div class="editorial prose-body mt-5 [&>p:first-child]:first-letter:float-left [&>p:first-child]:first-letter:mr-2 [&>p:first-child]:first-letter:mt-1 [&>p:first-child]:first-letter:font-serif [&>p:first-child]:first-letter:text-[4.6rem] [&>p:first-child]:first-letter:font-semibold [&>p:first-child]:first-letter:leading-[0.72]">{{ front_page_html|safe }}</div></section>
{% if (page.is_admin() && read_href.is_some()) || downloads.is_some() %}<div class="my-8 flex flex-wrap gap-2 border-y border-rule py-4">{% if page.is_admin() %}{% match read_href %}{% when Some with (href) %}<a class="btn" href="{{ href }}" target="_blank" rel="noopener">Read in BookOrbit</a>{% when None %}{% endmatch %}{% endif %}{% match downloads %}{% when Some with (downloads) %}{% if downloads.others.is_empty() %}<a class="btn" href="{{ downloads.primary.href }}">Download {{ downloads.primary.label }}</a>{% else %}<div class="relative inline-flex"><a class="btn rounded-r-none" href="{{ downloads.primary.href }}">Download {{ downloads.primary.label }}</a><details class="-ml-px"><summary class="btn list-none rounded-l-none px-2 [&::-webkit-details-marker]:hidden" aria-label="Choose download format"><span aria-hidden="true">▾</span></summary><div class="absolute left-0 top-full z-20 mt-1 min-w-52 border border-rule bg-paper p-1 shadow-[0_18px_32px_-26px_rgb(0_0_0_/_0.6)]"><a class="btn group w-full justify-between border-0" href="{{ downloads.primary.href }}"><span>{% if downloads.primary.label == "EPUB" %}Standard {% endif %}{{ downloads.primary.label }}</span><span class="ml-4 text-muted group-hover:text-paper">{{ downloads.primary.size }}</span></a>{% for download in downloads.others %}<a class="btn group w-full justify-between border-0" href="{{ download.href }}"><span>{{ download.label }}</span><span class="ml-4 text-muted group-hover:text-paper">{{ download.size }}</span></a>{% endfor %}</div></details></div>{% endif %}{% when None %}{% endmatch %}</div>{% endif %}
<section class="mt-12" aria-labelledby="index-heading"><h2 id="index-heading" class="text-center text-3xl font-semibold leading-[1.1] tracking-[-0.01em]">In This Issue</h2>
{% for section in sections %}<section class="mt-14"><h3 class="reader-section-heading">{{ section.name }}</h3><ul class="m-0 list-none p-0">{% for entry in section.entries %}<li class="border-b border-rule py-6" data-toc-entry="{{ entry.href }}"><h4 class="font-serif font-semibold leading-[1.1] tracking-[-0.01em] {% if entry.is_lead %}text-3xl{% else %}text-2xl{% endif %}"><a class="text-ink no-underline hover:text-accent" href="{{ entry.href }}">{{ entry.title }}</a></h4><p class="mt-2 font-sans text-sm text-muted">{{ entry.source }} · {{ entry.reading_minutes }} min read{% if page.is_admin() %} · <a class="text-muted hover:text-accent" href="{{ entry.dashboard_href }}">dashboard</a>{% endif %}</p>{% if !entry.summary.is_empty() %}<p class="index-summary mt-4">{{ entry.summary }}</p>{% endif %}{% match entry.why %}{% when Some with (why) %}<p class="mt-4 border-l-2 border-accent pl-3 italic text-ink-2">Why it's here: {{ why }}</p>{% when None %}{% endmatch %}{% match entry.understanding %}{% when Some with (line) %}<p class="mt-3 font-sans text-sm text-muted">{{ line }}</p>{% when None %}{% endmatch %}{% match entry.rating %}{% when Some with (widget) %}{% include "_rating_widget.html" %}{% when None %}{% endmatch %}</li>{% endfor %}</ul></section>{% endfor %}
{% for section in sections %}<section class="mt-14"><h3 class="reader-section-heading">{{ section.name }}</h3><ul class="m-0 list-none p-0">{% for entry in section.entries %}<li class="border-b border-rule py-6" data-toc-entry="{{ entry.href }}"><h4 class="font-serif font-semibold leading-[1.1] tracking-[-0.01em] {% if entry.is_lead %}text-3xl{% else %}text-2xl{% endif %}"><a class="text-ink no-underline hover:text-accent" href="{{ entry.href }}">{{ entry.title }}</a></h4><p class="mt-2 font-sans text-sm text-muted">{% let source = entry.source %}{% include "_source.html" %} · {{ entry.reading_minutes }} min read{% if page.is_admin() %} · <a class="text-muted hover:text-accent" href="{{ entry.dashboard_href }}">dashboard</a>{% endif %}</p>{% if !entry.summary.is_empty() %}<p class="index-summary mt-4">{{ entry.summary }}</p>{% endif %}{% match entry.why %}{% when Some with (why) %}<p class="mt-4 border-l-2 border-accent pl-3 italic text-ink-2">Why it's here: {{ why }}</p>{% when None %}{% endmatch %}{% match entry.understanding %}{% when Some with (line) %}<p class="mt-3 font-sans text-sm text-muted">{{ line }}</p>{% when None %}{% endmatch %}{% match entry.rating %}{% when Some with (widget) %}{% include "_rating_widget.html" %}{% when None %}{% endmatch %}</li>{% endfor %}</ul></section>{% endfor %}
</section>
{% if has_world || has_behind %}<nav class="my-12 flex flex-wrap items-center justify-center gap-x-3 gap-y-1 border-y border-rule py-4 text-center font-sans text-sm uppercase tracking-[0.08em]" aria-label="Issue chapters">{% if has_world %}<a class="text-ink no-underline hover:text-accent" href="/issues/{{ date }}/world">World Briefing</a>{% endif %}{% if has_world && has_behind %}<span class="text-muted" aria-hidden="true">·</span>{% endif %}{% if has_behind %}<a class="text-ink no-underline hover:text-accent" href="/issues/{{ date }}/behind">Behind the paper</a>{% endif %}</nav>{% endif %}
<footer id="colophon" class="mt-14 scroll-mt-6 font-sans text-sm leading-relaxed" data-toc-entry="/issues/{{ date }}#colophon"><h2 class="reader-section-heading">Colophon</h2><p class="my-4 text-ink-2"><em>The Daily EPUB</em> is assembled every morning from a personal feed reader.</p><dl class="kv border-y border-rule py-4 text-xs sm:text-sm"><dt>Generated</dt><dd>{{ colophon.generated_at }}</dd><dt>Bulk model</dt><dd>{{ colophon.bulk_model }}</dd><dt>Editor model</dt><dd>{{ colophon.editor_model }}</dd><dt>Summaries model</dt><dd>{{ colophon.summaries_model }}</dd><dt>Entries considered</dt><dd>{% match colophon.entries_fetched %}{% when Some with (entries) %}{{ entries }}{% match colophon.feeds_seen %}{% when Some with (feeds) %} from {{ feeds }} feeds{% when None %}{% endmatch %}{% when None %}n/a{% endmatch %}</dd><dt>Candidates scored</dt><dd>{% match colophon.candidates %}{% when Some with (candidates) %}{{ candidates }}{% when None %}n/a{% endmatch %}</dd><dt>Articles selected</dt><dd>{{ colophon.article_count }} across {{ colophon.section_count }} sections</dd><dt>Words</dt><dd>{{ colophon.total_words }} · ~{{ colophon.reading_minutes }} min read</dd>{% for cost in colophon.provider_costs %}<dt>{{ cost.provider }} cost</dt><dd>{{ cost.cost }}</dd>{% endfor %}<dt>Total token cost</dt><dd>{% match colophon.cost_usd %}{% when Some with (cost) %}{{ cost }}{% when None %}n/a{% endmatch %}</dd><dt>Generator</dt><dd>{{ colophon.generator_version }}</dd></dl></footer>
+1 -1
View File
@@ -4,6 +4,6 @@
<header class="mb-10 text-center"><p class="font-sans text-[0.72rem] uppercase tracking-[0.12em] text-muted">{{ issue.display_date }} · No. {{ issue.issue_number }}</p><p class="mt-2 font-sans text-sm text-muted">{{ issue.stats_line }}</p><p class="mx-auto mt-5 max-w-[58ch] text-lg italic leading-relaxed text-ink-2">A personal morning paper, assembled daily; the selection is the reader's, the words are the authors'.</p></header>
<p class="notice mb-8"><a href="/login">Sign in</a> to read every article's full text and download the editions, or <a href="/request-access">request access</a>.</p>
{% if !downloads.is_empty() %}<div class="mb-8 flex flex-wrap justify-center gap-2">{% for download in downloads %}<a class="btn" href="{{ download.href }}">{{ download.label }} <span class="ml-1 text-muted">{{ download.size }}</span></a>{% endfor %}</div>{% endif %}
{% for section in issue.sections %}<section class="mt-14"><h2 class="reader-section-heading">{{ section.name }}</h2><div>{% for entry in section.entries %}<article class="border-b border-rule py-6{% if entry.is_lead %} pt-5{% endif %}"><h3 class="font-semibold leading-[1.1] tracking-[-0.01em] {% if entry.is_lead %}text-3xl{% else %}text-2xl{% endif %}"><a class="text-ink no-underline hover:text-accent" href="{{ entry.url }}">{{ entry.title }}</a></h3><p class="mt-2 font-sans text-sm leading-relaxed text-muted">{% match entry.author %}{% when Some with (author) %}{{ author }} · {% when None %}{% endmatch %}{{ entry.source }}{% if !entry.domain.is_empty() %} ({{ entry.domain }}){% endif %} · {{ entry.reading_minutes }} min</p>{% match entry.summary %}{% when Some with (summary) %}<p class="index-summary mt-4">{{ summary }}</p>{% when None %}{% endmatch %}{% match entry.why %}{% when Some with (why) %}<p class="mt-4 border-l-2 border-accent pl-3 italic text-ink-2">Why it's here: {{ why }}</p>{% when None %}{% endmatch %}{% match entry.understanding %}{% when Some with (line) %}<p class="mt-3 font-sans text-sm text-muted">{{ line }}</p>{% when None %}{% endmatch %}{% if !entry.comment_links.is_empty() %}<p class="mt-3 flex flex-wrap gap-x-4 gap-y-1 font-sans text-sm text-muted">{% for link in entry.comment_links %}<a class="text-muted hover:text-accent" rel="noopener" target="_blank" href="{{ link.url }}">{{ link.label }}{% if !link.meta.is_empty() %}: {{ link.meta }}{% endif %}</a>{% endfor %}</p>{% endif %}</article>{% endfor %}</div></section>{% endfor %}
{% for section in issue.sections %}<section class="mt-14"><h2 class="reader-section-heading">{{ section.name }}</h2><div>{% for entry in section.entries %}<article class="border-b border-rule py-6{% if entry.is_lead %} pt-5{% endif %}"><h3 class="font-semibold leading-[1.1] tracking-[-0.01em] {% if entry.is_lead %}text-3xl{% else %}text-2xl{% endif %}"><a class="text-ink no-underline hover:text-accent" href="{{ entry.url }}">{{ entry.title }}</a></h3><p class="mt-2 font-sans text-sm leading-relaxed text-muted">{% match entry.author %}{% when Some with (author) %}{{ author }} · {% when None %}{% endmatch %}{{ entry.source }}{% match entry.publication %}{% when Some with (publication) %} · {{ publication }}{% when None %}{% endmatch %} · {{ entry.reading_minutes }} min</p>{% match entry.summary %}{% when Some with (summary) %}<p class="index-summary mt-4">{{ summary }}</p>{% when None %}{% endmatch %}{% match entry.why %}{% when Some with (why) %}<p class="mt-4 border-l-2 border-accent pl-3 italic text-ink-2">Why it's here: {{ why }}</p>{% when None %}{% endmatch %}{% match entry.understanding %}{% when Some with (line) %}<p class="mt-3 font-sans text-sm text-muted">{{ line }}</p>{% when None %}{% endmatch %}{% if !entry.comment_links.is_empty() %}<p class="mt-3 flex flex-wrap gap-x-4 gap-y-1 font-sans text-sm text-muted">{% for link in entry.comment_links %}<a class="text-muted hover:text-accent" rel="noopener" target="_blank" href="{{ link.url }}">{{ link.label }}{% if !link.meta.is_empty() %}: {{ link.meta }}{% endif %}</a>{% endfor %}</p>{% endif %}</article>{% endfor %}</div></section>{% endfor %}
<p class="mt-10 text-center font-sans text-sm"><a href="/issues">Browse the archive →</a></p>
{% endif %}</article>{% endblock %}