From 3692156f63877d72567214153934837b508e41c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Fri, 29 May 2026 12:25:25 -0700 Subject: [PATCH 1/2] feat(lobtop): install and configure Karabiner Elements Adds a karabiner.nix darwin program module that writes karabiner.json during activation. Enables it on lobtop with the MX Master 4 M device and the three mission-control/space-switching complex modifications. Co-Authored-By: Claude Sonnet 4.6 --- hosts/lobtop/programs.nix | 58 ++++++++++++++++++++++++++ modules/darwin/programs/default.nix | 1 + modules/darwin/programs/karabiner.nix | 59 +++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 modules/darwin/programs/karabiner.nix diff --git a/hosts/lobtop/programs.nix b/hosts/lobtop/programs.nix index 032d2f6e..defe0817 100644 --- a/hosts/lobtop/programs.nix +++ b/hosts/lobtop/programs.nix @@ -19,6 +19,64 @@ enable = true; }; + programs.karabiner = { + enable = true; + startOnActivation = true; + # MX Master 4 M (Logitech) — vendor 1133, product 45122 + devices = [ + { + identifiers = { + is_pointing_device = true; + product_id = 45122; + vendor_id = 1133; + }; + ignore = false; + } + ]; + complexModifications = [ + { + description = "Mission control"; + manipulators = [ + { + type = "basic"; + from.pointing_button = "button7"; + to = [{key_code = "mission_control";}]; + } + ]; + } + { + description = "Thumb button → Right space"; + manipulators = [ + { + type = "basic"; + from.pointing_button = "button4"; + to = [ + { + key_code = "right_arrow"; + modifiers = ["control"]; + } + ]; + } + ]; + } + { + description = "Thumb button → Left space"; + manipulators = [ + { + type = "basic"; + from.pointing_button = "button5"; + to = [ + { + key_code = "left_arrow"; + modifiers = ["control"]; + } + ]; + } + ]; + } + ]; + }; + programs.fastscripts = { enable = true; startOnActivation = true; diff --git a/modules/darwin/programs/default.nix b/modules/darwin/programs/default.nix index 9e40cdfc..6d65a95b 100644 --- a/modules/darwin/programs/default.nix +++ b/modules/darwin/programs/default.nix @@ -6,6 +6,7 @@ ./daisydisk.nix ./fastscripts.nix ./iina.nix + ./karabiner.nix ./little-snitch.nix ./popclip.nix ./postico.nix diff --git a/modules/darwin/programs/karabiner.nix b/modules/darwin/programs/karabiner.nix new file mode 100644 index 00000000..1e4d1a79 --- /dev/null +++ b/modules/darwin/programs/karabiner.nix @@ -0,0 +1,59 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.programs.karabiner; + + configJson = builtins.toJSON { + global.show_in_menu_bar = false; + profiles = [ + { + complex_modifications.rules = cfg.complexModifications; + inherit (cfg) devices; + name = "Default profile"; + selected = true; + virtual_hid_keyboard.keyboard_type_v2 = "ansi"; + } + ]; + }; +in { + options.programs.karabiner = { + enable = mkEnableOption "Karabiner-Elements"; + + startOnActivation = mkEnableOption "starting Karabiner-Elements on activation"; + + devices = mkOption { + type = types.listOf types.attrs; + default = []; + description = "Device entries to include in the Karabiner profile."; + }; + + complexModifications = mkOption { + type = types.listOf types.attrs; + default = []; + description = "Complex modification rules to include in the Karabiner profile."; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [pkgs.karabiner-elements]; + + # Karabiner-Elements does not support being managed via defaults(1); write + # the JSON config directly during activation instead. + system.activationScripts.postActivation.text = let + user = lib.escapeShellArg config.system.primaryUser; + configFile = pkgs.writeText "karabiner.json" configJson; + in '' + echo "configuring karabiner-elements..." >&2 + sudo --user=${user} -- mkdir -p ~${user}/.config/karabiner + sudo --user=${user} -- cp -f ${configFile} ~${user}/.config/karabiner/karabiner.json + ''; + + system.startOnActivation = mkIf cfg.startOnActivation { + "karabiner_observer" = "${pkgs.karabiner-elements}/Applications/Karabiner-Elements.app/"; + }; + }; +} From 534a641fa22aa8ca0585c665c1bf5e7a301c551a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Wed, 3 Jun 2026 15:27:29 -0700 Subject: [PATCH 2/2] refactor(karabiner): add enableDefaults for shared MX Master 4 M config Moves the device and complex modification rules into the module as mkDefault values, activated by enableDefaults. This allows both lobtop and Rhizome to share the config without duplication. Simplifies lobtop to use enableDefaults = true. Co-Authored-By: Claude Sonnet 4.6 --- hosts/lobtop/programs.nix | 54 +-------------- modules/darwin/programs/karabiner.nix | 95 ++++++++++++++++++++++----- 2 files changed, 78 insertions(+), 71 deletions(-) diff --git a/hosts/lobtop/programs.nix b/hosts/lobtop/programs.nix index defe0817..9dceb7de 100644 --- a/hosts/lobtop/programs.nix +++ b/hosts/lobtop/programs.nix @@ -21,60 +21,8 @@ programs.karabiner = { enable = true; + enableDefaults = true; startOnActivation = true; - # MX Master 4 M (Logitech) — vendor 1133, product 45122 - devices = [ - { - identifiers = { - is_pointing_device = true; - product_id = 45122; - vendor_id = 1133; - }; - ignore = false; - } - ]; - complexModifications = [ - { - description = "Mission control"; - manipulators = [ - { - type = "basic"; - from.pointing_button = "button7"; - to = [{key_code = "mission_control";}]; - } - ]; - } - { - description = "Thumb button → Right space"; - manipulators = [ - { - type = "basic"; - from.pointing_button = "button4"; - to = [ - { - key_code = "right_arrow"; - modifiers = ["control"]; - } - ]; - } - ]; - } - { - description = "Thumb button → Left space"; - manipulators = [ - { - type = "basic"; - from.pointing_button = "button5"; - to = [ - { - key_code = "left_arrow"; - modifiers = ["control"]; - } - ]; - } - ]; - } - ]; }; programs.fastscripts = { diff --git a/modules/darwin/programs/karabiner.nix b/modules/darwin/programs/karabiner.nix index 1e4d1a79..9e36ef2f 100644 --- a/modules/darwin/programs/karabiner.nix +++ b/modules/darwin/programs/karabiner.nix @@ -23,6 +23,8 @@ in { options.programs.karabiner = { enable = mkEnableOption "Karabiner-Elements"; + enableDefaults = mkEnableOption "default MX Master 4 M device and mission-control/space-switching complex modifications"; + startOnActivation = mkEnableOption "starting Karabiner-Elements on activation"; devices = mkOption { @@ -38,22 +40,79 @@ in { }; }; - config = mkIf cfg.enable { - environment.systemPackages = [pkgs.karabiner-elements]; - - # Karabiner-Elements does not support being managed via defaults(1); write - # the JSON config directly during activation instead. - system.activationScripts.postActivation.text = let - user = lib.escapeShellArg config.system.primaryUser; - configFile = pkgs.writeText "karabiner.json" configJson; - in '' - echo "configuring karabiner-elements..." >&2 - sudo --user=${user} -- mkdir -p ~${user}/.config/karabiner - sudo --user=${user} -- cp -f ${configFile} ~${user}/.config/karabiner/karabiner.json - ''; - - system.startOnActivation = mkIf cfg.startOnActivation { - "karabiner_observer" = "${pkgs.karabiner-elements}/Applications/Karabiner-Elements.app/"; - }; - }; + config = mkMerge [ + (mkIf cfg.enableDefaults { + programs.karabiner.devices = mkDefault [ + # Logitech MX Master 4 M + { + identifiers = { + is_pointing_device = true; + product_id = 45122; + vendor_id = 1133; + }; + ignore = false; + } + ]; + programs.karabiner.complexModifications = mkDefault [ + { + description = "Mission control"; + manipulators = [ + { + type = "basic"; + from.pointing_button = "button7"; + to = [{key_code = "mission_control";}]; + } + ]; + } + { + description = "Thumb button → Right space"; + manipulators = [ + { + type = "basic"; + from.pointing_button = "button4"; + to = [ + { + key_code = "right_arrow"; + modifiers = ["control"]; + } + ]; + } + ]; + } + { + description = "Thumb button → Left space"; + manipulators = [ + { + type = "basic"; + from.pointing_button = "button5"; + to = [ + { + key_code = "left_arrow"; + modifiers = ["control"]; + } + ]; + } + ]; + } + ]; + }) + (mkIf cfg.enable { + environment.systemPackages = [pkgs.karabiner-elements]; + + # Karabiner-Elements does not support being managed via defaults(1); write + # the JSON config directly during activation instead. + system.activationScripts.postActivation.text = let + user = lib.escapeShellArg config.system.primaryUser; + configFile = pkgs.writeText "karabiner.json" configJson; + in '' + echo "configuring karabiner-elements..." >&2 + sudo --user=${user} -- mkdir -p ~${user}/.config/karabiner + sudo --user=${user} -- cp -f ${configFile} ~${user}/.config/karabiner/karabiner.json + ''; + + system.startOnActivation = mkIf cfg.startOnActivation { + "karabiner_observer" = "${pkgs.karabiner-elements}/Applications/Karabiner-Elements.app/"; + }; + }) + ]; }