(defvar draw false)

; window

(defwindow niri
   :monitor 0
   :namespace "niri"
   :stacking "fg"
   :exclusive false
   :geometry (geometry
      :height "100%"
      :width "26px"
      :anchor "left center"
      :x "0px"
      :y "0px"
   )
   (eventbox
      :onscroll "eww update draw=true"
      :onhoverlost "eww update draw=false"
      (box
         :orientation "horizontal"
         :space-evenly false
         (revealer
            :reveal draw
            :transition "slideright"
            :duration "150ms"
            (draw)
         )
         (bar)
      )
   )
)

; bar

(defwidget bar []
   (box
      :class "bar"
      :orientation "horizontal"
      (centerbox
         :orientation "vertical"
         (box
            :class "top"
            :orientation "vertical"
            :valign "start"
            :spacing 3
            :space-evenly false
            (box)
            (block :class "char" (char))
            (box)
            (block :class "tray" (tray))
         )
         (box
            :class "mid"
            :orientation "vertical"
            :valign "center"
            :spacing 3
            :space-evenly false
            (block :class "spaces" (spaces))
         )
         (box
            :class "bot"
            :orientation "vertical"
            :valign "end"
            :spacing 3
            :space-evenly false
            (box)
            (box :class "reco" (reco))
            (box)
            (box :class "idle" (idle))
            (box)
            (box :class "volu" (volu))
            (box)
            (box :class "date" (date))
         )
      )
   )
)

; draw

(defwidget draw []
   (box
      :class "draw"
      :space-evenly false
      (box
         :orientation "horizontal"
         :space-evenly false
         (centerbox
            :orientation "vertical"
            (box
               :class "top"
               :orientation "vertical"
               :valign "start"
               :spacing 3
               :space-evenly false
            ;  (block :class "charge" (charge) (charge-indicator))
               (block :class "network" (network) (network-indicator))
               (block :class "bluetooth" (bluetooth) (bluetooth-indicator))
            ;  (block :class "sound" (sound) (sound-indicator))
               (label
                  :class "player"
                  :style "padding: 0px 8px;"
                  :xalign 0
                  :show-truncated true
                  :wrap true
                  :limit-width 20
                  :text "${player.artist}\\n${player.album}\\n${player.title}\\n${player.status}"
               )
               (label
                  :class "notifications"
                  :style "padding: 0px 8px;"
                  :xalign 0
                  :show-truncated true
                  :wrap true
                  :limit-width 20
                  :text notifications
               )
            )
            (box
               :class "mid"
               :orientation "vertical"
               :valign "center"
               :spacing 3
               :space-evenly false
               (block
                  :class "mem"
                  (mem-indicator)
               ;  (mem)
               )
               (block
                  :class "cpu"
                  (cpu-indicator)
               ;  (cpu)
               )
               (block
                  :class "disk"
                  (disk-indicator)
               ;  (disk)
               )
            )
            (box
               :class "bot"
               :orientation "vertical"
               :valign "end"
               :spacing 3
               :space-evenly false
               (block :class "moon" (moon) (box))
               (label
                  :class "cal"
                  :style "padding: 0px 8px;"
                  :xalign 0
                  :show-truncated false
                  :unindent true
                  :markup cal
               )
            ;  (calendar
            ;     :class "calendar"
            ;     :show-heading false
            ;     :show-day-names false
            ;  )
            ;  (label
            ;     :unindent true
            ;     :class "clipboard"
            ;     :xalign 0
            ;     :show-truncated false
            ;     :text clipboard
            ;  )
            )
         )
      )
   )
)

; block

(defwidget block [?class]
   (box
      :class "block ${class ?: ''}"
      (box
         :orientation "horizontal"
         :space-evenly false
         (box
            :width 26
            (children :nth 0)
         )
         (box
            :hexpand true
            :halign "end"
            :orientation "vertical"
            (children :nth 1)
         )
      )
   )
)

(defwidget date []
   (box
      :orientation "vertical"
      (label :text {date.month})
      (label :text {date.day})
      (label :style "color: #fff;" :text {date.hour})
      (label :style "color: #fff;" :text {date.minute})
   )
)

