diff --git a/users/modules/shell/default.nix b/users/modules/shell/default.nix index 36d8133..bfaa354 100644 --- a/users/modules/shell/default.nix +++ b/users/modules/shell/default.nix @@ -78,6 +78,7 @@ in { ./fish.nix ./starship.nix ./tmux.nix + ./zellij.nix ./zsh.nix ./zoxide.nix ]; diff --git a/users/modules/shell/zellij.nix b/users/modules/shell/zellij.nix new file mode 100644 index 0000000..f422c38 --- /dev/null +++ b/users/modules/shell/zellij.nix @@ -0,0 +1,173 @@ +{ + pkgs, + lib, + config, + ... +}: +with lib; let + cfg = config.home.shell.zellij; + isEmpty = list: list == []; + keybind = { + options = { + bind = mkOption { + type = types.either types.str (types.listOf types.str); + example = "Alt j"; + description = "Value used as a string after `bind` in zellij config"; + }; + actions = mkOption { + type = with types; let + allowed = oneOf [int str]; + in + attrsOf (either (listOf allowed) allowed); + default = {}; + example = { + SwitchToMode = ["normal"]; + SwitchFocus = []; + PaneNameInput = [0]; + }; + }; + useUnlockFirst = mkOption { + type = types.bool; + default = false; + description = '' + Go back to locked mode after the actions are done. + + Only works when `config.home.shell.zellij.useUnlockFirst` is true. + ''; + }; + }; + }; + convertBind = item: { + bind = let + useUnlockFirst = cfg.useUnlockFirst && item.useUnlockFirst; + children = + mapAttrsToList (action: args: let + actualArgs = + if isEmpty args + then {} + else {_args = lists.toList args;}; + in {"${action}" = actualArgs;}) + (item.actions + // ( + if useUnlockFirst + then {SwitchToMode = ["locked"];} + else {} + )); + in { + _args = lists.toList item.bind; + _children = children; + }; + }; + keybindsModule = { + options = mergeAttrsList (forEach [ + "normal" + "locked" + "resize" + "pane" + "move" + "tab" + "scroll" + "search" + "entersearch" + "renametab" + "renamepane" + "session" + "tmux" + ] + (x: { + "${x}" = mkOption { + type = types.listOf (types.submodule keybind); + default = []; + }; + })); + }; + makeKeybinds = keybinds: let + values = + attrsets.concatMapAttrs ( + mode: binds: + if (isEmpty binds) + then {} + else { + "${mode}"._children = lists.forEach binds convertBind; + } + ) + keybinds; + in + if values == {} + then {} + else { + keybinds = values; + }; +in { + options.home.shell.zellij = let + jsonFormat = pkgs.formats.yaml {}; + in { + enable = mkEnableOption "Enable Zellij"; + clearDefaultKeybinds = mkEnableOption "Clear default keybinds"; + settings = mkOption { + inherit (jsonFormat) type; + default = {}; + }; + layouts = mkOption { + inherit (jsonFormat) type; + default = {}; + }; + extraSettings = mkOption { + type = types.lines; + default = ""; + description = '' + Extra configuration lines to add to `$XDG_CONFIG_HOME/zellij/config.kdl` + ''; + }; + useUnlockFirst = mkEnableOption "Use Unlock-First (non-colliding) behaviour by default"; + plugins = mkOption { + type = types.listOf (types.submodule plugin); + default = {}; + example = [ + {name = "about";} + { + name = "filepicker"; + location = "zellij:strider"; + options = { + cwd = "/"; + }; + } + ]; + }; + keybinds = mkOption { + type = types.submodule keybindsModule; + default = {}; + example = { + pane = [ + { + bind = "c"; + actions = [ + { + action = "SwitchToMode"; + args = ["renamepane"]; + } + { + action = "PaneNameInput"; + args = [0]; + } + ]; + } + ]; + }; + }; + }; + config.programs.zellij = mkIf cfg.enable { + inherit (cfg) enable layouts; + extraConfig = cfg.extraSettings; + settings = let + resetKeybinds = + if cfg.clearDefaultKeybinds + then { + keybinds._props.clear-defaults = true; + } + else {}; + keybinds = makeKeybinds cfg.keybinds; + in + cfg.settings // resetKeybinds // keybinds; + }; +} diff --git a/users/phundrak/light-home.nix b/users/phundrak/light-home.nix index e41494d..84d74d3 100644 --- a/users/phundrak/light-home.nix +++ b/users/phundrak/light-home.nix @@ -7,7 +7,7 @@ with lib; let cfg = config.home.phundrak; in { - imports = [../modules]; + imports = [../modules ./zellij.nix]; options.home.phundrak = { sshKey = { diff --git a/users/phundrak/zellij.nix b/users/phundrak/zellij.nix new file mode 100644 index 0000000..b3fc65c --- /dev/null +++ b/users/phundrak/zellij.nix @@ -0,0 +1,264 @@ +{lib, ...}: { + home.shell.zellij = with lib; { + enable = true; + clearDefaultKeybinds = true; + useUnlockFirst = true; + settings.copy_on_select = true; + extraSettings = '' + plugins { + about location="zellij:about" + compact-bar location="zellij:compact-bar" + configuration location="zellij:configuration" + filepicker location="zellij:strider" { + cwd "/" + } + plugin-manager location="zellij:plugin-manager" + session-manager location="zellij:session-manager" + status-bar location="zellij:status-bar" + strider location="zellij:strider" + tab-bar location="zellij:tab-bar" + welcome-screen location="zellij:session-manager" { + welcome_screen true + } + } + ''; + keybinds = let + # bépo layout + left = ["c" "Left"]; + down = ["t" "Down"]; + up = ["s" "Up"]; + right = ["r" "Right"]; + numRow = ["\"" "«" "»" "(" ")" "@" "+" "-" "/" "*"]; + in { + locked = [ + { + bind = "Ctrl Alt g"; + actions = {SwitchToMode = "normal";}; + } + ]; + pane = [ + { + bind = left; + actions = {MoveFocus = "Left";}; + } + { + bind = down; + actions = {MoveFocus = "Down";}; + } + { + bind = up; + actions = {MoveFocus = "Up";}; + } + { + bind = right; + actions = {MoveFocus = "Right";}; + } + { + bind = "n"; + actions = {NewPane = [];}; + useUnlockFirst = true; + } + { + bind = "T"; + actions = {NewPane = "Down";}; + useUnlockFirst = true; + } + { + bind = "R"; + actions = {NewPane = "Right";}; + useUnlockFirst = true; + } + { + bind = "S"; + actions = {NewPane = "stacked";}; + useUnlockFirst = true; + } + { + bind = "N"; + actions = {SwitchToMode = "normal";}; + } + { + bind = "e"; + actions = {TogglePaneEmbedOrFloating = [];}; + useUnlockFirst = true; + } + { + bind = "i"; + actions = {TogglePanePinned = [];}; + } + { + bind = "f"; + actions = {ToggleFocusFullscreen = [];}; + useUnlockFirst = true; + } + { + bind = "F"; + actions = {ToggleFloatingPanes = [];}; + } + { + bind = "q"; + actions = {CloseFocus = [];}; + useUnlockFirst = true; + } + { + bind = "p"; + actions = {SwitchToMode = "normal";}; + } + { + bind = "P"; + actions = { + SwitchToMode = "renamepane"; + PaneNameInput = 0; + }; + } + { + bind = "z"; + actions = {TogglePaneFrames = [];}; + useUnlockFirst = true; + } + { + bind = "tab"; + actions = {SwitchFocus = [];}; + } + ]; + resize = [ + { + bind = "n"; + actions = {SwitchToMode = "locked";}; + } + { + bind = left; + actions = {Resize = "Increase Left";}; + } + { + bind = down; + actions = {Resize = "Increase Down";}; + } + { + bind = up; + actions = {Resize = "Increase Up";}; + } + { + bind = right; + actions = {Resize = "Increase Right";}; + } + { + bind = "C"; + actions = {Resize = "Decrease Left";}; + } + { + bind = "T"; + actions = {Resize = "Decrease Down";}; + } + { + bind = "S"; + actions = {Resize = "Decrease Up";}; + } + { + bind = "R"; + actions = {Resize = "Decrease Right";}; + } + { + bind = "+"; + actions = {Resize = "Increase";}; + } + { + bind = "-"; + actions = {Resize = "Decrease";}; + } + ]; + move = [ + { + bind = left; + actions = {MovePane = "left";}; + } + { + bind = down; + actions = {MovePane = "down";}; + } + { + bind = up; + actions = {MovePane = "up";}; + } + { + bind = right; + actions = {MovePane = "right";}; + } + { + bind = "m"; + actions = {SwitchToMode = "normal";}; + } + { + bind = ["n" "tab"]; + actions = {MovePane = [];}; + } + { + bind = "p"; + actions = {MovePaneBackwards = [];}; + } + ]; + tab = + [ + { + bind = left ++ up; + actions = {GoToPreviousTab = [];}; + } + { + bind = down ++ right; + actions = {GoToNextTab = [];}; + } + { + bind = "["; + actions = {BreakPaneLeft = [];}; + useUnlockFirst = true; + } + { + bind = "]"; + actions = {BreakPaneRight = [];}; + useUnlockFirst = true; + } + { + bind = "b"; + actions = {BreakPane = [];}; + useUnlockFirst = true; + } + { + bind = "n"; + actions = {NewTab = [];}; + useUnlockFirst = true; + } + { + bind = "R"; + actions = { + SwitchToMode = "renametab"; + TabNameInput = 0; + }; + } + { + bind = "s"; + actions = {ToggleActiveSyncTab = [];}; + useUnlockFirst = true; + } + { + bind = "T"; + actions = {SwitchToMode = "normal";}; + } + { + bind = "x"; + actions = {CloseTab = [];}; + useUnlockFirst = true; + } + { + bind = "tab"; + actions = {ToggleTab = [];}; + } + ] + ++ (lists.imap1 (i: key: { + bind = key; + actions = {GoToTab = i;}; + useUnlockFirst = true; + }) + numRow); + }; + }; +}