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
|
#+TITLE: Collapsible waybar sides — implementation spec
#+AUTHOR: Craig Jennings & Claude
#+DATE: 2026-06-19
* Goal
Let each side of the waybar collapse to a minimal base set with a click, and
expand again with another click. Each side carries a small arrowhead that points
toward the screen edge when expanded (click to collapse outward) and flips to
point toward center when collapsed (click to expand). Left and right collapse
independently.
This is dotfiles work (=~/.dotfiles=, =hyprland/= tier). Tracked by the
=Collapsible waybar sides= task in archsetup =todo.org=.
* Decisions (locked 2026-06-19)
- *Mechanism*: config-swap + =killall -SIGUSR2 waybar=. NOT state-file + CSS —
the spike proved CSS can't collapse native modules (they go invisible but hold
their space; GTK3 has no =display:none=). See [[file:spike-findings.org]].
- *Right base set*: =custom/date=, =custom/worldclock=, =tray= (plus the right
arrow). Tray reflows cleanly and survives the reload (spike-confirmed).
- *Left base set*: =custom/menu=, =hyprland/workspaces= (plus the left arrow).
- *Per-side*: left and right toggle independently, each with its own arrow.
- *Per-host*: host-agnostic. The base set is constant; the full set is whatever
each host's config already defines. ratio (no battery/touchpad/airplane) needs
no special-casing — collapse hides whatever modules that host has. Build once.
* Architecture
** The active-config indirection (the core piece)
=~/.config/waybar/config= is a stow symlink into the dotfiles, so the toggle
can't rewrite it in place (that would edit the repo). Instead:
1. *Canonical* config: the committed dotfiles config. Always holds the FULL
module arrays. Read-only source of truth. Unchanged by this feature except
for adding the two arrow modules and their definitions.
2. *Active* config: a generated copy at =$XDG_RUNTIME_DIR/waybar/config=. This is
what waybar loads (=waybar -c=). The toggle rewrites its =modules-left= /
=modules-right= between full and base.
3. *Launch change* (hyprland.conf exec-once): before launching waybar, generate
the active config from the canonical (initial state = expanded/full), then
=waybar -c "$XDG_RUNTIME_DIR/waybar/config" -s <style>=.
The style.css stays shared (canonical, stowed) — only the config (module arrays)
needs the runtime copy.
** Toggle scripts
=waybar-collapse <side>= where side ∈ {left, right}:
1. Read per-side state from =$XDG_RUNTIME_DIR/waybar/<side>.state= (expanded |
collapsed; absent = expanded).
2. Flip it.
3. Regenerate the active config's =modules-<side>= array:
- expanded → the canonical full array for that side.
- collapsed → the base set for that side (constant in the script) with the
arrow module included.
4. Write the new state file.
5. =killall -SIGUSR2 waybar=.
The full array is read from the canonical config each time, so the script never
loses it and stays correct as modules are added/removed upstream. The base set is
the only constant the script hardcodes (or reads from a tiny sidecar).
** Arrow modules
Two custom modules, =custom/arrow-left= and =custom/arrow-right=, each an exec
script (=waybar-arrow left= / =waybar-arrow right=) that:
- Reads the side's state file.
- Emits the glyph: expanded → points outward (left side ◀ toward left edge,
right side ▶ toward right edge); collapsed → points inward (left ▶, right ◀).
- =on-click= runs =waybar-collapse <side>=.
The arrow is always in the base set (it's the expand control), so it's present in
both states. Place the left arrow as the LAST module in =modules-left= (innermost,
nearest center) and the right arrow as the FIRST module in =modules-right=
(innermost), so each arrow sits at the inner edge of its side and the collapse
pulls outward away from it. (Confirm placement during implementation — the glyph
direction and module order must agree so the arrow visually points the right way.)
** State + reload
- State dir: =$XDG_RUNTIME_DIR/waybar/= (per-boot, ephemeral — collapse state
resets on logout, which is fine).
- Reload is =SIGUSR2= (full waybar reload). Cost: brief flicker, module state
resets, tray re-registers. Acceptable for a click action; spike confirmed tray
survives. This cost is per-toggle, never idle.
* Files (all in =~/.dotfiles/hyprland/=)
- =.local/bin/waybar-collapse= — the toggle (reads canonical, writes active,
signals). New.
- =.local/bin/waybar-arrow= — the arrow module exec (state → glyph + class). New.
- =.local/bin/waybar-active-config= — generates the active config from canonical
at login (used by exec-once and reused by waybar-collapse to resolve the full
arrays). New. (Or fold generation into waybar-collapse + a one-shot init call.)
- =.config/waybar/config= — add =custom/arrow-left= / =custom/arrow-right= module
defs + place them in the arrays. Edit.
- =.config/hypr/hyprland.conf= — exec-once: generate active config, then
=waybar -c "$XDG_RUNTIME_DIR/waybar/config"=. Edit.
- Optional keybinds: =$mod+[= / =$mod+]= to collapse left/right without the mouse.
* TDD plan (per the dotfiles suite)
- =tests/waybar-collapse/=: full↔base array rewrite against a fixture canonical
config; expanded→collapsed→expanded round-trips to the original arrays; state
file flips; base set always includes the arrow; SIGUSR2 sent (fake killall).
Use a fake canonical config + temp =$XDG_RUNTIME_DIR=, fake killall on PATH
(same harness style as tests/waybar-toggle).
- =tests/waybar-arrow/=: state → correct glyph + class for each side and state;
missing state file = expanded glyph (fail-safe).
- JSON validity of the generated active config (parse it back).
* Open / to-confirm during implementation
- Exact arrow glyphs (nerd-font triangles) and that order-vs-direction agree.
- Whether to keep =hyprland/window= (the title) out of the left base set — it's
long and variable-width; collapsing the left should drop it (it's not in the
base set, so it hides — correct).
- Animation: none (waybar doesn't animate width; the collapse snaps). Accepted.
* Risks
- Reload flicker on every toggle. Mitigation: none needed unless it annoys in use.
- If a future module is added to the canonical config, it lands in the full set
automatically (good) but the author should decide if it belongs in a base set.
- $XDG_RUNTIME_DIR active config must exist before waybar starts; the exec-once
ordering must generate it first. waybar-toggle (the crash-relaunch path, mod+B)
must also point at the active config, not the canonical — update it to match.
|