(defwidget reco []
   (eventbox
      :onclick `pgrep -x wf-recorder > /dev/null && pkill -SIGINT wf-recorder || sh -c 'selection=$(slurp) && [ -n "$selection" ] && wf-recorder --audio=@DEFAULT_SINK@.monitor -g "$selection" -f "$HOME/shot/reco $(date +\"%Y-%m-%d %H-%M-%S\").mp4" > /dev/null 2>&1 &'`
      :cursor "pointer"
      (circular-progress
         :style "color: ${reco == 'true' ? '#cc6666' : '#444'};"
         :height 6
         :thickness 3
         :value 100
      )
   )
)

(defwidget sound-indicator []
   (asciibar
      :text {sound.muted ? "──" : sound.bluetooth ? "BT" : ""}
      :value {sound.volume}
   )
)

(defwidget sound []
   (eventbox
      :onclick "amixer set Master toggle"
      :cursor "pointer"
      (label
         :xalign 0
         :limit-width 20
         :show-truncated false
         :text "${sound.device}"
      )
   )
)

(defwidget charge-indicator []
   (asciibar
      :text "${EWW_BATTERY == '' ? '' : '░░'}"
      :value {EWW_BATTERY == '' ? 100 : EWW_BATTERY?.BAT0?.capacity ?: EWW_BATTERY?.BAT1?.capacity ?: EWW_BATTERY?.BAT?.capacity ?: 100}
      :style {styles.charge}
   )
)

(defwidget charge []
   (asciilabel
      :text "${EWW_BATTERY == '' ? '' : EWW_BATTERY?.BAT0?.status ?: EWW_BATTERY?.BAT1?.status ?: EWW_BATTERY?.BAT?.status ?: '?'}"
      :value {EWW_BATTERY == '' ? 100 : EWW_BATTERY?.BAT0?.capacity ?: EWW_BATTERY?.BAT1?.capacity ?: EWW_BATTERY?.BAT?.capacity ?: 100}
      :style {styles.charge}
   )
)

(defwidget cpu-indicator []
   (box
      :orientation "vertical"
      (for w in {EWW_CPU.cores}
         (asciibar
            :value {w.usage}
            :style {styles.cpu}
         )
      )
   )
)

(defwidget cpu []
   (box
      :orientation "vertical"
      (for w in {EWW_CPU.cores}
         (label :show-truncated false :xalign 0 :text "${w.freq}")
      )
   )
)

(defwidget mem-indicator []
   (asciibar
      :value {EWW_RAM.used_mem_perc}
      :style {styles.mem}
   )
)

(defwidget mem []
   (asciilabel
      :text "${round(EWW_RAM.used_mem / 1024 / 1024 / 1024, 3)} G"
      :value {EWW_RAM.used_mem_perc}
      :style {styles.mem}
   )
)

(defwidget disk-indicator []
   (asciibar
      :value {EWW_DISK["/"].used_perc}
      :style {styles.disk}
   )
)

(defwidget disk []
   (asciilabel
      :text "${round(EWW_DISK['/'].used / 1024 / 1024 / 1024, 0)} ${round(EWW_DISK['/'].free / 1024 / 1024 / 1024, 0)} G"
      :value {EWW_DISK['/'].used_perc}
      :style {styles.disk}
   )
)

(defwidget network-indicator []
   (box
      :orientation "vertical"
      :space-evenly false
      (box
         :visible {ethernet.ip != ""}
         :orientation "vertical"
         (asciibin
            :maxl {ethernet.quality / 8}
            :maxr {ethernet.quality / 8}
            :valuel {round(ethernet.up_speed / 1000 / 1000 / 8, 0) ?: 0}
            :valuer {round(ethernet.down_speed / 1000 / 1000 / 8, 0) ?: 0}
         )
         (label :text '  ')
         (label :text '  ')
      )
      (box
         :visible {wifi.ip != ""}
         :orientation "vertical"
         (asciibar
            :text {wifi.quality == 0 ? '  ' : wifi.ip == '' ? '──' : ''}
            :value {wifi.quality}
            :style {styles.wifi}
         )
         (label :text '  ')
         (label :text '  ')
      )
   )
)

