mirror of
https://codeberg.org/muon/home.git
synced 2026-03-09 11:53:12 +00:00
250 lines
8.9 KiB
Nix
250 lines
8.9 KiB
Nix
{
|
|
pkgs,
|
|
lib,
|
|
config,
|
|
osConfig,
|
|
...
|
|
}: 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;
|
|
writeShellApplication {
|
|
name = "zmenu";
|
|
runtimeInputs =
|
|
[zoxide wmctrl i3 rofi zsh]
|
|
++ lib.optionals config.mods.terminal.wezterm.enable [wezterm]
|
|
++ lib.optionals (!config.mods.terminal.wezterm.enable) [zellij alacritty];
|
|
text =
|
|
if config.mods.terminal.wezterm.enable
|
|
then ''
|
|
ZPATH=$(zoxide query -l | sed -e "s|$HOME/||g" | rofi -dmenu)
|
|
[[ -z "$ZPATH" ]] && exit
|
|
ZSESH=$(echo "$ZPATH" | tr / -)
|
|
|
|
# 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
|
|
with lib; {
|
|
options.mods.i3.enable = mkEnableOption "enables i3";
|
|
options.mods.battery.enable = mkEnableOption "enables battery";
|
|
|
|
config = mkIf config.mods.i3.enable {
|
|
services.unclutter.enable = true;
|
|
programs.rofi.enable = true;
|
|
|
|
programs.i3status-rust = {
|
|
enable = true;
|
|
bars.default = lib.mkMerge [
|
|
{
|
|
blocks = lib.mkBefore [
|
|
{
|
|
block = "privacy";
|
|
driver = [{name = "v4l";} {name = "pipewire";}];
|
|
}
|
|
# {
|
|
# block = "net";
|
|
# format = " $icon ";
|
|
# inactive_format = " $icon ";
|
|
# }
|
|
];
|
|
}
|
|
(lib.mkIf config.mods.battery.enable {
|
|
blocks = [
|
|
{
|
|
block = "battery";
|
|
format = " $icon $percentage ";
|
|
# format_alt =
|
|
# " $icon $percentage {$time_remaining.dur(hms:true, min_unit:m) |}";
|
|
}
|
|
];
|
|
})
|
|
{
|
|
settings.theme.overrides = lib.mkMerge [
|
|
{separator = "";}
|
|
config.lib.stylix.i3status-rust.bar
|
|
];
|
|
icons = "awesome5";
|
|
blocks = lib.mkAfter [
|
|
{
|
|
block = "sound";
|
|
format = " $icon {$volume |} ";
|
|
click = [
|
|
{
|
|
button = "left";
|
|
cmd = "${lib.getExe pkgs.pavucontrol}";
|
|
}
|
|
];
|
|
}
|
|
{
|
|
block = "time";
|
|
format = "$icon $timestamp.datetime(f:'%Y-%m-%d %a %H:%M:%S') ";
|
|
# format_alt = "$icon $timestamp.datetime(f:'%Y-%m-%d %H:%M') ";
|
|
interval = 1;
|
|
}
|
|
];
|
|
}
|
|
];
|
|
};
|
|
|
|
xsession.windowManager.i3 = let
|
|
modifier = "Mod4";
|
|
wsAmount = 10;
|
|
genSpaces = f: map builtins.toString (builtins.genList f wsAmount);
|
|
workspaces = genSpaces (x: x + 1);
|
|
in {
|
|
enable = true;
|
|
config = {
|
|
modifier = modifier;
|
|
terminal =
|
|
if config.mods.terminal.wezterm.enable
|
|
then "wezterm"
|
|
else "alacritty";
|
|
menu = "rofi -show drun";
|
|
|
|
window = {
|
|
titlebar = false;
|
|
hideEdgeBorders = "smart";
|
|
};
|
|
|
|
floating.criteria = [
|
|
{title = "^Cheat$";}
|
|
];
|
|
|
|
defaultWorkspace = "workspace number 1";
|
|
workspaceAutoBackAndForth = true;
|
|
focus.wrapping = "yes";
|
|
|
|
# startup = [ ] ++ lib.optionals config.mods.social.enable [{
|
|
# command = "exec vesktop";
|
|
# }];
|
|
|
|
assigns = {
|
|
"2" = [{class = "Vesktop";}];
|
|
"3" = [{class = "^Steam$";}];
|
|
};
|
|
|
|
keybindings = let
|
|
# modKeyComb: string -> valueAction: string
|
|
# -> keys: [string] -> values: [string]
|
|
# -> keybindings: attrSet { string -> string }
|
|
zipBinds = comb: action: keys: values: (builtins.listToAttrs (zipListsWith (k: v: {
|
|
name = "${modifier}${comb}${k}";
|
|
value = "${action} ${v}";
|
|
})
|
|
keys
|
|
values));
|
|
|
|
moveKeys = ["h" "j" "k" "l"];
|
|
moveDirs = ["left" "down" "up" "right"];
|
|
|
|
workspKeys = genSpaces (x: mod (x + 1) wsAmount);
|
|
in
|
|
mkOptionDefault ((zipBinds "+" "focus" moveKeys moveDirs)
|
|
// (zipBinds "+Shift+" "move" moveKeys moveDirs)
|
|
// (zipBinds "+" "workspace number" workspKeys workspaces)
|
|
// (zipBinds "+Shift+" "move container to workspace number" workspKeys
|
|
workspaces)
|
|
// {
|
|
"XF86AudioRaiseVolume" = "exec --no-startup-id pactl set-sink-volume 0 +2%";
|
|
"XF86AudioLowerVolume" = "exec --no-startup-id pactl set-sink-volume 0 -2%";
|
|
"Print" = "exec flameshot gui -c -s";
|
|
"${modifier}+z" = "exec ${getExe zmenu}";
|
|
"${modifier}+p" = "exec clipmenu";
|
|
"${modifier}+b" = "exec ${getExe pkgs.rofi-rbw-x11}";
|
|
"${modifier}+y" = "sticky toggle";
|
|
"${modifier}+g" = "floating toggle";
|
|
});
|
|
|
|
bars = let
|
|
status_conf = "${config.xdg.configHome}/i3status-rust/config-default.toml";
|
|
stylix_bar =
|
|
{
|
|
mode = "dock";
|
|
hiddenState = "hide";
|
|
position = "bottom";
|
|
statusCommand = "${getExe pkgs.i3status-rust} ${status_conf}";
|
|
command = "${pkgs.i3}/bin/i3bar";
|
|
workspaceButtons = true;
|
|
workspaceNumbers = true;
|
|
trayOutput = "primary";
|
|
}
|
|
// config.stylix.targets.i3.exportedBarConfig;
|
|
in [stylix_bar];
|
|
};
|
|
};
|
|
};
|
|
}
|