Files
prosolis a15d67e210 web: the muted text that was never muted, and the stylesheet fetched every time
Every text-[color:var(--ink)]/50, border-[color:var(--ink)]/10 and
bg-[color:var(--ink)]/5 in the templates compiled to nothing. Tailwind v3
cannot put an opacity on an arbitrary var() colour, so all 538 of them were
dropped from output.css without a word. Meta text rendered at full ink, card
borders fell back to Tailwind's default grey (bright white rings at night),
and the washes, backdrops and hover pills simply weren't there, on the news
site, the casino and the adventure pages alike.

The palette is now written once per phase and casino room as bare rgb
channels (--ink-rgb and friends), the plain vars are built from them, and
tailwind.config.js exposes ink, muted, accent, warn and card as real colours.
The classes are text-ink/80, border-ink/10, bg-accent/20 now, and they compile.

Restoring the intended opacities as-is would have traded flat for illegible:
ink at 50% measures 2.7 to 2.9:1 on the lit phases. So secondary text gets
its own colour, --muted, per phase and per casino room, taken as faint as it
can go while still clearing 4.5:1 on both the card and the page. Everything
that was ink at 35 to 70% is text-muted. The 20-30% ones are separators and
empty-cell dashes and stay faint.

The bg-theme-* chips carry white text everywhere they appear, and eight of
them measured 2.5 to 3.2:1. Same hues, taken just dark enough to pass (4.6 to
4.7:1). eu, lego and adventure already passed and are unchanged, and the
underline and border colours keep the old pastels since they carry no text.

Static files came out of the embedded FS with no Last-Modified, no ETag and
no Cache-Control, so every page view downloaded the stylesheet and every
script in full, and the service worker's "cheap 304" never happened. Each
file is hashed once now. Templates link through {{asset}}, which appends
?v=<hash>; a request naming the current hash is served immutable for a year,
and everything else gets the hash as its ETag and revalidates to a 304. The
service worker serves versioned URLs cache-first, drops older versions of the
same file as new ones land, and moves to cache v6.
2026-09-22 22:54:50 -07:00

