1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
# conky-theme-udt
A fullscreen Conky dashboard for Hyprland, drawn with Cairo from Lua, living on
a special workspace that `SUPER+S` toggles.
Colours come from [unified-desktop-theme](../unified-desktop-theme): this repo
holds `conky.conf.in`, and UDT's `bin/udt-palette` renders it into `conky.conf`
using the current scheme's `[conky]` roles. Switching scheme recolours the
dashboard.
## Install
Rendering and linking are automatic:
cd ../unified-desktop-theme && ./install.sh
That renders `conky.conf` from `conky.conf.in` and symlinks it, along with
`dashboard.lua`, `lib/` and `widgets/`, into `~/.config/conky/`. Re-running it
after a scheme change re-renders the colours.
The Hyprland side is **not** installed by that script and is still yours to do.
Until you do it, the dashboard opens as an ordinary floating window on whatever
workspace is current, with no keybind, because the window rules that place it
are not loaded. Two things are needed, both described in
[`hypr/README.md`](hypr/README.md): symlinking `hypr/dashboard.lua` into
`~/.config/hypr/sections/` and requiring it from `hyprland.lua`, and changing
the bare `hl.exec_cmd("conky")` in `sections/autostart.lua` so it loads this
config. Both are deliberately left as your choice rather than done for you.
## Changing the layout
Edit the `layout` table at the top of `dashboard.lua`. `col`/`row` are grid
cells and `w`/`h` span them; cell size is derived from the screen, so the same
table works on differently shaped monitors.
local layout = {
{ widget = 'clock', col = 1, row = 1, w = 1, h = 2 },
}
Reordering widgets is an edit to that table and nothing else: a widget is handed
a rectangle and draws inside it, so it cannot care where it is. Adding one is a
file in `widgets/` exporting `draw(cr, rect, colors)` plus a row in the table.
`COLS` and `ROWS` above the table set the grid. Cards snap to it; there is no
absolute pixel placement, deliberately, because cells keep aligning when the
grid or the screen changes.
## Writing a widget
A widget module exports one function, `draw(cr, rect, colors)`. `lib/card.lua`
carries the primitives it should use rather than reaching for Cairo directly:
- `card(cr, rect, colors)` draws the rounded card and returns the padded inner
rect to lay out against.
- `font(cr, family, size, bold)` and `rgba(cr, colour)` set the state.
- `text(cr, x, y, s)` draws at a baseline, `text_right(cr, x, y, s)` aligns to a
right edge, `label(cr, x, y, s, colors)` draws a small-caps section label.
- `measure(cr, s)` returns the **ink** size, what the glyphs actually cover. Use
it to centre or right-align.
- `advance(cr, s)` returns the **x_advance**, how far the cursor moves after
drawing. Use it to step along a run of text.
`measure` and `advance` are not interchangeable, and conflating them was a real
bug here: ink width ignores leading and trailing spaces, so `' / '` measures 6px
of ink against a 14px advance, and stepping a cursor by the ink width renders
the date as `16 /SEP /2026`.
## Development
Run it in the foreground to see Lua output, which is the only debugging channel:
conky -c ~/.config/conky/conky.conf
Conky has no hot-reload, so kill any running instance first (`pkill -x conky`)
rather than ending up with two.
Run the parser and layout checks:
lua test/test_data.lua && lua test/test_layout.lua
Screenshot it. `grim` captures screen coordinates, not a window, so the
dashboard's workspace has to be the active one first, and the toggle needs the
Lua dispatch form because this Hyprland config is Lua (the plain
`togglespecialworkspace dash` form fails with `')' expected near 'dash'`):
hyprctl dispatch 'hl.dsp.workspace.toggle_special("dash")'
sleep 1
grim -g "$(hyprctl clients -j | jq -r '.[]|select(.class=="conky-dash")|"\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"')" /tmp/dash.png
hyprctl dispatch 'hl.dsp.workspace.toggle_special("dash")'
Before the Hyprland section is installed there is no `special:dash` to toggle,
so the window is simply wherever it opened and the `grim` line alone captures
it.
## Gotchas worth knowing
**Cairo on Wayland needs `conky_surface()`.** Under `out_to_wayland`,
`conky_window.drawable` and `.visual` are both `nil`, so the
`cairo_xlib_surface_create` idiom used by essentially every Conky-Lua tutorial
cannot work here.
**A Lua error is a blank screen and nothing else.** No stderr, no log. The draw
runs inside `pcall` and paints the caught error on the surface, which is the
only reason a mistake is visible at all.
**`own_window_type` must be `normal`.** A `desktop`-type window is a
layer-surface at level 0 and Hyprland cannot assign it to a workspace.
**Lua cannot read the palette through `conky_parse`.** `conky_parse('${color3}')`
comes back as an **empty string**: conky's colour variables emit renderer escape
codes into the text stream rather than evaluating to hex, and
`${default_shade_color}` is not a variable at all. `conky.config` is not exposed
to Lua either. What Lua does get is `conky_config`, the config file's path, so
`dashboard.lua` opens that file and parses the hex out of it.
**Cairo has no Black font weight.** `CAIRO_FONT_WEIGHT_BOLD` is the maximum, so
the heavy clock numerals ask for the family `Noto Sans Black` at normal weight.
**Sensors are globbed by hwmon `name`, never by index.** Indices drift across
kernel and hardware reorders, and a stale one silently reports a different chip.
## License
GPLv2 only. See `LICENSE`.
## Development Approach
This project is developed using AI-assisted tools. Code is generated with the help of AI based on human-provided specifications, design decisions, and iterative feedback.
All contributions are reviewed, tested, and curated by the maintainer before being included in the codebase. AI is used as a productivity and exploration tool, while human oversight remains central to all decisions.
The goal is to combine the flexibility of AI-assisted development with standard open-source practices such as transparency, review, and accountability.
|