(defwidget network []
   (box
      :style "padding: 0px 8px;"
      :orientation "vertical"
      (box
         :orientation "vertical"
         :space-evenly false
         :visible {ethernet.ip != ""}
         (label
            :xalign 0
            :show-truncated false
            :text "${ethernet.ip} ${ethernet.quality}M"
         )
         (label
            :xalign 0
            :show-truncated false
            :style "color: #666;"
            :text {ethernet.up_speed > 0 ? "▲ ${ethernet.up}" : ""}
         )
         (label
            :xalign 0
            :show-truncated false
            :style "color: #666;"
            :text {ethernet.down_speed > 0 ? "▼ ${ethernet.down}" : ""}
         )
      )
      (box
         :orientation "vertical"
         :space-evenly false
         :visible {wifi.ip != ""}
         (asciilabel
            :text "${wifi.ip} ${wifi.ssid}"
            :value {wifi.quality}
            :style {styles.wifi}
         )
         (label
            :xalign 0
            :show-truncated false
            :style "color: #666;"
            :text {wifi.up_speed > 0 ? "▲ ${wifi.up}" : ""}
         )
         (label
            :xalign 0
            :show-truncated false
            :style "color: #666;"
            :text {wifi.down_speed > 0 ? "▼ ${wifi.down}" : ""}
         )
      )
   )
)

(defwidget bluetooth-indicator []
   (box
      :orientation "vertical"
      (for device in bluetooth
         (eventbox
            :style "color: ${device.connected ? '#0099cc' : '#666666'};"
            :onclick "bluetoothctl ${device.connected ? 'disconnect' : 'connect'} ${device.mac}"
            :cursor "pointer"
            (box
               :spacing 5
               (asciibar :value {device.battery != "N/A" ? "${device.battery}" : "0"})
            )
         )
      )
   )
)

(defwidget bluetooth []
   (box
      :style "padding: 0px 8px;"
      :orientation "vertical"
      (for device in bluetooth
         (eventbox
            :style "color: ${device.connected ? '#0099cc' : '#666666'};"
            :onclick "bluetoothctl ${device.connected ? 'disconnect' : 'connect'} ${device.mac}"
            :cursor "pointer"
            (box
               (label :show-truncated false :xalign 0 :text {device.name})
            )
         )
      )
   )
)

(defwidget moon []
   (label
      :text moon
   )
)

(defwidget volu []
   (eventbox
      :onclick "amixer set Master toggle"
      :cursor "pointer"
      (circular-progress
         :style "color: ${ sound.muted ? '#444' : '#99cc99' };"
         :height 6
         :thickness 1
         :value { sound.volume }
      )
   )
)

(defwidget idle []
   (eventbox
      :style "color: ${idle-inhibited == 'on' ? '#ffcc66' : '#666'};"
      :onclick "matcha -t"
      :cursor "pointer"
      (box
         (label :text {idle-inhibited == "on" ? "◯" : "◠◡" })
      )
   )
)

(defwidget char []
   (eventbox
      (circular-progress
         :style "color: #527386;"
         :clockwise false
         :height 6
         :thickness { EWW_BATTERY == '' ? 3 : 1 }
         :value 30
         :value {EWW_BATTERY == '' ? 100 : EWW_BATTERY?.BAT0?.capacity ?: EWW_BATTERY?.BAT1?.capacity ?: EWW_BATTERY?.BAT?.capacity ?: 100}
      )
   )
)

(defwidget tray []
   (systray
      :orientation "vertical"
      :icon-size 12
      :spacing 15
      :prepend-new false
   )
)

(defwidget spaces []
   (box
      :orientation "vertical"
      (for w in spaces
         (eventbox
            :class "space ${w.is_urgent ? 'urgent' : ''}"
            :onclick "niri msg action focus-workspace ${w.idx}"
            :cursor "pointer"
            (label
               :text { w.is_focused ? '●' : w.is_active ? '◍' : w.active_window_id == 'null' ? '◌' : '○' }
            )
         )
      )
   )
)

(defwidget asciibar [value ?min ?max ?style ?text]
   (label
      :class "asciibar"
      :unindent false
      :style { style == "" ? "" : "${style[round(((value - (min ?: 0)) / ((max ?: 100) - (min ?: 0))) * (arraylength(style) - 1), 0)]}" }
      :text { text ?: asciibars[round(((value - (min ?: 0)) / ((max ?: 100) - (min ?: 0))) * (arraylength(asciibars) - 1), 0)] }
   )
)