112 lines
6.0 KiB
HTML
Raw Permalink 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.
{{define "title"}}Source health — {{.SiteTitle}}{{end}}
{{define "main"}}
{{$admin := .Admin}}
<section class="mt-2 mb-8">
<div class="rounded-3xl bg-card border-2 border-ink/10 p-6 sm:p-8 shadow-pete">
<p class="text-sm uppercase tracking-[0.2em] text-muted">{{if $admin}}owner view{{else}}live status{{end}}</p>
<h1 class="font-display text-3xl sm:text-4xl font-bold mt-1">Source health</h1>
<p class="mt-2 max-w-2xl text-muted">
{{if $admin}}Per-feed poll status and content stats. Rows that are currently failing float to the top.
{{else}}Every feed Pete follows and whether it's still updating. This runs on a best-effort basis, so a source going quiet for a bit is normal.{{end}}
</p>
<div class="mt-4 flex flex-wrap gap-2">
<span class="inline-flex items-center gap-1.5 rounded-full bg-ink/5 px-3 py-1 text-sm font-semibold tabular-nums">
{{len .Sources}} sources
</span>
{{if .DegradedCnt}}
<span class="inline-flex items-center gap-1.5 rounded-full bg-red-500/15 text-red-700 dark:text-red-300 px-3 py-1 text-sm font-semibold tabular-nums">
⚠ {{.DegradedCnt}} {{if $admin}}degraded{{else}}delayed{{end}}
</span>
{{else}}
<span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-500/15 text-emerald-700 dark:text-emerald-300 px-3 py-1 text-sm font-semibold">
✓ all updating
</span>
{{end}}
</div>
</div>
</section>
{{/* Adventure dispatch types gogobee sent that Pete has no template for. These
published on the neutral fallback — nothing was dropped — so this is a
vocabulary to-do, not an incident. Admin-only; absent when there are none. */}}
{{if and $admin .UnknownAdv}}
<section class="mb-8">
<div class="rounded-3xl bg-amber-500/10 border-2 border-amber-500/30 p-6">
<h2 class="font-display text-xl font-bold">Dispatches with no template</h2>
<p class="mt-1 text-sm text-muted max-w-2xl">
gogobee sent these event types since the last restart and Pete published them on the
generic fallback. Nothing was lost — they just read thin until he learns the words.
</p>
<ul class="mt-3 flex flex-wrap gap-2">
{{range .UnknownAdv}}
<li class="inline-flex items-center gap-2 rounded-full bg-card border border-amber-500/40 px-3 py-1 text-sm">
<code class="font-mono">{{.EventType}}</code>
<span class="tabular-nums text-muted">×{{.Count}}</span>
</li>
{{end}}
</ul>
</div>
</section>
{{end}}
<div class="overflow-x-auto rounded-3xl bg-card border-2 border-ink/10 shadow-pete">
<table class="w-full {{if $admin}}min-w-[52rem]{{else}}min-w-[28rem]{{end}} text-sm">
<thead>
<tr class="text-left text-muted uppercase text-xs tracking-wider border-b-2 border-ink/10">
<th class="px-4 py-3 font-semibold">Source</th>
<th class="px-4 py-3 font-semibold">Channel</th>
<th class="px-4 py-3 font-semibold">Status</th>
<th class="px-4 py-3 font-semibold">Last update</th>
{{if $admin}}
<th class="px-4 py-3 font-semibold">Last poll</th>
<th class="px-4 py-3 font-semibold text-right">Items</th>
<th class="px-4 py-3 font-semibold text-right">Stories</th>
<th class="px-4 py-3 font-semibold text-right">Paywall</th>
<th class="px-4 py-3 font-semibold">Last story</th>
<th class="px-4 py-3 font-semibold">Last posted</th>
{{end}}
</tr>
</thead>
<tbody>
{{range .Sources}}
<tr class="border-b border-ink/5 last:border-0 align-top">
<td class="px-4 py-3 font-semibold whitespace-nowrap">{{.Name}}</td>
<td class="px-4 py-3 text-muted whitespace-nowrap">{{.Channel}}</td>
<td class="px-4 py-3 whitespace-nowrap">
{{if .NeverRun}}
<span class="inline-flex items-center gap-1.5 rounded-full bg-ink/10 px-2.5 py-1 text-xs font-semibold">◌ idle</span>
{{else if .Healthy}}
<span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-500/15 text-emerald-700 dark:text-emerald-300 px-2.5 py-1 text-xs font-semibold">● live</span>
{{else if $admin}}
<span class="inline-flex items-center gap-1.5 rounded-full bg-red-500/15 text-red-700 dark:text-red-300 px-2.5 py-1 text-xs font-semibold"
title="{{.LastError}}">✕ {{.Failures}} fail{{if ne .Failures 1}}s{{end}}</span>
{{else}}
<span class="inline-flex items-center gap-1.5 rounded-full bg-red-500/15 text-red-700 dark:text-red-300 px-2.5 py-1 text-xs font-semibold">✕ delayed</span>
{{end}}
</td>
<td class="px-4 py-3 text-muted whitespace-nowrap">{{if .LastSuccessAt.IsZero}}never{{else}}{{timeAgo .LastSuccessAt}}{{end}}</td>
{{if $admin}}
<td class="px-4 py-3 text-muted whitespace-nowrap">{{if .LastPollAt.IsZero}}never{{else}}{{timeAgo .LastPollAt}}{{end}}</td>
<td class="px-4 py-3 text-right tabular-nums">{{.LastItemCount}}</td>
<td class="px-4 py-3 text-right tabular-nums" title="{{.Classified}} classified of {{.Total}}">{{.Total}}</td>
<td class="px-4 py-3 text-right tabular-nums {{if gt .PaywallRate 50}}text-amber-600 dark:text-amber-400 font-semibold{{end}}">
{{if .Total}}{{.PaywallRate}}%{{else}}—{{end}}
</td>
<td class="px-4 py-3 text-muted whitespace-nowrap">{{if .LastSeenAt.IsZero}}—{{else}}{{timeAgo .LastSeenAt}}{{end}}</td>
<td class="px-4 py-3 text-muted whitespace-nowrap">{{if .LastPostedAt.IsZero}}—{{else}}{{timeAgo .LastPostedAt}}{{end}}</td>
{{end}}
</tr>
{{if $admin}}{{if .LastError}}{{if not .Healthy}}
<tr class="border-b border-ink/5">
<td colspan="10" class="px-4 pb-3 -mt-1 text-xs text-red-600 dark:text-red-400 font-mono break-all">{{.LastError}}</td>
</tr>
{{end}}{{end}}{{end}}
{{else}}
<tr><td colspan="{{if $admin}}10{{else}}4{{end}}" class="px-4 py-8 text-center text-muted">No sources configured.</td></tr>
{{end}}
</tbody>
</table>
</div>
{{end}}