disable modules, README, warning fixes

This commit is contained in:
Damocles 2026-04-12 12:19:30 +02:00
parent ed68b9fd93
commit 5ad933c03b
9 changed files with 215 additions and 24 deletions

103
README.md
View file

@ -32,6 +32,109 @@ imports = [ inputs.nova-shell.homeModules.default ];
programs.nova-shell.enable = true; programs.nova-shell.enable = true;
``` ```
## Configuration
Surprisingly, the robot managed to wire up a Home Manager module that mostly works.
Here is how to make it do things.
### Flake setup
```nix
# flake.nix
inputs = {
nova-shell.url = "git+https://git.berlin.ccc.de/vinzenz/nova-shell";
nova-shell.inputs.nixpkgs.follows = "nixpkgs";
};
```
```nix
# home.nix
imports = [ inputs.nova-shell.homeModules.default ];
```
### Turning it on
```nix
programs.nova-shell.enable = true;
```
This installs the bar, the Symbols Nerd Font, and a systemd user service that
starts with `graphical-session.target`. If you use
[stylix](https://github.com/danth/stylix), colors and fonts are populated
automatically — one fewer thing for the AI to have gotten wrong.
### Disabling modules
All modules are on by default because the robot was optimistic. Set any to
`false` to get rid of them. Disabling `weather` also skips pulling in
`wttrbar`, which is the one genuinely useful thing the module system does.
```nix
programs.nova-shell.modules = {
weather = false; # also removes the wttrbar dependency
bluetooth = false; # unless you enjoy a ghost icon on your desktop
backlight = false; # desktops don't have screens that dim, allegedly
battery = false; # see above
temperature = false; # ignorance is thermally efficient
disk = false;
wlogout = false; # if you enjoy living dangerously without a logout button
};
```
Full list of things you can disable: `tray`, `windowTitle`, `clock`,
`notifications`, `mpris`, `volume`, `bluetooth`, `backlight`, `network`,
`powerProfile`, `idleInhibitor`, `weather`, `temperature`, `cpu`, `memory`,
`disk`, `battery`, `wlogout`.
### Theme
Theme keys merge on top of whatever stylix provides, so you only need to
specify what you want to override. The AI picked Catppuccin Mocha as the
fallback, because of course it did.
```nix
programs.nova-shell.theme = {
barHeight = 28;
barOpacity = 0.85;
barPadding = 10;
barSpacing = 8;
radius = 6;
fontSize = 13;
fontFamily = "JetBrains Mono";
# override individual palette entries if stylix's choices offend you
colors.base00 = "#1a1a2e";
colors.base05 = "#e0e0f0";
};
```
Full list of theme keys and their defaults:
| Key | Default | Controls |
|-----|---------|----------|
| `colors.base00``base0F` | Catppuccin Mocha | Base16 palette |
| `fontFamily` | `"sans-serif"` | Bar text font |
| `iconFontFamily` | `"Symbols Nerd Font"` | Nerd font for icons |
| `fontSize` | `12` | Base font size (px) |
| `barHeight` | `32` | Bar height (px) |
| `barOpacity` | `0.9` | Bar and flyout background opacity |
| `barPadding` | `8` | Left/right bar content margin (px) |
| `barSpacing` | `12` | Gap between modules (px) |
| `moduleSpacing` | `4` | Icon-to-label gap within a module (px) |
| `radius` | `4` | Corner radius for flyouts and menus (px) |
### Systemd service
Enabled by default. To attach it to a different target or disable it entirely
because you have opinions about service management:
```nix
programs.nova-shell.systemd = {
enable = true;
target = "niri.service";
};
```
## Contributing ## Contributing
Sure, why not. It can't get much worse. Sure, why not. It can't get much worse.

View file

@ -5,7 +5,7 @@ import "." as M
M.BarSection { M.BarSection {
id: root id: root
spacing: M.Theme.moduleSpacing spacing: M.Theme.moduleSpacing
visible: percent > 0 visible: M.Modules.backlight && percent > 0
tooltip: "Brightness: " + root.percent + "%" tooltip: "Brightness: " + root.percent + "%"
property int percent: 0 property int percent: 0

View file

@ -39,8 +39,8 @@ PanelWindow {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
spacing: M.Theme.barSpacing spacing: M.Theme.barSpacing
M.Clock {} M.Clock { visible: M.Modules.clock }
M.Notifications {} M.Notifications { visible: M.Modules.notifications }
} }
// ---- left ---- // ---- left ----
@ -52,9 +52,11 @@ PanelWindow {
M.Tray { M.Tray {
bar: bar bar: bar
visible: M.Modules.tray
} }
M.WindowTitle { M.WindowTitle {
Layout.maximumWidth: 400 Layout.maximumWidth: 400
visible: M.Modules.windowTitle
} }
} }
@ -69,19 +71,19 @@ PanelWindow {
Layout.fillWidth: true Layout.fillWidth: true
} }
M.Mpris {} M.Mpris {}
M.Volume {} M.Volume { visible: M.Modules.volume }
M.Bluetooth {} M.Bluetooth {}
M.Backlight {} M.Backlight {}
M.Network {} M.Network { visible: M.Modules.network }
M.PowerProfile {} M.PowerProfile { visible: M.Modules.powerProfile }
M.IdleInhibitor {} M.IdleInhibitor { visible: M.Modules.idleInhibitor }
M.Weather {} M.Weather { visible: M.Modules.weather }
M.Temperature {} M.Temperature { visible: M.Modules.temperature }
M.Cpu {} M.Cpu { visible: M.Modules.cpu }
M.Memory {} M.Memory { visible: M.Modules.memory }
M.Disk {} M.Disk { visible: M.Modules.disk }
M.Battery {} M.Battery {}
M.Wlogout {} M.Wlogout { visible: M.Modules.wlogout }
} }
} }
} }

