swarm-ui: apply the operator's stylix theme, same as the dashboard already does
mara: "swarm dash is in my own colors, but tab colors on swarm ui are the default catpucchin one." Root cause: hive-c0re/theme.nix's stylix overlay only ever wrote a themed colors.css onto the dashboard/agent frontend subtrees -- swarm-ui, served from its own separate package, was never in scope. Extracts the stylix-detection + colors.css-generation logic (previously inline in theme.nix) into a shared nix/host-modules/stylix-theme.nix, imported by both theme.nix and swarm-ui.nix -- one source instead of a second copy that has to agree by inspection. swarm-ui.nix gains its own themedPackage overlay (same shape as theme.nix's themedFrontend: copy the package, overwrite static/colors.css) and serves that instead of cfg.package directly when stylix is active; a clean passthrough otherwise. Verified with a throwaway nixosSystem eval (this repo's own flake checks do not exercise gateway-module wiring) confirming the unthemed path resolves cfg.package unchanged.
This commit is contained in:
parent
2e06957b32
commit
2ecf842a1b
3 changed files with 87 additions and 28 deletions
|
|
@ -7,6 +7,10 @@
|
||||||
# from the 16 base16 slots (see docs/web-ui/css-vars.md). The guarded
|
# from the 16 base16 slots (see docs/web-ui/css-vars.md). The guarded
|
||||||
# access makes this a clean no-op when stylix isn't imported into the
|
# access makes this a clean no-op when stylix isn't imported into the
|
||||||
# host config. Exposed as the read-only `c0re.servedFrontend` option.
|
# host config. Exposed as the read-only `c0re.servedFrontend` option.
|
||||||
|
#
|
||||||
|
# Detection + CSS-generation itself lives in `../stylix-theme.nix`
|
||||||
|
# (shared with `swarm-ui.nix`) — this file is the c0re-specific overlay
|
||||||
|
# consumer (which subtrees, which option).
|
||||||
{
|
{
|
||||||
pkgs,
|
pkgs,
|
||||||
lib,
|
lib,
|
||||||
|
|
@ -15,33 +19,8 @@
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.services.hyperhive.c0re;
|
cfg = config.services.hyperhive.c0re;
|
||||||
stylixThemeColors =
|
stylixTheme = import ../stylix-theme.nix { inherit lib config pkgs; };
|
||||||
if (config.stylix.enable or false) && ((config.lib.stylix or { }) ? colors) then
|
inherit (stylixTheme) stylixThemeColors themedColorsCss;
|
||||||
config.lib.stylix.colors.withHashtag
|
|
||||||
else
|
|
||||||
null;
|
|
||||||
themedColorsCss =
|
|
||||||
c:
|
|
||||||
pkgs.writeText "hyperhive-colors.css" ''
|
|
||||||
:root {
|
|
||||||
--base00: ${c.base00};
|
|
||||||
--base01: ${c.base01};
|
|
||||||
--base02: ${c.base02};
|
|
||||||
--base03: ${c.base03};
|
|
||||||
--base04: ${c.base04};
|
|
||||||
--base05: ${c.base05};
|
|
||||||
--base06: ${c.base06};
|
|
||||||
--base07: ${c.base07};
|
|
||||||
--base08: ${c.base08};
|
|
||||||
--base09: ${c.base09};
|
|
||||||
--base0A: ${c.base0A};
|
|
||||||
--base0B: ${c.base0B};
|
|
||||||
--base0C: ${c.base0C};
|
|
||||||
--base0D: ${c.base0D};
|
|
||||||
--base0E: ${c.base0E};
|
|
||||||
--base0F: ${c.base0F};
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
# Overlay the generated colors.css onto both dist subtrees. Both the
|
# Overlay the generated colors.css onto both dist subtrees. Both the
|
||||||
# dashboard (served by hive-c0re via HIVE_STATIC_DIR) and the agent UIs
|
# dashboard (served by hive-c0re via HIVE_STATIC_DIR) and the agent UIs
|
||||||
# (served by the gateway from HIVE_AGENT_FRONTEND_DIR — static files
|
# (served by the gateway from HIVE_AGENT_FRONTEND_DIR — static files
|
||||||
|
|
|
||||||
59
nix/host-modules/stylix-theme.nix
Normal file
59
nix/host-modules/stylix-theme.nix
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
# Shared stylix theming helper: detects an active stylix palette on the
|
||||||
|
# host and renders it as a base16 `colors.css` overlay file. `colors.css`
|
||||||
|
# is the entire theme-swap contract for every frontend bundle in this
|
||||||
|
# project (`theme.css` derives every semantic var from these 16 slots —
|
||||||
|
# see docs/web-ui/css-vars.md), so "apply the operator's stylix palette"
|
||||||
|
# always reduces to "write this one file over the prebuilt dist's own
|
||||||
|
# `colors.css`" — no npm/esbuild rebuild needed.
|
||||||
|
#
|
||||||
|
# Two consumers today: `hive-c0re/theme.nix` (dashboard + agent
|
||||||
|
# frontend), `swarm-ui.nix` (the swarm-level UI). Factored out here
|
||||||
|
# rather than duplicated in both — same "one source, not a copy that
|
||||||
|
# agrees by inspection" reasoning `hive-gateway/vhost-lib.nix` gives for
|
||||||
|
# its own split. Both callers `import` this directly (not published via
|
||||||
|
# an option): unlike the gateway's vhost kit, this needs no per-service
|
||||||
|
# knowledge to construct, it's a pure read of the host's own top-level
|
||||||
|
# `config` — an option indirection would add a layer with nothing to
|
||||||
|
# publish through it.
|
||||||
|
#
|
||||||
|
# Guarded access makes `stylixThemeColors` a clean `null` when stylix
|
||||||
|
# isn't imported into the host config at all, so every caller's own
|
||||||
|
# `if stylixThemeColors != null then ... else <untouched package>`
|
||||||
|
# stays a zero-op when the operator hasn't opted in.
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
stylixThemeColors =
|
||||||
|
if (config.stylix.enable or false) && ((config.lib.stylix or { }) ? colors) then
|
||||||
|
config.lib.stylix.colors.withHashtag
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
themedColorsCss =
|
||||||
|
c:
|
||||||
|
pkgs.writeText "hyperhive-colors.css" ''
|
||||||
|
:root {
|
||||||
|
--base00: ${c.base00};
|
||||||
|
--base01: ${c.base01};
|
||||||
|
--base02: ${c.base02};
|
||||||
|
--base03: ${c.base03};
|
||||||
|
--base04: ${c.base04};
|
||||||
|
--base05: ${c.base05};
|
||||||
|
--base06: ${c.base06};
|
||||||
|
--base07: ${c.base07};
|
||||||
|
--base08: ${c.base08};
|
||||||
|
--base09: ${c.base09};
|
||||||
|
--base0A: ${c.base0A};
|
||||||
|
--base0B: ${c.base0B};
|
||||||
|
--base0C: ${c.base0C};
|
||||||
|
--base0D: ${c.base0D};
|
||||||
|
--base0E: ${c.base0E};
|
||||||
|
--base0F: ${c.base0F};
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
inherit stylixThemeColors themedColorsCss;
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
{
|
{
|
||||||
lib,
|
lib,
|
||||||
config,
|
config,
|
||||||
|
pkgs,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
|
|
@ -17,6 +18,26 @@ let
|
||||||
autheliaCfg = config.services.hyperhive.swarm.authelia;
|
autheliaCfg = config.services.hyperhive.swarm.authelia;
|
||||||
controllerCfg = config.services.hyperhive.swarm.controller;
|
controllerCfg = config.services.hyperhive.swarm.controller;
|
||||||
|
|
||||||
|
# Same stylix auto-theming `hive-c0re/theme.nix` gives the dashboard —
|
||||||
|
# this UI was left out of that overlay entirely (an operator with a
|
||||||
|
# stylix-themed host saw the dashboard in their own colours but this
|
||||||
|
# UI still on the default Catppuccin palette), because nothing here
|
||||||
|
# ever wrote a themed `colors.css` over `cfg.package` before serving
|
||||||
|
# it; the c0re-side overlay only ever touched `dashboard/`/`agent/`.
|
||||||
|
# Detection + CSS-generation is shared (`./stylix-theme.nix`);
|
||||||
|
# `servedPackage` is this module's own overlay-onto-`cfg.package`
|
||||||
|
# consumer, same shape as `theme.nix`'s `themedFrontend`.
|
||||||
|
stylixTheme = import ./stylix-theme.nix { inherit lib config pkgs; };
|
||||||
|
inherit (stylixTheme) stylixThemeColors themedColorsCss;
|
||||||
|
themedPackage =
|
||||||
|
c:
|
||||||
|
pkgs.runCommand "hyperhive-swarm-ui-themed" { } ''
|
||||||
|
cp -r ${cfg.package} $out
|
||||||
|
chmod -R u+w $out
|
||||||
|
install -m644 ${themedColorsCss c} $out/static/colors.css
|
||||||
|
'';
|
||||||
|
servedPackage = if stylixThemeColors != null then themedPackage stylixThemeColors else cfg.package;
|
||||||
|
|
||||||
# Repeated verbatim by every location that should be operator-gated
|
# Repeated verbatim by every location that should be operator-gated
|
||||||
# (`/`, `/api/`, `/api/docs/`) rather than set once on the server:
|
# (`/`, `/api/`, `/api/docs/`) rather than set once on the server:
|
||||||
# nginx's `auth_request` does NOT inherit across sibling locations, so
|
# nginx's `auth_request` does NOT inherit across sibling locations, so
|
||||||
|
|
@ -170,7 +191,7 @@ in
|
||||||
extraConfig = gatewayCfg.lib.securityHeaders;
|
extraConfig = gatewayCfg.lib.securityHeaders;
|
||||||
locations = {
|
locations = {
|
||||||
"/" = {
|
"/" = {
|
||||||
root = "${cfg.package}";
|
root = "${servedPackage}";
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
${swarmAuthRequest}
|
${swarmAuthRequest}
|
||||||
# SPA: any path the bundle routes client-side is served the
|
# SPA: any path the bundle routes client-side is served the
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue