3PLW22JL4I4ZXBYEVFQWVREK4PU3UW2A2OFHUQKN7KWLGB3O4UTAC
KAREPDTUFLSHROK53ZCZ24VWRRTDNLBICMI7K3UUJMN4X4P4OL4QC
DWUHKA7TDWTRK3PZGFJR6ZXJ7Q2GO4R24SMYFL7LS2VW2KNV3I6QC
RPGBGVR75LTVQ7OV2WIJV5PTTF5D7QDJSJI2MPEY4HPHCWWIY3WQC
EG4DUC4NREW5NTDKCRTDRFTPVDAYCGT4TGU2Y3XC43HHTYQPHA3AC
H3GYCX27WTONFBIJN7UIV26LJSCENQJC4XXIOEYINXJLEDGKYJJQC
EVVP46IQ4U6JEDWVJ4D6KT77ABOMY7XKIO2ITX4DHJDRTGTYCUTQC
JOK7DA5XBOK2MIVR7ORTSAEMQ6U5642I537IGUYBX67DPFF5QJ3QC
BYZGRIFZY253OFK442SR6X743U4LAX4NLFBQ4XPKXEDPPXNNUM4AC
TXA66OLAOYNGEDRBC4SKWDZPLBZXUU7WHDIAYN4TXGEJR6WIJOIQC
F26DJCZ7RLGRDZSYACB6MGMNTW7L5FE332EBYEIJTBRK7QJ6ZIYAC
NNUJU4U7H6QBAKSVR2N5NGQDODJFAJ6UWFU6N656WU2ORRG7RQRQC
(defwidget system-menu []
(box
:class "system-menu-box"
:space-evenly false
:orientation "v"
(box
:class "top-row"
:space-evenly false
(label
:class "time"
:text "${time.hour}:${time.minute}")
(box
:class "date-box"
:space-evenly false
(label
:class "date"
:text {time.date})
(label
:class "day"
:text {time.day})))
(centerbox
:class "system-row"
(box
:class "wifi-box"
:space-evenly false
:orientation "v"
(box
:class "element icon ${net.class}"
:space-evenly false
(button
:class "wifi-button"
:onclick "scripts/net toggle"
{net.icon})
(label
:class "separator"
:text "│")
(button
:class "wifi-arrow-btn"
:onclick "eww close system-menu && gnome-control-center &"
""))
(label
:text {net.essid}
:xalign 0.5
:limit-width 15))
(box
:class "bluetooth-box"
:space-evenly false
:orientation "v"
(box
:class "element icon ${bluetooth.class}"
:space-evenly false
(button
:class "bluetooth-button"
:onclick "scripts/bluetooth toggle"
{bluetooth.icon})
(label
:class "separator"
:text "│")
(button
:class "bluetooth-arrow-btn"
:onclick "eww close system-menu && blueberry"
""))
(label
:text {bluetooth.text}
:xalign 0.5
:tooltip "${bluetooth.text} ${bluetooth.battery}"
:limit-width 15))
(box
:class "airplane-box"
:space-evenly false
:orientation "v"
(box
:class "element"
(button
:class "airplane-button"
:onclick "scripts/airplane toggle"
airplane))
(label
:text "Airplane Mode"
:xalign 0.5
:limit-width 16)))
(box
:class "sliders"
:orientation "v"
(box
:class "volume-slider-box"
:space-evenly false
(button
:class "volume-icon icon"
:onclick "scripts/volume mute SINK"
{volume.icon})
(scale
:class "volume-bar"
:value {volume.percent}
:tooltip "volume on ${volume.percent}%"
:onchange "scripts/volume setvol SINK {}"))
(box
:class "brightness-slider-box"
:space-evenly false
(button
:class "brightness-slider-icon icon"
{brightness.icon})
(scale
:class "brightness-slider"
:value {brightness.percent}
:marks true
:onchange "light -S {}")))
(box
:class "system-info-box"
; cpu
(box
:class "sys-box"
:space-evenly false
:halign "start"
(circular-progress
:value "${EWW_CPU.avg}"
:class "sys-cpu"
:thickness 3
(label
:text ""
:class "sys-icon-cpu icon"))
(box
:orientation "v"
:vexpand false
(label
:text "cpu"
:halign "start"
:class "sys-text-cpu")
(label
:text "${round(EWW_CPU.avg,2)}%"
:halign "start"
:class "sys-text-sub")
(label
:text "${EWW_CPU.cores[0].freq} MHz"
:halign "start"
:class "sys-text-sub")))
; memory
(box
:class "sys-box"
:space-evenly false
:halign "end"
(circular-progress
:value {memory.percent}
:class "sys-mem"
:thickness 3
(label
:text ""
:class "sys-icon-mem icon"))
(box
:orientation "v"
(label
:text "memory"
:halign "start"
:class "sys-text-mem")
(label
:text "${memory.used} | ${memory.total}"
:halign "start"
:class "sys-text-sub"))))
(centerbox
:class "bottom-row"
(box
:class "battery-box"
:space-evenly false
:halign "start"
(label
:class "battery-icon icon"
:style "color: ${battery.color}"
:text {battery.icon})
(label
:text {EWW_BATTERY["BAT0"].capacity})
(label
:class "battery-status"
:text {battery.status})
(label
:class "battery-wattage"
:text {battery.wattage}))
(label)
(box
:space-evenly false
:halign "end"
(button
:halign "end"
:class "power-button icon"
:onclick "wlogout -p layer-shell &"
"")))))
(defwindow system-menu
:stacking "fg"
:monitor 0
:geometry (geometry
:x "0"
:y "0"
:width "0%"
:height "0%"
:anchor "right top")
(system-menu))
(defwidget osd-part [icon value class]
(box
:class "osd-part osd-${class}"
:orientation "v"
:space-evenly false
(scale
:flipped true
:orientation "v"
:value value)
(label
:text icon)))
(defwidget osd []
(box
:class "osd-container"
:space-evenly false
(revealer
:reveal {osd-brightness}
:transition "slideright"
(osd-part
:class "brightness"
:icon {brightness.icon}
:value {brightness.percent}))
(revealer
:reveal {osd-volume}
:transition "slideright"
(osd-part
:class "volume"
:icon {volume.icon}
:value {volume.percent})))
)
(defwindow osd
:stacking "fg"
:focusable false
:monitor 0
:geometry (geometry
:x "1%"
:y "0%"
:width "0%"
:height "0%"
:anchor "center left")
(osd))
(defwidget music []
(box
:class "music-window"
:space-evenly false
:style "background-color: ${music.color1}; border: 1px solid ${music.color2};"
(box
:class "music-cover-art"
:style "background-image: url(\"${music.cover}\");")
(box
:orientation "v"
:class "music-box"
(label
:class "music-title"
:wrap true
:text {music.title})
(label
:class "music-artist"
:wrap true
:text {music.artist})
(centerbox
:halign "center"
:class "music-button-box icon"
(button :class "music-button" :onclick "playerctl previous" "")
(button :class "music-button" :onclick "playerctl play-pause" {music.status})
(button :class "music-button" :onclick "playerctl next" ""))
(box
:orientation "v"
(box
(label
:xalign 0
:class "music-time"
:text {music.position_time})
(label
:xalign 1
:class "music-time"
:text {music.length}))
(box
:class "music-bar"
(scale
; doesn't work, looking for other ways
; :style "background: linear-gradient(to right, ${music.color1}, ${music.color2});"
:onchange "playerctl position `bc <<< \"{} * $(playerctl metadata mpris:length) / 1000000 / 100\"`"
:value {music.position}))))))
(defwindow music
:stacking "fg"
:focusable false
:monitor 0
:geometry (geometry
:x "0%"
:y "0%"
:width "0%"
:height "0%"
:anchor "top center")
(music))
(defwidget calendar-win []
(box
:class "calendar-win"
(calendar)))
(defwindow calendar
:monitor 0
:geometry (geometry
:x "0%"
:y "0%"
:anchor "top right"
:width "0px"
:height "0px")
(calendar-win))
#!/usr/bin/env bash
# define colors
# red peach green blue
colors=("#f38ba8" "#fab387" "#a6e3a1" "#89b4fa")
# pink yellow teal lavender
dimmed=("#f5c2e7" "#f9e2af" "#94e2d5" "#b4befe")
empty="#313244"
# get initial focused workspace
focusedws=$(hyprctl -j monitors | jaq -r '.[] | select(.focused == true) | .activeWorkspace.id')
declare -A o=([1]=0 [2]=0 [3]=0 [4]=0 [5]=0 [6]=0 [7]=0 [8]=0 [9]=0 [10]=0)
declare -A monitormap
declare -A workspaces
# set color for each workspace
status() {
if [ "${o[$1]}" -eq 1 ]; then
mon=${monitormap[${workspaces[$1]}]}
if [ "$focusedws" -eq "$1" ]; then
echo -n "${colors[$mon]}"
else
echo -n "${dimmed[$mon]}"
fi
else
echo -n "$empty"
fi
}
# handle workspace create/destroy
workspace_event() {
o[$1]=$2
while read -r k v; do workspaces[$k]="$v"; done < <(hyprctl -j workspaces | gojq -r '.[]|"\(.id) \(.monitor)"')
}
# handle monitor (dis)connects
monitor_event() {
while read -r k v; do monitormap["$k"]=$v; done < <(hyprctl -j monitors | gojq -r '.[]|"\(.name) \(.id) "')
}
# get all apps titles in a workspace
applist() {
ws="$1"
apps=$(hyprctl -j clients | jaq -jr '.[] | select(.workspace.id == '"$ws"') | .title + "\\n"')
echo -En "${apps%"\n"}"
}
# generate the json for eww
generate() {
echo -n '['
for i in {1..10}; do
echo -n ''"$([ "$i" -eq 1 ] || echo ,)" '{ "number": "'"$i"'", "color": "'"$(status "$i")"'" }' #, "tooltip": "'$(applist "$i")'" }'
done
echo ']'
}
# setup
# add monitors
monitor_event
# add workspaces
while read -r k v; do workspaces[$k]="$v"; done < <(hyprctl -j workspaces | gojq -r '.[]|"\(.id) \(.monitor)"')
# check occupied workspaces
for num in "${!workspaces[@]}"; do
o[$num]=1
done
# generate initial widget
generate
# main loop
socat -u UNIX-CONNECT:/tmp/hypr/"$HYPRLAND_INSTANCE_SIGNATURE"/.socket2.sock - | rg --line-buffered "workspace|mon(itor)?" | while read -r line; do
case ${line%>>*} in
"workspace")
focusedws=${line#*>>}
;;
"focusedmon")
focusedws=${line#*,}
;;
"createworkspace")
workspace_event "${line#*>>}" 1
;;
"destroyworkspace")
workspace_event "${line#*>>}" 0
;;
"monitor"*)
monitor_event
;;
esac
generate
done
#!/usr/bin/env bash
volicons=("" "" "")
if [ ! "$XDG_CACHE_HOME" ]; then
XDG_CACHE_HOME="/home/mihai/.local/cache"
fi
date="$XDG_CACHE_HOME/eww/osd_vol.date"
vol() {
wpctl get-volume @DEFAULT_AUDIO_"$1"@ | awk '{print int($2*100)}'
}
ismuted() {
wpctl get-volume @DEFAULT_AUDIO_"$1"@ | rg -i muted
echo $?
}
setvol() {
wpctl set-volume @DEFAULT_AUDIO_"$1"@ "$(awk -v n="$2" 'BEGIN{print (n / 100)}')"
}
setmute() {
wpctl set-mute @DEFAULT_AUDIO_"$1"@ toggle
}
osd() {
if [ ! -f "$date" ]; then
mkdir -p "$XDG_CACHE_HOME/eww"
fi
date +%s > "$date"
}
osd_handler() {
lock=0
rundate=0
if [ ! -f "$date" ]; then
mkdir -p "$XDG_CACHE_HOME/eww"
echo 0 > "$date"
fi
while true; do
# get dates
rundate=$(cat "$date")
currentdate=$(date +%s)
# handle showing
if [ "$rundate" = "$currentdate" ] && [ "$lock" -eq 0 ]; then
eww open osd
eww update osd-volume=true
lock=1
elif [ "$((currentdate - rundate))" = "2" ] && [ "$lock" -eq 1 ]; then
eww update osd-volume=false
lock=0
if [ "$(eww get osd-volume)" = "false" ] && [ "$(eww get osd-brightness)" = "false" ]; then
eww close osd
fi
fi
sleep 0.1
done
eww close osd
}
if [ "$1" = "mute" ]; then
if [ "$2" != "SOURCE" ] && [ "$2" != "SINK" ]; then
echo "Can only mute SINK or SOURCE"; exit 1
fi
setmute "$2"
elif [ "$1" = "setvol" ]; then
if [ "$2" != "SOURCE" ] && [ "$2" != "SINK" ]; then
echo "Can only set volume for SINK or SOURCE"; exit 1
elif [ "$3" -lt 1 ] || [ "$3" -gt 100 ]; then
echo "Volume must be between 1 and 100"; exit 1
fi
setvol "$2" "$3"
elif [ "$1" = "osd" ]; then
osd
else
# initial values
lvl=$(awk -v n="$(vol "SINK")" 'BEGIN{print int(n/34)}')
ismuted=$(ismuted "SINK")
if [ "$ismuted" = 1 ]; then
icon="${volicons[$lvl]}"
else
icon=""
fi
echo '{ "icon": "'"$icon"'", "percent": "'"$(vol "SINK")"'", "microphone": "'"$(vol "SOURCE")"'" }'
osd_handler &
# event loop
pactl subscribe | rg --line-buffered "change" | while read -r _; do
lvl=$(awk -v n="$(vol "SINK")" 'BEGIN{print int(n/34)}')
ismuted=$(ismuted "SINK")
if [ "$ismuted" = 1 ]; then
icon="${volicons[$lvl]}"
else
icon=""
fi
echo '{ "icon": "'"$icon"'", "percent": "'"$(vol "SINK")"'", "microphone": "'"$(vol "SOURCE")"'" }'
done
fi
#!/usr/bin/env bash
toggle() {
status=$(rfkill -J | jaq -r '.rfkilldevices[] | select(.type == "wlan") | .soft' | head -1)
if [ "$status" = "unblocked" ]; then
rfkill block wlan
else
rfkill unblock wlan
fi
}
if [ "$1" = "toggle" ]; then
toggle
else
while true; do
status=$(nmcli -f state g| tail -1)
wifistatus=$(nmcli -t -f in-use,ssid,signal dev wifi | rg '\*' | sed 's/\"/\\"/g')
signal=$(echo "$wifistatus" | awk -F: '{print $3}')
essid=$(echo "$wifistatus" | awk -F: '{print $2}')
icons=("" "" "" "" "")
if [ "$status" = "disconnected" ] ; then
icon=""
color="#988ba2"
class=""
else
level=$(awk -v n="$signal" 'BEGIN{print int((n-1)/20)}')
if [ "$level" -gt 4 ]; then
level=4
fi
icon=${icons[$level]}
color="#cba6f7"
class="wifi-connected"
fi
echo '{ "essid": "'"$essid"'", "icon": "'"$icon"'", "color": "'"$color"'", "class": "'"$class"'" }'
sleep 3
done
fi
#!/usr/bin/env bash
get_status() {
s=$1
if [ "$s" = "Playing" ]; then
echo ""
else
echo ""
fi
}
get_length_sec() {
len=$1
if [ -z "$len" ]; then
echo 0
else
bc <<< "$len / 1000000"
fi
}
get_length_time() {
len=$1
if [ -n "$len" ]; then
len=$(bc <<< "$len / 1000000 + 1")
date -d@"$len" +%M:%S
else
echo ""
fi
}
get_position() {
pos=$1
len=$2
if [ -n "$pos" ]; then
bc -l <<< "$pos / $len * 100"
else
echo 0
fi
}
get_position_time() {
pos=$1
len=$2
if [ -n "$pos" ]; then
date -d@"$(bc <<< "$pos / 1000000")" +%M:%S
else
echo ""
fi
}
get_cover() {
mkdir -p "$XDG_CACHE_HOME/eww_covers"
cd "$XDG_CACHE_HOME/eww_covers" || exit
IMGPATH="$XDG_CACHE_HOME/eww_covers/cover_art.png"
COVER_URL="$1"
if [[ "$COVER_URL" = https* ]]; then
if [ ! -e "$XDG_CACHE_HOME/eww_covers/$(basename "$COVER_URL")" ]; then
wget -N "$COVER_URL" -o /dev/null
fi
rm "$IMGPATH"
ln -s "$(basename "$COVER_URL")" "$IMGPATH"
IMG="${IMGPATH}"
elif [ "$COVER_URL" = "" ]; then
IMG=""
else
IMG="$COVER_URL"
fi
echo "$IMG"
}
sanitize() {
echo "$1" | sed 's/"/\"/g'
}
prevCover=''
playerctl -F metadata -f '{{title}}\{{artist}}\{{status}}\{{position}}\{{mpris:length}}\{{mpris:artUrl}}' 2>/dev/null | while IFS="$(printf '\')" read -r title artist status position len cover; do
if [[ "$cover" != "$prevCover" ]]; then
COVER=$(get_cover "$cover")
if [ "$COVER" != "" ]; then
cols=$(convert "$COVER" -colors 2 -format "%c" histogram:info: | awk '{print $3}')
color1=$(echo "$cols" | head -1)
color2=$(echo "$cols" | tail -1)
else
color1="#1e1e2e"
color2="#28283d"
fi
fi
jaq --null-input -r -c \
--arg artist "$(sanitize "$artist")" \
--arg title "$(sanitize "$title")" \
--arg status "$(get_status "$status")" \
--arg pos "$(get_position "$position" "$len")" \
--arg pos_time "$(get_position_time "$position" "$len")" \
--arg length "$(get_length_time "$len")" \
--arg cover "$COVER" \
--arg color1 "$color1" \
--arg color2 "$color2" \
'{"artist": $artist, "title": $title, "status": $status, "position": $pos, "position_time": $pos_time, "length": $length, "cover": $cover, "color1": $color1, "color2": $color2}'
prevCover=$cover
done
#!/usr/bin/env bash
while true; do
# human-readable
freeH=$(free -h --si | rg "Mem:")
# non-human-readable
freeN=$(free --mega | rg "Mem:")
total="$(echo "$freeH" | awk '{ print $2 }')"
used="$(echo "$freeH" | awk '{ print $3 }')"
t="$(echo "$freeN" | awk '{ print $2 }')"
u="$(echo "$freeN" | awk '{ print $3 }')"
free=$(printf '%.1fG' "$(bc -l <<< "($t - $u) / 1000")")
perc=$(printf '%.1f' "$(free -m | rg Mem | awk '{print ($3/$2)*100}')")
echo '{ "total": "'"$total"'", "used": "'"$used"'", "free": "'"$free"'", "percent": '"$perc"' }'
sleep 3
done
#!/usr/bin/env bash
icons=("" "" "")
if [ ! "$XDG_CACHE_HOME" ]; then
XDG_CACHE_HOME="/home/mihai/.local/cache"
fi
date="$XDG_CACHE_HOME/eww/osd_brightness.date"
osd() {
if [ ! -f "$date" ]; then
mkdir -p "$XDG_CACHE_HOME/eww"
fi
date +%s > "$date"
}
osd_handler() {
lock=0
rundate=0
if [ ! -f "$date" ]; then
mkdir -p "$XDG_CACHE_HOME/eww"
echo 0 > "$date"
fi
while true; do
# get dates
rundate=$(cat "$date")
currentdate=$(date +%s)
# handle showing
if [ "$rundate" = "$currentdate" ] && [ "$lock" -eq 0 ]; then
eww open osd
eww update osd-brightness=true
lock=1
elif [ "$((currentdate - rundate))" = "2" ] && [ "$lock" -eq 1 ]; then
eww update osd-brightness=false
lock=0
if [ "$(eww get osd-brightness)" = "false" ] && [ "$(eww get osd-volume)" = "false" ]; then
eww close osd
fi
fi
sleep 0.1
done
eww close osd
}
if [ "$1" = "osd" ]; then
osd
else
osd_handler &
# initial
icon=${icons[$(awk -v n="$(light)" 'BEGIN{print int(n/34)}')]}
echo '{ "percent": '"$(light)"', "icon": "'"$icon"'" }'
udevadm monitor | rg --line-buffered "backlight" | while read -r _; do
icon="${icons[$(awk -v n="$(light)" 'BEGIN{print int(n/34)}')]}"
echo '{ "percent": '"$(light)"', "icon": "'"$icon"'" }'
done
fi
#!/usr/bin/env bash
toggle() {
status=$(rfkill -J | jaq -r '.rfkilldevices[] | select(.type == "bluetooth") | .soft' | head -1)
if [ "$status" = "unblocked" ]; then
rfkill block bluetooth
else
rfkill unblock bluetooth
fi
}
if [ "$1" = "toggle" ]; then
toggle
else
while true; do
powered=$(bluetoothctl show | rg Powered | cut -f 2- -d ' ')
status=$(bluetoothctl info)
name=$(echo "$status" | rg Name | cut -f 2- -d ' ')
mac=$(echo "$status" | head -1 | awk '{print $2}' | tr ':' '_')
if [[ "$(echo "$status" | rg Percentage)" != "" ]]; then
battery="$(upower -i /org/freedesktop/UPower/devices/headset_dev_"$mac" | rg percentage | awk '{print $2}' | cut -f 1 -d '%')%"
else
battery=""
fi
if [ "$powered" = "yes" ]; then
if [ "$status" != "Missing device address argument" ]; then
text="$name"
icon=""
color="#89b4fa"
class="bt-connected"
else
icon=""
text="Disconnected"
color="#45475a"
class=""
fi
else
icon=""
text="Bluetooth off"
color="#45475a"
class=""
fi
echo '{ "icon": "'"$icon"'", "battery": "'"$battery"'", "text": "'"$text"'", "color": "'"$color"'", "class": "'"$class"'" }'
sleep 3
done
fi
#!/usr/bin/env bash
icons=("" "" "" "" "" "" "" "")
num_icons=$(bc <<< "100 / ${#icons[@]}")
geticon() {
level=$(awk -v n="$CAPACITY" -v c="$num_icons" 'BEGIN{print int(n/c-1)}')
if [[ "$level" -lt 0 ]]; then
level=0
fi
echo "${icons[$level]}"
}
status() {
if [ "$STATE" = "Charging" ]; then
echo -n "charging"
if [ "$RATE" -gt 0 ]; then
echo ", $(gettime) left"
else
echo ""
fi
elif [ "$STATE" = "Discharging" ]; then
echo "$(gettime)h left"
else
echo "fully charged"
fi
}
color() {
if [ "$CAPACITY" -le 20 ]; then
echo '#f38ba8'
else
echo '#a6e3a1'
fi
}
wattage() {
microwatts=1000000
echo "$(bc -l <<< "scale=1; $RATE / $microwatts") W"
}
gettime() {
FULL=$(cat /sys/class/power_supply/BAT0/energy_full)
NOW=$(cat /sys/class/power_supply/BAT0/energy_now)
if [ "$RATE" -gt 0 ]; then
if [ "$STATE" = "Discharging" ]; then
EX="$NOW / $RATE"
else
EX="($FULL - $NOW) / $RATE"
fi
date -u -d@"$(bc -l <<< "$EX * 3600")" +%H:%M
fi
}
while true; do
RATE=$(cat /sys/class/power_supply/BAT0/power_now)
CAPACITY=$(cat /sys/class/power_supply/BAT0/capacity)
STATE=$(cat /sys/class/power_supply/BAT0/status)
echo '{ "icon": "'"$(geticon)"'", "percent": '"$CAPACITY"', "wattage": "'"$(wattage)"'", "status": "'"$(status)"'", "color": "'"$(color)"'" }'
sleep 3
done
#!/bin/sh
icon() {
if [ "$STATUS" = "no" ]; then
echo ""
else
echo ""
fi
}
toggle() {
if [ "$STATUS" = "no" ]; then
rfkill block all
notify-send --urgency=normal -i airplane-mode-symbolic "Airplane Mode" "Airplane mode has been turned on!"
else
rfkill unblock all
notify-send --urgency=normal -i airplane-mode-disabled-symbolic "Airplane Mode" "Airplane mode has been turned off!"
fi
}
if [ "$1" = "toggle" ]; then
toggle
else
while true; do
STATUS="$(rfkill list | sed -n 2p | awk '{print $3}')"
icon
sleep 3;
done
fi
(defwidget workspaces [o]
(eventbox
:onscroll "echo {} | sed -e \"s/up/-1/g\" -e \"s/down/+1/g\" | xargs hyprctl dispatch workspace"
(box
:class "module workspaces"
:orientation o
:spacing 5
(for ws in workspace
(button
:onclick "hyprctl dispatch workspace ${ws.number}"
:class "ws icon"
:style "color: ${ws.color};"
; :tooltip {ws.tooltip}
"●")))))
(defwidget volume-module []
(box
:class "module icon"
(eventbox
:onscroll "echo {} | sed -e 's/up/-/g' -e 's/down/+/g' | xargs -I% wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.005%"
:onclick "pavucontrol &"
:onrightclick "scripts/volume mute SINK"
(label
:class "vol-icon"
:tooltip "volume ${volume.percent}%"
:text {volume.icon}))))
(defvar bright_reveal false)
(defvar bt_rev false)
(defvar music_reveal false)
(defvar net_rev false)
(defvar time_rev false)
(defvar vol_reveal false)
(defvar osd-brightness false)
(defvar osd-volume false)
(defpoll time :interval "5s" `date +'{"date": "%d/%m", "hour": "%H", "minute": "%M", "day": "%A"}'`)
(deflisten airplane "scripts/airplane")
(deflisten battery "scripts/battery")
(deflisten bluetooth "scripts/bluetooth")
(deflisten brightness "scripts/brightness")
(deflisten memory "scripts/memory")
(deflisten music "scripts/music")
(deflisten net "scripts/net")
(deflisten volume "scripts/volume")
(deflisten workspace "scripts/workspaces")
(defwidget sys []
(box
:class "module"
:space-evenly false
:spacing 5
(circular-progress
:value "${EWW_CPU.avg}"
:class "cpubar"
:thickness 3
(button
:tooltip "using ${round(EWW_CPU.avg,0)}% cpu"
:onclick "${EWW_CMD} open --toggle system-menu"
(label :class "icon-text" :text "")))
(circular-progress
:value {memory.percent}
:class "membar"
:thickness 3
(button
:tooltip "using ${round(memory.percent,0)}% ram"
:onclick "${EWW_CMD} open --toggle system-menu"
(label :class "icon-text" :text "")))
(circular-progress
:value "${EWW_BATTERY["BAT0"].capacity}"
:class "batbar"
:style "color: ${battery.color};"
:thickness 3
(button
:tooltip "battery on ${EWW_BATTERY["BAT0"].capacity}%"
:onclick "${EWW_CMD} open --toggle system-menu"
(label :class "icon-text" :text "")))))
(defwidget net []
(button
:class "module icon"
:onclick "gnome-control-center &"
:tooltip {net.essid}
:style "color: ${net.color};"
{net.icon}))
(defwidget music-module []
(eventbox
:onhover "${EWW_CMD} update music_reveal=true"
:onhoverlost "${EWW_CMD} update music_reveal=false"
(box
:class "module"
:space-evenly false
(box
:class "song-cover-art"
:style "background-image: url(\"${music.cover}\");")
(button
:class "module"
:onclick "${EWW_CMD} open --toggle music"
{music.title})
(revealer
:transition "slideright"
:reveal music_reveal
:duration "350ms"
(box
:class "icon"
(button :class "song-button" :onclick "playerctl previous" "")
(button :class "song-button" :onclick "playerctl play-pause" {music.status})
(button :class "song-button" :onclick "playerctl next" ""))))))
(defvar date_rev false)
(defwidget clock_module []
(eventbox
:onhover "${EWW_CMD} update date_rev=true"
:onhoverlost "${EWW_CMD} update date_rev=false"
(overlay
:class "module"
(box
:space-evenly false
(label
:text {time.hour}
:class "hour")
(label
:text ":")
(label
:text {time.minute}
:class "minute"))
(revealer
:reveal date_rev
(button
:class "date"
:onclick "${EWW_CMD} open --toggle calendar"
{time.date})))))
(defwidget bright []
(box
:class "module"
(eventbox
:onscroll "echo {} | sed -e 's/up/-U 1/g' -e 's/down/-A 1/g' | xargs light"
(label
:text {brightness.icon}
:class "bright-icon icon"
:tooltip "brightness ${round(brightness.percent, 0)}%"))))
(defwidget bluetooth []
(button
:class "module-bt module icon"
:onclick "blueberry"
:onrightclick "scripts/bluetooth toggle"
:tooltip "${bluetooth.text} ${bluetooth.battery}"
:style "color: ${bluetooth.color};"
{bluetooth.icon}))
(include "./modules/bluetooth.yuck")
(include "./modules/bright.yuck")
(include "./modules/clock.yuck")
(include "./modules/music.yuck")
(include "./modules/net.yuck")
(include "./modules/sys.yuck")
(include "./modules/variables.yuck")
(include "./modules/volume.yuck")
(include "./modules/workspaces.yuck")
(include "./windows/calendar.yuck")
(include "./windows/music.yuck")
(include "./windows/osd.yuck")
(include "./windows/system-menu.yuck")
(defwidget left []
(box
:space-evenly false
:halign "start"
(workspaces :o "h")))
(defwidget right []
(box
:space-evenly false
:halign "end"
(net)
(sys)
(clock_module)))
(defwidget center []
(box
:space-evenly false
:halign "center"
))
(defwidget bar_1 []
(centerbox
:class "bar"
:orientation "v"
(box :valign "start" (workspaces :o "v"))
(box)
(box)))
(defwidget bar_2 []
(centerbox
:class "bar"
(left)
(center)
(right)))
(defwindow bar_1
:monitor 0
:geometry (geometry
:x "0%"
:y "0%"
:width "32px"
:height "100%"
:anchor "center left")
:stacking "fg"
:exclusive true
(bar_1))
(defwindow bar_2
:monitor 1
:geometry (geometry :x "0%"
:y "0%"
:width "100%"
:height "32px"
:anchor "top center")
:stacking "fg"
:exclusive true
(bar_2))
@import 'css/colors';
@mixin rounding {
border-radius: 16px;
}
@mixin window {
border: 1px solid $border;
box-shadow: 0 2px 3px $shadow;
margin: 5px 5px 10px;
@include rounding;
}
* {
all: unset;
font-family: "Jost *", Roboto, sans-serif;
transition: 200ms ease;
}
@import 'css/calendar';
@import 'css/music';
@import 'css/osd';
@import 'css/sidebar';
@import 'css/system';
@import 'css/volume';
.bar {
background-color: $bg;
color: $fg;
label {
font-size: 1.2rem;
}
}
tooltip {
background: $bg;
border: 1px solid $border;
border-radius: 8px;
label {
font-size: 1rem;
}
}
.icon,
.icon label { font-family: Material Symbols Outlined; }
.module { margin: 0 5px; }
.hour {
font-weight: bold;
padding-left: 5px;
}
.minute {
padding-right: .7rem;
}
.date {
background: $bg;
color: $flamingo;
label {
font-size: 1.2rem;
}
}
.bright-icon { color: $yellow; }
.module-bt { font-size: 1.2rem; }
scale trough {
background-color: $bg1;
border-radius: 24px;
margin: 0 1rem;
min-height: 10px;
min-width: 70px;
}
.workspaces { margin-left: 10px; }
{
config,
pkgs,
inputs,
lib,
...
}: let
dependencies = with pkgs; [
config.wayland.windowManager.hyprland.package
cfg.package
bash
bc
blueberry
bluez
coreutils
dbus
dunst
findutils
gawk
gnome.gnome-control-center
gnused
gojq
imagemagick
jaq
light
networkmanager
pavucontrol
playerctl
procps
pulseaudio
ripgrep
socat
udev
upower
util-linux
wget
wireplumber
wlogout
];
reload_script = pkgs.writeShellScript "reload_eww" ''
windows=$(eww windows | rg '\*' | tr -d '*')
systemctl --user restart eww.service
echo $windows | while read -r w; do
eww open $w
done
'';
cfg = config.programs.eww-hyprland;
in {
options.programs.eww-hyprland = {
enable = lib.mkEnableOption "eww Hyprland config";
package = lib.mkOption {
type = with lib.types; nullOr package;
default = pkgs.eww-wayland;
defaultText = lib.literalExpression "pkgs.eww-wayland";
description = "Eww package to use.";
};
autoReload = lib.mkOption {
type = lib.types.bool;
default = false;
defaultText = lib.literalExpression "false";
description = "Whether to restart the eww daemon and windows on change.";
};
colors = lib.mkOption {
type = with lib.types; nullOr lines;
default = null;
defaultText = lib.literalExpression "null";
description = ''
SCSS file with colors defined in the same way as Catppuccin colors are,
to be used by eww.
Defaults to Catppuccin Mocha.
'';
};
};
config = lib.mkIf cfg.enable {
home.packages = [cfg.package];
# remove nix files
xdg.configFile."eww" = {
source = lib.cleanSourceWith {
filter = name: _type: let
baseName = baseNameOf (toString name);
in
!(lib.hasSuffix ".nix" baseName) && !(baseName == "_colors.scss");
src = lib.cleanSource ./.;
};
# links each file individually, which lets us insert the colors file separately
recursive = true;
onChange =
if cfg.autoReload
then reload_script.outPath
else "";
};
# colors file
xdg.configFile."eww/css/_colors.scss".text =
if cfg.colors != null
then cfg.colors
else (builtins.readFile ./css/_colors.scss);
systemd.user.services.eww = {
Unit = {
Description = "Eww Daemon";
# not yet implemented
# PartOf = ["tray.target"];
PartOf = ["graphical-session.target"];
};
Service = {
Environment = "PATH=/run/wrappers/bin:${lib.makeBinPath dependencies}";
ExecStart = "${cfg.package}/bin/eww daemon --no-daemonize";
Restart = "on-failure";
};
Install.WantedBy = ["graphical-session.target"];
};
};
}
.vol-icon { color: $green; }
.volbar highlight {
background-image: linear-gradient(to right, $teal, $sky);
border-radius: 10px;
}
.membar {
color: $peach;
}
.cpubar {
color: $blue;
}
.batbar {
color: $green;
}
.membar,
.cpubar,
.batbar {
background-color: $bg1;
}
.iconmem {
color: $peach;
}
.iconcpu {
color: $blue;
}
.icon-text {
font-size: 3rem;
padding: .7rem;
}
.sys-text-sub {
color: $text;
}
.sys-text-mem,
.sys-text-cpu {
font-size: 1rem;
font-weight: bold;
}
.sys-icon-mem,
.sys-icon-cpu {
font-size: 1.5rem;
margin: 1.5rem;
}
.system-info-box {
@include rounding;
background-color: $surface0;
margin: .5rem 1rem;
padding: .5rem;
}
.sys-mem,
.sys-cpu {
background-color: $bg;
}
.sys-icon-mem,
.sys-text-mem,
.sys-mem {
color: $peach;
}
.sys-icon-cpu,
.sys-text-cpu,
.sys-cpu {
color: $blue;
}
.sys-box {
margin: .3em;
box { margin-left: 1rem; }
}
.system-menu-box {
@include window;
background-color: $bg;
border: 1px solid $border;
color: $text;
}
.separator {
font-size: 1rem;
}
.top-row {
margin: 1rem 1.5rem 0;
.time { font-size: 2rem; }
.date-box {
margin: 0 1rem;
label { font-size: 1.1rem; }
.date {
background: unset;
margin: 0 .5rem 0 0;
padding: 0;
}
}
}
.system-row {
margin: .5rem .7rem;
.airplane-box button {
padding: 1rem 3rem;
}
label {
font-size: 1rem;
margin: 0 .1rem;
}
}
.element {
@include rounding;
background-color: $surface0;
margin: .3rem;
button {
@include rounding;
padding: 1rem;
label {
font-size: 1.5rem;
}
&:hover {
background-color: $overlay0;
}
}
}
.sliders {
@include rounding;
background-color: $surface0;
margin: .5rem 1rem;
padding: .6rem 1rem;
scale {
margin-right: -1rem;
min-width: 21.5rem;
trough { margin-right: 0; }
}
box { margin: .2rem 0; }
label { font-size: 1.2rem; }
}
.volume-slider-box,
.brightness-slider-box {
trough { background-color: $base; }
}
.volume-bar highlight {
@include rounding;
background-image: linear-gradient(to right, $teal, $sky);
}
.brightness-slider-box scale highlight {
@include rounding;
background-image: linear-gradient(to right, $yellow, $peach);
}
.bottom-row {
margin: .5rem 1rem;
.battery-icon { font-size: 2rem; }
.battery-wattage { color: $mauve; }
.battery-status {
color: $subtext0;
margin: 0 .5rem;
}
button {
background-color: $surface0;
border-radius: 50%;
margin-bottom: .1rem;
padding: 0 .5rem;
label { font-size: 1.5rem; }
&:hover { background-color: $overlay0; }
}
}
.bt-connected {
background-color: $blue;
color: $crust;
button:hover { background-color: #7ba2e1; }
}
.wifi-connected {
background-color: $mauve;
color: $crust;
button:hover { background-color: #b695de; }
}
.osd-part {
@include window;
background: $base;
margin: 0 .5rem .5rem;
label {
color: $text;
font-size: 2rem;
margin: 0 .1rem 0 .2rem;
}
scale {
margin: -.2rem 0;
}
trough {
@include rounding;
background-color: $bg1;
margin: 1rem 0 .5rem;
min-height: 10rem;
min-width: .7rem;
highlight {
@include rounding;
min-width: 0;
}
}
}
.osd-volume highlight {
background-image: linear-gradient(to top, $teal, $sky);
}
.osd-brightness highlight {
background-image: linear-gradient(to top, $yellow, $peach);
}
.song-cover-art {
@include rounding;
background-position: center;
background-size: cover;
margin: 4px 5px 4px 0;
min-height: 24px;
min-width: 24px;
}
.music-window {
@include window;
background-color: $bg;
border: 1px solid $border;
color: $fg;
}
.music-cover-art {
background-position: center;
background-size: cover;
border-radius: 8px;
margin: 1em;
min-height: 170px;
min-width: 170px;
}
.music-box {
margin: 1rem 1rem 1rem 0;
}
.music-title {
font-size: 1.1rem;
font-weight: bold;
}
.music-artist {
color: $subtext1;
}
.music-button label {
color: $subtext1;
font-size: 2rem;
}
.music-time {
color: $subtext1;
margin: 0 1rem;
}
.music-bar scale {
highlight {
background-image: linear-gradient(to right, $teal, $sky);
border-radius: 24px;
}
trough {
background-color: $bg1;
border-radius: 24px;
margin-top: 0;
min-height: 10px;
min-width: 170px;
}
}
$rosewater: #f5e0dc;
$flamingo: #f2cdcd;
$pink: #f5c2e7;
$mauve: #cba6f7;
$red: #f38ba8;
$maroon: #eba0ac;
$peach: #fab387;
$yellow: #f9e2af;
$green: #a6e3a1;
$teal: #94e2d5;
$sky: #89dceb;
$sapphire: #74c7ec;
$blue: #89b4fa;
$lavender: #b4befe;
$text: #cdd6f4;
$subtext1: #bac2de;
$subtext0: #a6adc8;
$overlay2: #9399b2;
$overlay1: #7f849c;
$overlay0: #6c7086;
$surface2: #585b70;
$surface1: #45475a;
$surface0: #313244;
$base: #1e1e2e;
$mantle: #181825;
$crust: #11111b;
$fg: $text;
$bg: $base;
$bg1: $surface0;
$border: #28283d;
$shadow: $crust;
.calendar-win {
@include window;
background-color: $bg;
border: 1px solid $border;
color: $fg;
padding: .2em;
}
calendar {
padding: 5px;
:selected {
color: $mauve;
}
.header {
color: $subtext1;
}
.highlight {
color: $maroon;
font-weight: bold;
}
.button {
color: $sapphire;
}
:indeterminate {
color: $overlay0;
}
}
# Eww configuration
This configuration aims to provide a fully working shell replacement for
compositors/window managers. Features constantly get added and existing ones
get improved.
## 🗃️ Components
The same daemon runs multiple windows which interact with each other:
### bar
![bar](https://user-images.githubusercontent.com/36706276/216402839-0f8ec9b0-dc4b-4cb8-9834-db59b61db97f.png)
### music window
![music](https://user-images.githubusercontent.com/36706276/192146077-f8da4691-9a0c-487f-9805-3fd4d55551e9.gif)
### calendar
![calendar](https://user-images.githubusercontent.com/36706276/204923748-f5c7db3a-5000-40cf-ba41-cd2d5f14146a.png)
### system info
![system](https://user-images.githubusercontent.com/36706276/216403137-a3231c60-976a-4e5d-85c0-899679ab0a92.png)
## ❔ Usage
### Home Manager
If you use Home Manager, installing is as simple as adding my flake to your
inputs, passing `inputs` to `extraSpecialArgs` and importing the relevant
module:
```nix
{inputs, ...}: {
imports = [inputs.fufexan.homeManagerModules.eww-hyprland];
programs.eww-hyprland = {
enable = true;
# default package
package = pkgs.eww-wayland;
# if you want to change colors
colors = builtins.readFile ./macchiato.scss;
# set to true to reload on change
autoReload = false;
};
}
```
Make sure to also add the fonts listed below.
### Other distros
To quickly install this config, grab all the files in this directory and put
them in `~/.config/eww`. Then run `eww daemon` and `eww open bar`. Enjoy!
Dependencies:
- Icon fonts: `material-symbols-outline` (any variation can be used as long as you change the `font-family` property of `.icon`)
- Text font: [Jost](https://fonts.google.com/specimen/Jost)
- Script deps: everything in `default.nix`'s `dependencies` list.
## 🎨 Theme
The theme colors can be changed in `css/_colors.scss`. Currently the theme used
is [Catppuccin Mocha](https://github.com/catppuccin/catppuccin).
}: {
}: let
# TODO: Move to hyprland module
ewwDeps = with pkgs; [
bash
bc
blueberry
bluez
coreutils
dbus
dunst
findutils
gawk
gnused
gojq
imagemagick
jaq
light
networkmanager
pavucontrol
procps
pulseaudio
ripgrep
socat
udev
upower
util-linux
wget
wireplumber
wlogout
];
# TODO: Move to desktop module
bwDeps = with pkgs; [
wtype
pinentry.curses
rbw
rofi-rbw
];
in {
interactiveShellInit = lib.optionalString pkgs.stdenv.isDarwin ''
gpgconf --launch gpg-agent
set -gx GPG_TTY (tty)
set -gx SSH_AUTH_SOCK (gpgconf --list-dirs agent-ssh-socket)
interactiveShellInit = let
gpgAgentInit = lib.optionalString pkgs.stdenv.isDarwin ''
# Initialize GPG agent
gpgconf --launch gpg-agent
set -gx GPG_TTY (tty)
set -gx SSH_AUTH_SOCK (gpgconf --list-dirs agent-ssh-socket)
'';
in ''
# Initialize hooks
__cd_hook > /dev/null
${gpgAgentInit}
Rough roadmap:
- [x] Basic setup with ease of adding new hosts
- [ ] Modularize the codebase
- [ ] Make it possible to update user configuration without root access, even on machines which use NixOS Home Manager module
- [ ] Make the theme consistent throughout the system
- [ ] Hopefully make it so that others can use some parts of this config without having to copy or fork it :)
Rough roadmap (☑️ - Done, ☒ - In progress, ☐ - TODO):
- ☑️ Basic setup with ease of adding new hosts
- ☒ Modularize the codebase
- ☐ Make it possible to update user configuration without root access, even on machines which use NixOS Home Manager module
- ☐ Make the theme consistent throughout the system
- ☐ Hopefully make it so that others can use some parts of this config without having to copy or fork it :)
- `wsl` - Obsolete, configuration for when I used Nix on WSL
- `virtualBox` - Obsolete, VirtualBox config I tried before installing NixOS
- `hijiri` - (Currently) My main laptop and development machine. At some point will be split into -macos and -linux variants. (When Asahi gets more mature)
- `miyagi` - My computer station at work.
## Credits
Eww config for `miyagi` is temporarily borrowed from https://github.com/fufexan/dotfiles. At some point it'll just be heavily inspired instead.