Share your plugins #12
Replies: 134 comments 425 replies
-
Window TitleDESCRIPTION: This will display the current window's title in your bar. If the window's title is greater than 50 characters it will truncate it, if there it no title available it will fall back to the application name. This was written by @FelixKratz and Modified by me. NOTE: Assuming you are using yabai, you can quickly check window titles and app names of the current window with Updated Configuration: sketchybarrc# E V E N T S
sketchybar -m --add event window_focus \
--add event title_change
# W I N D O W T I T L E
sketchybar -m --add item title left \
--set title script="$HOME/.config/sketchybar/plugins/window_title.sh" \
--subscribe title window_focus front_app_switched space_change title_changewindow_title.sh#!/bin/bash
# W I N D O W T I T L E
WINDOW_TITLE=$(/opt/homebrew/bin/yabai -m query --windows --window | jq -r '.title')
if [[ $WINDOW_TITLE = "" ]]; then
WINDOW_TITLE=$(/opt/homebrew/bin/yabai -m query --windows --window | jq -r '.app')
fi
if [[ ${#WINDOW_TITLE} -gt 50 ]]; then
WINDOW_TITLE=$(echo "$WINDOW_TITLE" | cut -c 1-50)
sketchybar -m --set title label="│ $WINDOW_TITLE"…
exit 0
fi
sketchybar -m --set title label="│ $WINDOW_TITLE"yabairc # S K E T C H Y B A R E V E N T S
yabai -m signal --add event=window_focused action="sketchybar -m --trigger window_focus &> /dev/null"
yabai -m signal --add event=window_title_changed action="sketchybar -m --trigger title_change &> /dev/null" |
Beta Was this translation helpful? Give feedback.
-
24 Hour Change in Bitcoin PriceDESCRIPTION: This will display the 24hr change in price of bitcoin in your bar. sketchybarrcsketchybar -m --add item btc right \
--set btc icon= \
--set btc update_freq=20 \
--set btc script="~/.config/sketchybar/plugins/btc.sh"btc.sh#!/usr/bin/env python3
import requests
import os
response = requests.get('https://api.gemini.com/v1/pricefeed')
jsonResponse = response.json()
for i in jsonResponse:
if i["pair"] == "BTCUSD":
percentChange = str(round((float(i["percentChange24h"]) * 100), 2))
os.system('sketchybar -m --set btc label='+ percentChange + '%')
break |
Beta Was this translation helpful? Give feedback.
-
24 Hour Change in Ethereum PriceDESCRIPTION: This will display the 24hr change in price of Ethereum in your bar. sketchybarrcsketchybarrcsketchybar -m --add item eth right \
--set eth icon=ﲹ \
--set eth update_freq=20 \
--set eth script="~/.config/sketchybar/plugins/eth.sh"eth.sh#!/usr/bin/env python3
import requests
import os
response = requests.get('https://api.gemini.com/v1/pricefeed')
jsonResponse = response.json()
for i in jsonResponse:
if i["pair"] == "BTCUSD":
percentChange = str(round((float(i["percentChange24h"]) * 100), 2))
os.system('sketchybar -m --set eth label='+ percentChange + '%')
break |
Beta Was this translation helpful? Give feedback.
-
Battery StatusDESCRIPTION: This will display your current battery status. NOTE: This could probably be improved on greatly. I am not sure what an appropriate UPDATED: Updated to implement improvements made by ut0mt8 from this comment. sketchybarrcsketchybar -m --add item battery right \
--set battery update_freq=3 \
--set battery script="~/.config/sketchybar/plugins/power.sh" \
--set battery icon=power.sh#!/bin/bash
. ~/.cache/wal/colors.sh
BATT_PERCENT=$(pmset -g batt | grep -Eo "\d+%" | cut -d% -f1)
CHARGING=$(pmset -g batt | grep 'AC Power')
if [[ $CHARGING != "" ]]; then
sketchybar -m --set battery \
icon.color=0xFF5DFE67 \
icon= \
label=$(printf "${BATT_PERCENT}%%")
exit 0
fi
[[ ${BATT_PERCENT} -gt 10 ]] && COLOR=0xFF${color5:1} || COLOR=0xFFFF0000
case ${BATT_PERCENT} in
100) ICON="" ;;
9[0-9]) ICON="" ;;
8[0-9]) ICON="" ;;
7[0-9]) ICON="" ;;
6[0-9]) ICON="" ;;
5[0-9]) ICON="" ;;
4[0-9]) ICON="" ;;
3[0-9]) ICON="" ;;
2[0-9]) ICON="" ;;
1[0-9]) ICON="" ;;
*) ICON=""
esac
sketchybar -m --set battery\
icon.color=$COLOR \
icon=$ICON \
label=$(printf "${BATT_PERCENT}%%") |
Beta Was this translation helpful? Give feedback.
-
Music InformationUpdate History:
DESCRIPTION: This will display information about the current track in the Music.app. It will update based on the NSDistributedNotificationCenter Events sent by the Music app. sketchybarrc# Add event
sketchybar -m --add event song_update com.apple.iTunes.playerInfo
# Add Music Item
sketchybar -m --add item music right \
--set music script="~/.config/sketchybar/scripts/music" \
click_script="~/.config/sketchybar/scripts/music_click" \
label.padding_right=10 \
drawing=off \
--subscribe music song_updatemusic#!/usr/bin/env bash
# FIXME: Running an osascript on an application target opens that app
# This sleep is needed to try and ensure that theres enough time to
# quit the app before the next osascript command is called. I assume
# com.apple.iTunes.playerInfo fires off an event when the player quits
# so it imediately runs before the process is killed
sleep 1
APP_STATE=$(pgrep -x Music)
if [[ ! $APP_STATE ]]; then
sketchybar -m --set music drawing=off
exit 0
fi
PLAYER_STATE=$(osascript -e "tell application \"Music\" to set playerState to (get player state) as text")
if [[ $PLAYER_STATE == "stopped" ]]; then
sketchybar --set music drawing=off
exit 0
fi
title=$(osascript -e 'tell application "Music" to get name of current track')
artist=$(osascript -e 'tell application "Music" to get artist of current track')
# ALBUM=$(osascript -e 'tell application "Music" to get album of current track')
loved=$(osascript -l JavaScript -e "Application('Music').currentTrack().loved()")
if [[ $loved ]]; then
icon=""
fi
if [[ $PLAYER_STATE == "paused" ]]; then
icon=""
fi
if [[ $PLAYER_STATE == "playing" ]]; then
icon=""
fi
if [[ ${#title} -gt 25 ]]; then
TITLE=$(printf "$(echo $title | cut -c 1-25)…")
fi
if [[ ${#artist} -gt 25 ]]; then
ARTIST=$(printf "$(echo $artist | cut -c 1-25)…")
fi
# if [[ ${#ALBUM} -gt 25 ]]; then
# ALBUM=$(printf "$(echo $ALBUM | cut -c 1-12)…")
# fi
sketchybar -m --set music icon="$icon" \
--set music label="${title} x ${artist}" \
--set music drawing=on
music_click#!/usr/bin/env osascript
tell application "Music"
if loved of current track is true then
set loved of current track to false
do shell script "sketchybar -m --set music icon="
else
set loved of current track to true
do shell script "sketchybar -m --set music icon="
end if
end tell
delay 1
do shell script "sh $HOME/.config/sketchybar/scripts/music" |
Beta Was this translation helpful? Give feedback.
-
VPN StatusDESCRIPTION: Shows the vpn your currently connected to if any in your bar. sketchybarrcsketchybar -m --add item vpn right \
--set vpn icon= \
update_freq=5 \
script="~/.config/sketchybar/plugins/vpn.sh"vpn.sh#!/bin/bash
VPN=$(scutil --nc list | grep Connected | sed -E 's/.*"(.*)".*/\1/')
if [[ $VPN != "" ]]; then
sketchybar -m --set vpn icon= \
label="$VPN" \
drawing=on
else
sketchybar -m --set vpn drawing=off
fi |
Beta Was this translation helpful? Give feedback.
-
Mic StatusDESCRIPTION: Check and Mute/Unmute your mic. sketchybarrcsketchybar -m --add item mic right \
sketchybar -m --set mic update_freq=3 \
--set mic script="~/.config/sketchybar/plugins/mic.sh" \
--set mic click_script="~/.config/sketchybar/plugins/mic_click.sh"mic.sh#!/bin/bash
MIC_VOLUME=$(osascript -e 'input volume of (get volume settings)')
if [[ $MIC_VOLUME -eq 0 ]]; then
sketchybar -m --set mic icon=
elif [[ $MIC_VOLUME -gt 0 ]]; then
sketchybar -m --set mic icon=
fi
mic_click.sh#!/bin/bash
MIC_VOLUME=$(osascript -e 'input volume of (get volume settings)')
if [[ $MIC_VOLUME -eq 0 ]]; then
osascript -e 'set volume input volume 25'
sketchybar -m --set mic icon=
elif [[ $MIC_VOLUME -gt 0 ]]; then
osascript -e 'set volume input volume 0'
sketchybar -m --set mic icon=
fi |
Beta Was this translation helpful? Give feedback.
This comment has been hidden.
This comment has been hidden.
-
RemindersDESCRIPTION: Show your reminders. NOTE: This is just a very basic script, but it could be extended to show the reminder, or use a different app like taskwarrior or taskell instead. sketchybarrcsketchybar -m --add item reminders right \
--set reminders update_freq=20 \
--set reminders script="~/.config/sketchybar/plugins/reminders.sh" \
--set reminders click_script="~/.config/sketchybar/plugins/reminders_click.sh"reminders.sh#!/bin/bash
REMINDERS_COUNT=$(osascript -l JavaScript -e "Application('Reminders').lists.byName('📥 Inbox').reminders.whose({completed: false}).name().length")
if [[ $REMINDERS_COUNT -gt 0 ]]; then
sketchybar -m --set reminders icon="" \
--set reminders label="$REMINDERS_COUNT"
else
sketchybar -m --set reminders icon="" \
--set reminders label=""
fireminders_click.sh#!/bin/bash
open -a Reminders |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
This comment has been hidden.
This comment has been hidden.
-
Stack IndicatorDESCRIPTION: Show you Window's Stack Position. NOTE: Occasionally querying yabai can be delayed possibly related to this, which can lead to a number or dot being displayed late and overwriting the actual position in the indicator. sketchybarrc# S T A C K I N D I C A T O R
sketchybar -m --add item stack_sep center \
--add item stack center \
--set stack script="$HOME/.dotfiles/sketchybar/plugins/stack.sh" \
--subscribe stack window_focus front_app_switched space_change title_change \
--set stack_sep drawing=off \
--set stack drawing=off \
--set stack update_freq=0stack.sh#!/bin/bash
# Exit if Not in Stack
CURRENT=$(yabai -m query --windows --window | jq '.["stack-index"]')
if [[ $CURRENT -eq 0 ]]; then
sketchybar -m --set stack label="" \
--set stack_sep drawing=off \
--set stack drawing=off
exit 0
fi
# Use Numbers in place of Dots if the Stack is greater than 10
# Use a larger font for the unicode dots
LAST=$(yabai -m query --windows --window stack.last | jq '.["stack-index"]')
if [[ $LAST -gt 10 ]]; then
sketchybar -m --set stack label.font="Iosevka Nerd Font:Bold:16.0" \
--set stack label=$(printf "[%s/%s]" "$CURRENT" "$LAST") \
--set stack_sep drawing=on \
--set stack drawing=on
exit 0
else
sketchybar -m --set stack label.font="Iosevka Nerd Font:Bold:22.0"
fi
# Create Stack Indicator
declare -a dots=()
for i in $(seq 0 $(expr $LAST - 1))
do
# Theme 1
# if [[ $i -lt $(expr $CURRENT - 1) ]]; then
# dots+="◖"
# elif [[ $i -gt $(expr $CURRENT - 1) ]]; then
# dots+="◗"
# elif [[ $i -eq $(expr $CURRENT - 1) ]]; then
# dots+="●"
# fi
# Theme 2
[[ $( expr $CURRENT - 1) -eq $i ]] && dots+="●" || dots+="○"
done
# Display Indicator
sketchybar -m --set stack label=$(printf "%s" ${dots[@]}) \
--set stack_sep drawing=on \
--set stack drawing=on |
Beta Was this translation helpful? Give feedback.
-
SKHD Modal IndicatorDESCRIPTION: Show the current SKHD Mode NOTES: You can hardcode the sketchybar commands into your skhdrc. I just use a seperate helper script to clean things up. sketchybarrc# M O D A L I N D I C A T O R
sketchybar -m --add item modal left
sketchybar -m --set modal icon=[N]
sketchybar -m --set modal icon_color =0xFF83A1F1skhdrc# M O D E S
:: default : $HOME/.config/bin/helpers -n
:: window @ : $HOME/.config/bin/helpers -w
:: scripts @ : $HOME/.config/bin/helpers -s
# Mode Shortcuts
default, scripts < lcmd - escape ; window
window, scripts < escape ; default
default, window < lctrl - escape ; scriptshelpersfunction normal_mode() {
echo "N O R M A L M O D E";
# C O L O R S
. $HOME/.cache/wal/colors.sh;
# Y A B A I
yabai -m config active_window_border_color 0xff${color5:1};
yabai -m config normal_window_border_color 0xff${color8:1};
yabai -m config insert_feedback_color 0xff${color5:1};
# S K E T C H Y B A R
sketchybar -m --bar color=0xF0${color0:1}
sketchybar -m --default label.color=0xFF${foreground:1}
sketchybar -m --default icon.color=0xFF${color5:1}
sketchybar -m --set modal icon.color=0xFF83A1F1
sketchybar -m --set modal icon="[N]"
}
# W I N D O W M O D E
function window_mode() {
echo "W I N D O W M O D E";
# C O L O R S
. $HOME/.cache/wal/colors.sh;
# Y A B A I
yabai -m config active_window_border_color 0xff${color3:1};
yabai -m config normal_window_border_color 0xff${color3:1};
yabai -m config insert_feedback_color 0xff${color3:1};
# S K E T C H Y B A R
sketchybar -m --bar color=0xF0${color3:1}
sketchybar -m --default label.color=0xFF${foreground:1}
sketchybar -m --default icon.color=0xFF${background:1}
sketchybar -m --set modal icon.color=0xFFA8CD76
sketchybar -m --set modal icon="[W]"
}
# S C R I P T S M O D E
function scripts_mode() {
echo "S C R I P T S M O D E";
# C O L O R S
. $HOME/.cache/wal/colors.sh;
# Y A B A I
yabai -m config active_window_border_color 0xff${color5:1};
yabai -m config normal_window_border_color 0xff${color5:1};
yabai -m config insert_feedback_color 0xff${color5:1};
# S K E T C H Y B A R
sketchybar -m --bar color=0xF0${color5:1}
sketchybar -m --default label.color=0xFF${foreground:1}
sketchybar -m --default icon.color=0xFF${color6:1}
sketchybar -m --set modal icon.color=0xFFF29B9B
sketchybar -m --set modal icon="[S]"
}
|
Beta Was this translation helpful? Give feedback.
-
yabai helperDESCRIPTION: shows the current yabai space, and the mode. Click the menu item to switch between the sketchybarrcyabai.shyabai_click.sh |
Beta Was this translation helpful? Give feedback.
This comment has been hidden.
This comment has been hidden.
-
Aerospace Workspace Status with Multiple Monitors and Hover + Transition EffectsBuilding on top of the multiple Aerospace Tutorials in this thread and some opinionated choices, I customized my aerospace plugin. Features
items/spaces.sh#!/bin/bash
for sid in $(aerospace list-workspaces --all); do
monitor=$(aerospace list-windows --workspace "$sid" --format "%{monitor-appkit-nsscreen-screens-id}")
if [ -z "$monitor" ]; then
monitor="1"
fi
sketchybar --add item space."$sid" left \
--subscribe space."$sid" aerospace_workspace_change display_change system_woke mouse.entered mouse.exited \
--set space."$sid" \
display="$monitor" \
padding_right=0 \
icon="$sid" \
label.padding_right=7 \
icon.padding_left=7 \
icon.padding_right=4 \
background.drawing=on \
label.font="sketchybar-app-font:Regular:16.0" \
background.color="$ACCENT_COLOR" \
icon.color="$BACKGROUND" \
label.color="$BACKGROUND" \
background.corner_radius=5 \
background.height=25 \
label.drawing=on \
click_script="aerospace workspace $sid" \
script="$CONFIG_DIR/plugins/aerospace.sh $sid"
done
plugins/aerospace.sh#!/usr/bin/env bash
# A color config for hover and highlight effects
# Example
# export BAR_COLOR=0x40000000
# export ITEM_BG_COLOR=0xff353c3f
# export ACCENT_COLOR=0xffffffff
# export BACKGROUND=0xff101314
source "$CONFIG_DIR/colors.sh"
FOCUSED_WORKSPACE=$(aerospace list-workspaces --focused --format "%{workspace}")
if [ "$SENDER" == "mouse.entered" ]; then
if [ "$1" = "$FOCUSED_WORKSPACE" ]; then
exit 0
fi
sketchybar --set "$NAME" \
background.drawing=on \
label.color="$BACKGROUND" \
icon.color="$BACKGROUND" \
background.color="$ACCENT_COLOR"
exit 0
fi
if [ "$SENDER" == "mouse.exited" ]; then
if [ "$1" = "$FOCUSED_WORKSPACE" ]; then
exit 0
fi
sketchybar --set "$NAME" \
background.drawing=off \
label.color="$ACCENT_COLOR" \
icon.color="$ACCENT_COLOR" \
background.color="$BAR_COLOR"
exit 0
fi
icons=""
APPS_INFO=$(aerospace list-windows --workspace "$1" --json --format "%{monitor-appkit-nsscreen-screens-id}%{app-name}")
IFS=$'\n'
for sid in $(echo "$APPS_INFO" | jq -r "map ( .\"app-name\" ) | .[]"); do
icons+=$("$CONFIG_DIR/plugins/icon_map_fn.sh" "$sid")
icons+=" "
done
for monitor_id in $(echo "$APPS_INFO" | jq -r "map ( .\"monitor-appkit-nsscreen-screens-id\" ) | .[]"); do
monitor=$monitor_id
done
if [ -z "$monitor" ]; then
monitor="1"
fi
# When icons is empty, set it to " "
if [ -z "$icons" ]; then
if [ "$1" = "$FOCUSED_WORKSPACE" ]; then
sketchybar --animate sin 10 \
--set "$NAME" \
y_offset=10 y_offset=0 \
background.drawing=on
sketchybar --set "$NAME" \
display="$monitor" \
drawing=on \
label="$icons" \
label.color="$BACKGROUND" \
icon.color="$BACKGROUND" \
background.color="$ACCENT_COLOR"
else
sketchybar --set "$NAME" drawing=off
fi
else
if [ "$1" = "$FOCUSED_WORKSPACE" ]; then
sketchybar --animate sin 10 \
--set "$NAME" \
y_offset=10 y_offset=0 \
background.drawing=on
sketchybar --set "$NAME" \
display="$monitor" \
drawing=on \
label="$icons" \
label.color="$BACKGROUND" \
icon.color="$BACKGROUND" \
background.color="$ACCENT_COLOR"
else
sketchybar --set "$NAME" \
display="$monitor" \
drawing=on \
label="$icons" \
background.drawing=off \
label.color="$ACCENT_COLOR" \
icon.color="$ACCENT_COLOR" \
background.color="$BAR_COLOR"
fi
fiMonitor 1Monitor 2When 2 or more apps are in a single workspaceHover and Jump EffectsScreen.Recording.2025-04-28.at.12.55.28.mov |
Beta Was this translation helpful? Give feedback.
-
Youtube MusicPlugin for th-ch/youtube-music. Inspired by the built in plugin in Grsmto/simplebar. Features:
PrerequisitesMake sure the Screen.Recording.2025-04-28.at.13.06.52.movitems/music.sh#!/bin/bash
music=(
script="$PLUGIN_DIR/youtube-music.sh"
click_script="curl -s -X POST 0.0.0.0:26538/api/v1/toggle-play && $PLUGIN_DIR/youtube-music.sh"
label.padding_right=8
label.font="Hack Nerd Font:Bold:17.0"
padding_right=0
icon=
# Pinned it to the primary display since my other monitor is in vertical layout
# display=1
label="Loading…"
background.image.scale=0.9
background.image.corner_radius=8
background.image.border_color="$TRANSPARENT"
background.color="$TRANSPARENT"
icon.padding_left=36
icon.padding_right=8
label.align=left
update_freq=10
label.max_chars=40
scroll_texts=on
)
music_artwork=(
click_script="curl -s -X POST 0.0.0.0:26538/api/v1/toggle-play && $PLUGIN_DIR/youtube-music.sh"
label.padding_right=8
padding_right=16
display=1
label=""
width=40
background.image.scale=0.07
background.image.corner_radius=8
background.image.border_color="$TRANSPARENT"
background.color="$TRANSPARENT"
)
sketchybar \
--add item music center \
--set music "${music[@]}"
sketchybar \
--add item music-artwork center \
--set music-artwork "${music_artwork[@]}"plugins/youtube-music.sh#!/bin/bash
SONG_INFO=$(curl -s 0.0.0.0:26538/api/v1/song-info)
PAUSED="$(echo "$SONG_INFO" | jq -r '.isPaused')"
CURRENT_SONG="$(echo "$SONG_INFO" | jq -r '.title + " - " + .artist')"
ARTWORK="$(echo "$SONG_INFO" | jq -r '.imageSrc')"
ARTWORK_LOCATION="$(curl -O --output-dir "$TMPDIR" -s --remote-name -w "%{filename_effective}" "$ARTWORK")"
if [ "$PAUSED" = true ]; then
ICON=
else
ICON=
fi
sketchybar --set "$NAME" label="$CURRENT_SONG" icon="$ICON" drawing=on
sketchybar --set "$NAME"-artwork background.image="$ARTWORK_LOCATION"Now Playing with Album Art |
Beta Was this translation helpful? Give feedback.
-
Pomodoro TimerA simple, yet customizable Pomodoro timer. To customize the sounds you can play all back with this script ls /System/Library/PrivateFrameworks/ScreenReader.framework/Versions/A/Resources/Sounds/ | awk '{print $1}' | while read sound; do printf "using $sound...\n"; afplay /System/Library/PrivateFrameworks/ScreenReader.framework/Versions/A/Resources/Sounds/$sound; sleep 0.5; doneControls:
SketchyBar item#!/bin/bash
sketchybar --add item timer right \
--set timer label="No Timer" \
icon= \
icon.color=0xFFF9E2AF \
icon.padding_right=6 \
background.drawing=off \
y_offset=1 \
script="PATH/TO/SCRIPT/timer.sh" \
popup.background.corner_radius=10 \
popup.background.color=0xFF1E1E2E \
popup.background.border_width=1 \
popup.background.border_color=0xFF45475A \
--subscribe timer mouse.clicked mouse.entered mouse.exited mouse.exited.global
for timer in "5" "10" "25"; do
sketchybar --add item "timer.${timer}" popup.timer \
--set "timer.${timer}" label="${timer} Minutes" \
padding_left=16 \
padding_right=16 \
click_script="PATH/TO/SCRIPT/timer.sh $((timer * 60)); sketchybar -m --set timer popup.drawing=off"
doneScript#!/bin/bash
SOUNDS_PATH="/System/Library/PrivateFrameworks/ScreenReader.framework/Versions/A/Resources/Sounds/"
COUNTDOWN_PID_FILE="/tmp/sketchybar_timer_pid"
DEFAULT_DURATION=1500 # 25 minutes
__start() {
local name="$1"
local duration="$2"
(
local time_left="$duration"
while [ "$time_left" -gt 0 ]; do
local minutes=$((time_left / 60))
local seconds=$((time_left % 60))
sketchybar --set "$name" label="$(printf "%02d:%02d" "$minutes" "$seconds")"
sleep 1
time_left=$((time_left - 1))
done
afplay "$SOUNDS_PATH/GuideSuccess.aiff"
sketchybar --set "$name" label="Done"
) &
printf "%s\n" "$!" > "$COUNTDOWN_PID_FILE"
}
__stop() {
if [ -f "$COUNTDOWN_PID_FILE" ]; then
if IFS= read -r PID < "$COUNTDOWN_PID_FILE"; then
if ps -p "$PID" > /dev/null 2>&1; then
kill -- "$PID"
fi
fi
rm -f "$COUNTDOWN_PID_FILE"
fi
}
start_countdown() {
__stop
__start "$1" "$2"
afplay "$SOUNDS_PATH/TrackingOn.aiff"
}
stop_countdown() {
__stop
afplay "$SOUNDS_PATH/TrackingOff.aiff"
sketchybar --set "$NAME" label="No Timer"
}
# If script is run directly with a duration argument (e.g. ./timer.sh 300)
if [[ "$#" -eq 1 && "$1" =~ ^[0-9]+$ ]]; then
start_countdown "$(echo "$NAME" | awk -F'.' '{print $1}')" "$1"
exit 0
fi
# Handle SketchyBar mouse events
if [ "$SENDER" = "mouse.clicked" ]; then
case "$BUTTON" in
"left")
start_countdown "$NAME" "$DEFAULT_DURATION"
;;
"right")
stop_countdown
;;
esac
fi
case "$SENDER" in
"mouse.entered")
sketchybar --set "$NAME" popup.drawing=on
;;
"mouse.exited"|"mouse.exited.global")
sketchybar --set "$NAME" popup.drawing=off
;;
esac
|
Beta Was this translation helpful? Give feedback.
-
|
Hi everyone, I’ve been working on the Spotify setup by @FelixKratz and made some tweaks that might be useful:
I hope these improvements help streamline the setup and make it more flexible! IMPORTANT: Big thanks again to Felix for the original template! :)
|
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
Audio Output DisplayDisplays a simple icon based on audio output device. Requires that you have SwitchAudio-OSX installed ( sketchybarrcaudio.sh |
Beta Was this translation helpful? Give feedback.
-
Check for SecureInput being EnabledDisplays a warning alert if SecureInput is Enabled, which disables/overtakes global hotkey use. Main use would be for AeroSpace users, as if this is enabled, many hotkeys won't work (to move windows, spaces, or change focus). The alert displays the PID as well so that you can determine what app is causing it and you can decide if you want to close that app right away or deal with it until you are done with that app. sketchybarrcsecure_input.sh |
Beta Was this translation helpful? Give feedback.
-
|
Cider Now Playing info Requires enabling WebSockets in Cider, and generating a token (or disabling token requirement). |
Beta Was this translation helpful? Give feedback.
-
|
I made several plugins for myself I didn't see solutions for and I think some of it is fairly novel. Hopefully y'all can use / get inspiration from it: repo here |
Beta Was this translation helpful? Give feedback.
-
|
Just a fun little plugin that displays the battery status in a "N64 Zelda" style. the heart containers have 5 states: 0/4, 1/4, 2/4, 3/4 and 4/4, And if the battery is charging, the borders switch from black to white ( like when you have "Enhanced Defense" in-game ). Clicking the bar or hearts displays a little popup menu with actual % and estimated time to fully charged or depleted. Right now im using 3 hearts for the display, but the code Should be able to handle N amounts, but i haven't spent any real time testing it. The code is rough, and im sure there are better solutions out there, especially regarding the images themselves, so any input is welcome:) Requires: SbarLua Pngs for the hearts: Mine CodeCode local colors = require("colors")
local num_hearts = 3
local states_per_heart = 4
local asset_dir = "assets/battery/"
local total_states = num_hearts * states_per_heart
local current_charge = {
percentage = "N/A",
time_remaining = "No estimate",
}
local hearts = {}
local heart_names = {}
for i = num_hearts, 1, -1 do
local name = "battery." .. i
local padding_opts = {}
if i == 1 then
padding_opts.padding_left = 10
elseif i == num_hearts then
padding_opts.padding_right = 10
end
hearts[i] = sbar.add("item", name, {
position = "right",
background = {
color = colors.transparent,
border_color = colors.transparent,
},
padding_left = padding_opts.padding_left,
padding_right = padding_opts.padding_right,
})
table.insert(heart_names, name)
end
local bracket = sbar.add("bracket", heart_names, {
update_freq = 30,
background = {
color = colors.bg1,
border_color = colors.black0,
border_width = 2,
},
})
local popup_item = sbar.add("item", {
position = "popup." .. bracket.name,
icon = {
align = "left",
padding_left = 14,
},
label = {
string = "00:00h",
align = "right",
padding_right = 14,
},
})
local function calculate_heart_states(percentage)
local states = {}
local remaining_health = math.floor(percentage / 100 * total_states)
for i = 1, num_hearts do
local health = math.min(remaining_health, states_per_heart)
states[i] = health
remaining_health = remaining_health - health
end
return states
end
local function update_battery()
sbar.exec("pmset -g batt", function(batt_info)
local charge_str = batt_info:match("(%d+)%%")
if not charge_str then
return
end
local charge = tonumber(charge_str)
current_charge.percentage = charge_str
local _, _, remaining = batt_info:find(" (%d+:%d+) remaining")
current_charge.time_remaining = remaining and (remaining .. "h") or "No estimate"
local heart_states = calculate_heart_states(charge)
local charging_prefix = batt_info:match("AC Power") and "charge_" or ""
for i = 1, num_hearts do
local icon_path = asset_dir .. charging_prefix .. "heart_" .. heart_states[i] .. ".png"
hearts[i]:set({ background = { image = icon_path } })
end
end)
end
local function toggle_popup()
local is_drawing = bracket:query().popup.drawing == "on"
bracket:set({ popup = { drawing = not is_drawing } })
if not is_drawing then
popup_item:set({
icon = current_charge.percentage .. "% | ",
label = "Time remaining: " .. current_charge.time_remaining,
})
end
end
bracket:subscribe({ "routine", "power_source_change", "system_woke" }, update_battery)
bracket:subscribe("mouse.clicked", toggle_popup)
for i = 1, #hearts do
hearts[i]:subscribe("mouse.clicked", toggle_popup)
end
update_battery()
|
Beta Was this translation helpful? Give feedback.
-
Trash indicator
Just a simple script to monitor trash_monitor.cUpdated January 11, 2026 #include <CoreServices/CoreServices.h>
#include <dirent.h>
#include <dispatch/dispatch.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <unistd.h>
// --- Global State ---
static bool g_is_foreground = false;
static FSEventStreamRef g_stream =
NULL; // Global reference to the stream for cleanup
static int g_last_trash_count = -1; // Stores the last known count
static int g_lock_fd = -1; // Lock file descriptor
static const char *LOCK_FILE = "/tmp/trash_monitor.lock";
// --- Single Instance Lock ---
static bool acquire_lock(void) {
g_lock_fd = open(LOCK_FILE, O_CREAT | O_RDWR, 0644);
if (g_lock_fd < 0)
return false;
if (flock(g_lock_fd, LOCK_EX | LOCK_NB) < 0) {
close(g_lock_fd);
g_lock_fd = -1;
return false;
}
ftruncate(g_lock_fd, 0);
dprintf(g_lock_fd, "%d\n", getpid());
return true;
}
static void release_lock(void) {
if (g_lock_fd >= 0) {
flock(g_lock_fd, LOCK_UN);
close(g_lock_fd);
unlink(LOCK_FILE);
g_lock_fd = -1;
}
}
// --- Conditional Logging ---
void log_to_terminal(const char *format, ...) {
if (!g_is_foreground)
return;
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
fflush(stdout);
}
int get_trash_count() {
int count = 0;
const char *home_path = getenv("HOME");
if (!home_path)
return 0;
char trash_path[1024];
snprintf(trash_path, sizeof(trash_path), "%s/.Trash", home_path);
DIR *dir = opendir(trash_path);
if (dir == NULL)
return 0;
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0 &&
strcmp(entry->d_name, ".DS_Store") != 0) {
count++;
}
}
closedir(dir);
return count;
}
void update_sketchybar_trash() {
int count = get_trash_count();
// Only update if the count has changed
if (count == g_last_trash_count) {
log_to_terminal("Trash count unchanged (%d), skipping update.\n", count);
return;
}
g_last_trash_count = count; // Update the last known count
char command[512];
const char *sketchybar_path = "/opt/homebrew/bin/sketchybar";
snprintf(command, sizeof(command),
"'%s' --trigger trash_change TRASH_COUNT=%d", sketchybar_path,
count);
log_to_terminal("Executing command: %s\n", command);
int result = system(command);
if (result != 0) {
log_to_terminal("Command may have failed with exit code %d\n", result);
} else {
log_to_terminal("Command executed.\n");
}
}
void fsevents_callback(ConstFSEventStreamRef streamRef,
void *clientCallBackInfo, size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[]) {
(void)streamRef;
(void)clientCallBackInfo;
(void)numEvents;
(void)eventPaths;
(void)eventFlags;
(void)eventIds;
log_to_terminal(
"FSEvents callback triggered. Checking for trash changes...\n");
update_sketchybar_trash();
}
void signal_handler(int signum) {
log_to_terminal("\nSignal %d received, shutting down...\n", signum);
if (g_stream) {
FSEventStreamStop(g_stream);
FSEventStreamInvalidate(g_stream);
FSEventStreamRelease(g_stream);
}
release_lock();
exit(0);
}
int main(int argc, char **argv) {
// If called with '--count', it prints the number of items and exits.
if (argc > 1 && strcmp(argv[1], "--count") == 0) {
printf("%d", get_trash_count());
return 0;
}
if (!acquire_lock()) {
return 0;
}
g_is_foreground = isatty(STDOUT_FILENO);
signal(SIGINT, signal_handler); // Catch CTRL+C
signal(SIGTERM, signal_handler); // Catch kill command
log_to_terminal("Trash monitor starting up...\n");
const char *home_path = getenv("HOME");
if (!home_path) {
log_to_terminal("FATAL: HOME environment variable not set.\n");
return 1;
}
char trash_path[1024];
snprintf(trash_path, sizeof(trash_path), "%s/.Trash", home_path);
CFStringRef path_to_watch = CFStringCreateWithCString(
kCFAllocatorDefault, trash_path, kCFStringEncodingUTF8);
if (!path_to_watch) {
log_to_terminal("FATAL: Could not create CFString for path.\n");
return 1;
}
CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)&path_to_watch,
1, &kCFTypeArrayCallBacks);
CFRelease(path_to_watch);
if (!pathsToWatch) {
log_to_terminal("FATAL: Could not create CFArray for paths.\n");
return 1;
}
FSEventStreamContext context = {0, NULL, NULL, NULL, NULL};
g_stream = FSEventStreamCreate( // Assign to global stream
kCFAllocatorDefault, fsevents_callback, &context, pathsToWatch,
kFSEventStreamEventIdSinceNow, 1.0, kFSEventStreamCreateFlagFileEvents);
CFRelease(pathsToWatch);
if (!g_stream) {
log_to_terminal("FATAL: Failed to create FSEventStream.\n");
return 1;
}
FSEventStreamSetDispatchQueue(g_stream, dispatch_get_main_queue());
if (!FSEventStreamStart(g_stream)) {
log_to_terminal("FATAL: Failed to start FSEventStream.\n");
FSEventStreamInvalidate(g_stream);
FSEventStreamRelease(g_stream);
return 1;
}
log_to_terminal("Monitoring trash directory: %s\n", trash_path);
update_sketchybar_trash();
dispatch_main();
// Unreachable
return 0;
}
Compile with: clang -Wall -Wextra -O2 -framework CoreServices trash/trash_monitor.c -o trash/trash_monitorHow I use it: trash.lualocal colors = require 'colors'
local settings = require 'settings'
-- Execute the trash_monitor binary which provides the count of items in the trash
sbar.exec '$CONFIG_DIR/trash/trash_monitor &'
local ICON_TRASH_EMPTY = ''
local ICON_TRASH_FULL = ''
local trash = sbar.add('item', 'trash', {
position = 'right',
icon = {
font = {
family = settings.font.numbers,
},
padding_right = 0,
},
label = {
font = {
family = settings.font.text,
},
drawing = false,
},
})
local function update_trash(env)
-- Read the count from the TRASH_COUNT variable sent by trash_monitor
local count = tonumber(env.TRASH_COUNT)
if count == 0 then
-- Trash is empty
trash:set {
icon = {
string = ICON_TRASH_EMPTY,
color = colors.overlay0,
},
label = { drawing = false },
}
else
trash:set {
icon = {
string = ICON_TRASH_FULL,
color = colors.red,
},
label = {
string = tostring(count),
color = colors.red,
drawing = true,
},
}
end
end
trash:subscribe('trash_change', update_trash)
-- Add a click event to open the trash folder
trash:subscribe('mouse.clicked', function()
sbar.exec 'open ~/.Trash'
end)
-- Get the initial state on load/reload
local function get_initial_state()
sbar.exec('$CONFIG_DIR/trash/trash_monitor --count', function(count)
if count then
update_trash { TRASH_COUNT = count }
end
end)
end
get_initial_state() |
Beta Was this translation helpful? Give feedback.
-
(cursed) Hide MacOS Menu Bar and DockIn the past, I've been happy using "auto-hide" for the Apple menu bar and dock. Lately, the liquid glass updates in Tahoe have caused me to try and hide as much of the default MacOS look-and-feel as possible. That includes when my mouse gets too close to the edge of the screen and the menu bar or dock appear when I don't want them. I used to use This hack relies on a heavily modified Warning This hack seems a bit cursed, so use at your own risk menus.cUpdated Jan 11, 2026 #include <Carbon/Carbon.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <unistd.h>
// --- Constants ---
#define STATE_FILE_MENU "/tmp/menubar_state"
#define STATE_FILE_DOCK "/tmp/dock_state"
#define LOCK_FILE "/tmp/uiviz.lock"
#define DAEMON_LOCK_FILE "/tmp/uiviz_daemon.lock"
#define MENU_BAR_LAYER 0x19
#define MAX_BUFFER_SIZE 1024
#define MEDIUM_DELAY_US 100000
#define LONG_DELAY_US 1000000
// --- Global State ---
static bool g_menu_hidden = false;
static bool g_dock_hidden = false;
static volatile sig_atomic_t g_should_exit = 0;
static int g_lock_fd = -1;
static int g_daemon_lock_fd = -1;
// --- Private APIs ---
extern int SLSMainConnectionID();
extern void SLSSetMenuBarVisibilityOverrideOnDisplay(int cid, int did,
bool enabled);
extern void SLSSetMenuBarInsetAndAlpha(int cid, double u1, double u2,
float alpha);
extern void _SLPSGetFrontProcess(ProcessSerialNumber *psn);
extern void SLSGetConnectionIDForPSN(int cid, ProcessSerialNumber *psn,
int *cid_out);
extern void SLSConnectionGetPID(int cid, pid_t *pid_out);
// --- State Management ---
bool read_state(const char *path) {
FILE *file = fopen(path, "r");
if (!file)
return false;
int state = 0;
fscanf(file, "%d", &state);
fclose(file);
return (state == 1);
}
bool write_state(const char *path, bool is_hidden) {
char temp_path[MAX_BUFFER_SIZE];
snprintf(temp_path, sizeof(temp_path), "%s.tmp", path);
FILE *file = fopen(temp_path, "w");
if (!file) {
perror("Cannot write to state file");
return false;
}
fprintf(file, "%d\n", is_hidden ? 1 : 0);
fclose(file);
if (rename(temp_path, path) != 0) {
perror("Cannot update state file");
unlink(temp_path);
return false;
}
return true;
}
// --- File Locking ---
bool acquire_lock(void) {
g_lock_fd = open(LOCK_FILE, O_CREAT | O_RDWR, 0644);
if (g_lock_fd < 0) {
perror("Error opening lock file");
return false;
}
if (flock(g_lock_fd, LOCK_EX | LOCK_NB) < 0) {
if (errno == EWOULDBLOCK) {
fprintf(stderr,
"Error: Lock held by another process. Operation in progress.\n");
} else {
perror("Error acquiring lock");
}
close(g_lock_fd);
g_lock_fd = -1;
return false;
}
return true;
}
void release_lock(void) {
if (g_lock_fd >= 0) {
flock(g_lock_fd, LOCK_UN);
close(g_lock_fd);
g_lock_fd = -1;
}
}
// --- Core Visibility Functions ---
void set_menu_visibility(bool hide) {
g_menu_hidden = hide;
int cid = SLSMainConnectionID();
if (hide) {
// Try the exact approach from working version
SLSSetMenuBarVisibilityOverrideOnDisplay(cid, 0, true);
SLSSetMenuBarInsetAndAlpha(cid, 0, 1, 0.0f);
} else {
SLSSetMenuBarVisibilityOverrideOnDisplay(cid, 0, false);
SLSSetMenuBarInsetAndAlpha(cid, 0, 1, 1.0f);
}
write_state(STATE_FILE_MENU, hide);
}
void set_dock_visibility(bool hide) {
g_dock_hidden = hide;
if (hide) {
// Make dock invisible with very long delay but keep autohide enabled
system("defaults write com.apple.dock autohide -bool true");
system("defaults write com.apple.dock autohide-delay -float 1000");
system("defaults write com.apple.dock autohide-time-modifier -float 0");
system("killall Dock");
} else {
// Instant autohide behavior - immediate response
system("defaults write com.apple.dock autohide -bool true");
system("defaults write com.apple.dock autohide-delay -float 0.0");
system("defaults write com.apple.dock autohide-time-modifier -float 0.1");
system("killall Dock");
usleep(500000); // Give dock time to restart properly
}
write_state(STATE_FILE_DOCK, hide);
}
// --- Accessibility (AX) API Functions ---
void ax_init() {
const void *keys[] = {kAXTrustedCheckOptionPrompt};
const void *values[] = {kCFBooleanTrue};
CFDictionaryRef options = CFDictionaryCreate(
kCFAllocatorDefault, keys, values, 1,
&kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!AXIsProcessTrustedWithOptions(options)) {
fprintf(stderr, "Accessibility permissions required. Please grant them in "
"System Settings.\n");
CFRelease(options);
exit(1);
}
CFRelease(options);
}
AXUIElementRef ax_get_front_app() {
ProcessSerialNumber psn;
_SLPSGetFrontProcess(&psn);
// Use modern API instead of deprecated GetProcessPID
int cid = SLSMainConnectionID();
int target_cid;
SLSGetConnectionIDForPSN(cid, &psn, &target_cid);
pid_t pid;
SLSConnectionGetPID(target_cid, &pid);
return AXUIElementCreateApplication(pid);
}
void ax_perform_click(AXUIElementRef element) {
if (!element)
return;
// Temporarily show menu bar to perform the click
bool menu_was_hidden = read_state(STATE_FILE_MENU);
if (menu_was_hidden) {
set_menu_visibility(false);
usleep(MEDIUM_DELAY_US * 2); // Give UI time to update
}
AXUIElementPerformAction(element, kAXPressAction);
usleep(LONG_DELAY_US); // Wait for menu to open and actions to complete
// Restore original menu bar state
if (menu_was_hidden) {
set_menu_visibility(true);
}
}
CFStringRef ax_get_title(AXUIElementRef element) {
CFTypeRef title = NULL;
if (AXUIElementCopyAttributeValue(element, kAXTitleAttribute, &title) ==
kAXErrorSuccess) {
return (CFStringRef)title;
}
return NULL;
}
void ax_print_menu_options(AXUIElementRef app) {
AXUIElementRef menubar_ref = NULL;
AXUIElementCopyAttributeValue(app, kAXMenuBarAttribute,
(CFTypeRef *)&menubar_ref);
if (!menubar_ref) {
fprintf(stderr, "Failed to get menu bar.\n");
return;
}
CFArrayRef children_ref = NULL;
AXUIElementCopyAttributeValue(menubar_ref, kAXChildrenAttribute,
(CFTypeRef *)&children_ref);
if (!children_ref) {
fprintf(stderr, "Failed to get menu children.\n");
CFRelease(menubar_ref);
return;
}
CFIndex count = CFArrayGetCount(children_ref);
printf("Available menu options:\n");
for (CFIndex i = 0; i < count; ++i) {
AXUIElementRef item =
(AXUIElementRef)CFArrayGetValueAtIndex(children_ref, i);
CFStringRef title = ax_get_title(item);
if (title) {
char buffer[MAX_BUFFER_SIZE];
CFStringGetCString(title, buffer, sizeof(buffer), kCFStringEncodingUTF8);
printf(" [%ld] %s\n", i, buffer);
CFRelease(title);
}
}
CFRelease(children_ref);
CFRelease(menubar_ref);
}
void ax_select_menu_option(AXUIElementRef app, int index) {
AXUIElementRef menubar_ref = NULL;
AXUIElementCopyAttributeValue(app, kAXMenuBarAttribute,
(CFTypeRef *)&menubar_ref);
if (!menubar_ref) {
fprintf(stderr, "Failed to get menu bar for selection.\n");
return;
}
CFArrayRef children_ref = NULL;
AXUIElementCopyAttributeValue(menubar_ref, kAXChildrenAttribute,
(CFTypeRef *)&children_ref);
if (!children_ref) {
fprintf(stderr, "Failed to get menu children for selection.\n");
CFRelease(menubar_ref);
return;
}
if (index >= 0 && index < CFArrayGetCount(children_ref)) {
AXUIElementRef item =
(AXUIElementRef)CFArrayGetValueAtIndex(children_ref, index);
ax_perform_click(item);
} else {
fprintf(stderr, "Error: Menu index %d is out of bounds.\n", index);
}
CFRelease(children_ref);
CFRelease(menubar_ref);
}
// --- Signal Handling & Cleanup ---
void cleanup_and_exit(int sig) {
(void)sig;
g_should_exit = 1;
int cid = SLSMainConnectionID();
printf("\nRestoring UI and exiting...\n");
SLSSetMenuBarVisibilityOverrideOnDisplay(cid, 0, false);
SLSSetMenuBarInsetAndAlpha(cid, 0, 1, 1.0);
system("defaults delete com.apple.dock autohide-delay 2>/dev/null");
system("defaults write com.apple.dock autohide -bool true");
system("killall Dock");
unlink(STATE_FILE_MENU);
unlink(STATE_FILE_DOCK);
if (g_daemon_lock_fd >= 0) {
flock(g_daemon_lock_fd, LOCK_UN);
close(g_daemon_lock_fd);
}
_exit(0);
}
// --- Toggle Functions ---
void toggle_menu() {
if (!acquire_lock())
return;
bool is_currently_hidden = read_state(STATE_FILE_MENU);
set_menu_visibility(!is_currently_hidden);
printf("Menu bar visibility set to: %s\n",
!is_currently_hidden ? "Hidden" : "Visible");
release_lock();
}
void toggle_dock() {
if (!acquire_lock())
return;
bool is_currently_hidden = read_state(STATE_FILE_DOCK);
set_dock_visibility(!is_currently_hidden);
printf("Dock auto-hide set to: %s\n",
!is_currently_hidden ? "Invisible" : "Normal");
release_lock();
}
// --- Daemon ---
static bool acquire_daemon_lock(void) {
g_daemon_lock_fd = open(DAEMON_LOCK_FILE, O_CREAT | O_RDWR, 0644);
if (g_daemon_lock_fd < 0)
return false;
if (flock(g_daemon_lock_fd, LOCK_EX | LOCK_NB) < 0) {
close(g_daemon_lock_fd);
g_daemon_lock_fd = -1;
return false;
}
ftruncate(g_daemon_lock_fd, 0);
dprintf(g_daemon_lock_fd, "%d\n", getpid());
return true;
}
void run_daemon() {
if (!acquire_daemon_lock()) {
return;
}
printf("Starting UI visibility daemon...\n");
printf("Press Ctrl+C to stop and restore UI.\n");
signal(SIGINT, cleanup_and_exit);
signal(SIGTERM, cleanup_and_exit);
// Always start with dock and menu hidden by default
g_menu_hidden = true;
g_dock_hidden = true;
write_state(STATE_FILE_MENU, true);
write_state(STATE_FILE_DOCK, true);
set_menu_visibility(g_menu_hidden);
set_dock_visibility(g_dock_hidden);
while (!g_should_exit) {
sleep(2);
bool menu_state_in_file = read_state(STATE_FILE_MENU);
if (menu_state_in_file != g_menu_hidden)
set_menu_visibility(menu_state_in_file);
bool dock_state_in_file = read_state(STATE_FILE_DOCK);
if (dock_state_in_file != g_dock_hidden)
set_dock_visibility(dock_state_in_file);
int cid = SLSMainConnectionID();
if (g_menu_hidden)
SLSSetMenuBarVisibilityOverrideOnDisplay(cid, 0, true);
}
}
// --- Main Function ---
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "Usage: %s [flag] [argument]\n", argv[0]);
fprintf(stderr, "\n--- Visibility Control ---\n");
fprintf(stderr, " -d Run as a daemon to maintain UI state.\n");
fprintf(stderr, " -t, -tb Toggle BOTH menu bar and Dock.\n");
fprintf(stderr, " -tm Toggle menu bar ONLY.\n");
fprintf(stderr, " -td Toggle Dock ONLY.\n");
fprintf(stderr, "\n--- Menu Interaction ---\n");
fprintf(stderr, " -l List menu options for the frontmost app.\n");
fprintf(stderr, " -s [index] Select a menu option by its index.\n");
return 1;
}
const char *flag = argv[1];
if (strcmp(flag, "-d") == 0) {
run_daemon();
} else if (strcmp(flag, "-t") == 0 || strcmp(flag, "-tb") == 0) {
if (!acquire_lock())
return 1;
bool menu_hidden = read_state(STATE_FILE_MENU);
bool dock_hidden = read_state(STATE_FILE_DOCK);
set_menu_visibility(!menu_hidden);
set_dock_visibility(!dock_hidden);
printf("Menu bar visibility set to: %s\n",
!menu_hidden ? "Hidden" : "Visible");
printf("Dock auto-hide set to: %s\n",
!dock_hidden ? "Invisible" : "Normal");
release_lock();
} else if (strcmp(flag, "-tm") == 0) {
toggle_menu();
} else if (strcmp(flag, "-td") == 0) {
toggle_dock();
} else if (strcmp(flag, "-l") == 0) {
ax_init();
AXUIElementRef app = ax_get_front_app();
if (app) {
ax_print_menu_options(app);
CFRelease(app);
}
} else if (strcmp(flag, "-s") == 0) {
if (argc < 3) {
fprintf(stderr, "Error: -s flag requires an index argument.\n");
return 1;
}
ax_init();
int index = atoi(argv[2]);
AXUIElementRef app = ax_get_front_app();
if (app) {
ax_select_menu_option(app, index);
CFRelease(app);
}
} else {
fprintf(stderr, "Error: Invalid flag '%s'\n", flag);
return 1;
}
return 0;
}Usage: menus -d # Daemon mode; runs in background
menus -t # Toggle both menu bar and Dock
menus -tm # Toggle menu bar only
menus -td # Toggle Dock only
menus -l # List menu options for frontmost app (original functionality)
menus -s [index] # Click menu option by indexCompile with: clang -std=c99 -Wall -Wextra -O2 -F/System/Library/PrivateFrameworks/ -framework Carbon -framework SkyLight menus.c -o menusHow I use it:Within Sketchybar:I start the script in daemon mode with: sbar.exec 'PATH_TO_MENUS/menus -d &'I have a Within Aerospace:I have a "service mode" set of keybinds that includes keys to toggle visibility of the menu bar or dock: [mode.main.binding]
alt-shift-semicolon = [
'exec-and-forget sketchybar --bar color=0x22f9e2af',
'mode service'
]
[mode.service.binding]
m = [
'exec-and-forget PATH_TO_MENUS/menus -tm',
'exec-and-forget sketchybar --bar color=0x00000000',
'mode main',
]
d = [
'exec-and-forget PATH_TO_MENUS/menus -td',
'exec-and-forget sketchybar --bar color=0x00000000',
'mode main',
] |
Beta Was this translation helpful? Give feedback.
-
Apple Music DisplayI don't know why I can't get the information of the song being played after updating my mac mini m4 with macOS Tahoe (26.0.1) using other plugins, so I wrote my own version with the help of ai and tested it to work smoothly on my computer. Feel free to share my plugin or modify it based on mine, good luck! sketchybarrcmusicmusic_clickmusic_monitor_file.swiftCompile in the file directory with: |
Beta Was this translation helpful? Give feedback.
-
Aerospace with App Icon using pure shell scriptBased on @bin101 lua script. As I don't want to install lua extension, I rewrite the plugin in shell script. Kudos to the original author's idea. CustomizationNerd fontIf the icons don't appear, change the font to the nerd font you use in the file App iconYou can add more icons to Codesketchybarrcsource "$ITEM_DIR/space_with_app_icon.sh"icons.sh#!/usr/bin/env sh
ICON_CMD=
ICON_COG= # system settings, system information, tinkertool
ICON_CHART= # activity monitor, btop
ICON_LOCK=
ICONS_SPACE=( )
ICON_APP= # fallback app
ICON_TERM= # fallback terminal app, terminal, warp, iterm2
ICON_PACKAGE= # brew
ICON_DEV= # nvim, xcode, vscode
ICON_FILE= # ranger, finder
ICON_GIT= # lazygit
ICON_LIST= # taskwarrior, taskwarrior-tui, reminders, onenote
ICON_SCREENSAVOR= # unimatrix, pipe.sh
ICON_WEATHER= # weather
ICON_MAIL= # mail, outlook
ICON_CALC= # calculator, numi
ICON_MAP= # maps, find my
ICON_MICROPHONE= # voice memos
ICON_CHAT= # messages, slack, teams, discord, telegram
ICON_VIDEOCHAT= # facetime, zoom, webex
ICON_NOTE= # notes, textedit, stickies, word, bat
ICON_CAMERA= # photo booth
ICON_WEB= # safari, beam, duckduckgo, arc, edge, chrome, firefox
ICON_HOMEAUTOMATION= # home
ICON_MUSIC= # music, spotify
ICON_PODCAST= # podcasts
ICON_PLAY= # tv, quicktime, vlc
ICON_BOOK= # books
ICON_BOOKINFO= # font book, dictionary
ICON_PREVIEW= # screenshot, preview
ICON_PASSKEY= # 1password
ICON_DOWNLOAD= # progressive downloader, transmission
ICON_CAST= # airflow
ICON_TABLE= # excel
ICON_PRESENT= # powerpoint
ICON_CLOUD= # onedrive
ICON_PEN= # curve
ICON_REMOTEDESKTOP= # vmware, utm
ICON_CLOCK= # clock, timewarrior, tty-clock
ICON_CALENDAR= # calendar
ICON_WIFI=
ICON_WIFI_OFF=
ICON_VPN= # vpn, nordvpn
ICONS_VOLUME=( )
ICONS_BATTERY=( )
ICONS_BATTERY_CHARGING=( )
ICON_SWAP=
ICON_RAM=
ICON_DISK= # disk utility
ICON_CPU=
get_app_icon() {
local app="$1"
case "$app" in
"Arc" | "Safari" | "Google Chrome" | "Firefox" | "Brave Browser") echo "$ICON_WEB" ;;
"Code" | "Visual Studio Code" | "VSCodium" | "Xcode" | "Zed") echo "$ICON_DEV" ;;
"Ghostty" | "iTerm2" | "Terminal" | "Warp" | "Alacritty") echo "$ICON_TERM" ;;
"Finder" | "访达") echo "$ICON_FILE" ;;
"Discord" | "Slack" | "Telegram" | "Messages") echo "$ICON_CHAT" ;;
"Spotify" | "Music" | "音乐") echo "$ICON_MUSIC" ;;
"Notes" | "备忘录" | "TextEdit" | "Stickies" | "Microsoft Word") echo "$ICON_NOTE" ;;
"System Settings" | "设置" | "System Information") echo "$ICON_COG" ;;
"Activity Monitor" | "活动监视器") echo "$ICON_CHART" ;;
"Mail" | "邮件" | "Microsoft Outlook") echo "$ICON_MAIL" ;;
"Calendar" | "日历") echo "$ICON_CALENDAR" ;;
"1Password") echo "$ICON_PASSKEY" ;;
*) echo "$ICON_APP" ;; # Fallback
esac
}colors.sh#!/usr/bin/env sh
# Color Palette
BLACK=0xff181926
WHITE=0xffcad3f5
RED=0xffed8796
GREEN=0xffa6da95
BLUE=0xff8aadf4
YELLOW=0xffeed49f
ORANGE=0xfff5a97f
MAGENTA=0xffc6a0f6
GREY=0xff939ab7
TRANSPARENT=0x00000000
BG_INACTIVE=0x603c3e4f
BG_ACTIVE=0x903c3e4f
# General bar colors
BAR_COLOR=0xcc24273a #$GREY # Grey bar
ICON_COLOR=$WHITE # Color of all icons
LABEL_COLOR=$WHITE # Color of all labels
POPUP_BACKGROUND_COLOR=$BLACK
POPUP_BORDER_COLOR=$WHITE
SHADOW_COLOR=$BLACK
# Item specific special colors
SPOTIFY_GREEN=$GREENplugins/update_icons.sh#!/usr/bin/env bash
source "$CONFIG_DIR/icons.sh"
update_icons() {
local workspace_id="$1"
# Fetch window list, extract app name (column 2), and trim whitespace
local windows=$(aerospace list-windows --workspace "$workspace_id" | awk -F '|' '{print $2}' | sed 's/^[ \t]*//;s/[ \t]*$//')
local icon_line=""
if [ -z "$windows" ]; then
icon_line=" —"
else
local unique_apps=$(echo "$windows" | sort -u)
while read -r app; do
[ -z "$app" ] && continue
local icon=$(get_app_icon "$app")
icon_line+="$icon " # Spacing between icons
done <<< "$unique_apps"
fi
sketchybar --animate tanh 10 --set "space.$workspace_id" label="$icon_line"
}plugins/space_event_handler.sh#!/usr/bin/env bash
source "$CONFIG_DIR/colors.sh"
source "$CONFIG_DIR/plugins/update_icons.sh"
CURRENT_WORKSPACE="$1"
# Ensure we know which workspace is focused
if [ -z "$FOCUSED_WORKSPACE" ]; then
FOCUSED_WORKSPACE=$(aerospace list-workspaces --focused)
fi
if [ $CURRENT_WORKSPACE = "$FOCUSED_WORKSPACE" ]; then
sketchybar --set $NAME \
icon.highlight=on \
label.highlight=on \
background.border_color="$YELLOW" \
background.color="$BG_ACTIVE" \
background.border_width=2 \
--set "space.${CURRENT_WORKSPACE}.bracket" \
background.border_color="$YELLOW"
else
sketchybar --set $NAME \
icon.highlight=off \
label.highlight=off \
background.border_color="$GREY" \
background.color="$BG_INACTIVE" \
background.border_width=1 \
--set "space.${CURRENT_WORKSPACE}.bracket" \
background.border_color="$TRANSPARENT"
fi
update_icons "$CURRENT_WORKSPACE"items/space_with_app_icon.sh#!/bin/bash
# 1. Source files for colors
source "$CONFIG_DIR/colors.sh"
source "$CONFIG_DIR/plugins/update_icons.sh"
sketchybar --add event aerospace_workspace_change
monitors=$(aerospace list-monitors | awk -F ' | ' '{print $1}' | sort -n)
for monitor_id in $monitors; do
workspaces=$(aerospace list-workspaces --monitor "$monitor_id" | sort -n)
for workspace_id in $workspaces; do
# Add Space Item
sketchybar --add space "space.$workspace_id" left \
--set "space.$workspace_id" \
space="$monitor_id" \
icon.string="$workspace_id" \
icon.color="$WHITE" \
icon.highlight_color="$YELLOW" \
label.color="$GREY" \
label.highlight_color="$WHITE" \
label.font="UbuntuSans Nerd Font:Regular:16.0" \
label.padding_right=12 \
background.color="$BG_INACTIVE" \
background.border_width=1 \
background.border_color="$GREY" \
background.height=26 \
background.corner_radius=6 \
script="$CONFIG_DIR/plugins/space_event_handler.sh $workspace_id" \
click_script="aerospace workspace $workspace_id" \
--subscribe "space.$workspace_id" aerospace_workspace_change \
--subscribe "space.$workspace_id" space_windows_change
# Add Bracket
sketchybar --add bracket "space.$workspace_id.bracket" "space.$workspace_id" \
--set "space.$workspace_id.bracket" background.border_width=2
# Add Padding
sketchybar --add space "space.padding.$workspace_id" left \
--set "space.padding.$workspace_id" width=10
update_icons "$workspace_id"
done
done
sketchybar --trigger aerospace_workspace_change FOCUSED_WORKSPACE=$(aerospace list-workspaces --focused).aerospace.toml# Notify Sketchybar about workspace change
exec-on-workspace-change = ['/bin/bash', '-c',
'sketchybar --trigger aerospace_workspace_change FOCUSED_WORKSPACE=$AEROSPACE_FOCUSED_WORKSPACE'
] |
Beta Was this translation helpful? Give feedback.
-
|
Just discovered this project and I am really enjoying it. I'm Muslim and so keeping up to date on my prayers (5 times a day) is important to me. So I created this little plugin to help tell me when the next one starts. https://github.com/heyitsaamir/SketchySalatTime
|
Beta Was this translation helpful? Give feedback.




















Uh oh!
There was an error while loading. Please reload this page.
-
This is a thread to share the plugins you've created with the community.
Beta Was this translation helpful? Give feedback.
All reactions