Compare commits
9 Commits
7b6ba3b0d1
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d101b23351 | |||
| e6b7f53794 | |||
| 806841d435 | |||
| f9f73db10f | |||
| dbc2997de0 | |||
| dc47d1a6ec | |||
| 952776c4f9 | |||
| f4d60d387e | |||
| 1e8b0d4ab0 |
@@ -7,6 +7,15 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
paths:
|
||||||
|
# Only files that the moonarch-git PKGBUILD actually packages.
|
||||||
|
# README.md, DECISIONS.md, scripts/post-install.sh, scripts/lib.sh,
|
||||||
|
# CI workflow edits, etc. don't change the built package and must
|
||||||
|
# not trigger a rebuild.
|
||||||
|
- 'defaults/**'
|
||||||
|
- 'packages/**'
|
||||||
|
- 'scripts/moonarch-update'
|
||||||
|
- 'scripts/moonarch-doctor'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update-pkgver:
|
update-pkgver:
|
||||||
|
|||||||
@@ -1,68 +1,94 @@
|
|||||||
# Moonarch
|
# Moonarch
|
||||||
|
|
||||||
Reproduzierbares Arch-Linux-Setup basierend auf archinstall + Post-Install-Automatisierung.
|
Reproducible Arch Linux setup based on archinstall + post-install automation.
|
||||||
|
|
||||||
## Projektstruktur
|
## Project Structure
|
||||||
|
|
||||||
- `config/` — archinstall-Konfiguration (inkl. custom-commands die das Repo nach /opt/moonarch klonen, root-owned)
|
- `config/` — archinstall configuration (incl. custom-commands that clone the repo to /opt/moonarch, root-owned)
|
||||||
- `scripts/` — Post-Install- und Helper-Scripts
|
- `scripts/` — post-install and helper scripts
|
||||||
- `packages/` — Paketlisten (offiziell + AUR), getrennt gepflegt
|
- `packages/` — package lists (official + AUR), maintained separately
|
||||||
- `defaults/` — XDG-Configs, Shell-Config, Helper-Binaries, systemd Services, udev-Regeln, greetd/moongreet-Config, Wallpaper
|
- `defaults/` — XDG configs, shell config, helper binaries, systemd services, udev rules, greetd/moongreet config, wallpaper
|
||||||
|
|
||||||
## Battery Conservation Mode
|
## Battery Conservation Mode
|
||||||
|
|
||||||
Laptops mit `charge_control_end_threshold`-Support (ThinkPad, Framework, etc.) erhalten einen Waybar-Toggle:
|
Laptops with `charge_control_end_threshold` support (ThinkPad, Framework, etc.) get a Waybar toggle:
|
||||||
- Klick auf das Battery-Modul schaltet zwischen 80% und 100% Ladegrenze um
|
- Clicking the battery module toggles the charge limit between 80% and 100%
|
||||||
- Bei aktiver Conservation erscheint ein ♥-Icon neben der Battery-Anzeige
|
- When conservation is active, a ♥ icon appears next to the battery indicator
|
||||||
- Zustand wird in `/var/lib/moonarch/batsaver-threshold` persistiert und beim Boot via systemd-Service wiederhergestellt
|
- State is persisted in `/var/lib/moonarch/batsaver-threshold` and restored on boot via a systemd service (`moonarch-batsaver-restore`)
|
||||||
- udev-Regel gibt Gruppe `wheel` Schreibzugriff auf den Threshold (kein sudo nötig)
|
- Toggle flow: `moonarch-batsaver-toggle` (user script) reads sysfs, decides 80↔100, calls `pkexec /usr/bin/moonarch-batsaver-apply $NEW` for the privileged sysfs+state write step. Standard pkexec prompt (password once per session cache)
|
||||||
- Auf Desktops ohne Battery-Support versteckt sich das Feature komplett
|
- On desktops without battery support the feature is hidden entirely
|
||||||
|
|
||||||
## Nightlight (Blaufilter)
|
## Nightlight (Blue Light Filter)
|
||||||
|
|
||||||
Waybar-Toggle für wlsunset (Wayland-nativer Blaufilter), persistenter Zustand via systemd:
|
Waybar toggle for wlsunset (Wayland-native blue light filter), persistent state via systemd:
|
||||||
- `wlsunset.service` (systemd User-Service) mit `After=kanshi.service` — startet erst wenn alle Outputs konfiguriert sind
|
- `wlsunset.service` (systemd user service) with `After=kanshi.service` — starts only once all outputs are configured
|
||||||
- Klick auf das Nightlight-Modul in `group/brightness` toggled wlsunset an/aus (`enable --now` / `disable --now`)
|
- **Default OFF** — fresh installs start without the filter. The PKGBUILD deliberately creates NO symlink for `wlsunset` in `/etc/systemd/user/graphical-session.target.wants/`, and post-install.sh does not enable the service.
|
||||||
- Zustand überlebt Reboots (enabled/disabled bleibt bestehen)
|
- Clicking the nightlight module in `group/brightness` toggles wlsunset on/off (`enable --now` / `disable --now`)
|
||||||
- Aktiver Zustand zeigt in Catppuccin Yellow, inaktiv in Standard-Textfarbe
|
- State survives reboots (user-scope symlink in `~/.config/systemd/user/...wants/`)
|
||||||
- Signal SIGRTMIN+11 für sofortiges Waybar-Refresh
|
- Active state shows in Catppuccin Yellow, inactive in the default text color
|
||||||
- Scripts: `moonarch-nightlight` (Toggle), `moonarch-waybar-nightlight` (Status-JSON)
|
- Signal SIGRTMIN+11 for immediate Waybar refresh
|
||||||
|
- Scripts: `moonarch-nightlight` (toggle), `moonarch-waybar-nightlight` (status JSON)
|
||||||
|
- **Important**: Never create a global-scope symlink at `/etc/systemd/user/...wants/wlsunset.service` — it overrides any user `disable` and makes the filter effectively impossible to turn off.
|
||||||
|
|
||||||
## Waybar Config Merger (moonarch-waybar)
|
## Waybar Config Merger (moonarch-waybar)
|
||||||
|
|
||||||
Waybar wird über `moonarch-waybar` gestartet (nicht direkt). Der Wrapper merged eine optionale User-Config (`~/.config/waybar/userconfig`) mit der System-Config (`/etc/xdg/waybar/config`):
|
Waybar is started via `moonarch-waybar` (not directly). The wrapper merges an optional user config (`~/.config/waybar/userconfig`) with the system config (`/etc/xdg/waybar/config`):
|
||||||
- `prepend`/`append`-Keys in der userconfig erweitern `modules-left`/`modules-center`/`modules-right` Arrays
|
- `prepend`/`append` keys in the userconfig extend the `modules-left`/`modules-center`/`modules-right` arrays
|
||||||
- Alle anderen Top-Level-Keys werden als Modul-Definitionen per Object-Merge eingefügt
|
- All other top-level keys are inserted as module definitions via object merge
|
||||||
- Merge wird nur bei Änderungen ausgeführt (Timestamp-Vergleich)
|
- The merge runs only on changes (timestamp comparison)
|
||||||
- Bei Fehler: `notify-send` + `logger`, Waybar startet mit System-Config
|
- On error: `notify-send` + `logger`, Waybar starts with the system config
|
||||||
- Generiert `~/.config/waybar/style.css` mit `@import` der System-Styles falls nicht vorhanden
|
- Generates `~/.config/waybar/style.css` with an `@import` of the system styles if not present
|
||||||
- Benötigt `jq` (in PKGBUILD als Dependency)
|
- Requires `jq` (declared as a dependency in the PKGBUILD)
|
||||||
- System-Config muss valides JSON sein (kein JSONC)
|
- The system config must be valid JSON (no JSONC)
|
||||||
|
|
||||||
## mpv + ModernZ OSC
|
## mpv + ModernZ OSC
|
||||||
|
|
||||||
Videoplayer ist `mpv` mit [ModernZ](https://github.com/Samillion/ModernZ) als OSC, Thumbnails via thumbfast:
|
The video player is `mpv` with [ModernZ](https://github.com/Samillion/ModernZ) as the OSC, thumbnails via thumbfast:
|
||||||
- `mpv-modernz-git` liefert `modernz.lua` + Font + Default-Config nach `/etc/mpv/`
|
- `mpv-modernz-git` provides `modernz.lua` + font + default config to `/etc/mpv/`
|
||||||
- `mpv-thumbfast-git` liefert `thumbfast.lua` nach `/etc/mpv/scripts/` (wird von ModernZ automatisch erkannt)
|
- `mpv-thumbfast-git` provides `thumbfast.lua` to `/etc/mpv/scripts/` (auto-detected by ModernZ)
|
||||||
- `defaults/etc/mpv/mpv.conf` wird von moonarch-git direkt nach `/etc/mpv/mpv.conf` installiert (owned)
|
- `defaults/etc/mpv/mpv.conf` is installed directly to `/etc/mpv/mpv.conf` by moonarch-git (owned)
|
||||||
- Stock-OSC + Titelleiste deaktiviert, `autofit-larger=80%x80%` cappt übergroße Fenster
|
- Stock OSC + title bar disabled, `autofit-larger=80%x80%` caps oversized windows
|
||||||
- ModernZ-Overrides per `script-opts-append` in mpv.conf: Orange-Akzent → Catppuccin Lavender (`#b4befe`), OSC-Scale 0.75, `window_title_font_size=18`, `ontop_button=no`
|
- ModernZ overrides via `script-opts-append` in mpv.conf: orange accent → Catppuccin Lavender (`#b4befe`), OSC scale 0.75, `window_title_font_size=18`, `ontop_button=no`
|
||||||
- **Wichtig**: mpv behandelt `#` als Mid-Line-Kommentar; Hex-Farben müssen gequotet werden: `script-opts-append="modernz-seekbarfg_color=#b4befe"` (nicht `\#`, das escaped nur und verschluckt den Rest)
|
- **Important**: mpv treats `#` as a mid-line comment; hex colors must be quoted: `script-opts-append="modernz-seekbarfg_color=#b4befe"` (not `\#`, which only escapes and swallows the rest)
|
||||||
- Niri öffnet mpv floating (`window-rule` in `defaults/xdg/niri/config.kdl`)
|
- Niri opens mpv floating (`window-rule` in `defaults/xdg/niri/config.kdl`)
|
||||||
|
|
||||||
## System Health Check (moonarch-doctor / moondoc)
|
## System Health Check (moonarch-doctor / moondoc)
|
||||||
|
|
||||||
Diagnose-Script das den Systemzustand gegen moonarch-Defaults prüft:
|
Diagnostic script that checks the system state against moonarch defaults:
|
||||||
- Pakete (official.txt + aur.txt installiert? Orphans?)
|
- Packages (official.txt + aur.txt installed? Orphans?)
|
||||||
- System-Services (NetworkManager, bluetooth, greetd, ufw, auto-cpufreq, etc.)
|
- System services (NetworkManager, bluetooth, greetd, ufw, auto-cpufreq, etc.)
|
||||||
- User-Services (kanshi, wlsunset, stasis, walker, nautilus, cliphist-text, cliphist-image)
|
- User services (kanshi, wlsunset, stasis, walker, nautilus, cliphist-text, cliphist-image)
|
||||||
- Config-Dateien (SHA256-Vergleich deployed vs. moonarch-Default)
|
- Config files (SHA256 comparison deployed vs. moonarch default)
|
||||||
- Helper-Scripts + Symlinks (moonup, moondoc)
|
- Helper scripts + symlinks (moonup, moondoc)
|
||||||
- System-Config (UFW, Pacman/Paru Repos, Default Shell)
|
- System config (UFW, pacman/paru repos, default shell)
|
||||||
- Verzeichnisse + Permissions
|
- Directories + permissions
|
||||||
|
|
||||||
## Konventionen
|
## Fontconfig Defaults
|
||||||
|
|
||||||
- Paketlisten sind einfache Textdateien, ein Paket pro Zeile, Kommentare mit `#`
|
System-wide generic-family defaults via `defaults/etc/fonts/conf.d/65-moonarch-fonts.conf` (owned by moonarch-git):
|
||||||
- Shell-Scripts müssen POSIX-kompatibel oder explizit bash/zsh sein
|
- `sans-serif` → UbuntuSans Nerd Font, `monospace` → UbuntuSansMono Nerd Font
|
||||||
- Alle Pfade im archinstall-Config relativ zum Installationsziel
|
- Number **65**: loads after `60-latin.conf`, so the moonarch prefs beat the stock default (Noto/DejaVu). `local.conf` (at 51 via `51-local.conf`) loads too early and is overridden by 60-latin — therefore **do not** use it (it is also reserved for local user overrides).
|
||||||
|
- Aliases need `binding="strong"` — a weak `<prefer>` (fontconfig default) ranks behind the effective generic fallback and does not take effect.
|
||||||
|
- Only applies to apps that use generic families (e.g. Firefox web fallback). moonarch apps (Waybar, foot, GTK, walker, swaync) set the font explicitly.
|
||||||
|
|
||||||
|
## Browser Idle-Inhibit (xdg-desktop-portal)
|
||||||
|
|
||||||
|
So that windowed browser video (Firefox/Waterfox) keeps the screen awake, via `defaults/etc/xdg-desktop-portal/niri-portals.conf` (owned by moonarch-git, higher priority than niri's `/usr/share/xdg-desktop-portal/niri-portals.conf`):
|
||||||
|
- `xdg-desktop-portal-gtk` reports the `Inhibit` interface as success even though nobody implements it under Niri → Firefox believes the idle-inhibit went through the portal and does not use the native Wayland `idle-inhibit`. Result: no inhibitor, screen sleeps.
|
||||||
|
- Fix: `org.freedesktop.impl.portal.Inhibit=none` → Firefox falls back to `zwp_idle_inhibit`, which Niri honors. The remaining `[preferred]` lines are taken 1:1 from niri's default (portals.conf is not merged — the highest-priority file applies in full).
|
||||||
|
- stasis is uninvolved here: `monitor_media` (pactl) does not capture browser audio by design (non-browser players only); browsers go through the inhibit path.
|
||||||
|
- **Activation:** restart xdg-desktop-portal + the browser — Firefox queries portal support at startup.
|
||||||
|
|
||||||
|
### Windowed / Muted Video: wayland-pipewire-idle-inhibit
|
||||||
|
|
||||||
|
The portal fix only applies to **fullscreen** video — Firefox/Waterfox send the idle-inhibit only in fullscreen. Windowed video remains unprotected: stasis ignores browser audio (pactl, browser-excluded, no MPRIS backend), Niri gets no inhibitor, the screen sleeps mid-video.
|
||||||
|
|
||||||
|
Solution: `wayland-pipewire-idle-inhibit` (AUR, systemd user service; in `packages/aur.txt` + `post-install.sh` USER_SERVICES + moonarch-doctor). Holds a Wayland `zwp_idle_inhibit` inhibitor while PipeWire outputs audio (default threshold 5s). Niri then suppresses idle → stasis stays awake, daemon-independent. Releases automatically when audio stops.
|
||||||
|
- Catches windowed video **with sound**. Misses muted video (no audio signal) — edge case, accepted.
|
||||||
|
- **Never** put `waterfox`/a browser in stasis `inhibit_apps`: that inhibits unconditionally as long as the browser process runs → the system never idles. This exact wrong fix caused the "no idle" bug.
|
||||||
|
|
||||||
|
## Conventions
|
||||||
|
|
||||||
|
- Package lists are plain text files, one package per line, comments with `#`
|
||||||
|
- Shell scripts must be POSIX-compatible or explicitly bash/zsh
|
||||||
|
- All paths in the archinstall config are relative to the install target
|
||||||
|
|||||||
@@ -1,5 +1,32 @@
|
|||||||
# Decisions
|
# Decisions
|
||||||
|
|
||||||
|
## 2026-06-08 – Fontconfig generic-family defaults via owned conf.d (65)
|
||||||
|
- **Who**: Dominik, ClaudeCode
|
||||||
|
- **Why**: Waybar rendered in Hack instead of Ubuntu after the Nerd Fonts "Ubuntu"→"UbuntuSans" rename (Canonical rebrand). Waybar's style.css referenced the now-dead family "Ubuntu Nerd Font"; fontconfig token-matched it to "Hack Nerd Font". An unowned hand-written `/etc/fonts/local.conf` additionally pinned sans-serif/monospace to Hack — a stale relic.
|
||||||
|
- **Tradeoffs**: `local.conf` (loaded at 51 via `51-local.conf`) is structurally too early — `60-latin.conf` prepends Noto/DejaVu afterwards and wins, so its sans-serif pin never took effect. local.conf is also reserved for local admin overrides, and packaging it as owned would collide with pre-existing unowned files on pacman update. A conf.d file at 65 loads after 60-latin (convention: 60–69 = generic→family) and wins cleanly without conflict.
|
||||||
|
- **How**: New owned `defaults/etc/fonts/conf.d/65-moonarch-fonts.conf` maps sans-serif→UbuntuSans Nerd Font, monospace→UbuntuSansMono Nerd Font; installed by moonarch-git PKGBUILD. Stale `/etc/fonts/local.conf` removed. Waybar `style.css` font-family corrected "Ubuntu Nerd Font"→"UbuntuSans Nerd Font" (the bar wants the explicit proportional font, not a generic). The aliases need `binding="strong"` — verified that a weak `<prefer>` (fontconfig's default for `<alias>`) ranks behind the system's effective generic fallback and does not take effect; strong is required for it to apply.
|
||||||
|
|
||||||
|
## 2026-05-04 – Nightlight default OFF, no global enablement
|
||||||
|
|
||||||
|
- **Who**: Dominik, ClaudeCode
|
||||||
|
- **Why**: Filter survived reboots even after the user toggled it off. Root cause: `moonarch-pkgbuilds/moonarch-git/PKGBUILD` looped over every user service in `defaults/etc/systemd/user/*.service` and dropped a WantedBy symlink into `/etc/systemd/user/graphical-session.target.wants/`. That path is global scope. `moonarch-nightlight` runs `systemctl --user disable wlsunset`, which can only remove user-scope symlinks under `~/.config/`. Systemd's own warning during disable spelled it out: "The following unit files have been enabled in global scope. This means they will still be started automatically after a successful disablement in user scope." Verified empirically — `is-enabled` stayed `enabled`, root symlink untouched. Additionally, `scripts/post-install.sh` enabled `wlsunset` by default in its `USER_SERVICES` array, so even without the global symlink the filter would default ON.
|
||||||
|
- **Tradeoffs**: Three options weighed. (1) Default OFF + user-scope toggle — minimal change, fresh installs start without filter, toggle creates `~/.config/.../wants/` symlink that user-disable can actually remove. (2) Default ON + user-scope toggle — same plumbing, post-install enables in user scope; filter on by default but disable persists. (3) Status-file gate inside the unit — service stays enabled, ExecStartPre checks a file and exits when off. Picked (1): no behavioral default imposed on fresh installs, no extra plumbing, the toggle stays the single source of truth. Could have moved enablement to a per-user `systemctl --user --global enable` from the .install hook, but that fights the "this is a UI toggle" framing.
|
||||||
|
- **How**: `moonarch-pkgbuilds/moonarch-git/PKGBUILD` — symlink loop now skips a `skip_enable` list (currently `wlsunset.service`); skipped services are still installed under `/etc/systemd/user/` but not wanted by `graphical-session.target` at the global level. `moonarch-pkgbuilds/moonarch-git/moonarch.install` — `pre_upgrade()` deletes any pre-existing `/etc/systemd/user/graphical-session.target.wants/wlsunset.service` to clean up systems that received the old packaging. `moonarch/scripts/post-install.sh` — `wlsunset` removed from `USER_SERVICES`; comment explains why. `moonarch/CLAUDE.md` — Nightlight section reflects "Default OFF" and the global-scope-symlink hazard.
|
||||||
|
|
||||||
|
## 2026-05-04 – Battery threshold permissions: udev rule → pkexec helper
|
||||||
|
|
||||||
|
- **Who**: Dominik, ClaudeCode
|
||||||
|
- **Why**: The wheel-write-via-udev approach for `/sys/class/power_supply/BAT0/charge_control_end_threshold` had been broken since 2026-04-08 (commit `ac2b210`, "audit remediation Q-W3"). That commit added `ACTION=="add"` to `90-moonarch-battery.rules` to "avoid firing on every battery event" — but that filter is precisely what the rule needs not to have. On Lenovo, the threshold attribute does not exist yet at the `add` event (the driver creates it slightly later); the rule fires, `chmod` fails silently because `2>/dev/null` swallows the error, and permissions are never set. The unfiltered original rule worked by accident: `add` failed silently as well, but a subsequent `change` event on the same device caught the now-existing attribute and set permissions. After the audit commit, change events stopped re-firing the rule and the toggle was permanently broken — `moonarch-batsaver-toggle` returned `Permission denied`. Verified via journalctl + manual chmod: rule fires for hidpp_battery_0 (visible exit-1 errors), no trace for BAT0; manual `chmod g+w` on BAT0's threshold succeeds (sysfs accepts the change), so the permission model itself works — only the rule path failed.
|
||||||
|
- **Tradeoffs**: Three approaches considered. (A) Restore the original unfiltered rule — fixes the symptom by accident, leaves the failure mode intact (silent fail at add, retry hopefully at change). (B) Switch to `tmpfiles.d` — Arch Wiki explicitly warns this can run before driver modules load, undefined for sysfs. (C) pkexec helper with polkit-rule — standard pattern (Battery-Health-Charging GNOME extension uses exactly this). Picked C with default Standard-pkexec prompt rather than no-password polkit rule: minor UX cost (password once per pkauth session, ≈5min cache), eliminates the entire sysfs-permission problem class, no privilege-escalation surface from a misvalidated helper. The wheel-can-write-sysfs design was a moonarch-specific deviation from common Linux practice — bringing it in line with the standard root-orientiert helper pattern.
|
||||||
|
- **How**: `defaults/bin/moonarch-batsaver-apply` (new): privileged helper invoked via pkexec; strictly validates argument (digits only, range 1-100), writes sysfs (idempotent — skips kernel write when value already matches to avoid Lenovo EINVAL on same-value writes), writes state file. `defaults/bin/moonarch-batsaver-toggle` (rewritten): user-side reads current threshold, picks 80↔100, dispatches `pkexec /usr/bin/moonarch-batsaver-apply $NEW`, then signals waybar. `defaults/etc/udev/rules.d/90-moonarch-battery.rules` deleted (and the now-empty `defaults/etc/udev/` parent removed). PKGBUILD: udev install line removed. `moonarch-doctor`: removed the udev-effectiveness check (no longer relevant). `moonarch-batsaver.service` and `moonarch-batsaver-restore` (also new in this commit, extracted from the old inline ExecStart for readability) keep root-owned boot-time restore — no permission concerns there. `CLAUDE.md` Battery-Conservation-Mode section updated to describe the new flow.
|
||||||
|
|
||||||
|
## 2026-05-04 – Cleanup: remove invented zsh override layer, harden moondoc
|
||||||
|
|
||||||
|
- **Who**: Dominik, ClaudeCode
|
||||||
|
- **Why**: Audit revealed two classes of cruft introduced by earlier ClaudeCode sessions without explicit decision or DECISIONS.md entry. (1) An invented user-override mechanism (`~/.zshrc.d/*.zsh` snippet loop, `~/.zshrc.local` fallback) was wired into `defaults/shell/zshrc` and `scripts/post-install.sh`. Not a zsh convention, not documented, redundant to the user's own `~/.zshrc`. `post-install.sh` created the `~/.zshrc.d` directory unconditionally on every fresh install — leaving an empty directory in every user's home. (2) `moonarch-doctor` had only existence checks (`/etc/zsh/zshrc.moonarch` exists?, `/usr/share/moonarch/` exists?) which are redundant with the package check, and hardcoded service/script lists that drift silently when moonarch-git's payload changes. The udev rule for `charge_control_end_threshold` (battery conservation) had no effectiveness check at all — a broken rule would not show up.
|
||||||
|
- **Tradeoffs**: Could have left the invented override layer alone (no active harm) but it muddies `defaults/shell/zshrc` and produces empty directories on every fresh install. Could have kept the existence checks (cosmetic noise, no harm) but they create false positives — doctor reports "pass" while the actual mechanism may be broken. Kept the 7 hardcoded `check_config_match` entries for `/etc/xdg/foot/`, `/etc/greetd/`, `/etc/moongreet/` etc. — the source-to-destination mapping is not 1:1 (foot → `/etc/xdg/foot/`, greetd → `/etc/greetd/`, moongreet → `/etc/moongreet/`), so dynamic discovery would need a manifest. Acceptable hardcoding for now.
|
||||||
|
- **How**: `defaults/shell/zshrc` — `~/.zshrc.d/*.zsh` source loop and `~/.zshrc.local` fallback removed; second ABOUTME line that referenced them removed. `scripts/post-install.sh` — Zsh-block now writes `~/.zshrc` with only `source /etc/zsh/zshrc.moonarch` (no mkdir, no `~/.zshrc.d` reference); stale "rustup default stable" hint and "User overrides in `~/.zshrc.d/`" hint removed from next-steps. `scripts/lib.sh` — dead `confirm()` (orphaned since transform.sh deletion 2026-04-21) removed. `scripts/moonarch-doctor` — user-services and helper-scripts lists now derived from `pacman -Qql moonarch-git` plus an explicit list of post-install-enabled externals (currently `stasis`); useless existence checks for `/etc/zsh/zshrc.moonarch` and `/usr/share/moonarch/` removed; new udev-effectiveness check for `charge_control_end_threshold` (group=wheel + group-writable). `defaults/bin/moonarch-waybar-cpugov`, `moonarch-waybar-gpustat` — German ABOUTME comments translated to English for consistency.
|
||||||
|
|
||||||
## 2026-04-24 – Stasis: flip `ignore_remote_media` to false for browser video
|
## 2026-04-24 – Stasis: flip `ignore_remote_media` to false for browser video
|
||||||
- **Who**: Dominik, ClaudeCode
|
- **Who**: Dominik, ClaudeCode
|
||||||
- **Why**: Idle was firing on a second machine even while a video was playing in the browser. Original config carried the comment "browser uses D-Bus inhibit" and set `ignore_remote_media true`, deliberately excluding browser MPRIS on the assumption that Firefox/Chromium would keep the session alive via `org.freedesktop.ScreenSaver.Inhibit`. Verified against browser behavior: both browsers only raise that inhibit during **fullscreen** video playback (Firefox also requires `dom.screenwakelock.enabled`, default off on Linux). Windowed playback — the common case, YouTube in a tab — sends no inhibit, so stasis saw zero inhibitors and zero media players and ran the full idle plan to suspend. Upstream example config lists `r"firefox.*"` in `inhibit_apps` for exactly this reason; Moonarch removed it without a working substitute.
|
- **Why**: Idle was firing on a second machine even while a video was playing in the browser. Original config carried the comment "browser uses D-Bus inhibit" and set `ignore_remote_media true`, deliberately excluding browser MPRIS on the assumption that Firefox/Chromium would keep the session alive via `org.freedesktop.ScreenSaver.Inhibit`. Verified against browser behavior: both browsers only raise that inhibit during **fullscreen** video playback (Firefox also requires `dom.screenwakelock.enabled`, default off on Linux). Windowed playback — the common case, YouTube in a tab — sends no inhibit, so stasis saw zero inhibitors and zero media players and ran the full idle plan to suspend. Upstream example config lists `r"firefox.*"` in `inhibit_apps` for exactly this reason; Moonarch removed it without a working substitute.
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ desktop that can be rebuilt from scratch in minutes.
|
|||||||
| **Greeter** | [greetd](https://sr.ht/~kennylevinsen/greetd/) + [moongreet](https://gitea.moonarch.de/nevaforget/moongreet) | Minimal, Wayland-native login. moongreet provides GTK4 UI with fingerprint support, running inside its own Niri instance. |
|
| **Greeter** | [greetd](https://sr.ht/~kennylevinsen/greetd/) + [moongreet](https://gitea.moonarch.de/nevaforget/moongreet) | Minimal, Wayland-native login. moongreet provides GTK4 UI with fingerprint support, running inside its own Niri instance. |
|
||||||
| **Lock Screen** | [moonlock](https://gitea.moonarch.de/nevaforget/moonlock) | ext-session-lock-v1 protocol — compositor guarantees lock on crash. PAM + fprintd, GPU blur, multi-monitor. |
|
| **Lock Screen** | [moonlock](https://gitea.moonarch.de/nevaforget/moonlock) | ext-session-lock-v1 protocol — compositor guarantees lock on crash. PAM + fprintd, GPU blur, multi-monitor. |
|
||||||
| **Power Menu** | [moonset](https://gitea.moonarch.de/nevaforget/moonset) | GTK4 Layer Shell overlay above Waybar. Lock, logout, hibernate, reboot, shutdown with confirmation. |
|
| **Power Menu** | [moonset](https://gitea.moonarch.de/nevaforget/moonset) | GTK4 Layer Shell overlay above Waybar. Lock, logout, hibernate, reboot, shutdown with confirmation. |
|
||||||
| **Idle Manager** | [stasis](https://aur.archlinux.org/packages/stasis) | Separate AC/battery power plans. Brightness dimming, DPMS, lock (via moonlock), and suspend on configurable timeouts. |
|
| **Idle Manager** | [stasis](https://aur.archlinux.org/packages/stasis) + [wayland-pipewire-idle-inhibit](https://github.com/rafaelrc7/wayland-pipewire-idle-inhibit) | Separate AC/battery power plans. Brightness dimming, DPMS, lock (via moonlock), and suspend on configurable timeouts. The companion inhibitor holds a Wayland idle-inhibitor while audio plays — keeps the screen awake during windowed browser video, which stasis' pactl detection skips by design. |
|
||||||
| **Bar** | [Waybar](https://github.com/Alexays/Waybar) | Wayland-native, highly customizable. Niri workspace/window modules via community plugins. |
|
| **Bar** | [Waybar](https://github.com/Alexays/Waybar) | Wayland-native, highly customizable. Niri workspace/window modules via community plugins. |
|
||||||
| **Launcher** | [Walker](https://github.com/abenz1267/walker) + [Elephant](https://github.com/abenz1267/elephant) | Wayland-native GTK4 launcher with built-in providers for apps, clipboard, bluetooth, audio, files, and calculator. Dmenu mode for custom scripts (VPN, CPU governor). |
|
| **Launcher** | [Walker](https://github.com/abenz1267/walker) + [Elephant](https://github.com/abenz1267/elephant) | Wayland-native GTK4 launcher with built-in providers for apps, clipboard, bluetooth, audio, files, and calculator. Dmenu mode for custom scripts (VPN, CPU governor). |
|
||||||
| **Terminal** | [Foot](https://codeberg.org/dnkl/foot) | Fast, minimal Wayland-native terminal. Server mode for instant window spawning. |
|
| **Terminal** | [Foot](https://codeberg.org/dnkl/foot) | Fast, minimal Wayland-native terminal. Server mode for instant window spawning. |
|
||||||
@@ -206,6 +206,7 @@ are part of the system and updated via `paru -Syu`.
|
|||||||
| kanshi | Dynamic display configuration (auto-switch output profiles on hotplug) |
|
| kanshi | Dynamic display configuration (auto-switch output profiles on hotplug) |
|
||||||
| nautilus | File manager preload (faster first launch) |
|
| nautilus | File manager preload (faster first launch) |
|
||||||
| stasis | Idle manager (dimming, DPMS, lock, suspend on AC/battery plans) |
|
| stasis | Idle manager (dimming, DPMS, lock, suspend on AC/battery plans) |
|
||||||
|
| wayland-pipewire-idle-inhibit | Holds a Wayland idle-inhibitor while audio plays (keeps screen awake for windowed browser video that stasis skips) |
|
||||||
| walker | Walker application launcher (GTK4 service mode for instant startup) |
|
| walker | Walker application launcher (GTK4 service mode for instant startup) |
|
||||||
|
|
||||||
## Moonarch Ecosystem
|
## Moonarch Ecosystem
|
||||||
|
|||||||
Executable
+35
@@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
# ABOUTME: Privileged helper invoked via pkexec from moonarch-batsaver-toggle.
|
||||||
|
# ABOUTME: Validates the threshold value and writes it to sysfs and the state file.
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
VAL="${1:-}"
|
||||||
|
|
||||||
|
case "$VAL" in
|
||||||
|
""|*[!0-9]*)
|
||||||
|
echo "Invalid argument: '$VAL' (expected integer 1-100)" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ "$VAL" -lt 1 ] || [ "$VAL" -gt 100 ]; then
|
||||||
|
echo "Out of range: $VAL (expected 1-100)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
THRESHOLD_FILE="/sys/class/power_supply/BAT0/charge_control_end_threshold"
|
||||||
|
STATE_DIR="/var/lib/moonarch"
|
||||||
|
STATE_FILE="$STATE_DIR/batsaver-threshold"
|
||||||
|
|
||||||
|
[ -f "$THRESHOLD_FILE" ] || { echo "No battery threshold support" >&2; exit 1; }
|
||||||
|
|
||||||
|
# Skip the kernel write when the value already matches — some Lenovo drivers
|
||||||
|
# reject same-value writes with EINVAL.
|
||||||
|
CURRENT=$(cat "$THRESHOLD_FILE")
|
||||||
|
if [ "$CURRENT" != "$VAL" ]; then
|
||||||
|
printf %s "$VAL" > "$THRESHOLD_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$STATE_DIR"
|
||||||
|
printf %s "$VAL" > "$STATE_FILE"
|
||||||
Executable
+23
@@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# ABOUTME: Restores the saved battery charge end threshold on boot.
|
||||||
|
# ABOUTME: Skips silently when the kernel already reports the same value (avoids EINVAL on some Lenovo drivers).
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
STATE_FILE="/var/lib/moonarch/batsaver-threshold"
|
||||||
|
SYS_FILE="/sys/class/power_supply/BAT0/charge_control_end_threshold"
|
||||||
|
|
||||||
|
[ -f "$STATE_FILE" ] || exit 0
|
||||||
|
[ -f "$SYS_FILE" ] || exit 0
|
||||||
|
|
||||||
|
V=$(cat "$STATE_FILE")
|
||||||
|
case "$V" in
|
||||||
|
""|*[!0-9]*) exit 0 ;;
|
||||||
|
esac
|
||||||
|
[ "$V" -ge 1 ] && [ "$V" -le 100 ] || exit 0
|
||||||
|
|
||||||
|
# Some Lenovo drivers reject writing the same value with EINVAL.
|
||||||
|
C=$(cat "$SYS_FILE")
|
||||||
|
[ "$C" = "$V" ] && exit 0
|
||||||
|
|
||||||
|
printf %s "$V" > "$SYS_FILE"
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
# ABOUTME: Toggles battery conservation mode between 80% and 100% charge limit.
|
# ABOUTME: Toggles battery conservation mode between 80% and 100% charge limit.
|
||||||
# ABOUTME: Writes to sysfs (immediate) and state file (persistence across reboots).
|
# ABOUTME: Reads sysfs as user, dispatches the privileged write via pkexec.
|
||||||
|
|
||||||
THRESHOLD_FILE="/sys/class/power_supply/BAT0/charge_control_end_threshold"
|
THRESHOLD_FILE="/sys/class/power_supply/BAT0/charge_control_end_threshold"
|
||||||
STATE_DIR="/var/lib/moonarch"
|
|
||||||
STATE_FILE="${STATE_DIR}/batsaver-threshold"
|
|
||||||
CONSERVATION_LIMIT=80
|
CONSERVATION_LIMIT=80
|
||||||
|
|
||||||
[[ -f "$THRESHOLD_FILE" ]] || exit 1
|
[[ -f "$THRESHOLD_FILE" ]] || exit 1
|
||||||
@@ -18,12 +16,7 @@ else
|
|||||||
NEW="$CONSERVATION_LIMIT"
|
NEW="$CONSERVATION_LIMIT"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Apply immediately
|
pkexec /usr/bin/moonarch-batsaver-apply "$NEW" || exit 1
|
||||||
echo "$NEW" > "$THRESHOLD_FILE" || exit 1
|
|
||||||
|
|
||||||
# Persist for next boot
|
|
||||||
mkdir -p "$STATE_DIR"
|
|
||||||
echo "$NEW" > "$STATE_FILE" || exit 1
|
|
||||||
|
|
||||||
# Signal Waybar to refresh the batsaver module (SIGRTMIN+9)
|
# Signal Waybar to refresh the batsaver module (SIGRTMIN+9)
|
||||||
pkill -RTMIN+9 waybar
|
pkill -RTMIN+9 waybar
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
# ABOUTME: Waybar-Modul das den CPU-Governor als JSON ausgibt.
|
# ABOUTME: Waybar module that outputs the CPU governor as JSON.
|
||||||
# ABOUTME: Wird von der Waybar custom/cpugov Config referenziert.
|
# ABOUTME: Referenced by the Waybar custom/cpugov config.
|
||||||
|
|
||||||
CPU_GOV=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null)
|
CPU_GOV=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
# ABOUTME: Waybar-Modul das die GPU-Auslastung als JSON ausgibt.
|
# ABOUTME: Waybar module that outputs GPU utilization as JSON.
|
||||||
# ABOUTME: Wird von der Waybar custom/gpu-usage Config referenziert.
|
# ABOUTME: Referenced by the Waybar custom/gpu-usage config.
|
||||||
|
|
||||||
GPU_STAT=$(cat /sys/class/hwmon/hwmon*/device/gpu_busy_percent 2>/dev/null | head -1 || echo "0")
|
GPU_STAT=$(cat /sys/class/hwmon/hwmon*/device/gpu_busy_percent 2>/dev/null | head -1 || echo "0")
|
||||||
GPU_STAT="${GPU_STAT:-0}"
|
GPU_STAT="${GPU_STAT:-0}"
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- ABOUTME: System-wide fontconfig generic-family defaults for Moonarch. -->
|
||||||
|
<!-- ABOUTME: Loads after 60-latin (number 65) so these prefs win over stock defaults. -->
|
||||||
|
<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
|
||||||
|
<fontconfig>
|
||||||
|
<description>Moonarch generic-family defaults</description>
|
||||||
|
<!-- binding="strong" required: the default weak <prefer> ranks behind
|
||||||
|
stock generic fallbacks, so it would not take effect. -->
|
||||||
|
<alias binding="strong">
|
||||||
|
<family>sans-serif</family>
|
||||||
|
<prefer>
|
||||||
|
<family>UbuntuSans Nerd Font</family>
|
||||||
|
</prefer>
|
||||||
|
</alias>
|
||||||
|
<alias binding="strong">
|
||||||
|
<family>monospace</family>
|
||||||
|
<prefer>
|
||||||
|
<family>UbuntuSansMono Nerd Font</family>
|
||||||
|
</prefer>
|
||||||
|
</alias>
|
||||||
|
</fontconfig>
|
||||||
@@ -9,11 +9,7 @@ ConditionPathExists=/var/lib/moonarch/batsaver-threshold
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
# Validate the threshold (integer 1–100) before writing. The state file is
|
ExecStart=/usr/bin/moonarch-batsaver-restore
|
||||||
# written by wheel-group users via moonarch-batsaver-toggle; the kernel rejects
|
|
||||||
# non-numeric values on sysfs, but validating here prevents noise on boot and
|
|
||||||
# makes the trust boundary explicit.
|
|
||||||
ExecStart=/bin/sh -c 'V=$(cat /var/lib/moonarch/batsaver-threshold); case "$V" in ""|*[!0-9]*) exit 0;; esac; [ "$V" -ge 1 ] && [ "$V" -le 100 ] && printf %s "$V" > /sys/class/power_supply/BAT0/charge_control_end_threshold'
|
|
||||||
NoNewPrivileges=true
|
NoNewPrivileges=true
|
||||||
ProtectHome=true
|
ProtectHome=true
|
||||||
PrivateTmp=true
|
PrivateTmp=true
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
# ABOUTME: udev rule granting wheel group write access to battery charge threshold.
|
|
||||||
# ABOUTME: Enables unprivileged toggling of conservation mode via moonarch-batsaver-toggle.
|
|
||||||
|
|
||||||
SUBSYSTEM=="power_supply", ACTION=="add", ATTR{type}=="Battery", RUN+="/bin/sh -c 'chgrp wheel /sys%p/charge_control_end_threshold 2>/dev/null; chmod g+w /sys%p/charge_control_end_threshold 2>/dev/null'"
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[preferred]
|
||||||
|
default=gnome;gtk;
|
||||||
|
org.freedesktop.impl.portal.Access=gtk;
|
||||||
|
org.freedesktop.impl.portal.Notification=gtk;
|
||||||
|
org.freedesktop.impl.portal.Secret=gnome-keyring;
|
||||||
|
# xdg-desktop-portal-gtk reports the Inhibit interface as success even though
|
||||||
|
# nothing implements it under Niri, which makes Firefox/Waterfox skip the
|
||||||
|
# native Wayland idle-inhibit. With no backend the browser falls back to
|
||||||
|
# zwp_idle_inhibit, which Niri honors, so windowed video keeps the screen awake.
|
||||||
|
org.freedesktop.impl.portal.Inhibit=none;
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
# ABOUTME: Moonarch default zsh configuration with Catppuccin-themed prompt.
|
# ABOUTME: Moonarch default zsh configuration with Catppuccin-themed prompt.
|
||||||
# ABOUTME: Sources user overrides from ~/.zshrc.d/ and ~/.zshrc.local
|
|
||||||
|
|
||||||
# --- History ---
|
# --- History ---
|
||||||
HISTFILE=~/.histfile
|
HISTFILE=~/.histfile
|
||||||
@@ -140,14 +139,3 @@ export XDG_SESSION_TYPE="wayland"
|
|||||||
export EDITOR="nvim"
|
export EDITOR="nvim"
|
||||||
export SUDO_EDITOR="nvim"
|
export SUDO_EDITOR="nvim"
|
||||||
export MOZ_ENABLE_WAYLAND="1"
|
export MOZ_ENABLE_WAYLAND="1"
|
||||||
|
|
||||||
# --- User override scripts ---
|
|
||||||
# Drop custom config snippets into ~/.zshrc.d/*.zsh
|
|
||||||
if [[ -d "$HOME/.zshrc.d" ]]; then
|
|
||||||
for f in "$HOME/.zshrc.d"/*.zsh(N); do
|
|
||||||
source "$f"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Single-file user override (for simple additions)
|
|
||||||
[[ -f "$HOME/.zshrc.local" ]] && source "$HOME/.zshrc.local"
|
|
||||||
|
|||||||
@@ -5,9 +5,13 @@
|
|||||||
@description "Idle management for Moonarch (Niri + moonlock)"
|
@description "Idle management for Moonarch (Niri + moonlock)"
|
||||||
|
|
||||||
default:
|
default:
|
||||||
# Media playback inhibits idle. Browsers expose MPRIS during video playback
|
# monitor_media: detect active media via PipeWire/PulseAudio sink-inputs
|
||||||
# and only send D-Bus idle-inhibit in fullscreen, so we include remote media
|
# (pactl), not MPRIS. Non-browser players (mpv, vlc, ...) are counted and
|
||||||
# to catch windowed browser videos.
|
# inhibit idle. Browser audio is excluded by design; browser idle-inhibit is
|
||||||
|
# expected via D-Bus (enable_dbus_inhibit), which Waterfox/Firefox only send
|
||||||
|
# in fullscreen -- so windowed browser video is NOT caught by stasis.
|
||||||
|
# ignore_remote_media false: also count remote players (Spotify-remote,
|
||||||
|
# KDEConnect, Chromecast); has no effect on windowed browser video.
|
||||||
monitor_media true
|
monitor_media true
|
||||||
ignore_remote_media false
|
ignore_remote_media false
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
* {
|
* {
|
||||||
border: none;
|
border: none;
|
||||||
font-family: "Ubuntu Nerd Font", sans-serif;
|
font-family: "UbuntuSans Nerd Font", sans-serif;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: alpha(@theme_text_color, 0.8);
|
color: alpha(@theme_text_color, 0.8);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,3 +37,4 @@ mpv-thumbfast-git
|
|||||||
# System & Tools
|
# System & Tools
|
||||||
auto-cpufreq
|
auto-cpufreq
|
||||||
stasis
|
stasis
|
||||||
|
wayland-pipewire-idle-inhibit
|
||||||
|
|||||||
@@ -23,11 +23,6 @@ read_packages() {
|
|||||||
grep -v '^\s*#' "$1" | grep -v '^\s*$'
|
grep -v '^\s*#' "$1" | grep -v '^\s*$'
|
||||||
}
|
}
|
||||||
|
|
||||||
confirm() {
|
|
||||||
read -r -p "$1 [y/N] " response
|
|
||||||
[[ "$response" =~ ^[yY]$ ]]
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- Prerequisite checks ---
|
# --- Prerequisite checks ---
|
||||||
|
|
||||||
check_not_root() {
|
check_not_root() {
|
||||||
|
|||||||
+28
-41
@@ -180,10 +180,19 @@ section "User Services"
|
|||||||
|
|
||||||
if [[ $EUID -eq 0 ]]; then
|
if [[ $EUID -eq 0 ]]; then
|
||||||
warn "Running as root — skipping user service checks"
|
warn "Running as root — skipping user service checks"
|
||||||
else
|
elif pacman -Qq moonarch-git &>/dev/null; then
|
||||||
for svc in kanshi wlsunset stasis walker nautilus cliphist-text cliphist-image; do
|
EXPECTED_SVCS=()
|
||||||
|
while IFS= read -r svc_file; do
|
||||||
|
EXPECTED_SVCS+=("$(basename "$svc_file" .service)")
|
||||||
|
done < <(pacman -Qql moonarch-git | grep -E '^/etc/systemd/user/[^/]+\.service$')
|
||||||
|
# Services enabled by post-install.sh from other packages
|
||||||
|
EXPECTED_SVCS+=(stasis wayland-pipewire-idle-inhibit)
|
||||||
|
|
||||||
|
for svc in "${EXPECTED_SVCS[@]}"; do
|
||||||
check_user_service "$svc"
|
check_user_service "$svc"
|
||||||
done
|
done
|
||||||
|
else
|
||||||
|
warn "moonarch-git not installed — skipping user service checks"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- 4. Config Files ---
|
# --- 4. Config Files ---
|
||||||
@@ -200,46 +209,30 @@ check_config_match "/etc/greetd/config.toml" "$SRC/greetd/config.toml"
|
|||||||
check_config_match "/etc/greetd/niri-greeter.kdl" "$SRC/greetd/niri-greeter.kdl"
|
check_config_match "/etc/greetd/niri-greeter.kdl" "$SRC/greetd/niri-greeter.kdl"
|
||||||
check_config_match "/etc/moongreet/moongreet.toml" "$SRC/moongreet/moongreet.toml"
|
check_config_match "/etc/moongreet/moongreet.toml" "$SRC/moongreet/moongreet.toml"
|
||||||
|
|
||||||
if [[ -f /etc/zsh/zshrc.moonarch ]]; then
|
|
||||||
pass "/etc/zsh/zshrc.moonarch"
|
|
||||||
else
|
|
||||||
fail "/etc/zsh/zshrc.moonarch (missing)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- 5. Helper Scripts ---
|
# --- 5. Helper Scripts ---
|
||||||
|
|
||||||
section "Helper Scripts"
|
section "Helper Scripts"
|
||||||
|
|
||||||
EXPECTED_SCRIPTS=(
|
if pacman -Qq moonarch-git &>/dev/null; then
|
||||||
moonarch-batsaver-toggle
|
EXPECTED_SCRIPTS=()
|
||||||
moonarch-btnote
|
while IFS= read -r script; do
|
||||||
moonarch-capsnote
|
EXPECTED_SCRIPTS+=("$(basename "$script")")
|
||||||
moonarch-cpugov
|
done < <(pacman -Qql moonarch-git | grep -E '^/usr/bin/moonarch-[^/]+$')
|
||||||
moonarch-doctor
|
|
||||||
moonarch-nightlight
|
|
||||||
moonarch-sink-switcher
|
|
||||||
moonarch-update
|
|
||||||
moonarch-vpn
|
|
||||||
moonarch-waybar
|
|
||||||
moonarch-waybar-batsaver
|
|
||||||
moonarch-waybar-cpugov
|
|
||||||
moonarch-waybar-gpustat
|
|
||||||
moonarch-waybar-hidpp
|
|
||||||
moonarch-waybar-nightlight
|
|
||||||
moonarch-waybar-updates
|
|
||||||
)
|
|
||||||
|
|
||||||
MISSING_SCRIPTS=()
|
MISSING_SCRIPTS=()
|
||||||
for script in "${EXPECTED_SCRIPTS[@]}"; do
|
for script in "${EXPECTED_SCRIPTS[@]}"; do
|
||||||
if [[ ! -x "/usr/bin/$script" ]]; then
|
if [[ ! -x "/usr/bin/$script" ]]; then
|
||||||
MISSING_SCRIPTS+=("$script")
|
MISSING_SCRIPTS+=("$script")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ${#MISSING_SCRIPTS[@]} -eq 0 ]]; then
|
||||||
|
pass "All ${#EXPECTED_SCRIPTS[@]} helper scripts present"
|
||||||
|
else
|
||||||
|
fail "Missing scripts: ${MISSING_SCRIPTS[*]}"
|
||||||
fi
|
fi
|
||||||
done
|
|
||||||
|
|
||||||
if [[ ${#MISSING_SCRIPTS[@]} -eq 0 ]]; then
|
|
||||||
pass "All ${#EXPECTED_SCRIPTS[@]} helper scripts present"
|
|
||||||
else
|
else
|
||||||
fail "Missing scripts: ${MISSING_SCRIPTS[*]}"
|
warn "moonarch-git not installed — skipping helper script checks"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Symlinks
|
# Symlinks
|
||||||
@@ -297,12 +290,6 @@ else
|
|||||||
warn "/var/lib/moonarch/ missing (created on first battery toggle)"
|
warn "/var/lib/moonarch/ missing (created on first battery toggle)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -d /usr/share/moonarch ]]; then
|
|
||||||
pass "/usr/share/moonarch/"
|
|
||||||
else
|
|
||||||
fail "/usr/share/moonarch/ missing (moonarch-git not installed?)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Summary ---
|
# --- Summary ---
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
|||||||
@@ -114,9 +114,7 @@ gsettings set org.gnome.desktop.interface font-name 'UbuntuSans Nerd Font 11'
|
|||||||
|
|
||||||
if [[ ! -f "$HOME/.zshrc" ]]; then
|
if [[ ! -f "$HOME/.zshrc" ]]; then
|
||||||
log "No ~/.zshrc found — sourcing Moonarch defaults."
|
log "No ~/.zshrc found — sourcing Moonarch defaults."
|
||||||
mkdir -p "$HOME/.zshrc.d"
|
echo "source /etc/zsh/zshrc.moonarch" > "$HOME/.zshrc"
|
||||||
echo "# Load Moonarch defaults, add custom overrides in ~/.zshrc.d/ or below" > "$HOME/.zshrc"
|
|
||||||
echo "source /etc/zsh/zshrc.moonarch" >> "$HOME/.zshrc"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- Seed Stasis user config ---
|
# --- Seed Stasis user config ---
|
||||||
@@ -135,11 +133,14 @@ fi
|
|||||||
log "Enabling systemd user services..."
|
log "Enabling systemd user services..."
|
||||||
USER_SERVICES=(
|
USER_SERVICES=(
|
||||||
"kanshi"
|
"kanshi"
|
||||||
"wlsunset"
|
|
||||||
"stasis"
|
"stasis"
|
||||||
|
"wayland-pipewire-idle-inhibit"
|
||||||
"cliphist-text"
|
"cliphist-text"
|
||||||
"cliphist-image"
|
"cliphist-image"
|
||||||
)
|
)
|
||||||
|
# wlsunset deliberately excluded: nightlight is a user-toggle (off by default).
|
||||||
|
# Enabling it system-wide would create a global-scope WantedBy symlink that
|
||||||
|
# overrides any user-scope `systemctl --user disable`.
|
||||||
|
|
||||||
for service in "${USER_SERVICES[@]}"; do
|
for service in "${USER_SERVICES[@]}"; do
|
||||||
if systemctl --user cat "${service}.service" &>/dev/null; then
|
if systemctl --user cat "${service}.service" &>/dev/null; then
|
||||||
@@ -210,6 +211,5 @@ log ""
|
|||||||
log "Next steps:"
|
log "Next steps:"
|
||||||
log " 1. Reboot"
|
log " 1. Reboot"
|
||||||
log " 2. Place wallpapers in ~/Pictures/Wallpaper/"
|
log " 2. Place wallpapers in ~/Pictures/Wallpaper/"
|
||||||
log " 3. rustup default stable"
|
log " 3. User overrides in ~/.config/"
|
||||||
log " 4. User overrides in ~/.config/ or ~/.zshrc.d/"
|
|
||||||
log ""
|
log ""
|
||||||
|
|||||||
Reference in New Issue
Block a user