mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 19:07:04 +01:00
Phase 2 of the leisure rollout, dormant until daily-limits' cache says "leisure_active": true. gaming_budget.sh then labels the block "🛋 Leisure" (short "🛋", same colour logic, stale state keeps the label); leisure_shadow.sh prints nothing so i3bar hides it. A missing, false or non-boolean key keeps today's output. Also: gaming_budget.sh floors generated_at and says "no data" for a non-numeric one; a fractional timestamp used to make it exit 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dsh4Znwh2KnC7q6vbGpX1N
110 lines
3.8 KiB
Bash
Executable File
110 lines
3.8 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# ============================================================================
|
|
# Shadow-week leisure budget for i3blocks: what the leisure budget WOULD say
|
|
# (`🛋 SHADOW 3h12 left · 1h48 used`) while only games are enforced.
|
|
#
|
|
# Renders the `leisure_shadow` part of daily-limits' cache
|
|
# ($XDG_RUNTIME_DIR/daily-limits.json, rewritten every 60 s; contract C5) and
|
|
# owns no accounting. Same shape as gaming_budget.sh: jq re-parses only when
|
|
# the cache's mtime changes, the rendered block is kept in a small file, so a
|
|
# steady-state tick forks nothing. Missing numbers say "⏱? no data", never 0;
|
|
# a >180 s old cache says "stale". Yellow under 30 min, red at would-be 0.
|
|
# Never urgent: it always exits 0 (i3blocks flashes exit 33 as urgent).
|
|
#
|
|
# Phase 2 (leisure rollout): once the cache's top-level `leisure_active` is
|
|
# true the shadow week is over and the block prints nothing (i3bar hides it);
|
|
# gaming_budget.sh shows the real leisure budget instead. A missing/false key
|
|
# keeps the shadow block.
|
|
# ============================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
readonly LIMITS_CACHE="${DAILY_LIMITS_CACHE:-${XDG_RUNTIME_DIR:-/run/user/$UID}/daily-limits.json}"
|
|
readonly RENDER_CACHE="${LEISURE_SHADOW_RENDER_CACHE:-${XDG_RUNTIME_DIR:-/run/user/$UID}/i3blocks-leisure-shadow.render}"
|
|
readonly STALE_AFTER_SECONDS=180
|
|
|
|
# Dracula-ish palette, matching gaming_budget.sh.
|
|
readonly RED='#FF5555'
|
|
readonly YELLOW='#F1FA8C'
|
|
readonly ORANGE='#FFB86C'
|
|
readonly FOREGROUND='#F8F8F2'
|
|
|
|
emit() {
|
|
# full_text, short_text, color — the three lines i3blocks reads.
|
|
printf '%s\n%s\n%s\n' "$1" "$2" "$3"
|
|
}
|
|
|
|
# One jq pass renders four lines: generated_at, full text, short text, color.
|
|
# Once live full/short are empty and color is "hidden" (non-empty, so $(...)
|
|
# cannot strip the lines): the "hidden" render.
|
|
render_shadow() {
|
|
local rendered
|
|
rendered=$(jq -r \
|
|
--arg red "$RED" --arg yellow "$YELLOW" --arg orange "$ORANGE" \
|
|
--arg fg "$FOREGROUND" '
|
|
def hm: . as $m
|
|
| "\($m / 60 | floor)h\($m % 60 | tostring | if length < 2 then "0" + . else . end)";
|
|
(.leisure_shadow // {}) as $s
|
|
| (.generated_at | if type == "number" then floor else "none" end),
|
|
(if .leisure_active == true then
|
|
"", "", "hidden"
|
|
elif $s.used_minutes == null or $s.budget_minutes == null then
|
|
"🛋 SHADOW ⏱? no data", "🛋 ⏱?", $orange
|
|
elif $s.would_block or ($s.left_minutes // 0) <= 0 then
|
|
"🛋 SHADOW WOULD BLOCK · \($s.used_minutes | hm) used", "🛋 WOULD BLOCK", $red
|
|
else
|
|
"🛋 SHADOW \($s.left_minutes | hm) left · \($s.used_minutes | hm) used",
|
|
"🛋 \($s.left_minutes | hm)",
|
|
(if $s.left_minutes < 30 then $yellow else $fg end)
|
|
end)' "$LIMITS_CACHE" 2>/dev/null) || return 1
|
|
printf '%s\n' "$rendered" >"$RENDER_CACHE"
|
|
}
|
|
|
|
if [[ ! -r $LIMITS_CACHE ]]; then
|
|
emit '🛋 SHADOW ⏱? no data' '🛋 ⏱?' "$ORANGE"
|
|
exit 0
|
|
fi
|
|
|
|
if [[ $LIMITS_CACHE -nt $RENDER_CACHE ]]; then
|
|
if ! command -v jq >/dev/null 2>&1; then
|
|
emit '🛋 SHADOW no jq' '🛋 ?' "$ORANGE"
|
|
exit 0
|
|
fi
|
|
render_shadow || {
|
|
emit '🛋 SHADOW unreadable cache' '🛋 !' "$RED"
|
|
exit 0
|
|
}
|
|
fi
|
|
|
|
{
|
|
read -r generated_at
|
|
read -r full_text
|
|
read -r short_text
|
|
read -r color
|
|
} <"$RENDER_CACHE" || {
|
|
emit '🛋 SHADOW unreadable cache' '🛋 !' "$RED"
|
|
exit 0
|
|
}
|
|
|
|
# Hidden (leisure budget live): print nothing, whatever the cache's age.
|
|
[[ -n $full_text ]] || exit 0
|
|
|
|
if [[ -n ${NOW_EPOCH:-} ]]; then
|
|
now=$NOW_EPOCH
|
|
else
|
|
printf -v now '%(%s)T' -1
|
|
fi
|
|
# A cache without a numeric generated_at cannot be dated: say so, never crash.
|
|
if [[ ! $generated_at =~ ^-?[0-9]+$ ]]; then
|
|
emit '🛋 SHADOW ⏱? no data' '🛋 ⏱?' "$ORANGE"
|
|
exit 0
|
|
fi
|
|
age=$((now - generated_at))
|
|
if ((age > STALE_AFTER_SECONDS || age < -STALE_AFTER_SECONDS)); then
|
|
emit "🛋 SHADOW ⏱? stale $((age / 60))m" '🛋 ⏱?' "$ORANGE"
|
|
exit 0
|
|
fi
|
|
|
|
emit "$full_text" "$short_text" "$color"
|