330fecc3ccdb04b2a7e7335d17c8eb6ce84bd6af
Deploy to VPS / deploy (push) Has been cancelled
The single-favicon upload was leaving most platforms with the wrong icon:
Android Chrome looks for 192×192, iOS for 180×180, Windows for 48×48,
HiDPI tabs for 32×32, legacy tabs for 16×16, PWA splash for 512×512 —
six different files, all from the same source image.
ONE EDITOR ACTION → SIX FILES
- POST /api/branding/favicon takes a single PNG/JPG/WebP (≥ 192×192,
ideally 512×512+) and runs it through sharp to produce:
/branding/favicon-16.png /branding/favicon-32.png
/branding/favicon-48.png /branding/favicon-180.png
/branding/favicon-192.png /branding/favicon-512.png
/branding/favicon-master.png (kept for re-generation)
- Square-ish source images get center-cropped automatically.
- Returns the variant list + soft warnings ("not square — center-cropped",
"upload at least 512×512 for retina") so the editor sees what happened.
- Validates dimensions and file type, caps at 20MB.
ROOT LAYOUT (src/app/[locale]/layout.tsx)
- Detects whether the multi-variant set exists on disk. If yes, emits
<link rel="icon" sizes="16x16 / 32x32 / 192x192">, an explicit
Apple touch link, and a Safari pinned-tab mask-icon. If no, falls
back to the legacy single faviconUrl from SiteSetting.
- Adds <link rel="manifest" href="/manifest.webmanifest"> so Android
Chrome surfaces "Add to Home Screen" properly.
PWA MANIFEST (src/app/manifest.ts)
- Auto-served at /manifest.webmanifest via Next.js's MetadataRoute.
- Pulls themeColor from SiteSetting.branding so editor changes there
cascade to the standalone-app theme.
- Lists 192 + 512 icons with both 'any' and 'maskable' purposes for
Android adaptive icons.
HQ SETTINGS UX (src/app/hq-command/dashboard/settings/page.tsx)
- New <FaviconMasterField/> at the top of the Branding tab — replaces
the old standalone Favicon ImageField. Shows a 32×32 preview, an
upload button, the generation result with each variant's preview,
and warnings when the source isn't ideal.
- The Apple Touch Icon ImageField below it stays as an override path
for editors who want a different icon on iOS (rare, but supported).
- Cache buster query string on previews so re-uploads visibly refresh
without forcing the editor to hard-reload.
NO BACKEND/DB CHANGES OUTSIDE FILE I/O.
The SiteSetting.branding row is unchanged. The favicon variants are
plain files on disk under public/branding/, served by Nginx with the
existing 5-min cache headers.
Description
No description provided
Languages
TypeScript
98.1%
JavaScript
0.9%
Dockerfile
0.4%
Shell
0.4%
CSS
0.2%