diff --git a/apps/docs/content/docs/modules/process-compose.mdx b/apps/docs/content/docs/modules/process-compose.mdx index ae84d02e..497539bb 100644 --- a/apps/docs/content/docs/modules/process-compose.mdx +++ b/apps/docs/content/docs/modules/process-compose.mdx @@ -22,3 +22,18 @@ stackpanel.apps.web.path = "apps/web"; ```nix # Then run `dev` in the devshell ``` + +## Theme + +Process Compose uses the selected Prelude palette for its TUI custom theme. +By default it follows `stackpanel.prelude.theme` and falls back to `minted`. + +```nix +stackpanel.process-compose.theme = "nord"; +``` + +The generated theme is written to `.stack/gen/process-compose/theme.yaml` and +the `dev` wrapper starts Process Compose with `--theme "Custom Style"`. +`process-compose-flake` exposes generated process settings and CLI wrapper +options, but no theme-definition hook, so Stackpanel renders Prelude's Nix +theme attrset into Process Compose's required custom `theme.yaml` shape. diff --git a/nix/stackpanel/modules/process-compose/module.nix b/nix/stackpanel/modules/process-compose/module.nix index d0df3c4d..3d90ab32 100644 --- a/nix/stackpanel/modules/process-compose/module.nix +++ b/nix/stackpanel/modules/process-compose/module.nix @@ -29,6 +29,7 @@ # config.stackpanel.process-compose.package # ============================================================================== { + inputs ? { }, lib, config, pkgs, @@ -36,6 +37,12 @@ }: let meta = import ./meta.nix; + hasPreludeThemes = inputs ? prelude && inputs.prelude ? lib && inputs.prelude.lib ? themes; + preludeThemes = if hasPreludeThemes then inputs.prelude.lib.themes else { }; + preludeProcessComposeThemes = import ./themes.nix { + inherit lib preludeThemes; + }; + preludeProcessComposeThemeNames = builtins.attrNames preludeProcessComposeThemes; cfg = config.stackpanel; pcCfg = cfg.process-compose; @@ -385,9 +392,15 @@ let processes, commandName, port ? 8080, + themeConfigDir ? null, }: let processCount = builtins.length (lib.attrNames processes); + themeArgs = + lib.optionalString (themeConfigDir != null) '' + export PROC_COMP_CONFIG="${themeConfigDir}" + theme_args=(--theme "Custom Style") + ''; in pkgs.writeShellScriptBin commandName '' if [[ ${toString processCount} -eq 0 ]]; then @@ -422,10 +435,12 @@ let # Set PC_PORT_NUM for the process-compose API server export PC_PORT_NUM="${toString port}" + theme_args=() + ${themeArgs} # Run process-compose - auto-detects process-compose.yaml in repo root # The --port flag sets the API server port - exec ${pkgs.process-compose}/bin/process-compose --port ${toString port} "$@" + exec ${pkgs.process-compose}/bin/process-compose --port ${toString port} "''${theme_args[@]}" "$@" ''; in @@ -465,6 +480,20 @@ in example = "start"; }; + theme = lib.mkOption { + type = lib.types.nullOr (lib.types.enum preludeProcessComposeThemeNames); + default = null; + description = '' + Prelude theme to render for the Process Compose TUI. + + Null follows `stackpanel.prelude.theme`; when that is also null, + Stackpanel uses Prelude's default `minted` theme. Process Compose only + supports one custom theme file, so this option selects which Prelude + palette is written to `.stack/gen/process-compose/theme.yaml`. + ''; + example = "nord"; + }; + formatWatcher = { enable = lib.mkEnableOption "format watcher process" // { default = true; @@ -606,10 +635,19 @@ in # Resolve port: explicit config > computed from base port + fixed offset # Uses base-port + 90 to avoid hash collisions with other services resolvedPort = if pcCfg.port != null then pcCfg.port else cfg.ports.base-port + 90; + preludeTheme = cfg.prelude.theme or null; + effectiveTheme = + if pcCfg.theme != null then pcCfg.theme else if preludeTheme != null then preludeTheme else "minted"; configFile = mkConfigFile { inherit (pcCfg) processes; }; + themeFile = + if hasPreludeThemes then + (pkgs.formats.yaml { }).generate "process-compose-theme.yaml" preludeProcessComposeThemes.${effectiveTheme} + else + throw "process-compose: generated Prelude themes require the `prelude` flake input"; + themeConfigDir = "\${STACKPANEL_ROOT:-$PWD}/.stack/gen/process-compose"; in { # Set PC_PORT_NUM environment variable for the devshell @@ -621,6 +659,7 @@ in inherit (pcCfg) commandName; inherit (pcCfg) processes; port = resolvedPort; + inherit themeConfigDir; }) ]; @@ -633,6 +672,13 @@ in description = "Process-compose configuration (symlink to Nix store)"; }; + stackpanel.files.entries.".stack/gen/process-compose/theme.yaml" = { + enable = true; + format = "symlink"; + target = "${themeFile}"; + description = "Process Compose custom theme generated from Prelude theme `${effectiveTheme}`"; + }; + # Gitignore the symlink — it points to a Nix store path that changes on every rebuild stackpanel.gitignore.entries = [ "process-compose.yaml" ]; diff --git a/nix/stackpanel/modules/process-compose/themes.nix b/nix/stackpanel/modules/process-compose/themes.nix new file mode 100644 index 00000000..19a52aca --- /dev/null +++ b/nix/stackpanel/modules/process-compose/themes.nix @@ -0,0 +1,50 @@ +{ lib, preludeThemes }: +let + mkProcessComposeTheme = + name: palette: + { + style = { + name = "Prelude ${name}"; + body = { + fgColor = palette.fg; + bgColor = palette.bg; + secondaryTextColor = palette.accent2; + tertiaryTextColor = palette.success; + borderColor = palette.border; + }; + stat_table = { + keyFgColor = palette.accent2; + valueFgColor = palette.fg; + logoColor = palette.accent; + }; + proc_table = { + fgColor = palette.info; + fgWarning = palette.warning; + fgPending = palette.dim; + fgCompleted = palette.success; + fgError = palette.error; + headerFgColor = palette.accent2; + }; + help = { + fgColor = palette.fg; + keyColor = palette.accent; + hlColor = palette.secondary; + categoryFgColor = palette.accent2; + }; + dialog = { + fgColor = palette.fg; + bgColor = palette.surface; + contrastBgColor = palette.secondary; + attentionBgColor = palette.error; + buttonFgColor = palette.selectionFg; + buttonBgColor = palette.accent; + buttonFocusFgColor = palette.selectionFg; + buttonFocusBgColor = palette.info; + labelFgColor = palette.accent2; + fieldFgColor = palette.fg; + fieldBgColor = palette.secondary; + }; + }; + }; +in +lib.mapAttrs mkProcessComposeTheme preludeThemes diff --git a/nix/stackpanel/modules/process-compose/ui.nix b/nix/stackpanel/modules/process-compose/ui.nix index fec2da07..bc997c71 100644 --- a/nix/stackpanel/modules/process-compose/ui.nix +++ b/nix/stackpanel/modules/process-compose/ui.nix @@ -12,6 +12,7 @@ let meta = import ./meta.nix; cfg = config.stackpanel; pcCfg = cfg.process-compose or { }; + preludeTheme = cfg.prelude.theme or null; in lib.mkIf (cfg.enable && (pcCfg.enable or false)) { stackpanel.panels."${meta.id}-status" = { @@ -36,6 +37,17 @@ lib.mkIf (cfg.enable && (pcCfg.enable or false)) { type = "FIELD_TYPE_BOOLEAN"; value = if (pcCfg.formatWatcher.enable or true) then "true" else "false"; } + { + name = "theme"; + type = "FIELD_TYPE_STRING"; + value = + if pcCfg.theme != null then + pcCfg.theme + else if preludeTheme != null then + preludeTheme + else + "minted"; + } ]; }; }