Files
omarchy/bin/omarchy-theme-set-herdr-machines
T
David Heinemeier HanssonandClaude Opus 5.5 616feda103 Add opt-in theme sync to herdr machines (#13430)
* Add a theme-set sample hook that mirrors themes to herdr machines

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Mirror theme changes to herdr machines by default

Replaces the sample hook with omarchy-theme-sync, which omarchy-theme-set
starts detached after every theme change, and a theme-sync-off toggle that
stops a machine from both sending and receiving.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Rename omarchy-theme-sync to omarchy-theme-set-herdr-machines

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Label the toggle Herdr Theme Sync and extract its menu guard

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Answer the Herdr Theme Sync menu guard from omarchy-theme-set-herdr-machines

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Sync herdr machines one at a time and drop the dry run

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Check the theme sync toggle under the lock and stop special-casing this machine

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Make herdr theme sync opt-in

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 20:31:02 +02:00

76 lines
3.0 KiB
Bash
Executable File

#!/bin/bash
# omarchy:summary=Mirror the current theme to your herdr machines that run Omarchy
# omarchy:args=[--available]
# omarchy:hidden=true
# omarchy-theme-set runs this in the background after every theme change. Sync is off until
# the herdr-theme-sync toggle is turned on, and a machine only accepts themes with it on too.
# Columns: id, label, SSH target, session, state.
# This machine may be listed too, and it is already on the theme, so it needs no special case.
targets=$(herdr machine list 2>/dev/null | awk -F '\t' '$5 == "enabled" { print $3 }')
# The menu offers the toggle whenever there is a machine to sync to, whether or not sync is on.
if [[ $1 == "--available" ]]; then
[[ -n $targets ]]
exit
fi
# Serialize runs and send whichever theme is current once the lock is held,
# so a slow earlier run can never overwrite a later theme change.
exec 9>"${XDG_RUNTIME_DIR:-/tmp}/omarchy-theme-set-herdr-machines.lock"
flock 9
# A theme that arrived from another machine stops here, so machines never echo it back.
# The toggle is checked once the lock is held, so turning sync off also stops a queued run.
if [[ -n $OMARCHY_THEME_SYNC_FROM ]] || ! omarchy-toggle-enabled herdr-theme-sync; then
exit 0
fi
theme=$(cat "$HOME/.local/state/omarchy/current/theme.name" 2>/dev/null)
# Runs on the remote machine under a login shell, so OMARCHY_PATH and PATH are set there.
# SSH carries no desktop session, so the environment comes from the live Hyprland instance.
# The flag is read directly, so an Omarchy that predates theme sync simply counts as off.
# Braces make bash read the whole script before running it, so nothing it runs can consume the rest.
read -r -d '' REMOTE_SCRIPT <<'EOF'
{
if [[ ! -f ~/.local/state/omarchy/toggles/herdr-theme-sync ]]; then
echo "theme sync is off"
exit
fi
if [[ $(cat ~/.local/state/omarchy/current/theme.name 2>/dev/null) == "$theme" ]]; then
echo "already on $theme"
exit
fi
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
instance=$(hyprctl instances -j 2>/dev/null | jq -r 'max_by(.time) // empty | "\(.instance) \(.wl_socket)"')
if [[ -z $instance ]]; then
echo "no Hyprland session"
exit
fi
read -r HYPRLAND_INSTANCE_SIGNATURE WAYLAND_DISPLAY <<<"$instance"
export HYPRLAND_INSTANCE_SIGNATURE WAYLAND_DISPLAY
export DBUS_SESSION_BUS_ADDRESS="unix:path=$XDG_RUNTIME_DIR/bus"
export OMARCHY_THEME_SYNC_FROM="$from"
if omarchy theme set "$theme" >/dev/null 2>&1; then
echo "set $theme"
else
echo "failed to set $theme"
fi
} </dev/null
EOF
# One machine at a time, so an SSH agent like 1Password asks for approval at most once.
# SSH joins its arguments into a shell command, so the theme travels on stdin as a quoted assignment.
for target in $targets; do
result=$(printf 'theme=%q from=%q\n%s\n' "$theme" "$(hostname)" "$REMOTE_SCRIPT" |
timeout 60 ssh -o BatchMode=yes -o ConnectTimeout=5 "$target" bash -l -s 2>&1 | tail -n 1)
echo "$(date '+%F %T') $target: ${result:-unreachable}"
done