View file

@ -5,7 +5,7 @@ import "." as M
M.BarSection { M.BarSection {
id: root id: root
spacing: M.Theme.moduleSpacing spacing: M.Theme.moduleSpacing
visible: UPower.displayDevice?.isLaptopBattery ?? false visible: M.Modules.battery && (UPower.displayDevice?.isLaptopBattery ?? false)
tooltip: { tooltip: {
const state = root.charging ? "Charging" : "Discharging"; const state = root.charging ? "Charging" : "Discharging";
const t = root.charging ? root.dev?.timeToFull : root.dev?.timeToEmpty; const t = root.charging ? root.dev?.timeToFull : root.dev?.timeToEmpty;

View file

@ -5,7 +5,7 @@ import "." as M
M.BarSection { M.BarSection {
id: root id: root
spacing: M.Theme.moduleSpacing spacing: M.Theme.moduleSpacing
visible: root.state !== "unavailable" visible: M.Modules.bluetooth && root.state !== "unavailable"
tooltip: { tooltip: {
if (root.state === "off") return "Bluetooth: off"; if (root.state === "off") return "Bluetooth: off";
if (root.state === "connected") return "Bluetooth: " + root.device; if (root.state === "connected") return "Bluetooth: " + root.device;
@ -61,10 +61,9 @@ M.BarSection {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
MouseArea { TapHandler {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onTapped: {
toggle.cmd = root.state === "off" ? "on" : "off"; toggle.cmd = root.state === "off" ? "on" : "off";
toggle.running = true; toggle.running = true;
} }

48
modules/Modules.qml Normal file
View file

@ -0,0 +1,48 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
QtObject {
id: root
property bool tray: true
property bool windowTitle: true
property bool clock: true
property bool notifications: true
property bool mpris: true
property bool volume: true
property bool bluetooth: true
property bool backlight: true
property bool network: true
property bool powerProfile: true
property bool idleInhibitor: true
property bool weather: true
property bool temperature: true
property bool cpu: true
property bool memory: true
property bool disk: true
property bool battery: true
property bool wlogout: true
property FileView _file: FileView {
path: (Quickshell.env("XDG_CONFIG_HOME") || (Quickshell.env("HOME") + "/.config")) + "/nova-shell/modules.json"
watchChanges: true
onFileChanged: reload()
onLoaded: root._apply(text())
}
function _apply(raw) {
let data;
try {
data = JSON.parse(raw);
} catch (e) {
return;
}
for (const k of Object.keys(data)) {
if (k in root && typeof root[k] === "boolean")
root[k] = data[k];
}
}
}

View file

@ -5,7 +5,7 @@ import "." as M
M.BarSection { M.BarSection {
id: root id: root
spacing: M.Theme.moduleSpacing spacing: M.Theme.moduleSpacing
visible: player !== null visible: M.Modules.mpris && player !== null
tooltip: { tooltip: {
const p = root.player; const p = root.player;
if (!p) if (!p)
@ -14,7 +14,7 @@ M.BarSection {
if (p.trackTitle) if (p.trackTitle)
parts.push(p.trackTitle); parts.push(p.trackTitle);
if (p.trackArtists?.length) if (p.trackArtists?.length)
parts.push(p.trackArtists.join(", ")); parts.push(Array.isArray(p.trackArtists) ? p.trackArtists.join(", ") : p.trackArtists);
if (p.trackAlbum) if (p.trackAlbum)
parts.push(p.trackAlbum); parts.push(p.trackAlbum);
return parts.join("\n") || p.identity; return parts.join("\n") || p.identity;

View file

@ -1,6 +1,7 @@
module modules module modules
singleton Theme 1.0 Theme.qml singleton Theme 1.0 Theme.qml
singleton FlyoutState 1.0 FlyoutState.qml singleton FlyoutState 1.0 FlyoutState.qml
singleton Modules 1.0 Modules.qml
Bar 1.0 Bar.qml Bar 1.0 Bar.qml
BarSection 1.0 BarSection.qml BarSection 1.0 BarSection.qml
Flyout 1.0 Flyout.qml Flyout 1.0 Flyout.qml

View file

@ -52,6 +52,39 @@ in
description = "nova-shell package to use."; description = "nova-shell package to use.";
}; };
modules = lib.mkOption {
description = "Enable or disable individual bar modules.";
default = { };
type = lib.types.submodule {
options = lib.genAttrs
[
"tray"
"windowTitle"
"clock"
"notifications"
"mpris"
"volume"
"bluetooth"
"backlight"
"network"
"powerProfile"
"idleInhibitor"
"weather"
"temperature"
"cpu"
"memory"
"disk"
"battery"
"wlogout"
]
(name: lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable the ${name} module.";
});
};
};
theme = lib.mkOption { theme = lib.mkOption {
type = lib.types.attrsOf lib.types.anything; type = lib.types.attrsOf lib.types.anything;
default = { }; default = { };
@ -80,10 +113,15 @@ in
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
programs.nova-shell.theme = lib.mkIf stylixAvailable (lib.mkDefault stylixTheme); programs.nova-shell.theme = lib.mkIf stylixAvailable (lib.mkDefault stylixTheme);
home.packages = [ home.packages =
[
self.packages.${pkgs.stdenv.hostPlatform.system}.nova-shell-cli self.packages.${pkgs.stdenv.hostPlatform.system}.nova-shell-cli
pkgs.nerd-fonts.symbols-only 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" cfg.modules;
xdg.configFile."nova-shell/theme.json".source = xdg.configFile."nova-shell/theme.json".source =
(pkgs.formats.json { }).generate "nova-shell-theme.json" (pkgs.formats.json { }).generate "nova-shell-theme.json"