| Age | Commit message (Collapse) | Author | Files | Lines |
|
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.
|
|
|
|
|
|
disks draws a ring per filesystem from the df sample, with the NFS
server address read from the device field and a labelled-bar fallback
when the rings would be too small to read. cache shows the ~/.cache
total and its four largest children from the du sample.
card.ring now resets the cairo path first: a preceding card.text()
leaves a current point, and cairo_arc() joins to it with a straight
line, so the disks rings were drawn with stray chords. The ring
primitive had no caller until now, which is why it went unnoticed.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Eleven tasks. The palette's warning role first, since everything else
colours against it; then an offscreen render harness, so each later task
can look at its work without an install-restart-toggle cycle; then
parsers, samplers, primitives, the four cards, and the live pass.
Glyphs are taken from the old conky config and re-rendered rather than
guessed. That matters: a guess at the Slackware mark drew a stomach, and
the config had the real codepoint (U+F318) all along, along with the
disk and network-share icons. The icon is the label now, so no mount
name is drawn.
The two NFS shares are told apart by their server's address, read from
the mount's device field at runtime. The repo is public, so the address
belongs on screen and not in a committed file; deriving it also means
the card survives a renumbered LAN. df_parse keeps the device field for
this, and `host` is non-nil exactly for a network mount.
Two traps recorded because both would fail silently: df is aliased to
df -h in the user's shell, so the sampler calls /usr/bin/df -P -B1 by
absolute path or the parser reads '1.6G' where it expects bytes; and the
per-core counters must outlive a frame, since load is a delta and a
counter rebuilt each draw reports nil forever.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Thresholds colour every quantity that has a comfortable range: ok below
the first, warning between, critical above. 25/75 for filesystems, cache
and CPU load; per-sensor pairs for temperatures, because 70C is
unremarkable on a CPU and alarming on an NVMe.
The per-core row becomes an equaliser: sixteen vertical bars rising from
a baseline, coloured by load. Disks become idea2's rings, an icon at the
centre and the arc coloured by usage. Cache highlights its largest
subdirectory.
This needs a `warning` role the dashboard does not currently expose.
udt-palette already resolves it in every scheme, mapping yellow (or
Nord's aurora_yellow) to it globally, and other generators already read
it, so adding it is one word in gen_conky's role tuple plus a
placeholder and a palette line. No scheme file changes.
Three new primitives in lib/card.lua rather than per-widget drawing:
bar, ring, and a threshold helper so all four cards agree on the rule
instead of each re-deriving it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Four cards: CPU/RAM with every temperature, GPU, filesystems, and the
user cache. Three were asked for; cache became its own card because it
is a different refresh cycle and sharing the disks card would have made
the densest card in the dashboard.
Two findings that shaped it, both from probing the hardware rather than
assuming:
- The Arc B580 exposes no utilisation figure. The xe driver has no
gpu_busy_percent, intel_gpu_top refuses the device outright, and
gputop prints per-process rows with ANSI escapes, which is not an
interface to build on. The card shows temperatures and fan speed and
no load bar, rather than inventing a number.
- /data is on a spinning disk with no hwmon chip, so there is no sda
temperature to show. Reading SMART would need smartctl as root and
would spin up a sleeping disk each poll.
Also: the four NFS mounts are two filesystems behind two servers, so
showing all four printed every number twice. One mount per server.
Both new samplers follow the weather fetch's cache-and-read pattern,
which exists precisely so an unreachable NFS server costs a stale cache
instead of freezing the draw hook.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
A finer grid to place cards in smaller increments. The clock takes a
fractional 1.5-column span, which rect_for supports because it is plain
arithmetic with no integer assumption; note that a span of w covers w
cells plus the w-1 gaps between them, so 1.5 is slightly wider than one
and a half bare cells.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
The numeral size was capped at a flat 0.46 of the available height,
which for two baselines spanning 1.70x the size meant they could only
ever fill about four fifths of the band, leaving a void above the date.
The divisor is now derived from the baselines themselves.
The width cap is measured rather than guessed: two Oswald digits are
1.00x the font size wide (at size 100, '00' measures 100.0px and the
widest pair '88' 98.0px), so a width w permits a size of about w, less
a margin at the card edges.
A narrow cell is still width-bound and cannot fill its height. That
slack is now split above and below the numerals instead of collecting
under them, so the pair sits centred in the space it cannot fill.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Horizontally centred in the card; the weekday and date stay against the
left edge at the bottom.
Each line is centred on its own ink width via measure(), not advance():
ink is what centring wants, and Oswald is proportional, so 10 and 05 are
not the same width and a shared measurement would leave one of them
visibly off-centre.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Installs Oswald (OFL-1.1, from googlefonts/OswaldFont) under
~/.fonts/o/Oswald with its license, and points the clock numerals at it
for the squared look the rounded Noto Sans Black did not have.
Per-widget fonts needed no new machinery: card.font() already takes any
family string, so FONT_* are shared defaults rather than a restriction.
FONT_CLOCK joins them, and the weather card keeps FONT_HEAVY, so the two
no longer have to agree.
Cairo still cannot ask for a weight above bold, so a heavier cut is
selected by family name as FONT_HEAVY already did. Worth knowing that
fontconfig substitutes a default for a family it does not recognise,
which is indistinguishable from the font not applying; fc-match tells
you which happened.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Temperature and city stay against the left edge; the glyph moves to the
right edge, so the header reads as a value on one side and its icon on
the other rather than one run of text.
text_right takes the right edge as its x and measures ink, which is what
aligning to an edge wants, as against advance() for stepping a cursor.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
The conky surface is a fixed 2560x1080, because Hyprland's fullscreen
rule resizes the window but not the drawing surface, so on the
1920-wide DP-3 the dashboard would be cropped. A workspace rule keeps it
on the primary monitor.
hl.workspace_rule takes `workspace`, not `match`, and has no `name`
field, unlike hl.window_rule. Both were checked against the runtime,
which rejects an unknown field with "unknown field '<name>'", and
hyprctl workspacerules confirms the rule registers with monitor: DP-1.
NOT fully verified: whether it holds when the toggle is pressed while
DP-3 has focus. One observation suggested the dashboard still followed
focus, but it was taken before the reload had applied the rule, and
focus could not afterwards be moved to DP-3 without a window or the
pointer on it. Every toggle since the reload has opened on DP-1. Worth
confirming after a fresh Hyprland start.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|