(defwidget asciibin [valuel ?minl ?maxl valuer ?minr ?maxr]
   (label
      :class "asciibin"
      :unindent false
      :text "${
         (maxl ?: 100) == (minl ?: 0)
            ? asciibins[0]
            : asciibins[round(((valuel - (minl ?: 0)) / ((maxl ?: 100) - (minl ?: 0))) * (arraylength(asciibins) - 1), 0)]
      }${
         (maxr ?: 100) == (minr ?: 0)
            ? asciibins[0]
            : asciibins[round(((valuer - (minr ?: 0)) / ((maxr ?: 100) - (minr ?: 0))) * (arraylength(asciibins) - 1), 0)]
      }"
   )
)

(defwidget asciilabel [value ?min ?max ?style text]
   (label
      :xalign 0
      :unindent false
      :show-truncated false
      :style { style == "" ? "" : "${style[round(((value - (min ?: 0)) / ((max ?: 100) - (min ?: 0))) * (arraylength(style) - 1), 0)]}" }
      :text { text }
   )
)

; io

(defvar asciibars
   '["  ", "▏ ", "▎ ", "▍ ", "▌ ", "▋ ", "▊ ", "▉ ", "█ ", "█▏", "█▎", "█▍", "█▌", "█▋", "█▊", "█▉", "██"]'
)

(defvar asciibins
   '["█", "▝", "▐", "▖", "▄", "▞", "▟", "▘", "▚", "▀", "▜", "▌", "▙", "▛"]'
)

(defvar styles
  '{
    "charge": [
      "color: #992222; opacity: 1.0;",
      "color: #996666; opacity: 1.0;",
      "color: #996666; opacity: 1.0;",
      "color: #996666; opacity: 1.0;",
      "color: #527386; opacity: 1.0;",
      "color: #527386; opacity: 1.0;",
      "color: #527386; opacity: 1.0;",
      "color: #527386; opacity: 1.0;",
      "color: #527386; opacity: 1.0;",
      "color: #527386; opacity: 1.0;",
      "color: #527386; opacity: 1.0;",
      "color: #527386; opacity: 1.0;",
      "color: #527386; opacity: 1.0;",
      "color: #527386; opacity: 1.0;",
      "color: #527386; opacity: 1.0;",
      "color: #527386; opacity: 1.0;"
    ],
    "cpu": [
      "color: #222222; opacity: 0.0;",
      "color: #222222; opacity: 0.0;",
      "color: #747369; opacity: 0.2;",
      "color: #747369; opacity: 0.2;",
      "color: #747369; opacity: 0.4;",
      "color: #747369; opacity: 0.4;",
      "color: #99cc99; opacity: 0.4;",
      "color: #99cc99; opacity: 0.4;",
      "color: #99cc99; opacity: 0.6;",
      "color: #99cc99; opacity: 0.6;",
      "color: #99cc99; opacity: 0.8;",
      "color: #99cc99; opacity: 0.8;",
      "color: #99cc99; opacity: 1.0;",
      "color: #99cc99; opacity: 1.0;",
      "color: #cc9966; opacity: 1.0;",
      "color: #cc9966; opacity: 1.0;"
    ],
    "disk": [
      "color: #ffffff; opacity: 0.0;",
      "color: #ffffff; opacity: 0.0;",
      "color: #ffffff; opacity: 0.2;",
      "color: #ffffff; opacity: 0.2;",
      "color: #ffffff; opacity: 0.4;",
      "color: #ffffff; opacity: 0.4;",
      "color: #ffffff; opacity: 0.6;",
      "color: #ffffff; opacity: 0.6;",
      "color: #ffffff; opacity: 0.67;",
      "color: #ffffff; opacity: 0.67;",
      "color: #ffffff; opacity: 0.8;",
      "color: #ffffff; opacity: 0.8;",
      "color: #ffffff; opacity: 1.0;",
      "color: #ffffff; opacity: 1.0;",
      "color: #c47848; opacity: 1.0;",
      "color: #c47848; opacity: 1.0;"
    ],
    "mem": [
      "color: #846488; opacity: 0.4;",
      "color: #846488; opacity: 0.4;",
      "color: #846488; opacity: 0.6;",
      "color: #846488; opacity: 0.6;",
      "color: #846488; opacity: 0.8;",
      "color: #846488; opacity: 0.8;",
      "color: #846488; opacity: 1.0;",
      "color: #846488; opacity: 1.0;",
      "color: #cc99cc; opacity: 0.8;",
      "color: #cc99cc; opacity: 0.8;",
      "color: #cc99cc; opacity: 0.8;",
      "color: #cc99cc; opacity: 1.0;",
      "color: #cc99cc; opacity: 1.0;",
      "color: #cc99cc; opacity: 1.0;",
      "color: #ffccff; opacity: 1.0;",
      "color: #ffffff; opacity: 1.0;"
    ],
    "wifi": [
      "color: #ff0000; opacity: 1.0;",
      "color: #ff0000; opacity: 1.0;",
      "color: #ff6600; opacity: 1.0;",
      "color: #ff6600; opacity: 1.0;",
      "color: #ff9900; opacity: 1.0;",
      "color: #ff9900; opacity: 1.0;",
      "color: #ffae00; opacity: 1.0;",
      "color: #ffae00; opacity: 1.0;",
      "color: #ffdd00; opacity: 1.0;",
      "color: #ffdd00; opacity: 1.0;",
      "color: #ddff00; opacity: 1.0;",
      "color: #ddff00; opacity: 1.0;",
      "color: #aeff00; opacity: 1.0;",
      "color: #aeff00; opacity: 1.0;",
      "color: #00ff00; opacity: 1.0;",
      "color: #00ff00; opacity: 1.0;"
    ]
  }'
)

