summaryrefslogtreecommitdiff
path: root/users/kassouni/palette
diff options
context:
space:
mode:
authorAlexander Kassouni <alex@kassouni.net>2026-08-29 23:53:40 -0700
committerAlexander Kassouni <alex@kassouni.net>2026-08-29 23:53:40 -0700
commitaf99b4960a9bf1043234354d685ed23818c14aaa (patch)
tree9d6bd855c76c2c99e16321e1df4eeaadade28739 /users/kassouni/palette
parent8dcf355819cc6cd1e87924218c1adcd900ecda84 (diff)
new laptop
Diffstat (limited to 'users/kassouni/palette')
-rw-r--r--users/kassouni/palette/bluetooth.nix80
-rw-r--r--users/kassouni/palette/default.nix54
-rw-r--r--users/kassouni/palette/kbd-backlight.nix20
-rw-r--r--users/kassouni/palette/reboot.nix18
-rw-r--r--users/kassouni/palette/rebuild.nix40
-rw-r--r--users/kassouni/palette/screenshot.nix18
-rw-r--r--users/kassouni/palette/shutdown.nix18
7 files changed, 201 insertions, 47 deletions
diff --git a/users/kassouni/palette/bluetooth.nix b/users/kassouni/palette/bluetooth.nix
new file mode 100644
index 0000000..58cdf2f
--- /dev/null
+++ b/users/kassouni/palette/bluetooth.nix
@@ -0,0 +1,80 @@
+{ pkgs, config, ...}:
+{
+
+ kassouni.palette.commands = [
+ {
+ name = "toggle bluetooth device";
+ icon = "󰂯";
+ # `jc --bluetoothctl` turns `bluetoothctl devices` into JSON, so the menu is
+ # built from parsed records rather than by splitting "Device <addr> <name>"
+ # lines. That subcommand leaves every status field empty, so connectedness
+ # comes from a second `devices Connected` call intersected on address.
+ exe = pkgs.writers.writePython3 "bluetooth-toggle" {
+ # The interpolated store paths below are longer than 79 columns.
+ flakeIgnore = [ "E501" ];
+ } ''
+ import html
+ import json
+ import subprocess
+ import sys
+
+ BT = "${pkgs.bluez}/bin/bluetoothctl"
+ JC = "${pkgs.jc}/bin/jc"
+ ROFI = "${pkgs.rofi}/bin/rofi"
+
+
+ def devices(*args):
+ """Parse `bluetoothctl devices [args]` into a list of records."""
+ listed = subprocess.run(
+ [BT, "devices", *args], capture_output=True, text=True
+ )
+ parsed = subprocess.run(
+ [JC, "--bluetoothctl"],
+ input=listed.stdout,
+ capture_output=True,
+ text=True,
+ )
+ return json.loads(parsed.stdout or "[]")
+
+
+ def label(device, connected):
+ """Pango markup row. Names are escaped, they are not our text."""
+ name = html.escape(device["name"])
+ if device["address"] in connected:
+ name += ' <span alpha="45%">connected</span>'
+ return name
+
+
+ def main():
+ known = devices()
+ if not known:
+ return 0
+
+ # Older bluez has no `devices Connected`; that call just comes back
+ # empty there, which degrades to "nothing is connected".
+ connected = {d["address"] for d in devices("Connected")}
+
+ rows = [label(d, connected) for d in known]
+ choice = subprocess.run(
+ [ROFI, "-dmenu", "-i", "-markup-rows", "-format", "i",
+ "-p", "bluetooth"],
+ input="\n".join(rows),
+ capture_output=True,
+ text=True,
+ )
+ if choice.returncode != 0:
+ return 1
+
+ device = known[int(choice.stdout.strip())]
+ action = (
+ "disconnect" if device["address"] in connected else "connect"
+ )
+ return subprocess.run([BT, action, device["address"]]).returncode
+
+
+ if __name__ == "__main__":
+ sys.exit(main())
+ '';
+ }
+ ];
+}
diff --git a/users/kassouni/palette/default.nix b/users/kassouni/palette/default.nix
index 235f774..7e9eec4 100644
--- a/users/kassouni/palette/default.nix
+++ b/users/kassouni/palette/default.nix
@@ -1,18 +1,56 @@
-{ pkgs, lib, ... }:
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
let
- commandFiles = lib.attrNames (
- lib.filterAttrs (name: type: type == "regular" && name != "default.nix" && lib.hasSuffix ".nix" name)
- (builtins.readDir ./.)
+ cfg = config.kassouni.palette;
+
+ # Every sibling .nix file is an ordinary home-manager module that appends to
+ # kassouni.palette.commands, so dropping a file in here adds an entry.
+ commandModules = map (file: ./. + "/${file}") (
+ lib.attrNames (
+ lib.filterAttrs (name: type: type == "regular" && name != "default.nix" && lib.hasSuffix ".nix" name)
+ (builtins.readDir ./.)
+ )
);
- commands = map (file: pkgs.callPackage (./. + "/${file}") { }) commandFiles;
in
{
- home.packages = [
+ imports = commandModules;
+
+ options.kassouni.palette.commands = lib.mkOption {
+ type = lib.types.listOf (
+ lib.types.submodule {
+ options = {
+ name = lib.mkOption {
+ type = lib.types.str;
+ description = "Label shown in the palette menu.";
+ };
+ icon = lib.mkOption {
+ type = lib.types.str;
+ description = "Nerd Font glyph shown before the label.";
+ };
+ exe = lib.mkOption {
+ type = lib.types.package;
+ description = "Executable to run when the entry is selected.";
+ };
+ };
+ }
+ );
+ default = [ ];
+ description = ''
+ Entries shown in the rofi command palette. Menu order follows module
+ import order, which is alphabetical by filename.
+ '';
+ };
+
+ config.home.packages = [
(pkgs.writers.writeBashBin "open-palette" ''
set -eu
- exes=(${lib.concatMapStringsSep " " (c: c.exe) commands})
+ exes=(${lib.concatMapStringsSep " " (c: "${c.exe}") cfg.commands})
idx=$(${pkgs.rofi}/bin/rofi -dmenu -i -format i -p palette <<EOF
-${lib.concatMapStrings (c: "${c.icon} ${c.name}\n") commands}EOF
+${lib.concatMapStrings (c: "${c.icon} ${c.name}\n") cfg.commands}EOF
)
exec "''${exes[$idx]}"
'')
diff --git a/users/kassouni/palette/kbd-backlight.nix b/users/kassouni/palette/kbd-backlight.nix
new file mode 100644
index 0000000..9da0fb9
--- /dev/null
+++ b/users/kassouni/palette/kbd-backlight.nix
@@ -0,0 +1,20 @@
+{ pkgs, ... }:
+{
+ kassouni.palette.commands = [
+ {
+ name = "toggle keyboard backlight";
+ icon = "󰌌";
+ # The leds sysfs node is group-writable by `video`, which kassouni is in, so
+ # this needs no root. Toggle between off and full rather than cycling levels.
+ exe = pkgs.writers.writeBash "kbd-backlight" ''
+ set -eu
+ dev=framework_laptop::kbd_backlight
+ if [ "$(${pkgs.brightnessctl}/bin/brightnessctl -d "$dev" get)" -gt 0 ]; then
+ ${pkgs.brightnessctl}/bin/brightnessctl -d "$dev" set 0
+ else
+ ${pkgs.brightnessctl}/bin/brightnessctl -d "$dev" set 100%
+ fi
+ '';
+ }
+ ];
+}
diff --git a/users/kassouni/palette/reboot.nix b/users/kassouni/palette/reboot.nix
index bc25ef4..09b2efe 100644
--- a/users/kassouni/palette/reboot.nix
+++ b/users/kassouni/palette/reboot.nix
@@ -1,9 +1,13 @@
-{ writers, systemd }:
+{ pkgs, ... }:
{
- name = "reboot";
- icon = "󰑐";
- exe = writers.writeBash "reboot" ''
- set -eu
- exec ${systemd}/bin/systemctl reboot
- '';
+ kassouni.palette.commands = [
+ {
+ name = "reboot";
+ icon = "󰑐";
+ exe = pkgs.writers.writeBash "reboot" ''
+ set -eu
+ exec ${pkgs.systemd}/bin/systemctl reboot
+ '';
+ }
+ ];
}
diff --git a/users/kassouni/palette/rebuild.nix b/users/kassouni/palette/rebuild.nix
index e42a7c5..edffa6b 100644
--- a/users/kassouni/palette/rebuild.nix
+++ b/users/kassouni/palette/rebuild.nix
@@ -1,20 +1,24 @@
-{ writers, ghostty }:
+{ pkgs, ... }:
{
- name = "rebuild";
- icon = "󱄅";
- # Rebuilds need a terminal: sudo prompts for a password and nixos-rebuild
- # streams a lot of output. Spawn ghostty with a login shell so it inherits
- # the system sudo (setuid) and the user's nix from PATH. Mirrors `make rebuild`.
- exe = writers.writeBash "rebuild" ''
- set -eu
- exec ${ghostty}/bin/ghostty -e bash -lc '
- set -eu
- cd /home/kassouni/Developer/nixos-config
- nix flake update xmonad-config
- sudo nixos-rebuild switch --flake .
- echo
- echo "Rebuild finished. Press enter to close."
- read -r _
- '
- '';
+ kassouni.palette.commands = [
+ {
+ name = "rebuild";
+ icon = "󱄅";
+ # Rebuilds need a terminal: sudo prompts for a password and nixos-rebuild
+ # streams a lot of output. Spawn ghostty with a login shell so it inherits
+ # the system sudo (setuid) and the user's nix from PATH. Mirrors `make rebuild`.
+ exe = pkgs.writers.writeBash "rebuild" ''
+ set -eu
+ exec ${pkgs.ghostty}/bin/ghostty -e bash -lc '
+ set -eu
+ cd /home/kassouni/Developer/nixos-config
+ nix flake update xmonad-config
+ sudo nixos-rebuild switch --flake .
+ echo
+ echo "Rebuild finished. Press enter to close."
+ read -r _
+ '
+ '';
+ }
+ ];
}
diff --git a/users/kassouni/palette/screenshot.nix b/users/kassouni/palette/screenshot.nix
index 6f2beea..00d11d1 100644
--- a/users/kassouni/palette/screenshot.nix
+++ b/users/kassouni/palette/screenshot.nix
@@ -1,9 +1,13 @@
-{ writers, maim, xclip }:
+{ pkgs, ... }:
{
- name = "screenshot";
- icon = "󰹑";
- exe = writers.writeBash "screenshot" ''
- set -eu
- ${maim}/bin/maim -s | ${xclip}/bin/xclip -selection clipboard -t image/png
- '';
+ kassouni.palette.commands = [
+ {
+ name = "screenshot";
+ icon = "󰹑";
+ exe = pkgs.writers.writeBash "screenshot" ''
+ set -eu
+ ${pkgs.maim}/bin/maim -s | ${pkgs.xclip}/bin/xclip -selection clipboard -t image/png
+ '';
+ }
+ ];
}
diff --git a/users/kassouni/palette/shutdown.nix b/users/kassouni/palette/shutdown.nix
index 2db9425..28f8d8f 100644
--- a/users/kassouni/palette/shutdown.nix
+++ b/users/kassouni/palette/shutdown.nix
@@ -1,9 +1,13 @@
-{ writers, systemd }:
+{ pkgs, ... }:
{
- name = "shutdown";
- icon = "󰐥";
- exe = writers.writeBash "shutdown" ''
- set -eu
- exec ${systemd}/bin/systemctl poweroff
- '';
+ kassouni.palette.commands = [
+ {
+ name = "shutdown";
+ icon = "󰐥";
+ exe = pkgs.writers.writeBash "shutdown" ''
+ set -eu
+ exec ${pkgs.systemd}/bin/systemctl poweroff
+ '';
+ }
+ ];
}