From d2b62c9d6f30a0ea88b2a235cde120cd10ba332b Mon Sep 17 00:00:00 2001 From: Ryan Schanzenbacher Date: Sat, 11 Jul 2026 22:14:43 -0400 Subject: Sketchybar now, got tired of the battery issue --- hosts/RyanMac/configuration.nix | 2 +- modules/darwin/random-wallpaper/default.nix | 29 ++++ modules/darwin/random-wallpaper/module.nix | 29 ---- users/ryan/home.nix | 5 +- users/ryan/modules/sketchybar/bar-config/README.md | 63 ++++++++ users/ryan/modules/sketchybar/bar-config/colors.sh | 11 ++ users/ryan/modules/sketchybar/bar-config/icons.sh | 35 +++++ .../sketchybar/bar-config/plugins/battery.sh | 42 +++++ .../sketchybar/bar-config/plugins/bluetooth.sh | 37 +++++ .../sketchybar/bar-config/plugins/brightness.sh | 29 ++++ .../sketchybar/bar-config/plugins/caffeinate.sh | 21 +++ .../modules/sketchybar/bar-config/plugins/clock.sh | 3 + .../sketchybar/bar-config/plugins/front_app.sh | 10 ++ .../modules/sketchybar/bar-config/plugins/mail.sh | 12 ++ .../sketchybar/bar-config/plugins/network.sh | 45 ++++++ .../modules/sketchybar/bar-config/plugins/space.sh | 8 + .../sketchybar/bar-config/plugins/spotify.sh | 20 +++ .../sketchybar/bar-config/plugins/storage.sh | 27 ++++ .../sketchybar/bar-config/plugins/volume.sh | 36 +++++ .../sketchybar/bar-config/plugins/weather.sh | 56 +++++++ .../modules/sketchybar/bar-config/sketchybarrc | 172 +++++++++++++++++++++ users/ryan/modules/sketchybar/default.nix | 19 +++ 22 files changed, 678 insertions(+), 33 deletions(-) create mode 100644 modules/darwin/random-wallpaper/default.nix delete mode 100644 modules/darwin/random-wallpaper/module.nix create mode 100644 users/ryan/modules/sketchybar/bar-config/README.md create mode 100644 users/ryan/modules/sketchybar/bar-config/colors.sh create mode 100644 users/ryan/modules/sketchybar/bar-config/icons.sh create mode 100755 users/ryan/modules/sketchybar/bar-config/plugins/battery.sh create mode 100755 users/ryan/modules/sketchybar/bar-config/plugins/bluetooth.sh create mode 100755 users/ryan/modules/sketchybar/bar-config/plugins/brightness.sh create mode 100755 users/ryan/modules/sketchybar/bar-config/plugins/caffeinate.sh create mode 100755 users/ryan/modules/sketchybar/bar-config/plugins/clock.sh create mode 100755 users/ryan/modules/sketchybar/bar-config/plugins/front_app.sh create mode 100755 users/ryan/modules/sketchybar/bar-config/plugins/mail.sh create mode 100755 users/ryan/modules/sketchybar/bar-config/plugins/network.sh create mode 100755 users/ryan/modules/sketchybar/bar-config/plugins/space.sh create mode 100755 users/ryan/modules/sketchybar/bar-config/plugins/spotify.sh create mode 100755 users/ryan/modules/sketchybar/bar-config/plugins/storage.sh create mode 100755 users/ryan/modules/sketchybar/bar-config/plugins/volume.sh create mode 100755 users/ryan/modules/sketchybar/bar-config/plugins/weather.sh create mode 100755 users/ryan/modules/sketchybar/bar-config/sketchybarrc create mode 100644 users/ryan/modules/sketchybar/default.nix diff --git a/hosts/RyanMac/configuration.nix b/hosts/RyanMac/configuration.nix index f72c6aa..e5b38ca 100644 --- a/hosts/RyanMac/configuration.nix +++ b/hosts/RyanMac/configuration.nix @@ -45,7 +45,7 @@ in { # Modules imports = [ inputs.nix-homebrew.darwinModules.nix-homebrew - ../../modules/darwin/random-wallpaper/module.nix + ../../modules/darwin/random-wallpaper ]; # Define the system's user and home dir location diff --git a/modules/darwin/random-wallpaper/default.nix b/modules/darwin/random-wallpaper/default.nix new file mode 100644 index 0000000..b20ce88 --- /dev/null +++ b/modules/darwin/random-wallpaper/default.nix @@ -0,0 +1,29 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.local.randomWallpaper; + wallpaper-agent-src = ./wallpaper-daemon.swift; +in +{ + options.local.randomWallpaper = { + enable = lib.mkEnableOption "Random Rotating Wallpaper"; + directory = lib.mkOption { + type = lib.types.str; + description = "Folder containing images"; + }; + interval = lib.mkOption { + type = lib.types.int; + default = 1800; + description = "Seconds between wallpaper changes"; + }; + }; + + config = lib.mkIf cfg.enable { + launchd.user.agents.random-wallpaper.serviceConfig = { + Label = "org.rschanz.wallpaperDaemon"; + ProgramArguments = [ "/usr/bin/swift" "${wallpaper-agent-src}" "${cfg.directory}" "${builtins.toString cfg.interval}" ]; + RunAtLoad = true; + KeepAlive = true; + ProcessType = "Background"; + }; + }; +} diff --git a/modules/darwin/random-wallpaper/module.nix b/modules/darwin/random-wallpaper/module.nix deleted file mode 100644 index b20ce88..0000000 --- a/modules/darwin/random-wallpaper/module.nix +++ /dev/null @@ -1,29 +0,0 @@ -{ config, lib, pkgs, ... }: -let - cfg = config.local.randomWallpaper; - wallpaper-agent-src = ./wallpaper-daemon.swift; -in -{ - options.local.randomWallpaper = { - enable = lib.mkEnableOption "Random Rotating Wallpaper"; - directory = lib.mkOption { - type = lib.types.str; - description = "Folder containing images"; - }; - interval = lib.mkOption { - type = lib.types.int; - default = 1800; - description = "Seconds between wallpaper changes"; - }; - }; - - config = lib.mkIf cfg.enable { - launchd.user.agents.random-wallpaper.serviceConfig = { - Label = "org.rschanz.wallpaperDaemon"; - ProgramArguments = [ "/usr/bin/swift" "${wallpaper-agent-src}" "${cfg.directory}" "${builtins.toString cfg.interval}" ]; - RunAtLoad = true; - KeepAlive = true; - ProcessType = "Background"; - }; - }; -} diff --git a/users/ryan/home.nix b/users/ryan/home.nix index 2aaacb3..1c77723 100644 --- a/users/ryan/home.nix +++ b/users/ryan/home.nix @@ -15,9 +15,7 @@ # My own modules ryan.neovim.enable = true; - ryan.simple-bar.enable = pkgs.stdenv.isDarwin; - ryan.taskwarrior-widget.enable = pkgs.stdenv.isDarwin; - ryan.ubersicht.enable = pkgs.stdenv.isDarwin; + ryan.sketchybar.enable = true; programs.starship = { enable = true; @@ -148,6 +146,7 @@ audacity ffmpeg blender + nerd-fonts.anonymice ]; # Home Manager is pretty good at managing dotfiles. The primary way to manage diff --git a/users/ryan/modules/sketchybar/bar-config/README.md b/users/ryan/modules/sketchybar/bar-config/README.md new file mode 100644 index 0000000..7b1bf97 --- /dev/null +++ b/users/ryan/modules/sketchybar/bar-config/README.md @@ -0,0 +1,63 @@ +# SketchyBar config (translated from Waybar) + +This is a translation of a Hyprland/Waybar setup (`config` + `style.css`) into an +equivalent [SketchyBar](https://github.com/FelixKratz/SketchyBar) config for macOS. +Layout, fonts, and colours mirror the originals as closely as macOS allows. + +## Install + +```sh +# 1. SketchyBar + the font your CSS references +brew tap FelixKratz/formulae +brew install sketchybar +brew install --cask font-anonymice-nerd-font # "AnonymicePro Nerd Font" + +# 2. Drop these files in place +cp -r .config/sketchybar ~/.config/ # (or move this folder there) +chmod +x ~/.config/sketchybar/sketchybarrc ~/.config/sketchybar/plugins/*.sh + +# 3. Start it +brew services start sketchybar +``` + +## Optional dependencies (items self-hide / degrade gracefully without them) + +| Item | Needs | Install | +|-------------|--------------------------------|---------------------------------| +| workspaces | yabai (tiling WM) for `space` | `brew install koekeishiya/formulae/yabai` | +| bluetooth | `blueutil` | `brew install blueutil` | +| brightness | `brightness` CLI | `brew install brightness` | +| weather | `curl` (built in) | — | + +> **Spaces / yabai:** the `space` component needs yabai, which needs SIP partially +> disabled. If you'd rather not touch SIP, [AeroSpace](https://github.com/nikitabobko/AeroSpace) +> is a SIP-free tiling WM — swap the `space` loop for its `aerospace_workspace_change` +> event and point `click_script` at `aerospace workspace $sid`. + +## Module mapping + +| Waybar | SketchyBar | Notes | +|-----------------------|-----------------------------------|-------| +| hyprland/workspaces | `space` items (yabai) | number icon, dim→white for active (Waybar's 3px top-border → `icon.highlight`) | +| hyprland/window | `front_app` | shows app name; window *title* needs a yabai query (noted in plugin) | +| clock | `clock` | same `%a %d %b %H:%M` format | +| battery (BAT0) | `battery` | `pmset`; same icons, warning@25 / critical@10, charging bolt | +| bluetooth | `bluetooth` | `blueutil`; on/off/connected glyphs, click toggles power | +| network | `network` | ip + wifi/ethernet/off glyph (cidr & RSSI dropped — not exposed on macOS) | +| pulseaudio | `volume` | native `volume_change`; volume% + icon, mute glyph | +| backlight | `brightness` | `brightness` CLI; scroll to adjust (flaky on Apple Silicon) | +| custom/storage | `storage` | `df /`; warning@80 / critical@90 | +| custom/weather | `weather` | `wttr.in` in °F, click opens wttr.in | +| custom/mail | `mail` | Mail.app unread count (swap for your provider) | +| custom/spotify | `spotify` | native `media_change`; title–artist, click play/pause | +| idle_inhibitor | `caffeinate` | click toggles `caffeinate`; lock/unlock glyphs | + +## Omitted (no macOS/SketchyBar equivalent) + +- **tray** — the macOS menu bar already renders status/menu-extra icons. +- **privacy** (screenshare/mic) — macOS shows camera/mic indicators natively. +- **hyprland/submap** — yabai has no submap concept. +- **battery#num2** (BAT1) — Macs have a single battery. + +Colours in `colors.sh` and glyphs in `icons.sh` are taken verbatim from your +`style.css` and `config` (glyphs are byte-for-byte identical). diff --git a/users/ryan/modules/sketchybar/bar-config/colors.sh b/users/ryan/modules/sketchybar/bar-config/colors.sh new file mode 100644 index 0000000..1ef1264 --- /dev/null +++ b/users/ryan/modules/sketchybar/bar-config/colors.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# Colors translated from style.css (0xAARRGGBB) + +export WHITE=0xffffffff +export TEXT=0xffd9d8d8 # rgba(217,216,216,1) default text / visible+active workspace +export TEXT_DIM=0x66d9d8d8 # rgba(217,216,216,0.4) inactive workspace +export RED=0xffee2e24 # rgba(238,46,36,1) urgent / battery+storage critical +export YELLOW=0xffffd204 # rgba(255,210,4,1) battery+storage warning +export BAR_COLOR=0x80000000 # rgba(0,0,0,0.25) #waybar background +export POPUP_BG=0xd9231f20 # rgba(35,31,32,0.85) window#waybar.solo background +export TRANSPARENT=0x00000000 diff --git a/users/ryan/modules/sketchybar/bar-config/icons.sh b/users/ryan/modules/sketchybar/bar-config/icons.sh new file mode 100644 index 0000000..39de9cd --- /dev/null +++ b/users/ryan/modules/sketchybar/bar-config/icons.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# Nerd Font glyphs, byte-for-byte identical to the original Waybar config. +# Font: AnonymicePro Nerd Font + +# battery format-icons (low -> high) + charging +export BAT_0="󰁺"; export BAT_1="󰁻"; export BAT_2="󰁾" +export BAT_3="󰂀"; export BAT_4="󰁹"; export BAT_CHARGING="󰂄" + +# bluetooth +export BT_ON="󰂯"; export BT_OFF="󰂲"; export BT_CONNECTED="󰂱" + +# network (wifi weak -> strong, ethernet, disconnected) +export WIFI_1="󰤟"; export WIFI_2="󰤢"; export WIFI_3="󰤥" +export WIFI_4="󰤨"; export ETHERNET=""; export NET_OFF="󰤭" + +# pulseaudio / volume (default set) + muted +export VOL_0=""; export VOL_1=""; export VOL_2="" +export VOL_3=""; export VOL_MUTE="" + +# backlight / brightness (dim, bright) +export BL_LOW=""; export BL_HIGH="" + +# idle_inhibitor (activated=lock, deactivated=unlock) +export IDLE_ON=""; export IDLE_OFF="" + +# misc +export DISK=""; export MAIL=""; export SPOTIFY="" + +# weather (nf-weather-*, wttr.in weatherCode -> glyph) +export WEA_SUNNY=""; export WEA_NIGHT="" +export WEA_DAY_CLOUDY=""; export WEA_NIGHT_CLOUDY="" +export WEA_CLOUDY=""; export WEA_FOG="" +export WEA_SHOWERS=""; export WEA_RAIN="" +export WEA_SNOW=""; export WEA_SLEET="" +export WEA_THUNDER="" diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/battery.sh b/users/ryan/modules/sketchybar/bar-config/plugins/battery.sh new file mode 100755 index 0000000..3d01d63 --- /dev/null +++ b/users/ryan/modules/sketchybar/bar-config/plugins/battery.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +source "$CONFIG_DIR/colors.sh" +source "$CONFIG_DIR/icons.sh" + +CACHE="/tmp/sketchybar_${NAME}.txt" +case "$SENDER" in + mouse.entered) + [ -f "$CACHE" ] && sketchybar --set "$NAME.details" label="$(cat "$CACHE")" + sketchybar --set "$NAME" popup.drawing=on; exit 0 ;; + mouse.exited) + sketchybar --set "$NAME" popup.drawing=off; exit 0 ;; +esac + +RAW="$(pmset -g batt)" +PCT="$(printf '%s' "$RAW" | grep -Eo '[0-9]+%' | head -1 | tr -d '%')" +CHARGING="$(printf '%s' "$RAW" | grep -c 'AC Power')" +[ -z "$PCT" ] && exit 0 + +if [ "$PCT" -ge 80 ]; then ICON=$BAT_4 +elif [ "$PCT" -ge 60 ]; then ICON=$BAT_3 +elif [ "$PCT" -ge 40 ]; then ICON=$BAT_2 +elif [ "$PCT" -ge 20 ]; then ICON=$BAT_1 +else ICON=$BAT_0 +fi + +COLOR=$TEXT +if [ "$CHARGING" -ge 1 ]; then ICON=$BAT_CHARGING; COLOR=$TEXT +elif [ "$PCT" -le 10 ]; then COLOR=$RED +elif [ "$PCT" -le 25 ]; then COLOR=$YELLOW +fi + +sketchybar --set "$NAME" label="${PCT}% ${ICON}" label.color=$COLOR + +# popup: state + time remaining (Waybar battery format-alt "{time}") +STATE="$(printf '%s' "$RAW" | grep -Eo 'discharging|charging|charged|AC attached|finishing charge' | head -1)" +TIME="$(printf '%s' "$RAW" | grep -Eo '[0-9]+:[0-9]+' | head -1)" +LINE="${PCT}%" +[ -n "$STATE" ] && LINE="$LINE · $STATE" +if [ -n "$TIME" ] && [ "$TIME" != "0:00" ]; then + if [ "$CHARGING" -ge 1 ]; then LINE="$LINE · $TIME to full"; else LINE="$LINE · $TIME remaining"; fi +fi +printf '%s' "$LINE" > "$CACHE" diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/bluetooth.sh b/users/ryan/modules/sketchybar/bar-config/plugins/bluetooth.sh new file mode 100755 index 0000000..80185c0 --- /dev/null +++ b/users/ryan/modules/sketchybar/bar-config/plugins/bluetooth.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +source "$CONFIG_DIR/colors.sh" +source "$CONFIG_DIR/icons.sh" + +CACHE="/tmp/sketchybar_${NAME}.txt" +case "$SENDER" in + mouse.entered) + [ -f "$CACHE" ] && sketchybar --set "$NAME.details" label="$(cat "$CACHE")" + sketchybar --set "$NAME" popup.drawing=on; exit 0 ;; + mouse.exited) + sketchybar --set "$NAME" popup.drawing=off; exit 0 ;; +esac + +if ! command -v blueutil >/dev/null 2>&1; then + sketchybar --set "$NAME" label="$BT_OFF" label.color=$TEXT_DIM + printf '%s' "blueutil not installed" > "$CACHE" + exit 0 +fi + +if [ "$1" = "toggle" ]; then + if [ "$(blueutil -p)" = "1" ]; then blueutil -p 0; else blueutil -p 1; fi +fi + +if [ "$(blueutil -p)" = "1" ]; then + NAMES="$(blueutil --connected 2>/dev/null | grep -o 'name: "[^"]*"' | sed 's/name: "//; s/"$//' | paste -sd , - | sed 's/,/, /g')" + COUNT="$(blueutil --connected 2>/dev/null | grep -c 'name:')" + if [ "$COUNT" -gt 0 ]; then + sketchybar --set "$NAME" label="$BT_CONNECTED" label.color=$TEXT + printf '%s' "Bluetooth on · $COUNT connected: $NAMES" > "$CACHE" + else + sketchybar --set "$NAME" label="$BT_ON" label.color=$TEXT + printf '%s' "Bluetooth on · no devices connected" > "$CACHE" + fi +else + sketchybar --set "$NAME" label="$BT_OFF" label.color=$TEXT_DIM + printf '%s' "Bluetooth off" > "$CACHE" +fi diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/brightness.sh b/users/ryan/modules/sketchybar/bar-config/plugins/brightness.sh new file mode 100755 index 0000000..87a245f --- /dev/null +++ b/users/ryan/modules/sketchybar/bar-config/plugins/brightness.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# backlight -> brightness. Requires the `brightness` CLI (brew install brightness). +# NOTE: reading/writing internal-display brightness is unreliable on Apple Silicon; +# the item hides itself if the tool or a readable value is unavailable. +source "$CONFIG_DIR/colors.sh" +source "$CONFIG_DIR/icons.sh" + +command -v brightness >/dev/null 2>&1 || { sketchybar --set "$NAME" drawing=off; exit 0; } + +read_val() { brightness -l 2>/dev/null | awk '/display 0: brightness/{print $NF}'; } + +if [ "$SENDER" = "mouse.scrolled" ] && command -v bc >/dev/null 2>&1; then + CUR="$(read_val)" + if [ -n "$CUR" ]; then + case "$INFO" in + -*) brightness "$(echo "$CUR - 0.05" | bc)" 2>/dev/null ;; # scroll down + *) brightness "$(echo "$CUR + 0.05" | bc)" 2>/dev/null ;; # scroll up + esac + fi +fi + +CUR="$(read_val)" +[ -z "$CUR" ] && { sketchybar --set "$NAME" drawing=off; exit 0; } +PCT="$(printf '%.0f' "$(echo "$CUR * 100" | bc -l 2>/dev/null)" 2>/dev/null)" +[ -z "$PCT" ] && PCT=0 + +if [ "$PCT" -ge 50 ]; then ICON=$BL_HIGH; else ICON=$BL_LOW; fi +# Waybar: {percent}% {icon} +sketchybar --set "$NAME" drawing=on label="${PCT}% ${ICON}" diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/caffeinate.sh b/users/ryan/modules/sketchybar/bar-config/plugins/caffeinate.sh new file mode 100755 index 0000000..ce4c4d2 --- /dev/null +++ b/users/ryan/modules/sketchybar/bar-config/plugins/caffeinate.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# idle_inhibitor -> caffeinate (keeps display + system awake while "activated") +source "$CONFIG_DIR/colors.sh" +source "$CONFIG_DIR/icons.sh" + +PIDFILE="/tmp/sketchybar_caffeinate.pid" +is_on() { [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; } + +if [ "$1" = "toggle" ]; then + if is_on; then + kill "$(cat "$PIDFILE")" 2>/dev/null; rm -f "$PIDFILE" + else + caffeinate -d -i -s & echo $! > "$PIDFILE" + fi +fi + +if is_on; then + sketchybar --set "$NAME" label="$IDLE_ON" label.color=$TEXT # activated +else + sketchybar --set "$NAME" label="$IDLE_OFF" label.color=$TEXT_DIM # deactivated +fi diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/clock.sh b/users/ryan/modules/sketchybar/bar-config/plugins/clock.sh new file mode 100755 index 0000000..e78ee89 --- /dev/null +++ b/users/ryan/modules/sketchybar/bar-config/plugins/clock.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +# Waybar: {:%a %d %b %H:%M:%S} +sketchybar --set "$NAME" label="$(date '+%a %d %b %H:%M:%S')" diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/front_app.sh b/users/ryan/modules/sketchybar/bar-config/plugins/front_app.sh new file mode 100755 index 0000000..c5d8ed0 --- /dev/null +++ b/users/ryan/modules/sketchybar/bar-config/plugins/front_app.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# hyprland/window -> focused application name. +# (For the literal window *title* instead of the app name, query yabai: +# yabai -m query --windows --window | jq -r '.title') +source "$CONFIG_DIR/colors.sh" +source "$CONFIG_DIR/icons.sh" + +if [ "$SENDER" = "front_app_switched" ]; then + sketchybar --set "$NAME" label="$INFO" +fi diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/mail.sh b/users/ryan/modules/sketchybar/bar-config/plugins/mail.sh new file mode 100755 index 0000000..7ce2fe6 --- /dev/null +++ b/users/ryan/modules/sketchybar/bar-config/plugins/mail.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# Waybar used a custom mail.py; here we read Mail.app's unread inbox count. +# (First run may prompt for Automation permission. Swap this for your provider.) +source "$HOME/.config/sketchybar/icons.sh" + +UNREAD="$(osascript -e 'tell application "Mail" to get unread count of inbox' 2>/dev/null)" + +if [ -n "$UNREAD" ] && [ "$UNREAD" -gt 0 ] 2>/dev/null; then + sketchybar --set "$NAME" label="$MAIL $UNREAD" +else + sketchybar --set "$NAME" label="$MAIL" +fi diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/network.sh b/users/ryan/modules/sketchybar/bar-config/plugins/network.sh new file mode 100755 index 0000000..b6b003a --- /dev/null +++ b/users/ryan/modules/sketchybar/bar-config/plugins/network.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +source "$CONFIG_DIR/colors.sh" +source "$CONFIG_DIR/icons.sh" + +CACHE="/tmp/sketchybar_${NAME}.txt" +case "$SENDER" in + mouse.entered) + [ -f "$CACHE" ] && sketchybar --set "$NAME.details" label="$(cat "$CACHE")" + sketchybar --set "$NAME" popup.drawing=on; exit 0 ;; + mouse.exited) + sketchybar --set "$NAME" popup.drawing=off; exit 0 ;; +esac + +SERVICE="$(route -n get default 2>/dev/null | awk '/interface:/{print $2}')" +IP="$(ipconfig getifaddr "$SERVICE" 2>/dev/null)" + +if [ -z "$IP" ]; then + sketchybar --set "$NAME" label="$NET_OFF" + printf '%s' "Disconnected" > "$CACHE" + exit 0 +fi + +WIFI_DEV="$(networksetup -listallhardwareports 2>/dev/null | awk '/Wi-Fi/{getline; print $2}')" +if [ "$SERVICE" = "$WIFI_DEV" ]; then ICON=$WIFI_4; TYPE="Wi-Fi"; else ICON=$ETHERNET; TYPE="Ethernet"; fi + +sketchybar --set "$NAME" label="$IP $ICON" + +# ---- popup details: interface, ip/cidr, gateway, SSID ---- +MASK="$(ifconfig "$SERVICE" 2>/dev/null | awk '/inet /{print $4; exit}')" +CIDR="" +if [ -n "$MASK" ]; then + n=$(( MASK )); c=0 + while [ "$n" -gt 0 ]; do c=$(( c + (n & 1) )); n=$(( n >> 1 )); done + CIDR="$c" +fi +GW="$(route -n get default 2>/dev/null | awk '/gateway:/{print $2}')" +SSID="" +if [ "$TYPE" = "Wi-Fi" ]; then + SSID="$(networksetup -getairportnetwork "$SERVICE" 2>/dev/null | sed -n 's/^Current Wi-Fi Network: //p')" +fi + +LINE="$SERVICE · $TYPE · ${IP}${CIDR:+/$CIDR}" +[ -n "$GW" ] && LINE="$LINE · gw $GW" +[ -n "$SSID" ] && LINE="$LINE · $SSID" +printf '%s' "$LINE" > "$CACHE" diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/space.sh b/users/ryan/modules/sketchybar/bar-config/plugins/space.sh new file mode 100755 index 0000000..f15ecca --- /dev/null +++ b/users/ryan/modules/sketchybar/bar-config/plugins/space.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# hyprland/workspaces active/visible -> highlighted (white) icon. +# Waybar's 3px white top-border on the active button maps to icon.highlight here. +if [ "$SELECTED" = "true" ]; then + sketchybar --set "$NAME" icon.highlight=on +else + sketchybar --set "$NAME" icon.highlight=off +fi diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/spotify.sh b/users/ryan/modules/sketchybar/bar-config/plugins/spotify.sh new file mode 100755 index 0000000..8d91297 --- /dev/null +++ b/users/ryan/modules/sketchybar/bar-config/plugins/spotify.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# custom/spotify -> now playing, driven by the native media_change event. +source "$HOME/.config/sketchybar/icons.sh" + +# Waybar on-click: playerctl play-pause +if [ "$1" = "toggle" ]; then + osascript -e 'tell application "Spotify" to playpause' 2>/dev/null + exit 0 +fi + +STATE="$(printf '%s' "$INFO" | sed -nE 's/.*"state":"([^"]*)".*/\1/p')" +TITLE="$(printf '%s' "$INFO" | sed -nE 's/.*"title":"([^"]*)".*/\1/p')" +ARTIST="$(printf '%s' "$INFO" | sed -nE 's/.*"artist":"([^"]*)".*/\1/p')" + +# exec-if check-for-music -> only draw while something is playing +if [ "$STATE" = "playing" ] && [ -n "$TITLE" ]; then + sketchybar --set "$NAME" drawing=on label="$TITLE - $ARTIST" +else + sketchybar --set "$NAME" drawing=off +fi diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/storage.sh b/users/ryan/modules/sketchybar/bar-config/plugins/storage.sh new file mode 100755 index 0000000..b844bb5 --- /dev/null +++ b/users/ryan/modules/sketchybar/bar-config/plugins/storage.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +source "$CONFIG_DIR/colors.sh" +source "$CONFIG_DIR/icons.sh" + +CACHE="/tmp/sketchybar_${NAME}.txt" +case "$SENDER" in + mouse.entered) + [ -f "$CACHE" ] && sketchybar --set "$NAME.details" label="$(cat "$CACHE")" + sketchybar --set "$NAME" popup.drawing=on; exit 0 ;; + mouse.exited) + sketchybar --set "$NAME" popup.drawing=off; exit 0 ;; +esac + +# On APFS the real user usage lives on the Data volume; fall back to / +VOL="/System/Volumes/Data"; [ -d "$VOL" ] || VOL="/" + +DF="$(df -h "$VOL" | awk 'NR==2{gsub("%","",$5); print $2, $3, $4, $5}')" +set -- $DF; SIZE="$1"; USED="$2"; AVAIL="$3"; PCT="$4" +[ -z "$PCT" ] && exit 0 + +COLOR=$TEXT +if [ "$PCT" -ge 90 ]; then COLOR=$RED # custom-storage.critical +elif [ "$PCT" -ge 80 ]; then COLOR=$YELLOW # custom-storage.warning +fi + +sketchybar --set "$NAME" label="${PCT}% ${DISK}" label.color=$COLOR +printf '%s' "Disk · ${USED} used of ${SIZE} (${PCT}%) · ${AVAIL} free" > "$CACHE" diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/volume.sh b/users/ryan/modules/sketchybar/bar-config/plugins/volume.sh new file mode 100755 index 0000000..667d0e8 --- /dev/null +++ b/users/ryan/modules/sketchybar/bar-config/plugins/volume.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +source "$CONFIG_DIR/colors.sh" +source "$CONFIG_DIR/icons.sh" + +STEP=5 # % per scroll tick (Waybar used scroll-step: 1 — bump this down to taste) + +if [ "$SENDER" = "mouse.scrolled" ]; then + CUR="$(osascript -e 'output volume of (get volume settings)')" + case "$SCROLL_DELTA" in + -*) VOL=$(( CUR - STEP )) ;; # scroll down + *) VOL=$(( CUR + STEP )) ;; # scroll up + esac + [ "$VOL" -lt 0 ] && VOL=0 + [ "$VOL" -gt 100 ] && VOL=100 + osascript -e "set volume output volume $VOL" +elif [ "$SENDER" = "volume_change" ]; then + VOL="$INFO" +else + VOL="$(osascript -e 'output volume of (get volume settings)')" +fi + +MUTED="$(osascript -e 'output muted of (get volume settings)')" + +if [ "$MUTED" = "true" ] || { [ "$VOL" -eq 0 ] 2>/dev/null; }; then + sketchybar --set "$NAME" label="$VOL_MUTE" # format-muted + exit 0 +fi + +if [ "$VOL" -ge 66 ]; then ICON=$VOL_3 +elif [ "$VOL" -ge 33 ]; then ICON=$VOL_2 +elif [ "$VOL" -ge 1 ]; then ICON=$VOL_1 +else ICON=$VOL_0 +fi + +# Waybar: {volume} {icon} +sketchybar --set "$NAME" label="$VOL $ICON" diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/weather.sh b/users/ryan/modules/sketchybar/bar-config/plugins/weather.sh new file mode 100755 index 0000000..abf8103 --- /dev/null +++ b/users/ryan/modules/sketchybar/bar-config/plugins/weather.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# custom/weather -> condition icon + temp, with a hover popup ("tooltip"). +# Uses wttr.in JSON (format=j2, the smaller no-hourly variant). Needs jq + curl. +source "$CONFIG_DIR/colors.sh" +source "$CONFIG_DIR/icons.sh" + +CACHE="/tmp/sketchybar_weather.txt" + +# --- hover: show/hide the popup, populated from the cached line --- +case "$SENDER" in + mouse.entered) + [ -f "$CACHE" ] && sketchybar --set weather.details label="$(cat "$CACHE")" + sketchybar --set "$NAME" popup.drawing=on + exit 0 ;; + mouse.exited) + sketchybar --set "$NAME" popup.drawing=off + exit 0 ;; +esac + +command -v jq >/dev/null 2>&1 || { sketchybar --set "$NAME" label="wttr?"; exit 0; } + +JSON="$(curl -sf --max-time 6 'wttr.in/?format=j2')" \ + || JSON="$(curl -sf --max-time 6 'wttr.in/?format=j1')" \ + || { sketchybar --set "$NAME" label="--"; exit 0; } + +cc() { printf '%s' "$JSON" | jq -r ".current_condition[0].$1 // empty"; } +CODE="$(cc weatherCode)"; TEMP="$(cc temp_F)"; FEELS="$(cc FeelsLikeF)" +HUM="$(cc humidity)"; WSPD="$(cc windspeedMiles)"; WDIR="$(cc winddir16Point)" +DESC="$(printf '%s' "$JSON" | jq -r '.current_condition[0].weatherDesc[0].value // empty')" +AREA="$(printf '%s' "$JSON" | jq -r '.nearest_area[0].areaName[0].value // empty')" + +# daytime? simple local-hour heuristic (06:00–18:59) for clear/partly-cloudy variants +HOUR=$(date +%H); HOUR=${HOUR#0}; : "${HOUR:=0}" +if [ "$HOUR" -ge 6 ] && [ "$HOUR" -lt 19 ]; then DAY=1; else DAY=0; fi + +# WWO weatherCode -> nf-weather glyph +case "$CODE" in + 113) [ "$DAY" = 1 ] && ICON="$WEA_SUNNY" || ICON="$WEA_NIGHT" ;; + 116) [ "$DAY" = 1 ] && ICON="$WEA_DAY_CLOUDY" || ICON="$WEA_NIGHT_CLOUDY" ;; + 119|122) ICON="$WEA_CLOUDY" ;; + 143|248|260) ICON="$WEA_FOG" ;; + 200|386|389|392|395) ICON="$WEA_THUNDER" ;; + 176|263|266|293|296|353) ICON="$WEA_SHOWERS" ;; + 299|302|305|308|356|359) ICON="$WEA_RAIN" ;; + 179|227|230|323|326|329|332|335|338|368|371) ICON="$WEA_SNOW" ;; + 182|185|281|284|311|314|317|320|350|362|365|374|377) ICON="$WEA_SLEET" ;; + *) ICON="$WEA_CLOUDY" ;; +esac + +[ -z "$TEMP" ] && exit 0 + +# bar: °F (drop " ${TEMP}°F" if you want the icon only) +sketchybar --set "$NAME" label="$ICON ${TEMP}°F" + +# cache the single-line hover tooltip +printf '%s' "${AREA:+$AREA · }${DESC} · feels ${FEELS}°F · ${HUM}% humidity · ${WDIR} ${WSPD}mph" > "$CACHE" diff --git a/users/ryan/modules/sketchybar/bar-config/sketchybarrc b/users/ryan/modules/sketchybar/bar-config/sketchybarrc new file mode 100755 index 0000000..446da25 --- /dev/null +++ b/users/ryan/modules/sketchybar/bar-config/sketchybarrc @@ -0,0 +1,172 @@ +#!/usr/bin/env bash +# ============================================================================ +# sketchybarrc — translated from a Hyprland/Waybar config +# Layout, fonts and colours mirror the original Waybar config + style.css. +# ============================================================================ +CONFIG_DIR="$HOME/.config/sketchybar" +PLUGIN_DIR="$CONFIG_DIR/plugins" +source "$CONFIG_DIR/colors.sh" +source "$CONFIG_DIR/icons.sh" + +env + +FONT="AnonymicePro Nerd Font" + +# ---- bar -------------------------------------------------------------------- +# From #waybar { background: rgba(0,0,0,0.25) } and Waybar height:30, position:top +sketchybar --bar \ + height=32 \ + position=top \ + color=$BAR_COLOR \ + padding_left=8 \ + padding_right=8 \ + corner_radius=0 \ + border_width=0 \ + shadow=off + +# ---- defaults --------------------------------------------------------------- +# From * { color:white; font: AnonymicePro Nerd Font 15px; no radius/shadow } +sketchybar --default \ + updates=when_shown \ + icon.font="$FONT:Regular:15.0" \ + icon.color=$TEXT \ + icon.padding_left=4 \ + icon.padding_right=4 \ + label.font="$FONT:Regular:15.0" \ + label.color=$TEXT \ + label.padding_left=4 \ + label.padding_right=4 \ + background.corner_radius=0 \ + background.height=32 + +# ---- popup helper ----------------------------------------------------------- +# add_popup : attaches a hover "tooltip" popup + a .details child +# (styled with POPUP_BG from window#waybar.solo) and subscribes to hover events. +add_popup() { + sketchybar --set "$1" \ + popup.background.color=$POPUP_BG \ + popup.background.corner_radius=0 \ + popup.background.border_width=0 \ + popup.horizontal=off \ + popup.y_offset=-1 \ + --subscribe "$1" mouse.entered mouse.exited + sketchybar --add item "$1.details" "popup.$1" \ + --set "$1.details" icon.drawing=off \ + label.font="$FONT:Regular:13.0" \ + label.padding_left=12 label.padding_right=12 \ + background.height=24 +} + +# ============================================================================ +# LEFT — hyprland/workspaces -> yabai spaces +# (hyprland/submap has no yabai/macOS analog -> omitted) +# ============================================================================ +for sid in 1 2 3 4 5 6 7 8 9 10; do + sketchybar --add space space.$sid left \ + --set space.$sid \ + space=$sid \ + icon=$sid \ + icon.color=$TEXT_DIM \ + icon.highlight_color=$TEXT \ + label.drawing=off \ + background.drawing=off \ + padding_left=5 padding_right=5 \ + script="$PLUGIN_DIR/space.sh" \ + click_script="yabai -m space --focus $sid 2>/dev/null" +done + +# ============================================================================ +# CENTER — hyprland/window -> front_app +# ============================================================================ +sketchybar --add item front_app center \ + --set front_app \ + icon.drawing=off \ + label.max_chars=80 \ + label.padding_left=10 label.padding_right=10 \ + script="$PLUGIN_DIR/front_app.sh" \ + --subscribe front_app front_app_switched + +# ============================================================================ +# RIGHT — added right-to-left so the visual order matches Waybar's list: +# spotify weather mail storage brightness bluetooth volume network +# caffeinate battery clock +# ============================================================================ + +# clock (rightmost) — Waybar {:%a %d %b %H:%M} +sketchybar --add item clock right \ + --set clock update_freq=1 icon.drawing=off \ + label.padding_left=10 label.padding_right=16 \ + script="$PLUGIN_DIR/clock.sh" + +# battery (BAT0). Waybar battery#num2 (BAT1) omitted — Macs have one battery. +sketchybar --add item battery right \ + --set battery update_freq=60 icon.drawing=off \ + script="$PLUGIN_DIR/battery.sh" \ + --subscribe battery power_source_change system_woke +add_popup battery + +# idle_inhibitor -> caffeinate +sketchybar --add item caffeinate right \ + --set caffeinate update_freq=15 \ + script="$PLUGIN_DIR/caffeinate.sh" \ + click_script="$PLUGIN_DIR/caffeinate.sh toggle" + +# network +sketchybar --add item network right \ + --set network update_freq=5 icon.drawing=off \ + click_script="open -a 'System Settings' 2>/dev/null" \ + script="$PLUGIN_DIR/network.sh" +add_popup network + +# pulseaudio -> volume (native volume_change event) +sketchybar --add item volume right \ + --set volume icon.drawing=off script="$PLUGIN_DIR/volume.sh" \ + --subscribe volume volume_change mouse.scrolled + +# bluetooth (needs blueutil) +sketchybar --add item bluetooth right \ + --set bluetooth update_freq=30 icon.drawing=off \ + click_script="$PLUGIN_DIR/bluetooth.sh toggle" \ + script="$PLUGIN_DIR/bluetooth.sh" +add_popup bluetooth + +# backlight -> brightness (needs the `brightness` CLI; hides itself if absent) +sketchybar --add item brightness right \ + --set brightness update_freq=10 icon.drawing=off \ + script="$PLUGIN_DIR/brightness.sh" \ + --subscribe brightness mouse.scrolled + +# custom/storage +sketchybar --add item storage right \ + --set storage update_freq=60 icon.drawing=off \ + script="$PLUGIN_DIR/storage.sh" +add_popup storage + +# custom/mail (Mail.app unread) +# sketchybar --add item mail right \ +# --set mail update_freq=60 icon.drawing=off \ +# click_script="open -a Mail" \ +# script="$PLUGIN_DIR/mail.sh" + +# custom/weather (wttr.in j2 -> condition icon + temp, hover popup as a "tooltip") +sketchybar --add item weather right \ + --set weather update_freq=1800 icon.drawing=off \ + click_script="open https://wttr.in" \ + script="$PLUGIN_DIR/weather.sh" +add_popup weather + +# custom/spotify (now playing via native media_change) +# sketchybar --add item spotify right \ +# --set spotify icon=$SPOTIFY icon.color=$TEXT drawing=off \ +# label.max_chars=30 scroll_texts=on \ +# click_script="$PLUGIN_DIR/spotify.sh toggle" \ +# script="$PLUGIN_DIR/spotify.sh" \ +# --subscribe spotify media_change + +# ---------------------------------------------------------------------------- +# Omitted (no SketchyBar / macOS analog): +# tray -> the macOS menu bar already renders status/menu-extra items +# privacy -> macOS shows camera/mic indicators natively in the menu bar +# ---------------------------------------------------------------------------- + +sketchybar --update diff --git a/users/ryan/modules/sketchybar/default.nix b/users/ryan/modules/sketchybar/default.nix new file mode 100644 index 0000000..8179f18 --- /dev/null +++ b/users/ryan/modules/sketchybar/default.nix @@ -0,0 +1,19 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.ryan.sketchybar; +in +{ + options.ryan.sketchybar.enable = mkEnableOption "sketchybar"; + + config = mkIf cfg.enable { + # Enable the service + programs.sketchybar = { + enable = true; + extraPackages = [ pkgs.jq ]; + config = { source = ./bar-config; recursive = true; }; + }; + }; +} -- cgit v1.3