This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

wrapperModules.noctalia-shell: declarative settings wrapper

Wraps noctalia-shell so declarative bits are merged into the user's
~/.config/noctalia/settings.json on every start. The file stays
writable; only fields covered by 'settings' (deep-merge) and
'settingsPatches' (jq filters) are pinned, so user GUI tweaks to
other keys survive.

Also moves the sway workspace patch (named/global workspaces,
click-to-current-output) into the wrapper behind 'swayWorkspaceFix',
and ships a default config that pins the dark-mode hook and shows
workspace names in the bar.

+185 -40
+1
flake.nix
··· 194 194 195 195 ./wrapperModules/pi/flake-module.nix 196 196 ./wrapperModules/claude-code/flake-module.nix 197 + ./wrapperModules/noctalia-shell/flake-module.nix 197 198 { 198 199 options.flake.wrapperModules = nixpkgs.lib.mkOption { 199 200 type = nixpkgs.lib.types.lazyAttrsOf nixpkgs.lib.types.raw;
+29
tools/noctalia-shell/flake-module.nix
··· 1 + { self, inputs, ... }: 2 + { 3 + perSystem = 4 + { pkgs, ... }: 5 + let 6 + noctalia = inputs.wrappers.lib.wrapModule { 7 + imports = [ self.wrapperModules.noctalia-shell ]; 8 + inherit pkgs; 9 + 10 + # Declarative settings merged into ~/.config/noctalia/settings.json 11 + # on every start. Anything not listed here remains user-mutable. 12 + settings = { 13 + hooks = { 14 + enabled = true; 15 + # Mirror noctalia's dark mode toggle to the system theme. 16 + darkModeChange = ''if [ "$1" = "true" ]; then switch-theme dark; else switch-theme light; fi''; 17 + }; 18 + }; 19 + 20 + settingsPatches = [ 21 + # Show workspace names instead of indices in the bar. 22 + ''.bar.widgets.center |= map(if .id == "Workspace" then .labelMode = "name" else . end)'' 23 + ]; 24 + }; 25 + in 26 + { 27 + packages.noctalia-shell = noctalia.wrapper; 28 + }; 29 + }
+152
wrapperModules/noctalia-shell/flake-module.nix
··· 1 + { ... }: 2 + { 3 + flake.wrapperModules.noctalia-shell = 4 + { 5 + config, 6 + lib, 7 + ... 8 + }: 9 + let 10 + pkgs = config.pkgs; 11 + jsonFormat = pkgs.formats.json { }; 12 + 13 + sway-focus-workspace = pkgs.writeShellScript "sway-focus-workspace" '' 14 + # Bring a workspace to the currently focused output 15 + ws="$1" 16 + current_output=$(${pkgs.sway}/bin/swaymsg -t get_workspaces \ 17 + | ${pkgs.jq}/bin/jq -r '.[] | select(.focused) | .output') 18 + ${pkgs.sway}/bin/swaymsg "workspace $ws, move workspace to output $current_output" 19 + ''; 20 + 21 + # Patch noctalia-shell for sway: 22 + # - Use workspace name instead of number (activate() sends 23 + # "workspace number <num>" which fails for named workspaces) 24 + # - Show all workspaces on all screens (globalWorkspaces) 25 + # - Bring workspace to current screen on click instead of jumping 26 + swayPatched = pkgs.noctalia-shell.overrideAttrs (old: { 27 + postInstall = (old.postInstall or "") + '' 28 + substituteInPlace $out/share/noctalia-shell/Services/Compositor/SwayService.qml \ 29 + --replace-fail 'workspace.handle.activate();' \ 30 + 'Quickshell.execDetached(["${sway-focus-workspace}", workspace.name]);' \ 31 + --replace-fail 'property bool initialized: false' \ 32 + 'property bool globalWorkspaces: true 33 + property bool initialized: false' 34 + ''; 35 + }); 36 + 37 + declSettingsFile = jsonFormat.generate "noctalia-declarative-settings.json" config.settings; 38 + in 39 + { 40 + _class = "wrapper"; 41 + 42 + options = { 43 + settings = lib.mkOption { 44 + type = jsonFormat.type; 45 + default = { }; 46 + example = lib.literalExpression '' 47 + { 48 + hooks.enabled = true; 49 + hooks.darkModeChange = "switch-theme $1"; 50 + } 51 + ''; 52 + description = '' 53 + Declarative noctalia settings. Deep-merged into the on-disk 54 + `settings.json` on every start (declarative wins). Keys not 55 + mentioned here remain user-mutable and survive across 56 + invocations — only the fields you set are pinned. 57 + ''; 58 + }; 59 + 60 + settingsPath = lib.mkOption { 61 + type = lib.types.str; 62 + default = ".config/noctalia/settings.json"; 63 + description = '' 64 + Path to the noctalia settings file, relative to `$HOME`. 65 + ''; 66 + }; 67 + 68 + settingsPatches = lib.mkOption { 69 + type = lib.types.listOf lib.types.str; 70 + default = [ ]; 71 + example = lib.literalExpression '' 72 + [ 73 + # Force the Workspace bar widget to show workspace names. 74 + ".bar.widgets.center |= map(if .id == \"Workspace\" then .labelMode = \"name\" else . end)" 75 + ] 76 + ''; 77 + description = '' 78 + jq update expressions applied to `settings.json` on every start, 79 + after the deep-merge of `settings`. Use these to pin individual 80 + fields inside arrays (where deep-merge would replace the whole 81 + array) without touching sibling entries. 82 + 83 + Each string is passed as the filter to a separate `jq` invocation. 84 + Patches run in order; if any patch fails the file is left 85 + untouched. 86 + ''; 87 + }; 88 + 89 + swayWorkspaceFix = lib.mkOption { 90 + type = lib.types.bool; 91 + default = true; 92 + description = '' 93 + Patch noctalia-shell so its workspace bar works on sway with 94 + named/global workspaces and click-to-current-output. Applied 95 + to the default package only; ignored if you override `package`. 96 + ''; 97 + }; 98 + }; 99 + 100 + config.package = lib.mkDefault ( 101 + if config.swayWorkspaceFix then swayPatched else pkgs.noctalia-shell 102 + ); 103 + 104 + config.extraPackages = [ 105 + pkgs.jq 106 + pkgs.coreutils 107 + ]; 108 + 109 + # On every start: merge declarative settings into the user's 110 + # writable settings.json. User-edited keys outside the declarative 111 + # set are preserved; declarative keys are overwritten each run. 112 + config.preHook = '' 113 + NOCTALIA_SETTINGS="$HOME/${config.settingsPath}" 114 + mkdir -p "$(dirname "$NOCTALIA_SETTINGS")" 115 + if [ ! -f "$NOCTALIA_SETTINGS" ]; then 116 + echo '{}' > "$NOCTALIA_SETTINGS" 117 + fi 118 + NOCTALIA_TMP=$(mktemp "$NOCTALIA_SETTINGS.XXXXXX") 119 + noctalia_merge_ok=1 120 + if ! ${pkgs.jq}/bin/jq --slurpfile decl ${declSettingsFile} \ 121 + '. * $decl[0]' "$NOCTALIA_SETTINGS" > "$NOCTALIA_TMP"; then 122 + noctalia_merge_ok=0 123 + fi 124 + ${lib.concatMapStringsSep "\n" (patch: '' 125 + if [ "$noctalia_merge_ok" = 1 ]; then 126 + NOCTALIA_TMP2=$(mktemp "$NOCTALIA_SETTINGS.XXXXXX") 127 + if ${pkgs.jq}/bin/jq ${lib.escapeShellArg patch} "$NOCTALIA_TMP" > "$NOCTALIA_TMP2"; then 128 + mv "$NOCTALIA_TMP2" "$NOCTALIA_TMP" 129 + else 130 + rm -f "$NOCTALIA_TMP2" 131 + noctalia_merge_ok=0 132 + fi 133 + fi 134 + '') config.settingsPatches} 135 + if [ "$noctalia_merge_ok" = 1 ]; then 136 + mv "$NOCTALIA_TMP" "$NOCTALIA_SETTINGS" 137 + else 138 + rm -f "$NOCTALIA_TMP" 139 + echo "noctalia declarative settings merge failed; leaving settings.json untouched" >&2 140 + fi 141 + ''; 142 + 143 + config.meta.maintainers = [ 144 + { 145 + name = "lassulus"; 146 + github = "lassulus"; 147 + githubId = 621375; 148 + } 149 + ]; 150 + config.meta.platforms = lib.platforms.linux; 151 + }; 152 + }
+3 -40
2configs/desktops/sway/noctalia.nix
··· 5 5 ... 6 6 }: 7 7 let 8 - sway-focus-workspace = pkgs.writeShellScript "sway-focus-workspace" '' 9 - # Bring a workspace to the currently focused output 10 - ws="$1" 11 - current_output=$(${pkgs.sway}/bin/swaymsg -t get_workspaces | ${pkgs.jq}/bin/jq -r '.[] | select(.focused) | .output') 12 - ${pkgs.sway}/bin/swaymsg "workspace $ws, move workspace to output $current_output" 13 - ''; 14 - # Patch noctalia-shell for sway: 15 - # - Use workspace name instead of number (activate() sends "workspace number <num>" which fails for named workspaces) 16 - # - Show all workspaces on all screens (globalWorkspaces) 17 - # - Bring workspace to current screen on click instead of jumping to its screen 18 - noctalia-shell-patched = pkgs.noctalia-shell.overrideAttrs (old: { 19 - postInstall = (old.postInstall or "") + '' 20 - substituteInPlace $out/share/noctalia-shell/Services/Compositor/SwayService.qml \ 21 - --replace-fail 'workspace.handle.activate();' \ 22 - 'Quickshell.execDetached(["${sway-focus-workspace}", workspace.name]);' \ 23 - --replace-fail 'property bool initialized: false' \ 24 - 'property bool globalWorkspaces: true 25 - property bool initialized: false' 26 - ''; 27 - }); 8 + noctalia-shell = self.packages.${pkgs.system}.noctalia-shell; 28 9 in 29 10 { 30 11 systemd.user.services.noctalia-shell = { ··· 47 28 pkgs.wl-clipboard 48 29 ]; 49 30 serviceConfig = { 50 - ExecStart = "${noctalia-shell-patched}/bin/noctalia-shell"; 31 + ExecStart = "${noctalia-shell}/bin/noctalia-shell"; 51 32 Restart = "on-failure"; 52 33 }; 53 34 }; 54 35 55 36 environment.systemPackages = [ 56 - noctalia-shell-patched 37 + noctalia-shell 57 38 # ryzenadj and power-profile for power profile control 58 39 pkgs.ryzenadj 59 40 self.packages.${pkgs.system}.power-profile ··· 85 66 services.udev.extraRules = '' 86 67 SUBSYSTEM=="powercap", ACTION=="add", RUN+="${pkgs.coreutils}/bin/chmod a+r /sys/class/powercap/%k/energy_uj" 87 68 ''; 88 - 89 - # Configure noctalia darkModeChange hook to trigger switch-theme 90 - # This makes noctalia's dark mode button also switch the system theme 91 - system.activationScripts.noctalia-hooks.text = 92 - let 93 - jq = "${pkgs.jq}/bin/jq"; 94 - settingsFile = "/home/${config.users.users.mainUser.name}/.config/noctalia/settings.json"; 95 - in 96 - '' 97 - if [ -f "${settingsFile}" ]; then 98 - ${jq} ' 99 - .hooks.enabled = true | 100 - .hooks.darkModeChange = "if [ \"$1\" = \"true\" ]; then switch-theme dark; else switch-theme light; fi" 101 - ' "${settingsFile}" > "${settingsFile}.tmp" && \ 102 - mv "${settingsFile}.tmp" "${settingsFile}" && \ 103 - chown ${config.users.users.mainUser.name}:users "${settingsFile}" || true 104 - fi 105 - ''; 106 69 }