aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CLAUDE.md45
-rw-r--r--README.md134
-rw-r--r--breaktimer.css27
-rwxr-xr-xbreaktimer.sh234
-rw-r--r--waybar-breaktimer.config.jsonc43
-rwxr-xr-xwaybar-breaktimer.sh47
6 files changed, 530 insertions, 0 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..f47ab25
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,45 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## What this is
+
+`breaktimer` is a Bash break-reminder daemon for a Linux/Wayland desktop (dunst + pipewire + Waybar). No build, no package manager, no tests — it's a single shell script plus Waybar integration files. Comments and notification strings are in Italian; keep that convention when editing.
+
+## Running
+
+```bash
+./breaktimer.sh start|stop|restart|pause|resume|toggle|status
+./breaktimer.sh run # internal: the daemon loop, not called directly
+```
+
+Dependencies: `dunst` (notify-send), `pipewire` (pw-play, paplay fallback), coreutils.
+
+## Architecture
+
+State machine: `working -> (notify) -> breaking | longbreak -> working`. Every `LONG_EVERY` (4th) work block triggers a long break instead of a micro-break.
+
+**Time model is the core invariant** (breaktimer.sh:8-10): the daemon persists REMAINING seconds of the current phase, not absolute timestamps. Each `TICK` (5s) it decrements REMAINING *only while working/counting*. Consequences:
+- Manual pause (`paused` state) freezes the countdown — tick `continue`s without decrementing.
+- Outside the `WORK_START`..`WORK_STOP` window, the `working` phase freezes (breaks still count down).
+- Breaks never erode work time.
+
+Any change to the loop must preserve "freeze = don't decrement REMAINING" so the Waybar countdown stays coherent.
+
+### Daemon mechanics
+- `start_daemon` forks via `setsid "$0" run`. It does **not** write the PID itself — `$!` after a `setsid &` is not the loop's real PID. Instead the loop writes its own `$$` to the PID file, and `start_daemon` polls `is_running` until it appears (then reports success/failure).
+- **Singleton guard**: `run_loop` refuses to start (exit 1) if the PID file names a *live* process other than itself (`kill -0`). This stops a stray `breaktimer.sh run` from clobbering a running daemon's shared state files.
+- All state lives in files under `$XDG_RUNTIME_DIR` (falls back to `/tmp`): `breaktimer.pid`, `.state` (running|paused), `.phase` (working|breaking|longbreak|stopped), `.remain` (seconds). Subcommands like `pause`/`toggle` work by writing these files; the running loop reads them each tick. This file-based IPC is how the CLI talks to the daemon.
+- **TERM/INT `cleanup` trap deliberately does NOT delete the PID file.** Bash defers a trap until the current `sleep` returns (up to `TICK`=5s), so on `restart` the *old* daemon's trap fires ~5s after the *new* one already wrote its PID — deleting it there would orphan the new daemon. A stale PID file is harmless because `is_running` uses `kill -0`; the next `start` overwrites it. The trap only writes `stopped` and removes `.remain`.
+
+### Config
+Tunables are top-of-file variables in breaktimer.sh: timing (`MICRO_MIN`, `BREAK_MIN`, `LONG_MIN`, `LONG_EVERY`), work window (`WORK_START`, `WORK_STOP`), urgency levels, and sounds.
+
+**Sounds**: three events — micro-pause, long-pause, back-to-work — each with a `SOUND_*` user override and a `SYS_SOUND_*` default. Defaults point at `$SOUND_DIR` (`~/.local/share/sounds/modern-minimal-ui-sounds/stereo`): `message-new-instant.oga` / `alarm-clock-elapsed.oga` / `service-login.oga`. `sound_for_{micro,long,back}` use the override if set and the file exists, else the default. Playback via `pw-play`, falling back to `paplay`. Empty/missing file = silent (no error).
+
+## Waybar integration
+
+- `waybar-breaktimer.config.jsonc` — the `custom/breaktimer` module: polls every 5s, left-click `toggle`, right-click `restart`, expects `return-type: json`.
+- `breaktimer.css` — phase-colored styling for `#custom-breaktimer.{working,breaking,longbreak,paused,stopped}` (Catppuccin colors).
+
+- `waybar-breaktimer.sh` — JSON status emitter the Waybar `exec` calls. Reads the `.phase`/`.remain`/`.state` files (no time recomputation — the daemon already froze `.remain`), maps phase -> CSS `class`, formats `.remain` as `m:ss`, and prints `{text, class, tooltip}`. Nerd Font glyphs in `text`. Returns the `stopped` class when the daemon isn't running.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..fe59bfd
--- /dev/null
+++ b/README.md
@@ -0,0 +1,134 @@
+# breaktimer
+
+Break reminder for Linux/Wayland. A small Bash daemon that nudges you to take
+micro-pauses while working at the PC, with desktop notifications, sounds, and a
+[Waybar](https://github.com/Alexays/Waybar) module showing a live countdown.
+
+Notifications and tooltips are in Italian.
+
+## How it works
+
+State machine: **working → micro-pause → working**, and every 4th block a
+**long pause** instead. The countdown freezes when you pause manually and
+outside your work-hours window, so breaks never eat into work time and the
+Waybar number stays honest.
+
+| Phase | Default | Notification |
+|-----------|---------|--------------------------|
+| working | 30 min | — |
+| breaking | 3 min | "🚶 Micro-pausa" |
+| longbreak | 10 min | "⏸️ Pausa lunga" (every 4th) |
+
+## Dependencies
+
+- `dunst` (or any `notify-send` provider)
+- `pipewire` — `pw-play`, falls back to `paplay` (PulseAudio)
+- coreutils, Bash
+- [Waybar](https://github.com/Alexays/Waybar) (optional, for the bar module)
+- A Nerd Font for the Waybar glyphs (optional)
+
+## Install
+
+The Waybar config calls the scripts from `~/bin`. Put both there and make them
+executable:
+
+```bash
+mkdir -p ~/bin
+cp breaktimer.sh waybar-breaktimer.sh ~/bin/
+chmod +x ~/bin/breaktimer.sh ~/bin/waybar-breaktimer.sh
+```
+
+Make sure `~/bin` is on your `PATH` (or call the scripts by full path).
+
+### Sounds
+
+Defaults use the [Modern Minimal UI](https://github.com/cadecomposer/modern-minimal-ui-sounds) sound set at:
+
+```
+~/.local/share/sounds/modern-minimal-ui-sounds/stereo/
+```
+
+Three events map to `message-new-instant.oga` (micro), `alarm-clock-elapsed.oga`
+(long), `service-login.oga` (back to work). Don't have that set? Either install
+it there, or edit the `SOUND_DIR` / `SYS_SOUND_*` variables at the top of
+`breaktimer.sh` to point at any `.oga`/`.wav` you like (e.g. the freedesktop
+sounds in `/usr/share/sounds/freedesktop/stereo/`). A missing file is simply
+silent — no error.
+
+## Usage
+
+```bash
+breaktimer.sh start # start the daemon in the background
+breaktimer.sh stop # stop it
+breaktimer.sh restart # stop + start
+breaktimer.sh pause # freeze the countdown
+breaktimer.sh resume # unfreeze
+breaktimer.sh toggle # pause/resume in one command
+breaktimer.sh status # print state, phase, seconds remaining
+```
+
+(`breaktimer.sh run` is the internal loop — don't call it directly; it will
+refuse if a daemon is already running.)
+
+Auto-start on login by adding `breaktimer.sh start` to your compositor's
+autostart (e.g. Hyprland `exec-once`, Sway `exec`).
+
+## Configuration
+
+Edit the variables at the top of `breaktimer.sh`:
+
+| Variable | Meaning |
+|-------------------------------|-------------------------------------------|
+| `MICRO_MIN` / `BREAK_MIN` | work block / micro-pause length (minutes) |
+| `LONG_MIN` / `LONG_EVERY` | long-pause length / every N-th block |
+| `WORK_START` / `WORK_STOP` | work-hours window (`HH:MM`); outside it the work countdown freezes |
+| `URGENCY_MICRO` / `URGENCY_LONG` | dunst urgency (`low`/`normal`/`critical`) |
+| `SOUND_DIR`, `SOUND_*`, `SYS_SOUND_*` | sound files (see above) |
+
+## Waybar integration
+
+Three pieces:
+
+- **`waybar-breaktimer.sh`** — emits JSON (`{text, class, tooltip}`) that Waybar
+ renders. Reads the daemon's state files; no recalculation.
+- **`waybar-breaktimer.config.jsonc`** — the `custom/breaktimer` module.
+- **`breaktimer.css`** — phase colors (Catppuccin).
+
+### 1. Add the module
+
+Paste the inner block of `waybar-breaktimer.config.jsonc` into your
+`~/.config/waybar/config` modules, then add `"custom/breaktimer"` to one of your
+`modules-left/center/right` arrays:
+
+```jsonc
+"custom/breaktimer": {
+ "exec": "~/bin/waybar-breaktimer.sh",
+ "return-type": "json",
+ "interval": 5,
+ "on-click": "~/bin/breaktimer.sh toggle", // left-click: pause/resume
+ "on-click-right": "~/bin/breaktimer.sh restart", // right-click: restart
+ "tooltip": true
+}
+```
+
+### 2. Add the styling
+
+Append `breaktimer.css` to `~/.config/waybar/style.css`. It colors the module by
+phase:
+
+| Class | Color | Meaning |
+|-------------|--------|-------------------|
+| `working` | green | working |
+| `breaking` | blue | micro-pause |
+| `longbreak` | purple | long pause |
+| `paused` | yellow | manually paused |
+| `stopped` | grey | daemon not running |
+
+### 3. Reload
+
+```bash
+breaktimer.sh start
+killall -SIGUSR2 waybar # reload Waybar
+```
+
+Left-click the module to pause/resume, right-click to restart.
diff --git a/breaktimer.css b/breaktimer.css
new file mode 100644
index 0000000..8446531
--- /dev/null
+++ b/breaktimer.css
@@ -0,0 +1,27 @@
+/* breaktimer.css - incolla queste righe nel tuo ~/.config/waybar/style.css */
+/* Niente commenti // (GTK CSS accetta solo la sintassi slash-star). */
+
+#custom-breaktimer {
+ padding: 0 10px;
+ margin: 0 2px;
+}
+
+#custom-breaktimer.working {
+ color: #a6e3a1;
+}
+
+#custom-breaktimer.breaking {
+ color: #89b4fa;
+}
+
+#custom-breaktimer.longbreak {
+ color: #cba6f7;
+}
+
+#custom-breaktimer.paused {
+ color: #f9e2af;
+}
+
+#custom-breaktimer.stopped {
+ color: #6c7086;
+}
diff --git a/breaktimer.sh b/breaktimer.sh
new file mode 100755
index 0000000..483f0b4
--- /dev/null
+++ b/breaktimer.sh
@@ -0,0 +1,234 @@
+#!/bin/bash
+#
+# breaktimer.sh - reminder di micro-pause per spezzare le sessioni al PC
+#
+# Macchina a stati: working -> (notifica) -> breaking/longbreak -> working
+# La pausa NON erode il tempo di lavoro. Pausa manuale congela il countdown.
+#
+# Modello tempo: si salva REMAINING (secondi rimanenti della fase corrente),
+# decrementato a ogni tick solo quando si lavora/conta. Cosi' la pausa manuale
+# congela davvero e il countdown Waybar e' sempre coerente.
+#
+# Uso: breaktimer.sh start|stop|restart|pause|resume|toggle|status
+# breaktimer.sh run (interno)
+#
+# Dipendenze: dunst (notify-send), pipewire (pw-play / paplay), coreutils.
+
+# ---------- Configurazione ----------
+MICRO_MIN=30
+BREAK_MIN=3
+LONG_MIN=10
+LONG_EVERY=4
+WORK_START="09:00"
+WORK_STOP="18:30"
+URGENCY_MICRO="normal"
+URGENCY_LONG="critical"
+
+SOUND_DIR="$HOME/.local/share/sounds/modern-minimal-ui-sounds/stereo"
+SOUND_MICRO=""
+SOUND_LONG=""
+SOUND_BACK=""
+SYS_SOUND_MICRO="$SOUND_DIR/message-new-instant.oga"
+SYS_SOUND_LONG="$SOUND_DIR/alarm-clock-elapsed.oga"
+SYS_SOUND_BACK="$SOUND_DIR/service-login.oga"
+# ------------------------------------
+
+RUNTIME="${XDG_RUNTIME_DIR:-/tmp}"
+PID_FILE="$RUNTIME/breaktimer.pid"
+STATE_FILE="$RUNTIME/breaktimer.state" # running|paused
+PHASE_FILE="$RUNTIME/breaktimer.phase" # working|breaking|longbreak
+REMAIN_FILE="$RUNTIME/breaktimer.remain" # secondi rimanenti della fase
+TICK=5
+
+now_epoch() { date +%s; }
+hm_to_epoch() { date -d "$(date +%F) $1" +%s; }
+
+in_work_window() {
+ local n start stop
+ n=$(now_epoch); start=$(hm_to_epoch "$WORK_START"); stop=$(hm_to_epoch "$WORK_STOP")
+ [ "$n" -ge "$start" ] && [ "$n" -lt "$stop" ]
+}
+
+is_running() {
+ [ -f "$PID_FILE" ] && kill -0 "$(cat "$PID_FILE" 2>/dev/null)" 2>/dev/null
+}
+
+play_sound() {
+ local file="$1"
+ { [ -z "$file" ] || [ ! -f "$file" ]; } && return 0
+ if command -v pw-play >/dev/null 2>&1; then pw-play "$file" >/dev/null 2>&1 &
+ elif command -v paplay >/dev/null 2>&1; then paplay "$file" >/dev/null 2>&1 &
+ fi
+}
+sound_for_micro() {
+ if [ -n "$SOUND_MICRO" ] && [ -f "$SOUND_MICRO" ]; then play_sound "$SOUND_MICRO"
+ else play_sound "$SYS_SOUND_MICRO"; fi
+}
+sound_for_long() {
+ if [ -n "$SOUND_LONG" ] && [ -f "$SOUND_LONG" ]; then play_sound "$SOUND_LONG"
+ else play_sound "$SYS_SOUND_LONG"; fi
+}
+sound_for_back() {
+ if [ -n "$SOUND_BACK" ] && [ -f "$SOUND_BACK" ]; then play_sound "$SOUND_BACK"
+ else play_sound "$SYS_SOUND_BACK"; fi
+}
+
+notify_micro() {
+ notify-send -u "$URGENCY_MICRO" -a "breaktimer" \
+ "🚶 Micro-pausa ($BREAK_MIN min)" \
+ "Alzati e muoviti: camminata, squat, mobilita'. Occhi: guarda lontano 20s."
+ sound_for_micro
+}
+notify_long() {
+ notify-send -u "$URGENCY_LONG" -a "breaktimer" \
+ "⏸️ Pausa lunga ($LONG_MIN min)" \
+ "Stacca davvero. Cammina, bevi, allunga la schiena."
+ sound_for_long
+}
+notify_back() {
+ notify-send -u "low" -a "breaktimer" \
+ "▶️ Si riparte" "Pausa finita, blocco di lavoro da $MICRO_MIN min."
+ sound_for_back
+}
+
+# ---------- Loop principale ----------
+run_loop() {
+ # singleton: rifiuta se un altro daemon vivo possiede gia' il PID file.
+ # Evita due loop che si calpestano gli stessi file di stato.
+ local owner
+ owner="$(cat "$PID_FILE" 2>/dev/null)"
+ if [ -n "$owner" ] && [ "$owner" != "$$" ] && kill -0 "$owner" 2>/dev/null; then
+ echo "breaktimer: gia' in esecuzione (PID $owner), run annullato" >&2
+ exit 1
+ fi
+ echo "$$" > "$PID_FILE"
+ echo "running" > "$STATE_FILE"
+ echo "working" > "$PHASE_FILE"
+ local count=0
+ local work_sec=$((MICRO_MIN * 60))
+ local break_sec=$((BREAK_MIN * 60))
+ local long_sec=$((LONG_MIN * 60))
+ local remaining=$work_sec
+ echo "$remaining" > "$REMAIN_FILE"
+
+ # cleanup: NON tocca PID_FILE. Un vecchio daemon (ucciso da restart) puo'
+ # eseguire il trap in ritardo, dopo che il nuovo ha gia' scritto il suo PID:
+ # cancellare il file qui ucciderebbe il riferimento al nuovo daemon.
+ # is_running usa kill -0, quindi un PID file stantio e' innocuo; lo
+ # sovrascrive il prossimo start.
+ cleanup() {
+ echo "stopped" > "$STATE_FILE"; echo "stopped" > "$PHASE_FILE"
+ rm -f "$REMAIN_FILE"
+ exit 0
+ }
+ trap cleanup TERM INT
+
+ while true; do
+ sleep "$TICK"
+
+ # pausa manuale: NON decrementare, lascia remaining congelato
+ [ "$(cat "$STATE_FILE" 2>/dev/null)" = "paused" ] && continue
+
+ local phase="$(cat "$PHASE_FILE" 2>/dev/null)"
+
+ # fuori finestra oraria, solo in 'working': congela
+ if [ "$phase" = "working" ] && ! in_work_window; then
+ continue
+ fi
+
+ # decrementa il tempo rimanente della fase
+ remaining=$(( remaining - TICK ))
+
+ if [ "$remaining" -gt 0 ]; then
+ echo "$remaining" > "$REMAIN_FILE"
+ continue
+ fi
+
+ # fase scaduta: transizione
+ case "$phase" in
+ working)
+ count=$((count + 1))
+ if [ "$((count % LONG_EVERY))" -eq 0 ]; then
+ notify_long
+ echo "longbreak" > "$PHASE_FILE"
+ remaining=$long_sec
+ else
+ notify_micro
+ echo "breaking" > "$PHASE_FILE"
+ remaining=$break_sec
+ fi
+ ;;
+ breaking|longbreak)
+ notify_back
+ echo "working" > "$PHASE_FILE"
+ remaining=$work_sec
+ ;;
+ *)
+ echo "working" > "$PHASE_FILE"
+ remaining=$work_sec
+ ;;
+ esac
+ echo "$remaining" > "$REMAIN_FILE"
+ done
+}
+
+# ---------- Gestione daemon ----------
+start_daemon() {
+ if is_running; then
+ echo "breaktimer: gia' in esecuzione (PID $(cat "$PID_FILE"))"
+ return 1
+ fi
+ # il loop scrive da solo il proprio PID ($$) dopo il guard singleton:
+ # setsid fa fork, quindi $! qui non e' il PID reale del loop.
+ setsid "$0" run >/dev/null 2>&1 < /dev/null &
+ # attendi che il loop pubblichi il suo PID
+ for _ in 1 2 3 4 5 6 7 8 9 10; do
+ is_running && break
+ sleep 0.2
+ done
+ if is_running; then
+ echo "breaktimer: avviato in background (PID $(cat "$PID_FILE"))"
+ else
+ echo "breaktimer: avvio fallito" >&2
+ return 1
+ fi
+}
+stop_daemon() {
+ if is_running; then
+ kill -TERM "$(cat "$PID_FILE")" 2>/dev/null
+ rm -f "$PID_FILE" "$REMAIN_FILE"
+ echo "stopped" > "$STATE_FILE"; echo "stopped" > "$PHASE_FILE"
+ echo "breaktimer: fermato"
+ else
+ echo "stopped" > "$STATE_FILE"; echo "stopped" > "$PHASE_FILE"
+ rm -f "$PID_FILE" "$REMAIN_FILE"
+ echo "breaktimer: non era in esecuzione"
+ fi
+}
+
+case "$1" in
+ start) start_daemon ;;
+ stop) stop_daemon ;;
+ restart) stop_daemon; sleep 1; start_daemon ;;
+ run) run_loop ;;
+ pause) echo "paused" > "$STATE_FILE"; echo "breaktimer: sospeso" ;;
+ resume) echo "running" > "$STATE_FILE"; echo "breaktimer: ripreso" ;;
+ toggle)
+ if [ "$(cat "$STATE_FILE" 2>/dev/null)" = "paused" ]; then
+ echo "running" > "$STATE_FILE"; echo "breaktimer: ripreso"
+ else
+ echo "paused" > "$STATE_FILE"; echo "breaktimer: sospeso"
+ fi
+ ;;
+ status)
+ if is_running; then
+ echo "breaktimer: attivo (PID $(cat "$PID_FILE")), stato: $(cat "$STATE_FILE" 2>/dev/null), fase: $(cat "$PHASE_FILE" 2>/dev/null), rimane: $(cat "$REMAIN_FILE" 2>/dev/null)s"
+ else
+ echo "breaktimer: non in esecuzione"
+ fi
+ ;;
+ *)
+ echo "uso: $0 [start|stop|restart|pause|resume|toggle|status]"
+ exit 1
+ ;;
+esac
diff --git a/waybar-breaktimer.config.jsonc b/waybar-breaktimer.config.jsonc
new file mode 100644
index 0000000..a86bf5e
--- /dev/null
+++ b/waybar-breaktimer.config.jsonc
@@ -0,0 +1,43 @@
+// ============================================================
+// CONFIG WAYBAR - modulo breaktimer
+// IMPORTANTE: se lo salvi come file separato da includere, le
+// graffe esterne { } DEVONO esserci (vedi sotto). Se invece lo
+// incolli dentro il tuo config principale, togli le graffe esterne
+// e metti solo la riga "custom/breaktimer": { ... } tra i moduli.
+// ============================================================
+
+{
+ "custom/breaktimer": {
+ "exec": "~/bin/waybar-breaktimer.sh",
+ "return-type": "json",
+ "interval": 5,
+ "on-click": "~/bin/breaktimer.sh toggle",
+ "on-click-right": "~/bin/breaktimer.sh restart",
+ "tooltip": true
+ }
+}
+
+/* ============================================================
+ CSS (aggiungi a ~/.config/waybar/style.css)
+ Adatta i colori ai tuoi token.
+ ============================================================
+
+#custom-breaktimer {
+ padding: 0 10px;
+ margin: 0 2px;
+ border-radius: 6px;
+}
+
+#custom-breaktimer.working { color: #a6e3a1; } // verde: stai lavorando
+#custom-breaktimer.breaking { color: #89b4fa; } // blu: micro-pausa in corso
+#custom-breaktimer.longbreak { color: #cba6f7; } // viola: pausa lunga
+#custom-breaktimer.paused { color: #f9e2af; } // giallo: pausa manuale
+#custom-breaktimer.stopped { color: #6c7086; } // grigio: fermo
+
+// opzionale: lampeggio durante la pausa per attirare l'occhio
+#custom-breaktimer.breaking,
+#custom-breaktimer.longbreak {
+ // animation: blink 1s steps(2) infinite;
+}
+
+ ============================================================ */
diff --git a/waybar-breaktimer.sh b/waybar-breaktimer.sh
new file mode 100755
index 0000000..b445893
--- /dev/null
+++ b/waybar-breaktimer.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+#
+# waybar-breaktimer.sh - modulo custom Waybar per breaktimer
+# Legge il tempo rimanente gia' congelato dal daemon (no calcolo next-now).
+
+RUNTIME="${XDG_RUNTIME_DIR:-/tmp}"
+PID_FILE="$RUNTIME/breaktimer.pid"
+STATE_FILE="$RUNTIME/breaktimer.state"
+PHASE_FILE="$RUNTIME/breaktimer.phase"
+REMAIN_FILE="$RUNTIME/breaktimer.remain"
+
+is_running() {
+ [ -f "$PID_FILE" ] && kill -0 "$(cat "$PID_FILE" 2>/dev/null)" 2>/dev/null
+}
+
+if ! is_running; then
+ printf '{"text":"󰒲","tooltip":"breaktimer: fermo (click dx per avviare)","class":"stopped"}\n'
+ exit 0
+fi
+
+state="$(cat "$STATE_FILE" 2>/dev/null)"
+phase="$(cat "$PHASE_FILE" 2>/dev/null)"
+
+rem="$(cat "$REMAIN_FILE" 2>/dev/null)"
+[ -z "$rem" ] && rem=0
+[ "$rem" -lt 0 ] && rem=0
+countdown=$(printf '%d:%02d' $(( rem / 60 )) $(( rem % 60 )))
+
+if [ "$state" = "paused" ]; then
+ printf '{"text":"󰏤 %s","tooltip":"breaktimer in pausa (click sx per riprendere)","class":"paused"}\n' "$countdown"
+ exit 0
+fi
+
+case "$phase" in
+ working)
+ printf '{"text":"󰐊 %s","tooltip":"lavoro: pausa tra %s (sx: pausa, dx: stop/restart)","class":"working"}\n' "$countdown" "$countdown"
+ ;;
+ breaking)
+ printf '{"text":"󰗽 %s","tooltip":"micro-pausa: muoviti! ripresa tra %s","class":"breaking"}\n' "$countdown" "$countdown"
+ ;;
+ longbreak)
+ printf '{"text":"󰒲 %s","tooltip":"pausa lunga: stacca! ripresa tra %s","class":"longbreak"}\n' "$countdown" "$countdown"
+ ;;
+ *)
+ printf '{"text":"󰐊 %s","tooltip":"breaktimer attivo","class":"working"}\n' "$countdown"
+ ;;
+esac