Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit ba96514be8d7c63c3e0adca6e83ac2de9f00432e
parent 43cd4216526af04027304e0e8b345bbb19817bac
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Mon, 28 Sep 2026 23:12:54 -0400

export, common: stale reader-accent text; a named-accent site in the export suite (review L5, L10)

L5: export/globals.css, export/manifest.ts and brand.test.ts no longer speak
of a reader's accent pick; the export changelog's T1 bullet drops the Base
list that H2 removed in the same branch; the homepage changelog's e2e bullet
names what toggle.spec and growth-chart.spec test now.
L10: theme-accent.spec rewrites the fixture site to a named accent (violet)
for one test: its value on Light and on Dark, with a stored pick left in
storage and not applied; the custom-hex cases stay on the fixture.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Diffstat:
Mcommon/lib/brand.test.ts | 5++---
Mexport/CHANGELOG.md | 4++--
Mexport/app/globals.css | 2+-
Mexport/app/manifest.ts | 3++-
Mexport/e2e/theme-accent.spec.ts | 51++++++++++++++++++++++++++++++++++++++++++++++++++-
Mhomepage/CHANGELOG.md | 2+-
6 files changed, 58 insertions(+), 9 deletions(-)

diff --git a/common/lib/brand.test.ts b/common/lib/brand.test.ts @@ -103,9 +103,8 @@ test("bases and icon palettes are the plan's", () => { }); // The mark's two rules (brand.ts MIN_MARK_DIM_CONTRAST, MIN_MARK_LIT_OVER_DIM). -// A child site can be lit by any named accent — its own, Signal when it has -// none, or the one a reader picks, which the header mark follows — so the -// child dim is held against all seven. The parent mark is lit in bone; the +// A child site can be lit by any named accent — its own, or Signal when it +// has none — so the child dim is held against all seven. The parent mark is lit in bone; the // Media mark's Signal on the same slate is held in brandMedia.test.ts. const LIT_OF: ReadonlyArray<[string, string, string]> = [ ...ACCENT_IDS.map((id): [string, string, string] => [`child · ${id}`, ICON_PALETTES.child.dim, ACCENTS[id].onDark]), diff --git a/export/CHANGELOG.md b/export/CHANGELOG.md @@ -4,8 +4,8 @@ - **The charts count every transcript, once the site is rebuilt.** A transcript that arrived after its video was first indexed was missing from the charts' transcript and cue counts and from "Transcribed over time", and a video with YouTube captions alone had no transcription date. Both are counted now, and a captioned video is dated by when its captions arrived. - **A social icon that fails the check is shown as its label, and every icon paints inside its box.** The footer inlines a social link's SVG only if it passes the same check a save runs (what an icon may contain is in `SITE.md`); otherwise the link shows its label as text, at most 10rem with an ellipsis. Each icon is clipped to its own box. Needs a rebuild and deploy of each site. - **A chart's stacked bars are separated by a 2 px gap in the chart card's colour.** A stacked bar's segments were drawn touching; they now have a 2 px gap in the card's colour between them, and in high-contrast mode the system's background colour. Stacked areas keep their line in each series' colour along the top, charts of one series, line charts and side-by-side bars are unchanged. Needs a rebuild and deploy of each site. -- **Two grounds, Light and Dark, and each site in its own accent.** The third ground, the warm paper one, is gone: the header's toggle cycles System, Light and Dark, and the slide-out menu's Base list offers those three. A reader who had chosen it gets Light, before the page first paints and with no other ground on the way, and the stored choice becomes Light (the old paper theme's `archive` + `light` too). The theme menu's accent picker is gone from the header and the slide-out menu: every page wears the site's own accent (`site.json` `accent`), and a reader's stored pick from before is not read and is left in storage. Needs a rebuild and deploy of each site. -- **The header carries the operator's social links and one theme toggle, and links to the Archilyzer home in place of the sites menu.** Every site's header and the hub's now end with the social icons (the site's `socialLinks`, else `settings.json`'s; at most four: the ones marked **Show in header** when any is, else the last four) followed by the theme toggle, all 36 px keys (44 px on a touch screen) with a focus ring; the footer keeps every link, in the same keys (its icons were 20 px and turned the accent on hover; they now turn the text colour). The **Sites** dropdown and the **Hub** link are gone from the header and the slide-out menu: in their place a link, **Archilyzer**, goes to the Archilyzer home's Official Instances, in the same tab (not on the hub, which lists them itself). **Changelog** moved from the header and the menu to the footer, after Use with AI. The nav and the Archilyzer link are inline from 1024 px wide; below that they are in the slide-out menu, which now holds only them. On a narrow screen no icon is hidden: the site's name drops first and its mark stays, whenever the name does not fit beside the icons (whatever its length), and only then do the icons scroll sideways in their own box, the last one in view first. A site's `hubUrl` still loads and is no longer shown. Needs a rebuild and deploy of each site. +- **Two grounds, Light and Dark, and each site in its own accent.** The third ground, the warm paper one, is gone: the header's toggle cycles System, Light and Dark. A reader who had chosen it gets Light, before the page first paints and with no other ground on the way, and the stored choice becomes Light (the old paper theme's `archive` + `light` too). The theme menu's accent picker is gone from the header and the slide-out menu: every page wears the site's own accent (`site.json` `accent`), and a reader's stored pick from before is not read and is left in storage. Needs a rebuild and deploy of each site. +- **The header carries the operator's social links and one theme toggle, and links to the Archilyzer home in place of the sites menu.** Every site's header and the hub's now end with the social icons (the site's `socialLinks`, else `settings.json`'s; at most four: the ones marked **Show in header** when any is, else the last four) followed by the theme toggle, all 36 px keys (44 px on a touch screen) with a focus ring; the footer keeps every link, in the same keys (its icons were 20 px and turned the accent on hover; they now turn the text colour). The **Sites** dropdown and the **Hub** link are gone from the header and the slide-out menu: in their place a link, **Archilyzer**, goes to the Archilyzer home's Official Instances, in the same tab (not on the hub, which lists them itself). **Changelog** moved from the header and the menu to the footer, after Use with AI. The nav and the Archilyzer link are inline from 1024 px wide; below that they are in the slide-out menu, which now holds only them. On a narrow screen no icon is hidden: the site's name drops first and its mark stays, whenever the name does not fit beside the icons (whatever its length, and the same before and after the page's font has loaded), and only then do the icons scroll sideways in their own box, the last one in view first. The footer's icons wrap onto a second line rather than widen the page. A site's `hubUrl` still loads and is no longer shown. Needs a rebuild and deploy of each site. ## [0.10.0] - 2026-09-28 - **A video whose recheck failed shows as possibly missing rather than available.** When a video drops out of its channel's listing it is marked "Missing?" until a recheck says why. A recheck that could not reach the video — a blocked request or a network error — used to clear the mark as if the video had been found. It now leaves "Missing?" in place until a recheck actually reaches the video. Needs a rebuild and deploy of every export site. diff --git a/export/app/globals.css b/export/app/globals.css @@ -6,7 +6,7 @@ the accents live in common/styles/tokens.css; the faces in common/styles/fonts.ts. A site opens on the reader's system base in its own accent (site.json, rendered as `html[data-accent]`); the hub opens dark in - Signal. The ThemeMenu picks any base and any accent. */ + Signal. The header's toggle cycles the base; the accent is the site's. */ body { background: var(--background); diff --git a/export/app/manifest.ts b/export/app/manifest.ts @@ -23,7 +23,8 @@ export default function manifest(): MetadataRoute.Manifest { // right shape for the player view. // The splash is the icon's own ground, so the mark sits on its tile's // colour; the installed app's chrome is the dark base's ground. Neither is - // the accent: a reader may pick another, and the manifest cannot follow. + // the accent: the chrome is the ground a dark reader sees, whatever the + // site's accent. background_color: iconPalette().ground, theme_color: BASE_GROUNDS.dark, icons: [ diff --git a/export/e2e/theme-accent.spec.ts b/export/e2e/theme-accent.spec.ts @@ -1,5 +1,7 @@ +import fs from "node:fs"; +import path from "node:path"; import { test, expect, type Page } from "@playwright/test"; -import { MIN_ACCENT_CONTRAST, contrastRatio } from "../../common/lib/brand"; +import { ACCENTS, MIN_ACCENT_CONTRAST, contrastRatio } from "../../common/lib/brand"; import { resolveAccent } from "../../common/lib/accent"; import { installRoutes } from "./helpers"; @@ -134,3 +136,50 @@ test("the unapplied-search hint wears the site's accent, readable on each base", await onBase(page, "dark"); await expectAccent(FIXTURE.dark); }); + +// A site with a NAMED accent (the fixture's is a custom hex): its accent's own +// value on each base, and a reader's stored pick from before does not override +// it. The fixture site is rewritten for the test and put back after; the +// original rides along in `_e2ePristine`, which playwright.config.ts restores +// if a run dies mid-test. +test.describe("a site with a named accent", () => { + const SITE_FILE = path.resolve(process.cwd(), "e2e", "fixtures", "sites", "testsite", "site.json"); + let pristine = ""; + test.describe.configure({ mode: "serial" }); + test.beforeAll(() => { + pristine = fs.readFileSync(SITE_FILE, "utf8"); + }); + test.afterEach(() => { + fs.writeFileSync(SITE_FILE, pristine); + }); + + test("wears its accent's value on Light and on Dark, and a stored pick does not override it", async ({ + page, + }) => { + const site = JSON.parse(pristine) as Record<string, unknown>; + fs.writeFileSync( + SITE_FILE, + `${JSON.stringify({ ...site, accent: "violet", _e2ePristine: pristine }, null, 2)}\n`, + ); + await page.addInitScript((key) => { + try { + localStorage.setItem(key, "brass"); + } catch {} + }, ACCENT_KEY); + await page.emulateMedia({ colorScheme: "light" }); + await page.goto("/"); + await expect.poll(() => state(page)).toMatchObject({ + accent: "violet", + base: "light", + stored: "brass", + brand: ACCENTS.violet.onLight, + }); + await onBase(page, "dark"); + await expect.poll(() => state(page)).toMatchObject({ + accent: "violet", + base: "dark", + stored: "brass", + brand: ACCENTS.violet.onDark, + }); + }); +}); diff --git a/homepage/CHANGELOG.md b/homepage/CHANGELOG.md @@ -11,7 +11,7 @@ - **The growth chart's bands are separated by a 2 px gap in the ground's colour, where both bands can spare it.** The gap runs along a band's upper edge where another band sits on it, the same 2 px at every width (it was a 1.25 px line in the page colour). It is drawn only where both bands keep at least a pixel of their own colour, measured at right angles to the edge at the narrowest width the chart is drawn at: on a steep month, and beside the thinnest instances, the bands touch instead, so no band is ever covered, and the gap shows in stretches rather than all along. In high-contrast mode the gap is the system's background colour. The gridlines are unchanged. The `/stats` charts' stacked bars get the same 2 px gap in the chart panel's colour; their stacked areas keep their coloured top lines. - **Larger social links, with a focus ring.** Each icon, in the header and the footer, is a 36 px target around its 20 px glyph (44 px on a touch screen), in the muted text colour and the text colour on hover; the footer's were 20 px, in the faint colour, with no ring. Keyboard focus draws a 2 px ring in the accent, and in high-contrast mode the browser's own focus outline. An icon of two or more colours keeps its colours, and every icon paints inside its own box. A stored icon that fails the check a save runs is shown as its label (at most 10rem, with an ellipsis) instead. - **Tab no longer stops on the header's scrolling boxes in Firefox.** When the social icons' box or the nav's rule under the header overflows (a screen under about 300 px, or a large text size), Firefox made it a tab stop with no name of its own; the icons and links inside are the stops now, and each scrolls into view as it takes focus. -- **The e2e no longer reads the checkout's `settings.json` or `homepage.json`.** Its dev server reads `e2e/.e2e-settings.json` (`SETTINGS_FILE`), written by `e2e/fixture-social.ts`: three synthetic icons (a gradient with an outline, one colour, and a two-colour disc pasted with only its size), put through the same check a save runs; and `SITES_DIR` points at an empty directory. `e2e/social.spec.ts` covers the header and footer rows (at every width, with 1, 3 and 4 links, both pointers; the scroll fallback; hostile stored icons that must neither run nor fetch), `e2e/toggle.spec.ts` the theme toggle and the pinned accent, `e2e/svg-vectors.spec.ts` that every accepted icon stays inside its `<svg>` in a real parse, `e2e/growth-chart.spec.ts` the chart's lines, and `e2e/instance-wordmark.spec.ts` the cards' names; specs change the ground through one helper, `chooseTheme` (`e2e/helpers.ts`). +- **The e2e no longer reads the checkout's `settings.json` or `homepage.json`.** Its dev server reads `e2e/.e2e-settings.json` (`SETTINGS_FILE`), written by `e2e/fixture-social.ts`: three synthetic icons (a gradient with an outline, one colour, and a two-colour disc pasted with only its size), put through the same check a save runs; and `SITES_DIR` points at an empty directory. `e2e/social.spec.ts` covers the header and footer rows (at every width, with 1, 3 and 4 links, both pointers; the scroll fallback; hostile stored icons that must neither run nor fetch), `e2e/toggle.spec.ts` the theme toggle and that a stored accent is ignored, `e2e/svg-vectors.spec.ts` that every accepted icon stays inside its `<svg>` in a real parse, `e2e/growth-chart.spec.ts` the chart's gaps and that it paints its true peak and every band, and `e2e/instance-wordmark.spec.ts` the cards' names; specs change the ground through one helper, `chooseTheme` (`e2e/helpers.ts`). - **A site's card counts every transcript, and never shows 0 channels while it serves recordings.** A transcript that arrived after its video was first indexed, or a video with YouTube captions alone, could be left out of the family's numbers: one site served 1,889 recordings and its card said 0 transcripts, 0 channels and 0 hours. Such transcripts are counted now — in the card, the family totals and the archive-growth chart — and one with no transcription date is left off only what is placed by that date: the charts by transcription date, "this month" and the recent list. The official-instance figures on the hub move with them. - **The source is on the site, with its history: `/source/`.** A new **Source** page (and nav entry) gives `git clone https://archilyzer.pages.dev/source/archilyzer.git`, a read-only mirror of the main branch regenerated with every deploy, with its head, the private commit it reflects, a link to browse every file raw at `/source/tree/`, and the tarball with its size and sha256. Commit ids differ from the private repository's, because machine paths are scrubbed on the way out, and the page says so. A build without a published source says "No source published in this build." instead of offering a clone. The Downloads tarball is now regenerated by every build (its commit is the mirror's), and the page points at the mirror for history. The docs that said there is no public repository (*Install*, the FAQ, *What is Archilyzer*) now say how to clone. Below `md` the header's nav drops to its own row, as it did below `sm`, because five labels no longer fit beside the wordmark. `_headers` serves the raw tree as plain text. - **The docs' *Building several sites at once* page says what Build all does.** It called the container pipeline opt-in, turned on in the settings. Build all sites builds every site in parallel in containers whenever a container engine is available, and one after another when none is; there is nothing to switch on.