Archilyzer · Source

archilyzer

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

commit 7b603d8cf8dbb6d0a64c1e976f630d849e3967ed
parent 2907e08946920fb94b916d7d366a250c65208fc7
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Fri, 25 Sep 2026 21:30:59 -0400

editor: the parent mark beside adminTitle, and app/icon.svg as the favicon (brand S1)

The sidebar puts the mark before the title block; adminTitle is free text,
so it is not split, and the mark sits outside the title link. app/icon.svg is
markSvg(ICON_PALETTES.archilyzer), held to it byte for byte by
app/lib/brandIcon.test.ts; the editor had no favicon before. branding.spec
checks the mark, the link's name and the linked icon.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Diffstat:
Aeditor/app/icon.svg | 1+
Meditor/app/layout.tsx | 28+++++++++++++++++++---------
Aeditor/app/lib/brandIcon.test.ts | 18++++++++++++++++++
Meditor/e2e/branding.spec.ts | 29+++++++++++++++++++++++++++++
4 files changed, 67 insertions(+), 9 deletions(-)

diff --git a/editor/app/icon.svg b/editor/app/icon.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><rect width="512" height="512" rx="112" fill="#151b20"/><rect x="112" y="128" width="288" height="44" rx="22" fill="#3f4c56"/><polygon points="112,210 172,234 112,258" fill="#e7edf1"/><rect x="188" y="212" width="212" height="44" rx="22" fill="#e7edf1"/><rect x="112" y="296" width="232" height="44" rx="22" fill="#3f4c56"/><rect x="112" y="380" width="152" height="44" rx="22" fill="#3f4c56"/></svg> diff --git a/editor/app/layout.tsx b/editor/app/layout.tsx @@ -12,6 +12,8 @@ import { ThemeScript } from "yt-dlp-transcript-common/components/ThemeScript"; import { ThemeProvider } from "yt-dlp-transcript-common/components/ThemeProvider"; import { ThemeToggle } from "yt-dlp-transcript-common/components/ThemeToggle"; import { ThemeMenu } from "yt-dlp-transcript-common/components/ThemeMenu"; +import { BrandMark } from "yt-dlp-transcript-common/components/BrandMark"; +import { ICON_PALETTES } from "yt-dlp-transcript-common/lib/brand"; import { AppFrame } from "./components/AppFrame"; import { AutoRefresh } from "./components/AutoRefresh"; import { CommandPalette } from "./components/CommandPalette"; @@ -131,15 +133,23 @@ export default async function RootLayout({ sidebar={ <aside className="md:w-56 md:shrink-0 md:sticky md:top-0 md:self-start md:h-screen md:overflow-y-auto border-b md:border-b-0 md:border-r border-border bg-card flex flex-col"> <div className="px-3 py-2.5 md:py-3 md:border-b md:border-border flex items-start justify-between gap-2"> - <div className="min-w-0"> - <Link - href="/" - className="font-display text-base font-semibold tracking-tight block leading-tight" - > - {headerLabel} - </Link> - <div className="font-mono text-[10px] uppercase tracking-[0.18em] text-muted-foreground mt-0.5"> - editor + <div className="min-w-0 flex items-center gap-2.5"> + {/* The parent mark, beside the operator's title — which is + NOT split: adminTitle is free text, not a wordmark. */} + <BrandMark + palette={ICON_PALETTES.archilyzer} + className="size-8 shrink-0" + /> + <div className="min-w-0"> + <Link + href="/" + className="font-display text-base font-semibold tracking-tight block leading-tight" + > + {headerLabel} + </Link> + <div className="font-mono text-[10px] uppercase tracking-[0.18em] text-muted-foreground mt-0.5"> + editor + </div> </div> </div> <div className="flex items-center gap-1.5 shrink-0"> diff --git a/editor/app/lib/brandIcon.test.ts b/editor/app/lib/brandIcon.test.ts @@ -0,0 +1,18 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { ICON_PALETTES, markSvg } from "yt-dlp-transcript-common/lib/brand"; + +// editor/app/icon.svg is the editor's favicon (Next's app/icon.svg file +// convention links it from every page). It is a static file because the +// editor's icon never changes per install — it is the parent mark — but it +// must stay the mark: markSvg is the geometry, this file a copy of it. +// Regenerate with markSvg(ICON_PALETTES.archilyzer) if this fails. +test("app/icon.svg is markSvg(ICON_PALETTES.archilyzer), byte for byte", () => { + const file = fileURLToPath(new URL("../icon.svg", import.meta.url)); + assert.equal( + readFileSync(file, "utf8").trimEnd(), + markSvg(ICON_PALETTES.archilyzer), + ); +}); diff --git a/editor/e2e/branding.spec.ts b/editor/e2e/branding.spec.ts @@ -35,3 +35,32 @@ test("reflects sidebar header after saving via the settings form", async ({ page.locator("aside").getByRole("link", { name: "Saved Admin" }), ).toBeVisible(); }); + +test("the sidebar carries the parent mark beside adminTitle, and the favicon is the mark", async ({ + page, + request, +}) => { + await writeSettings({ + adminTitle: "Cypress HQ", + maxTranscriptPageBytes: 8388608, + sleepBetweenDownloadsSeconds: 0, + }); + await page.goto("/"); + const aside = page.locator("aside"); + // Decorative, and NOT inside the title link: its name stays adminTitle. + const mark = aside.locator("svg[data-brand-mark]"); + await expect(mark).toHaveCount(1); + await expect(mark).toHaveAttribute("aria-hidden", "true"); + await expect(mark).toBeVisible(); + await expect( + aside.getByRole("link", { name: "Cypress HQ", exact: true }), + ).toBeVisible(); + // app/icon.svg (Next's file convention) is linked from <head>. + const href = await page + .locator('head link[rel="icon"][type="image/svg+xml"]') + .getAttribute("href"); + expect(href).toMatch(/^\/icon\.svg/); + const svg = await (await request.get(href as string)).text(); + expect(svg).toContain('fill="#151b20"'); + expect(svg).toContain('fill="#e7edf1"'); +});