From e087f3c96a2eda4855d5b8235e1d07ef526b782f Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Sun, 3 May 2026 16:09:56 +0200 Subject: [PATCH] fix(templates): adjust emacs output --- assets/templates/builtin.toml | 2 +- assets/templates/emacs/output-path.sh | 10 ++++++++ src/theme/template_apply_service.cpp | 1 + src/theme/template_engine.cpp | 34 +++++++++++++++++++++++++-- 4 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 assets/templates/emacs/output-path.sh diff --git a/assets/templates/builtin.toml b/assets/templates/builtin.toml index b500dc033..f275c5806 100644 --- a/assets/templates/builtin.toml +++ b/assets/templates/builtin.toml @@ -101,7 +101,7 @@ post_hook = "bash '{{ config_dir }}/cava/apply.sh'" [templates.emacs] input_path = "./emacs/emacs.el" -# no output_path +output_path_dynamic = "bash '{{ config_dir }}/emacs/output-path.sh'" post_hook = "bash '{{ config_dir }}/emacs/apply.sh'" [templates.foot] diff --git a/assets/templates/emacs/output-path.sh b/assets/templates/emacs/output-path.sh new file mode 100644 index 000000000..433fc14cb --- /dev/null +++ b/assets/templates/emacs/output-path.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail +# Emit one absolute path: first existing config root wins (legacy emacsClients order). +: "${HOME?}" +for root in "${HOME}/.config/doom" "${HOME}/.config/emacs" "${HOME}/.emacs.d"; do + if [[ -d "$root" ]]; then + printf '%s/themes/noctalia-theme.el\n' "$root" + exit 0 + fi +done diff --git a/src/theme/template_apply_service.cpp b/src/theme/template_apply_service.cpp index 9fc26be40..75e61bd02 100644 --- a/src/theme/template_apply_service.cpp +++ b/src/theme/template_apply_service.cpp @@ -160,6 +160,7 @@ namespace noctalia::theme { out << "# [templates.my_app]\n"; out << "# input_path = \"~/.config/noctalia/templates/my-app.css\"\n"; out << "# output_path = \"~/.config/my-app/theme.css\"\n"; + out << "# output_path_dynamic = \"bash '~/.config/noctalia/templates/resolve-paths.sh'\"\n"; out << "# post_hook = \"my-app --reload-theme\"\n"; } diff --git a/src/theme/template_engine.cpp b/src/theme/template_engine.cpp index 3d6a09a08..ebcf2b5e2 100644 --- a/src/theme/template_engine.cpp +++ b/src/theme/template_engine.cpp @@ -146,6 +146,8 @@ namespace noctalia::theme { std::string name; std::string inputPath; std::vector outputPaths; + // Rendered like hooks, then run via sh -lc; non-comment stdout lines become extra output paths. + std::string outputPathDynamic; std::string compareTo; std::vector colorsToCompare; std::string preHook; @@ -1104,6 +1106,21 @@ namespace noctalia::theme { return base / expanded; } + void appendPathsFromDynamicStdout(const std::filesystem::path& configPath, std::vector& outputs, + const std::string& stdoutText) { + std::string_view remaining(stdoutText); + while (!remaining.empty()) { + const std::size_t nl = remaining.find('\n'); + const std::string_view line = nl == std::string_view::npos ? remaining : remaining.substr(0, nl); + remaining = nl == std::string_view::npos ? std::string_view{} : remaining.substr(nl + 1); + std::string trimmed = StringUtils::trim(line); + if (trimmed.empty() || trimmed.front() == '#') { + continue; + } + outputs.push_back(resolveConfigPath(configPath, trimmed).string()); + } + } + std::optional parseTemplateEntry(const std::filesystem::path& configPath, std::string_view name, const toml::table& tpl, std::string_view defaultMode) { @@ -1131,6 +1148,8 @@ namespace noctalia::theme { entry.preHook = preHook->get(); if (const auto postHook = tpl.get_as("post_hook")) entry.postHook = postHook->get(); + if (const auto opd = tpl.get_as("output_path_dynamic")) + entry.outputPathDynamic = opd->get(); if (const auto index = tpl.get_as("index")) entry.index = static_cast(index->get()); return entry; @@ -1248,6 +1267,17 @@ namespace noctalia::theme { renderOptions.configDir = configPath.has_parent_path() ? configPath.parent_path().string() : ""; renderOptions.configFile = configPath.string(); + std::vector effectiveOutputs = entry.outputPaths; + if (!entry.outputPathDynamic.empty()) { + const auto cmdRendered = EngineImpl(m_themeData, renderOptions).render(entry.outputPathDynamic); + if (cmdRendered.errorCount == 0 && !cmdRendered.text.empty()) { + const auto dynResult = process::runSync(cmdRendered.text); + if (dynResult.exitCode == 0) { + appendPathsFromDynamicStdout(configPath, effectiveOutputs, dynResult.out); + } + } + } + auto runHook = [&](const std::string& hook) { if (!hook.empty() && !cancelRequested()) { const auto hookRendered = EngineImpl(m_themeData, renderOptions).render(hook); @@ -1256,12 +1286,12 @@ namespace noctalia::theme { } }; - const bool hasOutputs = !entry.outputPaths.empty(); + const bool hasOutputs = !effectiveOutputs.empty(); if (hasOutputs) runHook(entry.preHook); bool wroteAny = false; - for (const std::string& outputPath : entry.outputPaths) { + for (const std::string& outputPath : effectiveOutputs) { if (cancelRequested()) { return ok; }