Skip to content

Commit 1b31f04

Browse files
authored
Merge pull request #605 from benvonh/master
Add Home Manager module
2 parents f26ea6c + ea15ce5 commit 1b31f04

File tree

3 files changed

+271
-0
lines changed

3 files changed

+271
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ wayland.windowManager.hyprland.settings.exec-once = [
221221
222222
```
223223

224+
For information on the Home Manager module, click [here](https://hyprpanel.com/getting_started/installation.html#nixos-home-manager).
225+
224226
### Launch the panel
225227

226228
Afterwards you can run the panel with the following command in your terminal:

flake.nix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,5 +80,7 @@
8080
fi
8181
'';
8282
};
83+
84+
homeManagerModules.hyprpanel = import ./nix/module.nix self;
8385
};
8486
}

nix/module.nix

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
self: { lib, pkgs, config, ... }:
2+
let
3+
inherit (lib) types mkIf mkOption mkEnableOption;
4+
5+
cfg = config.programs.hyprpanel;
6+
7+
jsonFormat = pkgs.formats.json { };
8+
9+
# No package option
10+
package = self.packages.${pkgs.system}.default;
11+
12+
# Shorthand lambda for self-documenting options under settings
13+
mkStrOption = default: mkOption { type = types.str; default = default; };
14+
mkIntOption = default: mkOption { type = types.int; default = default; };
15+
mkBoolOption = default: mkOption { type = types.bool; default = default; };
16+
17+
# TODO: Please merge https://github.com/Jas-SinghFSU/HyprPanel/pull/497
18+
# Do not ask what these do...
19+
flattenAttrs = attrSet: prefix:
20+
let
21+
process = key: value:
22+
if builtins.isAttrs value then
23+
flattenAttrs value "${prefix}${key}."
24+
else
25+
{ "${prefix}${key}" = value; };
26+
in
27+
builtins.foldl' (acc: key:
28+
acc // process key attrSet.${key}
29+
) {} (builtins.attrNames attrSet);
30+
31+
toNestedValue =
32+
let
33+
escapeString = s: builtins.replaceStrings [ "\"" ] [ "\\\"" ] s;
34+
in
35+
value:
36+
if builtins.isBool value then
37+
if value then "true" else "false"
38+
else if (builtins.isInt value || builtins.isFloat value) then
39+
builtins.toString value
40+
else if builtins.isString value then
41+
"\"" + escapeString value + "\""
42+
else if builtins.isList value then
43+
let
44+
items = builtins.map toNestedValue value;
45+
in
46+
"[" + (builtins.concatStringsSep ", " items) + "]"
47+
else if builtins.isAttrs value then
48+
let
49+
keys = builtins.attrNames value;
50+
toKeyValue = k: "\"${k}\": ${toNestedValue value.${k}}";
51+
inner = builtins.concatStringsSep ", " (builtins.map toKeyValue keys);
52+
in
53+
"{ " + inner + " }"
54+
else
55+
abort "Unexpected error! Please post a new issue...";
56+
57+
toNestedObject = attrSet:
58+
let
59+
keys = builtins.attrNames attrSet;
60+
kvPairs = builtins.map (k: "\"${k}\": ${toNestedValue attrSet.${k}}") keys;
61+
in
62+
"{ " + builtins.concatStringsSep ", " kvPairs + " }";
63+
in
64+
{
65+
options.programs.hyprpanel = {
66+
enable = mkEnableOption "HyprPanel";
67+
systemd.enable = mkEnableOption "systemd integration";
68+
hyprland.enable = mkEnableOption "Hyprland integration";
69+
overwrite.enable = mkEnableOption "overwrite config fix";
70+
71+
theme = mkOption {
72+
type = types.str;
73+
default = null;
74+
example = "catppuccin_mocha";
75+
description = "Theme to import (see ./themes/*.json)";
76+
};
77+
78+
layout = mkOption {
79+
type = jsonFormat.type;
80+
default = null;
81+
example = ''
82+
{
83+
"bar.layouts" = {
84+
"0" = {
85+
left = [ "dashboard" "workspaces" "windowtitle" ];
86+
middle = [ "media" ];
87+
right = [ "volume" "network" "bluetooth" "battery" "systray" "clock" "notifications" ];
88+
};
89+
"1" = {
90+
left = [ "dashboard" "workspaces" "windowtitle" ];
91+
middle = [ "media" ];
92+
right = [ "volume" "clock" "notifications" ];
93+
};
94+
"2" = {
95+
left = [ "dashboard" "workspaces" "windowtitle" ];
96+
middle = [ "media" ];
97+
right = [ "volume" "clock" "notifications" ];
98+
};
99+
};
100+
};
101+
'';
102+
description = "https://hyprpanel.com/configuration/panel.html";
103+
};
104+
105+
settings = {
106+
bar.autoHide = mkStrOption "never";
107+
bar.customModules.updates.pollingInterval = mkIntOption 1440000;
108+
bar.customModules.updates.updateCommand = mkOption {
109+
type = types.str; default = ""; description = "not applicable with nix";
110+
};
111+
bar.launcher.autoDetectIcon = mkBoolOption false;
112+
bar.launcher.icon = mkStrOption "󰣇";
113+
bar.launcher.middleClick = mkStrOption "";
114+
bar.launcher.rightClick = mkStrOption "";
115+
bar.launcher.scrollDown = mkStrOption "";
116+
bar.launcher.scrollUp = mkStrOption "";
117+
bar.workspaces.applicationIconOncePerWorkspace = mkBoolOption true;
118+
bar.workspaces.hideUnoccupied = mkBoolOption true;
119+
bar.workspaces.icons.active = mkStrOption "";
120+
bar.workspaces.icons.available = mkStrOption "";
121+
bar.workspaces.icons.occupied = mkStrOption "";
122+
bar.workspaces.monitorSpecific = mkBoolOption true;
123+
bar.workspaces.numbered_active_indicator = mkStrOption "underline";
124+
bar.workspaces.showAllActive = mkBoolOption true;
125+
bar.workspaces.showApplicationIcons = mkBoolOption false;
126+
bar.workspaces.showWsIcons = mkBoolOption false;
127+
bar.workspaces.show_icons = mkBoolOption false;
128+
bar.workspaces.show_numbered = mkBoolOption false;
129+
bar.workspaces.workspaces = mkIntOption 5;
130+
hyprpanel.restartAgs = mkBoolOption true;
131+
# FIXME: Flag does not exist anymore
132+
# hyprpanel.restartCommand = mkStrOption "hyprpanel -q; hyprpanel";
133+
hyprpanel.restartCommand = mkStrOption "${pkgs.procps}/bin/pkill -u $USER -USR1 hyprpanel; ${package}/bin/hyprpanel";
134+
menus.clock.time.hideSeconds = mkBoolOption false;
135+
menus.clock.time.military = mkBoolOption false;
136+
menus.clock.weather.enabled = mkBoolOption true;
137+
menus.clock.weather.interval = mkIntOption 60000;
138+
menus.clock.weather.key = mkStrOption "";
139+
menus.clock.weather.location = mkStrOption "Los Angeles";
140+
menus.clock.weather.unit = mkStrOption "imperial";
141+
menus.dashboard.controls.enabled = mkBoolOption true;
142+
menus.dashboard.directories.enabled = mkBoolOption true;
143+
menus.dashboard.directories.left.directory1.command = mkStrOption "bash -c \"xdg-open $HOME/Downloads/\"";
144+
menus.dashboard.directories.left.directory1.label = mkStrOption "󰉍 Downloads";
145+
menus.dashboard.directories.left.directory2.command = mkStrOption "bash -c \"xdg-open $HOME/Videos/\"";
146+
menus.dashboard.directories.left.directory2.label = mkStrOption "󰉏 Videos";
147+
menus.dashboard.directories.left.directory3.command = mkStrOption "bash -c \"xdg-open $HOME/Projects/\"";
148+
menus.dashboard.directories.left.directory3.label = mkStrOption "󰚝 Projects";
149+
menus.dashboard.directories.right.directory1.command = mkStrOption "bash -c \"xdg-open $HOME/Documents/\"";
150+
menus.dashboard.directories.right.directory1.label = mkStrOption "󱧶 Documents";
151+
menus.dashboard.directories.right.directory2.command = mkStrOption "bash -c \"xdg-open $HOME/Pictures/\"";
152+
menus.dashboard.directories.right.directory2.label = mkStrOption "󰉏 Pictures";
153+
menus.dashboard.directories.right.directory3.command = mkStrOption "bash -c \"xdg-open $HOME/\"";
154+
menus.dashboard.directories.right.directory3.label = mkStrOption "󱂵 Home";
155+
menus.dashboard.powermenu.avatar.image = mkStrOption "$HOME/.face.icon";
156+
menus.dashboard.powermenu.avatar.name = mkStrOption "system";
157+
menus.dashboard.powermenu.confirmation = mkBoolOption true;
158+
menus.dashboard.powermenu.logout = mkStrOption "hyprctl dispatch exit";
159+
menus.dashboard.powermenu.reboot = mkStrOption "systemctl reboot";
160+
menus.dashboard.powermenu.shutdown = mkStrOption "systemctl poweroff";
161+
menus.dashboard.powermenu.sleep = mkStrOption "systemctl suspend";
162+
menus.dashboard.shortcuts.enabled = mkBoolOption true;
163+
menus.dashboard.shortcuts.left.shortcut1.command = mkStrOption "microsoft-edge-stable";
164+
menus.dashboard.shortcuts.left.shortcut1.icon = mkStrOption "󰇩";
165+
menus.dashboard.shortcuts.left.shortcut1.tooltip = mkStrOption "Microsoft Edge";
166+
menus.dashboard.shortcuts.left.shortcut2.command = mkStrOption "spotify-launcher";
167+
menus.dashboard.shortcuts.left.shortcut2.icon = mkStrOption "";
168+
menus.dashboard.shortcuts.left.shortcut2.tooltip = mkStrOption "Spotify";
169+
menus.dashboard.shortcuts.left.shortcut3.command = mkStrOption "discord";
170+
menus.dashboard.shortcuts.left.shortcut3.icon = mkStrOption "";
171+
menus.dashboard.shortcuts.left.shortcut3.tooltip = mkStrOption "Discord";
172+
menus.dashboard.shortcuts.left.shortcut4.command = mkStrOption "rofi -show drun";
173+
menus.dashboard.shortcuts.left.shortcut4.icon = mkStrOption "";
174+
menus.dashboard.shortcuts.left.shortcut4.tooltip = mkStrOption "Search Apps";
175+
menus.dashboard.shortcuts.right.shortcut1.command = mkStrOption "sleep 0.5 && hyprpicker -a";
176+
menus.dashboard.shortcuts.right.shortcut1.icon = mkStrOption "";
177+
menus.dashboard.shortcuts.right.shortcut1.tooltip = mkStrOption "Color Picker";
178+
menus.dashboard.shortcuts.right.shortcut3.command = mkStrOption "bash -c \"${../scripts/snapshot.sh}\"";
179+
menus.dashboard.shortcuts.right.shortcut3.icon = mkStrOption "󰄀";
180+
menus.dashboard.shortcuts.right.shortcut3.tooltip = mkStrOption "Screenshot";
181+
menus.dashboard.stats.enable_gpu = mkBoolOption false;
182+
menus.dashboard.stats.enabled = mkBoolOption true;
183+
menus.dashboard.stats.interval = mkIntOption 2000;
184+
menus.media.displayTime = mkBoolOption false;
185+
menus.media.displayTimeTooltip = mkBoolOption false;
186+
menus.media.hideAlbum = mkBoolOption false;
187+
menus.media.hideAuthor = mkBoolOption false;
188+
menus.media.noMediaText = mkStrOption "No Media Currently Playing";
189+
menus.transition = mkStrOption "crossfade";
190+
menus.transitionTime = mkIntOption 200;
191+
tear = mkBoolOption false;
192+
terminal = mkStrOption "$TERM";
193+
theme.bar.border.location = mkStrOption "none";
194+
theme.bar.buttons.borderSize = mkStrOption "0.1em";
195+
theme.bar.buttons.dashboard.enableBorder = mkBoolOption false;
196+
theme.bar.buttons.enableBorders = mkBoolOption false;
197+
theme.bar.buttons.style = mkStrOption "default";
198+
theme.bar.buttons.workspaces.enableBorder = mkBoolOption false;
199+
theme.bar.buttons.workspaces.smartHighlight = mkBoolOption true;
200+
theme.bar.floating = mkBoolOption false;
201+
theme.bar.layer = mkStrOption "top";
202+
theme.bar.location = mkStrOption "top";
203+
theme.bar.menus.monochrome = mkBoolOption false;
204+
theme.bar.transparent = mkBoolOption false;
205+
theme.font.name = mkStrOption "Ubuntu Nerd Font";
206+
theme.font.size = mkStrOption "1.2rem";
207+
theme.font.weight = mkIntOption 600;
208+
theme.matugen = mkBoolOption false;
209+
theme.matugen_settings.contrast = mkIntOption 0;
210+
theme.matugen_settings.mode = mkStrOption "dark";
211+
theme.matugen_settings.scheme_type = mkStrOption "tonal-spot";
212+
theme.matugen_settings.variation = mkStrOption "standard_1";
213+
wallpaper.enable = mkBoolOption true;
214+
wallpaper.image = mkStrOption "";
215+
wallpaper.pywal = mkBoolOption false;
216+
};
217+
};
218+
219+
config = mkIf cfg.enable {
220+
# TODO:(benvonh) Nerd font packaging changes in NixOS 25.05
221+
home.packages = [ package (pkgs.nerdfonts.override { fonts = [ "JetBrainsMono" ]; }) ];
222+
223+
# NOTE:(benvonh)
224+
# When changing the configuration through the GUI, HyprPanel will delete the `config.json` file and create a new
225+
# one in its place which destroys the original symlink to the current Home Manager generation. To work around this,
226+
# we can automatically delete the `config.json` file before generating a new config by enabling the
227+
# `overwrite.enable` option. Though, at some point, a proper fix should be implemented.
228+
home.activation =
229+
let
230+
path = "${config.xdg.configFile.hyprpanel.target}";
231+
in mkIf cfg.overwrite.enable {
232+
hyprpanel = lib.hm.dag.entryBefore [ "writeBoundary" ] ''
233+
[[ -L "${path}" ]] || rm "${path}"
234+
'';
235+
};
236+
237+
xdg.configFile.hyprpanel = let
238+
theme = if cfg.theme != null then builtins.fromJSON (builtins.readFile ../themes/${cfg.theme}.json) else {};
239+
flatSet = flattenAttrs (lib.attrsets.recursiveUpdate cfg.settings theme) "";
240+
mergeSet = if cfg.layout == null then flatSet else flatSet // cfg.layout;
241+
in {
242+
target = "hyprpanel/config.json";
243+
text = toNestedObject mergeSet;
244+
onChange = "${pkgs.procps}/bin/pkill -u $USER -USR1 hyprpanel || true";
245+
};
246+
247+
systemd.user.services = mkIf cfg.systemd.enable {
248+
hyprpanel = {
249+
Unit = {
250+
Description = "A Bar/Panel for Hyprland with extensive customizability.";
251+
Documentation = "https://hyprpanel.com";
252+
PartOf = [ "graphical-session.target" ];
253+
After = [ "graphical-session-pre.target" ];
254+
};
255+
Service = {
256+
ExecStart = "${package}/bin/hyprpanel";
257+
ExecReload = "${pkgs.coreutils}/bin/kill -SIGUSR1 $MAINPID";
258+
Restart = "on-failure";
259+
KillMode = "mixed";
260+
};
261+
Install = { WantedBy = [ "graphical-session.target" ]; };
262+
};
263+
};
264+
265+
wayland.windowManager.hyprland.settings.exec-once = mkIf cfg.hyprland.enable [ "${package}/bin/hyprpanel" ];
266+
};
267+
}

0 commit comments

Comments
 (0)