feat(display): per-profile workspace→output pins (item 15c)
A display profile can now pin workspaces to outputs:
docked = {
monitors = [ … ];
workspaces = { "1" = "DP-3"; "9" = "eDP-1"; };
};
The bare list-of-monitors shape still works (either-type; hyprland.nix
normalizes — coercedTo refuses list-of-submodule sources). Pins bake as
Hyprland `workspace` rules from the active profile and apply instantly
on `nomarchy-display-profile apply`: keyword sets the session rule,
moveworkspacetomonitor moves open workspaces over. Pins from a
previously applied profile linger in-session until reload/rebuild
(hyprctl can only add) — inert if the output is absent, noted in the
applier.
V1: flake check green (checks.display-profiles now asserts
workspaceRule) + scratch-downstream eval: baked settings carry both
rule sets, the generated applier has the keyword+dispatch pairs
(bash -n), match/list correct with mixed shapes. V3 queued in
HARDWARE-QUEUE (rides the dock test). Item 15 complete — deleted.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -85,14 +85,27 @@ let
|
||||
# The night-light pattern: nomarchy-display-profile applies rules live
|
||||
# via hyprctl and writes settings.displayProfile with --no-switch;
|
||||
# resolve bakes the same choice here at the next rebuild.
|
||||
profiles = config.nomarchy.displayProfiles;
|
||||
# Profiles normalized to { monitors; workspaces; } — the option also
|
||||
# accepts the original bare-list-of-monitors shape.
|
||||
profiles = lib.mapAttrs
|
||||
(_: p: if builtins.isList p then { monitors = p; workspaces = { }; } else p)
|
||||
config.nomarchy.displayProfiles;
|
||||
resolvedMonitors = monitorLib.resolve {
|
||||
base = config.nomarchy.monitors;
|
||||
inherit profiles;
|
||||
profiles = lib.mapAttrs (_: p: p.monitors) profiles;
|
||||
active = config.nomarchy.settings.displayProfile or "";
|
||||
resOverrides = config.nomarchy.settings.monitors;
|
||||
};
|
||||
|
||||
# The active profile's workspace→output pins, baked as `workspace`
|
||||
# rules below (same rebuild path as the monitor overlay; the applier
|
||||
# covers the instant half). Same junk-state guard as resolve.
|
||||
activeProfile =
|
||||
let a = config.nomarchy.settings.displayProfile or "";
|
||||
in profiles.${if builtins.isString a then a else ""} or { workspaces = { }; };
|
||||
profileWorkspaceRules =
|
||||
lib.mapAttrsToList monitorLib.workspaceRule activeProfile.workspaces;
|
||||
|
||||
# Profile applier — on PATH only when profiles are declared (the menu
|
||||
# row self-gates on it). apply: the profile's rules live via hyprctl +
|
||||
# the in-flake state write; base: clear the state, then hyprctl reload
|
||||
@@ -107,11 +120,20 @@ ${lib.concatMapStringsSep "\n" (n: " echo ${lib.escapeShellArg n}") (lib.
|
||||
apply)
|
||||
case "''${2:-}" in
|
||||
${lib.concatStringsSep "\n" (lib.mapAttrsToList
|
||||
(name: entries:
|
||||
(name: p:
|
||||
" ${lib.escapeShellArg name})\n"
|
||||
+ lib.concatMapStringsSep "\n"
|
||||
(m: " hyprctl keyword monitor ${lib.escapeShellArg (monitorRule m)} >/dev/null 2>&1")
|
||||
entries
|
||||
p.monitors
|
||||
# Workspace pins: the keyword sets the session rule, the dispatch
|
||||
# moves an already-open workspace over. Pins from a previously
|
||||
# applied profile linger until reload/rebuild (hyprctl can only
|
||||
# add) — harmless, a rule naming an absent output is inert.
|
||||
+ lib.concatStrings (lib.mapAttrsToList
|
||||
(ws: out:
|
||||
"\n hyprctl keyword workspace ${lib.escapeShellArg (monitorLib.workspaceRule ws out)} >/dev/null 2>&1"
|
||||
+ "\n hyprctl dispatch moveworkspacetomonitor ${lib.escapeShellArg ws} ${lib.escapeShellArg out} >/dev/null 2>&1")
|
||||
p.workspaces)
|
||||
+ " ;;")
|
||||
profiles)}
|
||||
*) echo "unknown profile '$2' — try: nomarchy-display-profile list" >&2; exit 64 ;;
|
||||
@@ -147,7 +169,7 @@ ${lib.concatStringsSep "\n" (lib.mapAttrsToList
|
||||
fi
|
||||
done < <(printf '%s\n' ${lib.concatMapStringsSep " " lib.escapeShellArg
|
||||
(lib.mapAttrsToList
|
||||
(name: entries: "${name}:${lib.concatMapStringsSep " " (m: m.name) entries}")
|
||||
(name: p: "${name}:${lib.concatMapStringsSep " " (m: m.name) p.monitors}")
|
||||
profiles)})
|
||||
if [ -n "$exact" ] && [ -z "$exactdup" ]; then echo "$exact"; exit 0; fi
|
||||
if [ -n "$best" ] && [ -z "$dup" ]; then echo "$best"; exit 0; fi
|
||||
@@ -295,6 +317,10 @@ in
|
||||
monitor = lib.mkDefault
|
||||
([ ",preferred,auto,1" ] ++ map monitorRule resolvedMonitors);
|
||||
|
||||
# The active display profile's workspace→output pins (a list, so
|
||||
# downstream `workspace = [...]` rules concatenate).
|
||||
workspace = profileWorkspaceRules;
|
||||
|
||||
# exec-once is a list at normal priority, so downstream additions
|
||||
# CONCATENATE (your autostart runs alongside ours) rather than
|
||||
# replace. Same for the bind* lists below.
|
||||
|
||||
Reference in New Issue
Block a user