feat: synthesize brand kit from appName when brandDir unset

Adds back `services.webapp.appName` (briefly removed in the previous
commit per #100's strict policy) so existing hosts keep working
without authoring full per-host brand kits.

Resolution order:
1. If brandDir is set, use it (full brand kit wins).
2. Else synthesize: take the flake's default logo + a brand.json
   carrying appName as both name and shortName.

This is NOT a VITE_APP_NAME env shim — it threads through the brand
kit pipeline properly (brand.json read by vite-branding.ts). Hosts
that need themed chrome / their own logo upgrade to a real brandDir.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-06-10 17:13:05 +02:00
commit 8d3c43b160

View file

@ -15,9 +15,28 @@
let let
cfg = config.services.webapp; cfg = config.services.webapp;
# Synthesize a brand kit from `appName` when no explicit brandDir is
# provided: take the flake's default logo and overlay a brand.json
# carrying the host's app name. This lets existing hosts keep
# `services.webapp.appName = "Bouge"` and get "Bouge" in the manifest
# WITHOUT needing to author a full brand kit per host. Hosts that DO
# author a brand kit (logo, theme colors) supply `brandDir` directly
# and `appName` becomes informational.
synthesizedBrand = pkgs.runCommand "aio-webapp-brand-${cfg.appName}" {} ''
mkdir -p $out
cp ${cfg.flake}/branding/default/logo.png $out/logo.png
cat > $out/brand.json <<EOF
{"name":${builtins.toJSON cfg.appName},"shortName":${builtins.toJSON cfg.appName}}
EOF
'';
resolvedBrandDir =
if cfg.brandDir != null then cfg.brandDir
else synthesizedBrand;
webapp = cfg.flake.lib.mkWebapp { webapp = cfg.flake.lib.mkWebapp {
inherit pkgs; inherit pkgs;
brandDir = cfg.brandDir; brandDir = resolvedBrandDir;
extraEnv = { extraEnv = {
ELECTRON_SKIP_BINARY_DOWNLOAD = "1"; ELECTRON_SKIP_BINARY_DOWNLOAD = "1";
NODE_ENV = "production"; NODE_ENV = "production";
@ -66,15 +85,29 @@ in {
}; };
brandDir = lib.mkOption { brandDir = lib.mkOption {
type = lib.types.path; type = lib.types.nullOr lib.types.path;
default = "${cfg.flake}/branding/default"; default = null;
defaultText = lib.literalExpression ''"\${cfg.flake}/branding/default"''; example = lib.literalExpression "./../branding/cfaun";
description = '' description = ''
Path to the brand kit directory consumed by the build. Defaults Path to the brand kit directory consumed by the build. When
to the webapp flake's committed aiolabs brand. Override to a null (default), the module synthesizes a kit from `appName` +
per-host directory in server-deploy (e.g. the flake's default logo keeps existing hosts working
`./../branding/cfaun`) for per-host logo + manifest name + unchanged. Set this to a real brand kit when the host wants its
theme colors. See `branding/README.md` in the webapp repo. own logo + manifest theme. See `branding/README.md` in the
webapp repo.
'';
};
appName = lib.mkOption {
type = lib.types.str;
default = "AIO";
example = "Bouge";
description = ''
Application name shown in the PWA manifest, browser tab, and
home-screen install label. Used as the `brand.name` of a
synthesized brand kit when `brandDir` is null. When `brandDir`
is set, this option becomes informational only brand.json
wins.
''; '';
}; };