From a5c2a0534a08424734201628411809a081d0c5f7 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Sun, 18 Oct 2026 14:32:57 +0200 Subject: feat(hourly): next-24-hours card adapted from Conky-Mirage Co-Authored-By: Claude Sonnet 5.5 --- widgets/hourly.lua | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 widgets/hourly.lua diff --git a/widgets/hourly.lua b/widgets/hourly.lua new file mode 100644 index 0000000..9566823 --- /dev/null +++ b/widgets/hourly.lua @@ -0,0 +1,93 @@ +-- Hourly: the next 24 hours as a temperature curve over rain-chance bars. +-- +-- Adapted from the Conky-Mirage theme's hourly tile by alexsson-xexpanderx +-- (see lib/weather.lua), redrawn in this repo's palette, glyphs and fonts. + +local card = require 'lib.card' +local data = require 'lib.data' +local weather = require 'lib.weather' + +local M = {} + +-- The band, top to bottom: glyph line, temperature line, curve, hour line. +-- Every third hour is labelled; all 24 shape the curve. +local function draw_curve(cr, x, y, w, h, colors, hours, S) + local n = #hours + local label = math.max(9, math.min(S * 0.30, h * 0.11)) + local glyph = label * 1.5 + local slot = w / n + local function px(i) return x + (i - 0.5) * slot end + local top = y + glyph + label * 1.6 + local base = y + h - label * 1.6 + if base - top < label * 2 then return end -- no room: no curve beats a broken one + + local lo, hi = math.huge, -math.huge + for _, hr in ipairs(hours) do + if hr.temp then lo, hi = math.min(lo, hr.temp), math.max(hi, hr.temp) end + end + if lo > hi then return end + -- A flat day still needs a span to divide by; 1 degree keeps it mid-band. + local span = math.max(hi - lo, 1) + local function py(t) return base - (base - top) * (0.15 + 0.7 * (t - lo) / span) end + + -- Rain chance as bars rising from the baseline, under the curve. + card.rgba(cr, colors.rule, 0.5) + for i, hr in ipairs(hours) do + if hr.precip and hr.precip > 0 then + local bh = (base - top) * 0.5 * hr.precip / 100 + cairo_rectangle(cr, px(i) - slot * 0.3, base - bh, slot * 0.6, bh) + end + end + cairo_fill(cr) + + card.rgba(cr, colors.rule, 0.6) + cairo_set_line_width(cr, 1) + cairo_move_to(cr, x, base) + cairo_line_to(cr, x + w, base) + cairo_stroke(cr) + + -- The curve, broken where an hour has no temperature rather than bridged. + card.rgba(cr, colors.highlight) + cairo_set_line_width(cr, math.max(1.5, label * 0.15)) + local pen = false + for i, hr in ipairs(hours) do + if hr.temp then + if pen then cairo_line_to(cr, px(i), py(hr.temp)) + else cairo_move_to(cr, px(i), py(hr.temp)) end + pen = true + else + pen = false + end + end + cairo_stroke(cr) + + for i = 1, n, 3 do + local hr = hours[i] + card.font(cr, card.FONT_MONO, glyph, false) + card.rgba(cr, colors.highlight) + card.text_centre(cr, px(i), y + glyph, weather.icon(hr.code, hr.is_day)) + card.font(cr, card.FONT_MONO, label, false) + card.rgba(cr, colors.value) + card.text_centre(cr, px(i), y + glyph + label * 1.3, weather.deg(hr.temp)) + card.rgba(cr, colors.label) + card.text_centre(cr, px(i), y + h - label * 0.2, + hr.t and os.date('%H', hr.t) or '--') + end +end + +function M.draw(cr, rect, colors) + local inner = card.card(cr, rect, colors) + local w = weather.parse(data.slurp(weather.CACHE)) + if not w then + card.notice(cr, inner, colors, 'NEXT 24H', 'no weather data', 'bin/weather-fetch.sh') + return + end + -- hours[1] is the current hour, so the big value is the one after it. + local y, S = card.standard(cr, inner, colors, { + icon = card.ICON.hourly, title = 'NEXT 24H', + big = weather.deg((w.hours[2] or {}).temp), share = 0.3, + }) + draw_curve(cr, inner.x, y, inner.w, inner.y + inner.h - y, colors, w.hours, S) +end + +return M -- cgit v1.2.3