Archilyzer · Source

archilyzer

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

commit 0fd627c306a107f1bdbc620027ff517617282e60
parent ef987279bed05be673c522ab14a6b222740ba2fa
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Tue, 29 Sep 2026 20:47:30 -0400

sites: one checkbox, "List on the Archilyzer homepage and hub", on by default; saveSiteAction carries the opt-out into the Site it rebuilds, so a save of any other field keeps it

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

Diffstat:
Meditor/app/sites/actions.ts | 5+++++
Meditor/app/sites/components/SiteForm.tsx | 16++++++++++++++++
Meditor/e2e/helpers.ts | 1+
Meditor/e2e/sites-crud.spec.ts | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 78 insertions(+), 0 deletions(-)

diff --git a/editor/app/sites/actions.ts b/editor/app/sites/actions.ts @@ -92,6 +92,9 @@ export async function saveSiteAction( }; } const siteUrl = parseSiteUrl(siteUrlRaw); + // Listed on the homepage and hub by default: the same opt-out idiom as + // archives below (an unchecked box sends no key → persisted as false). + const listed = formData.get("listed") === "on"; // Hub parent (per-site override of the family default) + PWA opt-in. const hubUrlRaw = String(formData.get("hubUrl") ?? "").trim(); @@ -217,6 +220,8 @@ export async function saveSiteAction( ...(accent ? { accent } : {}), ...(cloudflareProject ? { cloudflareProject } : {}), ...(siteUrl ? { siteUrl } : {}), + // The Site is rebuilt from the form: a key missing here is dropped on save. + ...(listed ? {} : { listed: false }), ...(hubUrl ? { hubUrl } : {}), ...(pwa ? { pwa: true } : {}), ...(archives ? {} : { archives: false }), diff --git a/editor/app/sites/components/SiteForm.tsx b/editor/app/sites/components/SiteForm.tsx @@ -327,6 +327,22 @@ export function SiteForm({ initial, channels, allSites, isNew }: Props) { defaultValue={initial.siteUrl ?? ""} hint="Absolute URL this site is served at (e.g. https://jeralyzer.com). Used so other sites can link to it in their footer. Leave blank to omit this site from cross-site lists." /> + <label className="flex items-center gap-2 text-sm"> + <input + type="checkbox" + name="listed" + defaultChecked={initial.listed !== false} + className="accent-brand" + /> + List on the Archilyzer homepage and hub + </label> + <p className="-mt-2 text-xs text-muted-foreground"> + On by default. Turn off to leave this site out of the homepage (its + card, chart and stats), the hub (its members, search, corpus.json and + llms.txt) and every other site&apos;s footer, and to count its own + channels in none of the published totals. The site still builds and + deploys as before, at its own URL. + </p> <Field label="Hub URL" name="hubUrl" diff --git a/editor/e2e/helpers.ts b/editor/e2e/helpers.ts @@ -152,6 +152,7 @@ export async function writeSite( ? { cloudflareProject: site.cloudflareProject } : {}), ...(site.siteUrl ? { siteUrl: site.siteUrl } : {}), + ...(site.listed === false ? { listed: false } : {}), ...(site.relatedSites ? { relatedSites: site.relatedSites } : {}), }; await writeFile( diff --git a/editor/e2e/sites-crud.spec.ts b/editor/e2e/sites-crud.spec.ts @@ -345,6 +345,62 @@ test("archives + per-video transcript downloads opt-outs round-trip", async ({ await expect(downloads).toBeChecked(); }); +test("the listed opt-out round-trips, and a save of another field keeps it", async ({ + page, +}) => { + await resetData("empty"); + // An invented fixture id: no real site is named in a test. + await writeSite("fixture-unlisted", { + siteTitle: "Unlisted Fixture", + siteUrl: "https://fixture-unlisted.example", + listed: false, + }); + + type ListedSiteFile = { siteTitle?: string; listed?: boolean }; + const file = "test-transcripts/sites/fixture-unlisted/site.json"; + const listed = page.getByRole("checkbox", { + name: "List on the Archilyzer homepage and hub", + }); + const save = async () => { + await page.getByRole("button", { name: /save site/i }).click(); + await expect( + page.getByRole("status").filter({ hasText: "Saved" }), + ).toBeVisible(); + }; + + // `false` on disk opens unticked, and a save that changes only the title + // keeps it: the action rebuilds the site from the form. + await page.goto("/sites/fixture-unlisted"); + await expect(listed).not.toBeChecked(); + await page.getByLabel(/site title/i).fill("Unlisted Fixture Renamed"); + await save(); + await expect(async () => { + const site = await readJson<ListedSiteFile>(file); + expect(site.siteTitle).toBe("Unlisted Fixture Renamed"); + expect(site.listed).toBe(false); + }).toPass({ timeout: 10_000 }); + + // Ticking it removes the key — listed is the default. + await page.goto("/sites/fixture-unlisted"); + await expect(listed).not.toBeChecked(); + await listed.check(); + await save(); + await expect(async () => { + const site = await readJson<ListedSiteFile>(file); + expect("listed" in site).toBe(false); + }).toPass({ timeout: 10_000 }); + + // And unticking writes the explicit false again. + await page.goto("/sites/fixture-unlisted"); + await expect(listed).toBeChecked(); + await listed.uncheck(); + await save(); + await expect(async () => { + const site = await readJson<ListedSiteFile>(file); + expect(site.listed).toBe(false); + }).toPass({ timeout: 10_000 }); +}); + test("the hub form's per-video transcript downloads opt-out round-trips to homepage.json", async ({ page, }) => {