Compare commits
51 Commits
2b8e40f37f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d101b23351 | |||
| e6b7f53794 | |||
| 806841d435 | |||
| f9f73db10f | |||
| dbc2997de0 | |||
| dc47d1a6ec | |||
| 952776c4f9 | |||
| f4d60d387e | |||
| 1e8b0d4ab0 | |||
| 7b6ba3b0d1 | |||
| aef7f64b59 | |||
| 0064170430 | |||
| 39094ee026 | |||
| d5c1b8a155 | |||
| 8aaf7cae5b | |||
| 89c3a9261e | |||
| e4ea267b6b | |||
| 8485a63ab7 | |||
| 6e14258ad9 | |||
| 9432bc4831 | |||
| 373bfd4a9b | |||
| c2cee85488 | |||
| 971a6eb577 | |||
| 0a38347cb9 | |||
| f4f6ede2a7 | |||
| ee85b87d3a | |||
| fcac91b540 | |||
| 324dda0548 | |||
| 0433f08f08 | |||
| 9bc753e092 | |||
| a360d12bde | |||
| 97e4b81930 | |||
| b8753bf84f | |||
| a55c7ea9d1 | |||
| ab066724f0 | |||
| 2363e76b4a | |||
| 4dd8aae2f0 | |||
| 47ae8d5d51 | |||
| 4c609135e9 | |||
| f6869c9cea | |||
| 48b0de071e | |||
| b6beabe500 | |||
| 8ddbb23851 | |||
| 46ba8365db | |||
| d815e21b1d | |||
| 6d3a7c8d72 | |||
| ac2b210a1f | |||
| e1e80ca414 | |||
| 579a948449 | |||
| 6a258151fa | |||
| 037d311607 |
@@ -7,6 +7,15 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- 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:
|
||||
update-pkgver:
|
||||
@@ -21,6 +30,8 @@ jobs:
|
||||
echo "$PKGVER" > /tmp/pkgver
|
||||
|
||||
- name: Update PKGBUILD
|
||||
env:
|
||||
PKGBUILD_TOKEN: ${{ secrets.PKGBUILD_TOKEN }}
|
||||
run: |
|
||||
PKGVER=$(cat /tmp/pkgver)
|
||||
git clone https://gitea.moonarch.de/nevaforget/moonarch-pkgbuilds.git pkgbuilds
|
||||
@@ -39,4 +50,12 @@ jobs:
|
||||
git config user.email "gitea@moonarch.de"
|
||||
git add moonarch-git/PKGBUILD
|
||||
git commit -m "chore(moonarch-git): bump pkgver to $PKGVER"
|
||||
git -c http.extraHeader="Authorization: token ${{ secrets.PKGBUILD_TOKEN }}" push
|
||||
|
||||
# Push via credential helper with a chmod 600 temp file, so the token
|
||||
# never appears in /proc/PID/cmdline (as it would with `git -c
|
||||
# http.extraHeader=...`).
|
||||
CRED_FILE=$(mktemp)
|
||||
chmod 600 "$CRED_FILE"
|
||||
trap 'rm -f "$CRED_FILE"' EXIT
|
||||
printf "https://pkgver-bot:%s@gitea.moonarch.de\n" "$PKGBUILD_TOKEN" > "$CRED_FILE"
|
||||
git -c credential.helper="store --file=$CRED_FILE" push
|
||||
|
||||
@@ -1,20 +1,94 @@
|
||||
# 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)
|
||||
- `scripts/` — Post-Install- und Helper-Scripts
|
||||
- `packages/` — Paketlisten (offiziell + AUR), getrennt gepflegt
|
||||
- `defaults/` — XDG-Configs, Shell-Config, Helper-Binaries, systemd User-Services, greetd/moongreet-Config, Wallpaper
|
||||
- `config/` — archinstall configuration (incl. custom-commands that clone the repo to /opt/moonarch, root-owned)
|
||||
- `scripts/` — post-install and helper scripts
|
||||
- `packages/` — package lists (official + AUR), maintained separately
|
||||
- `defaults/` — XDG configs, shell config, helper binaries, systemd services, udev rules, greetd/moongreet config, wallpaper
|
||||
|
||||
## Konventionen
|
||||
## Battery Conservation Mode
|
||||
|
||||
- Paketlisten sind einfache Textdateien, ein Paket pro Zeile, Kommentare mit `#`
|
||||
- Shell-Scripts müssen POSIX-kompatibel oder explizit bash/zsh sein
|
||||
- Alle Pfade im archinstall-Config relativ zum Installationsziel
|
||||
Laptops with `charge_control_end_threshold` support (ThinkPad, Framework, etc.) get a Waybar toggle:
|
||||
- Clicking the battery module toggles the charge limit between 80% and 100%
|
||||
- When conservation is active, a ♥ icon appears next to the battery indicator
|
||||
- State is persisted in `/var/lib/moonarch/batsaver-threshold` and restored on boot via a systemd service (`moonarch-batsaver-restore`)
|
||||
- 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)
|
||||
- On desktops without battery support the feature is hidden entirely
|
||||
|
||||
## Ich bin Apollo
|
||||
## Nightlight (Blue Light Filter)
|
||||
|
||||
Benannt nach dem Programm, das Menschen zum Mond gebracht hat — passend für ein Projekt namens Moonarch.
|
||||
Waybar toggle for wlsunset (Wayland-native blue light filter), persistent state via systemd:
|
||||
- `wlsunset.service` (systemd user service) with `After=kanshi.service` — starts only once all outputs are configured
|
||||
- **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.
|
||||
- Clicking the nightlight module in `group/brightness` toggles wlsunset on/off (`enable --now` / `disable --now`)
|
||||
- State survives reboots (user-scope symlink in `~/.config/systemd/user/...wants/`)
|
||||
- Active state shows in Catppuccin Yellow, inactive in the default text color
|
||||
- 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 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 the userconfig extend the `modules-left`/`modules-center`/`modules-right` arrays
|
||||
- All other top-level keys are inserted as module definitions via object merge
|
||||
- The merge runs only on changes (timestamp comparison)
|
||||
- On error: `notify-send` + `logger`, Waybar starts with the system config
|
||||
- Generates `~/.config/waybar/style.css` with an `@import` of the system styles if not present
|
||||
- Requires `jq` (declared as a dependency in the PKGBUILD)
|
||||
- The system config must be valid JSON (no JSONC)
|
||||
|
||||
## mpv + ModernZ OSC
|
||||
|
||||
The video player is `mpv` with [ModernZ](https://github.com/Samillion/ModernZ) as the OSC, thumbnails via thumbfast:
|
||||
- `mpv-modernz-git` provides `modernz.lua` + font + default config to `/etc/mpv/`
|
||||
- `mpv-thumbfast-git` provides `thumbfast.lua` to `/etc/mpv/scripts/` (auto-detected by ModernZ)
|
||||
- `defaults/etc/mpv/mpv.conf` is installed directly to `/etc/mpv/mpv.conf` by moonarch-git (owned)
|
||||
- Stock OSC + title bar disabled, `autofit-larger=80%x80%` caps oversized windows
|
||||
- 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`
|
||||
- **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 opens mpv floating (`window-rule` in `defaults/xdg/niri/config.kdl`)
|
||||
|
||||
## System Health Check (moonarch-doctor / moondoc)
|
||||
|
||||
Diagnostic script that checks the system state against moonarch defaults:
|
||||
- Packages (official.txt + aur.txt installed? Orphans?)
|
||||
- System services (NetworkManager, bluetooth, greetd, ufw, auto-cpufreq, etc.)
|
||||
- User services (kanshi, wlsunset, stasis, walker, nautilus, cliphist-text, cliphist-image)
|
||||
- Config files (SHA256 comparison deployed vs. moonarch default)
|
||||
- Helper scripts + symlinks (moonup, moondoc)
|
||||
- System config (UFW, pacman/paru repos, default shell)
|
||||
- Directories + permissions
|
||||
|
||||
## Fontconfig Defaults
|
||||
|
||||
System-wide generic-family defaults via `defaults/etc/fonts/conf.d/65-moonarch-fonts.conf` (owned by moonarch-git):
|
||||
- `sans-serif` → UbuntuSans Nerd Font, `monospace` → UbuntuSansMono Nerd Font
|
||||
- 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
|
||||
|
||||
+89
-9
@@ -1,5 +1,85 @@
|
||||
# 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
|
||||
- **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.
|
||||
- **Tradeoffs**: Three options. (A) Put `firefox`/`chromium` back into `inhibit_apps` — works but inhibits whenever the browser *process* is running, even with zero tabs playing; wrong shape. (B) Tell users to flip `dom.screenwakelock.enabled` per browser — pushes per-user config, fragile across browsers. (C) Let browser MPRIS count as a media player by setting `ignore_remote_media false` — stasis already tracks playback state, so it only inhibits during actual playback. Picked C. Cost: any MPRIS source stasis classifies as "remote" (e.g. a Chromecast bridge) now also inhibits; acceptable — that is usually what a user wants anyway, and the inhibit releases the moment playback stops.
|
||||
- **How**: `defaults/xdg/stasis/stasis.rune` — `ignore_remote_media true` → `false`, comment rewritten to document the fullscreen-only D-Bus behavior. PKGBUILD deploys to `/etc/xdg/stasis/stasis.rune`; existing users still need the 2026-04-22 seed mechanism (or a manual merge) to pick it up in `~/.config/stasis/stasis.rune`.
|
||||
|
||||
## 2026-04-22 – moonarch-doctor housekeeping: drop stale check, add missing services
|
||||
- **Who**: Dominik, ClaudeCode
|
||||
- **Why**: Noticed while running `moondoc` on a healthy system that it reported `Paru [moonarch-pkgbuilds] repo missing from /etc/paru.conf` — a false failure. The paru PKGBUILD-repo mechanism was retired on 2026-04-20 in favor of the registry-only flow, and the `moonarch-git` install hook strips the legacy paru.conf section on upgrade; the doctor script was not updated in lockstep. Audit of the rest of the script surfaced two related gaps: the user-services loop skipped `walker.service` and `nautilus.service`, even though the PKGBUILD ships both in `/etc/systemd/user/` and enables them via `graphical-session.target.wants` symlinks. A silently missing walker or nautilus would not show up in diagnostics.
|
||||
- **How**: Removed the `[moonarch-pkgbuilds]` check from `scripts/moonarch-doctor`. Added `walker` and `nautilus` to the user-service loop. Updated `CLAUDE.md` user-services listing to match (also filled in the missing `wlsunset`). The `[moonarch]` pacman-repo check stays — that is the path that matters now.
|
||||
|
||||
## 2026-04-22 – Seed Stasis user config from post-install.sh
|
||||
- **Who**: Dominik, ClaudeCode
|
||||
- **Why**: Moonarch shipped `defaults/xdg/stasis/stasis.rune` (deployed to `/etc/xdg/stasis/stasis.rune` by the PKGBUILD) on the assumption that stasis honors the XDG system config hierarchy. It does not. Verified against upstream source (v1.1.0, `src/config/mod.rs:30` + `src/config/bootstrap.rs`): stasis only reads `~/.config/stasis/stasis.rune` (primary) or `/etc/stasis/stasis.rune` (fallback, no `xdg/`). On every start with no user config, `ensure_user_config_exists()` writes its own hardcoded default (laptop/desktop template compiled into the binary) to `~/.config/stasis/stasis.rune`. Net effect: Moonarch's Idle-Manager tuning (AC/battery plans, moonlock integration, inhibit apps, niri DPMS commands) was never active on fresh installs — users got the upstream defaults with `swaylock` as locker.
|
||||
- **Tradeoffs**: Three options were considered. (A) Service drop-in with `--config /etc/stasis/stasis.rune` — cleanest, upgrades propagate via package, no user-home touching; rejected because it takes stasis out of the standard config path users expect when they want to customize, and requires packaging a drop-in unit. (B) Seed once from post-install.sh into `~/.config/stasis/stasis.rune` — chosen: user-owned file, immediately editable, no magic. Cost: package updates to the template never reach users who already ran post-install; they have to merge manually. (C) Recurring systemd user service that keeps seeding — ruled out, too clever, user edits would race with it. Stasis has no config hierarchy / merging, so "system default + user override" cannot be modeled at all.
|
||||
- **How**: Added a block to `scripts/post-install.sh` before the user-services-enable step: if `~/.config/stasis/stasis.rune` does not exist and `/etc/xdg/stasis/stasis.rune` does, `install -Dm644` copies the template into the user home. Order is correct — moonarch-git is installed earlier in the script (deploys `/etc/xdg/…`), and stasis.service is only *enabled* (not started) later, so the seed is in place before stasis ever runs. The `/etc/xdg/stasis/` payload stays as the canonical template source inside the package.
|
||||
|
||||
## 2026-04-21 – post-install.sh pulls aur.txt, rust for paru build, rustup out of official
|
||||
- **Who**: Dominik, ClaudeCode
|
||||
- **Why**: Three related gaps uncovered while fixing the paru bootstrap: (1) `moonarch-git` cannot depend on AUR packages, so every AUR package in `aur.txt` (walker-bin, elephant-*-bin, awww's theme, waypaper, stasis, …) was silently never installed by post-install.sh — a fresh install would have a working pacman but no launcher, no idle manager, no theming. (2) `makepkg -si` for `paru` (AUR source build) needs `rust` as makedep; neither archinstall nor post-install.sh installed it, so the restored paru bootstrap would have crashed on rust-less systems. (3) `rustup` sat in `official.txt` "for something we don't remember" — turned out to be a leftover; `rust` suffices for the paru build, and rustup is only needed for dev toolchain management which is a per-user concern, not a Moonarch default.
|
||||
- **Tradeoffs**: Installing `rust` (~350MB) just to bootstrap paru feels heavy. Alternative `paru-bin` avoids the compile step entirely; rejected because Dominik's documented workflow uses `paru` (source) and consistency with that matters more than ~30s of build time. rustup can be re-added to official.txt if the dev workflow grows that demand.
|
||||
- **How**: (1) post-install.sh now runs `read_packages "$AUR_PACKAGES" | paru -S --needed` after `paru -S moonarch-git`. (2) `sudo pacman -S base-devel rust` before the paru git-clone. (3) `rustup` removed from `official.txt` (remains in PKGBUILD `optdepends` for discoverability).
|
||||
|
||||
## 2026-04-21 – Restore AUR bootstrap for paru in post-install.sh
|
||||
- **Who**: Dominik, ClaudeCode
|
||||
- **Why**: Commit 0726451 (2026-03-29) replaced the working `git clone https://aur.archlinux.org/paru.git && makepkg -si` bootstrap with `sudo pacman -S paru`, on the (wrong) assumption that paru had landed in `[extra]`. Verified against archlinux.org API: paru and paru-bin are AUR-only, not in any official repo. Fresh installs have been silently broken since — the command fails, the installer aborts before the moonarch registry is configured.
|
||||
- **Tradeoffs**: Bootstrap builds paru from source, which needs `base-devel` (already pulled by archinstall). Alternative `paru-bin` would skip the compile step; chose `paru` to match the upstream recommendation the user follows. Alternative "bundle paru in the Moonarch registry" would be internally consistent but adds a dependency on a running Gitea during install.
|
||||
- **How**: Replaced the `pacman -S paru` line in `post-install.sh` with the original bootstrap — `mktemp -d`, `git clone`, `makepkg -si --noconfirm`, `rm -rf`. Wrapped in an EXIT trap so the tempdir gets cleaned up even on failure.
|
||||
|
||||
## 2026-04-21 – Drop transform.sh and legacy update.sh shim
|
||||
- **Who**: Dominik, ClaudeCode
|
||||
- **Why**: transform.sh was added 2026-03-29 to onboard users from existing Arch+Wayland systems, but was never actually used — Moonarch is installed fresh via archinstall. Maintaining it meant duplicated paru/repo/key setup, a second entry point with its own backup/pre-flight logic, and a second reason for the `/opt/moonarch` clone. scripts/update.sh was a deprecation shim from before moonarch-update moved into the moonarch-git package; obsolete now that the package ships moonup/moondoc.
|
||||
- **Tradeoffs**: Existing Arch+Wayland users now have to install paru, add the `[moonarch]` repo + key manually, then `paru -S moonarch-git`. That's three commands instead of one script — acceptable since nobody actually uses that path. If the need returns, transform can be resurrected from git history.
|
||||
- **How**: Deleted `scripts/transform.sh` and `scripts/update.sh`. `lib.sh` stays (still sourced by post-install.sh). README "Transform" section removed, project-structure listing trimmed. Fresh-install flow via archinstall + post-install.sh is unchanged.
|
||||
|
||||
## 2026-04-20 – Registry-only install: drop paru --pkgbuilds from setup scripts
|
||||
- **Who**: Dominik, ClaudeCode
|
||||
- **Why**: Two parallel paths for finding moonarch packages (Arch registry via `[moonarch]` in pacman.conf, and paru's PKGBUILD-repo via `[moonarch-pkgbuilds]` in paru.conf) caused ambiguity during debugging: `paru -S moonarch-git` was resolving against the registry's stale DB (zombie r99 entry) while the PKGBUILD source would have had r105. Also: the PKGBUILD-repo path triggered a local build on every client, while the registry ships prebuilt binaries. With the registry DB now stable (see `moonarch-pkgbuilds/DECISIONS.md`, same date), the second mechanism is redundant.
|
||||
- **Tradeoffs**: If the registry is down or broken, clients have no local-build fallback. Acceptable — a broken registry is a server-side bug to fix, not something to mask with a parallel mechanism that complicates diagnostics.
|
||||
- **How**: `post-install.sh` and `transform.sh` no longer write `Mode = arp` or a `[moonarch-pkgbuilds]` section to `/etc/paru.conf`, and no longer call `paru -Syu --pkgbuilds`. They run `pacman -Sy` + `paru -S moonarch-git` against the registry. The `moonarch-git` install hook now strips the legacy paru.conf entries on upgrade (see moonarch-pkgbuilds repo).
|
||||
|
||||
## 2026-04-19 – moonup i18n: reuse pacman gettext catalog + inline fallback
|
||||
- **Who**: Dominik, ClaudeCode
|
||||
- **Why**: moonup prompts looked foreign next to pacman/paru output (`[y/N]` vs `[J/n]`, English strings on a German system). User wanted consistency with the rest of the pacman UX.
|
||||
- **Tradeoffs**: Full `.po`/`.mo` build-chain for moonarch was overkill for ~15 strings. gettext-only approach misses most moonarch-specific strings (no matching msgids in pacman catalog). Chose hybrid: `TEXTDOMAIN=pacman` for strings that match upstream msgids (prompts like `Proceed with installation?`, `Do you want to remove these packages?`, `[Y/n]`, `Starting full system upgrade...`), inline `_t()` helper for moonarch-specific strings.
|
||||
- **How**: `moonarch-update` sets `TEXTDOMAIN=pacman`/`TEXTDOMAINDIR=/usr/share/locale`. `_t "en" "de"` picks by `${LANG%%.*}` matching `de_*`. `confirm()` now uses `::` prefix, default Y, accepts `y/Y/j/J`. No PKGBUILD change — `gettext` is in `base`. pacman msgids with trailing `\n` (e.g. `Starting full system upgrade...\n`) require ANSI-C quoting `$'...\n'` to match.
|
||||
|
||||
## 2026-04-08 – Battery conservation mode: udev + sysfs + Waybar toggle
|
||||
- **Who**: Dominik, ClaudeCode
|
||||
- **Why**: Laptops with `charge_control_end_threshold` support benefit from limiting charge to 80% to extend battery lifespan. Needed a user-friendly toggle without requiring sudo.
|
||||
- **Tradeoffs**: udev RUN approach for permissions (group wheel gets write access) vs polkit/pkexec (password prompt on every toggle). Chose udev for seamless UX. State persisted in `/var/lib/moonarch/` (system-wide, not per-user) — acceptable since charge threshold is a hardware setting, not a user preference. Fixed 80% threshold instead of configurable — KISS, matches industry standard.
|
||||
- **How**: udev rule grants wheel group write on `charge_control_end_threshold`. Toggle script writes sysfs + state file. systemd oneshot service restores on boot. Waybar shows ♥ icon when active, hidden when inactive. Click on battery module toggles.
|
||||
|
||||
## 2026-04-07 – Walker theme: gtk-inherit → moonarch with fixed colors
|
||||
- **Who**: Dominik, ClaudeCode
|
||||
- **Why**: gtk-inherit theme relied on GTK4 color inheritance which works but doesn't update live when switching GTK themes (Walker service needs restart). Explicit color definitions make the theme self-contained and predictable.
|
||||
@@ -25,56 +105,56 @@
|
||||
- **How**: Added paru repo config (Mode=arp + [moonarch-pkgbuilds] section) to moonarch.install post_install/post_upgrade hook. Kept post-install.sh setup for bootstrap. Removed redundant setup from transform.sh.
|
||||
|
||||
## 2026-04-02 – Rename paru PKGBUILD repo, move config to /etc/paru.conf
|
||||
- **Who**: Dominik, Ragnar
|
||||
- **Who**: Dominik, ClaudeCode
|
||||
- **Why**: paru PKGBUILD repo and pacman package registry both used `[moonarch]` as section name. When pkgver-bot pushed version bumps, paru tried to resolve PKGBUILD targets (moongreet-git, moonlock-git) against the pacman repo — which only contains moonarch-git — causing "nicht alle benötigten Pakete gefunden" errors.
|
||||
- **Tradeoffs**: Renaming the PKGBUILD repo section means existing installations need a one-time manual fix. Using `/etc/paru.conf` instead of `~/.config/paru/paru.conf` is consistent with moonarch's system-wide config philosophy.
|
||||
- **How**: Renamed PKGBUILD repo section from `[moonarch]` to `[moonarch-pkgbuilds]`. Moved paru config (Mode + repo) from user-level `~/.config/paru/paru.conf` to system-wide `/etc/paru.conf`. Updated post-install.sh accordingly.
|
||||
|
||||
## 2026-04-01 – Replace dunst with swaync as notification daemon
|
||||
- **Who**: Dominik, Ragnar
|
||||
- **Who**: Dominik, ClaudeCode
|
||||
- **Why**: Dunst lacks wp_fractional_scale_v1 support, causing aliased/jagged font rendering on external monitors in mixed-DPI setups (laptop eDP-1 at 2.5x, externals at 1x). Confirmed by testing: removing fractional scaling fixed the issue. swaync uses GTK4 which handles fractional scaling natively.
|
||||
- **Tradeoffs**: swaync is heavier than dunst (GTK4 + libadwaita dependency). Loses dunstctl CLI (replaced by swaync-client). Gains notification center panel with DnD toggle, grouping, MPRIS widget support. Waybar already had swaync-client integration with exec-if guard.
|
||||
- **How**: Replaced dunst with swaync in packages/official.txt and archinstall config. Niri spawn-at-startup updated. Waybar dunstctl widget removed (swaync-client widget already present). New swaync config.json and style.css based on catppuccin/swaync upstream theme with Lavender accent instead of Blue.
|
||||
|
||||
## 2026-03-31 – Audit: shell script quoting fixes, PKGBUILD permissions
|
||||
|
||||
- **Who**: Ragnar, Dom
|
||||
- **Who**: ClaudeCode, Dom
|
||||
- **Why**: Security audit found command injection risk in moonarch-cpugov (unquoted array expansion with pkexec), word-splitting in moonarch-btnote (upower output from Bluetooth devices), and nmcli argument injection in moonarch-vpn. PKGBUILD for moongreet had world-readable cache dir.
|
||||
- **Tradeoffs**: `eval` in cpugov is safe because COMMANDS values are hardcoded string literals, not user input. Alternative (function dispatch) would be cleaner but over-engineered for 3 fixed entries. moonarch-btnote switched from for-loop to while+read with process substitution to avoid subshell.
|
||||
- **How**: (1) `eval "${COMMANDS[$choice]}"` in cpugov. (2) `while IFS= read -r` + process substitution + quoted `$DEVICE_DATA` in btnote. (3) `--` guard before `$connection` in vpn nmcli calls. (4) `install -dm700` for moongreet cache dirs in PKGBUILD. (5) `else err` logging in post-install.sh when USER_DEFAULTS missing.
|
||||
|
||||
## 2026-03-29 – /opt/moonarch stays root-owned, no chown to user
|
||||
- **Who**: Dominik, Ragnar
|
||||
- **Who**: Dominik, ClaudeCode
|
||||
- **Why**: Multi-user system — chown to UID 1000 locks out other users from moonarch-update
|
||||
- **Tradeoffs**: sudo required for git operations in update.sh vs. simpler user-owned repo
|
||||
- **How**: Repo stays at /opt/moonarch owned by root:root. update.sh uses `sudo git` for fetch/pull. All scripts already use sudo for system-level operations, so this is consistent.
|
||||
|
||||
## 2026-03-29 – Add transform.sh for existing Arch+Wayland systems
|
||||
- **Who**: Dominik, Ragnar
|
||||
- **Who**: Dominik, ClaudeCode
|
||||
- **Why**: Users with existing Arch+Wayland setups should be able to adopt Moonarch without reinstalling
|
||||
- **Tradeoffs**: Hard overwrite of all configs (user + system) vs. selective/merge approach — chose hard overwrite for simplicity and consistency
|
||||
- **How**: New transform.sh with pre-flight summary, backup, DM conflict resolution, and --dry-run flag. Shared helpers extracted to lib.sh.
|
||||
|
||||
## 2026-03-29 – Package moonarch as moonarch-git PKGBUILD
|
||||
- **Who**: Dominik, Ragnar
|
||||
- **Who**: Dominik, ClaudeCode
|
||||
- **Why**: System artifacts (XDG configs, helper scripts, zsh config, wallpaper) should be managed by pacman for clean deployment, versioning, rollback, and deinstallation
|
||||
- **Tradeoffs**: /etc/xdg/ configs NOT in backup= (moonarch philosophy: system defaults flow through, users override in ~/.config/). /etc/greetd/ and /etc/moongreet/ NOT owned by package (owned by greetd/moongreet-git, overwritten via .install hook). Helper scripts move from /usr/local/bin/ to /usr/bin/ (FHS for package-managed files)
|
||||
- **How**: moonarch-git PKGBUILD in moonarch-pkgbuilds repo. sweet-cursors-git as separate package. moonarch-update simplified (no git-sync, pacman handles file deployment). Installer scripts (post-install.sh, transform.sh) remain for orchestration, will be refactored in a follow-up to delegate file deployment to `paru -S moonarch-git`
|
||||
|
||||
## 2026-03-30 – Replace Rofi with Walker as application launcher
|
||||
- **Who**: Dominik, Ragnar
|
||||
- **Who**: Dominik, ClaudeCode
|
||||
- **Why**: Walker is Wayland-native (GTK4 + gtk4-layer-shell), has built-in providers for clipboard, bluetooth, audio (wireplumber), and Niri integration. Reduces custom shell scripts from 8 to 3. Rofi required a Wayland fork (rofi-lbonn-wayland-git) and every applet was a custom bash script.
|
||||
- **Tradeoffs**: Walker is newer/less battle-tested than Rofi. Requires separate Elephant daemon with per-provider packages. Dmenu mode lacks Rofi's `-a`/`-u` (active/urgent) and `-mesg` flags. Settings menu (moonarch-setmen) dropped entirely — apps are findable via Walker's app search.
|
||||
- **How**: Walker + Elephant as systemd user services. Native providers replace 5 rofi scripts (launcher, clipboard, bluetooth, volume, sink-switcher). 3 scripts ported to walker dmenu (vpn, cpugov, sink-switcher). Walker theme inherits GTK4 system theme colors (gtk-inherit). Old rofi configs preserved in `legacy/rofi/`.
|
||||
|
||||
## 2026-03-30 – Use nm-applet as VPN secret agent, add WireGuard support
|
||||
- **Who**: Dominik, Ragnar
|
||||
- **Who**: Dominik, ClaudeCode
|
||||
- **Why**: VPN auth previously spawned a foot terminal for `nmcli --ask`, which was fragile and ugly. WireGuard connections were invisible to the VPN script and Waybar indicator because both only checked for OpenVPN (`tun0` / `vpn` type).
|
||||
- **Tradeoffs**: nm-applet adds a tray indicator (mitigated with `--indicator` mode which is minimal). Requires nm-applet running at session start. Alternative was gnome-keyring or a custom secret agent — nm-applet is simpler and handles all NM secret types.
|
||||
- **How**: nm-applet started via niri spawn-at-startup. moonarch-vpn rewritten to support both vpn and wireguard types, uses nm-applet for auth instead of foot terminal, sends notify-send for connect/disconnect results. Waybar VPN module uses `nmcli` active connection check instead of `/proc/sys/net/ipv4/conf/tun0`, plus RTMIN+9 signal for instant updates after toggle.
|
||||
|
||||
## 2026-03-30 – Standardize GTK theme to Colloid-Grey-Dark-Catppuccin
|
||||
- **Who**: Dominik, Ragnar
|
||||
- **Who**: Dominik, ClaudeCode
|
||||
- **Why**: gsettings had `Colloid-Dark-Catppuccin` while config files had `Colloid-Catppuccin` — inconsistent. Grey accent matches the icon theme (Colloid-Grey-Catppuccin-Dark). Explicit `-Dark` variant is more reliable than depending on `prefer-dark` color-scheme setting.
|
||||
- **Tradeoffs**: Explicit dark locks out light mode toggle — acceptable since Moonarch is dark-only by design.
|
||||
- **How**: Updated transform.sh, post-install.sh, gtk-3.0/settings.ini, and gsettings to `Colloid-Grey-Dark-Catppuccin`. GTK4 symlinks updated accordingly.
|
||||
|
||||
@@ -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. |
|
||||
| **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. |
|
||||
| **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. |
|
||||
| **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. |
|
||||
@@ -30,6 +30,7 @@ desktop that can be rebuilt from scratch in minutes.
|
||||
| **Cursor** | Sweet-cursors | Visible on dark backgrounds without clashing. |
|
||||
| **Font** | UbuntuSans Nerd Font | Clean sans-serif with full Nerd Font glyph coverage for bar/terminal icons. |
|
||||
| **Wallpaper** | [waypaper](https://github.com/anufrievroman/waypaper) + [awww](https://codeberg.org/LGFae/awww) | waypaper provides GUI selection, awww handles animated transitions (60 FPS, 2s crossfade). |
|
||||
| **Night Light** | [wlsunset](https://sr.ht/~kennylevinsen/wlsunset/) | Wayland-native blue light filter via wlr-gamma-control. Toggleable from Waybar brightness group. |
|
||||
| **Firewall** | UFW | Simple deny-incoming/allow-outgoing default. Sane baseline without iptables complexity. |
|
||||
| **AUR Helper** | [paru](https://github.com/Morganamilo/paru) | Rust-based, pacman-compatible. Supports custom PKGBUILD repos for moongreet/moonlock/moonset. |
|
||||
|
||||
@@ -53,45 +54,26 @@ The archinstall config clones this repo to `/opt/moonarch` via custom-commands.
|
||||
post-install.sh handles the remaining ~95 packages, all configs, themes, services,
|
||||
and user setup.
|
||||
|
||||
### Transform (Existing Arch+Wayland System)
|
||||
|
||||
Already running Arch with a Wayland compositor (Sway, Hyprland, GNOME Wayland, etc.)?
|
||||
Transform converts your system to Moonarch without reinstalling.
|
||||
|
||||
**Prerequisites:** Active Wayland session, git installed.
|
||||
|
||||
```bash
|
||||
# Clone the repo
|
||||
sudo git clone https://gitea.moonarch.de/nevaforget/moonarch.git /opt/moonarch
|
||||
|
||||
# Preview what will happen (no changes made)
|
||||
/opt/moonarch/scripts/transform.sh --dry-run
|
||||
|
||||
# Run the transform
|
||||
/opt/moonarch/scripts/transform.sh
|
||||
|
||||
# Reboot (do NOT log out — your previous DM is already disabled)
|
||||
sudo reboot
|
||||
```
|
||||
|
||||
The script will:
|
||||
1. Show a pre-flight summary (package diff, config changes, detected conflicts)
|
||||
2. Back up your `~/.config/`, `~/.zshrc`, and `/etc/xdg/` to timestamped tar archives
|
||||
3. Disable conflicting display managers (SDDM, GDM, LightDM, etc.)
|
||||
4. Remove conflicting packages (e.g. PulseAudio → PipeWire)
|
||||
5. Install all Moonarch packages and configs (**hard overwrite** of most user configs; kanshi display profiles are preserved)
|
||||
6. Enable greetd, firewall, and system services
|
||||
|
||||
### Update
|
||||
|
||||
```bash
|
||||
moonarch-update
|
||||
moonup # or: moonarch-update
|
||||
```
|
||||
|
||||
Interactive updater that upgrades system and AUR packages, reconciles package lists
|
||||
against what's installed, and cleans orphaned packages. Config deployment happens
|
||||
automatically via the moonarch-git package on `paru -Syu`.
|
||||
|
||||
### Health Check
|
||||
|
||||
```bash
|
||||
moondoc # or: moonarch-doctor
|
||||
```
|
||||
|
||||
Diagnostic tool that verifies the system state against moonarch defaults: services,
|
||||
configs, packages, helper scripts, firewall, shell and directory permissions. Reports
|
||||
pass/fail/warn per check with a summary at the end.
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
@@ -103,10 +85,10 @@ packages/
|
||||
aur.txt AUR packages (~20), one per line
|
||||
|
||||
scripts/
|
||||
lib.sh Shared helpers sourced by all scripts
|
||||
lib.sh Shared helpers sourced by post-install.sh
|
||||
post-install.sh Main automation (packages, configs, themes, services)
|
||||
transform.sh Convert existing Arch+Wayland system to Moonarch
|
||||
moonarch-update Interactive updater (deployed to /usr/bin/ by package)
|
||||
moonarch-update Interactive updater (deployed to /usr/bin/ as moonup)
|
||||
moonarch-doctor System health checker (deployed to /usr/bin/ as moondoc)
|
||||
|
||||
defaults/
|
||||
xdg/ System-wide XDG configs (deployed to /etc/xdg/)
|
||||
@@ -131,12 +113,18 @@ defaults/
|
||||
moonarch-waybar-cpugov Waybar module: CPU governor status
|
||||
moonarch-waybar-gpustat Waybar module: GPU utilization
|
||||
moonarch-waybar-hidpp Waybar module: Logitech HID++ device battery
|
||||
moonarch-waybar-batsaver Waybar module: battery conservation mode indicator
|
||||
moonarch-waybar-updates Waybar module: package update checker (repo + AUR)
|
||||
moonarch-batsaver-toggle Toggle battery charge limit (80% ↔ 100%)
|
||||
moonarch-nightlight Toggle wlsunset blue light filter on/off
|
||||
moonarch-waybar-nightlight Waybar module: nightlight status
|
||||
|
||||
shell/zshrc Zsh config: prompt, aliases, FZF, completion
|
||||
etc/greetd/ greetd daemon + greeter Niri config
|
||||
etc/moongreet/ moongreet configuration
|
||||
etc/systemd/user/ Systemd user services (cliphist, kanshi, walker, nautilus)
|
||||
user/waybar/ Per-user Waybar overrides (only copied if missing)
|
||||
etc/systemd/system/ System service (battery conservation restore on boot)
|
||||
etc/udev/rules.d/ udev rules (battery threshold permissions)
|
||||
backgrounds/wallpaper.jpg Default wallpaper (shared by desktop, greeter, lock screen)
|
||||
```
|
||||
|
||||
@@ -150,8 +138,8 @@ greetd ─► niri (greeter instance) ─► moongreet ─► user authenticates
|
||||
│
|
||||
┌──────────┬────────┬────────┬──────────┐
|
||||
▼ ▼ ▼ ▼ ▼
|
||||
waybar swaync foot waypaper nm-applet
|
||||
(bar) (notify) (server) (wallpaper) (VPN)
|
||||
waybar swaync foot waypaper nm-applet wlsunset
|
||||
(bar) (notify) (server) (wallpaper) (VPN) (nightlight)
|
||||
|
||||
systemd user services (graphical-session.target):
|
||||
kanshi, cliphist, walker, nautilus
|
||||
@@ -187,7 +175,7 @@ All system configs live in `/etc/xdg/` and can be overridden per-user in `~/.con
|
||||
|
||||
| Component | System Default | User Override |
|
||||
|-----------|---------------|---------------|
|
||||
| Niri | `/etc/xdg/niri/config.kdl` | `~/.config/niri/config.kdl` |
|
||||
| Niri | `/etc/xdg/niri/config.kdl` | `~/.config/niri/config.kdl` (includes system config) |
|
||||
| Waybar | `/etc/xdg/waybar/` | `~/.config/waybar/` |
|
||||
| Walker | `/etc/xdg/walker/` | `~/.config/walker/` |
|
||||
| Foot | `/etc/xdg/foot/foot.ini` | `~/.config/foot/foot.ini` |
|
||||
@@ -206,7 +194,6 @@ are part of the system and updated via `paru -Syu`.
|
||||
| greetd | Login greeter (runs moongreet inside Niri) |
|
||||
| NetworkManager | Network management (WiFi, VPN, Ethernet) |
|
||||
| bluetooth | Bluetooth stack (bluez) |
|
||||
| docker | Container runtime |
|
||||
| systemd-timesyncd | NTP time synchronization |
|
||||
| ufw | Firewall (deny incoming, allow outgoing) |
|
||||
| auto-cpufreq | CPU frequency scaling (AC: performance, battery: powersave) |
|
||||
@@ -219,6 +206,7 @@ are part of the system and updated via `paru -Syu`.
|
||||
| kanshi | Dynamic display configuration (auto-switch output profiles on hotplug) |
|
||||
| nautilus | File manager preload (faster first launch) |
|
||||
| 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) |
|
||||
|
||||
## Moonarch Ecosystem
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
FROM archlinux:base-devel
|
||||
RUN pacman -Sy --noconfirm git curl && pacman -Scc --noconfirm
|
||||
RUN useradd -m builder && echo "builder ALL=(ALL) NOPASSWD: /usr/bin/pacman" >> /etc/sudoers
|
||||
RUN useradd -m builder && echo "builder ALL=(ALL) NOPASSWD: /usr/bin/pacman -S --needed *" >> /etc/sudoers
|
||||
ADD https://gitea.com/gitea/act_runner/releases/download/v0.3.1/act_runner-0.3.1-linux-amd64 /usr/local/bin/act_runner
|
||||
RUN chmod +x /usr/local/bin/act_runner
|
||||
COPY --from=gitea/act_runner:latest /usr/local/bin/run.sh /usr/local/bin/run.sh
|
||||
RUN echo "a05b2103a7cc5617197da214eaa06a1055362f21f9f475eb7fbacb8344d86cf8 /usr/local/bin/act_runner" | sha256sum -c - \
|
||||
&& chmod +x /usr/local/bin/act_runner
|
||||
COPY --from=gitea/act_runner:0.3.1 /usr/local/bin/run.sh /usr/local/bin/run.sh
|
||||
RUN mkdir -p /data && chown builder:builder /data
|
||||
USER builder
|
||||
ENV HOME=/home/builder
|
||||
|
||||
@@ -43,8 +43,6 @@
|
||||
"pipewire-jack",
|
||||
"pipewire-pulse",
|
||||
"wireplumber",
|
||||
"docker",
|
||||
"docker-compose",
|
||||
"fwupd",
|
||||
"ufw",
|
||||
"greetd",
|
||||
@@ -68,7 +66,6 @@
|
||||
"services": [
|
||||
"NetworkManager",
|
||||
"bluetooth",
|
||||
"docker",
|
||||
"greetd",
|
||||
"systemd-timesyncd",
|
||||
"ufw"
|
||||
|
||||
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"
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/bash
|
||||
# ABOUTME: Toggles battery conservation mode between 80% and 100% charge limit.
|
||||
# ABOUTME: Reads sysfs as user, dispatches the privileged write via pkexec.
|
||||
|
||||
THRESHOLD_FILE="/sys/class/power_supply/BAT0/charge_control_end_threshold"
|
||||
CONSERVATION_LIMIT=80
|
||||
|
||||
[[ -f "$THRESHOLD_FILE" ]] || exit 1
|
||||
|
||||
CURRENT=$(cat "$THRESHOLD_FILE")
|
||||
[[ "$CURRENT" =~ ^[0-9]+$ ]] || exit 1
|
||||
|
||||
if [[ "$CURRENT" -le "$CONSERVATION_LIMIT" ]]; then
|
||||
NEW=100
|
||||
else
|
||||
NEW="$CONSERVATION_LIMIT"
|
||||
fi
|
||||
|
||||
pkexec /usr/bin/moonarch-batsaver-apply "$NEW" || exit 1
|
||||
|
||||
# Signal Waybar to refresh the batsaver module (SIGRTMIN+9)
|
||||
pkill -RTMIN+9 waybar
|
||||
@@ -60,11 +60,12 @@ fi
|
||||
# check if choice exists
|
||||
if test "${COMMANDS[$choice]+isset}"
|
||||
then
|
||||
# Execute the choice — eval required because COMMANDS values contain
|
||||
# multi-word strings that must be interpreted as full commands.
|
||||
eval "${COMMANDS[$choice]}"
|
||||
${COMMANDS[$choice]}
|
||||
|
||||
notify-send -h string:x-canonical-private-synchronous:cpugov -i cpu "CPU Mode" "Set to $choice ${LABELS[$choice]}"
|
||||
|
||||
# Signal Waybar to refresh the cpugov module (SIGRTMIN+10)
|
||||
pkill -RTMIN+10 waybar
|
||||
else
|
||||
notify-send -u critical "CPU Governor" "Unknown command: ${choice}"
|
||||
fi
|
||||
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
# ABOUTME: Toggles wlsunset night light on/off via systemd user service.
|
||||
# ABOUTME: Persists state across reboots (enable/disable), signals waybar for refresh.
|
||||
|
||||
if systemctl --user is-active --quiet wlsunset; then
|
||||
systemctl --user disable --now wlsunset
|
||||
else
|
||||
systemctl --user enable --now wlsunset
|
||||
fi
|
||||
|
||||
pkill -RTMIN+11 waybar
|
||||
@@ -5,7 +5,11 @@
|
||||
# choose audio sink via rofi
|
||||
# changes default sink and moves all streams to that sink
|
||||
|
||||
sink=$(pactl list sinks short | awk '{print $1, $2}' | walker -d -p " Sink Switcher" | awk '{print $1}') &&
|
||||
sink=$(pactl list sinks short | awk '{print $1, $2}' | walker -d -p " Sink Switcher" | awk '{print $1}')
|
||||
|
||||
# Walker cancel returns empty — awk masks its non-zero exit. Guard here so we
|
||||
# don't call `pactl set-default-sink ""` on dismissal.
|
||||
[[ -n "$sink" ]] || exit 0
|
||||
|
||||
pactl set-default-sink "$sink" &&
|
||||
for input in $(pactl list sink-inputs short | awk '{print $1}'); do
|
||||
|
||||
@@ -39,13 +39,13 @@ function extract_connection_name() {
|
||||
# Requires nm-applet (or another NM secret agent) for interactive auth.
|
||||
function connect_vpn() {
|
||||
local connection="$1"
|
||||
nmcli connection up "$connection"
|
||||
nmcli connection up id "$connection"
|
||||
}
|
||||
|
||||
# Disconnect a VPN.
|
||||
function disconnect_vpn() {
|
||||
local connection="$1"
|
||||
nmcli connection down "$connection"
|
||||
nmcli connection down id "$connection"
|
||||
}
|
||||
|
||||
# Toggle the VPN connection based on its current state.
|
||||
|
||||
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
# ABOUTME: Wrapper that merges system waybar config with per-machine userconfig.
|
||||
# ABOUTME: Handles array prepend/append that waybar's native include cannot do.
|
||||
|
||||
SYSTEM_CONFIG="/etc/xdg/waybar/config"
|
||||
SYSTEM_STYLE="/etc/xdg/waybar/style.css"
|
||||
USER_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/waybar"
|
||||
USERCONFIG="$USER_DIR/userconfig"
|
||||
OUTPUT="$USER_DIR/config"
|
||||
USER_STYLE="$USER_DIR/style.css"
|
||||
|
||||
merge_config() {
|
||||
mkdir -p "$USER_DIR"
|
||||
|
||||
if ! jq -s '
|
||||
.[0] as $sys | .[1] as $user |
|
||||
(($user.prepend // {}) | to_entries) as $prepends |
|
||||
(($user.append // {}) | to_entries) as $appends |
|
||||
$sys |
|
||||
reduce $prepends[] as $p (.;
|
||||
.[$p.key] = ($p.value + (.[$p.key] // []))
|
||||
) |
|
||||
reduce $appends[] as $a (.;
|
||||
.[$a.key] = ((.[$a.key] // []) + $a.value)
|
||||
) |
|
||||
($user | del(.prepend) | del(.append)) as $extras |
|
||||
. * $extras
|
||||
' "$SYSTEM_CONFIG" "$USERCONFIG" > "${OUTPUT}.tmp" 2>&1; then
|
||||
local err
|
||||
err=$(cat "${OUTPUT}.tmp")
|
||||
rm -f "${OUTPUT}.tmp"
|
||||
logger -t moonarch-waybar "Config merge failed: $err"
|
||||
notify-send -u critical "moonarch-waybar" "Config merge failed — using system config.\n$err"
|
||||
return 1
|
||||
fi
|
||||
|
||||
mv "${OUTPUT}.tmp" "$OUTPUT"
|
||||
}
|
||||
|
||||
bootstrap_style() {
|
||||
if [[ ! -f "$USER_STYLE" ]]; then
|
||||
mkdir -p "$USER_DIR"
|
||||
cat > "$USER_STYLE" << 'CSS'
|
||||
/* Generated by moonarch-waybar — add custom styles below */
|
||||
@import url("/etc/xdg/waybar/style.css");
|
||||
CSS
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ -f "$USERCONFIG" ]]; then
|
||||
if [[ ! -f "$OUTPUT" ]] ||
|
||||
[[ "$USERCONFIG" -nt "$OUTPUT" ]] ||
|
||||
[[ "$SYSTEM_CONFIG" -nt "$OUTPUT" ]]; then
|
||||
# On merge failure the previous $OUTPUT is stale — remove it so waybar
|
||||
# falls back to XDG's system config instead of running with stale merged data.
|
||||
merge_config || rm -f "$OUTPUT"
|
||||
fi
|
||||
bootstrap_style
|
||||
fi
|
||||
|
||||
exec waybar "$@"
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/bash
|
||||
# ABOUTME: Waybar module showing battery conservation mode status.
|
||||
# ABOUTME: Outputs heart icon when active (threshold ≤80), empty when inactive.
|
||||
|
||||
THRESHOLD_FILE="/sys/class/power_supply/BAT0/charge_control_end_threshold"
|
||||
|
||||
# No battery threshold support → no output → Waybar hides module
|
||||
[[ -f "$THRESHOLD_FILE" ]] || exit 0
|
||||
|
||||
THRESHOLD=$(cat "$THRESHOLD_FILE")
|
||||
|
||||
if [[ "$THRESHOLD" -le 80 ]]; then
|
||||
jq --compact-output -n \
|
||||
--arg text "♥" \
|
||||
--arg tooltip "Battery Conservation: ON (limit ${THRESHOLD}%)" \
|
||||
--arg class "on" \
|
||||
'{text: $text, tooltip: $tooltip, class: $class}'
|
||||
fi
|
||||
# Threshold > 80 → no output → Waybar hides module
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/bash
|
||||
# ABOUTME: Waybar-Modul das den CPU-Governor als JSON ausgibt.
|
||||
# ABOUTME: Wird von der Waybar custom/cpugov Config referenziert.
|
||||
# ABOUTME: Waybar module that outputs the CPU governor as JSON.
|
||||
# ABOUTME: Referenced by the Waybar custom/cpugov config.
|
||||
|
||||
CPU_GOV=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor)
|
||||
CPU_GOV=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null)
|
||||
|
||||
case $CPU_GOV in
|
||||
performance)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/bash
|
||||
# ABOUTME: Waybar-Modul das die GPU-Auslastung als JSON ausgibt.
|
||||
# ABOUTME: Wird von der Waybar custom/gpu-usage Config referenziert.
|
||||
# ABOUTME: Waybar module that outputs GPU utilization as JSON.
|
||||
# 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="${GPU_STAT:-0}"
|
||||
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
# ABOUTME: Outputs JSON status for waybar nightlight module.
|
||||
# ABOUTME: Checks wlsunset systemd service, shows warm icon when active.
|
||||
|
||||
if systemctl --user is-active --quiet wlsunset; then
|
||||
jq -nc '{text: "", alt: "on", tooltip: "Nightlight: An (5000K)", class: "on"}'
|
||||
else
|
||||
jq -nc '{text: "", alt: "off", tooltip: "Nightlight: Aus", class: "off"}'
|
||||
fi
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/bash
|
||||
# ABOUTME: Waybar module that checks for available package updates with caching.
|
||||
# ABOUTME: Combines pacman repo updates (checkupdates) and AUR updates (paru -Qua).
|
||||
|
||||
CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/moonarch"
|
||||
CACHE_FILE="$CACHE_DIR/waybar-updates"
|
||||
PACMAN_DB="/var/lib/pacman/local"
|
||||
MAX_AGE=3600
|
||||
|
||||
mkdir -p "$CACHE_DIR"
|
||||
|
||||
needs_refresh() {
|
||||
# No cache yet
|
||||
[[ ! -f "$CACHE_FILE" ]] && return 0
|
||||
|
||||
# Pacman DB changed since last check (update was installed)
|
||||
[[ "$PACMAN_DB" -nt "$CACHE_FILE" ]] && return 0
|
||||
|
||||
# Cache older than MAX_AGE
|
||||
local age=$(( $(date +%s) - $(stat -c %Y "$CACHE_FILE") ))
|
||||
[[ "$age" -ge "$MAX_AGE" ]] && return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
if needs_refresh; then
|
||||
repo_updates=$(checkupdates 2>/dev/null)
|
||||
repo_count=0
|
||||
if [[ -n "$repo_updates" ]]; then
|
||||
repo_count=$(echo "$repo_updates" | wc -l)
|
||||
fi
|
||||
|
||||
aur_updates=""
|
||||
aur_count=0
|
||||
if command -v paru &>/dev/null; then
|
||||
aur_updates=$(paru -Qua 2>/dev/null)
|
||||
if [[ -n "$aur_updates" ]]; then
|
||||
aur_count=$(echo "$aur_updates" | wc -l)
|
||||
fi
|
||||
fi
|
||||
|
||||
total=$((repo_count + aur_count))
|
||||
|
||||
if [[ "$total" -eq 0 ]]; then
|
||||
echo "" > "$CACHE_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Build tooltip with package lists
|
||||
tooltip=""
|
||||
if [[ -n "$repo_updates" ]]; then
|
||||
tooltip+="Repo ($repo_count):"$'\n'"$repo_updates"
|
||||
fi
|
||||
if [[ -n "$aur_updates" ]]; then
|
||||
[[ -n "$tooltip" ]] && tooltip+=$'\n\n'
|
||||
tooltip+="AUR ($aur_count):"$'\n'"$aur_updates"
|
||||
fi
|
||||
|
||||
jq --compact-output -n \
|
||||
--arg text "$total" \
|
||||
--arg alt "has-updates" \
|
||||
--arg tooltip "$tooltip" \
|
||||
--arg class "has-updates" \
|
||||
'{text: $text, alt: $alt, tooltip: $tooltip, class: $class}' > "$CACHE_FILE"
|
||||
fi
|
||||
|
||||
# Output cached result (empty cache = no updates = module hidden)
|
||||
cat "$CACHE_FILE"
|
||||
@@ -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>
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
[appearance]
|
||||
background = "/usr/share/moonarch/wallpaper.jpg"
|
||||
cursor-theme = "Sweet-cursors"
|
||||
cursor-size = 24
|
||||
|
||||
[behavior]
|
||||
# show_user_list = true
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Disable stock OSC — ModernZ replaces it.
|
||||
osc=no
|
||||
|
||||
# Hide native title bar for a cleaner borderless look (ModernZ draws its own).
|
||||
title-bar=no
|
||||
|
||||
# Cap window size at 80% of screen for oversized videos.
|
||||
autofit-larger=80%x80%
|
||||
|
||||
# --- ModernZ overrides ---
|
||||
# mpv treats # as a mid-line comment; the full value must be quoted so the
|
||||
# hex color survives.
|
||||
script-opts-append="modernz-seekbarfg_color=#b4befe"
|
||||
script-opts-append="modernz-seek_handle_color=#b4befe"
|
||||
script-opts-append="modernz-seek_handle_border_color=#b4befe"
|
||||
script-opts-append="modernz-hover_effect_color=#b4befe"
|
||||
script-opts-append="modernz-nibble_color=#b4befe"
|
||||
script-opts-append="modernz-ontop_button=no"
|
||||
script-opts-append="modernz-window_title_font_size=18"
|
||||
|
||||
# Scale OSC down globally
|
||||
script-opts-append="modernz-scalewindowed=0.75"
|
||||
script-opts-append="modernz-scalefullscreen=0.75"
|
||||
@@ -0,0 +1,23 @@
|
||||
# ABOUTME: Restores battery charge threshold from saved state on boot.
|
||||
# ABOUTME: Only runs on laptops with threshold support and a saved state file.
|
||||
|
||||
[Unit]
|
||||
Description=Restore battery conservation mode threshold
|
||||
After=sysinit.target
|
||||
ConditionPathExists=/sys/class/power_supply/BAT0/charge_control_end_threshold
|
||||
ConditionPathExists=/var/lib/moonarch/batsaver-threshold
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/bin/moonarch-batsaver-restore
|
||||
NoNewPrivileges=true
|
||||
ProtectHome=true
|
||||
PrivateTmp=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictNamespaces=true
|
||||
RestrictRealtime=true
|
||||
LockPersonality=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,16 @@
|
||||
# ABOUTME: systemd user service for image clipboard history via cliphist + wl-paste.
|
||||
# ABOUTME: Stores image clipboard entries in XDG_RUNTIME_DIR.
|
||||
|
||||
[Unit]
|
||||
Description=Clipboard history manager (image)
|
||||
PartOf=graphical-session.target
|
||||
After=cliphist-text.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/wl-paste --type image --watch cliphist -db-path %t/cliphist/db store
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical-session.target
|
||||
@@ -0,0 +1,16 @@
|
||||
# ABOUTME: systemd user service for text clipboard history via cliphist + wl-paste.
|
||||
# ABOUTME: Wipes history on start, stores text entries in XDG_RUNTIME_DIR.
|
||||
|
||||
[Unit]
|
||||
Description=Clipboard history manager (text)
|
||||
PartOf=graphical-session.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStartPre=/bin/sh -c 'mkdir -p $XDG_RUNTIME_DIR/cliphist && /usr/bin/cliphist wipe'
|
||||
ExecStart=/usr/bin/wl-paste --watch cliphist -db-path %t/cliphist/db store
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical-session.target
|
||||
@@ -1,16 +0,0 @@
|
||||
# ABOUTME: systemd user service for clipboard history (cliphist + wl-paste).
|
||||
# ABOUTME: Stores text and image clipboard entries, wipes history on session end.
|
||||
|
||||
[Unit]
|
||||
Description=Clipboard history manager
|
||||
PartOf=graphical-session.target
|
||||
After=graphical-session.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
ExecStartPre=/usr/bin/cliphist wipe
|
||||
ExecStart=/bin/sh -c 'mkdir -p $XDG_RUNTIME_DIR/cliphist && wl-paste --watch cliphist -db-path $XDG_RUNTIME_DIR/cliphist/db store & wl-paste --type image --watch cliphist -db-path $XDG_RUNTIME_DIR/cliphist/db store &'
|
||||
RemainAfterExit=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical-session.target
|
||||
@@ -0,0 +1,20 @@
|
||||
# ABOUTME: systemd user service for wlsunset night light (blue light filter).
|
||||
# ABOUTME: Starts after kanshi to ensure all outputs are configured.
|
||||
|
||||
[Unit]
|
||||
Description=Wlsunset night light (blue light filter)
|
||||
Documentation=man:wlsunset(1)
|
||||
PartOf=graphical-session.target
|
||||
After=graphical-session.target kanshi.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
# Give kanshi time to configure all outputs before wlsunset captures them
|
||||
ExecStartPre=/bin/sleep 2
|
||||
ExecStart=/usr/bin/wlsunset -T 6500 -t 5000 -S 00:00 -s 00:01
|
||||
ExecStartPost=/usr/bin/pkill -RTMIN+11 waybar
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical-session.target
|
||||
@@ -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;
|
||||
+14
-27
@@ -1,5 +1,4 @@
|
||||
# ABOUTME: Moonarch default zsh configuration with Catppuccin-themed prompt.
|
||||
# ABOUTME: Sources user overrides from ~/.zshrc.d/ and ~/.zshrc.local
|
||||
|
||||
# --- History ---
|
||||
HISTFILE=~/.histfile
|
||||
@@ -30,26 +29,25 @@ add-zsh-hook preexec _preexec_title
|
||||
|
||||
# --- Prompt (Catppuccin Mocha) ---
|
||||
parse_git_branch() {
|
||||
local branch=""
|
||||
branch=$(git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/')
|
||||
local git_status=$(git status --porcelain 2>/dev/null)
|
||||
local color=green
|
||||
if echo "$git_status" | grep -q "^ M"; then
|
||||
color=yellow
|
||||
branch="${branch}*"
|
||||
# Gate on cheap check first — spawning git in every non-repo directory on every
|
||||
# prompt render costs 20-80ms per prompt. Pattern-match the status output with
|
||||
# zsh glob matching instead of piping to grep for three subshell-spawning checks.
|
||||
git rev-parse --git-dir &>/dev/null || return
|
||||
local branch="" git_status="" color=green flags=""
|
||||
branch=$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD 2>/dev/null)
|
||||
git_status=$(git status --porcelain 2>/dev/null)
|
||||
if [[ "$git_status" == *$'\n M '* || "$git_status" == " M "* || "$git_status" == *$'\nM'* ]]; then
|
||||
color=yellow; flags+="*"
|
||||
fi
|
||||
if echo "$git_status" | grep -qE "^ A|^\?\?"; then
|
||||
color=yellow
|
||||
branch="${branch}+"
|
||||
if [[ "$git_status" == *$'\nA '* || "$git_status" == "A "* || "$git_status" == *'??'* ]]; then
|
||||
color=yellow; flags+="+"
|
||||
fi
|
||||
if echo "$git_status" | grep -q "^ D"; then
|
||||
color=yellow
|
||||
branch="${branch}-"
|
||||
if [[ "$git_status" == *$'\n D '* || "$git_status" == " D "* ]]; then
|
||||
color=yellow; flags+="-"
|
||||
fi
|
||||
if [[ -n "$branch" ]]; then
|
||||
branch=[%F{${color}}${branch}%F{reset}]
|
||||
echo " [%F{${color}}${branch}${flags}%F{reset}]"
|
||||
fi
|
||||
echo " $branch"
|
||||
}
|
||||
|
||||
precmd() {
|
||||
@@ -141,14 +139,3 @@ export XDG_SESSION_TYPE="wayland"
|
||||
export EDITOR="nvim"
|
||||
export SUDO_EDITOR="nvim"
|
||||
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"
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
// ABOUTME: User override for Waybar — extends the system-wide config from /etc/xdg/waybar/.
|
||||
// ABOUTME: Define custom modules here and add them to modules-left/center/right.
|
||||
|
||||
// Load system-wide Moonarch config as base.
|
||||
// Properties defined here override those from the include.
|
||||
"include": ["/etc/xdg/waybar/config"]
|
||||
|
||||
// Example: extend module bar (must be specified completely as it replaces
|
||||
// the system-wide one):
|
||||
//
|
||||
// "modules-right": [
|
||||
// "mpris",
|
||||
// "custom/cpugov",
|
||||
// "group/net",
|
||||
// "group/sound",
|
||||
// "backlight",
|
||||
// "custom/keyboard",
|
||||
// "battery",
|
||||
// "group/indicators"
|
||||
// ],
|
||||
|
||||
// Example: show HID++ device battery (e.g. Logitech keyboard)
|
||||
// moonarch-waybar-hidpp finds the correct hidpp_battery_* entry dynamically
|
||||
//
|
||||
// "custom/keyboard": {
|
||||
// "exec": "moonarch-waybar-hidpp 'G515 LS TKL'",
|
||||
// "return-type": "json",
|
||||
// "interval": 120,
|
||||
// "format": "{}",
|
||||
// "exec-on-event": false
|
||||
// }
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
/* ABOUTME: User override for Waybar styling — extends the system-wide style.css. */
|
||||
/* ABOUTME: Define custom styles here, the system-wide base is loaded via @import. */
|
||||
|
||||
@import url("/etc/xdg/waybar/style.css");
|
||||
|
||||
/* Add custom styles below. */
|
||||
/* Selectors from the system-wide config can be overridden here. */
|
||||
|
||||
/* Example: color Logitech keyboard battery */
|
||||
/*
|
||||
#battery-keyboard.warning:not(.charging) {
|
||||
color: #e6a200;
|
||||
}
|
||||
|
||||
#battery-keyboard.critical:not(.charging) {
|
||||
color: #cc3436;
|
||||
}
|
||||
*/
|
||||
@@ -4,4 +4,6 @@
|
||||
[Settings]
|
||||
gtk-theme-name=Colloid-Grey-Dark-Catppuccin
|
||||
gtk-icon-theme-name=Colloid-Grey-Catppuccin-Dark
|
||||
gtk-cursor-theme-name=Sweet-cursors
|
||||
gtk-cursor-theme-size=24
|
||||
gtk-application-prefer-dark-theme=1
|
||||
|
||||
@@ -41,6 +41,7 @@ gestures {
|
||||
layout {
|
||||
gaps 8
|
||||
center-focused-column "never"
|
||||
always-center-single-column
|
||||
|
||||
preset-column-widths {
|
||||
proportion 0.33333
|
||||
@@ -78,13 +79,14 @@ layout {
|
||||
|
||||
// xwayland-satellite is managed automatically since niri 25.08
|
||||
// kanshi is managed via systemd user service (kanshi.service)
|
||||
spawn-at-startup "waybar"
|
||||
spawn-at-startup "moonarch-waybar"
|
||||
spawn-at-startup "swaync"
|
||||
spawn-at-startup "/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1"
|
||||
spawn-at-startup "nm-applet" "--indicator"
|
||||
spawn-sh-at-startup "waypaper --restore"
|
||||
// spawn-sh-at-startup "nemo . &> /dev/null &"
|
||||
spawn-sh-at-startup "foot --server"
|
||||
// wlsunset is managed via systemd user service (wlsunset.service)
|
||||
// Clipboard history managed by cliphist.service (systemd user service)
|
||||
|
||||
hotkey-overlay {
|
||||
@@ -114,6 +116,11 @@ window-rule {
|
||||
open-floating true
|
||||
}
|
||||
|
||||
window-rule {
|
||||
match app-id=r#"^mpv$"#
|
||||
open-floating true
|
||||
}
|
||||
|
||||
window-rule {
|
||||
geometry-corner-radius 4
|
||||
clip-to-geometry true
|
||||
@@ -124,7 +131,7 @@ binds {
|
||||
|
||||
Super+C hotkey-overlay-title=null { spawn "walker" "-s" "clipboard"; }
|
||||
|
||||
Alt+W { spawn-sh "killall waybar && waybar &"; }
|
||||
Alt+W { spawn-sh "killall waybar && moonarch-waybar &"; }
|
||||
|
||||
Super+E { spawn-sh "xdg-open ~"; }
|
||||
|
||||
|
||||
@@ -5,9 +5,15 @@
|
||||
@description "Idle management for Moonarch (Niri + moonlock)"
|
||||
|
||||
default:
|
||||
# Media playback inhibits idle (non-browser only, browser uses D-Bus inhibit)
|
||||
# monitor_media: detect active media via PipeWire/PulseAudio sink-inputs
|
||||
# (pactl), not MPRIS. Non-browser players (mpv, vlc, ...) are counted and
|
||||
# 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
|
||||
ignore_remote_media true
|
||||
ignore_remote_media false
|
||||
|
||||
# App/process inhibit patterns (apps that don't use D-Bus idle-inhibit)
|
||||
inhibit_apps [
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"cssPriority": "user",
|
||||
|
||||
"notification-window-width": 300,
|
||||
"notification-icon-size": 64,
|
||||
"notification-window-height": -1,
|
||||
"notification-body-image-height": 100,
|
||||
"notification-body-image-width": 200,
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
|
||||
* {
|
||||
all: unset;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
font-family: "UbuntuSans Nerd Font";
|
||||
transition: 200ms;
|
||||
--notification-icon-size: 36px;
|
||||
}
|
||||
|
||||
trough highlight {
|
||||
|
||||
+69
-94
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"__aboutme1": "Moonarch default waybar configuration for Niri.",
|
||||
"__aboutme2": "User overrides go in ~/.config/waybar/config",
|
||||
"__aboutme2": "Deployed to /etc/xdg/waybar/ by moonarch-git package.",
|
||||
"layer": "top",
|
||||
"margin-top": 0,
|
||||
"spacing": 5,
|
||||
@@ -17,18 +17,18 @@
|
||||
"modules-right": [
|
||||
"tray",
|
||||
"mpris",
|
||||
"custom/cpugov",
|
||||
"bluetooth",
|
||||
"group/sound",
|
||||
"backlight",
|
||||
"battery",
|
||||
"group/brightness",
|
||||
"group/bat",
|
||||
"group/indicators"
|
||||
],
|
||||
"group/indicators": {
|
||||
"orientation": "inherit",
|
||||
"modules": [
|
||||
"custom/cpugov",
|
||||
"gamemode",
|
||||
//"custom/updates",
|
||||
"custom/updates",
|
||||
"idle_inhibitor",
|
||||
"custom/notification",
|
||||
"privacy"
|
||||
@@ -55,9 +55,28 @@
|
||||
],
|
||||
"drawer": {
|
||||
"transition-duration": 500,
|
||||
"transition-left-to-right": true
|
||||
"transition-left-to-right": false
|
||||
}
|
||||
},
|
||||
"group/brightness": {
|
||||
"orientation": "inherit",
|
||||
"modules": [
|
||||
"custom/nightlight",
|
||||
"backlight",
|
||||
"backlight/slider"
|
||||
],
|
||||
"drawer": {
|
||||
"transition-duration": 500,
|
||||
"transition-left-to-right": false
|
||||
}
|
||||
},
|
||||
"group/bat": {
|
||||
"orientation": "inherit",
|
||||
"modules": [
|
||||
"battery",
|
||||
"custom/batsaver"
|
||||
]
|
||||
},
|
||||
"group/sys": {
|
||||
"orientation": "inherit",
|
||||
"modules": [
|
||||
@@ -96,7 +115,6 @@
|
||||
"on-scroll-down": "shift_down",
|
||||
"on-click-middle": "alarm-clock-applet"
|
||||
}
|
||||
// "on-click": "evolution"
|
||||
},
|
||||
"user": {
|
||||
"format": "{user}",
|
||||
@@ -172,7 +190,6 @@
|
||||
"format": "{icon}",
|
||||
"icon-size": 16,
|
||||
"tooltip-format": "{title:.100}",
|
||||
// "sort-by-app-id": true,
|
||||
"on-click": "activate",
|
||||
"on-click-middle": "close",
|
||||
"on-right-middle": "minimize-raise",
|
||||
@@ -185,7 +202,7 @@
|
||||
"firefox": "Firefox",
|
||||
"VSCodium": "Code",
|
||||
"codium": "Code",
|
||||
"Alacritty": "Terminal",
|
||||
"Alacritty": "Terminal"
|
||||
},
|
||||
"squash-list": [
|
||||
"firefox"
|
||||
@@ -198,9 +215,9 @@
|
||||
"has-updates": "",
|
||||
"updated": ""
|
||||
},
|
||||
"exec-if": "which waybar-module-pacman-updates",
|
||||
"exec": "waybar-module-pacman-updates",
|
||||
"on-click": "alacritty paru"
|
||||
"exec": "moonarch-waybar-updates",
|
||||
"interval": 60,
|
||||
"on-click": "foot env MOONUP_WAIT=1 moonarch-update"
|
||||
},
|
||||
"custom/notification": {
|
||||
"tooltip": true,
|
||||
@@ -223,10 +240,8 @@
|
||||
"escape": true
|
||||
},
|
||||
"cava": {
|
||||
// "cava_config": "$XDG_CONFIG_HOME/cava/cava.conf",
|
||||
"framerate": 30,
|
||||
"autosens": 1,
|
||||
//"sensitivity": 50,
|
||||
"bars": 2,
|
||||
"lower_cutoff_freq": 50,
|
||||
"higher_cutoff_freq": 10000,
|
||||
@@ -286,16 +301,25 @@
|
||||
"<span color='#dd532e' size='8pt' rise='1.5pt'>█</span>"
|
||||
]
|
||||
},
|
||||
"custom/nightlight": {
|
||||
"exec": "moonarch-waybar-nightlight",
|
||||
"return-type": "json",
|
||||
"interval": "once",
|
||||
"signal": 11,
|
||||
"on-click": "moonarch-nightlight",
|
||||
"format": "{}"
|
||||
},
|
||||
"custom/cpugov": {
|
||||
"exec": "moonarch-waybar-cpugov",
|
||||
"return-type": "json",
|
||||
"interval": 5,
|
||||
"interval": 60,
|
||||
"signal": 10,
|
||||
"on-click": "moonarch-cpugov"
|
||||
},
|
||||
"custom/gpu-usage": {
|
||||
"exec": "moonarch-waybar-gpustat",
|
||||
"return-type": "json",
|
||||
"restart-interval": 10
|
||||
"interval": 60
|
||||
},
|
||||
"battery": {
|
||||
"bat": "BAT0",
|
||||
@@ -306,18 +330,30 @@
|
||||
},
|
||||
"format": "{capacity}% {icon}",
|
||||
"format-icons": [
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""
|
||||
],
|
||||
"max-length": 25
|
||||
"max-length": 25,
|
||||
"on-click": "moonarch-batsaver-toggle"
|
||||
},
|
||||
"custom/batsaver": {
|
||||
"exec": "moonarch-waybar-batsaver",
|
||||
"return-type": "json",
|
||||
"interval": 30,
|
||||
"signal": 9
|
||||
},
|
||||
"bluetooth": {
|
||||
// "controller": "controller1", // specify the alias of the controller if there are more than 1 on the system
|
||||
"format": " {status}",
|
||||
"format-disabled": "", // an empty format will hide the module
|
||||
"format": "",
|
||||
"format-disabled": "",
|
||||
"format-connected": "<small>{num_connections}</small>",
|
||||
"tooltip-format": "{controller_alias}\t{controller_address}",
|
||||
"tooltip-format-connected": "{controller_alias}\t{controller_address}\n\n{device_enumerate}\t{device_battery_percentage}%",
|
||||
@@ -369,13 +405,10 @@
|
||||
"niri/workspaces": {
|
||||
"format": "{icon}",
|
||||
"format-icons": {
|
||||
// Named workspaces
|
||||
// (you need to configure them in niri)
|
||||
"browser": "",
|
||||
"discord": "",
|
||||
"chat": "<b></b>",
|
||||
|
||||
// Icons by state
|
||||
"active": "",
|
||||
"default": ""
|
||||
}
|
||||
@@ -399,114 +432,56 @@
|
||||
"device": "intel_backlight"
|
||||
},
|
||||
"cffi/niri-workspaces-enhanced": {
|
||||
// Make sure to set the path to the install location on your system
|
||||
// "module_path": "~/.config/waybar/niri-workspaces-enhanced.so",
|
||||
"module_path": "/usr/lib/waybar/libwaybar_niri_workspaces_enhanced.so",
|
||||
// Format string for workspace labels. Available placeholders:
|
||||
// {index} - Workspace index number
|
||||
// {name} - Workspace name (might be empty)
|
||||
// {index-and-name} - Index followed by name if present (e.g., "1 Work")
|
||||
// {value} - Name if present, otherwise index
|
||||
// {separator} - ": " when icons are present, "" when empty
|
||||
// {window-icons} - Formatted icons for windows in workspace
|
||||
"format": "{window-icons}",
|
||||
// Apply separate styles to icons depending on current state
|
||||
"window-icon-format": {
|
||||
"default": "{icon}",
|
||||
"urgent": "<span foreground='red'>{icon}</span>",
|
||||
"focused": "<span foreground='blue'>{icon}</span>",
|
||||
"focused": "<span foreground='blue'>{icon}</span>"
|
||||
},
|
||||
// A mapping from window app_id to icon. Note that this module does
|
||||
// case-insensitive matching of app_ids, so capitalization doesn't matter.
|
||||
"window-icons": {
|
||||
"com.mitchellh.ghostty": "",
|
||||
"darktable": "",
|
||||
"foot": "",
|
||||
"google-chrome": "",
|
||||
"spotify": "",
|
||||
"steam": "",
|
||||
"steam": ""
|
||||
},
|
||||
// If no icon is found for a window, the default is used instead
|
||||
"window-icon-default": "*",
|
||||
"window-icon-default": "*"
|
||||
},
|
||||
"height": 40,
|
||||
"cffi/niri-windows": {
|
||||
// path where you placed the .so file
|
||||
"module_path": "/usr/lib/waybar-niri-windows.so",
|
||||
// configure the module's behavior
|
||||
"options": {
|
||||
// set the module mode
|
||||
// "graphical" (default): draw a minimap of windows in the current workspace
|
||||
// "text": draws symbols and a focus indicator for each column (mirrors v1 behavior)
|
||||
"mode": "graphical",
|
||||
|
||||
// ======= graphical mode options =======
|
||||
// when to show floating windows
|
||||
// - "always": always show floating window view, even if there are no floating windows
|
||||
// - "auto" (default): show floating window view if there are floating windows on the current workspace
|
||||
// - "never": never show floating windows
|
||||
"show-floating": "auto",
|
||||
// pick where the floating windows be shown relative to tiled windows
|
||||
// - "left": show floating windows on the left
|
||||
// - "right" (default): show floating windows on the right
|
||||
"floating-position": "right",
|
||||
// set minimum size of windows, in pixels (default: 1, minimum: 1)
|
||||
// if this value is too large to fit all windows (e.g. in a column with many windows),
|
||||
// it will be reduced
|
||||
"minimum-size": 1,
|
||||
// set spacing between windows/columns, in pixels (default: 1, minimum: 0)
|
||||
// if this value is too large, it will be reduced
|
||||
"spacing": 1,
|
||||
// set minimum size of windows, in pixels, to draw icons for (default: 0, minimum: 0)
|
||||
// if unset or 0, icons will only be drawn for tiled windows that are the only one in their column
|
||||
// if 1+, icons will be drawn for all windows where w >= icon-minimum-size and h >= icon-minimum-size
|
||||
// icons must be set in the "rules" section below for this to have any effect
|
||||
"icon-minimum-size": 0,
|
||||
// account for borders when calculating window sizes; see note below (default: 0, minimum: 0)
|
||||
"column-borders": 0, // border on .column
|
||||
"floating-borders": 0, // border on .floating
|
||||
// trigger actions on tile click (see https://yalter.github.io/niri/niri_ipc/enum.Action.html for available actions)
|
||||
// only actions that take a single window ID are supported
|
||||
// set to an empty string to disable
|
||||
"on-tile-click": "FocusWindow", // (default: FocusWindow)
|
||||
"on-tile-middle-click": "CloseWindow", // (default: CloseWindow)
|
||||
"on-tile-right-click": "", // (default: none)
|
||||
// add CSS classes/icons to windows based on their App ID/Title (see `niri msg windows`)
|
||||
// Go regular expression syntax is supported for app-id and title (see https://pkg.go.dev/regexp/syntax)
|
||||
// rules are checked in the order they are defined - first match wins and checking stops
|
||||
// set "continue" to true to also check and apply subsequent rules even if this rule matches
|
||||
// if multiple rules with icons are applied, the first one will be used
|
||||
// *icons are not drawn for floating windows by default*; set "icon-minimum-size" to enable (see above)
|
||||
"column-borders": 0,
|
||||
"floating-borders": 0,
|
||||
"on-tile-click": "FocusWindow",
|
||||
"on-tile-middle-click": "CloseWindow",
|
||||
"on-tile-right-click": "",
|
||||
"rules": [
|
||||
// .alacritty will be added to all windows with the App ID "Alacritty"
|
||||
// will be drawn in windows that match
|
||||
{ "app-id": "Alacritty", "class": "alacritty", "icon": "" },
|
||||
// .firefox will be added to all windows with the App ID "firefox"
|
||||
// subsequent rules are also checked and applied for firefox windows
|
||||
{ "app-id": "firefox", "class": "firefox", "continue": true },
|
||||
// .youtube-music will be added to all windows that have "YouTube Music" at the end of their title
|
||||
// will be drawn in windows that match
|
||||
{ "title": "YouTube Music$", "class": "youtube-music", "icon": "" }
|
||||
],
|
||||
|
||||
// ======= text mode options =======
|
||||
// customize the symbols used to draw the columns/windows
|
||||
"symbols": {
|
||||
"unfocused": "⋅",
|
||||
"focused": "⊙",
|
||||
"unfocused-floating": "∗",
|
||||
"focused-floating": "⊛",
|
||||
// text to display when there are no windows on the current workspace
|
||||
// if this is an empty string (default), the module will be hidden when there are no windows
|
||||
"empty": ""
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
// use niri IPC action names to trigger them (see https://yalter.github.io/niri/niri_ipc/enum.Action.html for available actions)
|
||||
// any action that has no fields is supported
|
||||
"on-scroll-up": "FocusColumnLeft",
|
||||
"on-scroll-down": "FocusColumnRight"
|
||||
// in graphical mode, don't configure click actions here—they're handled by the module above
|
||||
}
|
||||
}
|
||||
}
|
||||
+169
-245
@@ -1,22 +1,23 @@
|
||||
/* ABOUTME: System-wide Waybar styling for Moonarch. */
|
||||
/* ABOUTME: Uses GTK theme colors via @theme_* variables, Catppuccin Mocha palette. */
|
||||
|
||||
* {
|
||||
border: none;
|
||||
font-family: "Ubuntu Nerd Font", sans-serif;
|
||||
font-family: "UbuntuSans Nerd Font", sans-serif;
|
||||
font-size: 13px;
|
||||
color: alpha(@theme_text_color, 0.8);
|
||||
}
|
||||
|
||||
window#waybar {
|
||||
/*background: transparent;*/
|
||||
background: alpha(@theme_selected_fg_color, 0.2);
|
||||
}
|
||||
|
||||
/*-----main groups----*/
|
||||
.modules-right {
|
||||
/* --- Module areas --- */
|
||||
|
||||
.modules-left {
|
||||
margin: 0;
|
||||
padding-left: 5px;
|
||||
padding-right: 0;
|
||||
border-radius: 0;
|
||||
|
||||
}
|
||||
|
||||
.modules-center {
|
||||
@@ -24,106 +25,63 @@ window#waybar {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.modules-left {
|
||||
margin: 0 0 0 5px;
|
||||
/* background-color:alpha(@theme_selected_bg_color, 0.15);
|
||||
background: @theme_bg_color */
|
||||
.modules-right {
|
||||
margin: 0;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
border-radius: 0;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
/**
|
||||
## ALL MODULES
|
||||
**/
|
||||
#clock,
|
||||
#battery,
|
||||
#backlight,
|
||||
#cpu,
|
||||
#memory,
|
||||
#bluetooth,
|
||||
#temperature,
|
||||
#pulseaudio,
|
||||
#tray,
|
||||
#mode,
|
||||
#idle_inhibitor,
|
||||
#workspaces,
|
||||
#custom-power,
|
||||
#custom-menu,
|
||||
#custom-media,
|
||||
#custom-notification,
|
||||
#custom-updates,
|
||||
#custom-pacman,
|
||||
#user,
|
||||
#cava,
|
||||
#custom-gpu-usage,
|
||||
#custom-cpugov,
|
||||
/* --- Module boxes --- */
|
||||
|
||||
#power-profiles-daemon,
|
||||
#privacy,
|
||||
#gamemode,
|
||||
.modules-left>widget>*,
|
||||
.modules-center>widget>*,
|
||||
.modules-right>widget>* {
|
||||
padding: 0px 10px;
|
||||
border-radius: 4px;
|
||||
margin: 8px 2px;
|
||||
background-color: alpha(@theme_selected_bg_color, 0.05);
|
||||
}
|
||||
|
||||
#workspaces,
|
||||
#taskbar,
|
||||
#window {
|
||||
padding: 0px 10px;
|
||||
border-radius: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* GROUPS
|
||||
**/
|
||||
/* --- Group children --- */
|
||||
|
||||
widget widget>* {
|
||||
padding: 0 6px;
|
||||
margin: 0;
|
||||
background-color: transparent;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* --- Groups --- */
|
||||
|
||||
#sys,
|
||||
#stats,
|
||||
#net,
|
||||
#sound {
|
||||
margin: 0;
|
||||
#sound,
|
||||
#brightness,
|
||||
#indicators {
|
||||
padding: 0 4px;
|
||||
color: alpha(@theme_text_color, 0.5);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* SYS
|
||||
**/
|
||||
#sys {
|
||||
padding: 0 0px 0 10px;
|
||||
}
|
||||
|
||||
#custom-power {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
#user {
|
||||
padding-left: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#custom-weather {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
|
||||
#stats .drawer-child {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#tray {
|
||||
border-radius: 4px;
|
||||
padding: 0px 8px 0px 8px;
|
||||
margin: 8px 0px 8px 0px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#custom-notification {
|
||||
padding-right: 0
|
||||
}
|
||||
|
||||
/**
|
||||
* SOUND
|
||||
**/
|
||||
|
||||
#cava {
|
||||
background-color: transparent;
|
||||
padding-left: 0;
|
||||
}
|
||||
/* --- Sound --- */
|
||||
|
||||
#pulseaudio-slider {
|
||||
padding-left: 10px;
|
||||
@@ -138,7 +96,6 @@ window#waybar {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background-color: @insensitive_bg_color;
|
||||
border: 1px solid alpha(@theme_selected_bg_color, 0.1);
|
||||
}
|
||||
|
||||
#pulseaudio-slider trough {
|
||||
@@ -149,119 +106,64 @@ window#waybar {
|
||||
}
|
||||
|
||||
#pulseaudio-slider highlight {
|
||||
|
||||
min-width: 5px;
|
||||
border-radius: 5px;
|
||||
background-color: #6c7086;
|
||||
background-color: alpha(@theme_selected_bg_color, 0.5);
|
||||
}
|
||||
|
||||
#custom-updates {
|
||||
padding-right: 5px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MISC
|
||||
**/
|
||||
#mpris {
|
||||
border-radius: 0;
|
||||
margin: 8px 10px 8px 0px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
/**
|
||||
* WORKSPACES
|
||||
**/
|
||||
/*#workspaces {
|
||||
padding: 0;
|
||||
border-radius: 0
|
||||
}
|
||||
|
||||
#workspaces button {
|
||||
padding: 0px 10px 0px 8px;
|
||||
margin: 8px 2px 8px 0px;
|
||||
outline: none;
|
||||
border-radius: 4px;
|
||||
background-color: alpha(@theme_selected_bg_color, 0.05);
|
||||
}
|
||||
|
||||
#workspaces button:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
#workspaces button.empty,
|
||||
#workspaces button.active.empty {
|
||||
color: inherit;
|
||||
padding: 0px 0px 0px 8px;
|
||||
}
|
||||
|
||||
#workspaces button.active {
|
||||
background-color: transparent;
|
||||
padding: 0px 10px 0px 8px;
|
||||
}
|
||||
|
||||
#workspaces button.visible {
|
||||
background-color: alpha(@theme_selected_bg_color, 0.1);
|
||||
}
|
||||
|
||||
#workspaces button.urgent {
|
||||
color: #cc3436;
|
||||
}
|
||||
|
||||
#workspaces button:hover {
|
||||
background-color: alpha(@theme_selected_bg_color, 0.05);
|
||||
box-shadow: inherit;
|
||||
text-shadow: inherit;
|
||||
}*/
|
||||
|
||||
#workspaces {
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
|
||||
}
|
||||
|
||||
#workspaces button {
|
||||
margin: 8px 2px 8px 0px;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
border-radius: 4px;
|
||||
background-color: alpha(@theme_selected_bg_color, 0.05);
|
||||
}
|
||||
|
||||
#workspaces button:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
#workspaces button.empty,
|
||||
#workspaces button.active.empty {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#workspaces button.active {}
|
||||
|
||||
#workspaces button.visible {
|
||||
background-color: alpha(@theme_selected_bg_color, 0.1);
|
||||
}
|
||||
|
||||
#workspaces button.urgent {
|
||||
color: #cc3436;
|
||||
}
|
||||
|
||||
#workspaces button:hover {
|
||||
background-color: alpha(@theme_selected_bg_color, 0.05);
|
||||
box-shadow: inherit;
|
||||
text-shadow: inherit;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
## INDICATORS
|
||||
**/
|
||||
#pulseaudio.muted {
|
||||
color: #cc3436;
|
||||
}
|
||||
|
||||
/* --- Brightness --- */
|
||||
|
||||
#backlight-slider {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#backlight-slider slider {
|
||||
min-height: 0px;
|
||||
min-width: 0px;
|
||||
opacity: 0;
|
||||
background-image: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background-color: @insensitive_bg_color;
|
||||
}
|
||||
|
||||
#backlight-slider trough {
|
||||
min-height: 4px;
|
||||
min-width: 80px;
|
||||
border-radius: 5px;
|
||||
background-color: alpha(@theme_selected_bg_color, 0.3);
|
||||
}
|
||||
|
||||
#backlight-slider highlight {
|
||||
min-width: 5px;
|
||||
border-radius: 5px;
|
||||
background-color: alpha(@theme_selected_bg_color, 0.5);
|
||||
}
|
||||
|
||||
/* --- Battery group (hidden when empty, requires Waybar PR #4941) --- */
|
||||
|
||||
#bat.empty,
|
||||
#bat.empty * {
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
font-size: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* --- Battery --- */
|
||||
|
||||
#battery {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#battery.charging {
|
||||
color: #2dcc36;
|
||||
}
|
||||
@@ -274,6 +176,79 @@ window#waybar {
|
||||
color: #cc3436;
|
||||
}
|
||||
|
||||
#custom-batsaver {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* --- Workspaces --- */
|
||||
|
||||
#workspaces button {
|
||||
margin: 8px 2px;
|
||||
padding: 0 2px 0 0;
|
||||
outline: none;
|
||||
border-radius: 4px;
|
||||
background-color: alpha(@theme_selected_bg_color, 0.05);
|
||||
}
|
||||
|
||||
#workspaces button.empty,
|
||||
#workspaces button.active.empty {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#workspaces button.visible {
|
||||
background-color: alpha(@theme_selected_bg_color, 0.1);
|
||||
}
|
||||
|
||||
#workspaces button.urgent {
|
||||
color: #cc3436;
|
||||
}
|
||||
|
||||
#workspaces button:hover {
|
||||
background-color: alpha(@theme_selected_bg_color, 0.05);
|
||||
box-shadow: inherit;
|
||||
text-shadow: inherit;
|
||||
}
|
||||
|
||||
/* --- Taskbar --- */
|
||||
|
||||
#taskbar {
|
||||
font-size: 8px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#taskbar.empty {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#taskbar button {
|
||||
padding: 3px 4px 0 10px;
|
||||
border-bottom: 3px solid transparent;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#taskbar button.empty {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#taskbar button:hover {
|
||||
background-color: alpha(@theme_selected_bg_color, 0.1);
|
||||
}
|
||||
|
||||
#taskbar button.active {
|
||||
background-color: alpha(@theme_selected_bg_color, 0.1);
|
||||
border-bottom: 3px solid @theme_selected_bg_color;
|
||||
padding-bottom: 0px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* --- Misc --- */
|
||||
|
||||
#mpris {
|
||||
border-radius: 0;
|
||||
margin: 8px 10px 8px 0px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
#temperature.critical {
|
||||
color: #cc3436;
|
||||
}
|
||||
@@ -282,65 +257,14 @@ window#waybar {
|
||||
color: alpha(@theme_selected_bg_color, 0.9);
|
||||
}
|
||||
|
||||
#indicators {
|
||||
padding-right: 5px;
|
||||
padding-left: 5px;
|
||||
border-radius: 0;
|
||||
#custom-nightlight.on {
|
||||
color: #f9e2af;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TASKBAR
|
||||
**/
|
||||
#taskbar {
|
||||
font-size: 8px;
|
||||
margin: 0 0 0 5px;
|
||||
opacity: 1;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#taskbar.empty {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#taskbar button {
|
||||
padding: 3px 10px 3px 10px;
|
||||
transition: 100ms border ease-in-out;
|
||||
border-radius: 0;
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
#taskbar button:not(:first-child) {
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
#taskbar button:first-child {}
|
||||
|
||||
#taskbar button:last-child {}
|
||||
|
||||
#taskbar button:last-child:first-child {}
|
||||
|
||||
#taskbar button.empty {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#taskbar button:hover {
|
||||
|
||||
background-color: alpha(@theme_selected_bg_color, 0.1);
|
||||
}
|
||||
|
||||
#taskbar button.active {
|
||||
background-color: alpha(@theme_selected_bg_color, 0.05);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
menu {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
menuitem {
|
||||
border-radius: 4px;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ colloid-catppuccin-theme-git
|
||||
otf-openmoji
|
||||
|
||||
# Niri / Wayland Extras
|
||||
walker-bin
|
||||
elephant-desktopapplications-bin
|
||||
elephant-clipboard-bin
|
||||
elephant-bluetooth-bin
|
||||
@@ -29,6 +30,11 @@ wl-color-picker
|
||||
blueberry
|
||||
waterfox-bin
|
||||
|
||||
# Media
|
||||
mpv-modernz-git
|
||||
mpv-thumbfast-git
|
||||
|
||||
# System & Tools
|
||||
auto-cpufreq
|
||||
stasis
|
||||
wayland-pipewire-idle-inhibit
|
||||
|
||||
@@ -63,6 +63,7 @@ waybar
|
||||
swaync
|
||||
cliphist
|
||||
wl-clipboard
|
||||
wlsunset
|
||||
libnotify
|
||||
upower
|
||||
awww
|
||||
@@ -90,15 +91,9 @@ viewnior
|
||||
|
||||
# Development
|
||||
git
|
||||
glab
|
||||
go
|
||||
neovim
|
||||
npm
|
||||
rustup
|
||||
|
||||
# System
|
||||
docker
|
||||
docker-compose
|
||||
fwupd
|
||||
plocate
|
||||
rebuild-detector
|
||||
|
||||
+1
-7
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
# ABOUTME: Shared helper functions and constants for Moonarch scripts.
|
||||
# ABOUTME: Sourced by post-install.sh, update.sh and transform.sh.
|
||||
# ABOUTME: Sourced by post-install.sh.
|
||||
|
||||
# Path constants — BASH_SOURCE[1] resolves to the calling script, not lib.sh itself.
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[1]}")" && pwd)"
|
||||
@@ -8,7 +8,6 @@ PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
OFFICIAL_PACKAGES="$PROJECT_DIR/packages/official.txt"
|
||||
AUR_PACKAGES="$PROJECT_DIR/packages/aur.txt"
|
||||
DEFAULTS_DIR="$PROJECT_DIR/defaults"
|
||||
USER_DEFAULTS="/usr/share/moonarch/user-defaults"
|
||||
|
||||
# --- Helper functions ---
|
||||
|
||||
@@ -24,11 +23,6 @@ read_packages() {
|
||||
grep -v '^\s*#' "$1" | grep -v '^\s*$'
|
||||
}
|
||||
|
||||
confirm() {
|
||||
read -r -p "$1 [y/N] " response
|
||||
[[ "$response" =~ ^[yY]$ ]]
|
||||
}
|
||||
|
||||
# --- Prerequisite checks ---
|
||||
|
||||
check_not_root() {
|
||||
|
||||
Executable
+301
@@ -0,0 +1,301 @@
|
||||
#!/bin/bash
|
||||
# ABOUTME: Moonarch system health checker — verifies services, configs, packages and paths.
|
||||
# ABOUTME: Shipped as /usr/bin/moonarch-doctor (alias: moondoc) by the moonarch-git package.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
# --- Output helpers ---
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
WARN=0
|
||||
|
||||
pass() {
|
||||
echo -e " \e[1;32m✓\e[0m $*"
|
||||
((PASS++))
|
||||
}
|
||||
|
||||
fail() {
|
||||
echo -e " \e[1;31m✗\e[0m $*"
|
||||
((FAIL++))
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo -e " \e[1;33m⚠\e[0m $*"
|
||||
((WARN++))
|
||||
}
|
||||
|
||||
section() {
|
||||
echo
|
||||
echo -e "\e[1;34m[$1]\e[0m"
|
||||
}
|
||||
|
||||
# --- Check functions ---
|
||||
|
||||
check_system_service() {
|
||||
local svc="$1"
|
||||
local type
|
||||
type=$(systemctl show "$svc" --property=Type 2>/dev/null | cut -d= -f2)
|
||||
|
||||
if systemctl is-enabled "$svc" &>/dev/null; then
|
||||
if systemctl is-active "$svc" &>/dev/null; then
|
||||
pass "$svc (enabled, active)"
|
||||
elif [[ "$type" == "oneshot" ]]; then
|
||||
# Oneshot services are inactive after completion — check if they succeeded
|
||||
local result
|
||||
result=$(systemctl show "$svc" --property=Result 2>/dev/null | cut -d= -f2)
|
||||
if [[ "$result" == "success" ]]; then
|
||||
pass "$svc (enabled, oneshot completed)"
|
||||
else
|
||||
fail "$svc (enabled, oneshot failed: $result)"
|
||||
fi
|
||||
else
|
||||
fail "$svc (enabled, NOT active)"
|
||||
fi
|
||||
else
|
||||
fail "$svc (NOT enabled)"
|
||||
fi
|
||||
}
|
||||
|
||||
check_user_service() {
|
||||
local svc="$1"
|
||||
if systemctl --user is-enabled "$svc" &>/dev/null; then
|
||||
if systemctl --user is-active "$svc" &>/dev/null; then
|
||||
pass "$svc (enabled, active)"
|
||||
else
|
||||
# User services may be inactive if not in a graphical session
|
||||
warn "$svc (enabled, not active — may need graphical session)"
|
||||
fi
|
||||
else
|
||||
fail "$svc (NOT enabled)"
|
||||
fi
|
||||
}
|
||||
|
||||
check_config_match() {
|
||||
local deployed="$1"
|
||||
local source="$2"
|
||||
local label="${deployed}"
|
||||
|
||||
if [[ ! -f "$deployed" ]]; then
|
||||
fail "$label (missing)"
|
||||
return
|
||||
fi
|
||||
if [[ ! -f "$source" ]]; then
|
||||
warn "$label (exists, but no source to compare at $source)"
|
||||
return
|
||||
fi
|
||||
|
||||
local hash_deployed hash_source
|
||||
hash_deployed=$(sha256sum "$deployed" | cut -d' ' -f1)
|
||||
hash_source=$(sha256sum "$source" | cut -d' ' -f1)
|
||||
|
||||
if [[ "$hash_deployed" == "$hash_source" ]]; then
|
||||
pass "$label"
|
||||
else
|
||||
warn "$label (differs from moonarch default)"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- Header ---
|
||||
|
||||
echo -e "\e[1;34m"
|
||||
echo " Moonarch Doctor"
|
||||
echo -e "\e[0m"
|
||||
|
||||
# --- 1. Packages ---
|
||||
|
||||
section "Packages"
|
||||
|
||||
OFFICIAL="/usr/share/moonarch/official.txt"
|
||||
AUR="/usr/share/moonarch/aur.txt"
|
||||
|
||||
# Hoist INSTALLED so the AUR block below can use it even if OFFICIAL is absent —
|
||||
# otherwise `set -u` aborts the script when $INSTALLED is referenced unset.
|
||||
INSTALLED=$(pacman -Qq 2>/dev/null)
|
||||
|
||||
if [[ -f "$OFFICIAL" ]]; then
|
||||
MISSING_OFFICIAL=()
|
||||
while IFS= read -r pkg; do
|
||||
[[ "$pkg" =~ ^[[:space:]]*# ]] && continue
|
||||
[[ -z "${pkg// }" ]] && continue
|
||||
if ! echo "$INSTALLED" | grep -qx "$pkg"; then
|
||||
MISSING_OFFICIAL+=("$pkg")
|
||||
fi
|
||||
done < "$OFFICIAL"
|
||||
|
||||
if [[ ${#MISSING_OFFICIAL[@]} -eq 0 ]]; then
|
||||
pass "All official packages installed"
|
||||
else
|
||||
fail "Missing official packages: ${MISSING_OFFICIAL[*]}"
|
||||
fi
|
||||
else
|
||||
warn "$OFFICIAL not found (moonarch-git not installed?)"
|
||||
fi
|
||||
|
||||
if [[ -f "$AUR" ]]; then
|
||||
MISSING_AUR=()
|
||||
while IFS= read -r pkg; do
|
||||
[[ "$pkg" =~ ^[[:space:]]*# ]] && continue
|
||||
[[ -z "${pkg// }" ]] && continue
|
||||
if ! echo "$INSTALLED" | grep -qx "$pkg"; then
|
||||
MISSING_AUR+=("$pkg")
|
||||
fi
|
||||
done < "$AUR"
|
||||
|
||||
if [[ ${#MISSING_AUR[@]} -eq 0 ]]; then
|
||||
pass "All AUR packages installed"
|
||||
else
|
||||
fail "Missing AUR packages: ${MISSING_AUR[*]}"
|
||||
fi
|
||||
else
|
||||
warn "$AUR not found (moonarch-git not installed?)"
|
||||
fi
|
||||
|
||||
ORPHANS=$(pacman -Qdtq 2>/dev/null || true)
|
||||
if [[ -z "$ORPHANS" ]]; then
|
||||
pass "No orphaned packages"
|
||||
else
|
||||
ORPHAN_COUNT=$(echo "$ORPHANS" | wc -l)
|
||||
warn "$ORPHAN_COUNT orphaned package(s) (run moonup to clean)"
|
||||
fi
|
||||
|
||||
# --- 2. System Services ---
|
||||
|
||||
section "System Services"
|
||||
|
||||
for svc in NetworkManager bluetooth greetd systemd-timesyncd ufw auto-cpufreq; do
|
||||
check_system_service "$svc"
|
||||
done
|
||||
|
||||
# Battery conservation service (laptop only)
|
||||
if [[ -f /sys/class/power_supply/BAT0/charge_control_end_threshold ]]; then
|
||||
check_system_service moonarch-batsaver
|
||||
else
|
||||
pass "moonarch-batsaver (skipped — no battery threshold support)"
|
||||
fi
|
||||
|
||||
# --- 3. User Services ---
|
||||
|
||||
section "User Services"
|
||||
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
warn "Running as root — skipping user service checks"
|
||||
elif pacman -Qq moonarch-git &>/dev/null; then
|
||||
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"
|
||||
done
|
||||
else
|
||||
warn "moonarch-git not installed — skipping user service checks"
|
||||
fi
|
||||
|
||||
# --- 4. Config Files ---
|
||||
|
||||
section "Config Files"
|
||||
|
||||
SRC="/usr/share/moonarch"
|
||||
|
||||
check_config_match "/etc/xdg/foot/foot.ini" "$SRC/foot/foot.ini"
|
||||
check_config_match "/etc/xdg/waybar/style.css" "$SRC/waybar/style.css"
|
||||
check_config_match "/etc/xdg/swaync/config.json" "$SRC/swaync/config.json"
|
||||
check_config_match "/etc/xdg/swaync/style.css" "$SRC/swaync/style.css"
|
||||
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/moongreet/moongreet.toml" "$SRC/moongreet/moongreet.toml"
|
||||
|
||||
# --- 5. Helper Scripts ---
|
||||
|
||||
section "Helper Scripts"
|
||||
|
||||
if pacman -Qq moonarch-git &>/dev/null; then
|
||||
EXPECTED_SCRIPTS=()
|
||||
while IFS= read -r script; do
|
||||
EXPECTED_SCRIPTS+=("$(basename "$script")")
|
||||
done < <(pacman -Qql moonarch-git | grep -E '^/usr/bin/moonarch-[^/]+$')
|
||||
|
||||
MISSING_SCRIPTS=()
|
||||
for script in "${EXPECTED_SCRIPTS[@]}"; do
|
||||
if [[ ! -x "/usr/bin/$script" ]]; then
|
||||
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
|
||||
else
|
||||
warn "moonarch-git not installed — skipping helper script checks"
|
||||
fi
|
||||
|
||||
# Symlinks
|
||||
for link in moonup moondoc; do
|
||||
if [[ -L "/usr/bin/$link" ]]; then
|
||||
pass "$link symlink"
|
||||
else
|
||||
fail "$link symlink (missing)"
|
||||
fi
|
||||
done
|
||||
|
||||
# --- 6. System Config ---
|
||||
|
||||
section "System Config"
|
||||
|
||||
# UFW (check via systemd, no sudo needed)
|
||||
if command -v ufw &>/dev/null; then
|
||||
if systemctl is-active ufw &>/dev/null; then
|
||||
pass "UFW active"
|
||||
else
|
||||
fail "UFW not active"
|
||||
fi
|
||||
else
|
||||
fail "UFW not installed"
|
||||
fi
|
||||
|
||||
# Pacman moonarch repo
|
||||
if grep -q '^\[moonarch\]' /etc/pacman.conf 2>/dev/null; then
|
||||
pass "Pacman [moonarch] repo configured"
|
||||
else
|
||||
fail "Pacman [moonarch] repo missing from /etc/pacman.conf"
|
||||
fi
|
||||
|
||||
# Default shell
|
||||
USER_SHELL=$(getent passwd "$USER" | cut -d: -f7)
|
||||
if [[ "$USER_SHELL" == */zsh ]]; then
|
||||
pass "Default shell: zsh"
|
||||
else
|
||||
warn "Default shell: $USER_SHELL (expected zsh)"
|
||||
fi
|
||||
|
||||
# --- 7. Directories ---
|
||||
|
||||
section "Directories"
|
||||
|
||||
if [[ -d /var/lib/moonarch ]]; then
|
||||
DIR_PERMS=$(stat -c '%a' /var/lib/moonarch)
|
||||
DIR_GROUP=$(stat -c '%G' /var/lib/moonarch)
|
||||
if [[ "$DIR_GROUP" == "wheel" ]]; then
|
||||
pass "/var/lib/moonarch/ (group: wheel, mode: $DIR_PERMS)"
|
||||
else
|
||||
warn "/var/lib/moonarch/ (group: $DIR_GROUP, expected wheel)"
|
||||
fi
|
||||
else
|
||||
warn "/var/lib/moonarch/ missing (created on first battery toggle)"
|
||||
fi
|
||||
|
||||
# --- Summary ---
|
||||
|
||||
echo
|
||||
TOTAL=$((PASS + FAIL + WARN))
|
||||
echo -e "\e[1;34m[Summary]\e[0m $TOTAL checks: \e[1;32m$PASS passed\e[0m, \e[1;31m$FAIL failed\e[0m, \e[1;33m$WARN warnings\e[0m"
|
||||
|
||||
if [[ $FAIL -gt 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
+55
-22
@@ -4,6 +4,37 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
_t() {
|
||||
# _t "english" "deutsch" — picks by $LANG
|
||||
case "${LANG%%.*}" in
|
||||
de_*) printf '%s' "$2" ;;
|
||||
*) printf '%s' "$1" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Pause on exit when launched from a GUI (Waybar sets MOONUP_WAIT=1).
|
||||
# Runs via trap so it fires even when `set -e` aborts the script mid-way.
|
||||
# Reads from /dev/tty because stdin may have been drained/closed by pacman/paru.
|
||||
_pause_on_exit() {
|
||||
local rc=$?
|
||||
if [[ -n "${MOONUP_WAIT:-}" ]]; then
|
||||
echo
|
||||
if (( rc != 0 )); then
|
||||
echo -e "\e[1;31m[Moonarch ERROR]\e[0m $(_t "Exited with error (code $rc)" "Mit Fehler beendet (Code $rc)")" >&2
|
||||
fi
|
||||
read -n 1 -s -r -p "$(_t "Press any key to close..." "Beliebige Taste drücken zum Schließen …")" < /dev/tty || true
|
||||
echo
|
||||
fi
|
||||
}
|
||||
trap _pause_on_exit EXIT
|
||||
|
||||
# --- i18n ---
|
||||
# Reuse pacman's gettext catalog for prompts that match upstream msgids.
|
||||
# For moonarch-specific strings fall back to _t() inline translation.
|
||||
export TEXTDOMAIN=pacman
|
||||
export TEXTDOMAINDIR=/usr/share/locale
|
||||
YN=$(gettext "[Y/n]")
|
||||
|
||||
# --- Helper functions ---
|
||||
|
||||
log() {
|
||||
@@ -19,8 +50,10 @@ read_packages() {
|
||||
}
|
||||
|
||||
confirm() {
|
||||
read -r -p "$1 [y/N] " response
|
||||
[[ "$response" =~ ^[yY]$ ]]
|
||||
# Read from /dev/tty so stdin state (drained by pacman/paru, closed by
|
||||
# a Waybar launch) doesn't auto-confirm prompts by returning empty input.
|
||||
read -r -p ":: $1 $YN " response < /dev/tty
|
||||
[[ -z "$response" || "$response" =~ ^[yYjJ]$ ]]
|
||||
}
|
||||
|
||||
OFFICIAL_PACKAGES="/usr/share/moonarch/official.txt"
|
||||
@@ -29,79 +62,79 @@ AUR_PACKAGES="/usr/share/moonarch/aur.txt"
|
||||
# --- Prerequisites ---
|
||||
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
err "Do NOT run as root. The script uses sudo where needed."
|
||||
err "$(_t "Do NOT run as root. The script uses sudo where needed." "Nicht als root ausführen. Das Script verwendet sudo wo nötig.")"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- 1. Update system packages ---
|
||||
|
||||
log "=== Update system packages ==="
|
||||
log "=== $(gettext $'Starting full system upgrade...\n' | tr -d '\n.') ==="
|
||||
|
||||
if confirm "Run pacman -Syu?"; then
|
||||
if confirm "$(_t "Run pacman -Syu?" "pacman -Syu ausführen?")"; then
|
||||
sudo pacman -Syu
|
||||
else
|
||||
log "System update skipped."
|
||||
log "$(_t "System update skipped." "Systemaktualisierung übersprungen.")"
|
||||
fi
|
||||
|
||||
if command -v paru &>/dev/null; then
|
||||
if confirm "Update AUR packages (paru -Sua)?"; then
|
||||
if confirm "$(_t "Update AUR packages (paru -Sua)?" "AUR-Pakete aktualisieren (paru -Sua)?")"; then
|
||||
paru -Sua
|
||||
else
|
||||
log "AUR update skipped."
|
||||
log "$(_t "AUR update skipped." "AUR-Aktualisierung übersprungen.")"
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- 2. Reconcile package lists ---
|
||||
|
||||
log "=== Reconcile package lists ==="
|
||||
log "=== $(_t "Reconcile package lists" "Paketlisten abgleichen") ==="
|
||||
|
||||
if [[ -f "$OFFICIAL_PACKAGES" ]]; then
|
||||
MISSING_OFFICIAL=$(comm -23 <(read_packages "$OFFICIAL_PACKAGES" | sort) <(pacman -Qqe | sort) || true)
|
||||
MISSING_OFFICIAL=$(comm -23 <(read_packages "$OFFICIAL_PACKAGES" | sort) <(pacman -Qq | sort) || true)
|
||||
if [[ -n "$MISSING_OFFICIAL" ]]; then
|
||||
log "Missing official packages:"
|
||||
log "$(_t "Missing official packages:" "Fehlende offizielle Pakete:")"
|
||||
echo "$MISSING_OFFICIAL"
|
||||
if confirm "Install?"; then
|
||||
if confirm "$(gettext 'Proceed with installation?')"; then
|
||||
# shellcheck disable=SC2086
|
||||
sudo pacman -S --needed --noconfirm $MISSING_OFFICIAL
|
||||
fi
|
||||
else
|
||||
log "All official packages installed."
|
||||
log "$(_t "All official packages installed." "Alle offiziellen Pakete installiert.")"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -f "$AUR_PACKAGES" ]] && command -v paru &>/dev/null; then
|
||||
MISSING_AUR=$(comm -23 <(read_packages "$AUR_PACKAGES" | sort) <(pacman -Qqe | sort) || true)
|
||||
MISSING_AUR=$(comm -23 <(read_packages "$AUR_PACKAGES" | sort) <(pacman -Qq | sort) || true)
|
||||
if [[ -n "$MISSING_AUR" ]]; then
|
||||
log "Missing AUR packages:"
|
||||
log "$(_t "Missing AUR packages:" "Fehlende AUR-Pakete:")"
|
||||
echo "$MISSING_AUR"
|
||||
if confirm "Install?"; then
|
||||
if confirm "$(gettext 'Proceed with installation?')"; then
|
||||
# shellcheck disable=SC2086
|
||||
paru -S --needed --noconfirm $MISSING_AUR
|
||||
fi
|
||||
else
|
||||
log "All AUR packages installed."
|
||||
log "$(_t "All AUR packages installed." "Alle AUR-Pakete installiert.")"
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- 3. Orphaned packages ---
|
||||
|
||||
log "=== Orphaned packages ==="
|
||||
log "=== $(_t "Orphaned packages" "Verwaiste Pakete") ==="
|
||||
|
||||
ORPHANS=$(pacman -Qdtq 2>/dev/null || true)
|
||||
if [[ -n "$ORPHANS" ]]; then
|
||||
log "Orphaned packages found:"
|
||||
log "$(_t "Orphaned packages found:" "Verwaiste Pakete gefunden:")"
|
||||
echo "$ORPHANS"
|
||||
if confirm "Remove?"; then
|
||||
if confirm "$(gettext 'Do you want to remove these packages?')"; then
|
||||
# shellcheck disable=SC2086
|
||||
sudo pacman -Rsn --noconfirm $ORPHANS
|
||||
fi
|
||||
else
|
||||
log "No orphaned packages."
|
||||
log "$(_t "No orphaned packages." "Keine verwaisten Pakete.")"
|
||||
fi
|
||||
|
||||
# --- Done ---
|
||||
|
||||
log ""
|
||||
log "============================================"
|
||||
log " Moonarch update complete!"
|
||||
log " $(_t "Moonarch update complete!" "Moonarch-Aktualisierung abgeschlossen!")"
|
||||
log "============================================"
|
||||
|
||||
+41
-62
@@ -14,8 +14,16 @@ check_pacman
|
||||
# --- Install paru (AUR Helper) ---
|
||||
|
||||
if ! command -v paru &>/dev/null; then
|
||||
log "Installing paru build dependencies..."
|
||||
sudo pacman -S --needed --noconfirm base-devel rust
|
||||
|
||||
log "Installing paru..."
|
||||
sudo pacman -S --needed --noconfirm paru
|
||||
PARU_TMPDIR=$(mktemp -d)
|
||||
trap 'rm -rf "$PARU_TMPDIR"' EXIT
|
||||
git clone https://aur.archlinux.org/paru.git "$PARU_TMPDIR/paru"
|
||||
(cd "$PARU_TMPDIR/paru" && makepkg -si --noconfirm)
|
||||
rm -rf "$PARU_TMPDIR"
|
||||
trap - EXIT
|
||||
else
|
||||
log "paru already installed."
|
||||
fi
|
||||
@@ -60,31 +68,23 @@ fi
|
||||
rm -f "$KEY_FILE"
|
||||
trap - EXIT
|
||||
|
||||
# --- Set up Moonarch custom paru repo (needed for first install bootstrap) ---
|
||||
|
||||
log "Setting up Moonarch paru repo..."
|
||||
PARU_CONF="/etc/paru.conf"
|
||||
if ! grep -q 'Mode.*p' "$PARU_CONF" 2>/dev/null; then
|
||||
sudo sed -i '/^\[options\]/a Mode = arp' "$PARU_CONF"
|
||||
log " + Mode = arp set in paru.conf."
|
||||
else
|
||||
log " ~ Mode already includes pkgbuilds."
|
||||
fi
|
||||
if ! grep -q '\[moonarch-pkgbuilds\]' "$PARU_CONF" 2>/dev/null; then
|
||||
printf '\n[moonarch-pkgbuilds]\nUrl = https://gitea.moonarch.de/nevaforget/moonarch-pkgbuilds.git\n' \
|
||||
| sudo tee -a "$PARU_CONF" > /dev/null
|
||||
log " + Moonarch repo added to paru.conf."
|
||||
else
|
||||
log " ~ Moonarch repo already in paru.conf."
|
||||
fi
|
||||
|
||||
paru -Syu --pkgbuilds --noconfirm
|
||||
|
||||
# --- Install moonarch-git (subsequent updates handled by moonarch.install hook) ---
|
||||
# --- Install moonarch-git from the Arch registry ---
|
||||
|
||||
log "Installing moonarch-git package..."
|
||||
sudo pacman -Sy --noconfirm
|
||||
paru -S --needed --noconfirm moonarch-git
|
||||
|
||||
# --- Install AUR extras (cannot be hard depends of moonarch-git) ---
|
||||
|
||||
if [[ -f "$AUR_PACKAGES" ]]; then
|
||||
log "Installing AUR packages from $AUR_PACKAGES..."
|
||||
# shellcheck disable=SC2046
|
||||
paru -S --needed --noconfirm $(read_packages "$AUR_PACKAGES")
|
||||
else
|
||||
err "AUR package list not found: $AUR_PACKAGES"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- User-level GTK4 symlinks for libadwaita apps ---
|
||||
|
||||
THEME_NAME="Colloid-Grey-Dark-Catppuccin"
|
||||
@@ -110,39 +110,22 @@ gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark'
|
||||
gsettings set org.gnome.desktop.interface icon-theme 'Colloid-Grey-Catppuccin-Dark'
|
||||
gsettings set org.gnome.desktop.interface font-name 'UbuntuSans Nerd Font 11'
|
||||
|
||||
# --- Install user config defaults ---
|
||||
|
||||
log "Installing user config defaults to ~/.config/..."
|
||||
if [[ -d "$USER_DEFAULTS" ]]; then
|
||||
# Each subdirectory in user-defaults/ corresponds to a ~/.config/ directory.
|
||||
# Files are only copied if they don't exist yet (no overwriting).
|
||||
for src_dir in "$USER_DEFAULTS"/*/; do
|
||||
app_name="$(basename "$src_dir")"
|
||||
dest_dir="$HOME/.config/$app_name"
|
||||
mkdir -p "$dest_dir"
|
||||
find "$src_dir" -type f -print0 | while IFS= read -r -d '' src_file; do
|
||||
rel_path="${src_file#"$src_dir"}"
|
||||
dest_file="$dest_dir/$rel_path"
|
||||
if [[ ! -f "$dest_file" ]]; then
|
||||
mkdir -p "$(dirname "$dest_file")"
|
||||
cp "$src_file" "$dest_file"
|
||||
log " + $app_name/$rel_path"
|
||||
else
|
||||
log " ~ $app_name/$rel_path already exists, skipped."
|
||||
fi
|
||||
done
|
||||
done
|
||||
else
|
||||
err "USER_DEFAULTS not found: $USER_DEFAULTS — skipping user config defaults."
|
||||
fi
|
||||
|
||||
# --- Zsh user config ---
|
||||
|
||||
if [[ ! -f "$HOME/.zshrc" ]]; then
|
||||
log "No ~/.zshrc found — sourcing Moonarch defaults."
|
||||
mkdir -p "$HOME/.zshrc.d"
|
||||
echo "# Load Moonarch defaults, add custom overrides in ~/.zshrc.d/ or below" > "$HOME/.zshrc"
|
||||
echo "source /etc/zsh/zshrc.moonarch" >> "$HOME/.zshrc"
|
||||
echo "source /etc/zsh/zshrc.moonarch" > "$HOME/.zshrc"
|
||||
fi
|
||||
|
||||
# --- Seed Stasis user config ---
|
||||
#
|
||||
# Stasis reads ~/.config/stasis/stasis.rune (or /etc/stasis/stasis.rune as
|
||||
# fallback) but never /etc/xdg/. Without a user config it writes its own
|
||||
# upstream default on first start. Seed Moonarch's template so the bootstrap
|
||||
# sees an existing file and skips. Never overwrite an existing user config.
|
||||
if [[ ! -f "$HOME/.config/stasis/stasis.rune" && -f /etc/xdg/stasis/stasis.rune ]]; then
|
||||
log "Seeding Moonarch stasis config to user home."
|
||||
install -Dm644 /etc/xdg/stasis/stasis.rune "$HOME/.config/stasis/stasis.rune"
|
||||
fi
|
||||
|
||||
# --- Enable systemd user services ---
|
||||
@@ -151,8 +134,13 @@ log "Enabling systemd user services..."
|
||||
USER_SERVICES=(
|
||||
"kanshi"
|
||||
"stasis"
|
||||
"cliphist"
|
||||
"wayland-pipewire-idle-inhibit"
|
||||
"cliphist-text"
|
||||
"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
|
||||
if systemctl --user cat "${service}.service" &>/dev/null; then
|
||||
@@ -169,7 +157,6 @@ log "Enabling services..."
|
||||
SERVICES=(
|
||||
"NetworkManager"
|
||||
"bluetooth"
|
||||
"docker"
|
||||
"greetd"
|
||||
"systemd-timesyncd"
|
||||
"ufw"
|
||||
@@ -209,13 +196,6 @@ for entry in /boot/loader/entries/*.conf; do
|
||||
fi
|
||||
done
|
||||
|
||||
# --- Docker-Gruppe ---
|
||||
|
||||
if ! groups | grep -q docker; then
|
||||
log "Adding user to docker group..."
|
||||
sudo usermod -aG docker "$USER"
|
||||
fi
|
||||
|
||||
# --- Screenshots directory ---
|
||||
|
||||
mkdir -p "$HOME/Pictures/Screenshots"
|
||||
@@ -231,6 +211,5 @@ log ""
|
||||
log "Next steps:"
|
||||
log " 1. Reboot"
|
||||
log " 2. Place wallpapers in ~/Pictures/Wallpaper/"
|
||||
log " 3. rustup default stable"
|
||||
log " 4. User overrides in ~/.config/ or ~/.zshrc.d/"
|
||||
log " 3. User overrides in ~/.config/"
|
||||
log ""
|
||||
|
||||
@@ -1,427 +0,0 @@
|
||||
#!/bin/bash
|
||||
# ABOUTME: Transforms an existing Arch+Wayland system into a Moonarch system.
|
||||
# ABOUTME: Backs up configs, installs moonarch-git package, deploys user configs with hard overwrite.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib.sh"
|
||||
|
||||
# --- Parse arguments ---
|
||||
|
||||
DRY_RUN=false
|
||||
if [[ "${1:-}" == "--dry-run" ]]; then
|
||||
DRY_RUN=true
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# Phase 1: Prerequisites & Detection
|
||||
# ============================================================
|
||||
|
||||
check_not_root
|
||||
check_pacman
|
||||
|
||||
# Require active Wayland session
|
||||
if [[ "${XDG_SESSION_TYPE:-}" != "wayland" ]] && [[ -z "${WAYLAND_DISPLAY:-}" ]]; then
|
||||
err "No active Wayland session detected. Transform requires Wayland."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Detect conflicting display managers
|
||||
CONFLICTING_DMS=()
|
||||
for dm in sddm gdm lightdm ly lemurs; do
|
||||
if systemctl is-enabled "${dm}.service" &>/dev/null; then
|
||||
CONFLICTING_DMS+=("$dm")
|
||||
fi
|
||||
done
|
||||
|
||||
# Detect conflicting packages
|
||||
CONFLICTING_PKGS=()
|
||||
if pacman -Qq pulseaudio &>/dev/null && ! pacman -Qq pipewire-pulse &>/dev/null; then
|
||||
CONFLICTING_PKGS+=("pulseaudio")
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# Phase 2: Pre-flight Summary
|
||||
# ============================================================
|
||||
|
||||
echo ""
|
||||
if $DRY_RUN; then
|
||||
log "============================================"
|
||||
log " Moonarch Transform — Dry Run"
|
||||
log "============================================"
|
||||
else
|
||||
log "============================================"
|
||||
log " Moonarch Transform — Pre-flight Summary"
|
||||
log "============================================"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
log "Wayland session: detected"
|
||||
|
||||
if [[ ${#CONFLICTING_DMS[@]} -gt 0 ]]; then
|
||||
log "Display managers: ${CONFLICTING_DMS[*]} (will be disabled)"
|
||||
else
|
||||
log "Display managers: none conflicting"
|
||||
fi
|
||||
|
||||
if [[ ${#CONFLICTING_PKGS[@]} -gt 0 ]]; then
|
||||
log "Package conflicts: ${CONFLICTING_PKGS[*]} (will be removed)"
|
||||
else
|
||||
log "Package conflicts: none"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log "Actions:"
|
||||
log " 1. (Optional) Backup ~/.config/, ~/.zshrc, /etc/xdg/"
|
||||
log " 2. Install moonarch-git package (pulls in all dependencies)"
|
||||
log " 3. Disable conflicting DMs, enable greetd"
|
||||
log " 4. Overwrite ALL user configs (~/.config/)"
|
||||
log " 5. Configure GTK themes, firewall, services"
|
||||
echo ""
|
||||
|
||||
# Show package diff
|
||||
MISSING_OFFICIAL=$(comm -23 <(read_packages "$OFFICIAL_PACKAGES" | sort) <(pacman -Qqe | sort) 2>/dev/null || true)
|
||||
MISSING_AUR=""
|
||||
if [[ -f "$AUR_PACKAGES" ]]; then
|
||||
MISSING_AUR=$(comm -23 <(read_packages "$AUR_PACKAGES" | sort) <(pacman -Qqe | sort) 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
if [[ -n "$MISSING_OFFICIAL" ]] || [[ -n "$MISSING_AUR" ]]; then
|
||||
log "Packages to install:"
|
||||
if [[ -n "$MISSING_OFFICIAL" ]]; then
|
||||
OFFICIAL_COUNT=$(echo "$MISSING_OFFICIAL" | wc -l)
|
||||
log " Official ($OFFICIAL_COUNT):"
|
||||
echo "$MISSING_OFFICIAL" | sed 's/^/ /'
|
||||
fi
|
||||
if [[ -n "$MISSING_AUR" ]]; then
|
||||
AUR_COUNT=$(echo "$MISSING_AUR" | wc -l)
|
||||
log " AUR ($AUR_COUNT):"
|
||||
echo "$MISSING_AUR" | sed 's/^/ /'
|
||||
fi
|
||||
else
|
||||
log "Packages: all already installed"
|
||||
fi
|
||||
|
||||
# Show config diff summary
|
||||
CHANGED_XDG=0
|
||||
CHANGED_BIN=0
|
||||
for src_dir in "$DEFAULTS_DIR/xdg/"*/; do
|
||||
app_name="$(basename "$src_dir")"
|
||||
dest_dir="$HOME/.config/$app_name"
|
||||
if [[ -d "$dest_dir" ]]; then
|
||||
if ! diff -rq "$src_dir" "$dest_dir" &>/dev/null 2>&1; then
|
||||
((CHANGED_XDG++)) || true
|
||||
fi
|
||||
else
|
||||
((CHANGED_XDG++)) || true
|
||||
fi
|
||||
done
|
||||
for bin in "$DEFAULTS_DIR/bin/moonarch-"*; do
|
||||
name=$(basename "$bin")
|
||||
if ! cmp -s "$bin" "/usr/bin/$name" 2>/dev/null; then
|
||||
((CHANGED_BIN++)) || true
|
||||
fi
|
||||
done
|
||||
log "Config changes: $CHANGED_XDG XDG app(s), $CHANGED_BIN helper script(s)"
|
||||
|
||||
echo ""
|
||||
err "This will REPLACE your current desktop configuration."
|
||||
echo ""
|
||||
|
||||
if $DRY_RUN; then
|
||||
log "Dry run complete — no changes were made."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! confirm "Proceed?"; then
|
||||
log "Transform cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# Phase 3: Backup (optional)
|
||||
# ============================================================
|
||||
|
||||
BACKUP_FILE=""
|
||||
SYSTEM_BACKUP=""
|
||||
|
||||
if confirm "Create backup of current configs before overwriting?"; then
|
||||
BACKUP_FILE="$HOME/moonarch-backup-$(date +%Y%m%d-%H%M%S).tar.gz"
|
||||
log "Creating backup: $BACKUP_FILE"
|
||||
|
||||
# Build list of paths that actually exist
|
||||
BACKUP_PATHS=()
|
||||
[[ -d "$HOME/.config" ]] && BACKUP_PATHS+=(".config")
|
||||
[[ -f "$HOME/.zshrc" ]] && BACKUP_PATHS+=(".zshrc")
|
||||
[[ -d "$HOME/.zshrc.d" ]] && BACKUP_PATHS+=(".zshrc.d")
|
||||
|
||||
if [[ ${#BACKUP_PATHS[@]} -gt 0 ]]; then
|
||||
tar czf "$BACKUP_FILE" -C "$HOME" "${BACKUP_PATHS[@]}"
|
||||
log " + User configs backed up."
|
||||
fi
|
||||
|
||||
# Backup system XDG separately (needs sudo)
|
||||
if [[ -d /etc/xdg ]]; then
|
||||
SYSTEM_BACKUP="$HOME/moonarch-backup-system-$(date +%Y%m%d-%H%M%S).tar.gz"
|
||||
sudo tar czf "$SYSTEM_BACKUP" -C / etc/xdg
|
||||
sudo chown "$USER:$USER" "$SYSTEM_BACKUP"
|
||||
log " + System configs backed up: $SYSTEM_BACKUP"
|
||||
fi
|
||||
|
||||
log "Backup complete: $(du -h "$BACKUP_FILE" | cut -f1)"
|
||||
else
|
||||
log "Skipping backup."
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# Phase 4: Disable Conflicting Display Managers
|
||||
# ============================================================
|
||||
|
||||
if [[ ${#CONFLICTING_DMS[@]} -gt 0 ]]; then
|
||||
log "Disabling conflicting display managers..."
|
||||
for dm in "${CONFLICTING_DMS[@]}"; do
|
||||
sudo systemctl disable "$dm"
|
||||
log " - $dm disabled"
|
||||
done
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# Phase 5: Remove Conflicting Packages
|
||||
# ============================================================
|
||||
|
||||
if pacman -Qq pulseaudio &>/dev/null; then
|
||||
log "Removing PulseAudio (replaced by PipeWire)..."
|
||||
sudo pacman -Rdd --noconfirm pulseaudio pulseaudio-alsa pulseaudio-bluetooth 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# Phase 6: Install moonarch-git Package
|
||||
# ============================================================
|
||||
|
||||
# Install paru if not present
|
||||
if ! command -v paru &>/dev/null; then
|
||||
log "Installing paru..."
|
||||
sudo pacman -S --needed --noconfirm paru
|
||||
else
|
||||
log "paru already installed."
|
||||
fi
|
||||
|
||||
# Moonarch package registry
|
||||
log "Setting up Moonarch package registry..."
|
||||
if ! grep -q '\[moonarch\]' /etc/pacman.conf 2>/dev/null; then
|
||||
sudo tee -a /etc/pacman.conf > /dev/null <<'EOCONF'
|
||||
|
||||
[moonarch]
|
||||
SigLevel = Required DatabaseOptional
|
||||
Server = https://gitea.moonarch.de/api/packages/nevaforget/arch/$repo/$arch
|
||||
EOCONF
|
||||
log " + Moonarch repo added to pacman.conf."
|
||||
else
|
||||
log " ~ Moonarch repo already in pacman.conf."
|
||||
fi
|
||||
|
||||
log "Importing Moonarch registry signing key..."
|
||||
EXPECTED_FINGERPRINT="9B02C596A4652C40CA768E75B90C8B82EA30A131"
|
||||
KEY_FILE=$(mktemp)
|
||||
trap 'rm -f "$KEY_FILE"' EXIT
|
||||
curl -sf https://gitea.moonarch.de/api/packages/nevaforget/arch/repository.key -o "$KEY_FILE"
|
||||
if [[ ! -s "$KEY_FILE" ]]; then
|
||||
err "Failed to download registry key (empty response)."
|
||||
exit 1
|
||||
fi
|
||||
KEY_FPR=$(gpg --show-keys --with-colons "$KEY_FILE" 2>/dev/null | awk -F: '/^fpr/{print $10; exit}')
|
||||
if [[ "$KEY_FPR" != "$EXPECTED_FINGERPRINT" ]]; then
|
||||
err "Registry key fingerprint mismatch! Expected $EXPECTED_FINGERPRINT, got ${KEY_FPR:-<empty>}"
|
||||
exit 1
|
||||
fi
|
||||
KEY_ID=$(gpg --show-keys --with-colons "$KEY_FILE" 2>/dev/null | awk -F: '/^pub/{print $5}')
|
||||
if [[ -n "$KEY_ID" ]] && ! sudo pacman-key --list-keys "$KEY_ID" &>/dev/null; then
|
||||
sudo pacman-key --add "$KEY_FILE"
|
||||
sudo pacman-key --lsign-key "$KEY_ID"
|
||||
log " + Registry key $KEY_ID imported and locally signed."
|
||||
else
|
||||
log " ~ Registry key already imported."
|
||||
fi
|
||||
rm -f "$KEY_FILE"
|
||||
trap - EXIT
|
||||
|
||||
# Install/update moonarch-git (paru repo config is set up by moonarch.install hook)
|
||||
paru -Syu --pkgbuilds --noconfirm
|
||||
|
||||
log "Installing moonarch-git package..."
|
||||
paru -S --needed --noconfirm moonarch-git
|
||||
|
||||
# Install packages from package lists
|
||||
if [[ -n "$MISSING_OFFICIAL" ]]; then
|
||||
log "Installing official packages..."
|
||||
echo "$MISSING_OFFICIAL" | paru -S --needed --noconfirm -
|
||||
fi
|
||||
|
||||
if [[ -n "$MISSING_AUR" ]]; then
|
||||
log "Installing AUR packages..."
|
||||
echo "$MISSING_AUR" | paru -S --needed --noconfirm -
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# Phase 7: User-Level Configuration
|
||||
# ============================================================
|
||||
|
||||
# User-level GTK4 symlinks for libadwaita apps
|
||||
THEME_NAME="Colloid-Grey-Dark-Catppuccin"
|
||||
THEME_GTK4="/usr/share/themes/$THEME_NAME/gtk-4.0"
|
||||
|
||||
if [[ -d "$THEME_GTK4" ]]; then
|
||||
log "Creating user-level GTK4 symlinks for $THEME_NAME..."
|
||||
USER_GTK4="$HOME/.config/gtk-4.0"
|
||||
mkdir -p "$USER_GTK4"
|
||||
ln -sf "$THEME_GTK4/gtk-dark.css" "$USER_GTK4/gtk.css"
|
||||
ln -sf "$THEME_GTK4/gtk-dark.css" "$USER_GTK4/gtk-dark.css"
|
||||
rm -f "$USER_GTK4/assets"
|
||||
ln -s "$THEME_GTK4/assets" "$USER_GTK4/assets"
|
||||
else
|
||||
err "GTK4 theme not found: $THEME_GTK4 — libadwaita apps will use fallback theme."
|
||||
fi
|
||||
|
||||
# gsettings
|
||||
log "Setting gsettings for GTK theme..."
|
||||
gsettings set org.gnome.desktop.interface gtk-theme "$THEME_NAME"
|
||||
gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark'
|
||||
gsettings set org.gnome.desktop.interface icon-theme 'Colloid-Grey-Catppuccin-Dark'
|
||||
gsettings set org.gnome.desktop.interface font-name 'UbuntuSans Nerd Font 11'
|
||||
|
||||
# ============================================================
|
||||
# Phase 8: Deploy User Configs (Hard Overwrite)
|
||||
# ============================================================
|
||||
|
||||
# Replace user-level XDG configs from /etc/xdg/ (deployed by moonarch-git)
|
||||
log "Deploying XDG configs to ~/.config/ (overwrite)..."
|
||||
for src_dir in /etc/xdg/*/; do
|
||||
app_name="$(basename "$src_dir")"
|
||||
# Only overwrite apps that moonarch manages
|
||||
[[ -d "$DEFAULTS_DIR/xdg/$app_name" ]] || continue
|
||||
# gtk-4.0 is handled separately with Colloid-Grey-Dark-Catppuccin theme symlinks
|
||||
[[ "$app_name" == "gtk-4.0" ]] && continue
|
||||
# kanshi profiles are user-specific (display setup) — only seed, never overwrite
|
||||
[[ "$app_name" == "kanshi" ]] && [[ -d "$HOME/.config/kanshi" ]] && continue
|
||||
dest_dir="$HOME/.config/$app_name"
|
||||
rm -rf "$dest_dir" 2>/dev/null || sudo rm -rf "$dest_dir"
|
||||
cp -r --no-preserve=ownership "$src_dir" "$dest_dir"
|
||||
log " + $app_name/"
|
||||
done
|
||||
|
||||
# Overwrite configs owned by other packages with moonarch versions
|
||||
log "Deploying moonarch config overrides..."
|
||||
cp /usr/share/moonarch/walker-config.toml "$HOME/.config/walker/config.toml"
|
||||
log " + walker/config.toml"
|
||||
|
||||
# Deploy user defaults (overwrite, no existence check)
|
||||
log "Deploying user config defaults to ~/.config/ (overwrite)..."
|
||||
if [[ -d "$USER_DEFAULTS" ]]; then
|
||||
for src_dir in "$USER_DEFAULTS"/*/; do
|
||||
app_name="$(basename "$src_dir")"
|
||||
dest_dir="$HOME/.config/$app_name"
|
||||
mkdir -p "$dest_dir"
|
||||
find "$src_dir" -type f -print0 | while IFS= read -r -d '' src_file; do
|
||||
rel_path="${src_file#"$src_dir"}"
|
||||
dest_file="$dest_dir/$rel_path"
|
||||
mkdir -p "$(dirname "$dest_file")"
|
||||
cp "$src_file" "$dest_file"
|
||||
log " + $app_name/$rel_path"
|
||||
done
|
||||
done
|
||||
fi
|
||||
|
||||
# Zsh: always create a fresh .zshrc that sources Moonarch defaults
|
||||
log "Creating ~/.zshrc with Moonarch defaults..."
|
||||
mkdir -p "$HOME/.zshrc.d"
|
||||
echo "# Load Moonarch defaults, add custom overrides in ~/.zshrc.d/ or below" > "$HOME/.zshrc"
|
||||
echo "source /etc/zsh/zshrc.moonarch" >> "$HOME/.zshrc"
|
||||
|
||||
# ============================================================
|
||||
# Phase 9: Services & Finalization
|
||||
# ============================================================
|
||||
|
||||
# Enable systemd user services
|
||||
log "Enabling systemd user services..."
|
||||
USER_SERVICES=(
|
||||
"kanshi"
|
||||
"stasis"
|
||||
"cliphist"
|
||||
)
|
||||
|
||||
for service in "${USER_SERVICES[@]}"; do
|
||||
if systemctl --user cat "${service}.service" &>/dev/null; then
|
||||
systemctl --user enable "$service"
|
||||
log " + $service (user)"
|
||||
else
|
||||
log " ~ $service (user) not found, skipped."
|
||||
fi
|
||||
done
|
||||
|
||||
log "Enabling services..."
|
||||
SERVICES=(
|
||||
"NetworkManager"
|
||||
"bluetooth"
|
||||
"docker"
|
||||
"greetd"
|
||||
"systemd-timesyncd"
|
||||
"ufw"
|
||||
"auto-cpufreq"
|
||||
)
|
||||
|
||||
for service in "${SERVICES[@]}"; do
|
||||
if systemctl list-unit-files "${service}.service" &>/dev/null; then
|
||||
sudo systemctl enable "$service"
|
||||
log " + $service"
|
||||
else
|
||||
log " ~ $service not found, skipped."
|
||||
fi
|
||||
done
|
||||
|
||||
# Set shell to zsh
|
||||
if [[ "$SHELL" != */zsh ]]; then
|
||||
log "Setting default shell to zsh..."
|
||||
chsh -s "$(which zsh)"
|
||||
fi
|
||||
|
||||
# Firewall
|
||||
log "Configuring UFW..."
|
||||
sudo ufw default deny incoming
|
||||
sudo ufw default allow outgoing
|
||||
sudo ufw --force enable
|
||||
|
||||
# Docker group
|
||||
if ! groups | grep -q docker; then
|
||||
log "Adding user to docker group..."
|
||||
sudo usermod -aG docker "$USER"
|
||||
fi
|
||||
|
||||
# Directories
|
||||
mkdir -p "$HOME/Pictures/Screenshots"
|
||||
mkdir -p "$HOME/Pictures/Wallpaper"
|
||||
|
||||
# ============================================================
|
||||
# Phase 10: Done
|
||||
# ============================================================
|
||||
|
||||
echo ""
|
||||
log "============================================"
|
||||
log " Moonarch transform complete!"
|
||||
log "============================================"
|
||||
echo ""
|
||||
if [[ -n "$BACKUP_FILE" ]]; then
|
||||
log "Your previous config is backed up at:"
|
||||
log " $BACKUP_FILE"
|
||||
if [[ -n "$SYSTEM_BACKUP" ]]; then
|
||||
log " $SYSTEM_BACKUP"
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
log "Next steps:"
|
||||
log " 1. Reboot (greetd replaces your previous display manager)"
|
||||
log " 2. Place wallpapers in ~/Pictures/Wallpaper/"
|
||||
log " 3. rustup default stable"
|
||||
log " 4. User overrides in ~/.config/ or ~/.zshrc.d/"
|
||||
echo ""
|
||||
err "Do NOT log out — your previous DM is disabled. Reboot instead."
|
||||
echo ""
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/bin/bash
|
||||
# ABOUTME: Legacy updater — replaced by moonarch-update from the moonarch-git package.
|
||||
# ABOUTME: Prints deprecation notice and forwards if package version is available.
|
||||
|
||||
echo ""
|
||||
echo -e "\e[1;33m[Moonarch]\e[0m moonarch-update is now provided by the moonarch-git package."
|
||||
echo -e "\e[1;33m[Moonarch]\e[0m Install with: paru -S moonarch-git"
|
||||
echo -e "\e[1;33m[Moonarch]\e[0m Then run: moonarch-update"
|
||||
echo ""
|
||||
|
||||
if command -v moonarch-update &>/dev/null && [[ "$(which moonarch-update)" == "/usr/bin/moonarch-update" ]]; then
|
||||
echo -e "\e[1;34m[Moonarch]\e[0m Package version detected. Forwarding..."
|
||||
exec moonarch-update
|
||||
fi
|
||||
Reference in New Issue
Block a user