Piping stdout without draining while blocking in child.wait() risks deadlock
if a command writes more than one OS pipe buffer (~64 KB on Linux). Current
callers (systemctl, niri msg, loginctl) stay well under that, but the
structure was fragile. stdout is now discarded; stderr continues to be
captured for error reporting.
Remove the Hekate persona block from CLAUDE.md and rewrite prior
DECISIONS entries from Hekate and leftover Ragnar to ClaudeCode
for consistency with the rest of the ecosystem.
After cancelling a confirmation prompt, the focused widget was removed
from the tree without restoring focus to the action buttons. With
layer-shell exclusive keyboard mode, GTK does not recover focus
automatically — the UI became keyboard-unreachable.
- Replace canonicalize() with symlink_metadata + is_file + !is_symlink for avatar
lookup (prevents symlink traversal to arbitrary files)
- Fix blur padding offset from (0,0) to (-pad,-pad) to prevent edge darkening
- Add MAX_BLUR_DIMENSION (1920px) downscale before GPU blur
- Validate blur per config source (invalid user value preserves system default)
- Wallpaper: use symlink_metadata + is_file + !is_symlink in resolve_background_path
paru reads .SRCINFO (not PKGBUILD) for version comparison during
sysupgrade. Without updating .SRCINFO, paru never detects upgrades
for PKGBUILD repository packages.
Colloid-Catppuccin theme loaded via ~/.config/gtk-4.0/gtk.css at
PRIORITY_USER (800) was overriding moonset's PRIORITY_APPLICATION (600),
causing action buttons to lose their circular border-radius.
- Use STYLE_PROVIDER_PRIORITY_USER for app CSS provider
- Replace border-radius: 50% with 9999px (GTK4 CSS percentage quirk)
GskBlurNode samples pixels outside texture bounds as transparent,
causing visible darkening at wallpaper edges. Fix renders the texture
with 3x-sigma padding before blur, then clips back to original size.
Three parallel audits (quality, performance, security) identified issues
across the codebase. This commit addresses all remaining findings:
- Replace busy-loop polling in run_command with child.wait() + timeout thread
- Canonicalize ~/.face and AccountsService avatar paths to prevent symlink abuse
- Add detect_locale_with() DI function for testable locale detection
- Move config I/O from activate() to main() to avoid blocking GTK main loop
- Validate background_blur range (0–200), reject invalid values with warning
- Remove embedded wallpaper from GResource — moonarch provides it via filesystem
(binary size ~3.2MB → ~1.3MB)
Replace env_logger with systemd-journal-logger for consistent logging
across moonset/moonlock/moongreet. Add MOONSET_DEBUG env var and debug
statements across all modules. Also includes shared blur cache for
multi-monitor and detached moonlock spawn for lock action.
Replace image crate + disk cache blur with GPU-side GskBlurNode,
symmetric with moonlock and moongreet. Removes ~15 transitive
dependencies and ~160 lines of caching code. Blur now happens once
on the GPU at widget realization — zero startup latency, no cache
management needed.
Without this, app.quit() destroys windows instantly, creating a jarring
pop-out. Now all windows fade out over 250ms (matching the fade-in)
before the app exits. Uses the same CSS opacity transition — just
removes the "visible" class and defers quit via glib timeout.
The Rust code already adds a "visible" CSS class on map, but the
stylesheet had no corresponding opacity transition. Add 250ms ease-in
fade via GPU-accelerated CSS opacity to eliminate the visual pop-in.
- Fix BGRA→RGBA channel swap in apply_blur so image::RgbaImage semantics
match the actual pixel data from GDK texture download
- Logout now calls app.quit() like lock does, via new quit_after field on
ActionDef (replaces fragile magic string comparison)
- Log TOML parse errors to stderr instead of silently ignoring
- Remove pointless zlib compression of JPEG wallpaper in GResource
- Add tests for quit_after behavior and config error handling
First launch with blur blurs and saves to ~/.cache/moonset/. Subsequent
starts load the cached PNG directly (~9x faster). Cache invalidates
when wallpaper path, size, mtime, or sigma changes.
Add CHANGELOG documenting all changes since 0.1.0 and the initial
release. Add DECISIONS.md as an architectural decision log. Update
CLAUDE.md to reflect current architecture. Bump to 0.1.1 for the
security and correctness fixes in the previous commit.
- Use absolute paths for all binaries in power.rs to prevent PATH hijacking
- Implement POWER_TIMEOUT via try_wait() polling (was declared but unused)
- Fix potential panic in load_background_texture when GResource path
fails to_str() — now falls back to known wallpaper resource path
- Compress wallpaper.jpg in GResource bundle (saves ~374 KB in binary)
- Merge double idle_add_local_once into single cycle for faster focus
- Centralize GRESOURCE_PREFIX as pub(crate) const in main.rs
- Fix fallback user UID from 0 (root) to u32::MAX
- Fix CSS comment: "square card" → "circular card" (border-radius: 50%)
Translate README.md and config/moonset.toml comments from German
to English to enforce the repo language policy. Remove journal.md
as it was a one-time snapshot, not an actively maintained document.
- Replace manual icon theme lookup + Pixbuf scaling with native
GTK4 Image::from_icon_name() (uses internal cache + GPU rendering)
- Decode wallpaper texture once and share across all windows
instead of N+1 separate JPEG decodes
- Load file-based avatars asynchronously via gio::spawn_blocking
to avoid blocking the UI thread
Feature-parity with Python v0.2.0. Same CSS, same UI, same actions.
Single 3.1 MB binary with embedded resources (CSS, wallpaper, avatar).
Modules: power.rs, i18n.rs, config.rs, users.rs, panel.rs, main.rs
45 unit tests passing. Python sources retained as reference.
loginctl lock-session requires a D-Bus listener that is
difficult to set up reliably. Direct moonlock invocation
is simpler and works immediately.
Also removes CSS fade-in animation (low-fps on layer shell).
Panel without set_monitor so compositor places it on focused output.
Wallpaper windows on TOP layer on all monitors (below OVERLAY panel).
Transparent confirmation dialog background.
Instead of guessing the primary monitor (unreliable on Niri),
show the panel with action buttons on every monitor. All get
keyboard exclusive so the focused monitor captures input.
Also set exclusive_zone -1 to overlay Waybar, and delay
initial focus grab via GLib.idle_add for layer shell readiness.
- Replace hardcoded colors with @theme_* variables for consistency
- Card backgrounds use alpha for subtle translucency over wallpaper
- Confirmation dialog grabs focus on "No" button for safe keyboard nav
Same pattern as moonlock — re-exec the process with LD_PRELOAD
set so gtk4-layer-shell is loaded before libwayland-client.
Skipped during tests via pytest/unittest module detection.