From af99b4960a9bf1043234354d685ed23818c14aaa Mon Sep 17 00:00:00 2001 From: Alexander Kassouni Date: Sat, 29 Aug 2026 23:53:40 -0700 Subject: new laptop --- TODO.md | 8 ++- flake.lock | 4 +- machines/common/base.nix | 2 + machines/common/x11.nix | 23 ++++++- machines/magician/configuration.nix | 28 +++++++- users/kassouni/firefox.nix | 2 + users/kassouni/fuzzel.nix | 23 ------- users/kassouni/home.nix | 2 +- users/kassouni/palette/bluetooth.nix | 80 +++++++++++++++++++++ users/kassouni/palette/default.nix | 54 ++++++++++++--- users/kassouni/palette/kbd-backlight.nix | 20 ++++++ users/kassouni/palette/reboot.nix | 18 +++-- users/kassouni/palette/rebuild.nix | 40 ++++++----- users/kassouni/palette/screenshot.nix | 18 +++-- users/kassouni/palette/shutdown.nix | 18 +++-- users/kassouni/wayland/default.nix | 8 +++ users/kassouni/wayland/fuzzel.nix | 23 +++++++ users/kassouni/wayland/sway.nix | 39 +++++++++++ users/kassouni/wayland/waybar.nix | 115 +++++++++++++++++++++++++++++++ users/kassouni/xbindkeys.nix | 32 +++++++++ 20 files changed, 480 insertions(+), 77 deletions(-) delete mode 100644 users/kassouni/fuzzel.nix create mode 100644 users/kassouni/palette/bluetooth.nix create mode 100644 users/kassouni/palette/kbd-backlight.nix create mode 100644 users/kassouni/wayland/default.nix create mode 100644 users/kassouni/wayland/fuzzel.nix create mode 100644 users/kassouni/wayland/sway.nix create mode 100644 users/kassouni/wayland/waybar.nix create mode 100644 users/kassouni/xbindkeys.nix diff --git a/TODO.md b/TODO.md index 7f6066f..ec4513f 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,9 @@ # TODO -- [ ] tailscale is not declarative +- [ ] tailscale more declarative +- [ ] declarative syncthing +- [ ] nvim binds, whichkey +- [ ] media keys +- [ ] trackpad tuning +- [ ] firefox vim extension +- [ ] glide browser diff --git a/flake.lock b/flake.lock index 2b489e2..bfda8af 100644 --- a/flake.lock +++ b/flake.lock @@ -332,8 +332,8 @@ "nixpkgs": "nixpkgs_4" }, "locked": { - "lastModified": 1787899585, - "narHash": "sha256-X5CUTMB5s1oaHId2vg/h51lPdU5Sl/LFkiSmsR0r+s4=", + "lastModified": 1788059985, + "narHash": "sha256-wNoWALnRqwCmDFct8ADoNtNsS5ihtobMy2WKVTeZw5A=", "path": "/home/kassouni/Developer/xmonad-config", "type": "path" }, diff --git a/machines/common/base.nix b/machines/common/base.nix index 46c82d9..4146d98 100644 --- a/machines/common/base.nix +++ b/machines/common/base.nix @@ -3,6 +3,8 @@ enable = true; }; + security.polkit.enable = true; + nix.settings.experimental-features = [ "nix-command" "flakes" diff --git a/machines/common/x11.nix b/machines/common/x11.nix index ed2084a..7fca346 100644 --- a/machines/common/x11.nix +++ b/machines/common/x11.nix @@ -1,4 +1,25 @@ -{...}: { +{pkgs, ...}: { + environment.systemPackages = [pkgs.brightnessctl]; + + # brightness keys, handled at the evdev level so they work in any WM + services.actkbd = { + enable = true; + bindings = [ + { + # XF86MonBrightnessUp + keys = [225]; + events = ["key"]; + command = "${pkgs.brightnessctl}/bin/brightnessctl set 5%+"; + } + { + # XF86MonBrightnessDown + keys = [224]; + events = ["key"]; + command = "${pkgs.brightnessctl}/bin/brightnessctl set 5%-"; + } + ]; + }; + services.displayManager.ly = { enable = true; x11Support = true; diff --git a/machines/magician/configuration.nix b/machines/magician/configuration.nix index 01734eb..6f085c5 100644 --- a/machines/magician/configuration.nix +++ b/machines/magician/configuration.nix @@ -7,6 +7,24 @@ services.upower.enable = true; + swapDevices = [{ + device = "/var/lib/swapfile"; + size = 16*1024; # 16 GB + }]; + + services.tlp = { + enable = true; + settings = { + CPU_SCALING_GOVERNOR_ON_AC = "performance"; + CPU_SCALING_GOVERNOR_ON_BAT = "powersave"; + }; + }; + + hardware.bluetooth = { + enable = true; + powerOnBoot = true; + }; + boot.loader.systemd-boot.enable = true; boot.loader.efi.canTouchEfiVariables = true; @@ -21,11 +39,17 @@ xrandr --output eDP-1 --mode 2880x1920 --rate 120.00 ''; - services.xserver.dpi = 192; + services.xserver.dpi = 168; networking.hostName = "magician"; - services.libinput.enable = true; + services.libinput = { + enable = true; + mouse.tapping = false; + # No bottom-right corner right-click; use two-finger click instead. + touchpad.clickMethod = "clickfinger"; + touchpad.naturalScrolling = true; + }; # This option defines the first version of NixOS you have installed on this particular machine, # and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions. diff --git a/users/kassouni/firefox.nix b/users/kassouni/firefox.nix index e1cee19..9bf130c 100644 --- a/users/kassouni/firefox.nix +++ b/users/kassouni/firefox.nix @@ -61,6 +61,7 @@ }; settings = { "browser.startup.homepage" = "about:blank"; + "browser.compactmode.show" = true; "startup.homepage_welcome_url" = "about:blank"; "extensions.autoDisableScopes" = 0; "browser.newtabpage.enabled" = false; @@ -71,6 +72,7 @@ "browser.newtabpage.activity-stream.showSponsoredTopSites" = false; "browser.newtabpage.activity-stream.widgets.enabled" = false; "browser.newtabpage.activity-stream.discoverystream.sections.enabled" = false; + }; search = { force = true; diff --git a/users/kassouni/fuzzel.nix b/users/kassouni/fuzzel.nix deleted file mode 100644 index a716b2b..0000000 --- a/users/kassouni/fuzzel.nix +++ /dev/null @@ -1,23 +0,0 @@ -{ - pkgs, - lib, - config, - ... -}: -{ - programs.fuzzel = { - enable = true; - settings = { - main = { - font = lib.mkForce "${config.theme.font.family}:size=${toString config.theme.font.size}"; - }; - - - - border = { - radius = 0; - }; - }; - }; - -} diff --git a/users/kassouni/home.nix b/users/kassouni/home.nix index 1b9fe48..6242863 100644 --- a/users/kassouni/home.nix +++ b/users/kassouni/home.nix @@ -8,7 +8,6 @@ imports = [ ./neovim.nix ./claude.nix - ./fuzzel.nix ./rofi.nix ./music.nix ./fonts.nix @@ -22,6 +21,7 @@ ./firefox.nix ./cursor.nix ./bash.nix + ./xbindkeys.nix ./scripts ./palette ]; 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 " + # 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 += ' connected' + 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 <