aboutsummaryrefslogtreecommitdiffstats
AgeCommit message (Collapse)AuthorFilesLines
2026-09-17docs: correct the claim about counting a symlinked directoryDanilo M.3-13/+25
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>
2026-09-17feat: sample the Slackware facts into a cache fileDanilo M.1-0/+47
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>
2026-09-17feat: add the network cardDanilo M.4-0/+214
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>
2026-09-17feat: sample the public IP into a cache fileDanilo M.1-0/+48
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>
2026-09-17feat: read the available column from dfDanilo M.2-1/+27
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>
2026-09-17fix: strip the clock from every Intel form, not just the fixture'sDanilo M.2-1/+26
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>
2026-09-17feat: parse the CPU and motherboard model stringsDanilo M.4-0/+118
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>
2026-09-17feat: parse the sampler's key/value cache formatDanilo M.3-0/+44
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>
2026-09-17test: cover the rate counter's explicit fallback intervalDanilo M.1-0/+9
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>
2026-09-17feat: add a byte-rate counter for interface statisticsDanilo M.2-0/+68
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>
2026-09-17feat: add card.plot, a shared-scale line chartDanilo M.1-0/+54
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>
2026-09-17fix: restore the line width card.ring sets, add card.truncateDanilo M.2-2/+54
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>
2026-09-17docs: plan the network and slackware widgetsDanilo M.1-0/+2046
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>
2026-09-17feat: move the cards to the right of the screenDanilo M.1-4/+4
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>
2026-09-17docs: design the network and slackware widgetsDanilo M.1-0/+313
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>
2026-09-17docs: record the visual identity in DESIGN.mdDanilo M.3-0/+108
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.
2026-09-17feat: put every card's big number in the same placeDanilo M.3-18/+21
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.
2026-09-17feat: make the system card type fluidDanilo M.1-18/+40
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.
2026-09-17fix: shorten the cpu equaliser bars to 75%Danilo M.1-1/+1
2026-09-17fix: sit the gpu and cache titles level with the big valueDanilo M.2-3/+3
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.
2026-09-17feat: fluid type for the gpu and cache cardsDanilo M.4-41/+132
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.
2026-09-17docs: correct the system-widget comments and defer the age markerDanilo M.5-7/+21
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.
2026-09-17fix: stop the widgets from stalling or raising the drawDanilo M.6-14/+33
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.
2026-09-17docs: document the system widgetsDanilo M.1-0/+94
2026-09-17fix: adjust the system cards against the screenshotsDanilo M.2-4/+4
2026-09-17feat: add the disks and cache cardsDanilo M.4-0/+204
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.
2026-09-17feat: add the GPU cardDanilo M.2-0/+74
2026-09-17feat: add the system card with a per-core equaliserDanilo M.2-0/+112
2026-09-17feat: add bar, vbar, ring and threshold primitivesDanilo M.1-0/+91
2026-09-17feat: add the disks and cache samplersDanilo M.3-1/+74
2026-09-17feat: parse df and du output for the disk and cache cardsDanilo M.4-0/+147
2026-09-17feat: add per-core CPU times and named sensor readsDanilo M.3-0/+101
2026-09-17test: add an offscreen widget render harnessDanilo M.1-0/+87
2026-09-17feat: expose the warning palette roleDanilo M.2-0/+2
2026-09-17docs: add the system widgets implementation planDanilo M.1-0/+1607
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>
2026-09-17docs: give the system widgets a shared colour languageDanilo M.1-12/+87
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>
2026-09-17docs: add the system widgets designDanilo M.1-0/+221
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>
2026-09-17feat: 16x12 grid, clock at 1.5 columnsDanilo M.1-3/+3
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>
2026-09-17fix: close the gap between the clock numerals and the dateDanilo M.1-5/+23
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>
2026-09-17feat: centre the clock numeralsDanilo M.1-2/+9
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>
2026-09-17feat: give the clock its own fontDanilo M.3-2/+24
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>
2026-09-17feat: right-align the condition glyphDanilo M.1-6/+6
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>
2026-09-17feat: pin the dashboard's workspace to DP-1Danilo M.1-0/+13
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>
2026-09-17feat: add restart.shDanilo M.2-4/+58
Editing the layout table or a widget needs a conky restart and nothing else, since the Lua is symlinked and conky never rereads its config. That was three commands to remember, one of them the hyprctl dispatch form that keeps the dashboard from popping open over the screen. The script syntax-checks before killing anything and refuses to restart on a Lua error, naming the file and line. Without that the first sign of a typo is a blank workspace with no message on any stream, which is the single most confusing failure mode this project has. Falls back to a plain exec where Hyprland is not running, and says what to run by hand if conky fails to come up. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17feat: make the clock fill whatever cell it is givenDanilo M.2-10/+58
The numerals scaled off the cell but the date block sat at a fixed drop below them, so a tall cell left a dead band under the date and a short one would have collided. Now the date pins to the bottom edge, the numerals take everything above it, and both type sizes derive from the width, so the slack of a tall cell falls in the middle of the card where it belongs. Type scales off width for the same reason the weather card does: height is what changes when a row is added to the grid, and a widget that shrinks its text whenever its cell gets shorter reads as broken next to its neighbours. Checked at 1x3, 1x6, 2x2, 2x4 and 3x3 on an 8x6 grid before restarting conky. The 1x6 case stays width-bound and leaves room below the numerals, which is correct for a cell far taller than it is wide. The README now states the convention, since it applies to every widget added later: derive offsets from the rect, scale off width, decide where the slack goes, and drop an element that would otherwise overlap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17feat: move to an 8x6 grid, and let the weather card fit any cell heightDanilo M.2-23/+15
The grid was 4x2, which made every placement decision a coarse one. 8x6 gives 299x157 cells, so a card can be sized in something closer to the increments the layout actually wants. Nothing in rect_for needed changing: it derives cell size from COLS and ROWS, so this is a two-number edit. Note that ROWS has to cover the tallest row in use. A widget placed on row 3 of a two-row grid is computed at y=1068 on a 1080px screen and draws off the bottom, silently. The arc band no longer has a minimum height. It had one, and in a cell 40px shorter than the content wanted, the minimum won and pulled the curve up through the stat rows, drawing the sunrise time across the word WIND. It now takes what is genuinely left and is skipped below the height where it could be read, so the same widget composes in a 2x2 or a 2x3 cell without overlapping anything. Checked by rendering the widget offscreen at 2x1, 2x2 and 2x3 before restarting conky. Layout is now a 1x3 clock with a 2x3 weather card beneath it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17docs: document the weather widget and fix the screenshot recipeDanilo M.1-9/+92
Adds a Weather section covering the API key, where it lives and why, the 401-while-activating behaviour a new key shows, the execi refresh, and the by-hand fetch check. Adds five gotchas: execi firing under empty text, the atomic cache write, what OWM actually returns for a bad key and city, the no-raise invariant in lib/weather.lua, and type scaling off width. Also corrects three things that had gone stale: the symlink list omitted bin/, the layout example still showed a single full-height clock, and the test line omitted test_weather.lua. The screenshot recipe was wrong in two ways, both of which bit during this work. It slept a fixed second and raced the compositor, which is how a capture came back showing a terminal instead of the dashboard; it now polls for the workspace switch. And it fed the window's geometry to grim -g, but a window on a hidden special workspace reports a negative Y, so that grabs the wrong region; it now captures the output. Every command in the file was run as written before committing this. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17feat: put the weather card under the clock, scale its type to widthDanilo M.3-16/+21
Placement is column 1 row 2, directly beneath the clock, which drops to a single-row cell to make room. The card's type now scales off the cell WIDTH rather than its height. Scaling off height meant the text shrank every time the cell got shorter, and beside the clock's numerals the card read as a whisper. Width is the stable dimension for this layout and is what constrains the text anyway, since the stat rows run edge to edge. Verified against the live dashboard, not only the offscreen render: the two disagreed about type size precisely because the offscreen harness was cropped to the card and never showed it next to the clock. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17fix: size the weather card to its contentDanilo M.3-21/+57
The first draft placed every band at a fixed pixel offset and let the arc absorb whatever height was left. In a 1x2 cell on this monitor that is 794px of arc: a cathedral arch under a fifth of a card of content, nothing like the mockup it was shaped after. Three changes, all visible in an offscreen render of the real widget: - Type sizes and band offsets scale with the rect, within clamps, so the proportions hold on the 1920 monitor as well as this one. - The arc gets a bounded band that follows the stats instead of being pinned to the bottom, so no dead strip opens between them. - The layout gives the widget a 1x1 cell. Its content is about 350px tall and a full-height cell cannot be filled without inventing data. Found before restarting conky by loading conky's own cairo bindings standalone and rendering the widget to a PNG, which is a far tighter loop than install, restart, switch workspace, screenshot. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17feat: add the weather cardDanilo M.3-2/+183
Draws current conditions plus a sunrise-to-sunset arc in widgets/weather.lua, placed at col 2 in dashboard.lua's layout. Schedules the periodic fetch via execi in conky.conf.in, tying refresh to conky's own lifecycle. The no-data env-file probe now closes its handle explicitly: the branch reruns every draw until a fetch succeeds, so an unclosed io.open there would leak one handle per frame. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>