fix(ui): #139 sheets size themselves; #141 waybar clicks stop reading the env
Some checks failed
Check / eval (push) Has been cancelled

#139 — calcurse/doctor floated full-screen. Two faults stacked, which is
why it looked like the float rules were dead:
  1. Hyprland 0.55.4 silently ignores percentage `size` rules. Matrix on
     hardware (both orders, kitty memory off): `size 60% 65%` never
     applies, `size 1536 936` applies either way, configerrors empty both
     times. Rule order was a red herring.
  2. Kitty defaults to remember_window_size=yes and replays the last OS
     window's size into every float — so after a tiled terminal, a sheet
     opens maximized. This is the Ghostty regression: Ghostty had no such
     memory, so fault 1 stayed invisible until #95.

Fix: remember_window_size=no (floats must be deterministic) and the sheets
ask for their own size — term-sheet.nix reads the focused monitor and
hands kitty the px, because px in a rule cannot mean "a fraction of *this*
screen" and one pair cannot serve 2560x1440 and the 1366x768 Acer (#131).
Those windows keep float/center and deliberately carry no size rule.

#141 — the updates click did nothing: whole-swaps hand-wrote
`sh -c '$TERMINAL …'` and Waybar has no TERMINAL (home.sessionVariables →
login shells only; the bar is spawned by Hyprland). Fixed at the root:
`nomarchy-updates upgrade-window` opens its own window, so all five call
sites name one env-free command and a theme file stops having an opinion
about terminals. checks.waybar-swap-env guards the class.

Bernardo asked mid-task for the update window to float — it is now the
third caller of the sheet helper (com.nomarchy.updates, 45%x50%).

V3 on the dev box: calendar 1536x936, doctor 1408x1008, updates 1152x720
— exactly the intended fractions, floating and centred; #141 driven under
`env -i` with no TERMINAL, matching Waybar's real environment; the guard
proven by reintroducing the bug. Acer V3 queued.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 10:04:28 +01:00
parent 4a63f27bf4
commit 55a516e1c7
15 changed files with 220 additions and 74 deletions

View File

@@ -1431,6 +1431,48 @@
in
assert ok; pkgs.runCommand "nomarchy-waybar-language" { } "touch $out";
# A whole-swap's commands must not read the session environment.
# Waybar is spawned by Hyprland and never sources a login shell, so
# `home.sessionVariables` (TERMINAL, EDITOR …) simply are not there:
# the on-click expands to nothing, the click does nothing, and there
# is no error to notice (#141 — it went unseen because only the
# whole-swap themes hand-write these; the generated module bakes the
# value at eval time and cannot regress). Guards the class: the next
# hand-written jsonc reaching for an env var fails here instead.
waybar-swap-env =
let
inherit (nixpkgs) lib;
expect = cond: msg: lib.assertMsg cond "waybar-swap-env: ${msg}";
swaps = lib.filter
(n: builtins.pathExists (./themes + "/${n}/waybar.jsonc"))
(builtins.attrNames (builtins.readDir ./themes));
# `$1`/`$2` (awk, sh positionals) are fine — a *name* after the
# sigil is what marks an environment read.
readsEnv = s: builtins.match ".*\\$[A-Za-z_{].*" s != null;
isCmdKey = k:
lib.hasPrefix "on-click" k || lib.hasPrefix "on-scroll" k
|| k == "exec" || k == "exec-if";
offendersIn = name:
let bar = builtins.fromJSON (builtins.readFile (./themes + "/${name}/waybar.jsonc"));
in lib.concatMap (mod:
let m = bar.${mod}; in
if !builtins.isAttrs m then [ ] else
lib.concatMap (k:
let v = m.${k}; in
if isCmdKey k && builtins.isString v && readsEnv v
then [ "${name}: ${mod}.${k} = ${v}" ] else [ ])
(builtins.attrNames m))
(builtins.attrNames bar);
offenders = lib.concatMap offendersIn swaps;
ok =
expect (swaps != [ ])
"fixture drift: no theme ships a waybar.jsonc the guard is checking nothing"
&& expect (offenders == [ ])
("a whole-swap command reads the environment, which Waybar does not have: "
+ lib.concatStringsSep "; " offenders);
in
assert ok; pkgs.runCommand "nomarchy-waybar-swap-env" { } "touch $out";
# Docking artifact guard: default/no-profile Home Manager output
# must still launch both hotplug watchers, put Rofi on the focused
# monitor, expose the manual keyboard picker, and never ship the