dunst: make the font size and width settings, defaulting to today's values (hq ADR 0262)

The laptop's 242 DPI panel shows Inter 10 at less than half the workstation's size. The
defaults keep every machine's file byte for byte as it is; a machine sets its own.
This commit is contained in:
jochen
2026-10-08 17:02:43 +02:00
parent bf542128ee
commit 4c34836037
5 changed files with 82 additions and 15 deletions
+18
View File
@@ -39,6 +39,24 @@ the account is not logged in.
| `dunst_rules` | the rules the running notifier holds, and the files they come from |
| `dunst_count` | shown, waiting, in history, and whether paused |
## Settings
Two preferences have defaults in the definition (novox/hq ADR 0262). The mesh's layer, then a machine's,
overrides them, and the file is written with the value, never with a variable dunst reads.
| setting | default | what it is |
|---|---|---|
| `font-size` | 10 | the points of `font = Inter <size>`; 10 reads well on a screen of about 100 DPI, which is the workstations |
| `width` | 250 | the notification's width in pixels; a larger font needs a wider notification |
A machine with a denser screen sets both for itself, and the next push writes them:
mesh-controller.settings {module: "dunst", node: "<machine>", values: '{"font-size": 16, "width": 400}'}
`mesh-controller.settings {module: "dunst", node: "<machine>"}` shows each value and where it came from:
the default, the mesh, or the machine. dunst reads its file when it starts, and nothing in this module
tells a running dunst that the file changed: a new size shows at the next login, or after `dunstctl reload`.
## What it chose, and what it improves
The workstations' files differed: one had the notifications bottom-right, 15 % transparent and with
@@ -12,22 +12,30 @@ import (
// every desktop module that carries it.
type manifest struct {
Module string `json:"module"`
Version string `json:"version"`
Capabilities []string `json:"capabilities"`
Requires []string `json:"requires"`
Claims []claim `json:"claims"`
Seats []any `json:"seats"`
Tools []string `json:"tools"`
Emits []string `json:"emits"`
Environment *environment `json:"environment"`
Shell []shellCode `json:"shell"`
Resources []map[string]any `json:"resources"`
Module string `json:"module"`
Version string `json:"version"`
Capabilities []string `json:"capabilities"`
Requires []string `json:"requires"`
Claims []claim `json:"claims"`
Seats []any `json:"seats"`
Tools []string `json:"tools"`
Settings map[string]setting `json:"settings"`
Emits []string `json:"emits"`
Environment *environment `json:"environment"`
Shell []shellCode `json:"shell"`
Resources []map[string]any `json:"resources"`
Build struct {
Artifacts []map[string]any `json:"artifacts"`
} `json:"build"`
}
// setting is one key's default, as the definition gives it (novox/hq ADR 0262).
type setting struct {
Kind string `json:"kind"`
Default any `json:"default"`
Why string `json:"why"`
}
type claim struct {
Name string `json:"name"`
Scope string `json:"scope"`
+30 -1
View File
@@ -43,7 +43,8 @@ func TestItOwnsItsFileAndTheDropInDirectory(t *testing.T) {
func TestTheFileIsTheDecidedOneAndCallsTheSeatsMenu(t *testing.T) {
m := readManifest(t)
c := m.resource(t, "configuration")["content"].(string)
for _, want := range []string{"origin = top-right", "transparency = 0", "corner_radius = 0", "font = Inter 10", "dmenu = dmenu -p dunst"} {
for _, want := range []string{"origin = top-right", "transparency = 0", "corner_radius = 0", "font = Inter ${setting:font-size}",
"width = ${setting:width}", "dmenu = dmenu -p dunst"} {
if !strings.Contains(c, " "+want+"\n") {
t.Errorf("lacks %q", want)
}
@@ -55,6 +56,34 @@ func TestTheFileIsTheDecidedOneAndCallsTheSeatsMenu(t *testing.T) {
}
}
// The font size and the width are preferences with today's values as their defaults (novox/hq ADR
// 0262): a machine with a denser screen sets its own, and every other machine's file is unchanged.
func TestTheFontSizeAndWidthAreSettingsWithTodaysDefaults(t *testing.T) {
m := readManifest(t)
want := map[string]float64{"font-size": 10, "width": 250}
if len(m.Settings) != len(want) {
t.Fatalf("settings: %+v", m.Settings)
}
for key, value := range want {
s, ok := m.Settings[key]
if !ok || s.Kind != "preference" || s.Default != value || strings.TrimSpace(s.Why) == "" {
t.Errorf("%s: %+v, want a preference defaulting to %v with why", key, s, value)
}
}
// Filled with the defaults, the file is the one every machine had before it took settings.
c := m.resource(t, "configuration")["content"].(string)
c = strings.ReplaceAll(c, "${setting:font-size}", "10")
c = strings.ReplaceAll(c, "${setting:width}", "250")
for _, want := range []string{"font = Inter 10", "width = 250"} {
if !strings.Contains(c, " "+want+"\n") {
t.Errorf("filled with the defaults, lacks %q", want)
}
}
if strings.Contains(c, "${setting:") {
t.Errorf("asks for a setting it gives no default: %s", c)
}
}
func TestNothingStartsItButTheBus(t *testing.T) {
m := readManifest(t)
if m.Shell != nil {
+2 -2
View File
@@ -11,7 +11,7 @@
follow = none
# Geometry
width = 250
width = ${setting:width}
height = (0, 300)
origin = top-right
offset = (10, 50)
@@ -37,7 +37,7 @@
corner_radius = 0
# Text: the interface face (research 026/04)
font = Inter 10
font = Inter ${setting:font-size}
line_height = 0
markup = full
format = "<b>%s</b>\n%b"
+13 -1
View File
@@ -21,6 +21,18 @@
"dunst_rules",
"dunst_count"
],
"settings": {
"font-size": {
"kind": "preference",
"default": 10,
"why": "Inter at 10 points reads well on a screen of about 100 DPI at 96 DPI for X, which is the workstations; a denser screen sets more for its machine"
},
"width": {
"kind": "preference",
"default": 250,
"why": "250 pixels holds about forty characters of a title at the default size; a larger font needs a wider notification"
}
},
"resources": [
{
"id": "package",
@@ -52,7 +64,7 @@
"path": "${machine:account-home}/.config/dunst/dunstrc",
"owner": "${machine:account}",
"mode": "0644",
"content": "# dunst, the notifier (module dunst, novox/hq ADR 0208). Owned by the mesh: this file is replaced\n# at every push. Adopted from the laptop's file of 2026-10-04 (the two workstations differed in\n# position, transparency and corner radius; the laptop's square, opaque, top-right one matches the\n# rest of the desktop). Only what differs from dunst's defaults, and what the desktop relies on.\n#\n# Other modules' rules go in ~/.config/dunst/dunstrc.d/*.conf, which dunst reads after this file,\n# so a drop-in outranks it. dunst is started by D-Bus on the first notification: nothing starts it.\n\n[global]\n monitor = 0\n follow = none\n\n # Geometry\n width = 250\n height = (0, 300)\n origin = top-right\n offset = (10, 50)\n notification_limit = 20\n\n progress_bar = true\n progress_bar_height = 10\n progress_bar_frame_width = 1\n progress_bar_min_width = 150\n progress_bar_max_width = 300\n\n indicate_hidden = yes\n transparency = 0\n separator_height = 2\n padding = 8\n horizontal_padding = 8\n text_icon_padding = 0\n frame_width = 3\n frame_color = \"#de5200\"\n gap_size = 0\n separator_color = frame\n sort = yes\n corner_radius = 0\n\n # Text: the interface face (research 026/04)\n font = Inter 10\n line_height = 0\n markup = full\n format = \"<b>%s</b>\\n%b\"\n alignment = left\n vertical_alignment = center\n show_age_threshold = 60\n ellipsize = middle\n ignore_newline = no\n stack_duplicates = true\n hide_duplicate_count = false\n show_indicators = yes\n\n # Icons, from the desktop's icon theme\n enable_recursive_icon_lookup = true\n icon_theme = Adwaita\n icon_position = left\n min_icon_size = 32\n max_icon_size = 128\n\n # History\n sticky_history = yes\n history_length = 20\n\n # The context menu is the node's dmenu-compatible command, which the holder of node-launcher\n # answers (rofi on the workstations). Links open in the desktop's default browser.\n dmenu = dmenu -p dunst\n browser = /usr/bin/xdg-open\n always_run_script = true\n\n title = Dunst\n class = Dunst\n ignore_dbusclose = false\n\n mouse_left_click = close_current\n mouse_middle_click = do_action, close_current\n mouse_right_click = close_all\n\n[urgency_low]\n background = \"#000000\"\n foreground = \"#ffffff\"\n timeout = 10\n\n[urgency_normal]\n background = \"#000000\"\n foreground = \"#ffffff\"\n timeout = 10\n\n[urgency_critical]\n background = \"#000000\"\n foreground = \"#ffffff\"\n frame_color = \"#ff0000\"\n timeout = 0\n"
"content": "# dunst, the notifier (module dunst, novox/hq ADR 0208). Owned by the mesh: this file is replaced\n# at every push. Adopted from the laptop's file of 2026-10-04 (the two workstations differed in\n# position, transparency and corner radius; the laptop's square, opaque, top-right one matches the\n# rest of the desktop). Only what differs from dunst's defaults, and what the desktop relies on.\n#\n# Other modules' rules go in ~/.config/dunst/dunstrc.d/*.conf, which dunst reads after this file,\n# so a drop-in outranks it. dunst is started by D-Bus on the first notification: nothing starts it.\n\n[global]\n monitor = 0\n follow = none\n\n # Geometry\n width = ${setting:width}\n height = (0, 300)\n origin = top-right\n offset = (10, 50)\n notification_limit = 20\n\n progress_bar = true\n progress_bar_height = 10\n progress_bar_frame_width = 1\n progress_bar_min_width = 150\n progress_bar_max_width = 300\n\n indicate_hidden = yes\n transparency = 0\n separator_height = 2\n padding = 8\n horizontal_padding = 8\n text_icon_padding = 0\n frame_width = 3\n frame_color = \"#de5200\"\n gap_size = 0\n separator_color = frame\n sort = yes\n corner_radius = 0\n\n # Text: the interface face (research 026/04)\n font = Inter ${setting:font-size}\n line_height = 0\n markup = full\n format = \"<b>%s</b>\\n%b\"\n alignment = left\n vertical_alignment = center\n show_age_threshold = 60\n ellipsize = middle\n ignore_newline = no\n stack_duplicates = true\n hide_duplicate_count = false\n show_indicators = yes\n\n # Icons, from the desktop's icon theme\n enable_recursive_icon_lookup = true\n icon_theme = Adwaita\n icon_position = left\n min_icon_size = 32\n max_icon_size = 128\n\n # History\n sticky_history = yes\n history_length = 20\n\n # The context menu is the node's dmenu-compatible command, which the holder of node-launcher\n # answers (rofi on the workstations). Links open in the desktop's default browser.\n dmenu = dmenu -p dunst\n browser = /usr/bin/xdg-open\n always_run_script = true\n\n title = Dunst\n class = Dunst\n ignore_dbusclose = false\n\n mouse_left_click = close_current\n mouse_middle_click = do_action, close_current\n mouse_right_click = close_all\n\n[urgency_low]\n background = \"#000000\"\n foreground = \"#ffffff\"\n timeout = 10\n\n[urgency_normal]\n background = \"#000000\"\n foreground = \"#ffffff\"\n timeout = 10\n\n[urgency_critical]\n background = \"#000000\"\n foreground = \"#ffffff\"\n frame_color = \"#ff0000\"\n timeout = 0\n"
}
],
"build": {