mirror of
https://codeberg.org/muon/home.git
synced 2026-03-08 03:25:16 +00:00
Add wezterm
This commit is contained in:
parent
9568bf4a6e
commit
6b01ee918a
5 changed files with 393 additions and 1 deletions
314
modules/home/terminal/wezterm.nix
Normal file
314
modules/home/terminal/wezterm.nix
Normal file
|
|
@ -0,0 +1,314 @@
|
|||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: let
|
||||
cfg = config.mods.terminal;
|
||||
color = config.lib.stylix.colors.withHashtag;
|
||||
|
||||
# Shell to use inside wezterm
|
||||
shell =
|
||||
if cfg.nushell.enable
|
||||
then lib.getExe pkgs.nushell
|
||||
else if cfg.zsh.enable
|
||||
then lib.getExe pkgs.zsh
|
||||
else null;
|
||||
|
||||
# ── Lua codegen helpers ──────────────────────────────────────────────────────
|
||||
|
||||
luaKey = {
|
||||
key,
|
||||
mods ? null,
|
||||
action,
|
||||
indent ? " ",
|
||||
}: let
|
||||
modsStr = lib.optionalString (mods != null) ", mods = \"${mods}\"";
|
||||
in "${indent}{ key = \"${key}\"${modsStr}, action = ${action} },\n";
|
||||
|
||||
# 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";}
|
||||
];
|
||||
|
||||
# hjkl + arrow equivalents; mkAction :: dir -> lua-action-string
|
||||
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;})
|
||||
viDirs;
|
||||
|
||||
# 1-9 tab-jump bindings
|
||||
tabJumpKeys = {mods ? null, indent ? " "}:
|
||||
lib.concatStrings (builtins.genList (i:
|
||||
luaKey {
|
||||
inherit mods indent;
|
||||
key = toString (i + 1);
|
||||
action = "act.ActivateTab(${toString i})";
|
||||
})
|
||||
9);
|
||||
|
||||
# hjkl + arrows for AdjustPaneSize at a given step
|
||||
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} })";})
|
||||
viDirs;
|
||||
|
||||
# Uppercase HJKL fine-tune resize
|
||||
resizeShiftKeys = {step, indent ? " "}:
|
||||
lib.concatMapStrings (d:
|
||||
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 ? " "}:
|
||||
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";})
|
||||
];
|
||||
|
||||
# ── Status bar ───────────────────────────────────────────────────────────────
|
||||
|
||||
# Per-mode accent colour (base16) and hint string shown on the left status bar
|
||||
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";};
|
||||
};
|
||||
|
||||
# Lua table literal mapping mode name -> { fg, hint }
|
||||
modeTableEntries = lib.concatStringsSep "\n " (lib.mapAttrsToList (name: m: ''
|
||||
["${name}"] = { fg = "${m.fg}", hint = "${m.hint}" },
|
||||
'') modes);
|
||||
in {
|
||||
options.mods.terminal.wezterm.enable = lib.mkEnableOption "enables wezterm";
|
||||
|
||||
config = lib.mkIf cfg.wezterm.enable {
|
||||
programs.wezterm = {
|
||||
enable = true;
|
||||
|
||||
extraConfig = ''
|
||||
local wezterm = require("wezterm")
|
||||
local act = wezterm.action
|
||||
|
||||
-- ─── Helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
-- move_focus_or_tab: mirrors Zellij's MoveFocusOrTab.
|
||||
-- Move focus in direction; if at the edge, switch to the adjacent tab.
|
||||
local function move_focus_or_tab(direction)
|
||||
return wezterm.action_callback(function(window, pane)
|
||||
local neighbour = pane:tab():get_pane_direction(direction)
|
||||
if neighbour ~= nil then
|
||||
window:perform_action(act.ActivatePaneDirection(direction), pane)
|
||||
else
|
||||
if direction == "Left" then
|
||||
window:perform_action(act.ActivateTabRelative(-1), pane)
|
||||
elseif direction == "Right" then
|
||||
window:perform_action(act.ActivateTabRelative(1), pane)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- ─── Status bar ────────────────────────────────────────────────────────────
|
||||
|
||||
-- Per-mode accent colour and hint text, generated from Nix/stylix palette
|
||||
local mode_info = {
|
||||
${modeTableEntries}
|
||||
}
|
||||
|
||||
local bg_bar = "${color.base01}"
|
||||
local fg_text = "${color.base05}"
|
||||
local bg_main = "${color.base00}"
|
||||
|
||||
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_right_status("")
|
||||
end)
|
||||
|
||||
-- ─── Appearance ────────────────────────────────────────────────────────────
|
||||
|
||||
local config = wezterm.config_builder()
|
||||
|
||||
-- Stylix sets the color scheme via its wezterm integration;
|
||||
-- this is a fallback in case it is not active.
|
||||
config.color_scheme = "Synthwave (Gogh)"
|
||||
|
||||
config.font = wezterm.font("CommitMono Nerd Font")
|
||||
config.font_size = 12.0
|
||||
|
||||
config.window_padding = { left = 6, right = 6, top = 6, bottom = 6 }
|
||||
|
||||
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
|
||||
|
||||
-- ─── Shell ─────────────────────────────────────────────────────────────────
|
||||
|
||||
${lib.optionalString (shell != null) ''
|
||||
config.default_prog = { "${shell}" }
|
||||
''}
|
||||
|
||||
-- ─── Scrollback ────────────────────────────────────────────────────────────
|
||||
|
||||
config.scrollback_lines = 10000
|
||||
|
||||
-- ─── Key bindings ──────────────────────────────────────────────────────────
|
||||
--
|
||||
-- Default mode = "locked" (all keys pass through to the shell).
|
||||
-- Alt+Space enters "normal" mode (like Zellij).
|
||||
-- From normal: p→pane t→tab r→resize s→scroll
|
||||
-- Esc / Enter / Alt+Space → back to locked from any mode.
|
||||
|
||||
config.disable_default_key_bindings = true
|
||||
|
||||
config.keys = {
|
||||
-- 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}\")";
|
||||
}}
|
||||
-- Alt+1-9: jump to tab by number
|
||||
${tabJumpKeys {mods = "ALT"; indent = " ";}}
|
||||
-- Alt+n: new pane (split right)
|
||||
{ key = "n", mods = "ALT", action = act.SplitHorizontal({ domain = "CurrentPaneDomain" }) },
|
||||
-- Alt+f: toggle zoom
|
||||
{ key = "f", mods = "ALT", action = act.TogglePaneZoomState },
|
||||
-- Alt+[/]: rotate panes
|
||||
{ key = "[", mods = "ALT", action = act.RotatePanes("CounterClockwise") },
|
||||
{ key = "]", mods = "ALT", action = act.RotatePanes("Clockwise") },
|
||||
-- Alt++/-/=: font size
|
||||
{ key = "+", mods = "ALT", action = act.IncreaseFontSize },
|
||||
{ key = "-", mods = "ALT", action = act.DecreaseFontSize },
|
||||
{ key = "=", mods = "ALT", action = act.ResetFontSize },
|
||||
-- Alt+i/o: reorder tabs
|
||||
{ key = "i", mods = "ALT", action = act.MoveTabRelative(-1) },
|
||||
{ key = "o", mods = "ALT", action = act.MoveTabRelative(1) },
|
||||
-- Alt+q: quit
|
||||
{ key = "q", mods = "ALT", action = act.QuitApplication },
|
||||
-- Ctrl+U/D: scroll half page (matches Alacritty)
|
||||
{ key = "u", mods = "CTRL", action = act.ScrollByPage(-0.5) },
|
||||
{ key = "d", mods = "CTRL", action = act.ScrollByPage(0.5) },
|
||||
-- Copy / paste
|
||||
{ key = "c", mods = "CTRL|SHIFT", action = act.CopyTo("Clipboard") },
|
||||
{ key = "v", mods = "CTRL|SHIFT", action = act.PasteFrom("Clipboard") },
|
||||
-- Enter normal mode
|
||||
{ key = "Space", mods = "ALT", action = act.ActivateKeyTable({ name = "normal", one_shot = false }) },
|
||||
}
|
||||
|
||||
config.key_tables = {
|
||||
|
||||
-- ── 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 = "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";}} },
|
||||
|
||||
-- ── RESIZE ─────────────────────────────────────────────────────────────
|
||||
resize = {
|
||||
${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";}} },
|
||||
}
|
||||
|
||||
return config
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue