nix/home/modules/ui.nix

473 lines
13 KiB
Nix

{
pkgs,
lib,
config,
...
}:
let
mkWaybarTheme = palette: ''
* { color: ${palette.fg}; }
window#waybar { background: ${palette.bg}; border-bottom: 2px solid ${palette.bgAlt}; }
#workspaces button { background: transparent; }
#workspaces button.active { box-shadow: inset 0 2px ${palette.accent}; }
#workspaces button:hover { background: ${palette.bgAlt}; }
#window { color: ${palette.fgAlt}; }
tooltip { background: ${palette.bgAlt}; color: ${palette.fg}; border: 1px solid ${palette.border}; }
'';
mkEwwTheme = palette: ''
$bg: ${palette.bg};
$fg: ${palette.fg};
$bgAlt: ${palette.bgAlt};
$fgAlt: ${palette.fgAlt};
$border: ${palette.border};
$accent: ${palette.accent};
'';
hexToFuzzel = hex: "${builtins.substring 1 6 hex}ff";
mkFuzzelTheme = palette: ''
[colors]
background=${hexToFuzzel palette.bg}
text=${hexToFuzzel palette.fg}
prompt=${hexToFuzzel palette.fgAlt}
placeholder=${hexToFuzzel palette.fgAlt}
input=${hexToFuzzel palette.fg}
match=${hexToFuzzel palette.accent}
selection=${hexToFuzzel palette.bgAlt}
selection-text=${hexToFuzzel palette.fg}
selection-match=${hexToFuzzel palette.accent}
border=${hexToFuzzel palette.border}
counter=${hexToFuzzel palette.fgAlt}
'';
mkDunstTheme = palette: ''
[global]
frame_color = "${palette.border}"
separator_color = "frame"
background = "${palette.bg}"
foreground = "${palette.fg}"
[urgency_low]
background = "${palette.bg}"
foreground = "${palette.fg}"
frame_color = "${palette.border}"
[urgency_normal]
background = "${palette.bg}"
foreground = "${palette.fg}"
frame_color = "${palette.border}"
[urgency_critical]
background = "${palette.bg}"
foreground = "${palette.red}"
frame_color = "${palette.red}"
'';
in
{
home.sessionVariables = lib.mkMerge [
{ QT_AUTO_SCREEN_SCALE_FACTOR = "1"; }
(lib.mkIf config.gtk.enable {
GTK_RC_FILES = "${config.xdg.configHome}/gtk-2.0/gtkrc";
})
];
dconf = {
enable = true;
settings."org/gnome/desktop/interface" = {
font-name = "SF Pro Display 11";
document-font-name = "SF Pro Display 11";
monospace-font-name = "Berkeley Mono 11";
};
};
home.packages = with pkgs; [
nerd-fonts.jetbrains-mono
papirus-icon-theme
psmisc
fuzzel
eww
wl-clipboard
grim
slurp
wf-recorder
libnotify
brightnessctl
socat
glib.bin
gsettings-desktop-schemas
(python3.withPackages (ps: [ ps.pillow ]))
];
programs.waybar = {
enable = true;
systemd.enable = false;
settings.mainBar = {
reload_style_on_change = true;
layer = "top";
position = "top";
exclusive = false;
height = 38;
modules-left = [ "hyprland/workspaces" ];
modules-center = [ "hyprland/window" ];
modules-right = [
"tray"
"custom/keyboard"
"privacy"
"wireplumber#source"
"wireplumber"
"network"
"battery"
"clock"
"custom/power"
];
"hyprland/workspaces" = {
format = "{id}";
disable-scroll = true;
all-outputs = true;
tooltip = true;
};
"custom/keyboard" = {
exec = "hyprctl devices -j | jq -r '.keyboards[] | select(.main) | .active_keymap' 2>/dev/null || echo 'unknown'";
interval = 1;
format = "󰌌";
tooltip = true;
tooltip-format = "Layout: {}";
on-click = "ctl keyboard pick";
};
privacy = {
icon-size = 14;
icon-spacing = 6;
};
tray = {
icon-size = 16;
spacing = 8;
tooltip = true;
show-passive-items = true;
};
"hyprland/window" = {
format = "{}";
max-length = 40;
separate-outputs = true;
rewrite = { };
};
wireplumber = {
format = "{icon}";
format-muted = "󰖁";
format-icons = [ "󰕿" "󰖀" "󰕾" ];
node-type = "Audio/Sink";
max-volume = 100;
scroll-step = 5;
on-click = "ctl audio sink";
on-click-right = "eww open --toggle volume";
on-click-middle = "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle";
tooltip = true;
tooltip-format = "{volume}%";
};
"wireplumber#source" = {
format = "{icon}";
format-muted = "󰍭";
format-icons = [ "󰍬" ];
node-type = "Audio/Source";
max-volume = 100;
scroll-step = 5;
on-click = "ctl audio source";
on-click-right = "eww open --toggle volume";
on-click-middle = "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle";
tooltip = true;
tooltip-format = "{volume}%";
};
network = {
format-wifi = "󰖩";
format-ethernet = "󰈀";
format-disconnected = "󰖪";
format-disabled = "󰖪";
interval = 10;
tooltip = true;
tooltip-format-wifi = "SSID: {essid}\nSignal: {signalStrength}%\nDownload: {bandwidthDownBits}\nUpload: {bandwidthUpBits}\nIP: {ipaddr}";
tooltip-format-ethernet = "Interface: {ifname}\nIP: {ipaddr}/{cidr}\nDownload: {bandwidthDownBits}\nUpload: {bandwidthUpBits}";
tooltip-format-disconnected = "Wireless LAN disconnected";
on-click = "ctl wifi pick";
};
battery = {
format = "{icon}";
format-charging = "{icon}";
format-full = "{icon}";
format-icons = [
""
""
""
""
""
];
states = {
hi = 30;
mid = 20;
lo = 10;
ultralo = 5;
};
events = {
on-discharging-hi = "notify-send -u low 'battery 30%'";
on-discharging-mid = "notify-send -u normal 'battery 20%'";
on-discharging-lo = "notify-send -u critical 'battery 10%'";
on-discharging-ultralo = "notify-send -u critical 'battery 5%'";
on-charging-100 = "notify-send -u low 'battery 100%'";
};
interval = 30;
tooltip = true;
tooltip-format-discharging = "Discharging: {capacity}%\n{timeTo}";
tooltip-format-charging = "Charging: {capacity}%\n{timeTo}";
tooltip-format-full = "Full: {capacity}%";
tooltip-format = "{capacity}%\n{timeTo}";
};
clock = {
format = " {:%a %d/%m/%Y %H:%M:%S}";
interval = 1;
tooltip = false;
};
"custom/power" = {
format = "󰐥";
tooltip = true;
tooltip-format = "power menu";
on-click = "ctl power";
};
};
style = ''
@import url("${config.xdg.configHome}/waybar/themes/theme.css");
* {
font-family: "SF Pro Display", "JetBrainsMono Nerd Font", sans-serif;
font-size: 14px;
border: none;
border-radius: 0;
min-height: 0;
}
#workspaces button {
font-family: "SF Pro Display", sans-serif;
padding: 0 7px;
min-width: 20px;
background: transparent;
box-shadow: none;
transition: none;
}
#workspaces button:hover {
transition: none;
}
#custom-keyboard,
#privacy,
#tray,
#wireplumber,
#network,
#battery,
#clock,
#custom-power {
padding: 0 10px;
}
#window {
padding: 0 16px;
}
tooltip {
border-radius: 0;
}
#custom-power {
padding: 0 16px 0 10px;
}
'';
};
services.cliphist.enable = true;
services.dunst = {
enable = true;
settings = {
global = {
font = "SF Pro Display 13";
width = "(0, 400)";
height = "(0, 120)";
origin = "top-right";
offset = "(16, 16)";
padding = 10;
horizontal_padding = 10;
frame_width = 3;
separator_height = 1;
gap_size = 8;
corner_radius = 0;
alignment = "left";
ellipsize = "end";
icon_position = "left";
max_icon_size = 32;
};
ctl = {
appname = "ctl";
icon_position = "off";
format = "%s";
};
};
};
xdg.configFile."dunst/themes/midnight.conf".text = mkDunstTheme config.palettes.midnight;
xdg.configFile."dunst/themes/daylight.conf".text = mkDunstTheme config.palettes.daylight;
xdg.configFile."waybar/themes/midnight.css".text = mkWaybarTheme config.palettes.midnight;
xdg.configFile."waybar/themes/daylight.css".text = mkWaybarTheme config.palettes.daylight;
xdg.configFile."eww/scripts/audio" = {
executable = true;
text = ''
#!/bin/sh
emit() {
sink=$(wpctl get-volume @DEFAULT_AUDIO_SINK@ 2>/dev/null)
source=$(wpctl get-volume @DEFAULT_AUDIO_SOURCE@ 2>/dev/null)
sv=$(echo "$sink" | awk '{printf "%d", $2 * 100}')
sm=$(echo "$sink" | grep -q MUTED && echo true || echo false)
iv=$(echo "$source" | awk '{printf "%d", $2 * 100}')
im=$(echo "$source" | grep -q MUTED && echo true || echo false)
printf '{"sv":%d,"sm":%s,"iv":%d,"im":%s}\n' "$sv" "$sm" "$iv" "$im"
}
emit
pactl subscribe 2>/dev/null | while read -r line; do
case "$line" in
*"change"*"sink"*|*"change"*"source"*|*"change"*"server"*) emit ;;
esac
done
'';
};
xdg.configFile."eww/eww.yuck".text = ''
(deflisten audio :initial '{"sv":0,"sm":false,"iv":0,"im":false}' "''${EWW_CONFIG_DIR}/scripts/audio")
(defwidget volume-popup []
(box :class "volume-popup" :orientation "v" :space-evenly false :spacing 12
(box :class "slider-row" :orientation "h" :space-evenly false :spacing 8
(button :class "mute-btn" :onclick "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
(label :text {audio.sm ? "󰖁" : audio.sv <= 33 ? "󰕿" : audio.sv <= 66 ? "󰖀" : "󰕾"}))
(scale :class "vol-slider" :value {audio.sv} :min 0 :max 100 :orientation "h"
:onchange "wpctl set-volume @DEFAULT_AUDIO_SINK@ {}%"))
(box :class "slider-row" :orientation "h" :space-evenly false :spacing 8
(button :class "mute-btn" :onclick "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"
(label :text {audio.im ? "󰍭" : "󰍬"}))
(scale :class "vol-slider" :value {audio.iv} :min 0 :max 100 :orientation "h"
:onchange "wpctl set-volume @DEFAULT_AUDIO_SOURCE@ {}%"))))
(defwindow volume
:monitor 0
:geometry (geometry :x "-16px" :y "8px" :width "280px" :anchor "top right")
:stacking "overlay"
:exclusive false
:focusable false
(volume-popup))
'';
xdg.configFile."eww/eww.scss".text = ''
@import "themes/theme";
.volume-popup {
background: $bg;
border: 2px solid $border;
padding: 16px;
}
.slider-row {
padding: 4px 0;
}
.mute-btn {
background: transparent;
color: $fg;
font-size: 18px;
min-width: 28px;
min-height: 28px;
padding: 0;
border: none;
&:hover { color: $accent; }
}
.vol-slider {
min-width: 200px;
min-height: 8px;
trough {
background: $bgAlt;
min-height: 4px;
border-radius: 2px;
}
highlight {
background: $accent;
border-radius: 2px;
}
slider {
background: $fg;
min-width: 12px;
min-height: 12px;
border-radius: 6px;
margin: -4px 0;
}
}
'';
xdg.configFile."eww/themes/midnight.scss".text = mkEwwTheme config.palettes.midnight;
xdg.configFile."eww/themes/daylight.scss".text = mkEwwTheme config.palettes.daylight;
xdg.configFile."fuzzel/fuzzel.ini".text = ''
include=${config.xdg.configHome}/fuzzel/themes/theme.ini
[main]
font=SF Pro Display:size=12,Symbols Nerd Font:size=12
prompt=""
width=50
lines=10
horizontal-pad=24
vertical-pad=20
inner-pad=12
line-height=24
letter-spacing=0
icons-enabled=yes
icon-theme=Papirus
image-size-ratio=0.5
layer=overlay
anchor=center
match-mode=fzf
dpi-aware=auto
hide-before-typing=no
match-counter=no
[border]
width=2
radius=0
selection-radius=0
[dmenu]
mode=text
exit-immediately-if-empty=no
[key-bindings]
custom-1=Control+r
'';
xdg.configFile."fuzzel/themes/midnight.ini".text = mkFuzzelTheme config.palettes.midnight;
xdg.configFile."fuzzel/themes/daylight.ini".text = mkFuzzelTheme config.palettes.daylight;
systemd.user.services.waybar = lib.mkIf config.programs.waybar.enable {
Unit.Description = lib.mkForce "Waybar (masked)";
Install.WantedBy = lib.mkForce [ ];
};
}