feat(keyboard): multi-layout cycle bind + summer-bar language parity
All checks were successful
Check / eval (push) Successful in 2m59s
All checks were successful
Check / eval (push) Successful in 2m59s
SUPER+SHIFT+K → hyprctl switchxkblayout current next, rendered only
when the session layout has a comma — the same gate as the Waybar
language indicator. Bind data is a separate multiLayoutBinds list in
keybinds.nix (still one source); hyprland.nix and the rofi cheatsheet
gate on the identical condition, so bind and cheatsheet cannot drift.
`current` targets the focused keyboard, so per-device-overridden
boards (single layout) are a no-op.
Parity: hyprland/language (with the keyboard glyph) added to both
summer waybar.jsonc whole-swaps + #language in their CSS. Static JSON
cannot eval-gate, so on summer themes the module shows even with one
layout — parity-over-minimalism, deliberate. Found en route:
summer-night has an idle_inhibitor the generated bar lacks (reverse
gap) — folded into backlog item 13.
Also — agent/BACKLOG.md gains items 21-24 from Bernardo's Latitude
5410 hardware QA (waybar crash on theme switch + no respawn; nameless
network-menu rows; a no-update sys-rebuild; Back-everywhere audit),
queued ahead of everything; HARDWARE-QUEUE's snapshot re-test now
carries the "update to >= a47aa3a + relogin" precondition, since
findings 5/6 match the pre-polkit-agent behavior exactly.
Verified: V0 (flake check, both summer JSONs parse) + V1 both
directions (default single-layout build renders no bind; us,de via
extendModules renders bind AND cheatsheet row). V3 cycle check queued.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,14 +22,51 @@ next, in what order*.
|
|||||||
|
|
||||||
## NOW
|
## NOW
|
||||||
|
|
||||||
### 5. Keyboard layouts — cycle bind + summer-bar parity
|
*Items 21–24: real-hardware QA findings from the Latitude 5410
|
||||||
ROADMAP § Keyboard layouts, remaining bullets: (a) a multi-layout cycle
|
(Bernardo, 2026-07-04) — bugs found on metal outrank the queue.*
|
||||||
bind (`hyprctl switchxkblayout` current-device, or xkb `grp:` option)
|
|
||||||
when >1 session layout, in the cheatsheet; (b) add `hyprland/language`
|
### 21. Waybar crash on theme switch — no respawn
|
||||||
to the summer-day/night `waybar.jsonc` whole-swaps (parity rule — the
|
Reported: after a theme switch on the Latitude, Waybar crashed and
|
||||||
generated bar has it, the static ones don't; gating doesn't translate,
|
never came back (exec-once = no supervisor; a crash orphans the session
|
||||||
decide the static behavior and note it). **Verify:** V1 + `jq` the
|
until relogin). Two halves: (a) **resilience** — Waybar must respawn on
|
||||||
rendered configs; V3 queue the on-hardware cycle check.
|
crash regardless of cause. exec-once a small supervisor loop (restart
|
||||||
|
with backoff; must not fight `pkill -x waybar` intentional stops), or
|
||||||
|
re-attempt the systemd user unit with an IPC-readiness gate (the naive
|
||||||
|
unit raced Hyprland IPC on relogin — see waybar.nix's comment). (b)
|
||||||
|
**root cause** — the switch path both flips `style.css` (Waybar's
|
||||||
|
`reload_style_on_change` picks it up) *and* sends SIGUSR2
|
||||||
|
(`nomarchy-theme-sync`), a double-reload during the config/css symlink
|
||||||
|
flip; suspect the race crashes Waybar's reload. Consider: drop the
|
||||||
|
SIGUSR2 when `reload_style_on_change` is on, or replace reload with a
|
||||||
|
clean restart on switch. **Verify:** V2 (kill waybar in the themed-VM
|
||||||
|
recipe → respawns; switch theme → bar survives); V3 re-check on the
|
||||||
|
Latitude.
|
||||||
|
|
||||||
|
### 22. Network menu shows nameless/weird entries
|
||||||
|
Reported: `networkmanager_dmenu` lists entries with no names (likely
|
||||||
|
hidden-SSID APs rendered as blank rows, possibly other adapters/
|
||||||
|
duplicate BSSIDs). Investigate its config.ini surface for filtering,
|
||||||
|
or patch the list source; blank rows in a themed picker read as
|
||||||
|
breakage. **Verify:** V1 (config renders) — behavior needs Wi-Fi
|
||||||
|
hardware → V3 on the Latitude.
|
||||||
|
|
||||||
|
### 23. `sys-rebuild` — rebuild without updating the lock
|
||||||
|
Reported: there's no way to rebuild the system *without* `sys-update`'s
|
||||||
|
`nix flake update` (home-update already skips the lock; the system side
|
||||||
|
lacks the twin). Add `sys-rebuild` (snapshot-first like sys-update,
|
||||||
|
rebuild against the current lock, no update) alongside `sys-update`;
|
||||||
|
document both in README §5 + the motd cheat sheet if it lists them.
|
||||||
|
**Verify:** V1 + eval that both scripts land on PATH.
|
||||||
|
|
||||||
|
### 24. "Back" audit — every list menu ends in ↩ Back
|
||||||
|
Reported: some submenu items/tools still lack a Back option. The "Back
|
||||||
|
everywhere" pass covered the hand-rolled dmenu lists at the time; audit
|
||||||
|
what's been added since (VPN, Display, power-profile, printers…) and
|
||||||
|
the external/plugin modules (networkmanager_dmenu, rofi-pulse-select,
|
||||||
|
rofi-calc/emoji modi) — hand-rolled lists get the shared `back` helper;
|
||||||
|
external modi that can't take an injected row get documented as such
|
||||||
|
(and Esc always works). **Verify:** V1 + a grep-the-generated-script
|
||||||
|
audit listing which menus have/lack Back; V3 spot-check.
|
||||||
|
|
||||||
### 6. Full docs review & restructure
|
### 6. Full docs review & restructure
|
||||||
ROADMAP § Full docs review. The roadmap/backlog split is done (this
|
ROADMAP § Full docs review. The roadmap/backlog split is done (this
|
||||||
@@ -100,8 +137,9 @@ supports it, else V3 queue.
|
|||||||
### 13. Small niceties batch (one slice per iteration)
|
### 13. Small niceties batch (one slice per iteration)
|
||||||
Each is a small, self-contained polish item in the existing patterns:
|
Each is a small, self-contained polish item in the existing patterns:
|
||||||
- **Idle-inhibit (caffeine) toggle:** Waybar `idle_inhibitor` module
|
- **Idle-inhibit (caffeine) toggle:** Waybar `idle_inhibitor` module
|
||||||
(blocks hypridle lock/suspend during video/presentations) + summer
|
(blocks hypridle lock/suspend during video/presentations) in the
|
||||||
whole-swap parity + cheatsheet mention.
|
GENERATED bar — summer-night already has one (reverse parity gap
|
||||||
|
found 2026-07-04); add to summer-day too + cheatsheet mention.
|
||||||
- **Low-battery notifications:** the bar colors at 25/10% but nothing
|
- **Low-battery notifications:** the bar colors at 25/10% but nothing
|
||||||
*notifies*; gate on `power.laptop` (poweralertd, or a small upower
|
*notifies*; gate on `power.laptop` (poweralertd, or a small upower
|
||||||
watcher consistent with how the bar reads state).
|
watcher consistent with how the bar reads state).
|
||||||
|
|||||||
@@ -21,11 +21,22 @@ QA machine), the **T14s** (webcam case).
|
|||||||
- [ ] **Auto-timezone** — enable from the menu; confirm geoclue finds the
|
- [ ] **Auto-timezone** — enable from the menu; confirm geoclue finds the
|
||||||
zone, `/etc/localtime` updates, and the Waybar clock refreshes
|
zone, `/etc/localtime` updates, and the Waybar clock refreshes
|
||||||
(SIGUSR2 watcher) — also after a manual `timedatectl set-timezone`.
|
(SIGUSR2 watcher) — also after a manual `timedatectl set-timezone`.
|
||||||
|
- [ ] **Keyboard layout cycle bind** — with a comma layout (e.g.
|
||||||
|
`nomarchy.keyboard.layout = "us,de"`), SUPER+SHIFT+K cycles the
|
||||||
|
focused keyboard's layout, the Waybar `` indicator follows, and
|
||||||
|
the row shows in the SUPER+? cheatsheet.
|
||||||
- [ ] **Keyboard hotplug picker (re-verify after in-flake graduation)** —
|
- [ ] **Keyboard hotplug picker (re-verify after in-flake graduation)** —
|
||||||
plug an external keyboard post-login, pick a layout in rofi, confirm
|
plug an external keyboard post-login, pick a layout in rofi, confirm
|
||||||
it applies per-device only, persists in `settings.keyboard.devices`,
|
it applies per-device only, persists in `settings.keyboard.devices`,
|
||||||
and graduates into a `device{}` block on the next rebuild.
|
and graduates into a `device{}` block on the next rebuild.
|
||||||
- [ ] **Snapshots restore/rollback** — `nomarchy-menu snapshot` now opens
|
- [ ] **Snapshots restore/rollback** — ⚠ PRECONDITION: update to main ≥
|
||||||
|
a47aa3a and RELOGIN first (the polkit agent starts with the session).
|
||||||
|
Bernardo's 2026-07-04 Latitude findings 5/6 ("btrfs-assistant still
|
||||||
|
crashes", "menu snapshots shows nothing") match the PRE-fix behavior
|
||||||
|
exactly: no agent → pkexec fails silently (= menu does nothing), and
|
||||||
|
a direct unprivileged run crashes (= the libbtrfsutil bug). If either
|
||||||
|
still reproduces ON THE FIXED BUILD after relogin, reopen BACKLOG
|
||||||
|
item 4 as [stuck]. Then: `nomarchy-menu snapshot` now opens
|
||||||
the **btrfs-assistant GUI**: a *themed* polkit prompt must appear
|
the **btrfs-assistant GUI**: a *themed* polkit prompt must appear
|
||||||
(hyprpolkitagent — first on-hardware outing) and the GUI must open
|
(hyprpolkitagent — first on-hardware outing) and the GUI must open
|
||||||
as root. Also exercise the fzf fallback in a terminal
|
as root. Also exercise the fzf fallback in a terminal
|
||||||
|
|||||||
@@ -17,6 +17,26 @@ Template:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-07-04 — Keyboard cycle bind + parity (iteration #6) + Latitude triage
|
||||||
|
- **Task:** BACKLOG NOW#5; mid-iteration Bernardo delivered Latitude
|
||||||
|
5410 hardware-QA findings (the machine got a real session!).
|
||||||
|
- **Did:** `multiLayoutBinds` in keybinds.nix (SUPER+SHIFT+K →
|
||||||
|
`switchxkblayout current next`), gated in hyprland.nix AND the rofi
|
||||||
|
cheatsheet on the same comma-in-layout condition; `hyprland/language`
|
||||||
|
+ `#language` CSS added to both summer whole-swaps (shows even
|
||||||
|
single-layout there — static JSON can't gate; deliberate). Reverse
|
||||||
|
parity gap found (summer-night has idle_inhibitor, generated bar
|
||||||
|
doesn't) → folded into item 13. **Triage:** four new NOW items from
|
||||||
|
hardware: #21 Waybar crash on theme switch + no respawn (top),
|
||||||
|
#22 nameless network-menu entries, #23 `sys-rebuild` (no-update
|
||||||
|
rebuild), #24 Back-everywhere audit.
|
||||||
|
- **Verified:** V0 (JSON + flake check) + V1 both directions
|
||||||
|
(single-layout build: no bind; `us,de` via extendModules: bind + the
|
||||||
|
cheatsheet row render; summer JSONs parse).
|
||||||
|
- **Pending:** V3 cycle-bind check queued; items 21/22/24 carry V3
|
||||||
|
re-checks on the Latitude.
|
||||||
|
- **Next suggestion:** #21 (waybar crash — stability pillar, user-hit).
|
||||||
|
|
||||||
## 2026-07-04 — Webcam follow-ups (loop iteration #5, quick item)
|
## 2026-07-04 — Webcam follow-ups (loop iteration #5, quick item)
|
||||||
- **Task:** BACKLOG NOW#7 (Bernardo asked for "a quick item").
|
- **Task:** BACKLOG NOW#7 (Bernardo asked for "a quick item").
|
||||||
- **Did:** template `home.packages` gains a commented "Webcam tuning"
|
- **Did:** template `home.packages` gains a commented "Webcam tuning"
|
||||||
|
|||||||
@@ -721,11 +721,23 @@ how to override it. Items marked ✓ are shipped.
|
|||||||
which the watcher sees it as declared and steps back. Eval-verified
|
which the watcher sees it as declared and steps back. Eval-verified
|
||||||
(graduation + precedence) and the writer round-trips; **still needs the
|
(graduation + precedence) and the writer round-trips; **still needs the
|
||||||
on-hardware hotplug re-verify** (the picker path isn't testable in CI).
|
on-hardware hotplug re-verify** (the picker path isn't testable in CI).
|
||||||
- Remaining: a multi-layout cycle bind (`hyprctl switchxkblayout` / xkb
|
- ✓ **Multi-layout cycle bind + summer parity (2026-07-04):**
|
||||||
`grp:` options) for switching layouts on one keyboard; add the
|
`SUPER+SHIFT+K` → `hyprctl switchxkblayout current next`, rendered
|
||||||
`hyprland/language` module to the summer whole-swap themes for parity
|
only when the session layout has a comma (same gate as the Waybar
|
||||||
(the gating doesn't translate to their static JSON). Keep the system
|
language indicator) — data lives in `keybinds.nix` as a separate
|
||||||
(console/initrd) and session layouts in sync (the LUKS-keymap work).
|
`multiLayoutBinds` list, so hyprland.nix and the cheatsheet consume
|
||||||
|
one source and gate identically (verified both ways: absent on a
|
||||||
|
single layout; bind + cheatsheet row render under `us,de` via
|
||||||
|
extendModules). `current` targets the focused keyboard, so a
|
||||||
|
per-device-overridden board (one layout) is a no-op, never a leak.
|
||||||
|
`hyprland/language` (` {short}`) added to both summer
|
||||||
|
`waybar.jsonc` whole-swaps + `#language` in their CSS — static JSON
|
||||||
|
can't eval-gate, so on summer themes the module shows even with one
|
||||||
|
layout (a deliberate parity-over-minimalism call; it's small).
|
||||||
|
Noticed en route: summer-night carries an `idle_inhibitor` the
|
||||||
|
generated bar lacks — folded into the idle-inhibit backlog item.
|
||||||
|
- Remaining: keep the system (console/initrd) and session layouts in
|
||||||
|
sync (the LUKS-keymap work).
|
||||||
- ✓ **Do-Not-Disturb:** swaync DND toggle wired into the menu
|
- ✓ **Do-Not-Disturb:** swaync DND toggle wired into the menu
|
||||||
(`nomarchy-menu dnd`, in the picker, SUPER+CTRL+D) and a Waybar bell
|
(`nomarchy-menu dnd`, in the picker, SUPER+CTRL+D) and a Waybar bell
|
||||||
indicator (`custom/notification` via `swaync-client -swb`: shows the
|
indicator (`custom/notification` via `swaync-client -swb`: shows the
|
||||||
|
|||||||
@@ -304,8 +304,13 @@ in
|
|||||||
# Rendered from ./keybinds.nix (the cheatsheet reads the same list),
|
# Rendered from ./keybinds.nix (the cheatsheet reads the same list),
|
||||||
# plus the generated per-workspace binds. Like exec-once above this
|
# plus the generated per-workspace binds. Like exec-once above this
|
||||||
# stays a normal-priority list, so a downstream `bind = [...]`
|
# stays a normal-priority list, so a downstream `bind = [...]`
|
||||||
# concatenates rather than replaces.
|
# concatenates rather than replaces. The layout-cycle bind only
|
||||||
bind = map mkBind keybinds.binds ++ workspaceBinds;
|
# exists when there is something to cycle (same comma condition
|
||||||
|
# the Waybar language indicator gates on).
|
||||||
|
bind = map mkBind (keybinds.binds
|
||||||
|
++ lib.optionals (lib.hasInfix "," config.nomarchy.keyboard.layout)
|
||||||
|
keybinds.multiLayoutBinds)
|
||||||
|
++ workspaceBinds;
|
||||||
|
|
||||||
# Media keys via swayosd-client: it performs the action AND shows
|
# Media keys via swayosd-client: it performs the action AND shows
|
||||||
# the on-screen display (the nomarchy.osd module). e = repeat,
|
# the on-screen display (the nomarchy.osd module). e = repeat,
|
||||||
|
|||||||
@@ -59,6 +59,15 @@
|
|||||||
{ mods = ""; key = "Print"; action = "exec, grim -g \"$(slurp)\" - | wl-copy"; desc = "Screenshot region → clipboard"; }
|
{ mods = ""; key = "Print"; action = "exec, grim -g \"$(slurp)\" - | wl-copy"; desc = "Screenshot region → clipboard"; }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# Rendered only when the session has >1 layout (a comma in
|
||||||
|
# nomarchy.keyboard.layout) — hyprland.nix and rofi.nix both gate on
|
||||||
|
# that same condition, so the bind and its cheatsheet row stay in
|
||||||
|
# step. `current` targets the focused keyboard, so a board with its
|
||||||
|
# own per-device layout (a single one) is a no-op, never a leak.
|
||||||
|
multiLayoutBinds = [
|
||||||
|
{ mods = "$mod SHIFT"; key = "K"; action = "exec, hyprctl switchxkblayout current next"; desc = "Cycle keyboard layout"; }
|
||||||
|
];
|
||||||
|
|
||||||
extra = [
|
extra = [
|
||||||
{ keys = "SUPER + 1-9"; desc = "Switch to workspace 1-9"; }
|
{ keys = "SUPER + 1-9"; desc = "Switch to workspace 1-9"; }
|
||||||
{ keys = "SUPER + SHIFT + 1-9"; desc = "Move window to workspace 1-9"; }
|
{ keys = "SUPER + SHIFT + 1-9"; desc = "Move window to workspace 1-9"; }
|
||||||
|
|||||||
@@ -119,7 +119,9 @@ let
|
|||||||
key = lib.replaceStrings [ "question" "slash" ] [ "?" "/" ] b.key;
|
key = lib.replaceStrings [ "question" "slash" ] [ "?" "/" ] b.key;
|
||||||
in prefix + key;
|
in prefix + key;
|
||||||
cheatRows =
|
cheatRows =
|
||||||
map (b: padRight 22 (prettyKeys b) + b.desc) keybinds.binds
|
map (b: padRight 22 (prettyKeys b) + b.desc) (keybinds.binds
|
||||||
|
++ lib.optionals (lib.hasInfix "," cfg.keyboard.layout)
|
||||||
|
keybinds.multiLayoutBinds)
|
||||||
++ map (e: padRight 22 e.keys + e.desc) keybinds.extra;
|
++ map (e: padRight 22 e.keys + e.desc) keybinds.extra;
|
||||||
cheatsheetFile = pkgs.writeText "nomarchy-keybinds.txt"
|
cheatsheetFile = pkgs.writeText "nomarchy-keybinds.txt"
|
||||||
(lib.concatStringsSep "\n" cheatRows + "\n");
|
(lib.concatStringsSep "\n" cheatRows + "\n");
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ window#waybar {
|
|||||||
#pulseaudio,
|
#pulseaudio,
|
||||||
#custom-powerprofile,
|
#custom-powerprofile,
|
||||||
#custom-nightlight,
|
#custom-nightlight,
|
||||||
|
#language,
|
||||||
#custom-updates,
|
#custom-updates,
|
||||||
#battery,
|
#battery,
|
||||||
#tray,
|
#tray,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
"modules-left": ["custom/launcher", "clock", "clock#date"],
|
"modules-left": ["custom/launcher", "clock", "clock#date"],
|
||||||
"modules-center": ["hyprland/workspaces"],
|
"modules-center": ["hyprland/workspaces"],
|
||||||
"modules-right": ["pulseaudio", "custom/powerprofile", "custom/nightlight", "custom/updates", "battery", "tray", "custom/vpn", "custom/notification", "custom/powermenu"],
|
"modules-right": ["pulseaudio", "custom/powerprofile", "custom/nightlight", "hyprland/language", "custom/updates", "battery", "tray", "custom/vpn", "custom/notification", "custom/powermenu"],
|
||||||
|
|
||||||
"hyprland/workspaces": {
|
"hyprland/workspaces": {
|
||||||
"disable-scroll": true,
|
"disable-scroll": true,
|
||||||
@@ -86,6 +86,11 @@
|
|||||||
"on-click": "nomarchy-vpn"
|
"on-click": "nomarchy-vpn"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"hyprland/language": {
|
||||||
|
"format": " {short}",
|
||||||
|
"tooltip": false
|
||||||
|
},
|
||||||
|
|
||||||
"custom/nightlight": {
|
"custom/nightlight": {
|
||||||
"return-type": "json",
|
"return-type": "json",
|
||||||
"interval": 3,
|
"interval": 3,
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ window#waybar {
|
|||||||
#pulseaudio,
|
#pulseaudio,
|
||||||
#custom-powerprofile,
|
#custom-powerprofile,
|
||||||
#custom-nightlight,
|
#custom-nightlight,
|
||||||
|
#language,
|
||||||
#custom-updates,
|
#custom-updates,
|
||||||
#idle_inhibitor,
|
#idle_inhibitor,
|
||||||
#battery,
|
#battery,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
"modules-left": ["custom/nomarchy", "clock", "clock#date"],
|
"modules-left": ["custom/nomarchy", "clock", "clock#date"],
|
||||||
"modules-center": ["hyprland/workspaces"],
|
"modules-center": ["hyprland/workspaces"],
|
||||||
"modules-right": ["idle_inhibitor", "pulseaudio", "custom/powerprofile", "custom/nightlight", "custom/updates", "battery", "tray", "custom/vpn", "custom/notification", "custom/powermenu"],
|
"modules-right": ["idle_inhibitor", "pulseaudio", "custom/powerprofile", "custom/nightlight", "hyprland/language", "custom/updates", "battery", "tray", "custom/vpn", "custom/notification", "custom/powermenu"],
|
||||||
|
|
||||||
"hyprland/workspaces": {
|
"hyprland/workspaces": {
|
||||||
"disable-scroll": true,
|
"disable-scroll": true,
|
||||||
@@ -93,6 +93,11 @@
|
|||||||
"on-click": "nomarchy-vpn"
|
"on-click": "nomarchy-vpn"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"hyprland/language": {
|
||||||
|
"format": " {short}",
|
||||||
|
"tooltip": false
|
||||||
|
},
|
||||||
|
|
||||||
"custom/nightlight": {
|
"custom/nightlight": {
|
||||||
"return-type": "json",
|
"return-type": "json",
|
||||||
"interval": 3,
|
"interval": 3,
|
||||||
|
|||||||
Reference in New Issue
Block a user