This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

1{ 2 pkgs, 3 lib, 4 ... 5}: 6let 7 8 # eww configuration 9 ewwYuck = pkgs.writeText "eww.yuck" '' 10 ; Magic variables (built-in, no polling needed) 11 ; EWW_TIME provides current time 12 ; EWW_CPU provides {cores: [...], avg: X} 13 ; EWW_RAM provides {used_mem_perc: X, available_mem: Y, total_mem: Z, ...} 14 15 ; Custom variables with polling 16 (defpoll cpu-freq :interval "2s" :initial 0 "${lib.getExe cpuFreqScript}") 17 (defpoll cpu-load :interval "2s" :initial "0.00" "cut -d' ' -f1 /proc/loadavg") 18 (defpoll temperature :interval "2s" :initial '{"temp": 0, "fan": 0}' "${lib.getExe tempScript}") 19 (defpoll battery :interval "10s" :initial '{"capacity": "100", "icon": "󰁹", "watts": "0", "time_remaining": "--", "status": "Full"}' "${lib.getExe batteryScript}") 20 (defpoll volume :interval "1s" :initial '{"level": "0", "icon": "󰕿"}' "${lib.getExe volumeScript}") 21 (defpoll network :interval "5s" :initial "󰖪 ..." "${lib.getExe networkScript}") 22 (defpoll brightness :interval "1s" :initial 100 "${lib.getExe brightnessScript}") 23 (defpoll idle-inhibited :interval "0.5s" :initial "false" "${lib.getExe idleStatusScript}") 24 (deflisten workspaces :initial '[]' "${lib.getExe workspacesScript}") 25 26 ; Widget definitions 27 (defwidget bar [] 28 (centerbox :orientation "h" 29 (left) 30 (center) 31 (right))) 32 33 (defwidget left [] 34 (box :class "left" :orientation "h" :space-evenly false :halign "start" 35 (workspaces-widget))) 36 37 (defwidget center [] 38 (box :class "center" :orientation "h" :space-evenly false :halign "center" 39 (label :text {formattime(EWW_TIME, "%H:%M")}))) 40 41 (defwidget right [] 42 (box :class "right" :orientation "h" :space-evenly false :halign "end" :spacing 8 43 (network-widget) 44 (volume-widget) 45 (graph-widget :value {EWW_CPU.avg} :icon "󰍛" :class "cpu" :unit "%" :tooltip "Load: ''${cpu-load} | ''${cpu-freq} MHz") 46 (graph-widget :value {EWW_RAM.used_mem_perc} :icon "󰘚" :class "memory" :unit "%") 47 (graph-widget :value {temperature.temp} :icon "󰔏" :class "temp" :unit "°" :tooltip "Fan: ''${temperature.fan} RPM") 48 (brightness-widget) 49 (battery-widget) 50 (idle-inhibitor) 51 (label :class "date" :text {formattime(EWW_TIME, "%Y-%m-%d")}) 52 (systray :class "systray" :icon-size 18 :spacing 4))) 53 54 (defwidget workspaces-widget [] 55 (box :class "workspaces" :orientation "h" :space-evenly false :spacing 0 56 (for ws in workspaces 57 (button :class {ws.urgent ? "workspace urgent" : (ws.focused ? "workspace focused" : "workspace")} 58 :onclick "swaymsg workspace ''${ws.name}" 59 {ws.name})))) 60 61 (defwidget graph-widget [value icon class unit ?tooltip] 62 (box :class "graph-module ''${class}" :orientation "h" :space-evenly false :spacing 4 63 :tooltip {tooltip ?: ""} 64 (graph :class "graph" 65 :value value 66 :thickness 3 67 :time-range "60s" 68 :min 0 69 :max 100 70 :dynamic true 71 :line-style "round" 72 :width 75) 73 (label :text "''${round(value, 0)}''${unit} ''${icon}"))) 74 75 (defwidget battery-widget [] 76 (box :class "battery" :orientation "h" :space-evenly false 77 :tooltip "''${battery.watts}W | ''${battery.time_remaining} ''${battery.status == 'Charging' ? 'to full' : 'remaining'}" 78 (label :text "''${battery.capacity}% ''${battery.icon}"))) 79 80 (defwidget volume-widget [] 81 (box :class "volume" :orientation "h" :space-evenly false 82 (label :text "''${volume.level}% ''${volume.icon}"))) 83 84 (defwidget network-widget [] 85 (box :class "network" :orientation "h" :space-evenly false 86 (label :text "''${network}"))) 87 88 (defwidget brightness-widget [] 89 (button :class "brightness" :onclick "/run/current-system/sw/bin/switch-theme toggle" 90 (label :text "''${brightness}% 󰃟"))) 91 92 (defwidget idle-inhibitor [] 93 (button :class {idle-inhibited == "true" ? "idle-inhibitor active" : "idle-inhibitor"} 94 :onclick "${lib.getExe idleToggleScript}" 95 {idle-inhibited == "true" ? "󰅶" : "󰾪"})) 96 97 ; Window definition - screen name passed as argument 98 (defwindow bar [screen] 99 :monitor screen 100 :geometry (geometry :x "0%" 101 :y "0%" 102 :width "100%" 103 :height "36px" 104 :anchor "top center") 105 :stacking "fg" 106 :exclusive true 107 :focusable false 108 (bar)) 109 ''; 110 111 ewwScss = pkgs.writeText "eww.scss" '' 112 * { 113 all: unset; 114 font-family: "Iosevka Nerd Font", monospace; 115 font-size: 14px; 116 } 117 118 window { 119 background-color: #1a1b26; 120 color: #c0caf5; 121 } 122 123 .left, .center, .right { 124 margin: 4px 8px; 125 } 126 127 .workspaces { 128 button { 129 padding: 0 4px; 130 margin: 0 1px; 131 border-radius: 2px; 132 min-width: 24px; 133 background-color: #24283b; 134 &.focused { 135 background-color: #7aa2f7; 136 color: #1a1b26; 137 } 138 &.urgent { 139 background-color: #f7768e; 140 color: #1a1b26; 141 } 142 &:hover { 143 background-color: #414868; 144 } 145 } 146 } 147 148 .graph-module { 149 padding: 0 8px; 150 margin: 0 2px; 151 border-radius: 4px; 152 &.cpu { 153 background-color: #9ece6a; 154 color: #1a1b26; 155 } 156 &.memory { 157 background-color: #7dcfff; 158 color: #1a1b26; 159 } 160 &.temp { 161 background-color: #ff9e64; 162 color: #1a1b26; 163 } 164 } 165 166 .graph { 167 min-height: 20px; 168 } 169 170 .battery, .volume, .network, .date, .brightness { 171 padding: 0 8px; 172 margin: 0 2px; 173 border-radius: 4px; 174 background-color: #24283b; 175 } 176 177 .idle-inhibitor { 178 padding: 0 8px; 179 margin: 0 2px; 180 border-radius: 4px; 181 background-color: #24283b; 182 &.active { 183 background-color: #bb9af7; 184 color: #1a1b26; 185 } 186 &:hover { 187 background-color: #414868; 188 } 189 } 190 191 .systray { 192 padding: 0 8px; 193 margin: 0 2px; 194 } 195 196 tooltip { 197 background-color: #1a1b26; 198 color: #c0caf5; 199 border: 1px solid #414868; 200 border-radius: 4px; 201 padding: 4px 8px; 202 } 203 ''; 204 205 cpuFreqScript = pkgs.writeShellApplication { 206 name = "eww-cpu-freq"; 207 runtimeInputs = [ ]; 208 text = '' 209 # Get average CPU frequency in MHz 210 freq=0 211 count=0 212 for f in /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq; do 213 if [ -f "$f" ]; then 214 val=$(cat "$f") 215 freq=$((freq + val)) 216 count=$((count + 1)) 217 fi 218 done 219 if [ "$count" -gt 0 ]; then 220 freq=$((freq / count / 1000)) 221 fi 222 echo "$freq" 223 ''; 224 }; 225 226 tempScript = pkgs.writeShellApplication { 227 name = "eww-temp"; 228 runtimeInputs = [ pkgs.jq ]; 229 text = '' 230 temp_raw=$(cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null || echo 0) 231 temp=$((temp_raw / 1000)) 232 233 # Find gpdfan hwmon for fan speed 234 fan=0 235 for hwmon in /sys/class/hwmon/hwmon*; do 236 if [ "$(cat "$hwmon/name" 2>/dev/null)" = "gpdfan" ]; then 237 fan=$(cat "$hwmon/fan1_input" 2>/dev/null || echo 0) 238 break 239 fi 240 done 241 242 jq -n --argjson temp "$temp" --argjson fan "$fan" '{temp: $temp, fan: $fan}' 243 ''; 244 }; 245 246 batteryScript = pkgs.writeShellApplication { 247 name = "eww-battery"; 248 runtimeInputs = [ 249 pkgs.jq 250 pkgs.bc 251 ]; 252 text = '' 253 # Find battery device dynamically 254 bat_path="" 255 for p in /sys/class/power_supply/BAT* /sys/class/power_supply/BATT*; do 256 if [ -f "$p/capacity" ]; then 257 bat_path="$p" 258 break 259 fi 260 done 261 262 if [ -z "$bat_path" ]; then 263 echo '{"capacity": "N/A", "icon": "󰂃", "watts": "0", "time_remaining": "N/A"}' 264 exit 0 265 fi 266 267 capacity=$(cat "$bat_path/capacity" 2>/dev/null || echo 100) 268 status=$(cat "$bat_path/status" 2>/dev/null || echo "Full") 269 power_now=$(cat "$bat_path/power_now" 2>/dev/null || echo 0) 270 energy_now=$(cat "$bat_path/energy_now" 2>/dev/null || echo 0) 271 energy_full=$(cat "$bat_path/energy_full" 2>/dev/null || echo 0) 272 273 # Convert microwatts to watts 274 watts=$(echo "scale=1; $power_now / 1000000" | bc) 275 276 # Calculate time remaining 277 if [ "$power_now" -gt 0 ]; then 278 if [ "$status" = "Charging" ]; then 279 # Time to full 280 remaining_energy=$((energy_full - energy_now)) 281 hours=$(echo "scale=2; $remaining_energy / $power_now" | bc) 282 else 283 # Time to empty 284 hours=$(echo "scale=2; $energy_now / $power_now" | bc) 285 fi 286 # Convert to hours:minutes 287 h=$(echo "$hours" | cut -d. -f1) 288 m=$(echo "scale=0; ($hours - $h) * 60 / 1" | bc) 289 time_remaining="''${h}h ''${m}m" 290 else 291 time_remaining="--" 292 fi 293 294 if [ "$status" = "Charging" ]; then 295 icon="󰂄" 296 elif [ "$status" = "Full" ]; then 297 icon="󰁹" 298 elif [ "$capacity" -gt 80 ]; then 299 icon="󰂂" 300 elif [ "$capacity" -gt 60 ]; then 301 icon="󰂀" 302 elif [ "$capacity" -gt 40 ]; then 303 icon="󰁾" 304 elif [ "$capacity" -gt 20 ]; then 305 icon="󰁼" 306 else 307 icon="󰁺" 308 fi 309 jq -n --arg cap "$capacity" --arg icon "$icon" --arg watts "$watts" --arg time "$time_remaining" --arg status "$status" \ 310 '{capacity: $cap, icon: $icon, watts: $watts, time_remaining: $time, status: $status}' 311 ''; 312 }; 313 314 volumeScript = pkgs.writeShellApplication { 315 name = "eww-volume"; 316 runtimeInputs = [ 317 pkgs.pulseaudio 318 pkgs.jq 319 pkgs.gnugrep 320 ]; 321 text = '' 322 vol=$(pactl get-sink-volume @DEFAULT_SINK@ | grep -oP '\d+%' | head -1 | tr -d '%') 323 muted=$(pactl get-sink-mute @DEFAULT_SINK@ | grep -oP '(yes|no)') 324 if [ "$muted" = "yes" ]; then 325 icon="󰖁" 326 elif [ "$vol" -gt 50 ]; then 327 icon="󰕾" 328 elif [ "$vol" -gt 0 ]; then 329 icon="󰖀" 330 else 331 icon="󰕿" 332 fi 333 jq -n --arg level "$vol" --arg icon "$icon" '{level: $level, icon: $icon}' 334 ''; 335 }; 336 337 networkScript = pkgs.writeShellApplication { 338 name = "eww-network"; 339 runtimeInputs = [ pkgs.networkmanager ]; 340 text = '' 341 connection=$(nmcli -t -f NAME,TYPE,DEVICE connection show --active 2>/dev/null | head -1) 342 if [ -z "$connection" ]; then 343 echo "󰖪 Disconnected" 344 else 345 name=$(echo "$connection" | cut -d: -f1) 346 type=$(echo "$connection" | cut -d: -f2) 347 if [ "$type" = "802-11-wireless" ]; then 348 echo "󰖩 $name" 349 else 350 echo "󰈀 $name" 351 fi 352 fi 353 ''; 354 }; 355 356 workspacesScript = pkgs.writeShellApplication { 357 name = "sway-workspaces"; 358 runtimeInputs = [ 359 pkgs.sway 360 pkgs.jq 361 ]; 362 text = '' 363 # Output initial state 364 swaymsg -t get_workspaces | jq -c '[.[] | {name: .name, focused: .focused, urgent: .urgent}]' 365 366 # Subscribe to workspace events and output updated state 367 swaymsg -t subscribe -m '["workspace"]' | while read -r _; do 368 swaymsg -t get_workspaces | jq -c '[.[] | {name: .name, focused: .focused, urgent: .urgent}]' 369 done 370 ''; 371 }; 372 373 brightnessScript = pkgs.writeShellApplication { 374 name = "eww-brightness"; 375 runtimeInputs = [ pkgs.brightnessctl ]; 376 text = '' 377 brightnessctl -m | cut -d, -f4 | tr -d '%' 378 ''; 379 }; 380 381 idleToggleScript = pkgs.writeShellApplication { 382 name = "eww-idle-toggle"; 383 runtimeInputs = [ pkgs.systemd ]; 384 text = '' 385 if systemctl --user is-active --quiet sway-idle-inhibit.service; then 386 systemctl --user stop sway-idle-inhibit.service 387 else 388 systemctl --user start sway-idle-inhibit.service 389 fi 390 ''; 391 }; 392 393 idleStatusScript = pkgs.writeShellApplication { 394 name = "eww-idle-status"; 395 runtimeInputs = [ pkgs.systemd ]; 396 text = '' 397 if systemctl --user is-active --quiet sway-idle-inhibit.service; then 398 echo "true" 399 else 400 echo "false" 401 fi 402 ''; 403 }; 404 405 ewwConfigDir = pkgs.runCommand "eww-config" { } '' 406 mkdir -p $out 407 cp ${ewwYuck} $out/eww.yuck 408 cp ${ewwScss} $out/eww.scss 409 ''; 410 411in 412{ 413 # eww daemon 414 systemd.user.services.eww = { 415 description = "ElKowars Wacky Widgets daemon"; 416 partOf = [ "sway-session.target" ]; 417 wantedBy = [ "sway-session.target" ]; 418 path = [ 419 pkgs.bash 420 pkgs.sway 421 ]; 422 environment.EWW_CONFIG = "${ewwConfigDir}"; 423 serviceConfig = { 424 Type = "simple"; 425 ExecStart = "${lib.getExe pkgs.eww} daemon --config ${ewwConfigDir} --no-daemonize"; 426 ExecStop = "${lib.getExe pkgs.eww} --config ${ewwConfigDir} kill"; 427 Restart = "on-failure"; 428 RestartSec = 1; 429 }; 430 }; 431 432 # Idle inhibit service - toggled by eww button 433 systemd.user.services.sway-idle-inhibit = { 434 description = "Inhibit idle/sleep"; 435 serviceConfig = { 436 Type = "simple"; 437 ExecStart = "${pkgs.systemd}/bin/systemd-inhibit --what=idle:sleep --who=eww --why='User requested' --mode=block sleep infinity"; 438 }; 439 }; 440 441 # eww bar per output - template unit using output name (e.g. eww-bar@eDP-1.service) 442 systemd.user.services."eww-bar@" = { 443 description = "Eww bar on output %i"; 444 partOf = [ "sway-session.target" ]; 445 after = [ "eww.service" ]; 446 requires = [ "eww.service" ]; 447 serviceConfig = { 448 Type = "oneshot"; 449 RemainAfterExit = true; 450 ExecStart = "${pkgs.eww}/bin/eww --config ${ewwConfigDir} open bar --id bar-%i --arg screen=%i"; 451 ExecStop = "${pkgs.eww}/bin/eww --config ${ewwConfigDir} close bar-%i"; 452 }; 453 }; 454 455 # Watches for output changes and starts/stops eww-bar@ instances 456 systemd.user.services.eww-output-watcher = { 457 description = "Watch for output changes and manage eww bars"; 458 partOf = [ "sway-session.target" ]; 459 wantedBy = [ "sway-session.target" ]; 460 after = [ "eww.service" ]; 461 requires = [ "eww.service" ]; 462 serviceConfig = { 463 Type = "simple"; 464 ExecStart = lib.getExe ( 465 pkgs.writeShellApplication { 466 name = "eww-output-watcher"; 467 runtimeInputs = [ 468 pkgs.sway 469 pkgs.jq 470 pkgs.systemd 471 pkgs.gawk 472 pkgs.gnused 473 pkgs.gnugrep 474 ]; 475 text = '' 476 update_bars() { 477 # Get current outputs 478 outputs=$(swaymsg -t get_outputs | jq -r '.[].name') 479 480 # Start bars for connected outputs 481 for output in $outputs; do 482 systemctl --user start "eww-bar@$output.service" || true 483 done 484 485 # Stop bars for disconnected outputs 486 running=$(systemctl --user list-units --type=service --state=running --plain --no-legend 'eww-bar@*' | awk '{print $1}' | sed 's/eww-bar@\(.*\)\.service/\1/') 487 for output in $running; do 488 if ! echo "$outputs" | grep -qx "$output"; then 489 systemctl --user stop "eww-bar@$output.service" || true 490 fi 491 done 492 } 493 494 update_bars 495 swaymsg -t subscribe -m '["output"]' | while read -r _; do 496 sleep 0.5 497 update_bars 498 done 499 ''; 500 } 501 ); 502 Restart = "on-failure"; 503 RestartSec = 1; 504 }; 505 }; 506}