Web dashboard step 6: jobs and stats

Job catalogue and daily-epub job run, the systemd job unit and polkit rule,
SystemdRunner/MockRunner, the Jobs pages with status and journal tail, the
stats_data refactor with a byte-identical CLI, the stats page and the
overview sparklines.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NHyYupFdBiR4VfoUM7NjSM
This commit is contained in:
2026-09-03 18:01:21 +00:00
co-authored by Claude Fable 5.1
parent d36f203e4c
commit aa3de51d9c
21 changed files with 2958 additions and 112 deletions
+12 -1
View File
@@ -73,7 +73,6 @@ pre.preview { white-space:pre-wrap; overflow-wrap:anywhere; font:.85rem/1.4 ui-m
.versions pre.preview { max-height:6rem; border:0; padding:0; }
.versions form { margin:0; }
@media (max-width:60rem) { .profile-grid { display:block; } }
||||||| 849231e
/* step 3: dashboard reads (overview, runs, articles) */
.dashboard h1 { font-size:1.5rem; margin:.5rem 0; }
.dashboard h1 a { color:inherit; }
@@ -107,3 +106,15 @@ details.explain { margin:1rem 0; }
.picks li { border-bottom:1px solid var(--rule); padding:.5rem 0; }
.budget meter { width:60%; max-width:14rem; height:.8rem; vertical-align:middle; margin-right:.5rem; }
.table-filter { margin:.5rem 0; padding:.3rem; width:20rem; max-width:100%; }
/* step 6: jobs and stats */
.sparklines { display:grid; grid-template-columns:repeat(auto-fit,minmax(16rem,1fr)); gap:1rem 1.5rem; margin:1rem 0; }
.spark-figure { margin:0; min-width:0; } .spark-figure figcaption { font-size:.9rem; margin-bottom:.2rem; }
.spark { display:block; width:100%; max-width:26rem; height:auto; border-bottom:1px solid var(--rule); }
.spark .s0 { fill:var(--good); } .spark .s1 { fill:var(--loved); } .spark .s2 { fill:var(--down); } .spark .s3 { fill:var(--accent); } .spark .s4 { fill:var(--muted); } .spark .s5 { fill:var(--fg); }
.spark .line { stroke:var(--accent); }
.spark-axis { display:flex; justify-content:space-between; max-width:26rem; font-size:.75rem; }
.spark-legend { list-style:none; padding:0; margin:.3rem 0 0; display:flex; flex-wrap:wrap; gap:.2rem .8rem; font-size:.8rem; }
.spark-legend svg { vertical-align:middle; }
.job-cards form.inline { margin:.4rem 0 0; } .job-cards label { display:inline-grid; }
td.message,dd.message { overflow-wrap:anywhere; max-width:32rem; }
pre.journal { max-height:40rem; }