(defpoll date :interval "1s"
   :initial '{"hour" : "XX", "minute" : "XX", "day" : "XX", "month" : "XX"}'
   `date +'{"hour" : "%H", "minute" : "%M", "day" : "%d", "month" : "%m"}'`
)

(defpoll moon :interval "1h"
   :initial "🌕︎"
   `# Synodic month in seconds (29.530588 days)
   SYNODIC_SECONDS=2551443

   # Reference date when moon was at known phase
   # Jan 15 2023 was third quarter (75%)
   REF_EPOCH=$(date --date "Jan 15 2023" +%s)
   REF_PERCENT=75

   # Get current time in seconds since epoch
   NOW_EPOCH=$(date +%s)

   # Calculate time difference
   SEC_DIFF=$((NOW_EPOCH - REF_EPOCH))

   # Convert to hundredths of synodic months (centi-moons)
   CENTI_MOONS=$((100 * SEC_DIFF / SYNODIC_SECONDS))

   # Calculate moon phase percentage (0-99)
   MOON_PERCENT=$(((CENTI_MOONS + REF_PERCENT) % 100))

   # Handle negative percentages
   if [ $MOON_PERCENT -lt 0 ]; then
      MOON_PERCENT=$((MOON_PERCENT + 100))
   fi

   # Map percentage to moon phase emoji
   # 0% = new moon, 50% = full moon
   if [ $MOON_PERCENT -lt 7 ]; then
      echo "🌕︎"  # new
   elif [ $MOON_PERCENT -lt 19 ]; then
      echo "🌖︎"  # waxing crescent
   elif [ $MOON_PERCENT -lt 31 ]; then
      echo "🌗︎"  # first quarter
   elif [ $MOON_PERCENT -lt 44 ]; then
      echo "🌘︎"  # waxing gibbous
   elif [ $MOON_PERCENT -lt 57 ]; then
      echo "🌑︎"  # full
   elif [ $MOON_PERCENT -lt 69 ]; then
      echo "🌒︎"  # waning gibbous
   elif [ $MOON_PERCENT -lt 81 ]; then
      echo "🌓︎"  # last quarter
   else
      echo "🌔︎"  # waning crescent
   fi`
)

(defpoll notifications :interval "3s"
   :initial ""
   `makoctl history | grep "^Notification" | sed 's/^Notification [0-9]*: //'`
)

;; Calendar with highlighted current day
(defpoll cal :interval "1m"
   `cal -mv --color=always | sed -e 's/\\x1b\\[7m/<span background="#999" foreground="#222">/g' -e 's/\\x1b\\[0m/<\\/span>/g'`
)

(defpoll clipboard :interval "1s"
   :initial ""
   `wl-paste`
)

(defpoll idle-inhibited :interval "500ms"
   `matcha -s`
)

