From 3fe607d310c547e8729b448d41e1f36e8b07b0b8 Mon Sep 17 00:00:00 2001 From: jasmine Date: Fri, 3 Oct 2025 16:33:17 +0800 Subject: [PATCH 1/3] refactor(wezterm): organize keybindings with key table namespaces Implemented key tables to create clean, organized namespaces for tab and pane management. This refactoring improves keybinding discoverability and reduces cognitive load by grouping related operations. Key changes: - LEADER + t enters tab mode (n/q/r for new/quit/rename) - LEADER + p enters pane mode (s/v/q/m for split/vsplit/quit/maximize) - LEADER + Escape enters copy mode (vim-like pseudo-normal mode) - CTRL + SHIFT + v for paste (standard terminal convention) - Removed smart-splits plugin in favor of simpler native navigation - Navigation keys remain at top level for quick access --- .../features/desktop/wezterm/wezterm.lua | 179 +++++++++--------- 1 file changed, 91 insertions(+), 88 deletions(-) diff --git a/home-manager/sajenim/features/desktop/wezterm/wezterm.lua b/home-manager/sajenim/features/desktop/wezterm/wezterm.lua index 16b9586..ea9720b 100644 --- a/home-manager/sajenim/features/desktop/wezterm/wezterm.lua +++ b/home-manager/sajenim/features/desktop/wezterm/wezterm.lua @@ -4,9 +4,6 @@ local wezterm = require("wezterm") -- Log warnings or generate errors if we define an invalid configuration option local config = wezterm.config_builder() --- Install plugins -local smart_splits = wezterm.plugin.require("https://github.com/mrjones2014/smart-splits.nvim") - -- -- General configuration options. -- @@ -92,124 +89,80 @@ config.leader = { key = "a", mods = "ALT", timeout_milliseconds = 2000 } -- General keymaps config.keys = { -- - -- Tab management + -- Enter key table modes -- - { -- Spawn new tab + { -- Enter tab management mode key = "t", mods = "LEADER", - action = wezterm.action.SpawnTab("CurrentPaneDomain"), + action = wezterm.action.ActivateKeyTable({ + name = "tab_mode", + one_shot = true, + }), }, + { -- Enter pane management mode + key = "p", + mods = "LEADER", + action = wezterm.action.ActivateKeyTable({ + name = "pane_mode", + one_shot = true, + }), + }, + + -- + -- Navigation + -- + { -- Focus previous tab - key = "Home", + key = "LeftArrow", mods = "ALT", action = wezterm.action.ActivateTabRelative(-1), }, { -- Focus next tab - key = "End", + key = "RightArrow", mods = "ALT", action = wezterm.action.ActivateTabRelative(1), }, - { -- Quit/close tab - key = "Q", - mods = "LEADER", - action = wezterm.action.CloseCurrentTab({ confirm = false }), - }, - - { -- Rename current tab - key = ",", - mods = "LEADER", - action = wezterm.action_callback(function(window, pane) - local success, stdout, stderr = wezterm.run_child_process({ - "dmenu", - "-fn", - "Fisa Code-10", - "-p", - "Tab name:", - }) - if success and stdout then - local name = stdout:gsub("\n", "") - if name ~= "" then - window:active_tab():set_title(name) - end - end - end), - }, - - -- - -- Pane management - -- - - { -- Split pane vertically (bottom, 30%) - key = "s", - mods = "LEADER", - action = wezterm.action.SplitPane({ - direction = "Down", - size = { Percent = 30 }, - }), - }, - - { -- Split pane horizontally (left, 28%) - key = "v", - mods = "LEADER", - action = wezterm.action.SplitPane({ - direction = "Left", - size = { Percent = 28 }, - }), - }, - { -- Focus previous pane - key = "PageUp", + key = "UpArrow", mods = "ALT", action = wezterm.action.ActivatePaneDirection("Prev"), }, { -- Focus next pane - key = "PageDown", + key = "DownArrow", mods = "ALT", action = wezterm.action.ActivatePaneDirection("Next"), }, { -- Rotate panes counter-clockwise key = "PageUp", - mods = "ALT|CTRL", + mods = "ALT", action = wezterm.action.RotatePanes("CounterClockwise"), }, { -- Rotate panes clockwise key = "PageDown", - mods = "ALT|CTRL", + mods = "ALT", action = wezterm.action.RotatePanes("Clockwise"), }, - { -- Maximize/zoom pane - key = "m", - mods = "LEADER", - action = wezterm.action.TogglePaneZoomState, - }, - - { -- Quit/close pane - key = "q", - mods = "LEADER", - action = wezterm.action.CloseCurrentPane({ confirm = false }), - }, - -- -- Copy / Paste -- - { -- Activate vi copy mode - key = "x", + { -- Enter copy mode + key = "Escape", mods = "LEADER", action = wezterm.action.ActivateCopyMode, }, { -- Paste from clipboard - key = "p", - mods = "LEADER", + key = "v", + mods = "CTRL|SHIFT", action = wezterm.action.PasteFrom("Clipboard"), }, @@ -224,6 +177,68 @@ config.keys = { }, } +-- +-- Key table definitions for modal keybinding namespaces +-- + +config.key_tables = { + -- Tab management mode (LEADER + t) + tab_mode = { + { -- Create new tab + key = "n", + action = wezterm.action.SpawnTab("CurrentPaneDomain"), + }, + { -- Close current tab + key = "q", + action = wezterm.action.CloseCurrentTab({ confirm = false }), + }, + { -- Rename current tab + key = "r", + action = wezterm.action_callback(function(window, pane) + local success, stdout, stderr = wezterm.run_child_process({ + "dmenu", + "-fn", + "Fisa Code-10", + "-p", + "Tab name:", + }) + if success and stdout then + local name = stdout:gsub("\n", "") + if name ~= "" then + window:active_tab():set_title(name) + end + end + end), + }, + }, + + -- Pane management mode (LEADER + p) + pane_mode = { + { -- Split pane vertically (bottom, 30%) + key = "s", + action = wezterm.action.SplitPane({ + direction = "Down", + size = { Percent = 30 }, + }), + }, + { -- Split pane horizontally (left, 28%) + key = "v", + action = wezterm.action.SplitPane({ + direction = "Left", + size = { Percent = 28 }, + }), + }, + { -- Close current pane + key = "q", + action = wezterm.action.CloseCurrentPane({ confirm = false }), + }, + { -- Maximize/zoom pane + key = "m", + action = wezterm.action.TogglePaneZoomState, + }, + }, +} + -- Jump to specific tabs by number (ALT + 1-9) for i = 1, 9 do table.insert(config.keys, { @@ -233,16 +248,4 @@ for i = 1, 9 do }) end --- --- Enable neovim integration --- - -smart_splits.apply_to_config(config, { - direction_keys = { "LeftArrow", "DownArrow", "UpArrow", "RightArrow" }, - modifiers = { - move = "ALT", - resize = "ALT|CTRL", - }, -}) - return config From 9971d2d2a51cf450866b1152ca5a10a7d2e6de29 Mon Sep 17 00:00:00 2001 From: jasmine Date: Fri, 3 Oct 2025 23:53:20 +0800 Subject: [PATCH 2/3] feat(wezterm): add Alt+Delete to focus master pane Implements spatial "master pane" focusing that mirrors XMonad's master window concept. Alt+Delete now focuses the largest pane in the current tab, completing the unified Delete key semantic across all tools: - Gui+Delete (XMonad): Focus master window (largest in layout) - Alt+Delete (WezTerm): Focus master pane (largest, ties to lowest index) - Delete (Neovim): Center cursor view The implementation is spatially-aware rather than content-aware, maintaining the navigation layer's positional abstraction. When panes are equal-sized, the lowest-indexed pane is chosen for predictability. --- .../features/desktop/wezterm/wezterm.lua | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/home-manager/sajenim/features/desktop/wezterm/wezterm.lua b/home-manager/sajenim/features/desktop/wezterm/wezterm.lua index ea9720b..eb94996 100644 --- a/home-manager/sajenim/features/desktop/wezterm/wezterm.lua +++ b/home-manager/sajenim/features/desktop/wezterm/wezterm.lua @@ -150,6 +150,29 @@ config.keys = { action = wezterm.action.RotatePanes("Clockwise"), }, + { -- Focus largest (master) pane + key = "Delete", + mods = "ALT", + action = wezterm.action_callback(function(window, pane) + local tab = window:active_tab() + local largest = nil + local largest_size = 0 + + for _, p in ipairs(tab:panes()) do + local dims = p:get_dimensions() + local size = dims.pixel_width * dims.pixel_height + if size > largest_size then + largest_size = size + largest = p + end + end + + if largest and largest:pane_id() ~= pane:pane_id() then + largest:activate() + end + end), + }, + -- -- Copy / Paste -- From d6e648595ab06320b0b574f3a57b3dc2e3064ce0 Mon Sep 17 00:00:00 2001 From: jasmine Date: Sat, 4 Oct 2025 10:25:53 +0800 Subject: [PATCH 3/3] refactor(zsh): use nixpkgs packages for zsh plugins Replace manual GitHub fetchFromGitHub with packaged versions of zsh plugins and consolidate plugin loading through the plugins list instead of manual sourcing. --- home-manager/sajenim/global/zsh.nix | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/home-manager/sajenim/global/zsh.nix b/home-manager/sajenim/global/zsh.nix index ae4948c..b8ad532 100644 --- a/home-manager/sajenim/global/zsh.nix +++ b/home-manager/sajenim/global/zsh.nix @@ -30,21 +30,20 @@ # Install plugins plugins = [ - { + { # vi(vim) mode for ZSH + name = "zsh-vi-mode"; + src = "${pkgs.zsh-vi-mode}/share/zsh-vi-mode"; + } + + { # replace zsh's completion with fzf name = "fzf-tab"; - src = pkgs.fetchFromGitHub { - owner = "Aloxaf"; - repo = "fzf-tab"; - rev = "5a81e13792a1eed4a03d2083771ee6e5b616b9ab"; - sha256 = "dPe5CLCAuuuLGRdRCt/nNruxMrP9f/oddRxERkgm1FE="; - }; + src = "${pkgs.zsh-fzf-tab}/share/fzf-tab"; } ]; # Extra commands that should be added to '.zshrc' initContent = '' eval "$(direnv hook zsh)" - source ${pkgs.zsh-vi-mode}/share/zsh-vi-mode/zsh-vi-mode.plugin.zsh bindkey "^[[1;5C" forward-word bindkey "^[[1;5D" backward-word export PATH