diff --git a/agent/HARDWARE-QUEUE.md b/agent/HARDWARE-QUEUE.md
index c9d03c7..61da91b 100644
--- a/agent/HARDWARE-QUEUE.md
+++ b/agent/HARDWARE-QUEUE.md
@@ -8,6 +8,16 @@ the failure as a NOW bug in BACKLOG.md. Machines: the **AMD dev box**
QA machine), the **T14s** (webcam case).
## Any machine (dev box is fine)
+- [ ] **Unfree CLI default (2026-07-10)** — after `sys-rebuild` + new
+ shell/login: `echo $NIXPKGS_ALLOW_UNFREE` → `1`; `nix-shell -p
+ vscode --run 'code --version'` (or another unfree pkg) succeeds
+ without a manual export; `cat ~/.config/nixpkgs/config.nix` has
+ `allowUnfree = true`. (Flake-pure `nix shell nixpkgs#…` still may
+ need `--impure` — that is expected.)
+- [ ] **Waybar icon+percentage spacing (2026-07-10)** — switch to
+ executive-slate (and spot-check boreal/summer): battery glyph and
+ capacity% do not collide; volume glyph and volume% do not collide.
+ Unified clock reads as one module (`HH:MM · Day DD Mon`).
- [ ] **Charge-limit live apply (2026-07-10)** — on a laptop with
`charge_control_end_threshold`: System › Battery limit → 80%;
confirm `cat …/charge_control_end_threshold` is 80 without rebuild;
@@ -331,16 +341,16 @@ QA machine), the **T14s** (webcam case).
night, executive-slate, boreal) behave the same and their shutdown
power-button (if present) still opens the lock/logout menu.
-- [ ] **Calendar on the clock click** (iteration #68, item 42) — after
- `home-update` + relogin: left-click the Waybar date/clock → calcurse
- opens in a **floating, centered** ghostty window (~60×65% of screen),
- showing the month calendar; closing calcurse (`q`) closes the window.
- The `--class=com.nomarchy.calendar` must match the windowrule (if it
+- [ ] **Calendar on the unified clock click** (iteration #68 + 2026-07-10
+ bar polish) — after `home-update` + relogin: the bar shows **one**
+ clock module (`HH:MM · Day DD Mon`, not separate date/time modules);
+ left-click it → calcurse opens in a **floating, centered** ghostty
+ window (~60×65% of screen); closing calcurse (`q`) closes the window.
+ calcurse is a bar package (no template line required). The
+ `--class=com.nomarchy.calendar` must match the windowrule (if it
tiles, run `hyprctl clients` while open and check the real app-id).
Hover the clock → tooltip shows the long date + zone + month grid.
- Same on a whole-swap bar (summer/boreal/executive-slate). If calcurse
- was removed from home.packages, the click toasts "calcurse isn't
- installed" instead of doing nothing.
+ Same on a whole-swap bar (summer/boreal/executive-slate).
- [ ] **Screenshot annotation (satty)** (iteration #73) — after `home-update`:
hit SUPER+SHIFT+Print (or Tools ▸ Capture ▸ Annotate region) → a region
select (slurp) appears, then the `satty` UI opens in fullscreen with the
diff --git a/modules/home/default.nix b/modules/home/default.nix
index 63553a7..ab70b88 100644
--- a/modules/home/default.nix
+++ b/modules/home/default.nix
@@ -66,6 +66,9 @@
home.sessionVariables = {
TERMINAL = config.nomarchy.terminal;
NIXOS_OZONE_WL = "1"; # Electron/Chromium native Wayland
+ # Same gate as modules/nixos: CLI nix-shell/shell/run for unfree pkgs
+ # (system nixpkgs.config alone does not cover those entry points).
+ NIXPKGS_ALLOW_UNFREE = "1";
# Where the Nomarchy flake (and therefore theme-state.json) lives on
# disk. nomarchy-theme-sync writes its state here; rebuilds read from
@@ -73,5 +76,12 @@
NOMARCHY_PATH = "$HOME/.nomarchy";
};
+ # Classic nix-shell / nix-env read this; pairs with NIXPKGS_ALLOW_UNFREE
+ # above so "allow unfree" is the default desktop experience, not a
+ # per-command export the user has to remember.
+ xdg.configFile."nixpkgs/config.nix".text = ''
+ { allowUnfree = true; }
+ '';
+
programs.home-manager.enable = true;
}
diff --git a/modules/home/waybar.nix b/modules/home/waybar.nix
index 2bf37bc..de2d27f 100644
--- a/modules/home/waybar.nix
+++ b/modules/home/waybar.nix
@@ -94,9 +94,9 @@ let
# A distinct --class gives the window a matchable app-id for the
# float+center+size windowrule (hyprland.nix); --gtk-single-instance=false
# forces a fresh standalone window (ghostty defaults single-instance on).
- # calcurse ships uncommented in the downstream template (opt-out), so
- # self-gate with a helpful notify if it's been removed. ghostty is always
- # installed (ghostty.nix), so it can be relied on for the --class window.
+ # calcurse is installed with the bar (home.packages below); the gate is
+ # only for someone who force-removed it. ghostty is always installed
+ # (ghostty.nix), so it can be relied on for the --class window.
calendarLauncher = pkgs.writeShellScriptBin "nomarchy-calendar" ''
if ! command -v calcurse >/dev/null 2>&1; then
notify-send "Calendar" "calcurse isn't installed (removed from home.packages?)." 2>/dev/null
@@ -200,10 +200,12 @@ let
};
clock = {
- format = "{:%H:%M}";
+ # Single module: time + short date (whole-swaps used to split these
+ # into clock + clock#date — one click surface for the calendar).
+ format = "{:%H:%M · %a %d %b}";
# Left-click → the calendar (nomarchy-calendar → calcurse in a floating
- # ghostty). Replaces the old format-alt date toggle; the long date now
- # rides in the tooltip's first line, so nothing is lost on hover.
+ # ghostty). Long weekday/month name + zone + month grid ride in the
+ # tooltip so the bar stays compact.
on-click = "nomarchy-calendar";
# The zone line stays live under auto-timezone (the tz-watch SIGUSR2
# reload keeps it showing the currently *detected* zone).
@@ -218,9 +220,12 @@ let
};
pulseaudio = {
- format = "{icon} {volume}%";
+ # Trailing spaces on icons (not only between span and text): Nerd Font
+ # glyphs often have tight right bearings and otherwise collide with
+ # the percentage digits.
+ format = "{icon} {volume}%";
format-muted = "";
- format-icons.default = [ "" "" "" ];
+ format-icons.default = [ " " " " " " ];
on-click = "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle";
# Right-click → the full mixer (per-app volumes) in a floating window
# (item 35). pwvucontrol ships in the template suite; the Hyprland
@@ -246,9 +251,10 @@ let
battery = {
states = { warning = 25; critical = 10; };
+ # Same icon-bearing fix as pulseaudio (see format-icons note there).
format = "{icon} {capacity}%";
- format-charging = " {capacity}%";
- format-icons = [ "" "" "" "" "" ];
+ format-charging = " {capacity}%";
+ format-icons = [ " " " " " " " " " " ];
# Click either the battery or the power-profile icon → the combined
# power menu (profile + charge cap). The granular System rows stay.
on-click = "nomarchy-menu powermgmt";
@@ -498,7 +504,8 @@ in
# The power-profile helpers on PATH, so both the generated bar and the
# whole-swap themes' static waybar.jsonc can exec them by name — plus
- # the supervisor hyprland.nix exec-onces.
+ # the supervisor hyprland.nix exec-onces. calcurse is the clock-click
+ # calendar (feature dependency of the bar, not an opt-in app).
home.packages = lib.optionals config.nomarchy.waybar.enable
- [ waybarSupervisor powerProfileStatus powerProfileCycle vpnStatus doctorStatus calendarLauncher ];
+ [ waybarSupervisor powerProfileStatus powerProfileCycle vpnStatus doctorStatus calendarLauncher pkgs.calcurse ];
}
diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix
index 672151d..58d978c 100644
--- a/modules/nixos/default.nix
+++ b/modules/nixos/default.nix
@@ -69,7 +69,11 @@ in
# GPU/wifi drivers, firmware, fonts, …). The custom nixpkgs-config
# type can't carry a nested mkDefault; disagree with
# `nixpkgs.config = lib.mkForce { allowUnfree = false; }`.
+ # `NIXPKGS_ALLOW_UNFREE` covers CLI `nix-shell` / `nix shell` /
+ # `nix run` (those ignore the system `nixpkgs.config`); pure flake
+ # eval still needs `--impure` when the env var is the only gate.
nixpkgs.config.allowUnfree = true;
+ environment.variables.NIXPKGS_ALLOW_UNFREE = "1";
# ── One keyboard layout everywhere, incl. the LUKS prompt ────────
# services.xserver.xkb.layout is the single source of truth for the
diff --git a/templates/downstream/home.nix b/templates/downstream/home.nix
index e47ef01..c722e49 100644
--- a/templates/downstream/home.nix
+++ b/templates/downstream/home.nix
@@ -86,7 +86,7 @@
mpv # media player
amberol # music player (local library; streaming → spotify below)
pwvucontrol # PipeWire volume mixer (right-click the Waybar volume)
- calcurse # TUI calendar — click the Waybar clock to open it
+ # calcurse ships with the Waybar clock (modules/home/waybar.nix)
# ── More apps — uncomment to add ─────────────────────────────────
# Web browsers
diff --git a/themes/boreal/waybar.css b/themes/boreal/waybar.css
index 2076eb7..08a5a5e 100644
--- a/themes/boreal/waybar.css
+++ b/themes/boreal/waybar.css
@@ -24,7 +24,9 @@
@define-color bad #E2818A;
* {
- font-family: "GeistMono Nerd Font", "Symbols Nerd Font";
+ /* Mono Nerd face for status glyphs (tight right bearings on the
+ proportional Symbols face make icon+percentage collide). */
+ font-family: "GeistMono Nerd Font", "Symbols Nerd Font Mono";
font-size: 13px;
font-weight: 500;
border: none;
@@ -85,13 +87,12 @@ window#waybar {
}
#custom-nomarchy:hover { color: @accentAlt; }
-/* ── Clock — mono digits align; date recedes to frost-grey ───────────*/
+/* ── Clock — unified time · date ─────────────────────────────────────*/
#clock {
color: @text;
font-weight: 600;
padding-left: 4px;
}
-#clock.date { color: @subtext; font-weight: 500; }
/* ── Workspaces — dots that bloom to an aurora pill when active ───────*/
#workspaces { padding: 0 2px; }
diff --git a/themes/boreal/waybar.jsonc b/themes/boreal/waybar.jsonc
index 6280ef1..937f621 100644
--- a/themes/boreal/waybar.jsonc
+++ b/themes/boreal/waybar.jsonc
@@ -10,8 +10,7 @@
"reload_style_on_change": true,
"modules-left": [
"custom/nomarchy",
- "clock",
- "clock#date"
+ "clock"
],
"modules-center": [
"hyprland/workspaces"
@@ -38,14 +37,10 @@
"tooltip-format": "Nomarchy menu"
},
"clock": {
- "format": "{:%H:%M}",
+ "format": "{:%H:%M · %a %d %b}",
"on-click": "nomarchy-calendar",
"tooltip": true,
- "tooltip-format": "{:%Z (UTC%z)}\n{calendar}"
- },
- "clock#date": {
- "format": "{:%a %d %b}",
- "tooltip": false
+ "tooltip-format": "{:%A %d %B %Y}\n{:%Z (UTC%z)}\n{calendar}"
},
"hyprland/workspaces": {
"disable-scroll": true,
@@ -101,21 +96,21 @@
},
"pulseaudio": {
"scroll-step": 5,
- "format": "{icon} {volume}%",
- "format-bluetooth": "{icon} {volume}%",
+ "format": "{icon}{volume}%",
+ "format-bluetooth": "{icon} {volume}%",
"format-bluetooth-muted": " {icon}",
- "format-muted": " muted",
+ "format-muted": " muted",
"format-icons": {
- "headphone": "",
- "hands-free": "",
- "headset": "",
- "phone": "",
- "portable": "",
- "car": "",
+ "headphone": " ",
+ "hands-free": " ",
+ "headset": " ",
+ "phone": " ",
+ "portable": " ",
+ "car": " ",
"default": [
- "",
- "",
- ""
+ " ",
+ " ",
+ " "
]
},
"on-click": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle",
@@ -128,15 +123,15 @@
"warning": 25,
"critical": 10
},
- "format": "{icon} {capacity}%",
- "format-charging": " {capacity}%",
- "format-plugged": " {capacity}%",
+ "format": "{icon}{capacity}%",
+ "format-charging": " {capacity}%",
+ "format-plugged": " {capacity}%",
"format-icons": [
- "",
- "",
- "",
- "",
- ""
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
],
"on-click": "nomarchy-menu powermgmt",
"tooltip-format": "Battery status"
diff --git a/themes/executive-slate/waybar.css b/themes/executive-slate/waybar.css
index 52ba94c..6408e72 100644
--- a/themes/executive-slate/waybar.css
+++ b/themes/executive-slate/waybar.css
@@ -22,7 +22,9 @@
@define-color bad #D06B71;
* {
- font-family: "Inter", "Symbols Nerd Font", "JetBrainsMono Nerd Font", sans-serif;
+ /* Mono Nerd face first for status glyphs: proportional "Symbols Nerd
+ Font" has tight right bearings that make icon+percentage collide. */
+ font-family: "Symbols Nerd Font Mono", "Inter", "JetBrainsMono Nerd Font", sans-serif;
font-size: 13px;
font-weight: 500;
border: none;
@@ -114,10 +116,11 @@ window#waybar {
border-bottom: 2px solid @bad;
}
-/* ── Clock — time (center) and date (left) share the #clock id ───── */
+/* ── Clock — unified time · date in one module ───────────────────── */
#clock {
color: @text;
font-weight: 600;
+ font-family: "Inter", "Symbols Nerd Font Mono", sans-serif;
}
/* ── Status cluster: neutral by default, semantic on state ───────── */
diff --git a/themes/executive-slate/waybar.jsonc b/themes/executive-slate/waybar.jsonc
index 7f5f94d..5a29998 100644
--- a/themes/executive-slate/waybar.jsonc
+++ b/themes/executive-slate/waybar.jsonc
@@ -10,8 +10,7 @@
"reload_style_on_change": true,
"modules-left": [
"custom/nomarchy",
- "hyprland/workspaces",
- "clock#date"
+ "hyprland/workspaces"
],
"modules-center": [
"clock"
@@ -45,14 +44,10 @@
"on-scroll-down": "hyprctl dispatch workspace r-1"
},
"clock": {
- "format": "{:%H:%M}",
+ "format": "{:%H:%M · %a %d %b}",
"on-click": "nomarchy-calendar",
"tooltip": true,
- "tooltip-format": "{:%Z (UTC%z)}\n{calendar}"
- },
- "clock#date": {
- "format": "{:%a %d %b}",
- "tooltip": false
+ "tooltip-format": "{:%A %d %B %Y}\n{:%Z (UTC%z)}\n{calendar}"
},
"custom/recording": {
"return-type": "json",
@@ -87,21 +82,21 @@
},
"pulseaudio": {
"scroll-step": 5,
- "format": "{icon} {volume}%",
- "format-bluetooth": "{icon} {volume}%",
+ "format": "{icon}{volume}%",
+ "format-bluetooth": "{icon} {volume}%",
"format-bluetooth-muted": " {icon}",
- "format-muted": " muted",
+ "format-muted": " muted",
"format-icons": {
- "headphone": "",
- "hands-free": "",
- "headset": "",
- "phone": "",
- "portable": "",
- "car": "",
+ "headphone": " ",
+ "hands-free": " ",
+ "headset": " ",
+ "phone": " ",
+ "portable": " ",
+ "car": " ",
"default": [
- "",
- "",
- ""
+ " ",
+ " ",
+ " "
]
},
"on-click": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle",
@@ -120,15 +115,15 @@
"warning": 25,
"critical": 10
},
- "format": "{icon} {capacity}%",
- "format-charging": " {capacity}%",
- "format-plugged": " {capacity}%",
+ "format": "{icon}{capacity}%",
+ "format-charging": " {capacity}%",
+ "format-plugged": " {capacity}%",
"format-icons": [
- "",
- "",
- "",
- "",
- ""
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
],
"on-click": "nomarchy-menu powermgmt",
"tooltip-format": "Battery status"
diff --git a/themes/summer-day/waybar.css b/themes/summer-day/waybar.css
index 71d2888..6aaa952 100755
--- a/themes/summer-day/waybar.css
+++ b/themes/summer-day/waybar.css
@@ -48,7 +48,6 @@ window#waybar {
#custom-recording,
#idle_inhibitor,
#clock,
-#clock-date,
#workspaces,
#pulseaudio,
#custom-powerprofile,
diff --git a/themes/summer-day/waybar.jsonc b/themes/summer-day/waybar.jsonc
index caa3b75..9aae8c6 100644
--- a/themes/summer-day/waybar.jsonc
+++ b/themes/summer-day/waybar.jsonc
@@ -9,7 +9,7 @@
"spacing": 15,
"reload_style_on_change": true,
- "modules-left": ["custom/launcher", "clock", "clock#date"],
+ "modules-left": ["custom/launcher", "clock"],
"modules-center": ["hyprland/workspaces"],
"modules-right": ["custom/recording", "idle_inhibitor", "pulseaudio", "custom/powerprofile", "custom/nightlight", "hyprland/language", "custom/updates", "battery", "tray", "custom/vpn", "custom/doctor", "custom/notification", "custom/powermenu"],
@@ -37,18 +37,18 @@
},
"pulseaudio": {
- "format": "{icon} {volume}%",
- "format-bluetooth": "{icon} {volume}%",
+ "format": "{icon}{volume}%",
+ "format-bluetooth": "{icon} {volume}%",
"format-bluetooth-muted": " muted",
- "format-muted": " muted",
+ "format-muted": " muted",
"format-icons": {
- "headphone": "",
- "hands-free": "",
- "headset": "",
- "phone": "",
- "portable": "",
- "car": "",
- "default": ["", "", ""]
+ "headphone": " ",
+ "hands-free": " ",
+ "headset": " ",
+ "phone": " ",
+ "portable": " ",
+ "car": " ",
+ "default": [" ", " ", " "]
},
"on-click": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle",
"on-click-right": "pwvucontrol",
@@ -58,26 +58,21 @@
"battery": {
"interval": 60,
"states": { "warning": 25, "critical": 10 },
- "max-length": 10,
- "format": "{icon} {capacity}%",
- "format-charging": " {capacity}%",
- "format-plugged": " {capacity}%",
- "format-full": " 100%",
- "format-icons": ["", "", "", "", ""],
+ "max-length": 14,
+ "format": "{icon}{capacity}%",
+ "format-charging": " {capacity}%",
+ "format-plugged": " {capacity}%",
+ "format-full": " 100%",
+ "format-icons": [" ", " ", " ", " ", " "],
"on-click": "nomarchy-menu powermgmt",
"tooltip-format": "Battery status"
},
"clock": {
- "format": " {:%H:%M}",
+ "format": " {:%H:%M · %a %d %b}",
"on-click": "nomarchy-calendar",
"tooltip": true,
- "tooltip-format": "{:%Z (UTC%z)}\n{calendar}"
- },
-
- "clock#date": {
- "format": " {:%A, %B %d, %Y}",
- "tooltip": false
+ "tooltip-format": "{:%A %d %B %Y}\n{:%Z (UTC%z)}\n{calendar}"
},
"tray": { "spacing": 8 },
diff --git a/themes/summer-night/waybar.css b/themes/summer-night/waybar.css
index f2f9c10..8627b52 100644
--- a/themes/summer-night/waybar.css
+++ b/themes/summer-night/waybar.css
@@ -53,7 +53,6 @@ window#waybar {
#custom-nomarchy,
#custom-recording,
#clock,
-#clock.date,
#workspaces,
#pulseaudio,
#custom-powerprofile,
diff --git a/themes/summer-night/waybar.jsonc b/themes/summer-night/waybar.jsonc
index 07a0791..7435824 100644
--- a/themes/summer-night/waybar.jsonc
+++ b/themes/summer-night/waybar.jsonc
@@ -9,7 +9,7 @@
"spacing": 15,
"reload_style_on_change": true,
- "modules-left": ["custom/nomarchy", "clock", "clock#date"],
+ "modules-left": ["custom/nomarchy", "clock"],
"modules-center": ["hyprland/workspaces"],
"modules-right": ["custom/recording", "idle_inhibitor", "pulseaudio", "custom/powerprofile", "custom/nightlight", "hyprland/language", "custom/updates", "battery", "tray", "custom/vpn", "custom/doctor", "custom/notification", "custom/powermenu"],
@@ -38,18 +38,18 @@
"pulseaudio": {
"scroll-step": 5,
- "format": "{icon} {volume}%",
- "format-bluetooth": "{icon} {volume}%",
+ "format": "{icon}{volume}%",
+ "format-bluetooth": "{icon} {volume}%",
"format-bluetooth-muted": " {icon}",
- "format-muted": " muted",
+ "format-muted": " muted",
"format-icons": {
- "headphone": "",
- "hands-free": "",
- "headset": "",
- "phone": "",
- "portable": "",
- "car": "",
- "default": ["", "", ""]
+ "headphone": " ",
+ "hands-free": " ",
+ "headset": " ",
+ "phone": " ",
+ "portable": " ",
+ "car": " ",
+ "default": [" ", " ", " "]
},
"on-click": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle",
"on-click-right": "pwvucontrol",
@@ -59,24 +59,19 @@
"battery": {
"interval": 30,
"states": { "warning": 25, "critical": 10 },
- "format": "{icon} {capacity}%",
- "format-charging": " {capacity}%",
- "format-plugged": " {capacity}%",
- "format-icons": ["", "", "", "", ""],
+ "format": "{icon}{capacity}%",
+ "format-charging": " {capacity}%",
+ "format-plugged": " {capacity}%",
+ "format-icons": [" ", " ", " ", " ", " "],
"on-click": "nomarchy-menu powermgmt",
"tooltip-format": "Battery status"
},
"clock": {
- "format": " {:%H:%M}",
+ "format": " {:%H:%M · %a %d %b}",
"on-click": "nomarchy-calendar",
"tooltip": true,
- "tooltip-format": "{:%Z (UTC%z)}\n{calendar}"
- },
-
- "clock#date": {
- "format": " {:%A, %B %d, %Y}",
- "tooltip": false
+ "tooltip-format": "{:%A %d %B %Y}\n{:%Z (UTC%z)}\n{calendar}"
},
"tray": { "spacing": 8 },