aboutsummaryrefslogtreecommitdiffstats
path: root/widgets
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-18 18:20:27 +0200
committerDanilo M. <danix@danix.xyz>2026-09-18 18:20:27 +0200
commit37cabe2fea2f86d481516aae6ddaeb14bac2e64f (patch)
tree1249429fa55c501f5b6fa74823ce6eba8130ea88 /widgets
parent46d5607764fb29ae5dbbe001519571f934379917 (diff)
downloadconky-theme-udt-37cabe2fea2f86d481516aae6ddaeb14bac2e64f.tar.gz
conky-theme-udt-37cabe2fea2f86d481516aae6ddaeb14bac2e64f.zip
feat: add a breaktimer card
Replaces the Waybar module that used to show breaktimer.sh with a card on the dashboard. The card's big value is the phase countdown, coloured by phase with the same language the Waybar module used, and two rows name the phase and what the countdown leads to. Data comes from bin/breaktimer-sample.sh, run by conky at execi 5, which reads the daemon's runtime files into a cache. The sampler owns the kill -0 liveness check, because conky's Lua cannot run a process and a dead daemon must read as 'fermo' rather than a stale 'running'. Drops outside the work-hours window freeze the countdown while the phase stays 'working'. The sampler detects this from the remain file's age and the card reads it as 'outside working hours', which also covers a hung daemon. The phase words and colour roles live in data.breaktimer_parse so they are unit-tested; the card only draws the result.
Diffstat (limited to 'widgets')
-rw-r--r--widgets/breaktimer.lua91
1 files changed, 91 insertions, 0 deletions
diff --git a/widgets/breaktimer.lua b/widgets/breaktimer.lua
new file mode 100644
index 0000000..698be80
--- /dev/null
+++ b/widgets/breaktimer.lua
@@ -0,0 +1,91 @@
+-- Breaktimer: the phase of breaktimer.sh and the time left in it.
+--
+-- Everything comes from a cache file written by bin/breaktimer-sample.sh:
+-- conky's Lua cannot run a process, so it can neither call `kill -0` to learn
+-- whether the daemon is alive nor ask breaktimer.sh itself. The sampler owns
+-- that; this card owns only how the state looks.
+--
+-- The countdown is the big value, coloured by phase: green for a work block,
+-- blue during a micro-pause, purple during a long one, yellow when paused, and
+-- the dim label colour when the clock is frozen. That is the same phase-to-
+-- colour language the old Waybar module used, mapped onto the palette roles.
+
+local card = require 'lib.card'
+local data = require 'lib.data'
+
+local CACHE = (os.getenv('XDG_CACHE_HOME') or (os.getenv('HOME') .. '/.cache'))
+ .. '/udt/breaktimer.txt'
+
+local M = {}
+
+function M.draw(cr, rect, colors)
+ local inner = card.card(cr, rect, colors)
+ local function clamp(v, lo, hi) return math.max(lo, math.min(hi, v)) end
+ local LABEL_F, BIG_F, ROW_F = 0.30, 0.85, 0.32
+
+ local bt = data.breaktimer_parse(data.slurp(CACHE) or '')
+
+ -- No cache at all: the sampler has not run. Name it, as the slackware card
+ -- does, rather than leaving a blank cell that is indistinguishable from a
+ -- crash. A stopped daemon is NOT this case: the sampler still writes a cache,
+ -- with running no, which the card draws as 'fermo'.
+ if not bt then
+ local S = clamp(inner.h * 0.94 / (1.78 + 2 * 0.72), 10, 72)
+ S = math.min(S, card.fit_unit(cr, inner.w * 0.96, {
+ { { 'BREAKTIMER', card.FONT_MONO, LABEL_F } },
+ { { 'no breaktimer data', card.FONT_UI, 0.36 } },
+ { { 'bin/breaktimer-sample.sh', card.FONT_MONO, ROW_F } },
+ }, 100))
+ card.font(cr, card.FONT_MONO, S * LABEL_F, false)
+ card.rgba(cr, colors.label)
+ card.text(cr, inner.x, inner.y + S * LABEL_F, 'BREAKTIMER')
+ card.font(cr, card.FONT_UI, S * 0.36, true)
+ card.text(cr, inner.x, inner.y + S + S * LABEL_F * 2.6, 'no breaktimer data')
+ card.font(cr, card.FONT_MONO, S * ROW_F, false)
+ card.text(cr, inner.x, inner.y + S + S * LABEL_F * 2.6 + S * 0.72,
+ 'bin/breaktimer-sample.sh')
+ return
+ end
+
+ local rows = {
+ { 'PHASE', bt.phase_label },
+ { 'NEXT', bt.next },
+ }
+
+ -- Fluid type: the height budget grows the content to fill the cell, the
+ -- measured width fit pulls it back where the phase label would overrun.
+ local groups = {
+ { { 'BREAKTIMER', card.FONT_MONO, LABEL_F },
+ { bt.countdown, card.FONT_HEAVY, BIG_F } },
+ }
+ for _, r in ipairs(rows) do
+ groups[#groups + 1] = { { r[1], card.FONT_MONO, ROW_F },
+ { tostring(r[2]), card.FONT_MONO, ROW_F } }
+ end
+ local S = clamp(math.min(inner.h * 0.94 / (1.5 + #rows * 0.66),
+ card.fit_unit(cr, inner.w * 0.96, groups, 100)), 10, 72)
+ local label_size = S * LABEL_F
+ local row_size = S * ROW_F
+
+ card.font(cr, card.FONT_HEAVY, S * BIG_F, false)
+ card.rgba(cr, colors[bt.role] or colors.value)
+ card.text_right(cr, inner.x + inner.w, inner.y + S * BIG_F, bt.countdown)
+
+ card.font(cr, card.FONT_MONO, label_size, false)
+ card.rgba(cr, colors.label)
+ card.text(cr, inner.x, inner.y + label_size, 'BREAKTIMER')
+
+ local ey = inner.y + S * BIG_F + row_size * 1.6
+ local step = row_size * 2.0
+ card.font(cr, card.FONT_MONO, row_size, false)
+ for _, r in ipairs(rows) do
+ if ey + step > inner.y + inner.h then break end
+ card.rgba(cr, colors.label)
+ card.text(cr, inner.x, ey, r[1])
+ card.rgba(cr, colors.value)
+ card.text_right(cr, inner.x + inner.w, ey, tostring(r[2]))
+ ey = ey + step
+ end
+end
+
+return M