(defpoll ethernet :interval "3s"
  :initial '{"ip":"","down":"0B/s","up":"0B/s","down_speed":0,"up_speed":0,"quality":0}'
  `iface=$(ls /sys/class/net/ 2>/dev/null | grep -E '^(eth|enp|eno|ens)' | head -1)
   [ -z "\$iface" ] && echo '{"ip":"","down":"0B/s","up":"0B/s","down_speed":0,"up_speed":0,"quality":0}' && exit
   [ ! -d "/sys/class/net/\$iface" ] && echo '{"ip":"","down":"0B/s","up":"0B/s","down_speed":0,"up_speed":0,"quality":0}' && exit
   ip=$(ip -4 addr show \$iface 2>/dev/null | grep -oP '(?<=inet\\s)\\d+(\\.\\d+){3}' | head -n 1)
   speed=$(cat /sys/class/net/\$iface/speed 2>/dev/null || echo 0)
   rx=$(cat /sys/class/net/\$iface/statistics/rx_bytes)
   tx=$(cat /sys/class/net/\$iface/statistics/tx_bytes)
   cache=/tmp/eth_\$iface
   if [ -f \$cache ]; then
     read rx_p tx_p < \$cache
     rx_rate=$(((rx-rx_p)/3))
     tx_rate=$(((tx-tx_p)/3))
   else
     rx_rate=0 tx_rate=0
   fi
   echo "\$rx \$tx" > \$cache
   down=$(awk -v b=\$rx_rate 'BEGIN{u[0]="B/s";u[1]="KB/s";u[2]="MB/s";u[3]="GB/s";for(i=0;b>=1024&&i<3;i++)b/=1024;printf(i==0?"%d%s":"%.1f%s",b,u[i])}')
   up=$(awk -v b=\$tx_rate 'BEGIN{u[0]="B/s";u[1]="KB/s";u[2]="MB/s";u[3]="GB/s";for(i=0;b>=1024&&i<3;i++)b/=1024;printf(i==0?"%d%s":"%.1f%s",b,u[i])}')
   printf '{"ip":"%s","down":"%s","up":"%s","down_speed":%s,"up_speed":%s,"quality":%s}' "\$ip" "\$down" "\$up" \$rx_rate \$tx_rate \$speed`
)

(defpoll wifi :interval "3s"
  :initial '{"ip":"","ssid":"","down":"0B/s","up":"0B/s","down_speed":0,"up_speed":0,"quality":0}'
  `iface=$(ls /sys/class/net/ 2>/dev/null | grep -E '^(wlan|wlp)' | head -1)
   [ -z "\$iface" ] && echo '{"ip":"","ssid":"","down":"0B/s","up":"0B/s","down_speed":0,"up_speed":0,"quality":0}' && exit
   [ ! -d "/sys/class/net/\$iface" ] && echo '{"ip":"","ssid":"","down":"0B/s","up":"0B/s","down_speed":0,"up_speed":0,"quality":0}' && exit
   ip=$(ip -4 addr show \$iface 2>/dev/null | grep -oP '(?<=inet\\s)\\d+(\\.\\d+){3}' | head -n 1)
   ssid=$(iw dev \$iface link 2>/dev/null | awk '/SSID/{for(i=2;i<=NF;i++)printf "%s%s",\$i,(i<NF?" ":""); print ""}')
   signal_dbm=""
   wifi_link=$(iw dev \$iface link 2>/dev/null)
   if echo "\$wifi_link" | grep -q "signal:"; then
     signal_dbm=$(echo "\$wifi_link" | awk '/signal/{print \$2}')
   fi
   if [ -z "\$signal_dbm" ] && [ -z "\$signal" ] && command -v iwctl >/dev/null 2>&1; then
     iwd_link=$(iwctl station \$iface show 2>/dev/null)
     iwd_rssi=$(echo "\$iwd_link" | grep -oP 'RSSI\s+\K-?[0-9]+')
     if [ -n "\$iwd_rssi" ]; then
       signal_dbm=\$iwd_rssi
     fi
   fi
   if [ -z "\$signal_dbm" ] && command -v nmcli >/dev/null 2>&1; then
     nmcli_signal=$(nmcli -t -f DEVICE,SIGNAL dev wifi 2>/dev/null | grep "^\$iface:" | cut -d':' -f2)
     if [ -n "\$nmcli_signal" ]; then
       signal=\$nmcli_signal
     fi
   fi
   if [ -n "\$signal_dbm" ]; then
     signal=$((2 * (signal_dbm + 100)))
     [ \$signal -lt 0 ] && signal=0
     [ \$signal -gt 100 ] && signal=100
   elif [ -z "\$signal" ]; then
     signal=0
   fi
   rx=$(cat /sys/class/net/\$iface/statistics/rx_bytes 2>/dev/null || echo 0)
   tx=$(cat /sys/class/net/\$iface/statistics/tx_bytes 2>/dev/null || echo 0)
   pf="/tmp/wifi_\$iface"
   if [ -f "\$pf" ]; then
     read pr pt < "\$pf"
     rs=$(((rx-pr)/3))
     ts=$(((tx-pt)/3))
   else
     rs=0
     ts=0
   fi
   echo "\$rx \$tx" > "\$pf"
   rh=$(awk -v b=\$rs 'BEGIN{if(b<1024)printf"%dB/s",b;else if(b<1048576)printf"%.1fK/s",b/1024;else if(b<1073741824)printf"%.1fM/s",b/1048576;else printf"%.1fG/s",b/1073741824}')
   th=$(awk -v b=\$ts 'BEGIN{if(b<1024)printf"%dB/s",b;else if(b<1048576)printf"%.1fK/s",b/1024;else if(b<1073741824)printf"%.1fM/s",b/1048576;else printf"%.1fG/s",b/1073741824}')
   printf '{"ip":"%s","ssid":"%s","down":"%s","up":"%s","down_speed":%d,"up_speed":%d,"quality":%d}' "\$ip" "\$ssid" "\$rh" "\$th" "\$rs" "\$ts" "\$signal"`
)

