From 5cdf9ef99563a457f12a712e1f42c4f0d709203c Mon Sep 17 00:00:00 2001 From: leiserfg Date: Fri, 31 Oct 2025 13:16:27 +0100 Subject: [PATCH] vicinae service: init Signed-off-by: Austin Horstman --- modules/programs/vicinae.nix | 244 ++++++++++++++++++ tests/modules/programs/vicinae/default.nix | 4 + .../programs/vicinae/example-settings.nix | 77 ++++++ 3 files changed, 325 insertions(+) create mode 100644 modules/programs/vicinae.nix create mode 100644 tests/modules/programs/vicinae/default.nix create mode 100644 tests/modules/programs/vicinae/example-settings.nix diff --git a/modules/programs/vicinae.nix b/modules/programs/vicinae.nix new file mode 100644 index 000000000..bde52d4eb --- /dev/null +++ b/modules/programs/vicinae.nix @@ -0,0 +1,244 @@ +{ + config, + pkgs, + lib, + ... +}: +let + cfg = config.programs.vicinae; + + jsonFormat = pkgs.formats.json { }; +in +{ + meta.maintainers = [ lib.maintainers.leiserfg ]; + + options.programs.vicinae = { + enable = lib.mkEnableOption "vicinae launcher daemon"; + + package = lib.mkPackageOption pkgs "vicinae" { nullable = true; }; + + systemd = { + enable = lib.mkEnableOption "vicinae systemd integration"; + + autoStart = lib.mkOption { + type = lib.types.bool; + default = true; + description = "If the vicinae daemon should be started automatically"; + }; + + target = lib.mkOption { + type = lib.types.str; + default = "graphical-session.target"; + example = "sway-session.target"; + description = '' + The systemd target that will automatically start the vicinae service. + ''; + }; + }; + + useLayerShell = lib.mkOption { + type = lib.types.bool; + default = true; + description = "If vicinae should use the layer shell"; + }; + + extensions = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + description = '' + List of Vicinae extensions to install. + + You can use the `config.lib.vicinae.mkExtension` and `config.lib.vicinae.mkRayCastExtension` functions to create them, like: + ```nix + [ + (config.lib.vicinae.mkExtension { + name = "test-extension"; + src = + pkgs.fetchFromGitHub { + owner = "schromp"; + repo = "vicinae-extensions"; + rev = "f8be5c89393a336f773d679d22faf82d59631991"; + sha256 = "sha256-zk7WIJ19ITzRFnqGSMtX35SgPGq0Z+M+f7hJRbyQugw="; + } + + "/test-extension"; + }) + (config.lib.vicinae.mkRayCastExtension { + name = "gif-search"; + sha256 = "sha256-G7il8T1L+P/2mXWJsb68n4BCbVKcrrtK8GnBNxzt73Q="; + rev = "4d417c2dfd86a5b2bea202d4a7b48d8eb3dbaeb1"; + }) + ], + ``` + ''; + }; + + themes = lib.mkOption { + inherit (jsonFormat) type; + default = { }; + description = '' + Theme settings to add to the themes folder in `~/.config/vicinae/themes`. + + The attribute name of the theme will be the name of theme json file, + e.g. `base16-default-dark` will be `base16-default-dark.json`. + ''; + example = + lib.literalExpression # nix + '' + { + base16-default-dark = { + version = "1.0.0"; + appearance = "dark"; + icon = /path/to/icon.png; + name = "base16 default dark"; + description = "base16 default dark by Chris Kempson"; + palette = { + background = "#181818"; + foreground = "#d8d8d8"; + blue = "#7cafc2"; + green = "#a3be8c"; + magenta = "#ba8baf"; + orange = "#dc9656"; + purple = "#a16946"; + red = "#ab4642"; + yellow = "#f7ca88"; + cyan = "#86c1b9"; + }; + }; + } + ''; + }; + + settings = lib.mkOption { + inherit (jsonFormat) type; + default = { }; + description = "Settings written as JSON to `~/.config/vicinae/vicinae.json."; + example = lib.literalExpression '' + { + faviconService = "twenty"; + font = { + size = 10; + }; + popToRootOnClose = false; + rootSearch = { + searchFiles = false; + }; + theme = { + name = "vicinae-dark"; + }; + window = { + csd = true; + opacity = 0.95; + rounding = 10; + }; + } + ''; + }; + }; + + config = lib.mkIf cfg.enable { + assertions = [ + (lib.hm.assertions.assertPlatform "programs.vicinae" pkgs lib.platforms.linux) + { + assertion = cfg.systemd.enable -> cfg.package != null; + message = "{option}programs.vicinae.systemd.enable requires non null {option}programs.vicinae.package"; + } + ]; + lib.vicinae.mkExtension = ( + { + name, + src, + }: + (pkgs.buildNpmPackage { + inherit name src; + installPhase = '' + runHook preInstall + + mkdir -p $out + cp -r /build/.local/share/vicinae/extensions/${name}/* $out/ + + runHook postInstall + ''; + npmDeps = pkgs.importNpmLock { npmRoot = src; }; + npmConfigHook = pkgs.importNpmLock.npmConfigHook; + }) + ); + + lib.vicinae.mkRayCastExtension = ( + { + name, + sha256, + rev, + }: + let + src = + pkgs.fetchgit { + inherit rev sha256; + url = "https://github.com/raycast/extensions"; + sparseCheckout = [ + "/extensions/${name}" + ]; + } + + "/extensions/${name}"; + in + (pkgs.buildNpmPackage { + inherit name src; + installPhase = '' + runHook preInstall + + mkdir -p $out + cp -r /build/.config/raycast/extensions/${name}/* $out/ + + runHook postInstall + ''; + npmDeps = pkgs.importNpmLock { npmRoot = src; }; + npmConfigHook = pkgs.importNpmLock.npmConfigHook; + }) + ); + + home.packages = lib.mkIf (cfg.package != null) [ cfg.package ]; + + xdg = { + configFile = { + "vicinae/vicinae.json" = lib.mkIf (cfg.settings != { }) { + source = jsonFormat.generate "vicinae-settings" cfg.settings; + }; + } + // lib.mapAttrs' ( + name: theme: + lib.nameValuePair "vicinae/themes/${name}.json" { + source = jsonFormat.generate "vicinae-${name}-theme" theme; + } + ) cfg.themes; + + dataFile = builtins.listToAttrs ( + builtins.map (item: { + name = "vicinae/extensions/${item.name}"; + value.source = item; + }) cfg.extensions + ); + }; + + systemd.user.services.vicinae = lib.mkIf (cfg.systemd.enable && cfg.package != null) { + Unit = { + Description = "Vicinae server daemon"; + Documentation = [ "https://docs.vicinae.com" ]; + After = [ cfg.systemd.target ]; + PartOf = [ cfg.systemd.target ]; + BindsTo = [ cfg.systemd.target ]; + }; + Service = { + EnvironmentFile = pkgs.writeText "vicinae-env" '' + USE_LAYER_SHELL=${if cfg.useLayerShell then builtins.toString 1 else builtins.toString 0} + ''; + Type = "simple"; + ExecStart = "${lib.getExe' cfg.package "vicinae"} server"; + Restart = "always"; + RestartSec = 5; + KillMode = "process"; + }; + Install = lib.mkIf cfg.systemd.autoStart { + WantedBy = [ cfg.systemd.target ]; + }; + }; + }; +} diff --git a/tests/modules/programs/vicinae/default.nix b/tests/modules/programs/vicinae/default.nix new file mode 100644 index 000000000..4a4736323 --- /dev/null +++ b/tests/modules/programs/vicinae/default.nix @@ -0,0 +1,4 @@ +{ lib, pkgs, ... }: +lib.optionalAttrs (pkgs.stdenv.hostPlatform.isLinux) { + vicinae-example-settings = ./example-settings.nix; +} diff --git a/tests/modules/programs/vicinae/example-settings.nix b/tests/modules/programs/vicinae/example-settings.nix new file mode 100644 index 000000000..b9de5f24f --- /dev/null +++ b/tests/modules/programs/vicinae/example-settings.nix @@ -0,0 +1,77 @@ +{ + pkgs, + config, + ... +}: + +{ + programs.vicinae = { + enable = true; + systemd.enable = true; + + settings = { + faviconService = "twenty"; + font = { + size = 10; + }; + popToRootOnClose = false; + rootSearch = { + searchFiles = false; + }; + theme = { + name = "vicinae-dark"; + }; + window = { + csd = true; + opacity = 0.95; + rounding = 10; + }; + }; + themes = { + base16-default-dark = { + version = "1.0.0"; + appearance = "dark"; + name = "base16 default dark"; + description = "base16 default dark by Chris Kempson"; + palette = { + background = "#181818"; + foreground = "#d8d8d8"; + blue = "#7cafc2"; + green = "#a3be8c"; + magenta = "#ba8baf"; + orange = "#dc9656"; + purple = "#a16946"; + red = "#ab4642"; + yellow = "#f7ca88"; + cyan = "#86c1b9"; + }; + }; + }; + + extensions = [ + (config.lib.vicinae.mkRayCastExtension { + name = "gif-search"; + sha256 = "sha256-G7il8T1L+P/2mXWJsb68n4BCbVKcrrtK8GnBNxzt73Q="; + rev = "4d417c2dfd86a5b2bea202d4a7b48d8eb3dbaeb1"; + }) + (config.lib.vicinae.mkExtension { + name = "test-extension"; + src = + pkgs.fetchFromGitHub { + owner = "schromp"; + repo = "vicinae-extensions"; + rev = "f8be5c89393a336f773d679d22faf82d59631991"; + sha256 = "sha256-zk7WIJ19ITzRFnqGSMtX35SgPGq0Z+M+f7hJRbyQugw="; + } + + "/test-extension"; + }) + ]; + }; + + nmt.script = '' + assertFileExists "home-files/.config/vicinae/vicinae.json" + assertFileExists "home-files/.config/systemd/user/vicinae.service" + assertFileExists "home-files/.local/share/vicinae/extensions/gif-search/package.json" + assertFileExists "home-files/.local/share/vicinae/extensions/test-extension/package.json" + ''; +}