| Age | Commit message (Collapse) | Author | Files | Lines |
|
Cairo's toy text API binds one face with no per-glyph fallback, so an
emoji in a calendar event title rendered as a box in Noto Sans.
text/measure/advance now split strings into runs and draw emoji runs
(U+1F000-1FAFF, U+2600-27BF) in monochrome Noto Emoji, restoring the
caller's face afterwards. Nerd Font PUA icons stay in FONT_MONO.
Plain strings and invalid UTF-8 take the old single-call path.
No shaping: ZWJ sequences and flags draw as their parts. Pango via
lgi (built for Lua 5.4) is the upgrade path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
At 0.1s the per-frame readings (temperatures, RAM, fan RPM) changed
ten times a second and read as jitter. 0.5s keeps the wheel scroll
responsive enough while settling the numbers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Sum drm-resident-vram0 from DRM fdinfo, the same per-client counters
gputop reads, counting each drm-client-id once since a client repeats
its totals on every fd it holds. One grep over /proc/*/fdinfo, about
10ms warm, sampled through new_gate every ~2s.
The 12 GiB total is hardcoded: xe exposes no VRAM capacity in sysfs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Conky gives Lua no way to request one redraw, so a scroll showed up to
2s late. Pointer enter and leave, which Hyprland sends on every toggle
of the special workspace, now switch conky_set_update_interval between
0.1s and the config's 2s.
At that rate the CPU and network counters would sample over 0.1s
windows and the chart would fill 20x faster, so both now sample through
data.new_gate on the /proc/uptime clock, about every 2s, and redraw
their last values in between. The samplers move to texeci: execi runs
in the main loop and a 0.7s nvtable or an Immich download stalled the
scroll.
Measured: about 12% of one core while visible, 0.5% while hidden.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Moves the nvchecker/nvtable table the login shell printed onto the
dashboard, as a wide card at the bottom centre. bin/updates-sample.sh
caches `nvtable -C -E` every ten minutes and starts nvchecker, fully
detached, at most every 30 minutes. The widget parses the cache and
colours it in the board's language: installed behind upstream is
critical, a shipped .info behind is a warning, with nvtable's status
dots per row and a legend on the bottom line. The mouse wheel scrolls
rows that do not fit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A day with no memories left the card showing only a notice. When today
has none, immich-fetch.sh now merges the memories of each of the past
six days, skipping a day whose request fails, and marks the index with
a scope week line. The subtitle names where the photos come from,
Today or This week, before the i/n counter, and the empty notice reads
no memories this week.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The years-ago count read as a bare number unless you read the subtitle.
It now carries a small Y at its top-right, where a degree sign sits on a
temperature, and ago under it on the baseline. The subtitle drops its
now redundant "years ago" and keeps only the i/n counter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A centred card showing today's "N years ago" photos from Immich, one at
a time on a 30-second rotation, with when each was taken and where.
bin/immich-fetch.sh runs from conky's execi every 30 minutes: it asks
/api/memories for today, converts each preview to PNG (Cairo loads
nothing else) and writes an index with the year, local capture time and
place. The place needs the key's asset.read permission and reads '--'
without it. The key lives in ~/.config/udt/immich.env, like the weather
key, because a GPG-backed store would raise a pinentry unattended.
The dashboard now registers lua_mouse_hook and hands pointer events to
the widget under the pointer. On this card, scrolling steps through the
photos and a left click opens the photo in Immich's web UI and hides the
dashboard so the browser is not stuck beneath it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
With khal uninstalled the sampler still wrote a cache with a generated
key and no events, because khal's stderr is discarded. The card then
read "nothing this week" while an event was scheduled. Removing the
cache instead shows the card's "no calendar data" face.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The quickshell keybind reminder lists hyprctl binds -j, which reports
Lua actions as an opaque handle, so a bind shows what it does only
through its description. This was the one bind left undescribed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The Slackware card's big value gave no hint it was the slackpkg ChangeLog
age. Add a small line under the title naming it.
The calendar's weekday initials were English. Use the Italian single-letter
form, matching the card's already-Italian breaktimer labels.
|
|
Add card.ICON and a card.title helper that draws the glyph in the
highlight colour before the label, at the label's own size. The eight
labelled cards use it, in both the fit_unit width measurement and the
draw, so the icon cannot overrun a narrow cell. Clock (no label) and
weather (condition glyph already) are unchanged, and the no-data notice
titles stay text-only.
Codepoints were chosen against a rendered contact sheet, not fontconfig
alone, since a wrong codepoint draws a plausible neighbour.
|
|
Extract card.notice so every cached card's no-data state is one fluid,
theme-consistent face instead of a copy per widget, and delete the
fixed-size card.label it replaces.
Derive weather's arc geometry from its band (pad, curve room, sun dot,
time labels) instead of fixed pixels, and cap the arc at a proportion of
the card's width rather than 240px.
Derive disks' bar-fallback height and its address type from the card's
own label scale.
Standardise the big-value factor to 1.0 across the fit_unit cards and
document the big-value colour rule and the disks headline exception.
|
|
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.
|
|
|
|
The workspace rule pinning special:dash to DP-1 does not work: special
workspaces ignore a workspace_rule's `monitor` and open on whichever
monitor has focus. Verified on 0.56.2; upstream hyprwm/Hyprland#7990 and
#5570, both closed in the issues-to-discussions migration rather than
fixed.
The comment claimed the opposite, which was convincing because the rule
does register and `hyprctl workspacerules` reports it enabled. The rule
stays, so the intent is recorded and the line is ready if upstream fixes
it, and the bind now names the manual step: focus DP-1 first.
Also notes that the window stays on DP-3 once opened there, so a later
toggle is cropped until it is moved back, the conky surface being a
fixed 2560x1080.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Calendar to the middle column, with the clock and weather shifted down
and right to make room for it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Three changes to the calendar card:
The month grid drops further below the header. The weekday initials sat
close enough to read as part of the header rather than as the top of the
grid.
Event rows carry the date before the time, day/month with no year: the
window is a week and the grid above already names the month, so a year
would spend three characters on the one field that cannot vary.
A legend of the calendar colours is pinned along the card's bottom, and
the event rows now end above it rather than at the card's inner edge. It
lists every calendar khal declared a colour for, in name order so it
does not reshuffle between frames, since pairs() walks a hash.
The row budget also counted one row more than the drawing loop would
draw, which suppressed the '+N more' line exactly when it was needed:
the loop cut the row the count had already spent. Both now test the same
condition.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
The month grid with the week's appointments below it, colour-coded by
calendar. Events come from a cache file rather than a live call: khal
costs about 200ms of Python startup, which is two orders of magnitude
over the 2-second draw budget, so bin/calendar-sample.sh writes the
cache on a 5-minute execi and the widget only parses.
Calendar colours are read out of khal's own config by the sampler and
mapped onto the board's palette roles, so the card follows a scheme
change instead of pinning three literal hues.
khal will not emit an ISO date: {start-date} uses the user's dateformat,
which carries no year here, and a strftime spec inside the field raises.
The sampler asks for {start-date-long} and data.calendar_date recovers
the numbers by position, resolving day/month by magnitude and reading a
genuinely ambiguous pair day-first. A 2-digit year is refused rather
than guessed at.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
The two rows name two different pieces of hardware and read as one block in a
single colour. heading and highlight are the palette roles that carry no state
meaning, so they can stand for an identity; ok/warning/critical could not,
since those say how something is doing and these strings never change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Notes the two things a reader would otherwise get wrong: br0 is a bridge, so
local VM traffic shows up in the chart, and the disks card reads df's
available column rather than computing size minus used, which would overstate
free space by the root-reserved blocks.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Placement is provisional: there is no settled arrangement yet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Replaces the percentage, which the ring already draws as an angle. A number
repeating it costs a row in a column about 75px wide.
Free carries the threshold colour because it is the measured quantity; the
total is fixed and stays in the plain value colour, so each column has one
coloured number.
The cache card adopts card.truncate in the same pass: it was slicing names by
byte, which halves a multi-byte character.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Both read once: neither changes without a reboot, and /proc/cpuinfo repeats
its model-name line once per thread.
The two rows come out of the equaliser's height budget, since it is the
card's designated slack absorber. Its floor drops from 18px to zero: with
rows above it the band can genuinely run out of room, and a fixed floor
meant it kept height it did not have and the temperatures drew over it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
The big value is the time since slackpkg's ChangeLog changed, coloured green
under a day, amber to a week, red past it.
A cache with no changelog key is a different failure from no cache at all:
the sampler ran and the ChangeLog is what was missing, so the rows still draw
and only the age reads '--'. Collapsing the two would send the reader to the
wrong problem.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
GNU ls follows a symlink to a directory with or without a trailing slash, so
both forms count the packages correctly. Only -d lists the link itself.
The original claim came from a count taken where the path did not resolve at
all, and it reached the spec, the plan and two commit messages. The count was
always right; the reason given for it was not, and a comment that teaches the
next reader something false is worse than no comment.
Records the real trap in its place: ls aliased to a long format counts its
"total" header as a package.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Conky's Lua has no lfs, so it cannot stat a file, and counting 2800 package
files per draw would be a directory walk every two seconds.
Writes epochs rather than formatted dates. The shell function this replaces
rebuilds a date from ls -l output with the year hardcoded, which breaks every
January and on any file older than six months.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Two lines on one chart against a shared ceiling, one sample per pixel column
so nothing is interpolated. The history is module state sized from the card's
width, so moving the card reframes the window rather than clearing it.
The public address is refused once stale: an address that may no longer be
yours, displayed with confidence, is worse than a dash.
The fixture uses TEST-NET-1. This repository is public.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
The draw hook must never make a network call: a hung request blocks the
Cairo draw and freezes the dashboard. Thirty minutes, not the old config's
five, because a residential address is stable for days.
Validates the response before writing. A captive portal and a rate-limit
message both arrive with a 200 and would otherwise be drawn as an address.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Free space is read rather than derived: size - used overstates it by the
root-reserved blocks, tens of gigabytes on a large filesystem, none of it
available to anyone but root.
Also asserts fs[3], the row whose device name ends in a digit. It sits next
to a numeric column and is the row most likely to break a parser counting
fields, and nothing covered it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
The pattern required a literal CPU token immediately before the @, which the
one Intel fixture happens to carry. A Xeon reads 'E5-2680 v4 @ 2.40GHz' and a
Tiger Lake 'i7-1165G7 @ 2.80GHz', so both kept their clock on the card.
A generation prefix hid the vendor word from its anchor for the same reason:
'11th Gen Intel Core i7-1165G7' kept 'Intel'.
Three more forms added as tests. The Intel fixture was meant to keep the
stripping a rule, and one fixture was not enough to do it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Stripping by rule rather than by a table of known parts: a leading vendor
word, trademark noise, a trailing core count or clock. The Intel fixture
exists to keep it a rule.
DMI placeholders read as no board at all. 'To Be Filled By O.E.M.' on the
dashboard is worse than a blank row.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Only the first space separates, so a value keeps its own spaces. Values stay
strings: the sampler writes epochs and the widget decides whether an age
reads as hours or days.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
The widget passes conky's update_interval rather than letting it default, so
the argument path is the one that ships and nothing exercised it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Mirrors new_cpu_counter: cumulative counters need two samples to yield a
rate, and each direction carries its own previous reading.
Clamps a negative delta to zero rather than letting a 32-bit wrap or an
interface reset produce a spike. One bogus sample sets the shared autoscale
and flattens the entire window.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
Several series on one baseline and one ceiling. The shared ceiling is the
point: scaled independently, a 200kB/s upload draws the same height as a
40MB/s download, which is a lie about a link's shape.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
A ring left its line width behind, so the next stroke on the card inherited
a hairline several pixels thick. It already restored the cap for the same
reason.
card.truncate replaces byte slicing, which cuts a multi-byte character in
half and emits an invalid sequence Cairo draws as a box.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Fifteen tasks, TDD throughout: parsers and primitives first, then the
samplers, then the cards, then the edits to existing widgets.
Verified two assumptions the plan rests on rather than assuming them: Lua
5.4 provides utf8, so card.truncate can cut on a character boundary, and
conky's Cairo bindings export cairo_get_line_width, without which the ring
line-state fix has nothing to restore.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Groups the machine cards into the right-hand columns and leaves the clock
and weather alone on the left, rather than spreading all six across the
middle. Provisional: there is no settled arrangement for the board yet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Two new cards and three fixes to existing ones.
The network card reads the interface byte counters directly and keeps its
own history, one sample per pixel column, so the chart never interpolates.
Both series share one autoscale: drawn independently, a 200kB/s upload would
look like a 40MB/s download. The public address moves from the old config's
5 minute poll to 30 minutes, since a residential address is stable for days.
The slackware card takes its numbers from a sampler rather than the draw
hook, and stores epochs instead of reconstructed dates. The shell function it
replaces parses ls -l output and hardcodes the year, which breaks every
January and on any file older than six months.
Records two things found while checking real values against the design:
/var/log/packages is a symlink, so counting it without a trailing slash
reports 1 package rather than 2817, and the ChangeLog mtime on this machine
currently reads in the future, so the age arithmetic has to survive a
negative difference.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
DESIGN.md states the two rules that carry the board's identity (big value
at the card's top-right, rows label-left/value-right), plus the type,
colour and glyph conventions, with AGENTS.md pointing at it for anyone
adding a widget.
|
|
Mirror the weather header so the glyph sits large on the left and the
temperature, city and condition run against the right edge, and move the
system card's CPU percentage onto its header line, right-aligned. All
big values now render at the card's right edge.
|
|
The label, headline, RAM and temperature rows now derive from one base
size, chosen from the cell height and a measured width fit, and the
per-core equaliser absorbs the height left over. Bigger cell, bigger
type instead of empty space; the size shrinks only on a width clash.
|
|
|
|
The 'GPU' and 'CACHE' labels shared the big number's baseline, so they
read as sitting under it. Move them to the card's top-left, the same
title position the other cards use, level with the top of the value.
|
|
The big value moves to the right of the heading, on the same baseline,
so the card's width carries it instead of leaving the right half empty.
Type now scales to fill the cell: card.fit_unit measures the rows and
returns the largest size that fits the width, and the widget takes the
smaller of that and its height budget, shrinking only where a row would
collide.
|
|
Fix the conky.conf.in comment to name all three samplers and the README
to say the cache sampler runs every 15 minutes. Rewrite the disks layout
comment to match per_row = n (no wrapping), drop the stale "seventeenth
bar" note, and record in the spec that the cache age marker is
deliberately deferred with the epoch-line upgrade path.
|
|
Memoize hwmon_dir by chip name so sensor reads no longer popen grep on
every frame, cache misses included. Floor fractional byte counts in
card.human so it cannot raise. Drop the GPU package-temperature row,
which duplicated the headline. Set umask before mkdir in both samplers
so a fresh cache directory is not world-readable. Make render.lua exit
non-zero on a draw error and tolerate an unset HOME.
|