From 075fd91bec4ec9206294e96bef4e0c8544a806a2 Mon Sep 17 00:00:00 2001 From: Ivy Pierlot Date: Fri, 29 Aug 2025 18:21:27 +1000 Subject: [PATCH] feat(hm-module): add darwin defaults akin to nix-community/home-manager#7501 (#103) --- hm-module.nix | 402 +++++++++++++++++++++++++------------------------- 1 file changed, 197 insertions(+), 205 deletions(-) diff --git a/hm-module.nix b/hm-module.nix index 12da9ee..f40c7df 100644 --- a/hm-module.nix +++ b/hm-module.nix @@ -2,14 +2,15 @@ home-manager, self, name, -}: { +}: +{ config, pkgs, lib, ... -}: let - inherit - (lib) +}: +let + inherit (lib) getAttrFromPath isPath mkIf @@ -29,16 +30,11 @@ linuxConfigPath = ".zen"; darwinConfigPath = "Library/Application Support/Zen"; - configPath = "${ - ( - if pkgs.stdenv.isDarwin - then darwinConfigPath - else linuxConfigPath - ) - }"; + configPath = "${(if pkgs.stdenv.isDarwin then darwinConfigPath else linuxConfigPath)}"; mkFirefoxModule = import "${home-manager.outPath}/modules/programs/firefox/mkFirefoxModule.nix"; -in { +in +{ imports = [ (mkFirefoxModule { inherit modulePath; @@ -53,6 +49,7 @@ in { }; darwin = { configPath = darwinConfigPath; + defaultsId = "app.zen-browser.zen"; }; }; }) @@ -60,10 +57,12 @@ in { options = setAttrByPath modulePath { profiles = mkOption { - type = with types; + type = + with types; attrsOf ( submodule ( - {...}: { + { ... }: + { options = { spacesForce = mkOption { type = bool; @@ -73,7 +72,8 @@ in { spaces = mkOption { type = attrsOf ( submodule ( - {name, ...}: { + { name, ... }: + { options = { name = mkOption { type = str; @@ -92,10 +92,7 @@ in { icon = mkOption { type = nullOr (either str path); description = "Emoji or icon URI to be used as space icon."; - apply = v: - if isPath v - then "file://${v}" - else v; + apply = v: if isPath v then "file://${v}" else v; default = null; }; container = mkOption { @@ -111,7 +108,8 @@ in { type = nullOr ( listOf ( submodule ( - {...}: { + { ... }: + { options = { red = mkOption { type = ints.between 0 255; @@ -165,7 +163,7 @@ in { ) ) ); - default = []; + default = [ ]; }; theme.opacity = mkOption { type = nullOr float; @@ -183,7 +181,7 @@ in { } ) ); - default = {}; + default = { }; }; }; } @@ -199,10 +197,10 @@ in { # Seems like zen uses relative (to the original binary) path to the policies.json file # and ignores the overrides by pkgs.wrapFirefox policies = cfg.policies; - }) {}).override - { - nativeMessagingHosts = cfg.nativeMessagingHosts; - }; + }) { }).override + { + nativeMessagingHosts = cfg.nativeMessagingHosts; + }; policies = { DisableAppUpdate = lib.mkDefault true; @@ -210,187 +208,181 @@ in { }; }; - home.file = let - inherit - (builtins) - isNull - toJSON - toString - ; - inherit - (lib) - concatStringsSep - concatMapStringsSep - concatMapAttrsStringSep - filterAttrs - getExe - getExe' - mapAttrs' - mapAttrsToList - nameValuePair - optionalString - pipe - ; - in (mapAttrs' (profileName: profile: let - sqlite3 = getExe' pkgs.sqlite "sqlite3"; - scriptFile = "${configPath}/${profileName}/places_update.sh"; - placesFile = "${config.home.homeDirectory}/${configPath}/${profileName}/places.sqlite"; + home.file = + let + inherit (builtins) + isNull + toJSON + toString + ; + inherit (lib) + concatStringsSep + concatMapStringsSep + concatMapAttrsStringSep + filterAttrs + getExe + getExe' + mapAttrs' + mapAttrsToList + nameValuePair + optionalString + pipe + ; + in + (mapAttrs' ( + profileName: profile: + let + sqlite3 = getExe' pkgs.sqlite "sqlite3"; + scriptFile = "${configPath}/${profileName}/places_update.sh"; + placesFile = "${config.home.homeDirectory}/${configPath}/${profileName}/places.sqlite"; - insertSpaces = '' - # Reference: https://github.com/zen-browser/desktop/blob/4e2dfd8a138fd28767bb4799a3ca9d8aab80430e/src/zen/workspaces/ZenWorkspacesStorage.mjs#L25-L55 - ${sqlite3} "${placesFile}" "${ - concatStringsSep " " [ - "CREATE TABLE IF NOT EXISTS zen_workspaces (" - "id INTEGER PRIMARY KEY," - "uuid TEXT UNIQUE NOT NULL," - "name TEXT NOT NULL," - "icon TEXT," - "container_id INTEGER," - "position INTEGER NOT NULL DEFAULT 0," - "created_at INTEGER NOT NULL," - "updated_at INTEGER NOT NULL" - ");" - ] - }" || exit 1 + insertSpaces = '' + # Reference: https://github.com/zen-browser/desktop/blob/4e2dfd8a138fd28767bb4799a3ca9d8aab80430e/src/zen/workspaces/ZenWorkspacesStorage.mjs#L25-L55 + ${sqlite3} "${placesFile}" "${ + concatStringsSep " " [ + "CREATE TABLE IF NOT EXISTS zen_workspaces (" + "id INTEGER PRIMARY KEY," + "uuid TEXT UNIQUE NOT NULL," + "name TEXT NOT NULL," + "icon TEXT," + "container_id INTEGER," + "position INTEGER NOT NULL DEFAULT 0," + "created_at INTEGER NOT NULL," + "updated_at INTEGER NOT NULL" + ");" + ] + }" || exit 1 - columns=($(${sqlite3} "${placesFile}" "SELECT name FROM pragma_table_info('zen_workspaces');")) - if [[ ! "''${columns[@]}" =~ "theme_type" ]]; then - ${sqlite3} "${placesFile}" "ALTER TABLE zen_workspaces ADD COLUMN theme_type TEXT;" || exit 1 - fi - if [[ ! "''${columns[@]}" =~ "theme_colors" ]]; then - ${sqlite3} "${placesFile}" "ALTER TABLE zen_workspaces ADD COLUMN theme_colors TEXT;" || exit 1 - fi - if [[ ! "''${columns[@]}" =~ "theme_opacity" ]]; then - ${sqlite3} "${placesFile}" "ALTER TABLE zen_workspaces ADD COLUMN theme_opacity REAL;" || exit 1 - fi - if [[ ! "''${columns[@]}" =~ "theme_rotation" ]]; then - ${sqlite3} "${placesFile}" "ALTER TABLE zen_workspaces ADD COLUMN theme_rotation INTEGER;" || exit 1 - fi - if [[ ! "''${columns[@]}" =~ "theme_texture" ]]; then - ${sqlite3} "${placesFile}" "ALTER TABLE zen_workspaces ADD COLUMN theme_texture REAL;" || exit 1 - fi - - # Reference: https://github.com/zen-browser/desktop/blob/4e2dfd8a138fd28767bb4799a3ca9d8aab80430e/src/zen/workspaces/ZenWorkspacesStorage.mjs#L141-L149 - ${sqlite3} "${placesFile}" "${ - (concatStringsSep " " [ - "INSERT OR REPLACE INTO zen_workspaces (" - "uuid," - "name," - "icon," - "container_id," - "position," - - "theme_type," - "theme_colors," - "theme_opacity," - "theme_rotation," - "theme_texture," - - "created_at," - "updated_at" - ") VALUES " - ]) - + (pipe profile.spaces [ - (mapAttrsToList (_: s: [ - "'{${s.id}}'" - "'${s.name}'" - ( - if isNull s.icon - then "NULL" - else "'${s.icon}'" - ) - ( - if isNull s.container - then "NULL" - else toString s.container - ) - (toString s.position) - ( - if isNull s.theme.type - then "NULL" - else "'${s.theme.type}'" - ) - ( - if isNull s.theme.colors - then "NULL" - else "'${toJSON (map (c: { - inherit (c) algorithm lightness position type; - c = [c.red c.green c.blue]; - isCustom = c.custom; - isPrimary = c.primary; - }) - s.theme.colors)}'" - ) - ( - if isNull s.theme.opacity - then "NULL" - else toString s.theme.opacity - ) - ( - if isNull s.theme.rotation - then "NULL" - else toString s.theme.rotation - ) - ( - if isNull s.theme.texture - then "NULL" - else toString s.theme.texture - ) - "COALESCE((SELECT created_at FROM zen_workspaces WHERE uuid = '{${s.id}}'), strftime('%s', 'now'))" - "strftime('%s', 'now')" - ])) - (map (row: concatStringsSep "," row)) - (concatMapStringsSep "," (row: "(${row})")) - ]) - }" || exit 1 - ''; - - deleteSpaces = '' - ${sqlite3} "${placesFile}" "DELETE FROM zen_workspaces ${ - if profile.spaces != {} - then "WHERE " - else "" - }${concatMapAttrsStringSep " AND " (_: s: "NOT uuid = '{${s.id}}'") profile.spaces}" || exit 1 - ''; - in - nameValuePair scriptFile { - source = getExe (pkgs.writeShellScriptBin "places_update_${profileName}" '' - # This file is generated by Zen browser Home Manager module, please to not change it since it - # will be overridden and executed on every rebuild of the home environment. - - function update_spaces() { - ${optionalString (profile.spaces != {}) insertSpaces} - ${optionalString (profile.spacesForce) deleteSpaces} - } - - error="$(update_spaces 2>&1 1>/dev/null)" - if [[ "$?" -ne 0 ]]; then - if [[ "$error" == *"database is locked"* ]]; then - echo "$error" - - YELLOW="\033[1;33m" - NC="\033[0m" - echo -e "zen-update-places:''${YELLOW} Atempted to update the \"zen_workspaces\" table with values declared in \"programs.zen.profiles.\"${profileName}\".spaces\".''${NC}" - echo -e "zen-update-places:''${YELLOW} Failed to update \"${placesFile}\" due to a Zen browser instance for profile \"${profileName}\" being opened, please close''${NC}" - echo -e "zen-update-places:''${YELLOW} Zen browser and rebuild the home environment to rerun \"home-manager-${config.home.username}.service\" and update places.sqlite.''${NC}" - else - echo "$error" + columns=($(${sqlite3} "${placesFile}" "SELECT name FROM pragma_table_info('zen_workspaces');")) + if [[ ! "''${columns[@]}" =~ "theme_type" ]]; then + ${sqlite3} "${placesFile}" "ALTER TABLE zen_workspaces ADD COLUMN theme_type TEXT;" || exit 1 fi - exit 1 - else - exit 0 - fi - ''); - onChange = '' - ${config.home.homeDirectory}/${scriptFile} - if [[ "$?" -ne 0 ]]; then - RED="\033[0;31m" - NC="\033[0m" - echo -e "zen-update-places:''${RED} Failed to update places.sqlite file for Zen browser \"${profileName}\" profile.''${NC}" - fi - ''; - executable = true; - force = true; - }) (filterAttrs (_: profile: profile.spaces != {} || profile.spacesForce) cfg.profiles)); + if [[ ! "''${columns[@]}" =~ "theme_colors" ]]; then + ${sqlite3} "${placesFile}" "ALTER TABLE zen_workspaces ADD COLUMN theme_colors TEXT;" || exit 1 + fi + if [[ ! "''${columns[@]}" =~ "theme_opacity" ]]; then + ${sqlite3} "${placesFile}" "ALTER TABLE zen_workspaces ADD COLUMN theme_opacity REAL;" || exit 1 + fi + if [[ ! "''${columns[@]}" =~ "theme_rotation" ]]; then + ${sqlite3} "${placesFile}" "ALTER TABLE zen_workspaces ADD COLUMN theme_rotation INTEGER;" || exit 1 + fi + if [[ ! "''${columns[@]}" =~ "theme_texture" ]]; then + ${sqlite3} "${placesFile}" "ALTER TABLE zen_workspaces ADD COLUMN theme_texture REAL;" || exit 1 + fi + + # Reference: https://github.com/zen-browser/desktop/blob/4e2dfd8a138fd28767bb4799a3ca9d8aab80430e/src/zen/workspaces/ZenWorkspacesStorage.mjs#L141-L149 + ${sqlite3} "${placesFile}" "${ + (concatStringsSep " " [ + "INSERT OR REPLACE INTO zen_workspaces (" + "uuid," + "name," + "icon," + "container_id," + "position," + + "theme_type," + "theme_colors," + "theme_opacity," + "theme_rotation," + "theme_texture," + + "created_at," + "updated_at" + ") VALUES " + ]) + + (pipe profile.spaces [ + (mapAttrsToList ( + _: s: [ + "'{${s.id}}'" + "'${s.name}'" + (if isNull s.icon then "NULL" else "'${s.icon}'") + (if isNull s.container then "NULL" else toString s.container) + (toString s.position) + (if isNull s.theme.type then "NULL" else "'${s.theme.type}'") + ( + if isNull s.theme.colors then + "NULL" + else + "'${ + toJSON ( + map (c: { + inherit (c) + algorithm + lightness + position + type + ; + c = [ + c.red + c.green + c.blue + ]; + isCustom = c.custom; + isPrimary = c.primary; + }) s.theme.colors + ) + }'" + ) + (if isNull s.theme.opacity then "NULL" else toString s.theme.opacity) + (if isNull s.theme.rotation then "NULL" else toString s.theme.rotation) + (if isNull s.theme.texture then "NULL" else toString s.theme.texture) + "COALESCE((SELECT created_at FROM zen_workspaces WHERE uuid = '{${s.id}}'), strftime('%s', 'now'))" + "strftime('%s', 'now')" + ] + )) + (map (row: concatStringsSep "," row)) + (concatMapStringsSep "," (row: "(${row})")) + ]) + }" || exit 1 + ''; + + deleteSpaces = '' + ${sqlite3} "${placesFile}" "DELETE FROM zen_workspaces ${ + if profile.spaces != { } then "WHERE " else "" + }${concatMapAttrsStringSep " AND " (_: s: "NOT uuid = '{${s.id}}'") profile.spaces}" || exit 1 + ''; + in + nameValuePair scriptFile { + source = getExe ( + pkgs.writeShellScriptBin "places_update_${profileName}" '' + # This file is generated by Zen browser Home Manager module, please to not change it since it + # will be overridden and executed on every rebuild of the home environment. + + function update_spaces() { + ${optionalString (profile.spaces != { }) insertSpaces} + ${optionalString (profile.spacesForce) deleteSpaces} + } + + error="$(update_spaces 2>&1 1>/dev/null)" + if [[ "$?" -ne 0 ]]; then + if [[ "$error" == *"database is locked"* ]]; then + echo "$error" + + YELLOW="\033[1;33m" + NC="\033[0m" + echo -e "zen-update-places:''${YELLOW} Atempted to update the \"zen_workspaces\" table with values declared in \"programs.zen.profiles.\"${profileName}\".spaces\".''${NC}" + echo -e "zen-update-places:''${YELLOW} Failed to update \"${placesFile}\" due to a Zen browser instance for profile \"${profileName}\" being opened, please close''${NC}" + echo -e "zen-update-places:''${YELLOW} Zen browser and rebuild the home environment to rerun \"home-manager-${config.home.username}.service\" and update places.sqlite.''${NC}" + else + echo "$error" + fi + exit 1 + else + exit 0 + fi + '' + ); + onChange = '' + ${config.home.homeDirectory}/${scriptFile} + if [[ "$?" -ne 0 ]]; then + RED="\033[0;31m" + NC="\033[0m" + echo -e "zen-update-places:''${RED} Failed to update places.sqlite file for Zen browser \"${profileName}\" profile.''${NC}" + fi + ''; + executable = true; + force = true; + } + ) (filterAttrs (_: profile: profile.spaces != { } || profile.spacesForce) cfg.profiles)); }; }