Add admin dashboard links on article pages and a single download menu

Signed-in issue and article pages now carry an admin-only link to the
article's dashboard page. The separate Download EPUB / X4 EPUB / XTC
buttons become one "Download EPUB" button (Standard edition first) with a
no-JS <details> menu listing every available format and its size.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QVPagF6jfDv78CC5Jv2wp4
This commit is contained in:
thalladaandClaude Fable 5.1 committed 2026-09-06 17:18:23 +00:00
1 parent 142a8d9905
commit 71665c293c
5 files changed
+125 -19

No files matched your search

+2 -2
View File
@@ -3,9 +3,9 @@
<article class="reader-page mx-auto mt-10 w-full max-w-[68ch] px-4 sm:px-6 lg:mt-12 lg:px-0">
<header class="mb-8 text-center"><p class="font-sans text-[0.72rem] uppercase tracking-[0.12em] text-muted">{{ display_date }} · No. {{ issue_number }}</p><p class="mt-2 font-sans text-sm text-muted">{{ stats_line }}</p></header>
<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 read_href.is_some() || !downloads.is_empty() %}<div class="my-8 flex flex-wrap gap-2 border-y border-rule py-4">{% match read_href %}{% when Some with (href) %}<a class="btn" href="{{ href }}" target="_blank" rel="noopener">Read in BookOrbit</a>{% when None %}{% endmatch %}{% for download in downloads %}<a class="btn" href="{{ download.href }}">Download {{ download.label }} <span class="ml-1 text-muted">{{ download.size }}</span></a>{% endfor %}</div>{% endif %}
{% if read_href.is_some() || downloads.is_some() %}<div class="my-8 flex flex-wrap gap-2 border-y border-rule py-4">{% match read_href %}{% when Some with (href) %}<a class="btn" href="{{ href }}" target="_blank" rel="noopener">Read in BookOrbit</a>{% when None %}{% endmatch %}{% 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</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.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">{{ 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.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>