Add wezterm binds

This commit is contained in:
muon 2026-03-08 11:39:06 +00:00
parent 86c3dbfa88
commit 3a4353b990
3 changed files with 310 additions and 153 deletions

View file

@ -28,70 +28,155 @@
# vi directions: key, arrow alias, wezterm direction name
viDirs = [
{key = "h"; arrow = "LeftArrow"; dir = "Left";}
{key = "j"; arrow = "DownArrow"; dir = "Down";}
{key = "k"; arrow = "UpArrow"; dir = "Up";}
{key = "l"; arrow = "RightArrow"; dir = "Right";}
{
key = "h";
arrow = "LeftArrow";
dir = "Left";
}
{
key = "j";
arrow = "DownArrow";
dir = "Down";
}
{
key = "k";
arrow = "UpArrow";
dir = "Up";
}
{
key = "l";
arrow = "RightArrow";
dir = "Right";
}
];
# hjkl + arrow equivalents; mkAction :: dir -> lua-action-string
viDirKeys = {mods ? null, indent ? " ", mkAction}:
viDirKeys = {
mods ? null,
indent ? " ",
mkAction,
}:
lib.concatMapStrings (d:
luaKey {inherit mods indent; key = d.key; action = mkAction d.dir;} +
luaKey {inherit mods indent; key = d.arrow; action = mkAction d.dir;})
luaKey {
inherit mods indent;
key = d.key;
action = mkAction d.dir;
}
+ luaKey {
inherit mods indent;
key = d.arrow;
action = mkAction d.dir;
})
viDirs;
# 1-9 tab-jump bindings
tabJumpKeys = {mods ? null, indent ? " "}:
tabJumpKeys = {
mods ? null,
indent ? " ",
}:
lib.concatStrings (builtins.genList (i:
luaKey {
inherit mods indent;
key = toString (i + 1);
key = toString (i + 1);
action = "act.ActivateTab(${toString i})";
})
9);
# hjkl + arrows for AdjustPaneSize at a given step
resizeDirKeys = {step, indent ? " "}:
resizeDirKeys = {
step,
indent ? " ",
}:
lib.concatMapStrings (d:
luaKey {inherit indent; key = d.key; action = "act.AdjustPaneSize({ \"${d.dir}\", ${toString step} })";} +
luaKey {inherit indent; key = d.arrow; action = "act.AdjustPaneSize({ \"${d.dir}\", ${toString step} })";})
luaKey {
inherit indent;
key = d.key;
action = "act.AdjustPaneSize({ \"${d.dir}\", ${toString step} })";
}
+ luaKey {
inherit indent;
key = d.arrow;
action = "act.AdjustPaneSize({ \"${d.dir}\", ${toString step} })";
})
viDirs;
# Uppercase HJKL fine-tune resize
resizeShiftKeys = {step, indent ? " "}:
resizeShiftKeys = {
step,
indent ? " ",
}:
lib.concatMapStrings (d:
luaKey {inherit indent; key = lib.toUpper d.key; action = "act.AdjustPaneSize({ \"${d.dir}\", ${toString step} })";})
luaKey {
inherit indent;
key = lib.toUpper d.key;
action = "act.AdjustPaneSize({ \"${d.dir}\", ${toString step} })";
})
viDirs;
# Wrap a lua action string so it also pops the key table (return to locked)
andPop = action: "act.Multiple({ ${action}, act.PopKeyTable })";
# Standard exit bindings, given the mode's own pop key
exitKeys = {selfKey, indent ? " "}:
exitKeys = {
selfKey,
indent ? " ",
}:
lib.concatStrings [
(luaKey {inherit indent; key = selfKey; action = "act.PopKeyTable";})
(luaKey {inherit indent; key = "Escape"; action = "act.PopKeyTable";})
(luaKey {inherit indent; key = "Enter"; action = "act.PopKeyTable";})
(luaKey {inherit indent; key = "Space"; mods = "ALT"; action = "act.PopKeyTable";})
(luaKey {
inherit indent;
key = selfKey;
action = "act.PopKeyTable";
})
(luaKey {
inherit indent;
key = "Escape";
action = "act.PopKeyTable";
})
(luaKey {
inherit indent;
key = "Enter";
action = "act.PopKeyTable";
})
(luaKey {
inherit indent;
key = "Space";
mods = "ALT";
action = "act.PopKeyTable";
})
];
# ── Status bar ───────────────────────────────────────────────────────────────
# Per-mode accent colour (base16) and hint string shown on the left status bar
# Per-mode accent colour (base16) and hint string shown in the status bar
# Hints use nerd font glyphs: separators, icons, key labels
modes = {
locked = {fg = color.base03; hint = "Alt+Space:enter Alt+hl:focus/tab Alt+1-9:tab Alt+n:split C-ud:scroll";};
normal = {fg = color.base0D; hint = "p:pane t:tab r:resize s:scroll n:split Esc:exit";};
pane = {fg = color.base0B; hint = "hjkl:focus n/r:split d:split f:zoom x:close Esc:exit";};
tab = {fg = color.base0C; hint = "hjkl:prev/next 1-9:jump n:new x:close r:rename Esc:exit";};
resize = {fg = color.base0A; hint = "hjkl:+5 HJKL:+1 Esc:exit";};
scroll = {fg = color.base0E; hint = "jk:line ud:half hl:page f:search Esc:exit";};
locked = {
fg = color.base03;
hint = "󱁐:󰌌 hl:󰿵 1-9:󰓩 n:󰤻 w:󰅙 ^ud:󱕷";
};
normal = {
fg = color.base0D;
hint = "t:󰐕 x:󰅙 r:󰩨 c:󰆏 s:󰍉 esc:󱊷";
};
resize = {
fg = color.base0A;
hint = "hjkl:󰁌 HJKL:󰁎 esc:󱊷";
};
copy_mode = {
fg = color.base0E;
hint = "hjkl:󰹳 v:󰒆 V:󰒇 ^v:󰒈 y:󰆏 /:󰍉 n/p:󰁹 esc:󱊷";
};
search_mode = {
fg = color.base08;
hint = "type:󰈙 ^n/^p:󰁹 ^r:󰑓 /esc:󰆐";
};
};
# Lua table literal mapping mode name -> { fg, hint }
modeTableEntries = lib.concatStringsSep "\n " (lib.mapAttrsToList (name: m: ''
["${name}"] = { fg = "${m.fg}", hint = "${m.hint}" },
'') modes);
["${name}"] = { fg = "${m.fg}", hint = "${m.hint}" },
'')
modes);
in {
options.mods.terminal.wezterm.enable = lib.mkEnableOption "enables wezterm";
@ -131,27 +216,51 @@ in {
local bg_bar = "${color.base01}"
local fg_text = "${color.base05}"
local fg_dim = "${color.base03}"
local bg_main = "${color.base00}"
-- Render hints with the key in the mode accent colour and the icon dimmed.
-- Each hint token is "key:icon"; we colour them individually.
local function render_hints(hint, accent)
local cells = {}
for token in hint:gmatch("%S+") do
local key, icon = token:match("^(.-):(.)$")
if key and icon then
table.insert(cells, { Background = { Color = bg_bar } })
table.insert(cells, { Foreground = { Color = accent } })
table.insert(cells, { Text = key })
table.insert(cells, { Foreground = { Color = fg_text } })
table.insert(cells, { Text = ":" .. icon .. " " })
else
table.insert(cells, { Background = { Color = bg_bar } })
table.insert(cells, { Foreground = { Color = fg_dim } })
table.insert(cells, { Text = token .. " " })
end
end
return cells
end
wezterm.on("update-status", function(window, _pane)
local kt = window:active_key_table()
local mode = kt or "locked"
local info = mode_info[mode] or mode_info["locked"]
-- Left: coloured mode badge + hint line
window:set_left_status(wezterm.format({
{ Background = { Color = info.fg } },
{ Foreground = { Color = bg_main } },
{ Attribute = { Intensity = "Bold" } },
{ Text = " " .. mode:upper() .. " " },
"ResetAttributes",
{ Background = { Color = bg_bar } },
{ Foreground = { Color = fg_text } },
{ Text = " " .. info.hint .. " " },
"ResetAttributes",
}))
window:set_left_status("")
window:set_right_status("")
-- Right: coloured hints then mode badge
local cells = { { Text = " " } }
for _, c in ipairs(render_hints(info.hint, info.fg)) do
table.insert(cells, c)
end
table.insert(cells, "ResetAttributes")
table.insert(cells, { Background = { Color = info.fg } })
table.insert(cells, { Foreground = { Color = bg_bar } })
table.insert(cells, { Text = "" })
table.insert(cells, { Foreground = { Color = bg_main } })
table.insert(cells, { Attribute = { Intensity = "Bold" } })
table.insert(cells, { Text = " " .. mode:upper() .. " " })
table.insert(cells, "ResetAttributes")
window:set_right_status(wezterm.format(cells))
end)
-- Appearance
@ -170,7 +279,18 @@ in {
config.default_cursor_style = "BlinkingBar"
config.hide_tab_bar_if_only_one_tab = false
config.use_fancy_tab_bar = false
config.tab_bar_at_bottom = true
config.colors = {
tab_bar = {
background = "${color.base01}",
active_tab = { bg_color = "${color.base0D}", fg_color = "${color.base00}", intensity = "Bold" },
inactive_tab = { bg_color = "${color.base01}", fg_color = "${color.base03}" },
inactive_tab_hover = { bg_color = "${color.base02}", fg_color = "${color.base04}" },
new_tab = { bg_color = "${color.base01}", fg_color = "${color.base03}" },
new_tab_hover = { bg_color = "${color.base02}", fg_color = "${color.base04}" },
},
}
config.tab_bar_at_bottom = false
-- Shell
@ -185,8 +305,8 @@ in {
-- Key bindings
--
-- Default mode = "locked" (all keys pass through to the shell).
-- Alt+Space enters "normal" mode (like Zellij).
-- From normal: ppane ttab rresize sscroll
-- Alt+Space enters "normal" mode.
-- From normal: tnew tab xclose tab p/nsplit rresize mode
-- Esc / Enter / Alt+Space back to locked from any mode.
config.disable_default_key_bindings = true
@ -195,15 +315,18 @@ in {
-- Alt+h/l: move focus or switch tab at edge (Zellij MoveFocusOrTab)
-- Alt+j/k: move focus between panes
${viDirKeys {
mods = "ALT";
indent = " ";
mkAction = d:
if d == "Left" || d == "Right"
then "move_focus_or_tab(\"${d}\")"
else "act.ActivatePaneDirection(\"${d}\")";
}}
mods = "ALT";
indent = " ";
mkAction = d:
if d == "Left" || d == "Right"
then "move_focus_or_tab(\"${d}\")"
else "act.ActivatePaneDirection(\"${d}\")";
}}
-- Alt+1-9: jump to tab by number
${tabJumpKeys {mods = "ALT"; indent = " ";}}
${tabJumpKeys {
mods = "ALT";
indent = " ";
}}
-- Alt+n: new pane (split right)
{ key = "n", mods = "ALT", action = act.SplitHorizontal({ domain = "CurrentPaneDomain" }) },
-- Alt+f: toggle zoom
@ -218,6 +341,8 @@ in {
-- Alt+i/o: reorder tabs
{ key = "i", mods = "ALT", action = act.MoveTabRelative(-1) },
{ key = "o", mods = "ALT", action = act.MoveTabRelative(1) },
-- Alt+w: close current pane
{ key = "w", mods = "ALT", action = act.CloseCurrentPane({ confirm = true }) },
-- Alt+q: quit
{ key = "q", mods = "ALT", action = act.QuitApplication },
-- Ctrl+U/D: scroll half page (matches Alacritty)
@ -234,77 +359,86 @@ in {
-- NORMAL
normal = {
{ key = "p", action = act.ActivateKeyTable({ name = "pane", one_shot = false }) },
{ key = "t", action = act.ActivateKeyTable({ name = "tab", one_shot = false }) },
{ key = "r", action = act.ActivateKeyTable({ name = "resize", one_shot = false }) },
{ key = "s", action = act.ActivateKeyTable({ name = "scroll", one_shot = false }) },
{ key = "n", action = ${andPop "act.SplitHorizontal({ domain = \"CurrentPaneDomain\" })"} },
${exitKeys {selfKey = "Escape";}} },
-- PANE
pane = {
${viDirKeys {
mkAction = d:
if d == "Left" || d == "Right"
then "move_focus_or_tab(\"${d}\")"
else "act.ActivatePaneDirection(\"${d}\")";
}}
{ key = "n", action = ${andPop "act.SplitHorizontal({ domain = \"CurrentPaneDomain\" })"} },
{ key = "r", action = ${andPop "act.SplitHorizontal({ domain = \"CurrentPaneDomain\" })"} },
{ key = "d", action = ${andPop "act.SplitVertical({ domain = \"CurrentPaneDomain\" })"} },
{ key = "f", action = ${andPop "act.TogglePaneZoomState"} },
{ key = "z", action = ${andPop "act.TogglePaneZoomState"} },
{ key = "x", action = ${andPop "act.CloseCurrentPane({ confirm = true })"} },
{ key = "Tab", action = act.ActivatePaneDirection("Next") },
${exitKeys {selfKey = "p";}} },
-- TAB
tab = {
{ key = "h", action = act.ActivateTabRelative(-1) },
{ key = "k", action = act.ActivateTabRelative(-1) },
{ key = "j", action = act.ActivateTabRelative(1) },
{ key = "l", action = act.ActivateTabRelative(1) },
{ key = "LeftArrow", action = act.ActivateTabRelative(-1) },
{ key = "UpArrow", action = act.ActivateTabRelative(-1) },
{ key = "RightArrow", action = act.ActivateTabRelative(1) },
{ key = "DownArrow", action = act.ActivateTabRelative(1) },
{ key = "Tab", action = act.ActivateTabRelative(1) },
${tabJumpKeys {}}
{ key = "n", action = ${andPop "act.SpawnTab(\"CurrentPaneDomain\")"} },
{ key = "t", action = ${andPop "act.SpawnTab(\"CurrentPaneDomain\")"} },
{ key = "x", action = ${andPop "act.CloseCurrentTab({ confirm = true })"} },
{ key = "r", action = act.PromptInputLine({
description = "Rename tab",
action = wezterm.action_callback(function(window, _, line)
if line then window:active_tab():set_title(line) end
window:perform_action(act.PopKeyTable, window:active_pane())
end),
})
},
${exitKeys {selfKey = "t";}} },
{ key = "r", action = act.ActivateKeyTable({ name = "resize", one_shot = false }) },
{ key = "c", action = ${andPop "act.ActivateCopyMode"} },
{ key = "s", action = ${andPop "act.Search({ CaseSensitiveString = \"\" })"} },
${exitKeys {selfKey = "Escape";}} },
-- RESIZE
resize = {
${resizeDirKeys {step = 5;}}${resizeShiftKeys {step = 1;}}
${resizeDirKeys {step = 5;}}${resizeShiftKeys {step = 1;}}
{ key = "+", action = act.AdjustPaneSize({ "Right", 5 }) },
{ key = "-", action = act.AdjustPaneSize({ "Left", 5 }) },
{ key = "=", action = act.AdjustPaneSize({ "Right", 5 }) },
${exitKeys {selfKey = "r";}} },
-- SCROLL
scroll = {
{ key = "j", action = act.ScrollByLine(1) },
{ key = "k", action = act.ScrollByLine(-1) },
{ key = "d", action = act.ScrollByPage(0.5) },
{ key = "u", action = act.ScrollByPage(-0.5) },
{ key = "h", action = act.ScrollByPage(-1) },
{ key = "l", action = act.ScrollByPage(1) },
{ key = "DownArrow", action = act.ScrollByLine(1) },
{ key = "UpArrow", action = act.ScrollByLine(-1) },
{ key = "PageDown", action = act.ScrollByPage(1) },
{ key = "PageUp", action = act.ScrollByPage(-1) },
{ key = "f", action = act.Search({ CaseSensitiveString = "" }) },
{ key = "c", mods = "CTRL", action = act.ScrollToBottom },
${exitKeys {selfKey = "s";}} },
-- COPY MODE
copy_mode = {
-- movement
{ key = "h", action = act.CopyMode("MoveLeft") },
{ key = "j", action = act.CopyMode("MoveDown") },
{ key = "k", action = act.CopyMode("MoveUp") },
{ key = "l", action = act.CopyMode("MoveRight") },
{ key = "LeftArrow", action = act.CopyMode("MoveLeft") },
{ key = "DownArrow", action = act.CopyMode("MoveDown") },
{ key = "UpArrow", action = act.CopyMode("MoveUp") },
{ key = "RightArrow", action = act.CopyMode("MoveRight") },
{ key = "w", action = act.CopyMode("MoveForwardWord") },
{ key = "b", action = act.CopyMode("MoveBackwardWord") },
{ key = "e", action = act.CopyMode("MoveForwardWordEnd") },
{ key = "0", action = act.CopyMode("MoveToStartOfLine") },
{ key = "^", action = act.CopyMode("MoveToStartOfLineContent") },
{ key = "$", action = act.CopyMode("MoveToEndOfLineContent") },
{ key = "g", action = act.CopyMode("MoveToScrollbackTop") },
{ key = "G", action = act.CopyMode("MoveToScrollbackBottom") },
{ key = "Enter", action = act.CopyMode("MoveToStartOfNextLine") },
{ key = "u", mods = "CTRL", action = act.CopyMode({ MoveByPage = -0.5 }) },
{ key = "d", mods = "CTRL", action = act.CopyMode({ MoveByPage = 0.5 }) },
{ key = "b", mods = "CTRL", action = act.CopyMode("PageUp") },
{ key = "f", mods = "CTRL", action = act.CopyMode("PageDown") },
{ key = "PageUp", action = act.CopyMode("PageUp") },
{ key = "PageDown", action = act.CopyMode("PageDown") },
-- selection
{ key = "v", action = act.CopyMode({ SetSelectionMode = "Cell" }) },
{ key = "V", action = act.CopyMode({ SetSelectionMode = "Line" }) },
{ key = "v", mods = "CTRL", action = act.CopyMode({ SetSelectionMode = "Block" }) },
{ key = "o", action = act.CopyMode("MoveToSelectionOtherEnd") },
{ key = "O", action = act.CopyMode("MoveToSelectionOtherEndHoriz") },
-- yank and exit
{ key = "y", action = act.Multiple({
act.CopyTo("ClipboardAndPrimarySelection"),
act.Multiple({ act.ScrollToBottom, act.CopyMode("Close") }),
})
},
-- search within copy mode
{ key = "/", action = act.Search({ CaseSensitiveString = "" }) },
{ key = "n", action = act.CopyMode("NextMatch") },
{ key = "p", action = act.CopyMode("PriorMatch") },
-- exit
{ key = "q", action = act.Multiple({ act.ScrollToBottom, act.CopyMode("Close") }) },
{ key = "Escape", action = act.Multiple({ act.ScrollToBottom, act.CopyMode("Close") }) },
{ key = "c", mods = "CTRL", action = act.Multiple({ act.ScrollToBottom, act.CopyMode("Close") }) },
},
-- SEARCH MODE
search_mode = {
-- navigate matches
{ key = "Enter", action = act.CopyMode("AcceptPattern") },
{ key = "n", mods = "CTRL", action = act.CopyMode("NextMatch") },
{ key = "p", mods = "CTRL", action = act.CopyMode("PriorMatch") },
{ key = "PageUp", action = act.CopyMode("PriorMatchPage") },
{ key = "PageDown", action = act.CopyMode("NextMatchPage") },
{ key = "UpArrow", action = act.CopyMode("PriorMatch") },
{ key = "DownArrow", action = act.CopyMode("NextMatch") },
-- cycle match type (case-sensitive / insensitive / regex)
{ key = "r", mods = "CTRL", action = act.CopyMode("CycleMatchType") },
-- clear search input
{ key = "u", mods = "CTRL", action = act.CopyMode("ClearPattern") },
-- Escape: dismiss search bar, return to copy mode at current match
{ key = "Escape", action = act.CopyMode("AcceptPattern") },
},
}
return config