mirror of
https://codeberg.org/muon/home.git
synced 2026-07-03 23:49:35 +00:00
Compare commits
11 commits
86c3dbfa88
...
0cda38f25b
| Author | SHA1 | Date | |
|---|---|---|---|
| 0cda38f25b | |||
| c9be88c1b6 | |||
| e085afcf6d | |||
| fb8b27ef3c | |||
| 5518f1ca35 | |||
| 5e84e6aba8 | |||
| 9433fc31ea | |||
| 6187422574 | |||
| 8485d8007e | |||
| 8e57979d3c | |||
| 3a4353b990 |
10 changed files with 627 additions and 200 deletions
59
flake.nix
59
flake.nix
|
|
@ -46,48 +46,31 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
utils = import ./utils.nix {inherit inputs system sources;};
|
utils = import ./utils.nix {inherit inputs system sources;};
|
||||||
|
|
||||||
|
# Discover hosts: all subdirectories of hosts/
|
||||||
|
hosts = builtins.attrNames (nixpkgs.lib.filterAttrs
|
||||||
|
(_: type: type == "directory")
|
||||||
|
(builtins.readDir ./hosts));
|
||||||
|
|
||||||
|
nixosConfigs = builtins.listToAttrs (map (host: {
|
||||||
|
name = host;
|
||||||
|
value = utils.mkHost ./hosts/${host}/configuration.nix;
|
||||||
|
}) hosts);
|
||||||
in {
|
in {
|
||||||
nixosConfigurations = {
|
nixosConfigurations = nixosConfigs;
|
||||||
# desktop
|
|
||||||
muon = utils.mkHost ./hosts/muon/configuration.nix;
|
|
||||||
|
|
||||||
# laptop
|
|
||||||
muop = utils.mkHost ./hosts/muop/configuration.nix;
|
|
||||||
|
|
||||||
# server
|
|
||||||
muho = utils.mkHost ./hosts/muho/configuration.nix;
|
|
||||||
|
|
||||||
# vps
|
|
||||||
mups = utils.mkHost ./hosts/mups/configuration.nix;
|
|
||||||
|
|
||||||
# vm
|
|
||||||
muvm = utils.mkHost ./hosts/muvm/configuration.nix;
|
|
||||||
|
|
||||||
# work
|
|
||||||
murk = utils.mkHost ./hosts/murk/configuration.nix;
|
|
||||||
|
|
||||||
# work desktop
|
|
||||||
musk = utils.mkHost ./hosts/musk/configuration.nix;
|
|
||||||
|
|
||||||
# lenovo
|
|
||||||
muvo = utils.mkHost ./hosts/muvo/configuration.nix;
|
|
||||||
|
|
||||||
# installer
|
|
||||||
muin = utils.mkHost ./hosts/muin/configuration.nix;
|
|
||||||
};
|
|
||||||
|
|
||||||
homeManagerModules.default = ./modules/home;
|
homeManagerModules.default = ./modules/home;
|
||||||
|
|
||||||
# Expose each host's HM activation package so `home-manager switch --flake .#muon@<host>`
|
# Standalone HM configurations — one per host.
|
||||||
# works without a full NixOS rebuild. Extracted from the already-evaluated
|
# osConfig is injected so all modules using it continue to work.
|
||||||
# nixosConfiguration, so osConfig remains fully populated.
|
# Use: home-manager switch --flake '.#muon@<host>'
|
||||||
homeConfigurations = nixpkgs.lib.mapAttrs' (host: nixos:
|
homeConfigurations = builtins.listToAttrs (map (host: {
|
||||||
nixpkgs.lib.nameValuePair "muon@${host}" {
|
name = "muon@${host}";
|
||||||
activationPackage = nixos.config.home-manager.users.muon.home.activationPackage;
|
value = utils.mkHome {
|
||||||
}
|
hostConfig = nixosConfigs.${host};
|
||||||
) (nixpkgs.lib.filterAttrs
|
homeFile = ./hosts/${host}/home.nix;
|
||||||
(_: nixos: nixos.config.home-manager.users ? muon)
|
};
|
||||||
inputs.self.outputs.nixosConfigurations);
|
}) hosts);
|
||||||
|
|
||||||
colmena = {
|
colmena = {
|
||||||
meta = {
|
meta = {
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ in {
|
||||||
mods.server.ntfy.enable = true;
|
mods.server.ntfy.enable = true;
|
||||||
mods.server.lemmy.enable = true;
|
mods.server.lemmy.enable = true;
|
||||||
mods.server.audio.enable = true;
|
mods.server.audio.enable = true;
|
||||||
|
mods.server.murmur.enable = true;
|
||||||
mods.server.atuin.enable = true;
|
mods.server.atuin.enable = true;
|
||||||
mods.server.seedbox.enable = true;
|
mods.server.seedbox.enable = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ in {
|
||||||
mods.xdg.enable = true;
|
mods.xdg.enable = true;
|
||||||
mods.social.enable = false;
|
mods.social.enable = false;
|
||||||
mods.i3.enable = false;
|
mods.i3.enable = false;
|
||||||
|
mods.terminal.wezterm.enable = true;
|
||||||
|
mods.terminal.nushell.enable = true;
|
||||||
mods.terminal.zsh.enable = true;
|
mods.terminal.zsh.enable = true;
|
||||||
mods.terminal.emulator.enable = false;
|
mods.terminal.emulator.enable = false;
|
||||||
mods.terminal.development.enable = true;
|
mods.terminal.development.enable = true;
|
||||||
|
|
|
||||||
|
|
@ -5,22 +5,97 @@
|
||||||
osConfig,
|
osConfig,
|
||||||
...
|
...
|
||||||
}: let
|
}: let
|
||||||
|
# Bootstrap script that runs as the first program inside a new wezterm window.
|
||||||
|
# It sets up the three tabs synchronously before the shell prompt appears,
|
||||||
|
# so by the time the window is visible the layout is already complete.
|
||||||
|
# Receives the project path as $1.
|
||||||
|
wezterm-zmenu-init = pkgs.writeShellApplication {
|
||||||
|
name = "wezterm-zmenu-init";
|
||||||
|
runtimeInputs = [pkgs.wezterm pkgs.zsh];
|
||||||
|
text = ''
|
||||||
|
ZPATH=$1
|
||||||
|
WIN=$(wezterm cli list --format json 2>/dev/null \
|
||||||
|
| tr ',' '\n' \
|
||||||
|
| awk -v pane="$WEZTERM_PANE" '
|
||||||
|
/window_id/ { gsub(/.*: */,""); w=int($0) }
|
||||||
|
/pane_id/ { gsub(/.*: */,""); if (int($0)==pane) { print w; exit } }')
|
||||||
|
P1=$(wezterm cli spawn --window-id "$WIN" --cwd "$ZPATH" -- direnv exec . nvim)
|
||||||
|
wezterm cli spawn --window-id "$WIN" --cwd "$ZPATH" > /dev/null
|
||||||
|
wezterm cli spawn --window-id "$WIN" --cwd "$ZPATH" -- lazygit > /dev/null
|
||||||
|
wezterm cli activate-tab --tab-index 0 --pane-id "$P1" 2>/dev/null
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
zmenu = with pkgs;
|
zmenu = with pkgs;
|
||||||
writeShellApplication {
|
writeShellApplication {
|
||||||
name = "zmenu";
|
name = "zmenu";
|
||||||
runtimeInputs = [zellij zoxide wmctrl i3 rofi alacritty zsh];
|
runtimeInputs =
|
||||||
text = ''
|
[zoxide wmctrl i3 rofi zsh]
|
||||||
ZPATH=$(zoxide query -l | sed -e "s|$HOME/||g" | rofi -dmenu)
|
++ lib.optionals config.mods.terminal.wezterm.enable [wezterm]
|
||||||
[[ -z "$ZPATH" ]] && exit
|
++ lib.optionals (!config.mods.terminal.wezterm.enable) [zellij alacritty];
|
||||||
ZSESH=$(echo "$ZPATH" | tr / -)
|
text =
|
||||||
ZWIND=$(wmctrl -l | grep "$ZSESH$" || echo "")
|
if config.mods.terminal.wezterm.enable
|
||||||
cd "$ZPATH"
|
then ''
|
||||||
if [[ -z "$ZWIND" ]]; then
|
ZPATH=$(zoxide query -l | sed -e "s|$HOME/||g" | rofi -dmenu)
|
||||||
alacritty -T "$ZSESH" -e zsh -c "zellij -s $ZSESH -n dev || zellij a $ZSESH"
|
[[ -z "$ZPATH" ]] && exit
|
||||||
else
|
ZSESH=$(echo "$ZPATH" | tr / -)
|
||||||
wmctrl -a "$ZSESH"
|
|
||||||
fi
|
# Per-workspace socket registry so we can focus existing workspaces.
|
||||||
'';
|
SOCKDIR="$XDG_RUNTIME_DIR/zmenu-wez"
|
||||||
|
mkdir -p "$SOCKDIR"
|
||||||
|
SOCKFILE="$SOCKDIR/$ZSESH"
|
||||||
|
|
||||||
|
SOCK=""
|
||||||
|
if [[ -f "$SOCKFILE" ]]; then
|
||||||
|
SOCK=$(cat "$SOCKFILE")
|
||||||
|
# Extract the PID from the socket path (gui-sock-<pid>) and check
|
||||||
|
# if the process is still alive. Dead sockets linger on disk.
|
||||||
|
SOCK_PID=''${SOCK##*-}
|
||||||
|
if ! kill -0 "$SOCK_PID" 2>/dev/null; then
|
||||||
|
rm -f "$SOCKFILE"
|
||||||
|
SOCK=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$SOCK" ]]; then
|
||||||
|
# Workspace open: raise its window by matching the wezterm PID in
|
||||||
|
# wmctrl's process list, then activate a pane to ensure focus.
|
||||||
|
SOCK_PID=''${SOCK##*-}
|
||||||
|
WIN_HEX=$(wmctrl -lp | awk -v pid="$SOCK_PID" '$3==pid {print $1; exit}')
|
||||||
|
wmctrl -i -a "$WIN_HEX"
|
||||||
|
PANE=$(WEZTERM_UNIX_SOCKET="$SOCK" wezterm cli list --format json 2>/dev/null \
|
||||||
|
| tr ',' '\n' \
|
||||||
|
| awk '/pane_id/{gsub(/.*: */,""); print int($0); exit}')
|
||||||
|
WEZTERM_UNIX_SOCKET="$SOCK" wezterm cli activate-pane --pane-id "$PANE" 2>/dev/null
|
||||||
|
else
|
||||||
|
# Not open: the init script runs as the first program inside the new
|
||||||
|
# window. It sets up all tabs synchronously before the shell prompt
|
||||||
|
# appears, so the layout is complete before anything is visible.
|
||||||
|
wezterm start --always-new-process \
|
||||||
|
--workspace "$ZSESH" --cwd "$HOME/$ZPATH" \
|
||||||
|
-- ${lib.getExe wezterm-zmenu-init} "$HOME/$ZPATH" &
|
||||||
|
WEZ_PID=$!
|
||||||
|
|
||||||
|
# Record the socket for future focus calls.
|
||||||
|
for _ in $(seq 50); do
|
||||||
|
sleep 0.1
|
||||||
|
[[ -S /run/user/$UID/wezterm/gui-sock-$WEZ_PID ]] && break
|
||||||
|
done
|
||||||
|
echo "/run/user/$UID/wezterm/gui-sock-$WEZ_PID" > "$SOCKFILE"
|
||||||
|
fi
|
||||||
|
''
|
||||||
|
else ''
|
||||||
|
ZPATH=$(zoxide query -l | sed -e "s|$HOME/||g" | rofi -dmenu)
|
||||||
|
[[ -z "$ZPATH" ]] && exit
|
||||||
|
ZSESH=$(echo "$ZPATH" | tr / -)
|
||||||
|
ZWIND=$(wmctrl -l | grep "$ZSESH$" || echo "")
|
||||||
|
cd "$HOME/$ZPATH"
|
||||||
|
if [[ -z "$ZWIND" ]]; then
|
||||||
|
alacritty -T "$ZSESH" -e zsh -c "zellij -s $ZSESH -n dev || zellij a $ZSESH"
|
||||||
|
else
|
||||||
|
wmctrl -a "$ZSESH"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
with lib; {
|
with lib; {
|
||||||
|
|
@ -94,7 +169,10 @@ in
|
||||||
enable = true;
|
enable = true;
|
||||||
config = {
|
config = {
|
||||||
modifier = modifier;
|
modifier = modifier;
|
||||||
terminal = "alacritty";
|
terminal =
|
||||||
|
if config.mods.terminal.wezterm.enable
|
||||||
|
then "wezterm"
|
||||||
|
else "alacritty";
|
||||||
menu = "rofi -show drun";
|
menu = "rofi -show drun";
|
||||||
|
|
||||||
window = {
|
window = {
|
||||||
|
|
|
||||||
|
|
@ -23,35 +23,43 @@ in {
|
||||||
|
|
||||||
shellAliases = aliases;
|
shellAliases = aliases;
|
||||||
|
|
||||||
# vi mode + sensible defaults
|
# vi mode + sensible defaults via flat assignments (avoids clobbering other modules)
|
||||||
extraConfig = ''
|
settings = {
|
||||||
$env.config = {
|
show_banner = false;
|
||||||
show_banner: false
|
edit_mode = "vi";
|
||||||
edit_mode: vi
|
cursor_shape = {
|
||||||
|
vi_insert = "line";
|
||||||
|
vi_normal = "block";
|
||||||
|
};
|
||||||
|
history = {
|
||||||
|
max_size = 2097152;
|
||||||
|
sync_on_enter = true;
|
||||||
|
file_format = "sqlite";
|
||||||
|
isolation = false;
|
||||||
|
};
|
||||||
|
completions = {
|
||||||
|
case_sensitive = false;
|
||||||
|
quick = true;
|
||||||
|
partial = true;
|
||||||
|
algorithm = "fuzzy";
|
||||||
|
};
|
||||||
|
table.mode = "rounded";
|
||||||
|
};
|
||||||
|
|
||||||
cursor_shape: {
|
# Append the / keybinding after all integrations (including atuin) are sourced,
|
||||||
vi_insert: line
|
# so _atuin_search_cmd is defined when this runs.
|
||||||
vi_normal: block
|
extraConfig = lib.mkAfter ''
|
||||||
}
|
$env.config = (
|
||||||
|
$env.config | upsert keybindings (
|
||||||
history: {
|
$env.config.keybindings | append {
|
||||||
max_size: 2097152
|
name: atuin_search_vi_normal
|
||||||
sync_on_enter: true
|
modifier: none
|
||||||
file_format: "sqlite"
|
keycode: char_/
|
||||||
isolation: false
|
mode: vi_normal
|
||||||
}
|
event: { send: executehostcommand cmd: (_atuin_search_cmd) }
|
||||||
|
}
|
||||||
completions: {
|
)
|
||||||
case_sensitive: false
|
)
|
||||||
quick: true
|
|
||||||
partial: true
|
|
||||||
algorithm: "fuzzy"
|
|
||||||
}
|
|
||||||
|
|
||||||
table: {
|
|
||||||
mode: rounded
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# Carry over zsh session variables
|
# Carry over zsh session variables
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ in
|
||||||
atuin = {
|
atuin = {
|
||||||
enable = true;
|
enable = true;
|
||||||
enableZshIntegration = true;
|
enableZshIntegration = true;
|
||||||
|
enableNushellIntegration = true;
|
||||||
flags = ["--disable-up-arrow"];
|
flags = ["--disable-up-arrow"];
|
||||||
settings = {
|
settings = {
|
||||||
sync_frequency = "5m";
|
sync_frequency = "5m";
|
||||||
|
|
|
||||||
|
|
@ -28,70 +28,155 @@
|
||||||
|
|
||||||
# vi directions: key, arrow alias, wezterm direction name
|
# vi directions: key, arrow alias, wezterm direction name
|
||||||
viDirs = [
|
viDirs = [
|
||||||
{key = "h"; arrow = "LeftArrow"; dir = "Left";}
|
{
|
||||||
{key = "j"; arrow = "DownArrow"; dir = "Down";}
|
key = "h";
|
||||||
{key = "k"; arrow = "UpArrow"; dir = "Up";}
|
arrow = "LeftArrow";
|
||||||
{key = "l"; arrow = "RightArrow"; dir = "Right";}
|
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
|
# hjkl + arrow equivalents; mkAction :: dir -> lua-action-string
|
||||||
viDirKeys = {mods ? null, indent ? " ", mkAction}:
|
viDirKeys = {
|
||||||
|
mods ? null,
|
||||||
|
indent ? " ",
|
||||||
|
mkAction,
|
||||||
|
}:
|
||||||
lib.concatMapStrings (d:
|
lib.concatMapStrings (d:
|
||||||
luaKey {inherit mods indent; key = d.key; action = mkAction d.dir;} +
|
luaKey {
|
||||||
luaKey {inherit mods indent; key = d.arrow; action = mkAction d.dir;})
|
inherit mods indent;
|
||||||
|
key = d.key;
|
||||||
|
action = mkAction d.dir;
|
||||||
|
}
|
||||||
|
+ luaKey {
|
||||||
|
inherit mods indent;
|
||||||
|
key = d.arrow;
|
||||||
|
action = mkAction d.dir;
|
||||||
|
})
|
||||||
viDirs;
|
viDirs;
|
||||||
|
|
||||||
# 1-9 tab-jump bindings
|
# 1-9 tab-jump bindings
|
||||||
tabJumpKeys = {mods ? null, indent ? " "}:
|
tabJumpKeys = {
|
||||||
|
mods ? null,
|
||||||
|
indent ? " ",
|
||||||
|
}:
|
||||||
lib.concatStrings (builtins.genList (i:
|
lib.concatStrings (builtins.genList (i:
|
||||||
luaKey {
|
luaKey {
|
||||||
inherit mods indent;
|
inherit mods indent;
|
||||||
key = toString (i + 1);
|
key = toString (i + 1);
|
||||||
action = "act.ActivateTab(${toString i})";
|
action = "act.ActivateTab(${toString i})";
|
||||||
})
|
})
|
||||||
9);
|
9);
|
||||||
|
|
||||||
# hjkl + arrows for AdjustPaneSize at a given step
|
# hjkl + arrows for AdjustPaneSize at a given step
|
||||||
resizeDirKeys = {step, indent ? " "}:
|
resizeDirKeys = {
|
||||||
|
step,
|
||||||
|
indent ? " ",
|
||||||
|
}:
|
||||||
lib.concatMapStrings (d:
|
lib.concatMapStrings (d:
|
||||||
luaKey {inherit indent; key = d.key; action = "act.AdjustPaneSize({ \"${d.dir}\", ${toString step} })";} +
|
luaKey {
|
||||||
luaKey {inherit indent; key = d.arrow; action = "act.AdjustPaneSize({ \"${d.dir}\", ${toString step} })";})
|
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;
|
viDirs;
|
||||||
|
|
||||||
# Uppercase HJKL fine-tune resize
|
# Uppercase HJKL fine-tune resize
|
||||||
resizeShiftKeys = {step, indent ? " "}:
|
resizeShiftKeys = {
|
||||||
|
step,
|
||||||
|
indent ? " ",
|
||||||
|
}:
|
||||||
lib.concatMapStrings (d:
|
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;
|
viDirs;
|
||||||
|
|
||||||
# Wrap a lua action string so it also pops the key table (return to locked)
|
# Wrap a lua action string so it also pops the key table (return to locked)
|
||||||
andPop = action: "act.Multiple({ ${action}, act.PopKeyTable })";
|
andPop = action: "act.Multiple({ ${action}, act.PopKeyTable })";
|
||||||
|
|
||||||
# Standard exit bindings, given the mode's own pop key
|
# Standard exit bindings, given the mode's own pop key
|
||||||
exitKeys = {selfKey, indent ? " "}:
|
exitKeys = {
|
||||||
|
selfKey,
|
||||||
|
indent ? " ",
|
||||||
|
}:
|
||||||
lib.concatStrings [
|
lib.concatStrings [
|
||||||
(luaKey {inherit indent; key = selfKey; action = "act.PopKeyTable";})
|
(luaKey {
|
||||||
(luaKey {inherit indent; key = "Escape"; action = "act.PopKeyTable";})
|
inherit indent;
|
||||||
(luaKey {inherit indent; key = "Enter"; action = "act.PopKeyTable";})
|
key = selfKey;
|
||||||
(luaKey {inherit indent; key = "Space"; mods = "ALT"; action = "act.PopKeyTable";})
|
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 ───────────────────────────────────────────────────────────────
|
# ── 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 = {
|
modes = {
|
||||||
locked = {fg = color.base03; hint = "Alt+Space:enter Alt+hl:focus/tab Alt+1-9:tab Alt+n:split C-ud:scroll";};
|
locked = {
|
||||||
normal = {fg = color.base0D; hint = "p:pane t:tab r:resize s:scroll n:split Esc:exit";};
|
fg = color.base03;
|
||||||
pane = {fg = color.base0B; hint = "hjkl:focus n/r:split→ d:split↓ f:zoom x:close Esc:exit";};
|
hint = "⌥: ⌥hl: ^ud: ⌥1-9: ⌥n: ⌥s: ⌥w: ⌥t: ⌥p:";
|
||||||
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";};
|
normal = {
|
||||||
scroll = {fg = color.base0E; hint = "jk:line ud:half hl:page f:search Esc:exit";};
|
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 }
|
# Lua table literal mapping mode name -> { fg, hint }
|
||||||
modeTableEntries = lib.concatStringsSep "\n " (lib.mapAttrsToList (name: m: ''
|
modeTableEntries = lib.concatStringsSep "\n " (lib.mapAttrsToList (name: m: ''
|
||||||
["${name}"] = { fg = "${m.fg}", hint = "${m.hint}" },
|
["${name}"] = { fg = "${m.fg}", hint = "${m.hint}" },
|
||||||
'') modes);
|
'')
|
||||||
|
modes);
|
||||||
in {
|
in {
|
||||||
options.mods.terminal.wezterm.enable = lib.mkEnableOption "enables wezterm";
|
options.mods.terminal.wezterm.enable = lib.mkEnableOption "enables wezterm";
|
||||||
|
|
||||||
|
|
@ -122,6 +207,51 @@ in {
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- scroll_or_passthrough: Ctrl+key scrolls at the shell prompt but passes
|
||||||
|
-- the key through when a program has taken the alt screen (vim, less, fzf…).
|
||||||
|
local function scroll_or_passthrough(key, pages)
|
||||||
|
return wezterm.action_callback(function(window, pane)
|
||||||
|
if pane:is_alt_screen_active() then
|
||||||
|
window:perform_action(act.SendKey({ key = key, mods = "CTRL" }), pane)
|
||||||
|
else
|
||||||
|
window:perform_action(act.ScrollByPage(pages), pane)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- tab_is_zoomed: true if any pane in the current tab is zoomed.
|
||||||
|
-- Used by move_vertical to decide whether to carry zoom forward.
|
||||||
|
local function tab_is_zoomed(pane)
|
||||||
|
for _, info in ipairs(pane:tab():panes_with_info()) do
|
||||||
|
if info.is_zoomed then return true end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- move_vertical: move focus Up/Down.
|
||||||
|
-- If any pane in the tab is currently zoomed (zoom-mode is "on" for this
|
||||||
|
-- tab), zoom the destination pane too — stacked-panel behaviour.
|
||||||
|
-- Otherwise just move focus plainly.
|
||||||
|
local function move_vertical(direction)
|
||||||
|
return wezterm.action_callback(function(window, pane)
|
||||||
|
local neighbour = pane:tab():get_pane_direction(direction)
|
||||||
|
if neighbour == nil then return end
|
||||||
|
if tab_is_zoomed(pane) then
|
||||||
|
-- unzoom_on_switch_pane unzooms the current pane when we move;
|
||||||
|
-- then SetPaneZoomState(true) zooms the newly active pane.
|
||||||
|
window:perform_action(
|
||||||
|
act.Multiple({
|
||||||
|
act.ActivatePaneDirection(direction),
|
||||||
|
act.SetPaneZoomState(true),
|
||||||
|
}),
|
||||||
|
pane
|
||||||
|
)
|
||||||
|
else
|
||||||
|
window:perform_action(act.ActivatePaneDirection(direction), pane)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
-- ─── Status bar ────────────────────────────────────────────────────────────
|
-- ─── Status bar ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
-- Per-mode accent colour and hint text, generated from Nix/stylix palette
|
-- Per-mode accent colour and hint text, generated from Nix/stylix palette
|
||||||
|
|
@ -131,27 +261,107 @@ in {
|
||||||
|
|
||||||
local bg_bar = "${color.base01}"
|
local bg_bar = "${color.base01}"
|
||||||
local fg_text = "${color.base05}"
|
local fg_text = "${color.base05}"
|
||||||
|
local fg_dim = "${color.base03}"
|
||||||
local bg_main = "${color.base00}"
|
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("format-window-title", function(tab, _pane, _tabs, _panes, _config)
|
||||||
|
local ok, win = pcall(function() return wezterm.mux.get_window(tab.window_id) end)
|
||||||
|
if ok and win then
|
||||||
|
local ws = win:get_workspace()
|
||||||
|
if ws ~= "default" then
|
||||||
|
return ws .. " — " .. tab.active_pane.title
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return tab.active_pane.title
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- format-tab-title: show "rank/total title" when the active tab is in
|
||||||
|
-- stacked (zoom) mode; otherwise show plain " title ".
|
||||||
|
-- Returning a cell table gives us explicit padding and colours;
|
||||||
|
-- returning a plain string loses the retro tab bar's built-in spacing.
|
||||||
|
local tab_bg_active = "${color.base0D}"
|
||||||
|
local tab_fg_active = "${color.base00}"
|
||||||
|
local tab_bg_inactive = "${color.base01}"
|
||||||
|
local tab_fg_inactive = "${color.base03}"
|
||||||
|
|
||||||
|
wezterm.on("format-tab-title", function(tab, _tabs, panes, _config, _hover, _max_width)
|
||||||
|
local title = (tab.tab_title and tab.tab_title ~= "") and tab.tab_title or tab.active_pane.title
|
||||||
|
local bg = tab.is_active and tab_bg_active or tab_bg_inactive
|
||||||
|
local fg = tab.is_active and tab_fg_active or tab_fg_inactive
|
||||||
|
|
||||||
|
-- Only compute stack position for the active tab.
|
||||||
|
-- The `panes` event parameter only contains the active pane snapshot
|
||||||
|
-- and lacks `top`, so we use wezterm.mux.get_tab():panes_with_info()
|
||||||
|
-- which returns full PaneInformation including top/is_zoomed/is_active.
|
||||||
|
if tab.is_active then
|
||||||
|
local mux_tab = wezterm.mux.get_tab(tab.tab_id)
|
||||||
|
if mux_tab then
|
||||||
|
local infos = mux_tab:panes_with_info()
|
||||||
|
table.sort(infos, function(a, b) return a.top < b.top end)
|
||||||
|
|
||||||
|
local any_zoomed = false
|
||||||
|
local active_rank, total = 0, 0
|
||||||
|
for _, p in ipairs(infos) do
|
||||||
|
total = total + 1
|
||||||
|
if p.is_active then active_rank = total end
|
||||||
|
if p.is_zoomed then any_zoomed = true end
|
||||||
|
end
|
||||||
|
|
||||||
|
if any_zoomed and total > 1 and active_rank > 0 then
|
||||||
|
title = active_rank .. "/" .. total .. " " .. title
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
{ Background = { Color = bg } },
|
||||||
|
{ Foreground = { Color = fg } },
|
||||||
|
{ Text = " " .. title .. " " },
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
wezterm.on("update-status", function(window, _pane)
|
wezterm.on("update-status", function(window, _pane)
|
||||||
local kt = window:active_key_table()
|
local kt = window:active_key_table()
|
||||||
local mode = kt or "locked"
|
local mode = kt or "locked"
|
||||||
local info = mode_info[mode] or mode_info["locked"]
|
local info = mode_info[mode] or mode_info["locked"]
|
||||||
|
|
||||||
-- Left: coloured mode badge + hint line
|
window:set_left_status("")
|
||||||
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("")
|
-- 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)
|
end)
|
||||||
|
|
||||||
-- ─── Appearance ────────────────────────────────────────────────────────────
|
-- ─── Appearance ────────────────────────────────────────────────────────────
|
||||||
|
|
@ -170,7 +380,29 @@ in {
|
||||||
config.default_cursor_style = "BlinkingBar"
|
config.default_cursor_style = "BlinkingBar"
|
||||||
config.hide_tab_bar_if_only_one_tab = false
|
config.hide_tab_bar_if_only_one_tab = false
|
||||||
config.use_fancy_tab_bar = false
|
config.use_fancy_tab_bar = false
|
||||||
config.tab_bar_at_bottom = true
|
config.tab_max_width = 24
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
-- ─── SSH Domains ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
config.ssh_domains = {
|
||||||
|
{
|
||||||
|
name = "muho",
|
||||||
|
remote_address = "muho",
|
||||||
|
username = "muon",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
-- ─── Shell ─────────────────────────────────────────────────────────────────
|
-- ─── Shell ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -182,30 +414,53 @@ in {
|
||||||
|
|
||||||
config.scrollback_lines = 10000
|
config.scrollback_lines = 10000
|
||||||
|
|
||||||
|
-- ─── Stacked panes ─────────────────────────────────────────────────────────
|
||||||
|
-- When switching away from a zoomed pane, unzoom it automatically.
|
||||||
|
-- Paired with stack_focus (Alt+j/k) this gives Zellij-style stacked panels.
|
||||||
|
config.unzoom_on_switch_pane = true
|
||||||
|
|
||||||
-- ─── Key bindings ──────────────────────────────────────────────────────────
|
-- ─── Key bindings ──────────────────────────────────────────────────────────
|
||||||
--
|
--
|
||||||
-- Default mode = "locked" (all keys pass through to the shell).
|
-- Default mode = "locked" (all keys pass through to the shell).
|
||||||
-- Alt+Space enters "normal" mode (like Zellij).
|
-- Alt+Space enters "normal" mode.
|
||||||
-- From normal: p→pane t→tab r→resize s→scroll
|
-- From normal: t→new tab x→close tab p/n→split r→resize mode
|
||||||
-- Esc / Enter / Alt+Space → back to locked from any mode.
|
-- Esc / Enter / Alt+Space → back to locked from any mode.
|
||||||
|
|
||||||
config.disable_default_key_bindings = true
|
config.disable_default_key_bindings = true
|
||||||
|
|
||||||
config.keys = {
|
config.keys = {
|
||||||
-- Alt+h/l: move focus or switch tab at edge (Zellij MoveFocusOrTab)
|
-- Alt+h/l: move focus or switch tab at edge (Zellij MoveFocusOrTab)
|
||||||
-- Alt+j/k: move focus between panes
|
-- Alt+j/k: initial bindings (overridden below by stacked-focus variants)
|
||||||
${viDirKeys {
|
${viDirKeys {
|
||||||
mods = "ALT";
|
mods = "ALT";
|
||||||
indent = " ";
|
indent = " ";
|
||||||
mkAction = d:
|
mkAction = d:
|
||||||
if d == "Left" || d == "Right"
|
if d == "Left" || d == "Right"
|
||||||
then "move_focus_or_tab(\"${d}\")"
|
then "move_focus_or_tab(\"${d}\")"
|
||||||
else "act.ActivatePaneDirection(\"${d}\")";
|
else "act.ActivatePaneDirection(\"${d}\")";
|
||||||
}}
|
}}
|
||||||
-- Alt+1-9: jump to tab by number
|
-- Alt+1-9: jump to tab by number
|
||||||
${tabJumpKeys {mods = "ALT"; indent = " ";}}
|
${tabJumpKeys {
|
||||||
|
mods = "ALT";
|
||||||
|
indent = " ";
|
||||||
|
}}
|
||||||
-- Alt+n: new pane (split right)
|
-- Alt+n: new pane (split right)
|
||||||
{ key = "n", mods = "ALT", action = act.SplitHorizontal({ domain = "CurrentPaneDomain" }) },
|
{ key = "n", mods = "ALT", action = act.SplitHorizontal({ domain = "CurrentPaneDomain" }) },
|
||||||
|
-- Alt+s: split below, then zoom the new pane so stacked mode starts.
|
||||||
|
-- Two separate perform_action calls: the split runs first and focuses
|
||||||
|
-- the new pane, then the second call zooms window:active_pane() which
|
||||||
|
-- is now the new pane (not the original `pane` argument).
|
||||||
|
{ key = "s", mods = "ALT", action = wezterm.action_callback(function(window, pane)
|
||||||
|
window:perform_action(act.SplitVertical({ domain = "CurrentPaneDomain" }), pane)
|
||||||
|
window:perform_action(act.SetPaneZoomState(true), window:active_pane())
|
||||||
|
end)
|
||||||
|
},
|
||||||
|
-- Alt+j/k: move_vertical — plain focus move normally, stacked zoom
|
||||||
|
-- when any pane in the tab is already zoomed (mirrors Alt+f state).
|
||||||
|
{ key = "j", mods = "ALT", action = move_vertical("Down") },
|
||||||
|
{ key = "k", mods = "ALT", action = move_vertical("Up") },
|
||||||
|
{ key = "DownArrow", mods = "ALT", action = move_vertical("Down") },
|
||||||
|
{ key = "UpArrow", mods = "ALT", action = move_vertical("Up") },
|
||||||
-- Alt+f: toggle zoom
|
-- Alt+f: toggle zoom
|
||||||
{ key = "f", mods = "ALT", action = act.TogglePaneZoomState },
|
{ key = "f", mods = "ALT", action = act.TogglePaneZoomState },
|
||||||
-- Alt+[/]: rotate panes
|
-- Alt+[/]: rotate panes
|
||||||
|
|
@ -218,11 +473,31 @@ in {
|
||||||
-- Alt+i/o: reorder tabs
|
-- Alt+i/o: reorder tabs
|
||||||
{ key = "i", mods = "ALT", action = act.MoveTabRelative(-1) },
|
{ key = "i", mods = "ALT", action = act.MoveTabRelative(-1) },
|
||||||
{ key = "o", mods = "ALT", action = act.MoveTabRelative(1) },
|
{ key = "o", mods = "ALT", action = act.MoveTabRelative(1) },
|
||||||
|
-- Alt+w: close current pane; if tab was in stacked (zoom) mode, re-zoom
|
||||||
|
-- the new active pane so zoom mode persists across close.
|
||||||
|
-- confirm=false because the callback must run synchronously after close;
|
||||||
|
-- a confirm dialog would make the zoom fire before the user answers.
|
||||||
|
-- Guard: only re-zoom when there will still be panes left after close.
|
||||||
|
{ key = "w", mods = "ALT", action = wezterm.action_callback(function(window, pane)
|
||||||
|
local panes = pane:tab():panes()
|
||||||
|
local was_zoomed = tab_is_zoomed(pane)
|
||||||
|
window:perform_action(act.CloseCurrentPane({ confirm = false }), pane)
|
||||||
|
if was_zoomed and #panes > 1 then
|
||||||
|
window:perform_action(act.SetPaneZoomState(true), window:active_pane())
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
},
|
||||||
|
-- Alt+p: command palette
|
||||||
|
{ key = "p", mods = "ALT", action = act.ActivateCommandPalette },
|
||||||
|
-- Alt+t: tab navigator (searchable tab list)
|
||||||
|
{ key = "t", mods = "ALT", action = act.ShowTabNavigator },
|
||||||
-- Alt+q: quit
|
-- Alt+q: quit
|
||||||
{ key = "q", mods = "ALT", action = act.QuitApplication },
|
{ key = "q", mods = "ALT", action = act.QuitApplication },
|
||||||
-- Ctrl+U/D: scroll half page (matches Alacritty)
|
-- Ctrl+U/D: scroll half page when at the shell prompt; pass through
|
||||||
{ key = "u", mods = "CTRL", action = act.ScrollByPage(-0.5) },
|
-- to the running program when it is using the alt screen (vim, less,
|
||||||
{ key = "d", mods = "CTRL", action = act.ScrollByPage(0.5) },
|
-- fzf, etc. switch to the alt screen and need the raw key).
|
||||||
|
{ key = "u", mods = "CTRL", action = scroll_or_passthrough("u", -0.5) },
|
||||||
|
{ key = "d", mods = "CTRL", action = scroll_or_passthrough("d", 0.5) },
|
||||||
-- Copy / paste
|
-- Copy / paste
|
||||||
{ key = "c", mods = "CTRL|SHIFT", action = act.CopyTo("Clipboard") },
|
{ key = "c", mods = "CTRL|SHIFT", action = act.CopyTo("Clipboard") },
|
||||||
{ key = "v", mods = "CTRL|SHIFT", action = act.PasteFrom("Clipboard") },
|
{ key = "v", mods = "CTRL|SHIFT", action = act.PasteFrom("Clipboard") },
|
||||||
|
|
@ -234,77 +509,86 @@ in {
|
||||||
|
|
||||||
-- ── NORMAL ─────────────────────────────────────────────────────────────
|
-- ── NORMAL ─────────────────────────────────────────────────────────────
|
||||||
normal = {
|
normal = {
|
||||||
{ key = "p", action = act.ActivateKeyTable({ name = "pane", one_shot = false }) },
|
{ key = "t", action = ${andPop "act.SpawnTab(\"CurrentPaneDomain\")"} },
|
||||||
{ 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 = "x", action = ${andPop "act.CloseCurrentTab({ confirm = true })"} },
|
||||||
{ key = "r", action = act.PromptInputLine({
|
{ key = "r", action = act.ActivateKeyTable({ name = "resize", one_shot = false }) },
|
||||||
description = "Rename tab",
|
{ key = "c", action = ${andPop "act.ActivateCopyMode"} },
|
||||||
action = wezterm.action_callback(function(window, _, line)
|
{ key = "s", action = ${andPop "act.Search({ CaseSensitiveString = \"\" })"} },
|
||||||
if line then window:active_tab():set_title(line) end
|
${exitKeys {selfKey = "Escape";}} },
|
||||||
window:perform_action(act.PopKeyTable, window:active_pane())
|
|
||||||
end),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
${exitKeys {selfKey = "t";}} },
|
|
||||||
|
|
||||||
-- ── RESIZE ─────────────────────────────────────────────────────────────
|
-- ── RESIZE ─────────────────────────────────────────────────────────────
|
||||||
resize = {
|
resize = {
|
||||||
${resizeDirKeys {step = 5;}}${resizeShiftKeys {step = 1;}}
|
${resizeDirKeys {step = 5;}}${resizeShiftKeys {step = 1;}}
|
||||||
{ key = "+", action = act.AdjustPaneSize({ "Right", 5 }) },
|
{ key = "+", action = act.AdjustPaneSize({ "Right", 5 }) },
|
||||||
{ key = "-", action = act.AdjustPaneSize({ "Left", 5 }) },
|
{ key = "-", action = act.AdjustPaneSize({ "Left", 5 }) },
|
||||||
{ key = "=", action = act.AdjustPaneSize({ "Right", 5 }) },
|
{ key = "=", action = act.AdjustPaneSize({ "Right", 5 }) },
|
||||||
${exitKeys {selfKey = "r";}} },
|
${exitKeys {selfKey = "r";}} },
|
||||||
|
|
||||||
-- ── SCROLL ─────────────────────────────────────────────────────────────
|
-- ── COPY MODE ──────────────────────────────────────────────────────────
|
||||||
scroll = {
|
copy_mode = {
|
||||||
{ key = "j", action = act.ScrollByLine(1) },
|
-- movement
|
||||||
{ key = "k", action = act.ScrollByLine(-1) },
|
{ key = "h", action = act.CopyMode("MoveLeft") },
|
||||||
{ key = "d", action = act.ScrollByPage(0.5) },
|
{ key = "j", action = act.CopyMode("MoveDown") },
|
||||||
{ key = "u", action = act.ScrollByPage(-0.5) },
|
{ key = "k", action = act.CopyMode("MoveUp") },
|
||||||
{ key = "h", action = act.ScrollByPage(-1) },
|
{ key = "l", action = act.CopyMode("MoveRight") },
|
||||||
{ key = "l", action = act.ScrollByPage(1) },
|
{ key = "LeftArrow", action = act.CopyMode("MoveLeft") },
|
||||||
{ key = "DownArrow", action = act.ScrollByLine(1) },
|
{ key = "DownArrow", action = act.CopyMode("MoveDown") },
|
||||||
{ key = "UpArrow", action = act.ScrollByLine(-1) },
|
{ key = "UpArrow", action = act.CopyMode("MoveUp") },
|
||||||
{ key = "PageDown", action = act.ScrollByPage(1) },
|
{ key = "RightArrow", action = act.CopyMode("MoveRight") },
|
||||||
{ key = "PageUp", action = act.ScrollByPage(-1) },
|
{ key = "w", action = act.CopyMode("MoveForwardWord") },
|
||||||
{ key = "f", action = act.Search({ CaseSensitiveString = "" }) },
|
{ key = "b", action = act.CopyMode("MoveBackwardWord") },
|
||||||
{ key = "c", mods = "CTRL", action = act.ScrollToBottom },
|
{ key = "e", action = act.CopyMode("MoveForwardWordEnd") },
|
||||||
${exitKeys {selfKey = "s";}} },
|
{ 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
|
return config
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
{ pkgs, lib, ... }: {
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
imports = [
|
imports = [
|
||||||
./containers
|
./containers
|
||||||
./gaming
|
./gaming
|
||||||
|
|
@ -25,5 +29,6 @@
|
||||||
./lemmy.nix
|
./lemmy.nix
|
||||||
./audio.nix
|
./audio.nix
|
||||||
./atuin.nix
|
./atuin.nix
|
||||||
|
./murmur.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
modules/nixos/server/murmur.nix
Normal file
25
modules/nixos/server/murmur.nix
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
cfg = config.mods.server.murmur;
|
||||||
|
in
|
||||||
|
with lib; {
|
||||||
|
options.mods.server = {
|
||||||
|
murmur = {
|
||||||
|
enable = mkEnableOption {
|
||||||
|
default = false;
|
||||||
|
description = "enables murmur server";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
services.murmur = {
|
||||||
|
enable = true;
|
||||||
|
openFirewall = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
42
utils.nix
42
utils.nix
|
|
@ -1,4 +1,7 @@
|
||||||
{ inputs, system, sources, ... }: {
|
{ inputs, system, sources, ... }:
|
||||||
|
let
|
||||||
|
pkgs = import inputs.nixpkgs { inherit system; };
|
||||||
|
in {
|
||||||
mkHost = host:
|
mkHost = host:
|
||||||
inputs.nixpkgs.lib.nixosSystem {
|
inputs.nixpkgs.lib.nixosSystem {
|
||||||
specialArgs = { inherit inputs system sources; };
|
specialArgs = { inherit inputs system sources; };
|
||||||
|
|
@ -11,4 +14,41 @@
|
||||||
inputs.impermanence.nixosModules.impermanence
|
inputs.impermanence.nixosModules.impermanence
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Build a standalone HM configuration for a given host NixOS config and
|
||||||
|
# home.nix file. osConfig is injected as a specialArg so all modules that
|
||||||
|
# reference it continue to work.
|
||||||
|
mkHome = { hostConfig, homeFile, username ? "muon" }:
|
||||||
|
inputs.home-manager.lib.homeManagerConfiguration {
|
||||||
|
inherit pkgs;
|
||||||
|
extraSpecialArgs = {
|
||||||
|
inherit inputs system sources;
|
||||||
|
osConfig = hostConfig.config;
|
||||||
|
};
|
||||||
|
modules = [
|
||||||
|
homeFile
|
||||||
|
inputs.self.outputs.homeManagerModules.default
|
||||||
|
inputs.stylix.homeManagerModules.stylix
|
||||||
|
({ osConfig, ... }: {
|
||||||
|
home.username = username;
|
||||||
|
home.homeDirectory = "/home/${username}";
|
||||||
|
# Mirror the stylix settings from the NixOS config so the standalone
|
||||||
|
# HM build has the same theme without re-declaring it.
|
||||||
|
stylix = {
|
||||||
|
enable = osConfig.stylix.enable;
|
||||||
|
autoEnable = osConfig.stylix.autoEnable;
|
||||||
|
base16Scheme = osConfig.stylix.base16Scheme;
|
||||||
|
image = osConfig.stylix.image;
|
||||||
|
cursor = osConfig.stylix.cursor;
|
||||||
|
# Forward individual font options; stylix derives fonts.packages
|
||||||
|
# internally as read-only so we must not forward that attr.
|
||||||
|
fonts.monospace = osConfig.stylix.fonts.monospace;
|
||||||
|
fonts.sansSerif = osConfig.stylix.fonts.sansSerif;
|
||||||
|
fonts.serif = osConfig.stylix.fonts.serif;
|
||||||
|
fonts.emoji = osConfig.stylix.fonts.emoji;
|
||||||
|
fonts.sizes = osConfig.stylix.fonts.sizes;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue