commit 02545df562a0da24327e90ad33a6f26e7110055e
parent 7982d433a32c69a43548d4a9ac8f6d0a98c45200
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Fri, 25 Sep 2026 21:27:03 -0400
export: the icons and favicon are rendered per build from the mark, lit with the site's accent (brand S1)
app/icons/[file]/route.ts and app/favicon.ico/route.ts are force-static GET
handlers (generateStaticParams over ICON_FILES, dynamicParams false), which
static export writes byte-for-byte to out/icons/<file> and out/favicon.ico.
lib/brand.ts iconPalette(): the parent mark on the hub, else the child ground
lit with resolveAccent(site.accent).dark. The committed public/icons/* and
app/favicon.ico are gone. The layout links ICON_METADATA (icon.svg first,
icon-32, 192, 512, apple). The manifest's theme_color is the dark base's
ground and its background_color the icon's ground, replacing the hex-only
parseAccent fallback (a named accent read as #2563eb), and it lists icon.svg
with sizes "any".
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
12 files changed, 74 insertions(+), 32 deletions(-)
diff --git a/export/app/favicon.ico b/export/app/favicon.ico
Binary files differ.
diff --git a/export/app/favicon.ico/route.ts b/export/app/favicon.ico/route.ts
@@ -0,0 +1,11 @@
+import { iconResponse, renderFaviconIco } from "yt-dlp-transcript-common/lib/brandIcons";
+import { iconPalette } from "../lib/brand";
+
+// /favicon.ico: the mark at 16/32/48 in one PNG-payload ICO, for the browsers
+// and crawlers that ask for it by name whatever <head> says. Static; written to
+// out/favicon.ico at build.
+export const dynamic = "force-static";
+
+export async function GET() {
+ return iconResponse(await renderFaviconIco(iconPalette()), "image/x-icon");
+}
diff --git a/export/app/icons/[file]/route.ts b/export/app/icons/[file]/route.ts
@@ -0,0 +1,27 @@
+import {
+ ICON_FILES,
+ iconResponse,
+ renderIconFile,
+} from "yt-dlp-transcript-common/lib/brandIcons";
+import { iconPalette } from "../../lib/brand";
+
+// /icons/<file>: the site's icon set, rendered from the one mark at build time
+// (common/lib/brandIcons.ts). Static: under `output: "export"` each listed file
+// is written to out/icons/<file> byte-for-byte, and nothing else resolves.
+// Never reads the request.
+export const dynamic = "force-static";
+export const dynamicParams = false;
+
+export function generateStaticParams() {
+ return ICON_FILES.map((f) => ({ file: f.file }));
+}
+
+export async function GET(
+ _req: Request,
+ { params }: { params: Promise<{ file: string }> },
+) {
+ const { file } = await params;
+ const out = await renderIconFile(file, iconPalette());
+ if (!out) return new Response("Not found", { status: 404 });
+ return iconResponse(out.body, out.contentType);
+}
diff --git a/export/app/layout.tsx b/export/app/layout.tsx
@@ -4,6 +4,7 @@ import { ThemeScript } from "yt-dlp-transcript-common/components/ThemeScript";
import { ThemeProvider } from "yt-dlp-transcript-common/components/ThemeProvider";
import { QueryProvider } from "yt-dlp-transcript-common/components/QueryProvider";
import { parseAccent, siteAccentVars } from "yt-dlp-transcript-common/lib/accent";
+import { ICON_METADATA } from "yt-dlp-transcript-common/lib/brandIcons";
import { currentSite } from "./lib/site";
import { instanceMode, shipsPwa } from "./lib/mode";
import Header from "./components/Header";
@@ -42,13 +43,9 @@ export function generateMetadata(): Metadata {
// (and registers the SW below). A dumb instance omits both — it stays
// federatable JSON, but not independently installable.
...(shipsPwa() ? { manifest: "/manifest.webmanifest" } : {}),
- icons: {
- icon: [
- { url: "/icons/icon-192.png", sizes: "192x192", type: "image/png" },
- { url: "/icons/icon-512.png", sizes: "512x512", type: "image/png" },
- ],
- apple: [{ url: "/icons/apple-touch-icon.png", sizes: "180x180" }],
- },
+ // Rendered per build by app/icons/[file]/route.ts: the site's accent, or
+ // the parent mark on the hub.
+ icons: ICON_METADATA,
appleWebApp: {
capable: true,
statusBarStyle: "black-translucent",
diff --git a/export/app/lib/brand.ts b/export/app/lib/brand.ts
@@ -0,0 +1,15 @@
+import { ICON_PALETTES, type IconPalette } from "yt-dlp-transcript-common/lib/brand";
+import { siteIconPalette } from "yt-dlp-transcript-common/lib/brandIcons";
+import { currentSite } from "./site";
+import { instanceMode } from "./mode";
+
+// The palette this build's icons and header mark are drawn in. The hub is the
+// family's own tool, so it wears the parent mark — achromatic, like the
+// homepage and the editor. Every archive site is lit with its own accent's
+// on-dark value (the favicon keeps the SITE default; the header mark follows
+// the reader's pick through --brand-mark). Server-only, like currentSite().
+export function iconPalette(): IconPalette {
+ return instanceMode() === "hub"
+ ? ICON_PALETTES.archilyzer
+ : siteIconPalette(currentSite().accent);
+}
diff --git a/export/app/manifest.ts b/export/app/manifest.ts
@@ -1,22 +1,17 @@
import type { MetadataRoute } from "next";
-import { parseAccent } from "yt-dlp-transcript-common/lib/accent";
+import { BASE_GROUNDS } from "yt-dlp-transcript-common/lib/brand";
import { currentSite } from "./lib/site";
+import { iconPalette } from "./lib/brand";
// Web App Manifest → statically emitted as /manifest.webmanifest at build time
-// (output: "export"). Per-site: name/description from currentSite(), theme_color
-// from the site's brand accent (falls back to the base brand blue). Makes each
-// deployed export site installable as a standalone PWA. Icons are the shared
-// mark under public/icons (a per-site accent-tinted icon is a later
-// refinement — theme_color already differentiates the browser chrome).
+// (output: "export"). Per-site: name/description from currentSite(). Makes each
+// deployed export site installable as a standalone PWA. The icons are this
+// build's own — the site's accent, or the parent mark on the hub — rendered by
+// app/icons/[file]/route.ts.
export const dynamic = "force-static";
-// Base brand blue (tokens.css --brand), used when a site sets no accent — the
-// export default family is base.
-const FALLBACK_THEME_COLOR = "#2563eb";
-
export default function manifest(): MetadataRoute.Manifest {
const site = currentSite();
- const themeColor = parseAccent(site.accent) ?? FALLBACK_THEME_COLOR;
return {
name: site.siteTitle,
short_name: site.headerTitle || site.siteTitle,
@@ -26,10 +21,19 @@ export default function manifest(): MetadataRoute.Manifest {
display: "standalone",
// No `orientation` lock: the archive is mostly video, and landscape is the
// right shape for the player view.
- background_color: "#0a0a0a",
- theme_color: themeColor,
+ // 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.
+ background_color: iconPalette().ground,
+ theme_color: BASE_GROUNDS.dark,
icons: [
{
+ src: "/icons/icon.svg",
+ sizes: "any",
+ type: "image/svg+xml",
+ purpose: "any",
+ },
+ {
src: "/icons/icon-192.png",
sizes: "192x192",
type: "image/png",
diff --git a/export/public/icons/apple-touch-icon.png b/export/public/icons/apple-touch-icon.png
Binary files differ.
diff --git a/export/public/icons/icon-192.png b/export/public/icons/icon-192.png
Binary files differ.
diff --git a/export/public/icons/icon-512.png b/export/public/icons/icon-512.png
Binary files differ.
diff --git a/export/public/icons/icon.svg b/export/public/icons/icon.svg
@@ -1,6 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512" role="img" aria-label="Archive">
- <rect width="512" height="512" rx="112" fill="#0c0a08"/>
- <polygon points="256,92 420,256 256,420 92,256" fill="#e3b15c"/>
- <polygon points="256,150 362,256 256,362 150,256" fill="#0c0a08"/>
- <polygon points="256,196 316,256 256,316 196,256" fill="#e3b15c"/>
-</svg>
diff --git a/export/public/icons/maskable-512.png b/export/public/icons/maskable-512.png
Binary files differ.
diff --git a/export/public/icons/maskable.svg b/export/public/icons/maskable.svg
@@ -1,6 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512" role="img" aria-label="Archive">
- <rect width="512" height="512" fill="#0c0a08"/>
- <polygon points="256,140 372,256 256,372 140,256" fill="#e3b15c"/>
- <polygon points="256,182 330,256 256,330 182,256" fill="#0c0a08"/>
- <polygon points="256,214 298,256 256,298 214,256" fill="#e3b15c"/>
-</svg>