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
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 {
inherit pkgs;
brandDir = cfg.brandDir;
brandDir = resolvedBrandDir;
extraEnv = {
ELECTRON_SKIP_BINARY_DOWNLOAD = "1";
NODE_ENV = "production";
@ -66,15 +85,29 @@ in {
};
brandDir = lib.mkOption {
type = lib.types.path;
default = "${cfg.flake}/branding/default";
defaultText = lib.literalExpression ''"\${cfg.flake}/branding/default"'';
type = lib.types.nullOr lib.types.path;
default = null;
example = lib.literalExpression "./../branding/cfaun";
description = ''
Path to the brand kit directory consumed by the build. Defaults
to the webapp flake's committed aiolabs brand. Override to a
per-host directory in server-deploy (e.g.
`./../branding/cfaun`) for per-host logo + manifest name +
theme colors. See `branding/README.md` in the webapp repo.
Path to the brand kit directory consumed by the build. When
null (default), the module synthesizes a kit from `appName` +
the flake's default logo keeps existing hosts working
unchanged. Set this to a real brand kit when the host wants its
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.
'';
};