heartwood every commit a ring

Refresh home cards, backfill changelog, align footer with status

7f80119b by Isaac Bythewood · 2 days ago

Refresh home cards, backfill changelog, align footer with status

Drop the "capabilities" eyebrow on the home page and rewrite each card
for accuracy (the PDF report no longer uses headless Chromium) and
brevity. Add a 2026-05-09 changelog entry for the embedded Typst PDF
swap, the routes/ restructure, and the mobile nav fix. Bring the footer
into line with the status app: rename the column to // Operator, add a
Source link to // Pages, and replace the brand-logo back-link in the
footer-bar with the same GitHub icon status uses (DB-IP attribution
stays).
modified templates/base.html
@@ -96,10 +96,11 @@            <li class="mb-2"><a href="/" class="link-footer">Home</a></li>            <li class="mb-2"><a href="/documentation" class="link-footer">Documentation</a></li>            <li class="mb-2"><a href="/changelog" class="link-footer">Changelog</a></li>            <li class="mb-2"><a href="https://github.com/overshard/analytics" class="link-footer" target="_blank">Source</a></li>          </ul>        </div>        <div class="col-6 col-lg-2">          <div class="h5 mb-3">// Account</div>          <div class="h5 mb-3">// Operator</div>          <ul class="list-unstyled">            {% if user.is_authenticated %}            <li class="mb-2"><a href="/properties" class="link-footer">Properties</a></li>
@@ -124,13 +125,9 @@          <small>&copy; {{ now.year }} Isaac Bythewood · Some rights reserved · <a href="https://db-ip.com" class="link-footer" target="_blank" rel="noopener">IP Geolocation by DB-IP</a></small>        </div>        <div class="col-sm-6 d-flex justify-content-center justify-content-sm-end py-3 py-sm-0">          <a href="https://isaacbythewood.com/" target="_blank" class="logo logo-sm ms-3" aria-label="Isaac Bythewood">            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">              <rect x="6"  y="38" width="10" height="22" rx="1.5" fill="#6b9e78"/>              <rect x="20" y="28" width="10" height="32" rx="1.5" fill="#6b9e78"/>              <rect x="34" y="18" width="10" height="42" rx="1.5" fill="#6b9e78"/>              <rect x="48" y="8"  width="10" height="52" rx="1.5" fill="#6b9e78"/>              <rect x="48" y="8"  width="10" height="6"  rx="1.5" fill="#c9a84c"/>          <a href="https://github.com/overshard/analytics" target="_blank" class="footer-bar-link" aria-label="GitHub">            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">              <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>            </svg>          </a>        </div>
modified templates/pages/changelog.html
@@ -34,15 +34,27 @@  <div class="row mt-4">    <div class="col-lg-8 offset-lg-2">      <div class="changelog-entry">        <div class="changelog-date">2026-05-09</div>        <ul>          <li>Replace the dashboard PDF pipeline with the embedded Typst compiler (<code>typst</code> + <code>typst-pdf</code> + <code>typst-kit</code>). No chromium subprocess, no temp files; fonts are searched once at startup and shared across renders</li>          <li>Restructure the axum server into per-feature route modules under <code>src/routes/</code>, with <code>AppState</code>/router assembly split into <code>src/app.rs</code> and the standard render context centralized in <code>src/render.rs</code></li>          <li>Fix several dashboard issues exposed by the restructure (404 handling, breadcrumb edge cases)</li>          <li>Expand <code>make clean</code> to wipe <code>target/</code>, frontend deps, runtime data, and the cached topojson</li>          <li>Fix the mobile nav menu being clipped by the fixed navbar height</li>        </ul>      </div>      <div class="changelog-entry">        <div class="changelog-date">2026-05-07</div>        <ul>          <li>Rewrite the entire backend from Django to a single statically-linked axum binary in Rust. sqlx + SQLite directly, minijinja for templates, no more Gunicorn or Uvicorn or separate scheduler process. The collector endpoint and embed snippet are unchanged, so existing tracked sites keep reporting without edits</li>          <li>Drop the multi-user accounts model. Auth is now a single operator password from <code>.env</code> with signed-cookie sessions and <code>SameSite=Strict</code> instead of CSRF tokens</li>          <li>Route bot traffic into a dedicated <code>bot_events</code> table at write time so human dashboard queries never have to filter it</li>          <li>Auto-download the GeoIP database and ua-parser regexes on boot when missing or stale; the server still boots and serves dashboards if either download fails</li>          <li>Auto-download the GeoIP database and ua-parser regexes on boot when missing or stale. The server still boots and serves dashboards if either download fails</li>          <li>Add an <code>analytics migrate</code> subcommand that imports an existing Django SQLite into the new schema while preserving property UUIDs, so embedded snippets keep working after the cutover</li>          <li>Add an in-memory dashboard cache (5-minute TTL, keyed by property + date range + filter), bypassed for PDF and markdown exports so they always reflect the live data</li>          <li>Add a stable <code>/static/collector.js</code> route so embed snippets don't break when Vite re-hashes the collector bundle</li>        </ul>      </div>
@@ -65,7 +77,7 @@      <div class="changelog-entry">        <div class="changelog-date">2026-04-18</div>        <ul>          <li>Replace the Playwright PDF pipeline with a headless Chromium subprocess wrapper and move the Docker base image to Alpine — smaller image, faster cold starts</li>          <li>Replace the Playwright PDF pipeline with a headless Chromium subprocess wrapper and move the Docker base image to Alpine: smaller image, faster cold starts</li>          <li>Centralize SQLite settings and expand PRAGMA tuning (WAL, synchronous, cache, mmap, temp_store)</li>          <li>Speed up the property dashboard, split the property view, and surface bot traffic separately</li>          <li>Swap the Exim relay for a Python direct-to-MX email backend</li>
@@ -75,10 +87,10 @@      <div class="changelog-entry">        <div class="changelog-date">2026-04-17</div>        <ul>          <li>Redesign the whole dashboard with a warm-earth palette — mossy forest green and amber on deep charcoal, Monaspace Argon throughout, aligned to the <a href="https://status.bythewood.me/">status</a> app</li>          <li>Redesign the whole dashboard with a warm-earth palette: mossy forest green and amber on deep charcoal, Monaspace Argon throughout, aligned to the <a href="https://status.bythewood.me/">status</a> app</li>          <li>Rework the property dashboard with metric tiles, period-over-period deltas, chart panels, and compact ranked top-lists instead of Bootstrap list groups</li>          <li>Rework the properties listing page with search, live-signal tiles, and a dense row layout</li>          <li>Swap the 📊 emoji favicon for a themed SVG bar-chart mark matching the in-app logo</li>          <li>Swap the bar-chart emoji favicon for a themed SVG mark matching the in-app logo</li>          <li>Add a <code>/documentation</code> link into the main nav so the collector snippet docs are easier to find</li>          <li>Restyle the collector-tag modal and accordion docs as terminal-style code blocks</li>        </ul>
@@ -105,7 +117,7 @@      <div class="changelog-entry">        <div class="changelog-date">2022-07-26</div>        <ul>          <li>Add URL property filtering — click any top-page row to scope the whole dashboard</li>          <li>Add URL property filtering: click any top-page row to scope the whole dashboard</li>        </ul>      </div>
@@ -119,14 +131,14 @@      <div class="changelog-entry">        <div class="changelog-date">2022-06-18</div>        <ul>          <li>Rename all "tracking" terminology to "collecting" — we're not spyware</li>          <li>Rename all "tracking" terminology to "collecting" (we're not spyware)</li>        </ul>      </div>      <div class="changelog-entry">        <div class="changelog-date">2022-06-05</div>        <ul>          <li>Add PDF report generator — hit <code>?report</code> on any property page to export</li>          <li>Add PDF report generator: hit <code>?report</code> on any property page to export</li>        </ul>      </div>
@@ -146,7 +158,7 @@          <li>Add UTM query-param collection (source, medium, campaign)</li>          <li>Add platform doughnut graph</li>          <li>Add date-range selection with presets and a custom picker</li>          <li>Add custom event cards — pin any custom event as a headline metric</li>          <li>Add custom event cards: pin any custom event as a headline metric</li>        </ul>      </div>
modified templates/pages/home.html
@@ -30,7 +30,7 @@          Page views, clicks, scrolls, sessions, and custom events. GeoIP maps,          UTM attribution, device and browser breakdowns, period-over-period          comparisons, and PDF reports. Collected through a single POST          endpoint you control — no third-party script, no hidden trackers,          endpoint you control. No third-party script, no hidden trackers,          no data leaving your infrastructure.        </p>        <div class="hero-ctas">
@@ -89,7 +89,6 @@<section class="container my-5 py-4">  <div class="row mb-4">    <div class="col-12">      <div class="section-label mb-2">capabilities</div>      <h2 class="h3 text-white fw-bolder" style="letter-spacing: -0.01em;">        Everything your marketing and product team needs in one dashboard.      </h2>
@@ -101,10 +100,9 @@        <div class="feature-label">collection</div>        <div class="feature-title">One-line site tag</div>        <p class="feature-desc">          Drop a tiny async script in your <code>&lt;head&gt;</code>. It fires          session_start, page_view, click, scroll, and page_leave events to          a single POST endpoint. Custom events push onto the queue from any          frontend code.          Drop a tiny async script in your <code>&lt;head&gt;</code>. It posts          session_start, page_view, click, scroll, and page_leave to one          endpoint. Custom events queue from any frontend code.        </p>      </div>    </div>
@@ -113,8 +111,8 @@        <div class="feature-label">attribution</div>        <div class="feature-title">UTM + referrer tracking</div>        <p class="feature-desc">          Break traffic down by source, medium, and campaign. Top referrers,          top pages, and top custom events are ranked in the dashboard with          Slice traffic by source, medium, and campaign. Top referrers,          pages, and custom events rank in the dashboard with          period-over-period deltas baked in.        </p>      </div>
@@ -124,9 +122,9 @@        <div class="feature-label">geo + device</div>        <div class="feature-title">Maps, browsers, platforms</div>        <p class="feature-desc">          A Natural Earth world map with click-through admin-1 drill-down,          plus doughnut breakdowns for device, browser, platform, and          screen size. GeoIP runs locally, IPs never leave your host.          A Natural Earth world map with click-through admin-1 drill-down.          Doughnut breakdowns for device, browser, platform, and screen          size. GeoIP runs locally, IPs never leave your host.        </p>      </div>    </div>
@@ -135,9 +133,9 @@        <div class="feature-label">reports</div>        <div class="feature-title">Dashboard + PDF export</div>        <p class="feature-desc">          Flip any date range to a headless-Chromium PDF with one click.          Custom cards let you pin bespoke metrics. Share a property publicly          with one toggle — no login required for viewers.          Flip any date range to PDF or markdown with one click, rendered          in-process via embedded Typst (no chromium subprocess). Custom          cards pin bespoke metrics. A public toggle shares without a login.        </p>      </div>    </div>
@@ -147,10 +145,10 @@        <div class="feature-title">Typed schema, JSON extras</div>        <p class="feature-desc">          Hot fields (url, referrer, geo, UA, UTMs) live in typed columns          for fast queries. Anything custom lands in an <code>extra</code>          JSON column, no migration required, queryable with SQLite JSON1          (json_extract). Bot traffic is routed to a separate table so          human dashboards never have to filter it.          for fast queries. Custom keys land in an <code>extra</code> JSON          column, queryable via SQLite JSON1 (json_extract). Bot traffic          routes to a separate table at write time so human dashboards          never have to filter it.        </p>      </div>    </div>
@@ -159,9 +157,8 @@        <div class="feature-label">stack</div>        <div class="feature-title">Built in Rust, ultralight</div>        <p class="feature-desc">          A single statically linked Rust binary plus SQLite. Ultra          performant and very resource friendly, the whole stack runs as a          tiny Docker image behind your own reverse proxy. Your domain,          One statically linked Rust binary plus SQLite, shipped as a tiny          Alpine Docker image behind your own reverse proxy. Your domain,          your retention policy. No vendor, no pixel, no lock-in.        </p>      </div>