{ 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-) 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]; }; }; }; }