(defpoll reco :interval "300ms"
  `pgrep -x wf-recorder > /dev/null && echo true || echo false`
)

(deflisten bluetooth
   :initial "[]"
   `#!/bin/bash
   update_devices() {
      {
         bluetoothctl devices | awk '{print \$2}' | while read -r mac; do
            info=\$(bluetoothctl info "\$mac")
            name=\$(echo "\$info" | sed -n 's/.*Name: //p')
            connected=\$(echo "\$info" | grep -q "Connected: yes" && echo "true" || echo "false")
            battery=\$(echo "\$info" | awk '/Battery Percentage/{print \$4}' | tr -d '()')
            [ -z "\$battery" ] && battery="0"
            jq -nc --arg name "\$name" --arg mac "\$mac" --arg battery "\$battery" --argjson connected "\$connected" \
               '{name: \$name, battery: (\$battery | tonumber), connected: \$connected, mac: \$mac}'
         done
      } | jq -sc 'sort_by(.connected) | reverse'
   }

   update_devices

   dbus-monitor --system "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',arg0='org.bluez.Device1'" 2>/dev/null | \
   grep --line-buffered -E "(Connected|Percentage)" | while read -r line; do
      sleep 0.3
      update_devices
   done`
)

(deflisten spaces
   :initial "[]"
   `niri msg -j workspaces && niri msg -j event-stream | while read -r line; \
      do niri msg -j workspaces | jq -c 'sort_by(.idx)'; \
   done`
)

(deflisten sound
   :initial '{"volume": 0, "muted": false, "bluetooth": false, "device": "Unknown"}'
   `device=$(wpctl inspect @DEFAULT_AUDIO_SINK@ | sed -n 's/.*node\\.description = "\\(.*\\)".*/\\1/p');
      wpctl get-volume @DEFAULT_AUDIO_SINK@ | awk -v dev="$device" '{
        vol = int($2*100);
        muted = /MUTED/ ? "true" : "false";
        printf "{\\\"volume\\\": %d, \\\"muted\\\": %s, \\\"bluetooth\\\": false, \\\"device\\\": \\\"%s\\\"}\\n", vol, muted, dev;
      }';
      pw-dump --monitor 2>/dev/null | grep --line-buffered "\\"Props\\"" | while read -r line; do
     device=$(wpctl inspect @DEFAULT_AUDIO_SINK@ | sed -n 's/.*node\\.description = "\\(.*\\)".*/\\1/p');
     wpctl get-volume @DEFAULT_AUDIO_SINK@ | awk -v dev="$device" '{
       vol = int($2*100);
       muted = /MUTED/ ? "true" : "false";
       printf "{\\\"volume\\\": %d, \\\"muted\\\": %s, \\\"bluetooth\\\": false, \\\"device\\\": \\\"%s\\\"}\\n", vol, muted, dev;
     }';
   done`
)

(deflisten player
   :initial  '{"playerName": "", "status": "", "title": "", "artist": "", "album": ""}'
   `playerctl -F metadata -f '{"playerName": "{{playerName}}", "status": "{{status}}", "title": "{{title}}", "artist": "{{artist}}", "album": "{{album}}"}'`
)