From 48c58e2fbf5e600679a3aa8002a33998f2423b68 Mon Sep 17 00:00:00 2001 From: Damocles Date: Sun, 12 Apr 2026 15:54:58 +0200 Subject: [PATCH 1/3] group modules --- modules/Bar.qml | 48 ++++++++++++++++++++++++++++++++------------ modules/BarGroup.qml | 26 ++++++++++++++++++++++++ modules/Wlogout.qml | 1 + modules/qmldir | 1 + 4 files changed, 63 insertions(+), 13 deletions(-) create mode 100644 modules/BarGroup.qml diff --git a/modules/Bar.qml b/modules/Bar.qml index c0ee33b..27bf8f2 100644 --- a/modules/Bar.qml +++ b/modules/Bar.qml @@ -72,19 +72,41 @@ PanelWindow { Item { Layout.fillWidth: true } - M.Mpris {} - M.Volume { visible: M.Modules.volume } - M.Bluetooth {} - M.Backlight {} - M.Network { visible: M.Modules.network } - M.PowerProfile { visible: M.Modules.powerProfile } - M.IdleInhibitor { visible: M.Modules.idleInhibitor } - M.Weather { visible: M.Modules.weather } - M.Temperature { visible: M.Modules.temperature } - M.Cpu { visible: M.Modules.cpu } - M.Memory { visible: M.Modules.memory } - M.Disk { visible: M.Modules.disk } - M.Battery {} + + // Media + M.BarGroup { + M.Mpris {} + M.Volume { visible: M.Modules.volume } + } + + // Connectivity + M.BarGroup { + M.Network { visible: M.Modules.network } + M.Bluetooth {} + } + + // Controls + M.BarGroup { + M.Backlight {} + M.PowerProfile { visible: M.Modules.powerProfile } + M.IdleInhibitor { visible: M.Modules.idleInhibitor } + } + + // System + M.BarGroup { + M.Cpu { visible: M.Modules.cpu } + M.Memory { visible: M.Modules.memory } + M.Temperature { visible: M.Modules.temperature } + } + + // Status + M.BarGroup { + M.Weather { visible: M.Modules.weather } + M.Disk { visible: M.Modules.disk } + M.Battery {} + } + + // Power M.Wlogout { bar: bar; visible: M.Modules.wlogout } } } diff --git a/modules/BarGroup.qml b/modules/BarGroup.qml new file mode 100644 index 0000000..1e2d37f --- /dev/null +++ b/modules/BarGroup.qml @@ -0,0 +1,26 @@ +import QtQuick +import "." as M + +Rectangle { + id: root + + default property alias content: row.children + + color: "transparent" + border.color: M.Theme.base02 + border.width: 1 + radius: M.Theme.radius + + visible: row.visibleChildren.length > 0 + + implicitWidth: row.implicitWidth + _pad * 2 + implicitHeight: row.implicitHeight + _pad * 2 + + readonly property int _pad: 6 + + Row { + id: row + anchors.centerIn: parent + spacing: M.Theme.moduleSpacing + 2 + } +} diff --git a/modules/Wlogout.qml b/modules/Wlogout.qml index 32dc365..4e388f2 100644 --- a/modules/Wlogout.qml +++ b/modules/Wlogout.qml @@ -1,5 +1,6 @@ import QtQuick import Quickshell +import Quickshell.Io import "." as M M.BarIcon { diff --git a/modules/qmldir b/modules/qmldir index 8dd63bb..ccad069 100644 --- a/modules/qmldir +++ b/modules/qmldir @@ -4,6 +4,7 @@ singleton FlyoutState 1.0 FlyoutState.qml singleton OsdState 1.0 OsdState.qml singleton Modules 1.0 Modules.qml Bar 1.0 Bar.qml +BarGroup 1.0 BarGroup.qml BarSection 1.0 BarSection.qml Flyout 1.0 Flyout.qml Workspaces 1.0 Workspaces.qml From 4df704844e7a8e8f86469d27e9a0602a10079d5b Mon Sep 17 00:00:00 2001 From: Damocles Date: Sun, 12 Apr 2026 15:57:22 +0200 Subject: [PATCH 2/3] rename power --- README.md | 6 +++--- modules/Bar.qml | 2 +- modules/Modules.qml | 2 +- modules/{Wlogout.qml => Power.qml} | 0 modules/qmldir | 2 +- nix/hm-module.nix | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) rename modules/{Wlogout.qml => Power.qml} (100%) diff --git a/README.md b/README.md index a70b484..d06c4c0 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ exactly when you should be most suspicious. - OSD overlay for volume and brightness changes — works with niri hotkeys, not just the bar - Screen corner rounding — tiny overlay windows that draw quarter-circle masks, configurable via `screenRadius` (set to 0 if you prefer your corners sharp and your desktop ugly) - Weather via wttrbar with configurable arguments and rich HTML tooltips -- Power menu with lock, suspend, logout, reboot, shutdown — no more `wlogout` dependency +- Power menu with lock, suspend, logout, reboot, shutdown - Flyout tooltips that actually filter by screen on multi-monitor setups, which only took three attempts to get right - Home Manager module with stylix integration, per-module enable/disable, and a theme system that hot-reloads - treefmt + nixfmt for formatting, because even AI slop deserves consistent indentation @@ -75,14 +75,14 @@ programs.nova-shell.modules = { battery = false; # see above, but for power temperature = false; # what you don't measure can't alarm you disk = false; # the number will only make you anxious - wlogout = false; # if you enjoy living dangerously without a logout button + power = false; # if you enjoy living dangerously without a logout button }; ``` Full list of things you can disable: `workspaces`, `tray`, `windowTitle`, `clock`, `notifications`, `mpris`, `volume`, `bluetooth`, `backlight`, `network`, `powerProfile`, `idleInhibitor`, `weather`, `temperature`, `cpu`, -`memory`, `disk`, `battery`, `wlogout`. +`memory`, `disk`, `battery`, `power`. ### Theme diff --git a/modules/Bar.qml b/modules/Bar.qml index 27bf8f2..24e8db1 100644 --- a/modules/Bar.qml +++ b/modules/Bar.qml @@ -107,7 +107,7 @@ PanelWindow { } // Power - M.Wlogout { bar: bar; visible: M.Modules.wlogout } + M.Power { bar: bar; visible: M.Modules.power } } } } diff --git a/modules/Modules.qml b/modules/Modules.qml index c8c6d1d..d8a5d04 100644 --- a/modules/Modules.qml +++ b/modules/Modules.qml @@ -25,7 +25,7 @@ QtObject { property bool memory: true property bool disk: true property bool battery: true - property bool wlogout: true + property bool power: true property var weatherArgs: ["--nerd"] diff --git a/modules/Wlogout.qml b/modules/Power.qml similarity index 100% rename from modules/Wlogout.qml rename to modules/Power.qml diff --git a/modules/qmldir b/modules/qmldir index ccad069..0a84d2e 100644 --- a/modules/qmldir +++ b/modules/qmldir @@ -30,4 +30,4 @@ Weather 1.0 Weather.qml PowerProfile 1.0 PowerProfile.qml IdleInhibitor 1.0 IdleInhibitor.qml Notifications 1.0 Notifications.qml -Wlogout 1.0 Wlogout.qml +Power 1.0 Power.qml diff --git a/nix/hm-module.nix b/nix/hm-module.nix index 0f00a76..91aa91e 100644 --- a/nix/hm-module.nix +++ b/nix/hm-module.nix @@ -76,7 +76,7 @@ in "memory" "disk" "battery" - "wlogout" + "power" ] (name: lib.mkOption { type = lib.types.bool; From 9e1716aa394328b9de34e110072f4bcd65497c7c Mon Sep 17 00:00:00 2001 From: Damocles Date: Sun, 12 Apr 2026 15:58:13 +0200 Subject: [PATCH 3/3] nix fmt --- modules/Backlight.qml | 3 +- modules/Bar.qml | 58 ++++++++++++++++++++++++-------- modules/Battery.qml | 5 +-- modules/Bluetooth.qml | 23 +++++-------- modules/Flyout.qml | 5 +-- modules/PowerMenu.qml | 42 ++++++++++++++++++------ modules/PowerProfile.qml | 4 +-- modules/ScreenCorners.qml | 24 +++++++++++--- modules/Temperature.qml | 4 +-- modules/Tray.qml | 3 +- modules/TrayMenu.qml | 10 ++---- modules/WindowTitle.qml | 3 +- modules/Workspaces.qml | 3 +- nix/hm-module.nix | 69 ++++++++++++++++++++------------------- 14 files changed, 154 insertions(+), 102 deletions(-) diff --git a/modules/Backlight.qml b/modules/Backlight.qml index 03be350..040dc5a 100644 --- a/modules/Backlight.qml +++ b/modules/Backlight.qml @@ -9,7 +9,8 @@ M.BarSection { tooltip: "Brightness: " + root.percent + "%" property int percent: 0 - onPercentChanged: if (percent > 0) M.OsdState.show(percent / 100, "\uF185") + onPercentChanged: if (percent > 0) + M.OsdState.show(percent / 100, "\uF185") Process { id: adjProc diff --git a/modules/Bar.qml b/modules/Bar.qml index 24e8db1..c0ed034 100644 --- a/modules/Bar.qml +++ b/modules/Bar.qml @@ -39,8 +39,12 @@ PanelWindow { anchors.verticalCenter: parent.verticalCenter spacing: M.Theme.barSpacing - M.Clock { visible: M.Modules.clock } - M.Notifications { visible: M.Modules.notifications } + M.Clock { + visible: M.Modules.clock + } + M.Notifications { + visible: M.Modules.notifications + } } // ---- left ---- @@ -50,7 +54,10 @@ PanelWindow { anchors.verticalCenter: parent.verticalCenter spacing: M.Theme.barSpacing - M.Workspaces { bar: bar; visible: M.Modules.workspaces } + M.Workspaces { + bar: bar + visible: M.Modules.workspaces + } M.Tray { bar: bar visible: M.Modules.tray @@ -59,7 +66,9 @@ PanelWindow { Layout.maximumWidth: 400 visible: M.Modules.windowTitle } - Item { Layout.fillWidth: true } + Item { + Layout.fillWidth: true + } } // ---- right ---- @@ -76,38 +85,59 @@ PanelWindow { // Media M.BarGroup { M.Mpris {} - M.Volume { visible: M.Modules.volume } + M.Volume { + visible: M.Modules.volume + } } // Connectivity M.BarGroup { - M.Network { visible: M.Modules.network } + M.Network { + visible: M.Modules.network + } M.Bluetooth {} } // Controls M.BarGroup { M.Backlight {} - M.PowerProfile { visible: M.Modules.powerProfile } - M.IdleInhibitor { visible: M.Modules.idleInhibitor } + M.PowerProfile { + visible: M.Modules.powerProfile + } + M.IdleInhibitor { + visible: M.Modules.idleInhibitor + } } // System M.BarGroup { - M.Cpu { visible: M.Modules.cpu } - M.Memory { visible: M.Modules.memory } - M.Temperature { visible: M.Modules.temperature } + M.Cpu { + visible: M.Modules.cpu + } + M.Memory { + visible: M.Modules.memory + } + M.Temperature { + visible: M.Modules.temperature + } } // Status M.BarGroup { - M.Weather { visible: M.Modules.weather } - M.Disk { visible: M.Modules.disk } + M.Weather { + visible: M.Modules.weather + } + M.Disk { + visible: M.Modules.disk + } M.Battery {} } // Power - M.Power { bar: bar; visible: M.Modules.power } + M.Power { + bar: bar + visible: M.Modules.power + } } } } diff --git a/modules/Battery.qml b/modules/Battery.qml index a138917..5505efa 100644 --- a/modules/Battery.qml +++ b/modules/Battery.qml @@ -17,10 +17,7 @@ M.BarSection { readonly property var dev: UPower.displayDevice readonly property real pct: (dev?.percentage ?? 0) * 100 readonly property bool charging: dev?.state === UPowerDeviceState.Charging - readonly property color _stateColor: charging ? M.Theme.base0B - : pct < 15 ? M.Theme.base08 - : pct < 30 ? M.Theme.base09 - : M.Theme.base0B + readonly property color _stateColor: charging ? M.Theme.base0B : pct < 15 ? M.Theme.base08 : pct < 30 ? M.Theme.base09 : M.Theme.base0B M.BarIcon { icon: { diff --git a/modules/Bluetooth.qml b/modules/Bluetooth.qml index 64e35f8..66ff6d7 100644 --- a/modules/Bluetooth.qml +++ b/modules/Bluetooth.qml @@ -7,8 +7,10 @@ M.BarSection { spacing: M.Theme.moduleSpacing visible: M.Modules.bluetooth && root.state !== "unavailable" tooltip: { - if (root.state === "off") return "Bluetooth: off"; - if (root.state === "connected") return "Bluetooth: " + root.device; + if (root.state === "off") + return "Bluetooth: off"; + if (root.state === "connected") + return "Bluetooth: " + root.device; return "Bluetooth: on"; } @@ -18,20 +20,14 @@ M.BarSection { function _parse(text) { const t = text.trim(); const sep = t.indexOf(":"); - root.state = sep === -1 ? t : t.slice(0, sep); + root.state = sep === -1 ? t : t.slice(0, sep); root.device = sep === -1 ? "" : t.slice(sep + 1); } Process { id: proc running: true - command: ["sh", "-c", - "s=$(bluetoothctl show 2>/dev/null); " + - "[ -z \"$s\" ] && echo unavailable && exit; " + - "echo \"$s\" | grep -q 'Powered: yes' || { echo off:; exit; }; " + - "d=$(bluetoothctl info 2>/dev/null | awk -F': ' '/\\tName:/{n=$2}/Connected: yes/{c=1}END{if(c)print n}'); " + - "[ -n \"$d\" ] && echo \"connected:$d\" || echo on:" - ] + command: ["sh", "-c", "s=$(bluetoothctl show 2>/dev/null); " + "[ -z \"$s\" ] && echo unavailable && exit; " + "echo \"$s\" | grep -q 'Powered: yes' || { echo off:; exit; }; " + "d=$(bluetoothctl info 2>/dev/null | awk -F': ' '/\\tName:/{n=$2}/Connected: yes/{c=1}END{if(c)print n}'); " + "[ -n \"$d\" ] && echo \"connected:$d\" || echo on:"] stdout: StdioCollector { onStreamFinished: root._parse(text) } @@ -47,14 +43,13 @@ M.BarSection { id: toggle property string cmd: "" command: ["bluetoothctl", "power", cmd] - onRunningChanged: if (!running && cmd !== "") proc.running = true + onRunningChanged: if (!running && cmd !== "") + proc.running = true } M.BarIcon { icon: "\uF294" - color: root.state === "connected" ? M.Theme.base0D - : root.state === "off" ? M.Theme.base04 - : M.Theme.base0D + color: root.state === "connected" ? M.Theme.base0D : root.state === "off" ? M.Theme.base04 : M.Theme.base0D anchors.verticalCenter: parent.verticalCenter } M.BarLabel { diff --git a/modules/Flyout.qml b/modules/Flyout.qml index 80ad34d..78810ef 100644 --- a/modules/Flyout.qml +++ b/modules/Flyout.qml @@ -20,10 +20,7 @@ PanelWindow { // Flush below bar, centered on hovered item margins.top: 0 - margins.left: Math.max(0, Math.min( - Math.round(M.FlyoutState.itemX - implicitWidth / 2), - screen.width - implicitWidth - )) + margins.left: Math.max(0, Math.min(Math.round(M.FlyoutState.itemX - implicitWidth / 2), screen.width - implicitWidth)) implicitWidth: label.implicitWidth + M.Theme.barPadding * 2 implicitHeight: label.implicitHeight + M.Theme.barPadding * 2 diff --git a/modules/PowerMenu.qml b/modules/PowerMenu.qml index 59ab4b6..5b50de5 100644 --- a/modules/PowerMenu.qml +++ b/modules/PowerMenu.qml @@ -10,7 +10,7 @@ PanelWindow { required property var screen required property real anchorX - signal menuClosed() + signal menuClosed signal runCommand(var cmd) readonly property bool _isNiri: Quickshell.env("NIRI_SOCKET") !== "" @@ -40,10 +40,7 @@ PanelWindow { Item { id: panel - x: Math.max(0, Math.min( - Math.round(menuWindow.anchorX - menuCol.width / 2), - menuWindow.width - menuCol.width - )) + x: Math.max(0, Math.min(Math.round(menuWindow.anchorX - menuCol.width / 2), menuWindow.width - menuCol.width)) y: 0 width: menuCol.width @@ -73,11 +70,36 @@ PanelWindow { Repeater { model: [ - { label: "Lock", icon: "\uF023", cmd: ["loginctl", "lock-session"], color: M.Theme.base0D }, - { label: "Suspend", icon: "\uF186", cmd: ["systemctl", "suspend"], color: M.Theme.base0E }, - { label: "Logout", icon: "\uF2F5", cmd: menuWindow._isNiri ? ["niri", "msg", "action", "quit"] : ["loginctl", "terminate-user", ""], color: M.Theme.base0A }, - { label: "Reboot", icon: "\uF021", cmd: ["systemctl", "reboot"], color: M.Theme.base09 }, - { label: "Shutdown", icon: "\uF011", cmd: ["systemctl", "poweroff"], color: M.Theme.base08 } + { + label: "Lock", + icon: "\uF023", + cmd: ["loginctl", "lock-session"], + color: M.Theme.base0D + }, + { + label: "Suspend", + icon: "\uF186", + cmd: ["systemctl", "suspend"], + color: M.Theme.base0E + }, + { + label: "Logout", + icon: "\uF2F5", + cmd: menuWindow._isNiri ? ["niri", "msg", "action", "quit"] : ["loginctl", "terminate-user", ""], + color: M.Theme.base0A + }, + { + label: "Reboot", + icon: "\uF021", + cmd: ["systemctl", "reboot"], + color: M.Theme.base09 + }, + { + label: "Shutdown", + icon: "\uF011", + cmd: ["systemctl", "poweroff"], + color: M.Theme.base08 + } ] delegate: Item { diff --git a/modules/PowerProfile.qml b/modules/PowerProfile.qml index 904492f..f533c98 100644 --- a/modules/PowerProfile.qml +++ b/modules/PowerProfile.qml @@ -8,9 +8,7 @@ M.BarIcon { property string profile: "" - color: root.profile === "performance" ? M.Theme.base09 - : root.profile === "power-saver" ? M.Theme.base0B - : M.Theme.base05 + color: root.profile === "performance" ? M.Theme.base09 : root.profile === "power-saver" ? M.Theme.base0B : M.Theme.base05 icon: { if (root.profile === "performance") diff --git a/modules/ScreenCorners.qml b/modules/ScreenCorners.qml index 1164743..0e0b356 100644 --- a/modules/ScreenCorners.qml +++ b/modules/ScreenCorners.qml @@ -70,8 +70,24 @@ Item { } } - Corner { corner: 0; anchors.top: true; anchors.left: true } - Corner { corner: 1; anchors.top: true; anchors.right: true } - Corner { corner: 2; anchors.bottom: true; anchors.left: true } - Corner { corner: 3; anchors.bottom: true; anchors.right: true } + Corner { + corner: 0 + anchors.top: true + anchors.left: true + } + Corner { + corner: 1 + anchors.top: true + anchors.right: true + } + Corner { + corner: 2 + anchors.bottom: true + anchors.left: true + } + Corner { + corner: 3 + anchors.bottom: true + anchors.right: true + } } diff --git a/modules/Temperature.qml b/modules/Temperature.qml index 9b1d98e..a96c57a 100644 --- a/modules/Temperature.qml +++ b/modules/Temperature.qml @@ -8,9 +8,7 @@ M.BarSection { tooltip: "Temperature: " + root.celsius + "\u00B0C" property int celsius: 0 - readonly property color _stateColor: celsius > 80 ? M.Theme.base08 - : celsius > 60 ? M.Theme.base09 - : M.Theme.base0C + readonly property color _stateColor: celsius > 80 ? M.Theme.base08 : celsius > 60 ? M.Theme.base09 : M.Theme.base0C FileView { id: thermal diff --git a/modules/Tray.qml b/modules/Tray.qml index 2474a3f..a824348 100644 --- a/modules/Tray.qml +++ b/modules/Tray.qml @@ -30,8 +30,7 @@ RowLayout { HoverHandler { onHoveredChanged: { - const tip = [iconItem.modelData.tooltipTitle, iconItem.modelData.tooltipDescription] - .filter(s => s).join("\n") || iconItem.modelData.title; + const tip = [iconItem.modelData.tooltipTitle, iconItem.modelData.tooltipDescription].filter(s => s).join("\n") || iconItem.modelData.title; if (hovered && tip) { M.FlyoutState.text = tip; M.FlyoutState.itemX = iconItem.mapToGlobal(iconItem.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0); diff --git a/modules/TrayMenu.qml b/modules/TrayMenu.qml index 30aa161..51cfc43 100644 --- a/modules/TrayMenu.qml +++ b/modules/TrayMenu.qml @@ -13,7 +13,7 @@ PanelWindow { required property var screen required property real anchorX - signal menuClosed() + signal menuClosed // Current menu level — swapped when entering/leaving submenus property var _currentHandle: handle @@ -41,10 +41,7 @@ PanelWindow { Item { id: panel - x: Math.max(0, Math.min( - Math.round(menuWindow.anchorX - menuCol.width / 2), - menuWindow.width - menuCol.width - )) + x: Math.max(0, Math.min(Math.round(menuWindow.anchorX - menuCol.width / 2), menuWindow.width - menuCol.width)) y: 0 width: menuCol.width @@ -143,8 +140,7 @@ PanelWindow { anchors.fill: parent anchors.leftMargin: 4 anchors.rightMargin: 4 - color: rowArea.containsMouse && entryItem.modelData.enabled - ? M.Theme.base02 : "transparent" + color: rowArea.containsMouse && entryItem.modelData.enabled ? M.Theme.base02 : "transparent" radius: M.Theme.radius } diff --git a/modules/WindowTitle.qml b/modules/WindowTitle.qml index de1661a..08e694c 100644 --- a/modules/WindowTitle.qml +++ b/modules/WindowTitle.qml @@ -14,7 +14,8 @@ M.BarSection { property string _title: "" property string _appId: "" readonly property string _iconSource: { - if (!root._appId) return ""; + if (!root._appId) + return ""; const entry = DesktopEntries.heuristicLookup(root._appId); return entry ? Quickshell.iconPath(entry.icon) : ""; } diff --git a/modules/Workspaces.qml b/modules/Workspaces.qml index e185d6f..d4ebd17 100644 --- a/modules/Workspaces.qml +++ b/modules/Workspaces.qml @@ -44,8 +44,7 @@ Row { try { const ev = JSON.parse(line); if (ev.WorkspacesChanged !== undefined) { - root._allWorkspaces = ev.WorkspacesChanged.workspaces - .sort((a, b) => a.idx - b.idx); + root._allWorkspaces = ev.WorkspacesChanged.workspaces.sort((a, b) => a.idx - b.idx); } else if (ev.WorkspaceActivated !== undefined) { if (ev.WorkspaceActivated.focused) root._activeId = ev.WorkspaceActivated.id; diff --git a/nix/hm-module.nix b/nix/hm-module.nix index 91aa91e..d2b37d2 100644 --- a/nix/hm-module.nix +++ b/nix/hm-module.nix @@ -56,33 +56,37 @@ in description = "Enable or disable individual bar modules."; default = { }; type = lib.types.submodule { - options = lib.genAttrs - [ - "workspaces" - "tray" - "windowTitle" - "clock" - "notifications" - "mpris" - "volume" - "bluetooth" - "backlight" - "network" - "powerProfile" - "idleInhibitor" - "weather" - "temperature" - "cpu" - "memory" - "disk" - "battery" - "power" - ] - (name: lib.mkOption { - type = lib.types.bool; - default = true; - description = "Enable the ${name} module."; - }); + options = + lib.genAttrs + [ + "workspaces" + "tray" + "windowTitle" + "clock" + "notifications" + "mpris" + "volume" + "bluetooth" + "backlight" + "network" + "powerProfile" + "idleInhibitor" + "weather" + "temperature" + "cpu" + "memory" + "disk" + "battery" + "power" + ] + ( + name: + lib.mkOption { + type = lib.types.bool; + default = true; + description = "Enable the ${name} module."; + } + ); }; }; @@ -123,12 +127,11 @@ in }; config = lib.mkIf cfg.enable { - home.packages = - [ - self.packages.${pkgs.stdenv.hostPlatform.system}.nova-shell-cli - pkgs.nerd-fonts.symbols-only - ] - ++ lib.optional cfg.modules.weather pkgs.wttrbar; + home.packages = [ + self.packages.${pkgs.stdenv.hostPlatform.system}.nova-shell-cli + pkgs.nerd-fonts.symbols-only + ] + ++ lib.optional cfg.modules.weather pkgs.wttrbar; xdg.configFile."nova-shell/modules.json".source = (pkgs.formats.json { }).generate "nova-shell-modules.json"