summaryrefslogtreecommitdiff
path: root/arch/eww/.config
diff options
context:
space:
mode:
authorsteven-na <noreply.github@stvnc.dev>2026-04-26 11:58:32 -0700
committersteven-na <noreply.github@stvnc.dev>2026-04-26 11:58:32 -0700
commit4b309f0a5555b5fed5a65ee425dcb6da15421f5b (patch)
treeb923f4f76df6205ee3099d9bf8072209ff9e8256 /arch/eww/.config
Initial commit.
Diffstat (limited to 'arch/eww/.config')
-rw-r--r--arch/eww/.config/eww/eww.scss112
-rw-r--r--arch/eww/.config/eww/eww.yuck150
-rwxr-xr-xarch/eww/.config/eww/scripts/active-workspace4
-rwxr-xr-xarch/eww/.config/eww/scripts/active-ws2
-rwxr-xr-xarch/eww/.config/eww/scripts/cpu7
-rwxr-xr-xarch/eww/.config/eww/scripts/hypr-socket24
-rwxr-xr-xarch/eww/.config/eww/scripts/netspeed9
-rwxr-xr-xarch/eww/.config/eww/scripts/powermenu7
-rwxr-xr-xarch/eww/.config/eww/scripts/workspaces6
9 files changed, 321 insertions, 0 deletions
diff --git a/arch/eww/.config/eww/eww.scss b/arch/eww/.config/eww/eww.scss
new file mode 100644
index 0000000..7fd3240
--- /dev/null
+++ b/arch/eww/.config/eww/eww.scss
@@ -0,0 +1,112 @@
+// Colors generated by wallust — see ~/.config/wallust/templates/eww-colors.scss
+// Fallback values used until wallust has run at least once
+$bg: #1e2121;
+$fg: #cdd6f4;
+$accent: #89b4fa;
+$surface: #313244;
+$muted: #6c7086;
+$warn: #f38ba8;
+
+@import "/home/blake/.cache/wallust/eww-colors";
+
+* {
+ font-family: "JetBrainsMono Nerd Font";
+ font-size: 12px;
+ all: unset;
+}
+
+.bar {
+ background-color: transparentize($bg, 0.4);
+ color: $fg;
+ border: none;
+ box-shadow: none;
+}
+
+// ── Left ───────────────────────────────────────────────────────────────────
+
+.left {
+ padding: 0 8px;
+}
+
+.workspaces {
+ padding: 0 2px;
+}
+
+.ws {
+ color: $muted;
+ padding: 0 5px;
+}
+
+.ws.active {
+ color: $accent;
+ font-weight: bold;
+}
+
+// ── Center ─────────────────────────────────────────────────────────────────
+
+.center {
+ padding: 0 8px;
+}
+
+.date {
+ color: $muted;
+}
+
+.clock {
+ color: $fg;
+ font-weight: bold;
+}
+
+.music-icon {
+ color: $accent;
+}
+
+.song {
+ color: $muted;
+}
+
+// ── Right ──────────────────────────────────────────────────────────────────
+
+.right {
+ padding: 0 8px;
+}
+
+.stat {
+ padding: 0 1px;
+}
+
+.stat-label {
+ color: $muted;
+ padding-right: 3px;
+}
+
+.stat-val {
+ color: $fg;
+}
+
+.stat-val.net {
+ color: $muted;
+}
+
+.stat-val.warn {
+ color: $warn;
+}
+
+.stat-btn {
+ all: unset;
+}
+
+.stat-btn:hover .stat-label,
+.stat-btn:hover .stat-val {
+ color: $accent;
+}
+
+.power-btn {
+ color: $muted;
+ padding: 0 4px;
+ margin-left: 2px;
+}
+
+.power-btn:hover {
+ color: $warn;
+}
diff --git a/arch/eww/.config/eww/eww.yuck b/arch/eww/.config/eww/eww.yuck
new file mode 100644
index 0000000..8574e1f
--- /dev/null
+++ b/arch/eww/.config/eww/eww.yuck
@@ -0,0 +1,150 @@
+;; ── Listeners (Hyprland IPC) ──────────────────────────────────────────────
+;; hyprland-workspaces emits JSON arrays with {id, name, windows, isActive}
+;; on every workspace change — single source of truth for list + active state.
+
+(deflisten workspaces :initial "[]"
+ `scripts/workspaces`)
+
+;; ── Polls ──────────────────────────────────────────────────────────────────
+
+(defpoll clock :interval "1s"
+ `date +"%-H:%M"`)
+
+(defpoll date-str :interval "60s"
+ `date +"%a %b %d"`)
+
+;; Show the PLAYING source; fall back to any paused one
+(defpoll player-status :interval "2s"
+ `playerctl -a status 2>/dev/null | grep -q "^Playing" && echo "Playing" || playerctl status 2>/dev/null || echo "Stopped"`)
+
+(defpoll player-song :interval "2s"
+ `playerctl -a metadata --format "{{status}}||{{ artist }} - {{ title }}" 2>/dev/null | grep "^Playing||" | head -1 | sed 's/^Playing||//' || playerctl metadata --format "{{ artist }} - {{ title }}" 2>/dev/null || echo ""`)
+
+(defpoll battery-level :interval "30s" :initial "100"
+ `cat /sys/class/power_supply/BAT0/capacity 2>/dev/null || echo "100"`)
+
+(defpoll battery-status :interval "10s" :initial "Unknown"
+ `cat /sys/class/power_supply/BAT0/status 2>/dev/null || echo "Unknown"`)
+
+(defpoll cpu :interval "3s"
+ `scripts/cpu`)
+
+(defpoll ram :interval "5s"
+ `free | awk '/^Mem/{printf "%d", $3/$2*100}'`)
+
+(defpoll disk :interval "60s"
+ `df / | awk 'NR==2{printf "%d", $3/$2*100}'`)
+
+(defpoll volume :interval "1s"
+ `pamixer --get-volume 2>/dev/null || echo "0"`)
+
+;; Use sysfs operstate — works regardless of network manager backend
+(defpoll wifi-iface :interval "5s"
+ `IFACE=$(ls /sys/class/net | grep -m1 '^wl'); [ -n "$IFACE" ] && [ "$(cat /sys/class/net/$IFACE/operstate 2>/dev/null)" = "up" ] && echo "$IFACE" || echo "disconnected"`)
+
+(defpoll brightness :interval "2s"
+ `brightnessctl -m 2>/dev/null | awk -F, '{print int($4)}' || echo "100"`)
+
+(defpoll netspeed :interval "3s"
+ `scripts/netspeed`)
+
+;; ── Left: Workspaces + Music ───────────────────────────────────────────────
+
+(defwidget workspaces []
+ (box :class "workspaces" :space-evenly false :spacing 5
+ (for ws in workspaces
+ (button
+ :class {ws.active ? "ws active" : "ws"}
+ :onclick "hyprctl dispatch workspace ${ws.id}"
+ {ws.id}))))
+
+(defwidget music []
+ (eventbox
+ :onclick "playerctl play-pause"
+ (box :class "music"
+ :space-evenly false :spacing 5
+ :visible {player-status != "Stopped" && player-status != ""}
+ (label :class "music-icon"
+ :text {player-status == "Playing" ? "▶" : "⏸"})
+ (label :class "song" :text player-song :limit-width 30))))
+
+;; ── Center: Clock ──────────────────────────────────────────────────────────
+
+(defwidget center-section []
+ (box :class "center" :halign "center" :space-evenly false :spacing 8
+ (label :class "date" :text date-str)
+ (label :class "clock" :text clock)))
+
+;; ── Right: Stats ───────────────────────────────────────────────────────────
+
+(defwidget bright-stat []
+ (eventbox
+ :onscroll "[ '{}' = 'up' ] && brightnessctl set +5% || brightnessctl set 5%-"
+ (box :class "stat" :space-evenly false :spacing 3
+ (label :class "stat-label" :text "bright")
+ (label :class "stat-val" :text "${brightness}%"))))
+
+(defwidget audio-stat []
+ (eventbox
+ :onscroll "[ '{}' = 'up' ] && pamixer --increase 5 || pamixer --decrease 5"
+ (box :class "stat" :space-evenly false :spacing 3
+ (label :class "stat-label" :text "audio")
+ (label :class "stat-val" :text "${volume}%"))))
+
+(defwidget wifi-stat []
+ (eventbox
+ :onclick "ghostty -e nmtui &"
+ (box :class "stat" :space-evenly false :spacing 3
+ (label :class "stat-label" :text "wifi")
+ (label :class "stat-val" :text wifi-iface))))
+
+(defwidget cpu-stat []
+ (eventbox
+ :onclick "ghostty -e btop &"
+ (box :class "stat" :space-evenly false :spacing 3
+ (label :class "stat-label" :text "cpu")
+ (label :class "stat-val" :text "${cpu}%"))))
+
+(defwidget right-section []
+ (box :class "right" :halign "end" :space-evenly false :spacing 8
+ (bright-stat)
+ (audio-stat)
+ (label :class "stat-val net" :text netspeed)
+ (wifi-stat)
+ (stat :lbl "bat" :value "${battery-level}%"
+ :class {battery-level < 20 ? "warn" : ""})
+ (stat :lbl "strg" :value "${disk}%")
+ (stat :lbl "ram" :value "${ram}%")
+ (cpu-stat)
+ (button :class "power-btn" :onclick "scripts/powermenu &" "pwr")))
+
+;; Plain stat (non-interactive) ─────────────────────────────────────────────
+
+(defwidget stat [lbl value ?class]
+ (box :class "stat" :space-evenly false :spacing 3
+ (label :class "stat-label" :text lbl)
+ (label :class {class == "" ? "stat-val" : "stat-val ${class}"} :text value)))
+
+;; ── Bar ────────────────────────────────────────────────────────────────────
+
+(defwidget bar []
+ (centerbox :class "bar" :orientation "h"
+ (box :class "left" :halign "start" :space-evenly false :spacing 12
+ (workspaces)
+ (music))
+ (center-section)
+ (right-section)))
+
+(defwindow bar
+ :monitor 0
+ :windowtype "dock"
+ :geometry (geometry
+ :x "0%"
+ :y "0%"
+ :width "100%"
+ :height "24px"
+ :anchor "top center")
+ :stacking "fg"
+ :exclusive true
+ :focusable false
+ (bar))
diff --git a/arch/eww/.config/eww/scripts/active-workspace b/arch/eww/.config/eww/scripts/active-workspace
new file mode 100755
index 0000000..21795b9
--- /dev/null
+++ b/arch/eww/.config/eww/scripts/active-workspace
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+hyprctl activeworkspace -j | jq '.id'
+socat -u UNIX-CONNECT:"${XDG_RUNTIME_DIR}/hypr/${HYPRLAND_INSTANCE_SIGNATURE}/.socket2.sock" STDOUT \
+ | while read -r _; do hyprctl activeworkspace -j | jq '.id'; done
diff --git a/arch/eww/.config/eww/scripts/active-ws b/arch/eww/.config/eww/scripts/active-ws
new file mode 100755
index 0000000..c7b3f3e
--- /dev/null
+++ b/arch/eww/.config/eww/scripts/active-ws
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+hyprctl activeworkspace | awk '/^workspace/{print $3}'
diff --git a/arch/eww/.config/eww/scripts/cpu b/arch/eww/.config/eww/scripts/cpu
new file mode 100755
index 0000000..f06ae7b
--- /dev/null
+++ b/arch/eww/.config/eww/scripts/cpu
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+read -r _ u1 _ s1 id1 _ < /proc/stat
+sleep 1
+read -r _ u2 _ s2 id2 _ < /proc/stat
+used=$(( (u2+s2) - (u1+s1) ))
+total=$(( (u2+s2+id2) - (u1+s1+id1) ))
+printf "%d\n" $(( total > 0 ? 100 * used / total : 0 ))
diff --git a/arch/eww/.config/eww/scripts/hypr-socket b/arch/eww/.config/eww/scripts/hypr-socket
new file mode 100755
index 0000000..9012c17
--- /dev/null
+++ b/arch/eww/.config/eww/scripts/hypr-socket
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3
+"""Stream lines from Hyprland's socket2 IPC."""
+import socket, sys, os
+
+sock_path = (
+ os.environ.get("XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}")
+ + "/hypr/"
+ + os.environ["HYPRLAND_INSTANCE_SIGNATURE"]
+ + "/.socket2.sock"
+)
+
+s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+s.connect(sock_path)
+
+buf = b""
+while True:
+ chunk = s.recv(4096)
+ if not chunk:
+ break
+ buf += chunk
+ while b"\n" in buf:
+ line, buf = buf.split(b"\n", 1)
+ sys.stdout.write(line.decode(errors="replace") + "\n")
+ sys.stdout.flush()
diff --git a/arch/eww/.config/eww/scripts/netspeed b/arch/eww/.config/eww/scripts/netspeed
new file mode 100755
index 0000000..f5bb25b
--- /dev/null
+++ b/arch/eww/.config/eww/scripts/netspeed
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+IFACE=$(ip route show default 2>/dev/null | awk '{print $5; exit}')
+if [[ -z "$IFACE" ]]; then echo "↓0 ↑0 KB/s"; exit; fi
+rx1=$(cat /sys/class/net/"$IFACE"/statistics/rx_bytes 2>/dev/null || echo 0)
+tx1=$(cat /sys/class/net/"$IFACE"/statistics/tx_bytes 2>/dev/null || echo 0)
+sleep 1
+rx2=$(cat /sys/class/net/"$IFACE"/statistics/rx_bytes 2>/dev/null || echo 0)
+tx2=$(cat /sys/class/net/"$IFACE"/statistics/tx_bytes 2>/dev/null || echo 0)
+echo "↓$(( (rx2-rx1)/1024 )) ↑$(( (tx2-tx1)/1024 )) KB/s"
diff --git a/arch/eww/.config/eww/scripts/powermenu b/arch/eww/.config/eww/scripts/powermenu
new file mode 100755
index 0000000..e136674
--- /dev/null
+++ b/arch/eww/.config/eww/scripts/powermenu
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+chosen=$(printf "Suspend\nReboot\nShutdown" | bemenu -p " Power")
+case "$chosen" in
+ Suspend) systemctl suspend ;;
+ Reboot) systemctl reboot ;;
+ Shutdown) systemctl poweroff ;;
+esac
diff --git a/arch/eww/.config/eww/scripts/workspaces b/arch/eww/.config/eww/scripts/workspaces
new file mode 100755
index 0000000..28571a7
--- /dev/null
+++ b/arch/eww/.config/eww/scripts/workspaces
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+# hyprland-workspaces streams JSON arrays on every workspace change.
+# Filter out special/scratchpad workspaces (negative IDs).
+hyprland-workspaces eDP-1 | while IFS= read -r line; do
+ echo "$line" | jq -c '[.[] | select(.id > 0)]'
+done