{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.home.i3_sway;
shared_config = recursiveUpdate rec {
modifier = "Mod4";
terminal = "ghostty";
defaultWorkspace = "workspace number 1";
keybindings = {
"${shared_config.modifier}+Return" = "exec ${terminal}";
"${shared_config.modifier}+Shift+q" = "kill";
"${shared_config.modifier}+h" = "focus left";
"${shared_config.modifier}+j" = "focus down";
"${shared_config.modifier}+k" = "focus up";
"${shared_config.modifier}+l" = "focus right";
"${shared_config.modifier}+Shift+h" = "move left";
"${shared_config.modifier}+Shift+j" = "move down";
"${shared_config.modifier}+Shift+k" = "move up";
"${shared_config.modifier}+Shift+l" = "move right";
"${shared_config.modifier}+Left" = "focus left";
"${shared_config.modifier}+Down" = "focus down";
"${shared_config.modifier}+Up" = "focus up";
"${shared_config.modifier}+Right" = "focus right";
"${shared_config.modifier}+Shift+Left" = "move left";
"${shared_config.modifier}+Shift+Down" = "move down";
"${shared_config.modifier}+Shift+Up" = "move up";
"${shared_config.modifier}+Shift+Right" = "move right";
#"${shared_config.modifier}+h" = "split h";
"${shared_config.modifier}+v" = "split v";
"${shared_config.modifier}+Shift+f" = "fullscreen toggle";
"${shared_config.modifier}+s" = "layout stacking";
"${shared_config.modifier}+w" = "layout tabbed";
"${shared_config.modifier}+e" = "layout toggle split";
"${shared_config.modifier}+Shift+space" = "floating toggle";
"${shared_config.modifier}+space" = "focus mode_toggle";
"${shared_config.modifier}+a" = "focus parent";
"${shared_config.modifier}+Shift+minus" = "move scratchpad";
"${shared_config.modifier}+minus" = "scratchpad show";
"${shared_config.modifier}+1" = "workspace number 1";
"${shared_config.modifier}+2" = "workspace number 2";
"${shared_config.modifier}+3" = "workspace number 3";
"${shared_config.modifier}+4" = "workspace number 4";
"${shared_config.modifier}+5" = "workspace number 5";
"${shared_config.modifier}+6" = "workspace number 6";
"${shared_config.modifier}+7" = "workspace number 7";
"${shared_config.modifier}+8" = "workspace number 8";
"${shared_config.modifier}+9" = "workspace number 9";
"${shared_config.modifier}+0" = "workspace number 10";
"${shared_config.modifier}+Shift+1" =
"move container to workspace number 1";
"${shared_config.modifier}+Shift+2" =
"move container to workspace number 2";
"${shared_config.modifier}+Shift+3" =
"move container to workspace number 3";
"${shared_config.modifier}+Shift+4" =
"move container to workspace number 4";
"${shared_config.modifier}+Shift+5" =
"move container to workspace number 5";
"${shared_config.modifier}+Shift+6" =
"move container to workspace number 6";
"${shared_config.modifier}+Shift+7" =
"move container to workspace number 7";
"${shared_config.modifier}+Shift+8" =
"move container to workspace number 8";
"${shared_config.modifier}+Shift+9" =
"move container to workspace number 9";
"${shared_config.modifier}+Shift+0" =
"move container to workspace number 10";
"${shared_config.modifier}+Shift+c" = "reload";
"${shared_config.modifier}+Shift+r" = "restart";
"${shared_config.modifier}+r" = "mode resize";
"XF86MonBrightnessUp" = "exec brightnessctl s +5%";
"XF86MonBrightnessDown" = "exec brightnessctl s 5%-";
};
} cfg.extraSharedConfig;
in {
options.home.i3_sway = {
extraSharedConfig = mkOption {
default = {};
};
extraI3Config = mkOption {
default = {};
};
extraSwayConfig = mkOption {
default = {};
};
};
config = {
# i3blocks configuration file
home.file.".config/i3blocks/config".text = ''
# i3blocks config - replicating waybar setup
separator_block_width=15
markup=pango
[disk]
command=df -h / | awk 'NR==2 {print "đž " $5}'
interval=30
separator=true
[cpu]
command=top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print "đ§ " int(100 - $1) "%"}'
interval=2
separator=true
[memory]
command=free | awk 'NR==2 {printf "đ %.0f%%\n", $3*100/$2}'
interval=5
separator=true
[pulseaudio]
command=${pkgs.writeShellScript "i3blocks-pulseaudio" ''
volume=$(pactl get-sink-volume @DEFAULT_SINK@ | grep -Po '\d+%' | head -1)
muted=$(pactl get-sink-mute @DEFAULT_SINK@ | grep -o 'yes')
if [ "$muted" = "yes" ]; then
echo "đ"
else
vol_num=''${volume%\%}
if [ $vol_num -le 33 ]; then
echo "đ $volume"
elif [ $vol_num -le 66 ]; then
echo "đ $volume"
else
echo "đ $volume"
fi
fi
''}
interval=1
signal=10
separator=true
[backlight]
command=${pkgs.writeShellScript "i3blocks-backlight" ''
if command -v brightnessctl &>/dev/null; then
brightnessctl g | awk -v max=$(brightnessctl m) '{printf "âī¸ %.0f%%\n", ($1/max)*100}'
fi
''}
interval=1
separator=true
[network]
command=${pkgs.writeShellScript "i3blocks-network" ''
if iwgetid -r &>/dev/null; then
ssid=$(iwgetid -r)
signal=$(grep "^\s*w" /proc/net/wireless | awk '{print int($3 * 100 / 70)}')
echo "đļ $ssid ($signal%)"
else
ip=$(ip -4 addr show | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v '127.0.0.1' | head -1)
if [ -n "$ip" ]; then
echo "đ $ip"
else
echo "â"
fi
fi
''}
interval=5
separator=true
[battery]
command=${pkgs.writeShellScript "i3blocks-battery" ''
if [ -d /sys/class/power_supply/BAT0 ]; then
capacity=$(cat /sys/class/power_supply/BAT0/capacity)
status=$(cat /sys/class/power_supply/BAT0/status)
if [ "$status" = "Charging" ]; then
echo "⥠$capacity%"
else
echo "đ $capacity%"
fi
fi
''}
interval=10
separator=true
[time]
command=date '+%Y-%m-%d %H:%M'
interval=1
separator=false
'';
xsession.windowManager.i3 = let
base_i3_config = recursiveUpdate shared_config {
bars = [{
position = "bottom";
statusCommand = "${pkgs.i3blocks}/bin/i3blocks";
trayOutput = "primary"; # Enable system tray on primary output
fonts = {
names = [ "Fira Code" "monospace" ];
size = 11.0;
};
colors = {
background = "#000000";
statusline = "#ffffff";
separator = "#666666";
# Workspace button colors (matching waybar)
focusedWorkspace = {
border = "#285577";
background = "#285577";
text = "#ffffff";
};
activeWorkspace = {
border = "#5f676a";
background = "#5f676a";
text = "#ffffff";
};
inactiveWorkspace = {
border = "#222222";
background = "#222222";
text = "#888888";
};
urgentWorkspace = {
border = "#900000";
background = "#900000";
text = "#ffffff";
};
};
}];
keybindings = shared_config.keybindings // {
"${shared_config.modifier}+d" = "exec rofi -show drun";
"${shared_config.modifier}+Shift+e" =
"exec i3-nagbar -t warning -m 'Do you want to exit i3?' -b 'Yes' 'i3-msg exit'";
};
startup = [
# GNOME polkit authentication agent
{
command = "/run/current-system/sw/libexec/polkit-gnome-authentication-agent-1";
always = false;
notification = false;
}
# Picom compositor for smooth rendering and no tearing (important for Nvidia)
{
command = "picom --backend glx -b";
always = false;
notification = false;
}
# NetworkManager system tray applet
{
command = "nm-applet";
always = false;
notification = false;
}
# Set wallpaper with feh
{
command = "feh --bg-scale ${../../wallpapers/metroid-samus-returns-kz-3440x1440.jpg}";
always = false;
notification = false;
}
];
};
in {
enable = true;
config = recursiveUpdate base_i3_config cfg.extraI3Config;
};
wayland.windowManager.sway = let
base_sway_config = recursiveUpdate shared_config {
bars = []; # Disable default bar, use waybar instead
keybindings = shared_config.keybindings // {
"${shared_config.modifier}+d" = "exec wofi --show drun";
"${shared_config.modifier}+Shift+e" =
"exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'";
};
input = {
"type:keyboard" = {
xkb_options = "caps:escape";
};
"type:touchpad" = {
tap = "enabled";
tap_button_map = "lrm";
drag = "enabled";
natural_scroll = "disabled";
dwt = "enabled";
};
};
output = {
"*" = {
bg = "${../../wallpapers/metroid-samus-returns-kz-3440x1440.jpg} fill";
};
};
};
in {
enable = true;
config = recursiveUpdate base_sway_config cfg.extraSwayConfig;
};
programs.waybar = {
enable = true;
systemd.enable = true;
settings = {
mainBar = {
layer = "top";
position = "bottom";
height = 30;
spacing = 4;
modules-left = [ "sway/workspaces" "sway/mode" ];
modules-center = [ ];
modules-right = [ "disk" "cpu" "memory" "pulseaudio" "backlight" "network" "battery" "tray" "clock" ];
"sway/workspaces" = {
disable-scroll = true;
all-outputs = true;
};
"clock" = {
format = "{:%Y-%m-%d %H:%M}";
tooltip-format = "{calendar}";
calendar = {
mode = "year";
mode-mon-col = 3;
weeks-pos = "right";
on-scroll = 1;
format = {
months = "{}";
days = "{}";
weeks = "W{}";
weekdays = "{}";
today = "{}";
};
};
};
"disk" = {
interval = 30;
format = "đž {percentage_used}%";
path = "/";
tooltip-format = "Used: {used} / {total} ({percentage_used}%)\nFree: {free} ({percentage_free}%)";
};
"cpu" = {
format = "đ§ {usage}%";
tooltip = false;
};
"memory" = {
format = "đ {percentage}%";
tooltip-format = "RAM: {used:0.1f}G / {total:0.1f}G";
};
"pulseaudio" = {
format = "{icon} {volume}%";
format-muted = "đ";
format-icons = {
headphone = "đ§";
default = [ "đ" "đ" "đ" ];
};
on-click = "pavucontrol";
};
"backlight" = {
format = "âī¸ {percent}%";
tooltip = false;
};
"network" = {
format-wifi = "đļ {essid} ({signalStrength}%)";
format-ethernet = "đ {ipaddr}";
format-disconnected = "â";
tooltip-format = "{ifname}: {ipaddr}/{cidr}";
};
"battery" = {
states = {
warning = 30;
critical = 15;
};
format = "{icon} {capacity}%";
format-charging = "⥠{capacity}%";
format-icons = [ "đĒĢ" "đ" "đ" "đ" "đ" ];
};
"tray" = {
spacing = 10;
};
};
};
style = ''
* {
padding: 0 4px;
font-family: "Fira Code", monospace;
font-size: 13px;
}
#workspaces button {
padding: 0 8px;
background-color: transparent;
color: #ffffff;
border: none;
}
#workspaces button.focused {
background-color: #285577;
font-weight: bold;
}
#workspaces button.visible {
background-color: #5f676a;
}
#workspaces button.urgent {
background-color: #900000;
}
'';
};
programs.rofi = {
enable = true;
theme = "solarized";
extraConfig = {
modi = "drun,run,window";
show-icons = true;
drun-display-format = "{name}";
disable-history = false;
hide-scrollbar = true;
display-drun = " Apps";
display-run = " Run";
display-window = " Windows";
sidebar-mode = true;
};
};
};
}