Diagnosed live in a VM: the bar rendered twice (two stacked rows). `hyprctl
layers` showed two `gtk-layer-shell` bar surfaces owned by two different eww
pids — an `eww daemon` and an `eww open bar`. The monitor count was correctly 1,
so the per-monitor loop was not at fault.
Root cause: ewwstart.sh started `eww daemon` and then immediately ran `eww open`
in a loop. The first `eww open` frequently ran before the freshly-started daemon
was ready, so it spawned its OWN second daemon and drew a second bar.
Fix (all three DE variants — hyprlua, hyprland, niri):
- Drop the separate `eww daemon`; rely on `eww open` to auto-start the daemon and
block until the window is mapped, so the first call establishes a single daemon
that the rest reuse (no race, no second daemon).
- killall eww + short settle before opening, so a reload starts clean.
- hyprlua/hyprland: count monitors via `hyprctl monitors -j | jq length` instead
of the fragile `hyprctl monitors | grep ID | wc -l`.
- export GTK_THEME (the bare assignment never took effect).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM
Same Vicinae theming bug as hyprlua, now fixed across the remaining DE modules:
- Vicinae loads named custom themes only from ~/.local/share/vicinae/themes/*.toml,
not ~/.config/vicinae/. Both modules now deploy cyberqueer.toml there so
settings.json's theme.dark.name="cyberqueer" resolves instead of falling back
to the stock dark theme.
- hyprland: the source had settings.json double-nested at vicinae/vicinae/, so it
deployed to ~/.config/vicinae/vicinae/settings.json and was never read. Flattened
the source to vicinae/{settings.json,cyberqueer.toml} (matching hyprlua), so it
now lands at ~/.config/vicinae/settings.json.
- niri/vicinae is a symlink to hyprlua/vicinae and niri.sh uses `cp -rL`, so its
settings.json was already correct; only the theme-deploy line was needed.
Verified by simulating both deploys into a temp HOME: final layout matches the
known-good hyprlua result.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM
Target ~/.config/hypr/usr/monitors.lua (the config Hyprland actually
loads) so `hyprctl reload` applies layouts immediately; the Dotfiles
repo copy stays the deploy source and is no longer written.
Add overlap geometry helpers and integrate them into the apply flow:
- block moves that would drive a monitor into a neighbor (TUI coords)
- snap positions to the MOVE_STEP_FINE grid to avoid frozen digits
- auto-resolve snap-induced collisions by re-reading the live layout
and nudging the moved monitor clear, up to MAX_RESOLVE_ITERS
- warn on residual overlap after apply and after save/reload
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the machine-specific DP-1/DP-3/HDMI-A-1 layout with a wildcard
catch-all rule (preferred mode, auto position, auto scale) that brings up
any connected output. Per-machine layout stays the job of monitor-manager,
which overwrites this file.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Installs ydotool via pacman and OpenDeck via Flatpak, wires ydotoold
and OpenDeck into the Hyprland autostart. Registers the module in the
TUI installer, answerfile generator, and docs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Steps taken:
- Added overlay/airootfs/etc/os-release with NAME="M-Archy", ID=m-archy,
ID_LIKE=arch so fastfetch and other tools show the correct distro name while
keeping pacman/AUR helpers happy via ID_LIKE.
- Added overlay/airootfs/etc/fastfetch/config.jsonc — system-wide fastfetch
config equivalent to:
--logo-type file --logo /etc/fastfetch/m-archy-SPC.txt
--logo-color-1 red --logo-color-2 red --color red
(keys + title colored red, custom file logo, no other defaults changed).
- Added overlay/airootfs/etc/fastfetch/m-archy-SPC.txt — copy of the pin.txt
ASCII logo; lands on the live ISO via the existing cp -r overlay/airootfs/
step in build.sh, no build.sh or profiledef.sh changes needed.
- Renamed resources/pin.txt → resources/m-archy-SPC.txt and updated all
references in .bashrc, .zshrc, and both kitty/bash-remoteconf files.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
logical_width/height now divide physical pixels by scale so the canvas
correctly represents the compositor's coordinate space (e.g. 3840px @1.5x
is 2560 logical px wide, not 3840).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The merge agent reverted the switch from usr.monitors to root-level monitors
(managed by hyprmoncfg). Restore main's version and update the surrounding
comment to explain the new arrangement.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces fixed 0.25 increments with mathematically valid scales p/q
(lowest terms, q≤6) where both width/s and height/s are integers.
For 1920x1200 this gives 25 steps including 2.4, matching what
Hyprland actually applies — no more mismatch between TUI display
and live value.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
hyprctl returns mirrorOf:"none" (string) for non-mirrored monitors.
Python treated it as truthy, causing apply_monitor to always emit a
mirror command, resetting resolution and scale on every keypress.
Also restores monitors.lua with correct mode/scale/transform fields.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Python curses TUI for managing Hyprland monitors interactively:
- Canvas shows monitors as boxes at their real relative positions
- Tab/Shift+Tab to cycle selection; hjkl/HJKL to move (50/10 px)
- u/i to rotate CCW/CW; n/N to cycle display modes live
- m to mirror (pick target with Tab, confirm with Enter)
- s saves to hypr/usr/monitors.lua atomically
- Scale cached and only recomputed on resize or viewport overflow
- Bound to Super+Shift+M as a centered-L floating kitty popup
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- caffeine: inhibit only `idle`, not `sleep`, so lid close still locks
- binds: lid-close unconditionally calls hyprlock; lid-open does dpms on
- hypridle: reduce lock timeout from 3 min to 2.5 min (150 s)
- eww (all 3 variants): add caffeine toggle button (☕/) with tooltip
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Brio (and most UVC webcams) expose a second /dev/video* node that is
metadata-capture only. The chooser listed it, the existence-only guard
accepted it, and howdy aborted with a bare exit 1 because that node can
never deliver a frame.
- list_cameras: skip nodes with no capture pixel formats (is_capture_device)
- howdy_camera_ok: require an actual capture node, not just an existing path,
so a stale metadata-node device_path triggers reconfiguration
- howdy_add: tee howdy's output and surface the real reason on failure
instead of only the exit code
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Enrollment failed because howdy's own config.ini kept device_path=none,
so VideoCapture aborted before ever opening the lens (generic exit 1).
The presence-detect camera setting is separate and never reached howdy.
- add a "Howdy face auth — configure camera" menu entry that writes
device_path into /usr/lib/security/howdy/config.ini
- guard howdy_add: detect an unset/stale device_path and offer to fix it
before enrolling, instead of surfacing howdy's opaque exit 1
- shared camera chooser shows a live still from the selected node via
kitty's icat (gated on KITTY_WINDOW_ID), used for presence + howdy
- support non-IR colour webcams: detect IR vs colour from the preview's
saturation and tune recording_plugin (ffmpeg) + dark_threshold per type
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
howdy 2.x installs its CLI under root-only /usr/lib/security/howdy/ with
/usr/bin/howdy symlinked into it, so `command -v howdy` reports "not found"
for a normal user — the script wrongly thought howdy wasn't installed and
tried to reinstall it. Detect via the symlink and pacman -Qq instead; all
ops already run through `sudo howdy`.
This version also has no pam_howdy.so — it authenticates via pam_python.so
loading /usr/lib/security/howdy/pam.py. Switch the emitted PAM block to that
module and add pam_python_require() to install the AUR `pam-python`
dependency, replacing the dead pam_howdy.so existence check.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
enroll-biometrics.sh used non-existent howdy flags and a broken install
path. Fixes and additions:
- howdy add: label has no flag; feed it via stdin, use -U "$USER"
- howdy remove: id is positional, not -I
- list/test: pass -U "$USER" so all ops target the same account
- install: howdy is AUR-only, so the pacman fallback could never work;
require an AUR helper (yay/paru) and message clearly if absent
- new PAM 2FA menu: enroll FIDO key + wire howdy + pam_u2f into
sudo/hyprlock/login (both factors required, password fallback kept)
- hyprlock gets its own clean fallback (include system-auth) so the
block is not re-run via include login
- idempotent, timestamped backups, and a symmetric teardown option
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Start chamel as a daemon in autostart so keybinds (toggle/clear/
clear-and-deactivate) work on first use. Fix stale hyprland script
paths in frequentcommands.list and add all utility scripts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add presence-detect.sh daemon: checks webcam every 2 min via OpenCV haar
cascade (presence_detect.py); holds systemd-inhibit --what=idle lock while a
face is detected so hypridle never fires during an active session
- Add enroll-biometrics.sh: Cyberqueer dialog TUI for camera configuration/test
and howdy face auth enrollment (add/list/remove/test models)
- Rewrite caffeine.sh (hyprlua + niri): replace kill/restart of idle daemon with
systemd-inhibit --what=idle:sleep + PID file; idle daemon stays running
- Fix hypridle.conf: correct ;; → ; in after_sleep_cmd, add
ignore_dbus_inhibit=false, bump lock timeout 120s→180s to account for the
2-min presence detection cycle
- Wire Super+Shift+B keybind for enrollment TUI in both hyprlua and niri
- Fix niri/scripts/python: was real dir with inner symlink causing cp -rL to
create a nested python/python/ hierarchy; replaced with direct symlink
- Add python-opencv and v4l-utils to hyprlua + niri installers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reverts incorrect touch_long_press_time option (not a real Hyprland setting).
Adds evdev-right-click-emulation install + systemd enable to the tablet (T)
eww bar selection in the hyprlua installer.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Switch to -i 25 icon size and -d autohide flag; drop old per-position
margin flags and -r resident flag; apply uniform command to all position
binds, toggle, and autostart reference.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Position binds used && which prevented the dock from starting when it
wasn't already running. Toggle bind had no kill logic so pressing it
again stacked a second instance instead of hiding the dock.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reduce icon size to 28 (-is 28) and increase all margins to 25px for a
cleaner, less intrusive dock appearance.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Restores the screenrotate centre widget (OSK, prev/next workspace,
rotate CW/CCW, close) that was lost to an external edit, and re-applies
all session changes on top:
- Circular (round) class on OSK, drawer, and all five screenrotate buttons
- Close button uses hyprctl dispatch killactive
- Workspace buttons use focusleft/focusright scripts
- winsworks hexpand + metric/scale hexpand for responsive volume slider
- Clock widget split into icon label + time label with a space between
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Switch font to 16pt with proportionally scaled padding/margins
- Full pill border-radius (999px) on all widgets
- Circular buttons (50%) for OSK, drawer, close, workspace, screen rotation
- Volume slider expands to fill available bar width (hexpand)
- Workspace buttons use focusleft.sh / focusright.sh scripts
- Add space between clock icon and time
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>