From ed5ff36f8bfe10fd0ed344b9cdf3e0df47754db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20N=C3=A4geli?= Date: Mon, 12 Oct 2026 19:41:17 +0200 Subject: [PATCH] Group the analytics cards by what they count The cards counted page views, the crawlers' card requests, side by side without saying so - a lambda with 79 requests showed 13 devices next to 50 crawler visits, which read as figures that did not add up. They are now two groups under the names of the figures at the top: page views (pages, sites, countries, devices, browsers, systems) and requests (what was requested by path, crawlers). Devices joined the browsers and systems as a third pill, so the groups pair up. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014qQBMy3YGyxKt7EzfanaSK --- CLAUDE.md | 5 + README.md | 7 ++ src/Frontend/src/control/AnalyticsTab.tsx | 119 ++++++++++++++++------ 3 files changed, 98 insertions(+), 33 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index bfe381d..eff0e13 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -485,6 +485,11 @@ section. what the columns below show, the breakdowns are ranked bars, the devices one bar of shares; every chart has its values as a table one click away. It does not poll - it reads again when the page is looked at again. +- **The breakdowns are grouped by what they count - decided by the owner.** + Page views (pages, sites, countries, devices, browsers, systems) under one + heading, requests (what was requested, crawlers) under another: the two can + be far apart, and a card counting one beside a card counting the other + read as figures that did not add up. - This is said in the README (Analytics), `/docs` (Who visits it), `platform_guide` (`analytics`), the tool's description and the privacy page - keep them the same. diff --git a/README.md b/README.md index 66642ae..850b811 100644 --- a/README.md +++ b/README.md @@ -839,6 +839,13 @@ feature's preview. believed from an agent that is Chromium's, and only where they name a browser or system that is known, so nobody writes words of their own into an owner's figures (`VisitorClassifier`). +- **Two groups in the editor, by what they count.** The pages, the referring + sites, the countries, the devices, the browsers and the systems are counted + in page views, under that heading; what was requested by path and the + crawlers in requests, under theirs. The two can be far apart - a crawler + asks for a hundred things that are no page - so a card never leaves the + reader to guess which it is. A lambda without pages people open has the + requests group only. - **Crawlers, hardened.** A program is told from a person by [crawler-user-agents](https://github.com/monperrus/crawler-user-agents) (MIT, `Resources/Crawlers`), a maintained list of about 1,500 crawlers with a diff --git a/src/Frontend/src/control/AnalyticsTab.tsx b/src/Frontend/src/control/AnalyticsTab.tsx index ff76a0d..e269f9f 100644 --- a/src/Frontend/src/control/AnalyticsTab.tsx +++ b/src/Frontend/src/control/AnalyticsTab.tsx @@ -190,35 +190,64 @@ export function AnalyticsTab({ control }: { control: Control }) { -
- - p[by] > 0)} - by={by} - mono - name={(value) => (value === '(other)' ? said.other : value)} - /> - + {/* + * In two groups by what their numbers count, since the two can be far + * apart: a person opens a page and it loads a dozen things, a crawler + * asks for a hundred that are no page at all. An app that has no pages + * people open is described by its requests alone. + */} + {by === 'views' && ( + + + p.views > 0)} + by="views" + mono + name={(value) => (value === '(other)' ? said.other : value)} + /> + + + + r.views > 0)} + by="views" + name={(value) => (value === '(other)' ? said.other : value)} + icon={} + rest={{ label: said.direct, value: Math.max(0, state.totals.views - sum(state.referrers, 'views')) }} + /> + - - r[by] > 0)} - by={by} - name={(value) => (value === '(other)' ? said.other : value)} - icon={} - rest={by === 'views' ? { label: said.direct, value: Math.max(0, state.totals.views - sum(state.referrers, 'views')) } : undefined} - /> - + + c.views > 0)} by="views" tag={tag} /> + - - c[by] > 0)} by={by} tag={tag} /> - + + + )} - - + + + (value === '(other)' ? said.other : value)} /> - + {by === 'hits' && ( + <> + + (value === '(other)' ? said.other : value)} + icon={} + /> + + + + + + + + + )} {(state.crawlers ?? []).length === 0 ? ( @@ -227,7 +256,7 @@ export function AnalyticsTab({ control }: { control: Control }) { } /> )} -
+