diff --git a/README.md b/README.md index 142546c..b6c253d 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ This is where I tweak config files till 4am like a goblin. It's pretty cozy in h - **[pi](https://github.com/badlogic/pi-mono)** - **[wezterm](https://wezterm.org/index.html)** - **[stow](https://www.gnu.org/software/stow/)** -- **[SwiftBar](https://github.com/swiftbar/SwiftBar)** - **[Tinycast](https://github.com/abue-ammar/tinycast)** - **[yazi](https://yazi-rs.github.io/)** diff --git a/fish/.config/fish/functions/latte.fish b/fish/.config/fish/functions/latte.fish new file mode 100644 index 0000000..60ead69 --- /dev/null +++ b/fish/.config/fish/functions/latte.fish @@ -0,0 +1,128 @@ +# latte: friendlier `caffeinate` wrapper that takes hours or minutes instead +# of seconds. Handy for keeping the Mac awake while long-running agents / +# background jobs do their thing. `latte -a` stays awake only as long as +# coding agents (claude, codex, pi) are running, then exits. +function latte --description 'caffeinate with -h HOURS, -m MINUTES, or -a until coding agents finish' + argparse 'h/hours=' 'm/minutes=' 'a/agents' -- $argv + or return + + set -l seconds + set -l label + + function __latte_plural -S -a n word + if test "$n" = 1 + echo "$n $word" + else + echo "$n "$word"s" + end + end + + function __latte_brew -S -a msg + set_color -o magenta + echo -n "☕ latte" + set_color normal + echo " · $msg" + end + + # Coding agents watched by -a/--agents. pi execs a node script, so its + # process name is `node` — match its command line instead. claude is + # lowercase so this won't match the Claude desktop app (capital C). + function __latte_agents_running -S + pgrep -qx claude; and echo claude + pgrep -qx codex; and echo codex + pgrep -qf pi-coding-agent; and echo pi + end + + # Run caffeinate with the TTY in no-echo, non-canonical mode so stray + # keystrokes in the pane don't corrupt the display or leak into the next + # prompt. isig stays on so Ctrl-C still terminates caffeinate normally. + # Restored via a SIGINT handler so interrupts can't leave the TTY broken. + function __latte_run -S -a mode seconds + set -l tty_saved "" + set -l tty_active false + if isatty stdin + set tty_saved (stty -g 2>/dev/null) + if test -n "$tty_saved" + stty -echo -icanon 2>/dev/null + printf '\e[?25l' + set tty_active true + end + end + function __latte_restore_tty --inherit-variable tty_saved --inherit-variable tty_active + if test "$tty_active" = true + command -q python3; and python3 -c 'import termios,sys; termios.tcflush(sys.stdin, termios.TCIFLUSH)' 2>/dev/null + stty "$tty_saved" 2>/dev/null + printf '\e[?25h' + set tty_active false + end + functions -q __latte_sigint_handler; and functions -e __latte_sigint_handler + end + function __latte_sigint_handler --on-signal SIGINT + __latte_restore_tty + end + switch $mode + case timed + caffeinate -dimsu -t $seconds + case agents + # Poll for agents every 30s; caffeinate -t doubles as the + # sleep so the wake assertion is held between checks. + while test (count (__latte_agents_running)) -gt 0 + caffeinate -dimsu -t 30 + if test $status -gt 128 + break + end + end + case '*' + caffeinate -dimsu + end + set -l rc $status + __latte_restore_tty + return $rc + end + + set -l flag_count 0 + set -q _flag_hours; and set flag_count (math $flag_count + 1) + set -q _flag_minutes; and set flag_count (math $flag_count + 1) + set -q _flag_agents; and set flag_count (math $flag_count + 1) + if test $flag_count -gt 1 + set_color -o red + echo -n "☕ latte" + set_color normal + echo " · pick one: -h/--hours, -m/--minutes, or -a/--agents" >&2 + return 1 + end + + if set -q _flag_agents + set -l running (__latte_agents_running) + if test (count $running) -eq 0 + __latte_brew "no coding agents running — nothing to babysit" + return 0 + end + __latte_brew "keeping macOS awake while agents run: "(string join ', ' $running) + __latte_run agents + set -l rc $status + __latte_brew "all agents done — macOS can nap now" + return $rc + else if set -q _flag_hours + set seconds (math -s0 "$_flag_hours * 3600") + or begin + __latte_brew "invalid hours: $_flag_hours" >&2 + return 1 + end + set label (__latte_plural $_flag_hours hour) + else if set -q _flag_minutes + set seconds (math -s0 "$_flag_minutes * 60") + or begin + __latte_brew "invalid minutes: $_flag_minutes" >&2 + return 1 + end + set label (__latte_plural $_flag_minutes minute) + else + __latte_brew "bottomless cup — keeping macOS awake until ctrl-c" + __latte_run forever + return + end + + __latte_brew "keeping macOS awake for $label — sip slow" + __latte_run timed $seconds +end diff --git a/homebrew/Brewfile b/homebrew/Brewfile index 74961c2..31ff798 100644 --- a/homebrew/Brewfile +++ b/homebrew/Brewfile @@ -44,7 +44,6 @@ if OS.mac? cask "codex" cask "handy" cask "orbstack" - cask "swiftbar" cask "tamnd/tap/kage" cask "tinycast" cask "vlc" diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh index bedecf1..e3649ba 100644 --- a/scripts/bootstrap.sh +++ b/scripts/bootstrap.sh @@ -176,46 +176,6 @@ bootstrap_fish() { ok 'Login shell changed to Fish (takes effect on next login)' fi } -bootstrap_insomnia() { - section 'SwiftBar / Insomnia' - if [[ "$(uname -s)" != Darwin ]]; then warn 'SwiftBar skipped: macOS only'; return; fi - ensure_brew - if ! brew list --cask swiftbar >/dev/null 2>&1; then - run 'Install SwiftBar' brew install --cask swiftbar - else - ok 'SwiftBar already installed' - fi - link_configs swiftbar - local plugins domain plist reload=0 - plugins="$HOME/.config/swiftbar/plugins" - domain="gui/$(id -u)" - plist="$HOME/Library/LaunchAgents/com.wu-json.swiftbar-login.plist" - need defaults 'Run this recipe on macOS.' - need launchctl 'Run this recipe in a macOS desktop session.' - if [[ "$(defaults read com.ameba.SwiftBar PluginDirectory 2>/dev/null || true)" != "$plugins" ]]; then - run 'Set SwiftBar plugin directory' defaults write com.ameba.SwiftBar PluginDirectory -string "$plugins" - reload=1 - else - ok 'SwiftBar plugin directory already configured' - fi - if ! launchctl print "$domain" >/dev/null 2>&1; then - warn 'SwiftBar launch skipped: no GUI session; rerun just bootstrap insomnia from the desktop' - return - fi - if ! launchctl print "$domain/com.wu-json.swiftbar-login" >/dev/null 2>&1; then - run 'Register SwiftBar login agent' launchctl bootstrap "$domain" "$plist" - else - ok 'SwiftBar login agent already registered' - fi - if [[ "$reload" == 1 ]] && pgrep -x SwiftBar >/dev/null 2>&1; then - run 'Quit SwiftBar to apply its plugin directory' osascript -e 'tell application "SwiftBar" to quit' - fi - if ! pgrep -x SwiftBar >/dev/null 2>&1; then - run 'Start SwiftBar' open -a SwiftBar - else - ok 'SwiftBar already running' - fi -} bootstrap_tinycast() { section 'Tinycast' if [[ "$(uname -s)" != Darwin ]]; then warn 'Tinycast skipped: macOS only'; return; fi @@ -292,11 +252,11 @@ bootstrap_tailscale() { require_tool just just case "${1:-all}" in - all) bootstrap_brew; bootstrap_stow; bootstrap_gh; bootstrap_pi; bootstrap_fish; bootstrap_insomnia; bootstrap_tinycast ;; + all) bootstrap_brew; bootstrap_stow; bootstrap_gh; bootstrap_pi; bootstrap_fish; bootstrap_tinycast ;; brew) bootstrap_brew ;; stow) bootstrap_stow ;; gh) bootstrap_gh ;; pi) bootstrap_pi ;; - fish) bootstrap_fish ;; insomnia) bootstrap_insomnia ;; obscura) bootstrap_obscura ;; tailscale) bootstrap_tailscale ;; + fish) bootstrap_fish ;; obscura) bootstrap_obscura ;; tailscale) bootstrap_tailscale ;; tinycast) bootstrap_tinycast ;; - *) fail "Unknown setup target: $1. Use all, brew, stow, gh, pi, fish, insomnia, tinycast, obscura, or tailscale." ;; + *) fail "Unknown setup target: $1. Use all, brew, stow, gh, pi, fish, tinycast, obscura, or tailscale." ;; esac printf '\n' if [[ "$skipped" -gt 0 ]]; then diff --git a/swiftbar/.config/swiftbar/plugins/insomnia.sh b/swiftbar/.config/swiftbar/plugins/insomnia.sh deleted file mode 100755 index 290114f..0000000 --- a/swiftbar/.config/swiftbar/plugins/insomnia.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash -# Insomnia -# Toggle macOS sleep prevention. -# true - -pid_file="$HOME/Library/Caches/insomnia/caffeinate.pid" -legacy_pid_file="$HOME/Library/Caches/latte/caffeinate.pid" -if [[ -f "$legacy_pid_file" && ! -e "$pid_file" ]]; then - mkdir -p "${pid_file%/*}" - mv "$legacy_pid_file" "$pid_file" -fi - -running_pid() { - local command - [[ -f "$pid_file" ]] || return 1 - read -r pid < "$pid_file" - [[ "$pid" =~ ^[0-9]+$ ]] || return 1 - command=$(ps -p "$pid" -o command= 2>/dev/null) || return 1 - [[ "$command" == "/usr/bin/caffeinate -dimsu" ]] && kill -0 "$pid" 2>/dev/null -} - -if [[ "${1:-}" == toggle ]]; then - if running_pid; then - kill "$pid" - rm -f "$pid_file" - else - mkdir -p "${pid_file%/*}" - nohup /usr/bin/caffeinate -dimsu /dev/null 2>&1 & - printf '%s\n' "$!" > "$pid_file" - fi - exit -fi - -if running_pid; then - symbol='eye.fill' - checked=true -else - symbol='eye' - checked=false -fi -printf '| sfimage=%s\n' "$symbol" -echo '---' -printf 'Keep Mac Awake | checked=%s bash="%s" param1=toggle terminal=false refresh=true shortcut=CTRL+CMD+I\n' "$checked" "$0" diff --git a/swiftbar/Library/LaunchAgents/com.wu-json.swiftbar-login.plist b/swiftbar/Library/LaunchAgents/com.wu-json.swiftbar-login.plist deleted file mode 100644 index 61c1fe3..0000000 --- a/swiftbar/Library/LaunchAgents/com.wu-json.swiftbar-login.plist +++ /dev/null @@ -1,16 +0,0 @@ - - - - - Label - com.wu-json.swiftbar-login - ProgramArguments - - /usr/bin/open - -a - /Applications/SwiftBar.app - - RunAtLoad - - - diff --git a/tests/integration.py b/tests/integration.py index 00b9688..fcce4c5 100644 --- a/tests/integration.py +++ b/tests/integration.py @@ -40,7 +40,7 @@ def main(): repo = base / 'repo with spaces' home = base / 'home' home.mkdir() - for directory in ('scripts', 'homebrew', 'fish', 'gh-dash', 'nvim', 'pi', 'wezterm', 'yazi', 'swiftbar', 'tinycast'): + for directory in ('scripts', 'homebrew', 'fish', 'gh-dash', 'nvim', 'pi', 'wezterm', 'yazi', 'tinycast'): shutil.copytree(ROOT / directory, repo / directory, ignore=shutil.ignore_patterns('node_modules', '__pycache__')) shutil.copy2(ROOT / 'justfile', repo / 'justfile')