Compare commits
4 Commits
main
...
28a0e28f2d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28a0e28f2d | ||
|
|
0e93639702 | ||
|
|
0656f33611 | ||
|
|
749f970fb0 |
@@ -1,28 +0,0 @@
|
||||
---
|
||||
name: nomarchy-runner
|
||||
description: Mechanical execution of the Nomarchy headless test harness. Use PROACTIVELY when a build or VM run just needs to be executed and its artifacts collected — nix build, tools/test-live-iso.sh, tools/test-install.sh, screenshot capture via tools/vm/vncshot.py and tools/vm/qmp.py, and the scripted checks (check-theme-contrast.py, audit-theme-design.py, check-option-docs.py). Never use for interpreting ambiguous failures, judging visual quality, or deciding what to test.
|
||||
tools: Read, Bash, Glob, Grep
|
||||
model: haiku
|
||||
---
|
||||
|
||||
You are a deterministic test executor for the Nomarchy repository. You run
|
||||
exactly the commands you are asked to run, headlessly, and hand back the
|
||||
evidence. You do not decide what to test, do not interpret ambiguous
|
||||
results, and do not judge whether something "looks fine".
|
||||
|
||||
Rules:
|
||||
- Run everything headless and unattended. Never launch a graphical VM
|
||||
window; never wait on human input. Wrap long-running commands in
|
||||
timeouts as instructed by the caller, and treat a timeout as a recorded
|
||||
failure, not something to silently retry.
|
||||
- Collect artifacts to the working directory the caller specifies: full
|
||||
command lines, exit codes, the tail of stdout/stderr (plus the complete
|
||||
logs as files), serial console output, and screenshot file paths.
|
||||
- Report format: for each command — command, exit code, wall time,
|
||||
artifact paths, and verbatim error lines if the exit code was nonzero.
|
||||
Nothing else. No summaries of what the results "mean".
|
||||
- Never mark anything as passed or verified. You return evidence; the
|
||||
caller makes the verification claim.
|
||||
- If a command fails in a way that prevents collecting artifacts, report
|
||||
exactly what happened and stop — do not improvise recovery steps beyond
|
||||
what the caller authorized.
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: nomarchy-scout
|
||||
description: Read-only reconnaissance inside the Nomarchy repo. Use PROACTIVELY for any task that is pure information gathering — locating where an option/module/theme is defined, mapping which files touch a subsystem, scanning build logs or serial-console output for errors, checking docs against code for drift, summarizing a directory. Never use for anything requiring judgment about design, correctness, or visual quality.
|
||||
tools: Read, Grep, Glob
|
||||
model: haiku
|
||||
---
|
||||
|
||||
You are a fast, cheap scout for the Nomarchy NixOS distribution repository.
|
||||
Your only job is to find things and report facts. You never edit, never
|
||||
judge design quality, and never draw conclusions beyond what the files
|
||||
literally say.
|
||||
|
||||
Rules:
|
||||
- Report file paths and line numbers, quote the minimum relevant snippet.
|
||||
- If asked to scan logs, extract the error/warning lines verbatim with
|
||||
enough surrounding context to locate them, and note timestamps.
|
||||
- If you cannot find something, say so plainly — never guess or infer that
|
||||
something "probably" exists.
|
||||
- Keep output terse and structured: the caller pays for every token you
|
||||
produce. Facts first, no prose padding, no recommendations.
|
||||
@@ -1,88 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Read",
|
||||
"Edit",
|
||||
"Write",
|
||||
"Glob",
|
||||
"Grep",
|
||||
|
||||
"Bash(ls *)",
|
||||
"Bash(cat *)",
|
||||
"Bash(head *)",
|
||||
"Bash(tail *)",
|
||||
"Bash(grep *)",
|
||||
"Bash(rg *)",
|
||||
"Bash(find *)",
|
||||
"Bash(tree *)",
|
||||
"Bash(wc *)",
|
||||
"Bash(file *)",
|
||||
"Bash(stat *)",
|
||||
"Bash(du *)",
|
||||
"Bash(df *)",
|
||||
"Bash(which *)",
|
||||
"Bash(diff *)",
|
||||
"Bash(jq *)",
|
||||
"Bash(sha256sum *)",
|
||||
|
||||
"Bash(mkdir *)",
|
||||
"Bash(cp *)",
|
||||
"Bash(mv *)",
|
||||
"Bash(touch *)",
|
||||
"Bash(ln -s *)",
|
||||
"Bash(tar *)",
|
||||
|
||||
"Bash(nix *)",
|
||||
"Bash(nix-store *)",
|
||||
"Bash(nix-instantiate *)",
|
||||
|
||||
"Bash(./tools/test-live-iso.sh *)",
|
||||
"Bash(./tools/test-install.sh *)",
|
||||
"Bash(./tools/audit-theme-design.py *)",
|
||||
"Bash(./tools/check-option-docs.py *)",
|
||||
"Bash(./tools/check-theme-contrast.py *)",
|
||||
"Bash(./tools/import-palettes.py *)",
|
||||
"Bash(./tools/vm/qmp.py *)",
|
||||
"Bash(./tools/vm/vncshot.py *)",
|
||||
"Bash(./tools/vm/gap-analysis.py *)",
|
||||
"Bash(python3 tools/*)",
|
||||
"Bash(bash tools/*)",
|
||||
|
||||
"Bash(qemu-system-x86_64 *)",
|
||||
"Bash(qemu-img *)",
|
||||
|
||||
"Bash(git status *)",
|
||||
"Bash(git diff *)",
|
||||
"Bash(git log *)",
|
||||
"Bash(git show *)",
|
||||
"Bash(git add *)",
|
||||
"Bash(git commit *)",
|
||||
"Bash(git branch *)",
|
||||
"Bash(git switch *)",
|
||||
"Bash(git checkout *)",
|
||||
"Bash(git restore *)",
|
||||
"Bash(git stash *)",
|
||||
"Bash(git worktree *)",
|
||||
|
||||
"Bash(systemctl --user status *)",
|
||||
"Bash(journalctl --user *)",
|
||||
"Bash(pgrep *)",
|
||||
"Bash(pkill -f qemu*)"
|
||||
],
|
||||
"ask": [
|
||||
"Bash(git push *)",
|
||||
"Bash(rm *)",
|
||||
"Bash(git reset --hard *)",
|
||||
"Bash(git clean *)",
|
||||
"Bash(nix-collect-garbage *)",
|
||||
"Bash(curl *)",
|
||||
"Bash(wget *)"
|
||||
],
|
||||
"deny": [
|
||||
"Bash(sudo *)",
|
||||
"Bash(git push --force *)",
|
||||
"Bash(git push -f *)"
|
||||
],
|
||||
"defaultMode": "default"
|
||||
}
|
||||
}
|
||||
102
.forgejo/workflows/check.yml
Normal file
102
.forgejo/workflows/check.yml
Normal file
@@ -0,0 +1,102 @@
|
||||
# Nomarchy CI — eval + lint.
|
||||
#
|
||||
# Catches the regressions that hurt today:
|
||||
# 1. Flake stops evaluating (broken option ref, missing import, etc.).
|
||||
# 2. A `nomarchy-*` shell script has a syntax error or a shellcheck
|
||||
# error-severity issue.
|
||||
# 3. `docs/SCRIPTS.md` drifts from the repo state because somebody
|
||||
# added / removed / renamed a script and didn't run the generator
|
||||
# (the pre-commit hook handles this, but only when enabled per-clone).
|
||||
#
|
||||
# Doesn't build ISOs — that needs a binary cache. Add a separate job
|
||||
# once Cachix/Attic is in place.
|
||||
|
||||
name: Check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
eval-and-lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
with:
|
||||
# Match the runner's effective channel. Nomarchy itself tracks
|
||||
# nixos-25.11 via flake.nix; the installer-action default is fine.
|
||||
extra-conf: |
|
||||
experimental-features = nix-command flakes
|
||||
|
||||
- name: nix flake check --no-build
|
||||
run: nix flake check --no-build
|
||||
|
||||
- name: Lint nomarchy-* scripts (bash -n + shellcheck)
|
||||
run: |
|
||||
# Mirror what .githooks/pre-commit runs locally, but across the
|
||||
# whole tree instead of just changed files. Pre-commit gates
|
||||
# individual commits; CI gates branches (including --no-verify
|
||||
# bypasses).
|
||||
set -e
|
||||
fail=0
|
||||
while IFS= read -r script; do
|
||||
[[ -f "$script" ]] || continue
|
||||
# Python helpers ship under the same nomarchy- prefix
|
||||
# (e.g. nomarchy-haptic-touchpad). Skip non-bash.
|
||||
head -1 "$script" | grep -qE '^#!.*\bbash\b' || continue
|
||||
if ! bash -n "$script"; then
|
||||
echo "::error file=$script::bash syntax error"
|
||||
fail=1
|
||||
fi
|
||||
if ! nix shell nixpkgs#shellcheck --command shellcheck \
|
||||
--severity=error --shell=bash "$script"; then
|
||||
echo "::error file=$script::shellcheck error-severity issue"
|
||||
fail=1
|
||||
fi
|
||||
done < <(find features/scripts/utils core/system/scripts \
|
||||
themes/engine/scripts \
|
||||
-maxdepth 1 -type f -name 'nomarchy-*')
|
||||
exit "$fail"
|
||||
|
||||
- name: docs/SCRIPTS.md is up to date
|
||||
run: |
|
||||
# Regenerate to a temp file and compare. If different, the
|
||||
# contributor forgot to run the generator (or skipped the
|
||||
# pre-commit hook). Fail loudly and tell them the fix.
|
||||
./bin/utils/nomarchy-docs-scripts --out /tmp/SCRIPTS.regen.md
|
||||
if ! diff -q docs/SCRIPTS.md /tmp/SCRIPTS.regen.md >/dev/null; then
|
||||
echo "::error::docs/SCRIPTS.md is stale."
|
||||
echo "Run: ./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md"
|
||||
echo "Then commit the regenerated file."
|
||||
echo "--- diff ---"
|
||||
diff -u docs/SCRIPTS.md /tmp/SCRIPTS.regen.md || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: installer/hardware-db.sh references real nixos-hardware modules
|
||||
run: |
|
||||
# Every 4th-pipe-field in HARDWARE_DB is a nixos-hardware module
|
||||
# name. Half the DB used to point at modules that don't exist
|
||||
# (e.g. microsoft-surface-pro-8 — there's only -pro-intel and
|
||||
# -pro-9), which made the install fail at eval time with
|
||||
# cryptic "attribute not found" errors on real laptops. This
|
||||
# step catches that regression class.
|
||||
awk -F'|' '/^ "/ { gsub(/"/,"",$4); gsub(/^[[:space:]]+|[[:space:]]+$/,"",$4); if ($4) print $4 }' \
|
||||
installer/hardware-db.sh | sort -u > /tmp/db-refs.txt
|
||||
nix eval --impure --json --expr '
|
||||
let
|
||||
nh = (builtins.getFlake (toString ./.)).inputs.nixos-hardware.nixosModules;
|
||||
in builtins.attrNames nh' \
|
||||
| nix shell nixpkgs#jq --command jq -r '.[]' | sort -u > /tmp/db-real.txt
|
||||
missing=$(comm -23 /tmp/db-refs.txt /tmp/db-real.txt)
|
||||
if [[ -n "$missing" ]]; then
|
||||
echo "::error::hardware-db.sh references nixos-hardware modules that don't exist:"
|
||||
printf ' - %s\n' $missing
|
||||
echo "Either fix the name (check the actual attr in nixos-hardware) or drop the row."
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,103 +0,0 @@
|
||||
# Nomarchy scheduled lock bump — the automated half of "rock-stable
|
||||
# without rotting": once a day, update flake.lock (inputs track pinned
|
||||
# release branches, so this never jumps a NixOS release — that's a
|
||||
# deliberate hand-edited v2, see agent/GOALS.md), gate it, and land it
|
||||
# on main only on green. `v1` promotion stays human, always.
|
||||
#
|
||||
# Fast lane: workflow_dispatch. For a security fix upstream, run this
|
||||
# workflow manually from the Actions tab instead of waiting for the
|
||||
# next daily run.
|
||||
#
|
||||
# Gate scope: `nix flake check --no-build` (eval tier) followed by a V1 build
|
||||
# (home-manager activation package + nixos toplevel) to catch compilation errors.
|
||||
# (this runner has no KVM; see item 20 for the VM-suite
|
||||
# upgrade path). The py_compile/bash -n steps are skipped on purpose:
|
||||
# they don't read flake.lock, so a lock bump cannot break them. The
|
||||
# bump commit's own push then triggers check.yml as a second net.
|
||||
# A green bump therefore guards evaluation and compilation, not VM behaviour —
|
||||
# the checks.* suite still runs locally / at promotion time.
|
||||
#
|
||||
# Failure mode: a red gate fails this run visibly and pushes nothing;
|
||||
# next schedule retries. A push race (someone landed on main mid-run)
|
||||
# also just fails the final push — rerun or wait a day.
|
||||
#
|
||||
# Container recipe (nixbld users, sandbox=false, pinned Nix, plain-shell
|
||||
# install) inherited from check.yml — the gotchas are documented there.
|
||||
|
||||
name: Lock bump
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '17 5 * * *' # daily 05:17 UTC
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
bump:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
NIX_CONFIG: |
|
||||
max-jobs = 1
|
||||
cores = 2
|
||||
experimental-features = nix-command flakes
|
||||
sandbox = false
|
||||
NIX_SSL_CERT_FILE: /etc/ssl/certs/ca-certificates.crt
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Nix
|
||||
run: |
|
||||
NIX_VERSION=2.31.5
|
||||
groupadd -r nixbld 2>/dev/null || true
|
||||
for i in $(seq 1 10); do
|
||||
useradd -r -g nixbld -G nixbld -d /var/empty \
|
||||
-s /usr/sbin/nologin -c "Nix build user $i" "nixbld$i" 2>/dev/null || true
|
||||
done
|
||||
curl -L "https://releases.nixos.org/nix/nix-${NIX_VERSION}/install" | sh -s -- --no-daemon
|
||||
echo "$HOME/.nix-profile/bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Update flake.lock
|
||||
# Inputs track release branches (nixos-26.05, release-26.05), so
|
||||
# the update stays within them by construction. The human-readable
|
||||
# change list goes into the commit body below.
|
||||
run: |
|
||||
nix flake update 2>&1 | tee /tmp/bump-log
|
||||
if git diff --quiet flake.lock; then
|
||||
echo "Lock already up to date — nothing to do."
|
||||
echo "changed=0" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "changed=1" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
id: update
|
||||
|
||||
- name: Gate — evaluate every output (eval tier, memory-bounded)
|
||||
# Per-output processes, not one big `nix flake check --no-build`:
|
||||
# the single-process walk peaks ~6 GB RSS and OOMs inside the
|
||||
# runner's 2 GB container cap (details in tools/ci-eval.sh).
|
||||
if: steps.update.outputs.changed == '1'
|
||||
run: bash tools/ci-eval.sh
|
||||
|
||||
- name: Gate — V1 build (toplevel + home-manager)
|
||||
if: steps.update.outputs.changed == '1'
|
||||
run: |
|
||||
nix build .#homeConfigurations.nomarchy.activationPackage --no-link
|
||||
nix build .#nixosConfigurations.nomarchy.config.system.build.toplevel --no-link
|
||||
|
||||
- name: Commit + push on green
|
||||
if: steps.update.outputs.changed == '1'
|
||||
# The checkout step persists the Actions token, so this push
|
||||
# authenticates as the workflow; its push triggers check.yml as
|
||||
# the second net. Only flake.lock is committed — pathspec-limited,
|
||||
# nothing else can ride along.
|
||||
run: |
|
||||
{
|
||||
echo "chore(lock): scheduled upstream bump"
|
||||
echo
|
||||
grep -E '^(• | )' /tmp/bump-log || true
|
||||
echo
|
||||
echo "Gate: nix flake check --no-build (eval tier — see bump.yml header)."
|
||||
} > /tmp/bump-msg
|
||||
git -c user.name="nomarchy-bump" -c user.email="actions@git.bemagri.xyz" \
|
||||
commit --only flake.lock -F /tmp/bump-msg
|
||||
git push origin HEAD:main
|
||||
@@ -1,108 +0,0 @@
|
||||
# Nomarchy CI — the always-on net under direct-to-main pushes.
|
||||
#
|
||||
# Scope (deliberate): the EVAL tier only. The runner behind this Gitea
|
||||
# instance is act_runner + docker containers (the legacy repo's check.yml
|
||||
# ran 57 times on it) — no systemd, no /dev/kvm — so the checks.* VM
|
||||
# tests and full toplevel builds can't run here. `nix flake check
|
||||
# --no-build` still catches most breakage (type errors, missing options,
|
||||
# bad merges, template/mkFlake drift — see docs/TESTING.md §1); the VM
|
||||
# suite stays a local/promotion gate until a KVM-capable NixOS runner
|
||||
# exists (see the commented vm-checks job at the bottom).
|
||||
#
|
||||
# Inherited-from-legacy gotchas (learned over 57 runs, kept verbatim):
|
||||
# - Single-user Nix (--no-daemon): no systemd in the container. The
|
||||
# installer runs as root and honours build-users-group=nixbld from
|
||||
# its bundled nix.conf, aborting unless the group exists AND has
|
||||
# members — create nixbld + users first.
|
||||
# - sandbox=false: Stylix/base16.nix do import-from-derivation; the
|
||||
# single-user Nix in this container can't set up the build sandbox
|
||||
# (no user namespaces), which otherwise surfaces as
|
||||
# "path '…-source' is not valid".
|
||||
# - Pin the Nix version: 2.34's lazy-trees git cache doesn't
|
||||
# materialise flake-input `-source` paths into the store, breaking
|
||||
# the same IFD reads. 2.31.5 matches what wrote flake.lock.
|
||||
# - Plain-shell Nix install, not a JS action: act_runner's bundled act
|
||||
# tops out at node20; a `run:` step has no node-runtime coupling.
|
||||
|
||||
name: Check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, v1]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
eval:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
NIX_CONFIG: |
|
||||
max-jobs = 1
|
||||
cores = 1
|
||||
experimental-features = nix-command flakes
|
||||
sandbox = false
|
||||
NIX_SSL_CERT_FILE: /etc/ssl/certs/ca-certificates.crt
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Nix
|
||||
run: |
|
||||
NIX_VERSION=2.31.5
|
||||
groupadd -r nixbld 2>/dev/null || true
|
||||
for i in $(seq 1 10); do
|
||||
useradd -r -g nixbld -G nixbld -d /var/empty \
|
||||
-s /usr/sbin/nologin -c "Nix build user $i" "nixbld$i" 2>/dev/null || true
|
||||
done
|
||||
curl -L "https://releases.nixos.org/nix/nix-${NIX_VERSION}/install" | sh -s -- --no-daemon
|
||||
echo "$HOME/.nix-profile/bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Evaluate every output (eval tier, memory-bounded)
|
||||
# Same coverage as `nix flake check --no-build` — both nixos
|
||||
# configs, the home config, the checks.* derivations (instantiated,
|
||||
# not run) and the downstream template through lib.mkFlake — but
|
||||
# ONE output per nix process. A single process walking everything
|
||||
# peaks at ~6.0 GB RSS (measured 2026-07-12): it OOM'd the 4 GB
|
||||
# VPS itself, and can never fit this runner's 2 GB container cap.
|
||||
# Per-output processes cap at the largest single output (~1 GB).
|
||||
run: bash tools/ci-eval.sh
|
||||
|
||||
- name: Python syntax (all tracked scripts)
|
||||
# Every tracked *.py — theme-sync, the installer composers
|
||||
# (compose-lock, patch-template) and the maintainer tools
|
||||
# (tools/, tools/vm/). Via nix shell so the step doesn't depend on
|
||||
# the runner image preinstalling python3.
|
||||
run: |
|
||||
nix shell nixpkgs#python3 --command \
|
||||
bash -c 'git ls-files "*.py" | xargs -r python3 -m py_compile'
|
||||
|
||||
- name: Shell syntax (tracked scripts)
|
||||
# The distro's user-facing scripts are generated by Nix (their
|
||||
# syntax is exercised by the eval + local builds); this covers the
|
||||
# hand-written .sh files: installer helpers and maintainer tools.
|
||||
run: |
|
||||
set -e
|
||||
fail=0
|
||||
while IFS= read -r script; do
|
||||
head -1 "$script" | grep -qE '^#!.*\b(bash|sh)\b' || continue
|
||||
if ! bash -n "$script"; then
|
||||
echo "::error file=$script::bash syntax error"
|
||||
fail=1
|
||||
fi
|
||||
done < <(git ls-files '*.sh')
|
||||
exit "$fail"
|
||||
|
||||
# ── vm-checks (DISABLED — FUTURE #20, not NEXT) ─────────────────────
|
||||
# Eval-only CI is the standing decision: Gitea act_runner stays docker
|
||||
# compose (no /dev/kvm). Full checks.* VMs need a *separate* host with
|
||||
# nix + KVM (label nix-kvm); see agent/BACKLOG.md FUTURE #20. Do NOT
|
||||
# uncomment until that label is online — Gitea queues forever otherwise.
|
||||
#
|
||||
# vm-checks:
|
||||
# runs-on: nix-kvm
|
||||
# timeout-minutes: 120
|
||||
# steps:
|
||||
# - uses: actions/checkout@v4
|
||||
# - run: nix flake check # builds + runs the VM tests
|
||||
# - run: nix build .#nixosConfigurations.nomarchy.config.system.build.toplevel --no-link
|
||||
# - run: nix build .#homeConfigurations.nomarchy.activationPackage --no-link
|
||||
54
.githooks/pre-commit
Executable file
54
.githooks/pre-commit
Executable file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env bash
|
||||
# Nomarchy pre-commit hook.
|
||||
#
|
||||
# Enable per-clone with:
|
||||
# git config core.hooksPath .githooks
|
||||
#
|
||||
# Two responsibilities:
|
||||
# 1. Lint changed nomarchy-* scripts (bash -n + shellcheck if available)
|
||||
# so syntax errors and unquoted-var bugs don't ship.
|
||||
# 2. Regenerate docs/SCRIPTS.md when any nomarchy-* script under the three
|
||||
# script directories is added, modified, or deleted in this commit, and
|
||||
# stage the refreshed file so it lands with the change.
|
||||
|
||||
set -e
|
||||
|
||||
repo_root="$(git rev-parse --show-toplevel)"
|
||||
cd "$repo_root"
|
||||
|
||||
script_dirs_re='^(features/scripts/utils|core/system/scripts|themes/engine/scripts)/nomarchy-'
|
||||
|
||||
# 1. Lint changed scripts. bash -n catches syntax errors (always fatal).
|
||||
# shellcheck catches unquoted-var, use-before-define, missing-shebang, etc.
|
||||
# We only fail on severity=error so the long tail of pre-existing warnings
|
||||
# (info / style / warning) doesn't block commits — those can be cleaned up
|
||||
# incrementally without a flag day.
|
||||
changed_scripts=$(git diff --cached --name-only --diff-filter=ACMR \
|
||||
| grep -E "$script_dirs_re" || true)
|
||||
if [[ -n "$changed_scripts" ]]; then
|
||||
while IFS= read -r script; do
|
||||
[[ -f "$script" ]] || continue
|
||||
# Only lint scripts with a bash shebang. nomarchy-* is a name
|
||||
# convention, not a language guarantee — at least one Python helper
|
||||
# ships under the same prefix (nomarchy-haptic-touchpad).
|
||||
head -1 "$script" | grep -qE '^#!.*\bbash\b' || continue
|
||||
if ! bash -n "$script"; then
|
||||
echo "pre-commit: bash syntax error in $script — aborting commit." >&2
|
||||
exit 1
|
||||
fi
|
||||
if command -v shellcheck >/dev/null 2>&1; then
|
||||
if ! shellcheck --severity=error --shell=bash "$script"; then
|
||||
echo "pre-commit: shellcheck found error-level issues in $script — aborting commit." >&2
|
||||
echo "pre-commit: fix the reported issues, or rerun with --no-verify after a deliberate decision to ship." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done <<< "$changed_scripts"
|
||||
fi
|
||||
|
||||
# 2. Regenerate the script audit doc.
|
||||
if git diff --cached --name-only --diff-filter=ACMRD | grep -qE "$script_dirs_re"; then
|
||||
echo "pre-commit: regenerating docs/SCRIPTS.md (script change detected)…"
|
||||
./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md
|
||||
git add docs/SCRIPTS.md
|
||||
fi
|
||||
18
.gitignore
vendored
18
.gitignore
vendored
@@ -1,13 +1,15 @@
|
||||
# Nix build artifacts
|
||||
# ---> Nix
|
||||
# Ignore build outputs from performing a nix-build or `nix build` command
|
||||
result
|
||||
result-*
|
||||
|
||||
__pycache__/
|
||||
.DS_Store
|
||||
# Ignore automatically generated direnv output
|
||||
.direnv
|
||||
|
||||
# Ad-hoc hardware evidence photos dropped in the tree (not repo content)
|
||||
WhatsApp Image*.jpeg
|
||||
# Local IDE settings
|
||||
.claude/
|
||||
|
||||
# Claude Code machine-local settings (permissions etc.)
|
||||
.claude/settings.local.json
|
||||
.claude/settings.local.json.tmp.*
|
||||
# VM and ISO artifacts
|
||||
*.qcow2
|
||||
*.qcow
|
||||
*.iso
|
||||
|
||||
65
AGENTS.md
65
AGENTS.md
@@ -1,65 +0,0 @@
|
||||
# Nomarchy — agent entry point
|
||||
|
||||
Nomarchy is a NixOS-based distro: rock-stable, fully reproducible, themed
|
||||
from one JSON, configured through a menu that writes into the user's own
|
||||
flake checkout. Read the README for the architecture.
|
||||
|
||||
This file is the entry point for **any** AI coding agent, whatever the
|
||||
vendor or harness. Everything agents need lives in vendor-neutral,
|
||||
git-tracked markdown (`agent/`, `docs/`); harness-specific config is a
|
||||
thin *adapter* (see bottom) and never holds shared content.
|
||||
|
||||
## Where things live (read this first)
|
||||
|
||||
| Path | Role |
|
||||
|------|------|
|
||||
| **`agent/`** | Agent instructions + loop state — **only executable queue is `BACKLOG.md`** |
|
||||
| **`agent/README.md`** | Map of the agent files |
|
||||
| **`docs/VISION.md`** | Product themes toward **v1.0** (not a queue) |
|
||||
| **`docs/ROADMAP.md`** | Design history + shipped log |
|
||||
| **`docs/README.md`** | Full documentation map |
|
||||
| **`.claude/`** | Claude Code adapter only (permissions, subagent defs) — not the backlog |
|
||||
|
||||
## If you're here to work autonomously (the loop)
|
||||
Follow **`agent/LOOP.md`** — one iteration: orient → pick one BACKLOG
|
||||
item → work → verify → commit+push on `main` → record. All loop state is
|
||||
git-tracked in `agent/`.
|
||||
|
||||
## Rules that apply to every session, loop or not
|
||||
- **Before your first change, read `agent/VERIFICATION.md`** and follow
|
||||
it — every change, however small, climbs its V0–V3 ladder. For theme
|
||||
or visual work, also read `agent/THEME-DESIGN.md`; before any VM test,
|
||||
`docs/TESTING.md`.
|
||||
- **Honesty rule** (`agent/VERIFICATION.md`, `docs/TESTING.md`): for
|
||||
visual/interactive changes, evaluation is not rendering — state the
|
||||
tier you reached. Cheap first: `nix flake check --no-build`.
|
||||
- **Conventions** (`agent/CONVENTIONS.md`): in-flake state, menu
|
||||
placement, Waybar parity with whole-swaps, toggle-vs-package discipline,
|
||||
template as SoT for opt-in comments (`templates/downstream`).
|
||||
- **Sync sweep on completion** (`agent/LOOP.md` §5): when a task ships,
|
||||
update or delete every cross-reference it made stale — BACKLOG
|
||||
pitches/pointers, HARDWARE-QUEUE entries, docs — in the same commit.
|
||||
No stale items, no unsynced information.
|
||||
- **Git:** direct commits on `main`, pushed; **`v1` is human-only** —
|
||||
never touch it. Never `nix flake update` unless the task is a lock bump.
|
||||
No formatter — match aligned hand-formatting.
|
||||
- Layout: `hosts/` machine · `modules/` distro · `themes/` data ·
|
||||
`pkgs/` code · `tools/` maintainer · `agent/` loop · `docs/` human docs.
|
||||
|
||||
## Delegation & escalation
|
||||
|
||||
Push mechanical work down to cheaper models; keep judgment on the
|
||||
strongest model available. Capability tiers, standing roles (scout /
|
||||
runner), and per-harness model mappings: **`agent/DELEGATION.md`**.
|
||||
Work above your tier? Return it — don't burn tokens.
|
||||
|
||||
## Harness adapters
|
||||
|
||||
One directory per harness, holding only what that harness *requires* in
|
||||
its own format (permissions, subagent/skill registration). Adapters point
|
||||
into `agent/`; they never carry policy, queue items, or vision text.
|
||||
|
||||
| Harness | Adapter |
|
||||
|---------|---------|
|
||||
| Claude Code | `.claude/` — `CLAUDE.md` is a symlink to this file |
|
||||
| others | add a sibling dir + a row here; keep it a thin shim |
|
||||
21
LICENSE
21
LICENSE
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Bernardo Magri
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
617
README.md
617
README.md
@@ -1,549 +1,118 @@
|
||||
# Nomarchy
|
||||
# 👑 Nomarchy
|
||||
|
||||
**The rock-solid reproducibility of NixOS 26.05. The out-of-the-box polish of
|
||||
Omarchy/Omakub.** One JSON file rules the look of the entire desktop; every
|
||||
theme change is a Home Manager generation — atomic, rollbackable, never
|
||||
partial.
|
||||
**Nomarchy** is a professional-grade NixOS distribution that ships a highly curated Hyprland desktop on a strictly declarative, flake-based foundation. It provides a highly polished, "it just works" experience for power users who want a beautiful Wayland environment without sacrificing the reliability of NixOS.
|
||||
|
||||
## ✨ Key Features
|
||||
|
||||
- **Purely Declarative:** Your entire desktop—themes, fonts, wallpapers, and toolchains—is defined in code.
|
||||
- **Modular Merging Architecture:** Distro code and user code are strictly separated. You can update the distro core without ever touching or breaking your personal configurations.
|
||||
- **Interactive "Smart" Installer:** Detects hardware, sets up networking, localizes timezones/keymaps, and offers multi-select software profiles.
|
||||
- **Erase Your Darlings (Optional):** Optional BTRFS root-wipe on boot ensures your system stays pristine and 100% declarative.
|
||||
- **Dynamic Theming Engine:** 20+ built-in themes with instant UI feedback via IPC (swww, waybar, and stylix).
|
||||
- **Portable State (`nomarchy-sync`):** Easily backup and sync your declarative config and dynamic state (theme/wallpaper choices) to a private Git repo.
|
||||
|
||||
---
|
||||
|
||||
## 📂 Component-Based Architecture
|
||||
|
||||
Nomarchy uses a **Feature-Centric Directory Structure**. For a comprehensive breakdown of the system architecture, folder roles, and module logic, see the [Detailed Architecture Documentation](docs/STRUCTURE.md).
|
||||
|
||||
Configuration, modules, and utilities are strictly organized to maintain sanity as the system grows.
|
||||
|
||||
- **`core/`**: Foundational OS & User defaults (Bootloader, Audio, Bluetooth, core system features).
|
||||
- **`features/`**: Isolated modules containing Nix logic and raw dotfiles.
|
||||
- **`features/apps/`**: App-specific configs (e.g., `features/apps/btop/`, `features/apps/kitty/`), each containing their own `default.nix` and standalone `config/` directory mapped via Home Manager.
|
||||
- **`features/desktop/`**: Desktop environment components (e.g., Hyprland, Waybar).
|
||||
- **`features/scripts/utils/`**: Consolidated repository for all custom Nomarchy bash scripts, centrally packaged and injected into the user's `PATH` with correct dependencies.
|
||||
- **`themes/`**: The global theming engine. It holds pure color data and logic. *Theme-specific app layouts* (like a custom Waybar layout) are stored directly inside the app's feature folder, solving the matrix problem of theming.
|
||||
|
||||
---
|
||||
|
||||
## 📥 Installation
|
||||
|
||||
### 1. Try it in a VM (Recommended)
|
||||
Verify the experience without touching your hardware:
|
||||
```bash
|
||||
./bin/utils/nomarchy-test-installer
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────────┐
|
||||
│ state.json (single source of truth) │
|
||||
│ lives INSIDE your flake checkout, git-tracked │
|
||||
└───────────────────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
nomarchy-state-sync apply gruvbox
|
||||
1. merges the preset into the JSON (atomic write)
|
||||
2. runs `home-manager switch` (no sudo, no system rebuild)
|
||||
│
|
||||
▼ pure read (nomarchy.stateFile)
|
||||
┌──────────────────────────────────────────────────────────────────────────┐
|
||||
│ Home Manager bakes EVERYTHING into one read-only generation: │
|
||||
│ Hyprland (colors/gaps/borders) Waybar (palette or whole-swap) │
|
||||
│ Kitty (full ANSI palette) btop (asset or generated) │
|
||||
│ Stylix → GTK, Qt, cursors, fonts │
|
||||
└───────────────────────────────────┬──────────────────────────────────────┘
|
||||
▼
|
||||
wallpaper via awww (the one runtime piece:
|
||||
applied post-switch + at session start, `bg next` cycles)
|
||||
This builds a full graphical VM of the installer environment. Once inside, click the **Install Nomarchy** icon or run `nomarchy-install`.
|
||||
|
||||
### 2. Build the Installer ISO
|
||||
To install on physical hardware, generate your own bootable image:
|
||||
```bash
|
||||
./features/scripts/utils/nomarchy-build-iso # Minimal TTY installer
|
||||
./features/scripts/utils/nomarchy-build-live-iso # Graphical try-before-install
|
||||
```
|
||||
The ISO will be located at `./result/iso/nixos-*.iso`. Flash it to a USB drive and boot.
|
||||
|
||||
## 1. Layout
|
||||
|
||||
Flat on purpose. Two module trees, one options file each, no hidden layers.
|
||||
|
||||
```
|
||||
.
|
||||
├── flake.nix # inputs + the downstream API (exports below)
|
||||
├── lib.nix # nomarchy.lib.mkFlake — one-call downstream wrapper
|
||||
├── state.json # ★ THE single source of truth (git-tracked!)
|
||||
├── themes/ # 28 presets: <slug>.json + optional <slug>/ assets
|
||||
│ ├── nord.json # palette (required, works alone)
|
||||
│ └── nord/ # assets (optional, fixed filenames)
|
||||
│ ├── backgrounds/ # wallpapers (pinned nomarchy-wallpapers input, not this repo)
|
||||
│ ├── btop.theme # hand-made config drop (else generated)
|
||||
│ └── waybar.css # whole-swap: replaces the generated bar style
|
||||
├── modules/
|
||||
│ ├── nixos/ # the distro, system side
|
||||
│ │ ├── default.nix # Hyprland session, Pipewire, greetd, fonts
|
||||
│ │ ├── options.nix # nomarchy.system.* toggles
|
||||
│ │ ├── plymouth.nix # boot splash, tinted from the JSON
|
||||
│ │ ├── file-manager.nix # Thunar GUI + gvfs/tumbler/udisks2
|
||||
│ │ └── power.nix # power-profiles-daemon/TLP, thermald, charge limit
|
||||
│ └── home/ # the distro, user side
|
||||
│ ├── default.nix # entry point
|
||||
│ ├── options.nix # nomarchy.* option surface
|
||||
│ ├── theme.nix # JSON ingestion + wallpaper hook
|
||||
│ ├── stylix.nix # GTK/Qt/cursors/fonts from the same JSON
|
||||
│ ├── hyprland.nix # all JSON-driven
|
||||
│ ├── waybar.nix
|
||||
│ ├── kitty.nix
|
||||
│ ├── btop.nix
|
||||
│ ├── rofi.nix # launcher + nomarchy-menu (calc, emoji, clip…)
|
||||
│ ├── keybinds.nix # single source: Hyprland binds + SUPER+? sheet
|
||||
│ ├── swaync.nix # notifications, same JSON
|
||||
│ ├── idle.nix # hyprlock + hypridle, same JSON
|
||||
│ ├── battery-notify.nix # low-battery toasts at the bar's thresholds
|
||||
│ ├── yazi.nix # flagship TUI file manager + plugins
|
||||
│ ├── osd.nix # swayosd volume/brightness OSD
|
||||
│ ├── shell.nix # zsh + starship + bat/eza/zoxide
|
||||
│ ├── keys.nix # gpg-agent fronting SSH, pinentry-qt
|
||||
│ └── fastfetch.nix # themed Nomarchy logo + system info
|
||||
├── hosts/
|
||||
│ ├── default/ # reference machine (thin: boot, user, hostname)
|
||||
│ └── live.nix # bootable live ISO (try the distro, no install)
|
||||
├── pkgs/
|
||||
│ ├── nomarchy-state-sync/ # state writer + rebuild dispatcher (Python)
|
||||
│ ├── nomarchy-install/ # live-ISO installer (gum + disko + mkFlake)
|
||||
│ ├── nomarchy-doctor/ # read-only health sheet, one command per failure
|
||||
│ └── nomarchy-battery-notify/ # low-battery toast watcher backing battery-notify.nix
|
||||
├── templates/downstream/ # machine flake SoT (`flake init` + installer copy/patch)
|
||||
├── docs/ # human docs — map: docs/README.md
|
||||
│ ├── VISION.md # product themes toward v1.0 (not a queue)
|
||||
│ ├── ROADMAP.md # design/decision records + shipped log
|
||||
│ ├── HARDWARE.md # firmware, profiles, drivers
|
||||
│ ├── TESTING.md · RECOVERY.md · OVERRIDES.md · MIGRATION.md
|
||||
├── agent/ # agent instructions + loop state — map: agent/README.md
|
||||
│ # BACKLOG (executable queue), LOOP, VERIFICATION, …
|
||||
├── AGENTS.md # agent entry, any vendor (CLAUDE.md symlinks here)
|
||||
├── .claude/ # Claude Code adapter: permissions + subagent defs
|
||||
└── tools/ # maintainer-only
|
||||
├── import-palettes.py # converts old-distro themes → JSON + assets
|
||||
├── test-live-iso.sh # build the ISO + boot it in QEMU
|
||||
├── test-install.sh # full offline-install regression in QEMU
|
||||
└── vm/ # headless-VM helpers (QMP keys, VNC shots,
|
||||
# offline-pin gap analysis)
|
||||
### 3. Run the Installer
|
||||
Once booted into the Live environment, launch the installer:
|
||||
```bash
|
||||
nomarchy-install
|
||||
```
|
||||
The wizard will guide you through:
|
||||
- **Networking:** An interactive wizard to connect to Wi-Fi if needed.
|
||||
- **Hardware:** Optimized profiles for Dell XPS, Framework, Apple T2, and more.
|
||||
- **Storage:** Choice between Standard Ext4 or Encrypted BTRFS with optional **Impermanence**.
|
||||
- **Localization:** Searchable timezones and keyboard layout selection.
|
||||
|
||||
**Rule of thumb:** `modules/` is the distro (reusable, no machine specifics),
|
||||
`hosts/` is a machine, `themes/` is data, `pkgs/` is code, `tools/` is
|
||||
maintainer-only. If a new file doesn't obviously belong to one of those, it
|
||||
probably shouldn't exist.
|
||||
### Already on NixOS?
|
||||
Layer Nomarchy onto an existing 25.11 install without reformatting — see the [Migration Guide](docs/MIGRATION.md).
|
||||
|
||||
## 2. Try it first (live ISO)
|
||||
---
|
||||
|
||||
**Before you install:** check **[docs/REQUIREMENTS.md](docs/REQUIREMENTS.md)**
|
||||
(UEFI x86_64, disk planning for the Nix store + BTRFS snapshots — 128 GiB is
|
||||
the comfortable floor).
|
||||
## 🛠️ Configuration & Usage
|
||||
|
||||
Boot the full desktop from a USB stick or VM without installing anything:
|
||||
Nomarchy uses a "Downstream" model. After installation, your configuration lives in `/etc/nixos/` and is split into three main parts:
|
||||
|
||||
```sh
|
||||
nix build .#nixosConfigurations.nomarchy-live.config.system.build.isoImage
|
||||
# → result/iso/nomarchy-live-….iso — dd to a stick, or boot it in QEMU:
|
||||
tools/test-live-iso.sh
|
||||
```
|
||||
|
||||
The live session auto-logs-in, seeds the flake at `~/.nomarchy`, and pins
|
||||
the locked inputs into the ISO store — so theme switching (including the
|
||||
`home-manager switch` it triggers) works **offline**, exactly like on an
|
||||
installed system. Verification checklist: [docs/TESTING.md](docs/TESTING.md).
|
||||
|
||||
It's a working desktop, not a demo shell: Chromium, LibreOffice, Text Editor,
|
||||
Amberol and Snapshot are in the launcher alongside the terminal tooling — so
|
||||
you can read these docs in a browser while you try it, or boot the stick to
|
||||
rescue a machine that won't start and get a document off it. Installed
|
||||
machines get their app list from the template's `home.packages` instead, which
|
||||
is yours to edit.
|
||||
|
||||
Like what you see? **`nomarchy-install`** (in a terminal) walks you through
|
||||
installing to disk: pick a disk, LUKS2 full-disk encryption **by default**
|
||||
(in exchange the desktop logs in passwordless — the passphrase already
|
||||
gates the machine), user + hostname + timezone, hardware autodetection
|
||||
(DMI → nixos-hardware profile — see **[docs/HARDWARE.md](docs/HARDWARE.md)**),
|
||||
a hibernation-ready swapfile sized to RAM,
|
||||
then disko partitions (GPT + ESP + BTRFS subvolumes incl. `@snapshots` —
|
||||
snapper timeline snapshots are on) and `nixos-install` runs — **without a
|
||||
network** when the ISO was built from a clean tree (the target's
|
||||
`flake.lock` is composed from the rev the ISO carries). The installed
|
||||
machine gets the standard downstream layout: the flake at `~/.nomarchy`
|
||||
(`/etc/nixos` symlinks to it), one `mkFlake` call, your
|
||||
`system.nix`/`home.nix`. First boot lands in the fully themed desktop —
|
||||
the installer pre-activates the Home Manager generation. UEFI only for now.
|
||||
|
||||
## 3. Using Nomarchy on your machine (downstream)
|
||||
|
||||
Nomarchy is consumed as a flake input — you never fork or edit this repo:
|
||||
|
||||
```sh
|
||||
mkdir my-machine && cd my-machine
|
||||
nix flake init -t "git+https://git.bemagri.xyz/bernardo/nomarchy.git?ref=v1"
|
||||
```
|
||||
|
||||
The template's own README then walks the first-run steps: a real
|
||||
`hardware-configuration.nix`, `git init` (flakes only see tracked
|
||||
files), the two first rebuilds, and landing the checkout at
|
||||
`~/.nomarchy` so the theme CLI finds it. (The installer does all of
|
||||
this for you.)
|
||||
|
||||
Already running NixOS on this machine? No reinstall needed — adopt
|
||||
Nomarchy in place, reusing your existing `hardware-configuration.nix`:
|
||||
**[docs/MIGRATION.md](docs/MIGRATION.md)**.
|
||||
|
||||
You own two files day-to-day: `system.nix` and `home.nix` (plus
|
||||
`state.json`, written by the CLI). Your `flake.nix` is set up once —
|
||||
later by the installer — and never hand-edited; it's a single call:
|
||||
### 1. Distro Core (Upstream)
|
||||
Managed via the public Git repository. This is the engine. You should generally not modify files here.
|
||||
|
||||
### 2. Your System (`system.nix`)
|
||||
Add system-wide packages, services, and hardware tweaks here.
|
||||
*Example: Adding a persistent directory for Docker if using Impermanence:*
|
||||
```nix
|
||||
outputs = { nomarchy, ... }:
|
||||
nomarchy.lib.mkFlake {
|
||||
src = ./.;
|
||||
username = "me";
|
||||
hardwareProfile = "framework-13-7040-amd"; # optional, nixos-hardware name
|
||||
};
|
||||
environment.persistence."/persist" = {
|
||||
directories = [ "/var/lib/docker" ];
|
||||
};
|
||||
```
|
||||
|
||||
| `mkFlake` arg | Default | Purpose |
|
||||
|---|---|---|
|
||||
| `src` | — (required) | Your flake directory (`./.`) |
|
||||
| `username` | — (required) | Login name; flows into `system.nix` and names the HM config |
|
||||
| `hardwareProfile` | `null` | One [nixos-hardware](https://github.com/NixOS/nixos-hardware) module name, or a list of them (pinned + tested by Nomarchy; unknown names fail with suggestions) |
|
||||
| `system` | `"x86_64-linux"` | Platform |
|
||||
|
||||
(Power users can skip `mkFlake` and wire `nixosModules.nomarchy` /
|
||||
`homeModules.nomarchy` / `overlays.default` by hand — the wrapper is sugar.)
|
||||
|
||||
Two deliberately separate rebuild paths:
|
||||
|
||||
```sh
|
||||
sudo nixos-rebuild switch --flake .#default # system: rare
|
||||
home-manager switch --flake .#me # desktop: every theme change, no sudo
|
||||
### 3. Your User Environment (`home.nix`)
|
||||
Add user-level packages, aliases, and dotfiles here.
|
||||
*Example: Overriding the default terminal:*
|
||||
```nix
|
||||
nomarchy.home.terminal = "kitty";
|
||||
```
|
||||
|
||||
Day-to-day you'll use the shipped shortcuts instead:
|
||||
For the full list of `nomarchy.*` options you can set in `system.nix` and `home.nix`, see the [Options Reference](docs/OPTIONS.md). Hit a rebuild error? Check [Troubleshooting](docs/TROUBLESHOOTING.md). For where the project is heading next, see the [Roadmap](docs/ROADMAP.md).
|
||||
|
||||
```sh
|
||||
nomarchy-pull # nix flake update — refresh inputs (nomarchy, nixpkgs, …);
|
||||
# optional git pull only if ~/.nomarchy tracks a remote
|
||||
nomarchy-rebuild # system rebuild against the CURRENT lock (sudo inside;
|
||||
# BTRFS snapshot first when snapper is on; nvd diff after)
|
||||
nomarchy-home # home-manager switch — desktop only, no sudo, no lock bump
|
||||
# (prints nvd + a "What changed" toast when the generation moved)
|
||||
nomarchy-what-changed # plain-language last rebuild (system + desktop; --summary for one-liners)
|
||||
### Applying Changes
|
||||
After editing your files, apply them instantly. **IMPORTANT:** Nomarchy requires the `--impure` flag for evaluation. You **MUST** use the following aliases rather than standard NixOS commands:
|
||||
|
||||
```bash
|
||||
sys-update # Rebuilds the NixOS system (Runs: sudo nixos-rebuild switch --flake .#default --impure)
|
||||
env-update # Reloads your Home Manager environment (Runs: home-manager switch --flake .#default --impure)
|
||||
```
|
||||
|
||||
`~/.nomarchy` is **your machine flake** (`system.nix` / `home.nix`). Newer
|
||||
Nomarchy code arrives when `flake.lock` updates the `nomarchy` input
|
||||
(`nomarchy-pull`), not by `git pull` of that directory (many installs have
|
||||
no remote there — only local commits from auto-commit). With auto-commit
|
||||
on (menu: **System › Auto-commit**), all three commands above also sweep
|
||||
any pending hand edits into a commit before they switch, so
|
||||
`git -C ~/.nomarchy log` mirrors your generations.
|
||||
---
|
||||
|
||||
| When | Run |
|
||||
|---|---|
|
||||
| Pull newer Nomarchy / nixpkgs / inputs | `nomarchy-pull` |
|
||||
| You changed `system.nix` (or after pull) | `nomarchy-rebuild` |
|
||||
| You changed `home.nix` / theme / desktop | `nomarchy-home` |
|
||||
| Full upgrade (inputs + both layers) | `nomarchy-pull && nomarchy-rebuild && nomarchy-home` |
|
||||
## 🚀 Commands & Keybindings
|
||||
|
||||
**Order matters for distro updates.** `nomarchy-home` rebuilds against the
|
||||
**current** `flake.lock` — it never updates inputs. A new Nomarchy revision
|
||||
lands only when the lock is updated (`nomarchy-pull`). So for an upstream
|
||||
desktop change: **pull → rebuild → home**. Home before pull rebuilds against
|
||||
the old inputs and silently skips new home-side changes. After a home-side
|
||||
keybind/config change, also `hyprctl reload` (or relogin) so the session
|
||||
re-reads it.
|
||||
The full list lives in [`docs/KEYBINDINGS.md`](docs/KEYBINDINGS.md) (auto-generated from the Hyprland configs). A few highlights:
|
||||
|
||||
Legacy aliases still work: `sys-update` → pull+rebuild, `sys-rebuild` →
|
||||
`nomarchy-rebuild`, `home-update` → `nomarchy-home`.
|
||||
| Keybinding | Action |
|
||||
| :--- | :--- |
|
||||
| `Super + Space` | **App Launcher** (Walker) |
|
||||
| `Super + Shift + Space` | **Nomarchy Menu** (Walker) |
|
||||
| `Super + Alt + Space` | **Toggle Top Bar** (Waybar) |
|
||||
| `Super + Return` | Open Terminal |
|
||||
| `Super + Q` | Close Window |
|
||||
|
||||
Override anything via the `nomarchy.*` surface or plain NixOS/HM options:
|
||||
appearance (gaps/colors/fonts) changes through `nomarchy-state-sync`,
|
||||
behaviour (input/misc/monitor/chrome) is `mkDefault` so a plain `home.nix`
|
||||
assignment wins, and bind/exec-once lists concatenate. Full guide with
|
||||
examples: **[docs/OVERRIDES.md](docs/OVERRIDES.md)**.
|
||||
### Utility Scripts
|
||||
Nomarchy includes dozens of productivity scripts available in your PATH. Some highlights:
|
||||
- `nomarchy-sync push <repo>`: Backup your setup to Git.
|
||||
- `nomarchy-theme-bg-next`: Cycle to the next wallpaper in the current theme.
|
||||
- `nomarchy-menu`: The central hub for all utilities and pickers.
|
||||
|
||||
**Where each option goes.** `nomarchy.system.*`, `nomarchy.hardware.*`, and
|
||||
`nomarchy.services.*` are NixOS options — set them in `system.nix`. Everything
|
||||
else under `nomarchy.*` is a Home Manager option — set it in `home.nix`. The
|
||||
two tables below are split along exactly that line.
|
||||
|
||||
**`home.nix`** (Home Manager — the desktop):
|
||||
|
||||
| Option | Default | Purpose |
|
||||
|---|---|---|
|
||||
| `nomarchy.stateFile` | — (required) | Path to your state.json |
|
||||
| `nomarchy.terminal` | `"kitty"` | Terminal for keybinds and `$TERMINAL` (Kitty is the only shipped/themed emulator) |
|
||||
| `nomarchy.kitty.enable` | `true` | Nomarchy's Kitty config (palette/font/opacity from theme-state) |
|
||||
| `nomarchy.keyboard.layout` | `"us"` | XKB layout for the Hyprland session (installer writes the matching `services.xserver.xkb` in system.nix; the console + LUKS prompt follow via the distro default) |
|
||||
| `nomarchy.keyboard.variant` | `""` | XKB variant for the session |
|
||||
| `nomarchy.keyboard.devices` | `{}` | Per-device layout overrides (Hyprland `device` blocks keyed by `hyprctl devices` name) — e.g. an external keyboard with its own layout/variant |
|
||||
| `nomarchy.keyboard.layouts` | `[]` | Optional layouts to put first in the new-keyboard picker; the watcher runs by default, offers every installed XKB layout, and remembers the choice per-device. Pick/change one anytime at System › Keyboard |
|
||||
| `nomarchy.nightlight.enable` | `false` | Scheduled blue-light filter (hyprsunset) — warm at night (`.temperature`, default 4000K) between `.sunset`/`.sunrise`, no shift by day; set BOTH `.latitude`/`.longitude` for **geo mode** (wlsunset computes sunrise/sunset from your location; the fixed times are ignored); off by default — enable it from the menu (System › Night light; the first enable rebuilds), then toggle on/off instantly from the menu or the Waybar moon indicator (writes `settings.nightlight.on` in your flake, no rebuild) and it survives reboot, so it stays reproducible |
|
||||
| `nomarchy.updates.enable` | `false` | Passive update awareness: a background check (`.interval`, default daily) that flags when flake inputs (nixpkgs, the Nomarchy input, …) are behind upstream — and, with Flatpak on, when apps have updates (`.flatpak`) — via a Waybar indicator + notification. Never changes anything; click the indicator to run the upgrade flow |
|
||||
| `nomarchy.hyprland.enable` | `true` | Nomarchy's Hyprland config |
|
||||
| `nomarchy.waybar.enable` | `true` | Nomarchy's Waybar |
|
||||
| `nomarchy.rofi.enable` | `true` | Themed rofi launcher + `nomarchy-menu` dispatcher |
|
||||
| `nomarchy.swaync.enable` | `true` | swaync notifications, themed |
|
||||
| `nomarchy.batteryNotify.enable` | `true` | Low-battery toasts at the bar's thresholds — 25% low, 10% critical (stays up until dismissed); silent no-op on machines without a battery |
|
||||
| `nomarchy.dockAudio.enable` | `true` | On a fresh external-monitor plug, PipeWire/WirePlumber are reprobed after hardware settles and the highest-priority available HDMI/DP/USB sink becomes default, with a toast and journal result. A later manual speaker choice sticks until the next physical plug; unplug falls back to built-in. Manual route: System › Audio |
|
||||
| `nomarchy.firstBootWelcome.enable` | `true` | One dismissible “you're set” toast on the first session (SUPER+M / SUPER+T / SUPER+? + network pointer); marker is `settings.firstBootShown` in the flake checkout. Also fires at most one follow-up “Hardware tips” toast pointing at System › Firmware / Fingerprint when `fwupdmgr` / `fprintd-list` are on PATH (`settings.hardwareHintsShown`) |
|
||||
| `nomarchy.idle.enable` | `true` | hyprlock + hypridle (lock 5 min; display off 10 min in every dock/lid state — input wakes it compositor-side, #127; suspend 15 min battery-only via `nomarchy-suspend`) |
|
||||
| `nomarchy.idle.fingerprint` | `false` | Unlock the lock screen with a fingerprint as well as the password, and say so on the input field. Set it alongside `nomarchy.hardware.fingerprint.pam` in system.nix: hyprlock is configured from Home Manager, which cannot read the NixOS option — and hyprlock does **not** take a fingerprint through PAM at all (its PAM stack runs only on submit), so it uses its own fprintd backend that this switch turns on |
|
||||
| `nomarchy.yazi.enable` | `true` | yazi TUI file manager, themed + curated plugins |
|
||||
| `nomarchy.osd.enable` | `true` | swayosd on-screen display for volume/brightness/mute |
|
||||
| `nomarchy.shell.enable` | `true` | zsh + starship prompt + bat/eza/zoxide (zsh is the default login shell) |
|
||||
| `nomarchy.btop.enable` | `true` | btop with per-theme colors |
|
||||
| `nomarchy.stylix.enable` | `true` | GTK/Qt/cursor theming |
|
||||
| `nomarchy.fastfetch.enable` | `true` | fastfetch fronted by the themed Nomarchy logo |
|
||||
| `nomarchy.keys.enable` | `true` | gpg-agent fronting SSH + pinentry-qt |
|
||||
| `nomarchy.displays.enable` | `true` | nwg-displays interactive monitor arranger (helper for `nomarchy.monitors`) |
|
||||
| `nomarchy.viewers.enable` | `true` | Document/image viewers: zathura (Stylix-themed PDF) + imv |
|
||||
| `nomarchy.mime.enable` | `true` | Default "open with" associations (PDF/image/video/text/browser/directory); entries for apps you removed are skipped, so it degrades with the suite |
|
||||
| `nomarchy.monitors` | `[]` | Declarative per-output layout → Hyprland `monitor` rules (applied on hotplug); `,preferred,auto,1` wildcard kept as fallback |
|
||||
| `nomarchy.displayProfiles` | `{}` | Named layouts for the same outputs (docked/undocked/…), switched from System › Display › Profiles: instant via hyprctl, persisted in-flake (`settings.displayProfile`), baked over `nomarchy.monitors` at the next rebuild. Profiles that disable the laptop panel use the same safe ordering as Dock mode (external on → workspace handoff → internal off). The menu's Auto-switch row (`settings.displayProfileAuto`) applies the matching profile from Hyprland hotplug events. Workspace pins (`workspaces = { "1" = "DP-3"; }`) are moved instantly and baked as rules |
|
||||
| `nomarchy.launchOrFocus` | `[]` | Launch-or-focus binds: `SUPER+<key>` focuses the app's window (case-insensitive class match) or launches it; entries land in the SUPER+? cheatsheet, and a bind whose app was removed notifies instead of failing silently |
|
||||
| `nomarchy.themesDir` | Nomarchy's `themes/` | Where per-theme app overrides are probed |
|
||||
| `nomarchy.package` | overlay's `nomarchy-state-sync` | The theme/state tool package, overridable if you fork it |
|
||||
|
||||
**Greeter keyboard layout:** tuigreet runs on a kernel VT, which has exactly
|
||||
one keymap — `console.useXkbConfig` follows `services.xserver.xkb.layout`
|
||||
(`nomarchy.keyboard.layout`), not Hyprland's per-device `kb_layout` binding.
|
||||
Per-device layouts (`nomarchy.keyboard.devices`) only take effect once the
|
||||
Hyprland session starts, so at the login prompt an external keyboard types
|
||||
the **system** layout, not its remembered one — if your password comes out
|
||||
wrong there, type it as if on the system layout, or use the laptop's own
|
||||
keyboard. A VT constraint, not a bug.
|
||||
|
||||
**Always-on, no toggle by design:** `services.cliphist`, `services.udiskie`
|
||||
(automount + safe-removal toasts) and `services.easyeffects` (mic noise
|
||||
cancellation) ship unconditionally — small, low-risk pieces with no
|
||||
Nomarchy-specific config behind them (the toggle-vs-package rule in
|
||||
`agent/CONVENTIONS.md`). Disable one from `home.nix` with the plain HM
|
||||
option, e.g. `services.easyeffects.enable = lib.mkForce false;` — see
|
||||
**[docs/OVERRIDES.md](docs/OVERRIDES.md)**.
|
||||
|
||||
**`system.nix`** (NixOS — the machine):
|
||||
|
||||
| Option | Default | Purpose |
|
||||
|---|---|---|
|
||||
| `nomarchy.system.plymouth.enable` | `true` | Branded boot splash, background from the theme JSON (recolors on system rebuilds) |
|
||||
| `nomarchy.system.stateFile` | `null` | state.json for the system-side consumers (the Plymouth tint); `lib.mkFlake` wires it for you — set it only when composing the modules by hand |
|
||||
| `nomarchy.system.fileManager.enable` | `true` | Thunar GUI + gvfs/tumbler/udisks2 (the "open folder" handler) |
|
||||
| `nomarchy.system.greeter.enable` | `true` | greetd/tuigreet |
|
||||
| `nomarchy.system.greeter.autoLogin` | state | Auto-login this user into Hyprland. Owned by System › Auto-login (`settings.greeter.autoLogin`; installer seeds it on LUKS machines) — set it here only to pin it against the menu |
|
||||
| `nomarchy.system.audio.enable` | `true` | PipeWire stack; WirePlumber priority rules provide HDMI/USB preference/fallback, while the Home Manager dock watcher performs the fresh-display reprobe and explicit default selection |
|
||||
| `nomarchy.system.bluetooth.enable` | `true` | Bluetooth + blueman |
|
||||
| `nomarchy.system.autoTimezone.enable` | `false` | Automatic timezone (geoclue + automatic-timezoned) — the clock follows your location; toggle from System › Auto timezone (a menu enable rebuilds: it has to unset the static `time.timeZone`) |
|
||||
| `nomarchy.system.snapper.enable` | `false` | Hourly/daily BTRFS timeline snapshots + `nixos-rebuild-snap` (installer enables it; no-op unless root is BTRFS) |
|
||||
| `nomarchy.system.power.enable` | `true` | Active power management (see below) |
|
||||
| `nomarchy.system.power.backend` | `"ppd"` | `"ppd"` (power-profiles-daemon + menu/Waybar switcher) or `"tlp"` (deeper battery tuning, no switcher) — mutually exclusive |
|
||||
| `nomarchy.system.power.laptop` | `false` | Marks a laptop, gating battery-only features; the installer sets it when a battery is present. Clamshell uses logind's `HandleLidSwitchDocked=ignore`; the desktop additionally holds a low-level lid-switch inhibitor across dock/undock, releasing only after the internal panel is restored and the lid opens. Normal future undocked lid-close suspend remains enabled |
|
||||
| `nomarchy.system.power.thermal.enable` | `false` | thermald (Intel-only); the installer enables it on a GenuineIntel CPU |
|
||||
| `nomarchy.system.power.batteryChargeLimit` | `null` | Stop charging at this % (e.g. `80`) where the hardware supports it; needs `power.laptop` |
|
||||
| `nomarchy.system.power.suspendThenHibernate` | `true` (state) | On battery, suspend falls through to hibernate after **1 hour** (`suspend-then-hibernate` + `HibernateDelaySec=1h`; never while on AC). Needs a hibernate resume path (`boot.resumeDevice`). Toggle: System › Preferences › **Suspend then hibernate** (`settings.power.suspendThenHibernate`; system rebuild for logind lid policy). Idle and the Power menu use `nomarchy-suspend` (live state). |
|
||||
| `nomarchy.hardware.intel.enable` | `false` | Intel enablement above nixos-hardware (GuC/HuC firmware via `i915.enable_guc=3` — `.guc` toggles just that; the installer unsets it on `xe`-driver GPUs); the installer sets it on an Intel CPU/GPU |
|
||||
| `nomarchy.hardware.intel.computeRuntime` | `false` | Opt-in: Intel GPU compute — OpenCL/Level-Zero (`intel-compute-runtime`) + oneVPL (`vpl-gpu-rt`) |
|
||||
| `nomarchy.hardware.amd.enable` | `false` | AMD enablement above nixos-hardware (amd-pstate EPP + radeonsi VA-API, each toggleable via `.pstate` / `.vaapi`); installer-set on an AMD CPU/GPU |
|
||||
| `nomarchy.hardware.amd.rocm.enable` | `false` | Opt-in: ROCm HIP/OpenCL GPU compute (multi-GB); pair with `.gfxOverride` (e.g. `"11.0.0"`) for an unlisted iGPU |
|
||||
| `nomarchy.hardware.fingerprint.enable` | `false` | fprintd for a detected fingerprint reader (installer-set); enroll with `fprintd-enroll` |
|
||||
| `nomarchy.hardware.fingerprint.pam` | `false` | Opt-in: use the fingerprint for login + sudo (PAM) |
|
||||
| `nomarchy.hardware.fingerprint.parallel` | `true` | With PAM on: password *or* fingerprint at the same prompt (whichever comes first); `false` = stock sequential pam_fprintd. Password alone always stays sufficient |
|
||||
| `nomarchy.hardware.npu.enable` | `false` | Opt-in/experimental: load the on-die NPU driver (`amdxdna`/`intel_vpu`); userspace runtime is BYO |
|
||||
| `nomarchy.hardware.latestKernel` | `false` | Opt-in: ship `linuxPackages_latest` instead of the default kernel — for very new hardware whose drivers landed recently |
|
||||
| `nomarchy.hardware.camera.hideIrSensor` | `false` | Hide a dual-sensor webcam's IR node from PipeWire's **v4l2** path so apps only ever see the colour camera (the "second, dark Integrated Camera"); installer-set on a paired RGB+IR webcam. `/dev/video*` stays open, so Howdy-style face unlock still works; `.irMatch` overrides the IR-name regex. Does **not** hide IR from libcamera / portal / Flatpak pickers — see [HARDWARE.md §7](docs/HARDWARE.md) |
|
||||
| `nomarchy.hardware.i2c.enable` | `false` | I2C devices support — access to `/dev/i2c-*` (RGB controllers, sensors, DDC/CI monitor control) |
|
||||
| `nomarchy.hardware.i2c.ddcci` | `false` (distro default: **`true`**) | the ddcci-driver kernel module, exposing external monitors as standard backlight devices via DDC/CI so brightness keys and swayosd natively control them |
|
||||
| `nomarchy.services.tailscale.enable` | `false` | Opt-in: Tailscale mesh VPN — the login user is made the operator, so `tailscale up/down/set` and the System › VPN menu work without sudo |
|
||||
| `nomarchy.services.syncthing.enable` | `false` | Opt-in: Syncthing file sync as the login user (GUI at `127.0.0.1:8384`) |
|
||||
| `nomarchy.services.podman.enable` | `false` | Opt-in: rootless Podman (`docker` aliased to it) |
|
||||
| `nomarchy.services.flatpak.enable` | `false` | Opt-in: Flatpak + the Flathub remote |
|
||||
| `nomarchy.services.pika.enable` | `false` | Opt-in: Pika Backup (GUI Borg backups) |
|
||||
| `nomarchy.services.steam.enable` | `false` | Opt-in: Steam via `programs.steam` (32-bit libs, controller udev, Remote-Play ports) |
|
||||
| `nomarchy.services.libvirt.enable` | `false` | Opt-in: libvirt/KVM + virt-manager GUI (login user added to `libvirtd`) |
|
||||
| `nomarchy.services.obs.enable` | `false` | Opt-in: OBS Studio + a v4l2loopback virtual camera (selectable as a webcam in Zoom/Teams) |
|
||||
| `nomarchy.services.docker.enable` | `false` | Opt-in: rootful Docker (user in `docker` group); don't enable alongside podman |
|
||||
| `nomarchy.services.kdeconnect.enable` | `false` | Opt-in: KDE Connect phone integration (opens its firewall ports) |
|
||||
| `nomarchy.services.gamemode.enable` | `false` | Opt-in: Feral GameMode performance daemon (`gamemoderun`) |
|
||||
| `nomarchy.services.adb.enable` | `false` | Opt-in: Android adb/fastboot tools (systemd handles device udev rules) |
|
||||
| `nomarchy.services.wireshark.enable` | `false` | Opt-in: Wireshark Qt GUI (user in `wireshark` group, capture without root) |
|
||||
| `nomarchy.services.ollama.enable` | `false` | Opt-in: Ollama local LLM runtime on `127.0.0.1:11434` (CPU; GPU via `services.ollama.acceleration`) |
|
||||
| `nomarchy.services.printing.enable` | `false` | Opt-in: CUPS + Avahi/mDNS network printer discovery |
|
||||
| `nomarchy.services.openrgb.enable` | `false` | Opt-in: OpenRGB daemon for RGB peripheral/motherboard lighting |
|
||||
| `nomarchy.services.restic.enable` | `false` | Opt-in: scheduled daily restic backup (set `.repository` + `.passwordFile`; backs up `.paths`, default `/home`; 7/4/6 retention; list/restore via the `restic-nomarchy` wrapper) |
|
||||
|
||||
Beyond the `nomarchy.*` surface, the system layer turns on the usual
|
||||
desktop services with `lib.mkDefault` (override natively) and enforces
|
||||
baseline security/stability defaults. A few worth calling out:
|
||||
**`security.apparmor.enable`** is on by default for mandatory access
|
||||
control confinement. **Kernel panics** are configured to auto-reboot the
|
||||
machine after 10 seconds (`panic=10 oops=panic`) rather than hanging
|
||||
indefinitely. **`services.fwupd.enable`** is on by default for firmware
|
||||
updates via LVFS — it only refreshes metadata, never flashes on its own,
|
||||
so run `fwupdmgr update` to apply; disable with `services.fwupd.enable =
|
||||
false;` on machines without real firmware (VMs/headless). Full hardware
|
||||
story (profiles, fingerprint, NVIDIA, unsupported machines, contributing
|
||||
a DMI line): **[docs/HARDWARE.md](docs/HARDWARE.md)**. And
|
||||
**`services.earlyoom`** is on by default so running out of memory kills
|
||||
the offending process (with a desktop notification) instead of freezing
|
||||
the desktop — process-level on purpose, since a Hyprland session is one
|
||||
cgroup and systemd-oomd would kill all of it (oomd is disabled
|
||||
accordingly). Opt out with `services.earlyoom.enable = false;`.
|
||||
**`zramSwap`** is also on by default (zstd, 50% of RAM, priority 100):
|
||||
day-to-day memory pressure pages into compressed RAM instead of the
|
||||
disk, keeping the disk swapfile free for **hibernation** — the installer
|
||||
creates a RAM-sized encrypted swapfile and wires `resume`, so
|
||||
Power › Hibernate works out of the box (swap size 0 at install = no
|
||||
swap). Opt out with `zramSwap.enable = false;`; to add hibernation to a
|
||||
machine installed before it existed, follow
|
||||
**[docs/MIGRATION.md](docs/MIGRATION.md)** § Enabling hibernation.
|
||||
|
||||
## 4. How theming works
|
||||
|
||||
### Pure JSON ingestion
|
||||
|
||||
The trap with "read a mutable file from Nix" is pure evaluation: flakes
|
||||
cannot read arbitrary `$HOME` paths without `--impure` (the old prototype
|
||||
required it — never again). Nomarchy's convention: **the state file lives
|
||||
inside the consuming flake** and is wired via
|
||||
`nomarchy.stateFile = ./state.json;`. Reading it is pure — it's flake
|
||||
source. It must be git-tracked (`nomarchy-state-sync` runs
|
||||
`git add --intent-to-add` after every write as a safety net).
|
||||
|
||||
### One change = one generation
|
||||
|
||||
`nomarchy-state-sync apply <theme>` merges the preset into the JSON and runs
|
||||
`home-manager switch` (override the command with `$NOMARCHY_REBUILD`, or pass
|
||||
`--no-switch` to only write). Everything is baked: Hyprland, Waybar, Kitty,
|
||||
btop, and — via Stylix, mapped onto base16 roles — GTK, Qt, cursors and
|
||||
fonts. No runtime patching means no partial states, and `home-manager
|
||||
generations` is also your theme history. Waybar even restyles in place: it
|
||||
re-reads `style.css` when the symlink flips.
|
||||
|
||||
**Cleanup is automatic.** A weekly timer (`nomarchy-gen-prune`) removes
|
||||
**system** and **Home Manager** generations that are older than **14 days**
|
||||
*and* beyond the **three most recent past** gens — so you always keep the
|
||||
current generation plus at least three rollbacks, even on a rarely rebuilt
|
||||
machine. Manual: `sudo nomarchy-gen-prune --dry-run`. Full story:
|
||||
[docs/RECOVERY.md §4](docs/RECOVERY.md#4-how-generations-are-kept-and-cleaned-up).
|
||||
|
||||
The **wallpaper** is the one runtime piece (awww — nixpkgs' swww — is
|
||||
imperative; nothing in Nix consumes the path): applied at session start,
|
||||
after every switch via a tiny activation hook, and again when a monitor is
|
||||
hotplugged; cycled instantly with `bg next`.
|
||||
|
||||
### Config the menu writes (not just themes)
|
||||
|
||||
The in-flake-state model isn't only for appearance. **Feature toggles you flip
|
||||
from the menu are written into a `settings.*` section of the *same* state file**
|
||||
— git-tracked, reproducible, never stashed in `~/.local/state`. The menu is just
|
||||
an ergonomic writer for your flake, so version-controlling your downstream
|
||||
reproduces the machine, settings and all. Where a toggle can take effect without
|
||||
a rebuild it does: the menu writes the key (`--no-switch`) and flips the running
|
||||
service, which reads the *live* flake state at session start, so the choice is
|
||||
both instant and survives reboot.
|
||||
|
||||
**Night light** is the first to use this — enable it from the menu (System ›
|
||||
Night light; the first enable rebuilds to install hyprsunset), then on/off is
|
||||
instant. Internally it's two keys: `settings.nightlight.installed` (sticky —
|
||||
gates the unit, the first enable rebuilds) and `settings.nightlight.on` (the
|
||||
instant on/off). Expect more `nomarchy.*` toggles to migrate to this pattern.
|
||||
|
||||
**Auto-commit (opt-in):** System › Auto-commit makes every `apply`/`set`
|
||||
mutation also `git commit` state.json in your flake — *only* that
|
||||
file, so unrelated dirty work is never swept up — turning your settings
|
||||
history into `git log`. Off by default; the toggle is instant (nothing in
|
||||
Nix consumes the flag), the off-write is itself committed so history stays
|
||||
consistent, and wallpaper cycling (`bg next`) is deliberately excluded —
|
||||
the current wallpaper rides along with the next real commit.
|
||||
|
||||
### Per-theme app assets (`themes/<slug>/`)
|
||||
|
||||
Recoloring covers 95% of theming; the rest is one optional assets directory
|
||||
per theme — a single place to look, unlike the old distro's split:
|
||||
|
||||
| Asset | Mechanism |
|
||||
|---|---|
|
||||
| `backgrounds/` | wallpapers; empty `wallpaper` in the state means "first one"; `bg next` cycles. Pinned in a separate `nomarchy-wallpapers` flake input, not this repo (ROADMAP § "Faster switches") — merged in at build time |
|
||||
| `btop.theme` | baked into the generation (generated from the palette when absent) |
|
||||
| `waybar.css` | **whole-swap**: replaces the generated bar style entirely (probed at eval time, self-contained) |
|
||||
| `waybar.jsonc` | whole-swap for the bar *layout* (must be plain JSON) |
|
||||
| `rofi.rasi` | **whole-swap**: replaces the generated launcher/menu theme entirely |
|
||||
|
||||
Eight themes ship a `waybar.css` identity: the three Nomarchy-exclusive
|
||||
day/night pairs — boreal ↔ boreal-dawn, executive-slate ↔ executive-ivory,
|
||||
kiln ↔ kiln-clay — plus summer-night ↔ summer-day. Custom user themes can live in
|
||||
`$NOMARCHY_PATH/themes/` (preset lookup) and `nomarchy.themesDir` (eval-time
|
||||
asset probe) — including their own `backgrounds/`, which wins over the
|
||||
pinned `nomarchy-wallpapers` input (checked first); an explicit `wallpaper`
|
||||
path in `state.json` works too.
|
||||
|
||||
## 5. Day-to-day
|
||||
|
||||
```sh
|
||||
nomarchy-state-sync list # 28 presets (nord, gruvbox, rose-pine, …)
|
||||
nomarchy-state-sync apply kanagawa # whole desktop, one generation (~a switch)
|
||||
nomarchy-state-sync set ui.gapsOut 16 # tweak one knob (also a switch)
|
||||
nomarchy-state-sync bg next # cycle wallpapers — instant, no rebuild
|
||||
nomarchy-state-sync bg auto # back to the theme's default wallpaper
|
||||
nomarchy-state-sync get colors.accent
|
||||
nomarchy-pull # flake input update (no rebuild)
|
||||
nomarchy-rebuild # rebuild the system, current lock
|
||||
nomarchy-home # rebuild just the desktop layer
|
||||
nomarchy-doctor # read-only health sheet (also: menu › System › Doctor)
|
||||
```
|
||||
|
||||
Something broke anyway? Every rebuild is a generation and (on BTRFS)
|
||||
every hour is a snapshot — the undo story, from a bad theme to a
|
||||
machine that won't boot, is **[docs/RECOVERY.md](docs/RECOVERY.md)**.
|
||||
|
||||
Keybinds: `SUPER+Return` terminal · `SUPER+Space` launcher · `SUPER+T` theme
|
||||
picker · `SUPER+SHIFT+T` next wallpaper · `SUPER+X` power menu ·
|
||||
`SUPER+E` file manager (yazi) · `SUPER+N` notifications · `SUPER+CTRL+V`
|
||||
clipboard history · `SUPER+SHIFT+C` color picker (hyprpicker) · `SUPER+Q`
|
||||
close · `SUPER+1..9` workspaces · `SUPER+ALT+arrow` move workspace to monitor · `Print` region screenshot.
|
||||
|
||||
Shell aliases (zsh, gated on `nomarchy.shell.enable`) — `alias` lists them
|
||||
all; the curated set:
|
||||
|
||||
```sh
|
||||
# navigation
|
||||
.. ... .... # cd up 1 / 2 / 3 levels
|
||||
|
||||
# git
|
||||
g gst # git · git status -sb
|
||||
ga gaa # git add · git add -A
|
||||
gc gcm # git commit · git commit -m
|
||||
gco gsw gb # checkout · switch · branch
|
||||
gd gds # diff · diff --staged
|
||||
gl glg # log graph (last 20 · all branches)
|
||||
gp gpl gf # push · pull · fetch --all --prune
|
||||
|
||||
# nix (lifecycle: nomarchy-pull / nomarchy-rebuild / nomarchy-home — full names)
|
||||
ns nr # nix shell · nix run (e.g. ns nixpkgs#ripgrep)
|
||||
nfu nfc # nix flake update · check (prefer nomarchy-pull for day-to-day)
|
||||
nsearch ngc # nix search · nix-collect-garbage -d (prefer: sudo nomarchy-gen-prune)
|
||||
|
||||
# misc
|
||||
path # print $PATH, one entry per line
|
||||
reload # exec zsh (reload the shell)
|
||||
```
|
||||
|
||||
## 6. Extending
|
||||
|
||||
- **New theme:** drop a JSON into `themes/` (schema = any existing preset),
|
||||
plus an optional `themes/<slug>/` assets directory.
|
||||
- **New themed value:** add the key to `state.json` and consume it in
|
||||
the Nix modules. One place — there is no second renderer to keep in sync.
|
||||
- **Importing more old-distro palettes:**
|
||||
`tools/import-palettes.py <palettes-dir> themes/` (roles are first-class:
|
||||
when ANSI color0==color8 the tool derives an overlay step; light themes
|
||||
do not use dark ANSI black as surface — hand-tune after import; do not
|
||||
bulk-reimport shipped JSON without a hierarchy pass).
|
||||
- **New opt-in feature (convention):** when a feature is off by default and
|
||||
needs the user to set `nomarchy.*` options (e.g. night light, per-device
|
||||
keyboard layouts, monitor layout, power management), ship a **commented**
|
||||
example of it in `templates/downstream/home.nix` or `system.nix`. New
|
||||
installs then enable it by uncommenting + tweaking, rather than hunting the
|
||||
docs for the option name.
|
||||
- **Applications:** the starter complete-workstation set (LibreOffice, VS
|
||||
Code, GIMP, Inkscape) lives in the `home.packages` list in your
|
||||
`home.nix` — it's a plain package list, so curate it directly: delete a
|
||||
line to slim the machine, uncomment an extra (a browser, email, full TeX
|
||||
Live), or add your own. No `nomarchy.apps.*` toggles — a package list is
|
||||
already its own opt-out, so the distro doesn't impose these or wrap them.
|
||||
- **A newer individual app (unstable channel, #134):** Nomarchy carries a
|
||||
second, newer nixpkgs channel and exposes it as `unstable` through its
|
||||
overlay, so any package can be taken from it by prefixing `unstable.` in
|
||||
`home.packages` — e.g. `unstable.lmstudio` — with no second flake input of
|
||||
your own to manage. Three costs, stated plainly: (a) **you own this
|
||||
combination** — nothing upstream tests a pinned Nomarchy plus an unstable
|
||||
app together; (b) an unstable package brings its own second toolchain into
|
||||
your closure (measured: `unstable.lmstudio` shares only ~690 of ~4k store
|
||||
paths with the pinned set); (c) it moves whenever the flake lock moves
|
||||
(`nomarchy-pull`), not on its own schedule. Home packages only — that's
|
||||
the supported surface, not `system.nix` or the module set.
|
||||
|
||||
## Roadmap & known issues
|
||||
|
||||
| Doc | Role |
|
||||
|-----|------|
|
||||
| **[agent/BACKLOG.md](agent/BACKLOG.md)** | What to do next (agent queue) |
|
||||
| **[docs/VISION.md](docs/VISION.md)** | Product themes toward **v1.0** |
|
||||
| **[docs/ROADMAP.md](docs/ROADMAP.md)** | Design history + shipped log |
|
||||
| **[docs/README.md](docs/README.md)** · **[agent/README.md](agent/README.md)** | Maps |
|
||||
| **[agent/LOOP.md](agent/LOOP.md)** | Autonomous iteration protocol |
|
||||
|
||||
Kept out of the README body so this stays a focused entry point.
|
||||
|
||||
License: [MIT](LICENSE).
|
||||
---
|
||||
*Built with ❤️ using NixOS, Hyprland, Stylix, and the Nomarchy Community.*
|
||||
|
||||
353
agent/BACKLOG.md
353
agent/BACKLOG.md
@@ -1,353 +0,0 @@
|
||||
# Backlog — the prioritized task queue
|
||||
|
||||
**This is the only executable work list for agents.** Product themes and
|
||||
v1.0 intent live in [`docs/VISION.md`](../docs/VISION.md); design history
|
||||
in [`docs/ROADMAP.md`](../docs/ROADMAP.md); map in
|
||||
[`docs/README.md`](../docs/README.md) and [`agent/README.md`](README.md).
|
||||
|
||||
**Rules:**
|
||||
- Agents take the topmost actionable item (see LOOP.md). Finished items
|
||||
are **deleted** here — the journal + git log are the record; durable
|
||||
design notes get a ✓-entry in docs/ROADMAP.md (and/or a note in VISION)
|
||||
if worth keeping.
|
||||
- Item numbers are **stable IDs** — never renumbered or reused. A gap in
|
||||
the sequence means shipped (or dropped) work; new items take the next
|
||||
free number regardless of tier.
|
||||
- Tags: `[blocked:hw]` needs real hardware (see HARDWARE-QUEUE.md) ·
|
||||
`[human]` needs Bernardo · `[stuck]` two failed attempts, needs help ·
|
||||
`[big]` must be split before starting.
|
||||
- Agents may append to **PROPOSED** and **Decisions** freely (include
|
||||
`VISION § …` or `ROADMAP § …` when relevant); only Bernardo moves items
|
||||
*out* of PROPOSED into the tiers.
|
||||
|
||||
---
|
||||
|
||||
## NOW
|
||||
|
||||
### Live ISO / install hardware findings — Acer Aspire M5-481T + Dell XPS 9350
|
||||
|
||||
Bernardo, real installs 2026-07-13–14 (photos of the install end screens and
|
||||
post-boot sessions). Preserve separation: the installer bake failure and the
|
||||
flake pin are different root causes even when they show up on the same machine.
|
||||
(Terminal / Ghostty-on-Acer → shipped as Kitty-only, #95.)
|
||||
|
||||
## NEXT
|
||||
|
||||
### 151. `[human]` v1 launch plan — devise with Bernardo (includes the GitHub move)
|
||||
|
||||
Bernardo, 2026-07-17: before tagging v1, sit down and devise the launch plan.
|
||||
Known ingredients, so that session starts concrete:
|
||||
- **The repo moves to GitHub.** Everything baking the Gitea URL follows: the
|
||||
downstream template's `inputs.nomarchy.url`, the `nomarchy-wallpapers`
|
||||
input URL (baked 2026-07-17 — sweep it in the same move), README/docs
|
||||
links, and CI (eval CI runs on Gitea act_runner — decide GitHub Actions
|
||||
vs keeping Gitea CI on a mirror).
|
||||
- The move is the natural moment for the **history-rewrite decision**, and
|
||||
it is now measured (2026-07-17): the 107 MiB pack is 94.2 MB wallpaper
|
||||
blobs + 9.9 MB preview generations; all 848 commits of *text* pack to
|
||||
~15–20 MiB. So the history is not long-heavy, it is image-heavy.
|
||||
Recommendation on the table: **filter, don't reset** —
|
||||
`git filter-repo` stripping only `themes/*/backgrounds/` keeps every
|
||||
commit's diffs/blame/`log -S` (load-bearing: it diagnosed #147) at a
|
||||
~15–20 MiB clone. Cost: all SHAs change, and ROADMAP/journal cite them —
|
||||
mitigate by freezing the Gitea repo as a read-only archive (old SHAs
|
||||
resolve there; stash filter-repo's commit-map in it) + one ROADMAP note.
|
||||
Argue against a full mirror (ships 94 MB of deleted images to every
|
||||
clone forever) and against fresh-start (burns the archaeology to save
|
||||
~15 MiB).
|
||||
- The existing v1 bar (VISION § v1.0): HARDWARE-QUEUE burn-down, install P0
|
||||
re-verify before the `v1` fast-forward, visual ritual. LICENSE shipped
|
||||
(MIT, 2026-07-17).
|
||||
- Adjacent calls that may fold in: cachix/public binary cache (#120's
|
||||
prerequisite), the netinstall decision itself, release notes/announce.
|
||||
|
||||
`v1` stays human-only throughout — agents prepare evidence and checklists,
|
||||
never the tag or branch.
|
||||
|
||||
## LATER
|
||||
|
||||
- **Pre-built theme variants** (ROADMAP § Faster switches follow-on): if
|
||||
`home-manager switch` itself is still the theme-switch bottleneck now
|
||||
that the wallpapers split shipped (2026-07-17), pre-build each theme's
|
||||
generation so a switch just activates a cached one. Measure first.
|
||||
- **Installer round 2** (ROADMAP § Installer): multi-disk BTRFS RAID,
|
||||
impermanence, BIOS/legacy boot.
|
||||
- **Boot-from-snapshot**: a systemd-boot equivalent of grub-btrfs.
|
||||
- **MIPI/IPU software-ISP camera** support (no-UVC machines).
|
||||
- **NixOS release bump → v2** `[human]`: deliberate, hand-edited, never
|
||||
automated; the previous attempt was discarded (2026-06-22) over a
|
||||
Hyprland OOM blocker — see MEMORY.md before retrying.
|
||||
|
||||
## FUTURE (decided deferred — not the agent queue head)
|
||||
|
||||
Work we **intend** someday but explicitly **not** NEXT. Agents do not
|
||||
pick these unless Bernardo promotes one into NEXT/NOW.
|
||||
|
||||
### 20. KVM runner → VM suite in CI `[human]`
|
||||
**Status (2026-07-10):** keep **eval-only** CI on the current Gitea
|
||||
stack (act_runner in docker-compose on the 4c/4 GB IONOS VPS). Nested
|
||||
KVM + RAM headroom on that host are a poor fit next to Gitea; full
|
||||
`checks.*` VMs stay local / promotion-time until a **separate**
|
||||
KVM-capable machine exists.
|
||||
|
||||
**When ready:** register a second runner (host-mode nix + `/dev/kvm`,
|
||||
label `nix-kvm` — not the existing docker eval runner), then uncomment
|
||||
the `vm-checks` job in `.gitea/workflows/check.yml` (`runs-on: nix-kvm`,
|
||||
`nix flake check` + toplevel/HM builds). Do not enable the job until
|
||||
that label is online (Gitea queues forever otherwise).
|
||||
|
||||
### Formatter — adopt later `[human]`
|
||||
**Intent:** add a Nix formatter (likely `nixfmt-rfc-style`) in a dedicated
|
||||
pass: reformat the tree once, document in CONVENTIONS, optional CI
|
||||
check. **Not** the queue head — no drive-by reformats until that pass.
|
||||
|
||||
## PROPOSED (agent suggestions — await human triage)
|
||||
|
||||
*Agents: append here with a one-paragraph pitch (what/why/cost). Do not
|
||||
implement. Bernardo moves accepted items into a tier.*
|
||||
|
||||
*Open work only. Shipped exam/A–C items (#47–#63, #14, #52 theme
|
||||
high-ROI, etc.) live in the journal + ROADMAP — not here.*
|
||||
|
||||
### Product / day-2
|
||||
|
||||
### 153. Desktop widgets (clock / system stats) as an opt-in per-theme surface
|
||||
|
||||
Pitch (filed while shipping the three exclusive theme pairs, 2026-07-18):
|
||||
themed desktop widgets — a clock, calendar, or system-stats panel living on
|
||||
the wallpaper, conky-style — would deepen the identity themes (imagine
|
||||
kiln's copper stats panel or executive-ivory's letterhead clock). No such
|
||||
surface exists today, so it is a NEW theming surface: per CONVENTIONS it
|
||||
must consume the palette from the state JSON (no side pipeline) and ship
|
||||
as an opt-in `nomarchy.*` toggle with a commented template example. Engine
|
||||
choice is the real decision — conky is X11-era; a Wayland layer-shell
|
||||
widget system (eww or a small QML/GTK layer-shell tool) fits the stack
|
||||
better. Cost: moderate (engine spike + palette bridge + 2–3 widget
|
||||
layouts; start with the exclusive pairs only, not all 28 themes). Value:
|
||||
v1+ polish, not a v1 blocker.
|
||||
|
||||
### 146. `[watch]` hypridle hangs instead of exiting when its compositor vanishes
|
||||
|
||||
**Re-measured 2026-07-20 (dev box; user journal is persistent back to 2026-05-01,
|
||||
so this is the full window, not a fragment).** The linger fix landed between 17:25
|
||||
and 20:13 on 2026-07-16: the last zombie-shaped disconnect is the known one
|
||||
(17:25:39, PID 1698 — logout→greeter with **no** following `Stopped hypridle`), and
|
||||
**every** `Disconnected from pollfd id 1` since is a clean teardown bracketed by both
|
||||
a logind `Session N logged out` and a systemd `Stopped hypridle` within ≤11s
|
||||
(07-16 20:13:04, 07-17 17:04:38, 07-18 13:35:25, 07-19 16:46:49). **Genuine zombies
|
||||
post-fix: 0.** The live daemon is healthy too — PID 1707: `active`, `NRestarts=0`,
|
||||
`ExecMainPID`==pid, `ss` Recv-Q 0 on its D-Bus socket (reading, not the 48 KB-unread
|
||||
wedge), `WCHAN futex_do_wait`, logged activity <10 min before inspection. **Why this
|
||||
stays open a little longer, not closed today:** all four post-fix disconnects were
|
||||
*deliberate* logouts — no Hyprland *crash* (the zombie's actual trigger; the nearby
|
||||
coredumps were xdg-desktop-portal/swww/swayosd during teardown, not the compositor)
|
||||
has occurred since the fix, so the crash path is confirmed by argument (a crash ends
|
||||
the logind session too → no-linger manager teardown reaps hypridle) but not yet by an
|
||||
observed crash, and ~4 days is under the "few weeks" bar. **Close condition:** if
|
||||
genuine zombies stay 0 by ~2026-08-10 (≈3 weeks post-fix), close unbuilt; one wedged
|
||||
process (`ps` alive + `ss` Recv-Q high + `NRestarts=0` while `active`) reopens it as
|
||||
real work. Everything below is prior context, unchanged.
|
||||
|
||||
**Rewritten 2026-07-16, hours after filing: the premise was disproven and almost
|
||||
nothing survives.** As filed it said hypridle "dies silently ~2×/week — 19 times
|
||||
in 11 days", with Jul 14 as the clean example ("`CRITICAL` at 13:08:17, then
|
||||
eleven hours of silence"). Every number there was an artifact of the
|
||||
measurement. **18 of the 19** `Disconnected from pollfd id 1` lines are
|
||||
hypridle's noisy goodbye while systemd *deliberately stops it* — each bracketed
|
||||
by `Stopped hypridle` / `Started hypridle` with a healthy replacement seconds
|
||||
later. The "eleven hours of silence" came from grepping **one PID**: systemd
|
||||
logged `Stopped hypridle` 13:08:17, `Started hypridle` 13:09:00, and PID 1678
|
||||
ran fine all evening where the filter could not see it. Do not re-derive the old
|
||||
rate; it counts normal shutdown noise.
|
||||
|
||||
**What is real, and it is small.** Exactly one genuine zombie has been observed
|
||||
(2026-07-16 17:25:39), verified by process inspection rather than logs — `ps`
|
||||
(alive 9h36m), `ss` (48 KB unread on its D-Bus socket, i.e. not reading),
|
||||
`NRestarts=0` while `systemctl --user is-active` said `active (running)`. That
|
||||
is upstream hyprwm/hypridle#171 ("Possible deadlock in pollThr when compositor
|
||||
exits?", open since 2025-09, in neither 0.1.7 nor main) and it is a true bug:
|
||||
hypridle hangs rather than exits, so `Restart=always` never fires.
|
||||
|
||||
**A second real one joined it with #127's close (2026-07-17):** an
|
||||
inhibit-lock underflow (`BUG THIS: inhibit locks < 0: -1`, hypridle's own
|
||||
words, Jul 15 10:36:29) left the fired DPMS listener's `on-resume` permanently
|
||||
dead on 0.1.7 — the incident's actual mechanism (ROADMAP § "The idle brick's
|
||||
CAUSE found"). Reported upstream with the full trace 2026-07-17:
|
||||
https://github.com/hyprwm/hypridle/issues/208
|
||||
(#74/#104/#128 are the same class, closed before 0.1.7 evidently without
|
||||
killing it). Its impact path here is closed by the compositor-side wake fix
|
||||
(a2151f4); same watch posture — take the upstream fix when a release carries
|
||||
it.
|
||||
|
||||
**But its impact path is closed, which is why this is `[watch]` and not work.**
|
||||
The zombie only *persists* if systemd never stops the unit — which needed the
|
||||
user manager to survive logout, i.e. the stray pre-migration linger removed in
|
||||
#147. Without linger, logout tears the manager down and takes any zombie with
|
||||
it; the Jul 14 trace above shows the healthy path (`Stopped` → `Started`) doing
|
||||
exactly that. A Hyprland death ends the logind session anyway (2026-07-16:
|
||||
`Session 2 logged out` → greeter), so there is no known route left to a
|
||||
surviving zombie.
|
||||
|
||||
**Do before building anything:** re-measure. Count only disconnects with **no**
|
||||
`Stopped/Started hypridle` around them and **no** logind session event, and
|
||||
confirm by `ps`/`ss` that the process is really wedged — not by grepping a PID.
|
||||
If that count is zero over a few weeks post-linger, **close this unbuilt**. If it
|
||||
is not, the upstream issue has sat ten months on vaxerski's "that will cause a
|
||||
segfault no?" and a reproducer would be worth more than a local guard.
|
||||
|
||||
### 149. `[watch]` Waybar's exec-once workaround may be defending against nothing post-linger
|
||||
|
||||
Spun out of #147 (shipped 2026-07-17) so its loose thread survives the entry:
|
||||
`modules/home/hyprland.nix` runs Waybar from `exec-once` rather than a systemd
|
||||
unit because the unit "raced Hyprland's IPC on a **warm** relogin ... never
|
||||
retried, so the bar vanished" — and a warm relogin is exactly what linger
|
||||
created. With the stray linger removed (2026-07-16) that workaround may be
|
||||
defending against nothing. Do not rip it out on this note alone: it works, and
|
||||
the claim needs a real relogin without linger to test (V3 — the same relogin
|
||||
that verifies #148's watcher fix would answer it).
|
||||
|
||||
**2026-07-18 evidence: the warm relogin is real post-linger.** On the dev
|
||||
box the user manager survived a Hyprland logout (the tty1 greetd session
|
||||
keeps it alive — no linger involved) and the teardown broke:
|
||||
`graphical-session.target`'s stop transaction was rejected as destructive
|
||||
(easyeffects had a queued start job), the target stayed active,
|
||||
cliphist/swaync/portals/swayosd crash-looped against the dead Wayland
|
||||
socket into `start-limit-hit`, and the relogin's plain `start` was a no-op
|
||||
— doctor red for the whole session. Session bring-up now stops stale
|
||||
targets + `reset-failed` before starting (`hyprland.nix`
|
||||
systemd.extraCommands); relogin V3 check queued in HARDWARE-QUEUE. The
|
||||
exec-once posture this entry watches is therefore still earning its keep.
|
||||
|
||||
### 120. A netinstall ISO, next to the fat offline one
|
||||
|
||||
**Deferred to PROPOSED 2026-07-16 (Bernardo): not now.** Nothing below is
|
||||
stale — the measurements stand and the decision it needs is unchanged. It sits
|
||||
here because it is the only genuinely large item left, and because the gotcha
|
||||
at the bottom (no binary cache for our own derivations) probably makes a
|
||||
cachix the real first step, not the ISO.
|
||||
|
||||
Bernardo 2026-07-14, after seeing the measured size: **keep the current ISO
|
||||
exactly as it is** — the guaranteed offline install is the feature it buys —
|
||||
and ship a **much lighter netinstall variant alongside it**. Two products, one
|
||||
distro: "works on a plane" and "8 GiB is absurd to download" are both true, and
|
||||
a second target settles them without compromising either.
|
||||
|
||||
**Measured facts (2026-07-14), so this starts from numbers, not vibes.**
|
||||
*(These stand as measured: #121 would have cut ~0.67 GiB of duplicate chromium
|
||||
from them, but it was **reverted** — decided against, ROADMAP § one chromium,
|
||||
not two. If a netinstall ships, revisit it: the duplicate is worth ~195 MiB of
|
||||
**download**, which is this item's whole currency, even though it is worth
|
||||
almost nothing on disk or on the ISO.)*
|
||||
|
||||
> **Read this before using the numbers below.** They are **closure arithmetic**,
|
||||
> and #121 proved the hard way that closure size is neither disk size nor image
|
||||
> size: removing a 687 MiB path shrank the ISO by **8 KiB**, because
|
||||
> **mksquashfs dedupes duplicate files** and **`auto-optimise-store` hardlinks**
|
||||
> them on disk. So a change that looks like it sheds gigabytes of closure can
|
||||
> shed nothing off the actual image. **Measure the artifact — build the ISO and
|
||||
> `stat` it.** The corollary cuts the other way and is the good news for this
|
||||
> item: what dedupe cannot help is the **wire**, so a netinstall's download is
|
||||
> the one figure closure/NAR size predicts honestly (`nix path-info --store
|
||||
> https://cache.nixos.org --json` gives the real `downloadSize`).
|
||||
- Current ISO **8.078 GiB** compressed; **18.03 GiB** of store uncompressed
|
||||
(`zstd -19`, 2.23:1 — compression is already near-max, not the lever).
|
||||
- The offline pin (`system.extraDependencies`, 60 roots: a representative
|
||||
installed system + the template HM closure + all flake inputs) is **4.02 GiB
|
||||
uncompressed of that — only ~22%**. Dropping it entirely still leaves a
|
||||
**~13.3 GiB** desktop → roughly **6 GiB** compressed at the same ratio.
|
||||
**So "no pin" alone is NOT the lighter ISO** — this is the trap to avoid.
|
||||
- The desktop's own top weights: libreoffice 1457 MiB, initrd 1369,
|
||||
linux-firmware 770, chromium 1391 (two builds — #121, left in), llvm-lib 540,
|
||||
bibata-cursors 322, mesa 264, mbrola-voices 259, nerd-fonts ~420 combined.
|
||||
Note what that list implies: no single lever gets a desktop ISO under ~4 GB —
|
||||
which is the case for (b) below.
|
||||
|
||||
**So the real decision is what a netinstall ISO IS**, and it should be settled
|
||||
first (`[human]`): (a) the full try-before-install desktop minus the pin
|
||||
(~6.3 GiB — barely lighter, probably not worth a second target); (b) a **TUI
|
||||
installer only, no desktop** (~1 GiB, the actual "netinstall" in the Debian
|
||||
sense) which drops "try before install" from that medium — the fat ISO still
|
||||
offers it; (c) a middle desktop (no libreoffice/chromium — but note #103 just
|
||||
put those there deliberately, and a *demo* desktop that can't browse is the
|
||||
bug #103 fixed).
|
||||
|
||||
**The gotcha that decides feasibility:** without the pin, a netinstall target
|
||||
fetches from `cache.nixos.org` for stock nixpkgs paths — but **Nomarchy's own
|
||||
derivations are in no binary cache**, so they would build *from source on the
|
||||
user's machine* during install. That is the same failure `tools/vm/gap-analysis.py`
|
||||
exists to diagnose (and #113 is a live instance of). So this item probably
|
||||
depends on a public binary cache (cachix) for the flake's own outputs, or it
|
||||
trades an 8 GiB download for a 40-minute install. Establish that before
|
||||
building the target.
|
||||
|
||||
Pass = a second, documented ISO target that is *substantially* smaller (state
|
||||
the measured number, both ISOs built from one tree), installs successfully with
|
||||
a network in a QEMU run, says clearly at boot that it needs one, and leaves the
|
||||
offline ISO's behaviour untouched (`checks.*` for the offline path stay green).
|
||||
- **NVIDIA first-class options** — **deferred past v1** (Bernardo
|
||||
2026-07-10). Keep #59 commented install guidance; no
|
||||
`nomarchy.hardware.nvidia.*` until a hybrid maintainer + queue.
|
||||
|
||||
_(#80–#83 + #85–#88 shipped 2026-07-11. Theme A day-2 + neon-glass finish
|
||||
shipped — VISION ✓. Dock/hibernate V3 → HARDWARE-QUEUE. Parallel
|
||||
fingerprint-or-password shipped 2026-07-12 (Bernardo promoted it live;
|
||||
`fingerprint.parallel`, pam-fprint-grosshack) — reader V3 →
|
||||
HARDWARE-QUEUE.)_
|
||||
|
||||
### v1.0 pointer
|
||||
|
||||
See **VISION**. Open PROPOSED: NVIDIA deferred past v1; IR portal (b)/(c)
|
||||
need T14s (HARDWARE-QUEUE § T14s). Standing calls: browser = Chromium;
|
||||
power = PPD. (Post-install hardware hints shipped 2026-07-17.)
|
||||
|
||||
## Decisions `[human]`
|
||||
|
||||
Open calls only Bernardo can make; agents add options/evidence but never
|
||||
decide. **Resolved** entries stay for history; agents treat them as closed.
|
||||
|
||||
### Resolved (2026-07-17)
|
||||
|
||||
- **#143 rofi refilter highlight** — the 07-16 "wait for upstream" ruling
|
||||
was priced against carrying a patch; the maintainer's reply on
|
||||
rofi#2317 pointed at the `inputchange {}` config block instead. Adopted
|
||||
same day as pure config (`kb-row-first` on query edits, generated
|
||||
config.rasi) — no patch, no source build, watch closed
|
||||
(ROADMAP § Rofi highlight).
|
||||
- **#120 netinstall** — stays deferred in PROPOSED (re-affirmed). When it
|
||||
is promoted, a public binary cache (cachix) is the likely first step.
|
||||
- **#134 unstable packages** — build it: Nomarchy carries the
|
||||
`nixpkgs-unstable` input, `unstable.*` via the overlay, home-scope only.
|
||||
Shipped the same day (ROADMAP § `unstable.<pkg>` in the downstream).
|
||||
- **#114 greeter layouts** — document only: a VT has one keymap by design,
|
||||
so tuigreet cannot honour per-device layouts. Shipped as the README
|
||||
"Greeter keyboard layout" note + a RECOVERY.md pointer; entry deleted.
|
||||
|
||||
### Resolved (2026-07-10)
|
||||
|
||||
- **Docs site vs Markdown-in-repo** — **markdown in-repo for now**
|
||||
(`docs/`, README). A rendered docs site is FUTURE if wanted.
|
||||
- **Default browser** — **ship Chromium** in
|
||||
`templates/downstream/home.nix`; mime → `chromium-browser.desktop`.
|
||||
Opt out: delete the line / override mime.
|
||||
- **Default power backend** — **keep PPD** (`nomarchy.system.power.backend`
|
||||
default). TLP remains the one-line opt-in. Rationale: stability + live
|
||||
profile API for menu/Waybar; Omarchy’s TLP experiment reverted.
|
||||
|
||||
### Resolved (2026-07-10, more)
|
||||
|
||||
- **Formatter adoption** — **yes, but not now.** Tracked as FUTURE
|
||||
(below). Nix-source style only (`nixfmt-rfc-style` or similar); one
|
||||
bulk reformat + CI/check when promoted. Until then: hand-aligned
|
||||
style per CONVENTIONS.
|
||||
|
||||
- **Hibernation** — **want by default** (product intent). Needs a
|
||||
disk-backed swap (file or partition) sized for resume; not zram alone.
|
||||
**Shipped as #76**; V3 power-cycle PASSED on TuringMachine 2026-07-12
|
||||
(ROADMAP § Hibernation + zram by default).
|
||||
|
||||
### Resolved (2026-07-10, #76 design)
|
||||
|
||||
- **Swap sizing** — **exactly RAM** (installer default, unchanged). Hibernate
|
||||
image ≤ RAM; zram takes day-to-day paging. **`swapSize=0`** stays no-swap.
|
||||
- **Migration** — **docs runbook** (`docs/MIGRATION.md`), not a tool.
|
||||
- **No-swap Hibernate** — keep the menu row; **notify on failure**.
|
||||
@@ -1,75 +0,0 @@
|
||||
# Conventions — how Nomarchy code is written
|
||||
|
||||
The standing rules an agent must follow while coding. GOALS.md says what
|
||||
we're building; this says how. Details/rationale live in docs/ROADMAP.md's
|
||||
decision records and the README.
|
||||
|
||||
## Repo layout (the rule of thumb)
|
||||
`modules/` is the distro (reusable, no machine specifics) · `hosts/` is a
|
||||
machine · `themes/` is data · `pkgs/` is code · `tools/` is maintainer-only
|
||||
· `agent/` is loop state. If a new file doesn't obviously belong to one of
|
||||
those, it probably shouldn't exist.
|
||||
|
||||
## Nix style
|
||||
- **No formatter.** Files use deliberate aligned hand-formatting — match
|
||||
the surrounding style exactly; never reflow a file you're only touching
|
||||
a line of.
|
||||
- Distro defaults use `lib.mkDefault` so a plain downstream assignment
|
||||
wins; bind/exec lists concatenate. Behaviour options overridable,
|
||||
appearance flows from the state JSON.
|
||||
- Options live in the existing surfaces: `nomarchy.system.*` /
|
||||
`nomarchy.hardware.*` / `nomarchy.services.*` (NixOS, `system.nix`),
|
||||
everything else `nomarchy.*` (HM, `home.nix`). Update the README tables
|
||||
when the surface changes.
|
||||
|
||||
## Feature design
|
||||
- **In-flake state:** any user-settable config gets a menu writer that
|
||||
lands it in `state.json` (`settings.*`), git-tracked. No
|
||||
`~/.local/state`, no side files. Instant-effect where possible
|
||||
(`--no-switch` + flip the service; the service reads the *live* working
|
||||
tree at start — the night-light `ExecCondition` pattern). Rebuild-baked
|
||||
values graduate via `mkDefault` reads of the settings key.
|
||||
- **Toggle vs package:** a `nomarchy.*` toggle only when there's real
|
||||
config behind it (units, groups, udev, firewall). A bare package goes
|
||||
in the downstream template's `home.packages` — opt-out is deleting the
|
||||
line.
|
||||
- **Opt-in features** ship a commented example in
|
||||
`templates/downstream/home.nix` or `system.nix`. That template is the
|
||||
single source of truth for machine files: `nomarchy-install` copies it
|
||||
and patches install-time values only (never a thinner second catalog).
|
||||
- **Menu:** new entries go in the right submenu (Tools › / System ›; root
|
||||
stays six entries), end lists with the shared `↩ Back`, self-gate on
|
||||
the feature's availability, and add the direct
|
||||
`SUPER+CTRL+<mnemonic>` bind in `keybinds.nix` (single source — it
|
||||
feeds both Hyprland and the SUPER+? cheatsheet).
|
||||
- **Waybar:** new indicators self-gate (hidden when irrelevant), use
|
||||
named `writeShellScriptBin`s on PATH (so static configs can exec them
|
||||
by bare name), and are added to **both** the generated `waybar.nix`
|
||||
config **and** every `waybar.jsonc` whole-swap — summer-day, summer-night,
|
||||
executive-slate, executive-ivory, boreal, boreal-dawn, kiln and
|
||||
kiln-clay (the parity rule).
|
||||
- **Theming:** every new visual surface consumes the palette from the
|
||||
state JSON. There is no second renderer to keep in sync — add the key
|
||||
to the JSON, consume it in the module.
|
||||
|
||||
## Testing
|
||||
- docs/TESTING.md is canonical; LOOP.md's ladder (V0–V3) sets the
|
||||
required tier. Cheap first: `nix flake check --no-build`, `bash -n`,
|
||||
`py_compile`.
|
||||
- Prefer a permanent `checks.*` runNixOSTest over a one-off manual poke;
|
||||
reusable recipes (headless Hyprland with software GL, QMP screenshots,
|
||||
udev-event fakes) are indexed in MEMORY.md and demonstrated by the
|
||||
existing checks (`distro-id`, `hardware-toggles`,
|
||||
`battery-charge-limit`).
|
||||
- The honesty rule: report exactly what you verified and at which tier.
|
||||
|
||||
## Git
|
||||
- `main` is development (direct commits, pushed); `v1` is the release
|
||||
pointer — **human-only, fast-forward-only, never touched by agents**.
|
||||
- Commit style: `feat|fix|test|docs|chore(scope): summary`, body with
|
||||
what/why + verification tier + what remains. Bookkeeping (`agent/`
|
||||
updates) rides in the same commit as the change.
|
||||
- `flake.lock` moves only when the task *is* a lock bump. The release
|
||||
inputs stay within their pinned release branches; the `nixpkgs-unstable`
|
||||
input (#134, feeds `unstable.*`) tracks `nixos-unstable` and bumps
|
||||
together with the rest — never alone, never `nix flake update`.
|
||||
@@ -1,112 +0,0 @@
|
||||
# Delegation — capability tiers, roles, token economy
|
||||
|
||||
How to spend model capacity in this repo, for **any** agent harness.
|
||||
Tasks are matched to capability *tiers*, not vendor model names; each
|
||||
harness maps tiers to its own models (table at the bottom). GOALS says
|
||||
what to build, CONVENTIONS how to write it — this says who does which
|
||||
part.
|
||||
|
||||
## The dividing line: evidence vs judgment
|
||||
|
||||
Cheap models gather evidence; they never make verification claims or
|
||||
design decisions. The following always stay on the **frontier** tier and
|
||||
are never delegated downward:
|
||||
|
||||
- deciding what to test and the regression scope (VERIFICATION.md)
|
||||
- interpreting an ambiguous or flaky failure
|
||||
- viewing screenshots and judging visual quality — aesthetic judgment is
|
||||
exactly what small models do badly, and it's load-bearing here
|
||||
- writing non-trivial Nix (module structure, overlays, cross-cutting
|
||||
refactors)
|
||||
- the final diff review and report
|
||||
|
||||
Product calls ("finish vs quarantine", promoting PROPOSED items) sit
|
||||
above even that: they belong to the human.
|
||||
|
||||
## Tiers
|
||||
|
||||
| Tier | Best for | Delegate to it? |
|
||||
|------|----------|-----------------|
|
||||
| **light** | bulk mechanical: search, summarize, audit sweeps, running the harness | freely — never for judgment |
|
||||
| **standard** | well-specified scoped edits, routine research | when the spec is already written |
|
||||
| **frontier** | design, novel code, ambiguous failures, taste | this is the loop's own tier |
|
||||
|
||||
Reasoning effort: default moderate; go maximum only for the hardest
|
||||
calls. A frontier parent may spawn a frontier child for one hard call.
|
||||
Work above your tier gets returned, not attempted.
|
||||
|
||||
## Standing roles
|
||||
|
||||
Two mechanical roles exist for any harness that supports subagents
|
||||
(Claude Code implementations: `.claude/agents/`; other harnesses
|
||||
implement the same contracts in their own format):
|
||||
|
||||
- **scout** (light, read-only): locate where things are defined, map
|
||||
which files touch a subsystem, scan build logs / serial output for
|
||||
error lines, docs-vs-code drift sweeps. Reports facts with paths and
|
||||
line numbers, quotes the minimum snippet, says "not found" plainly —
|
||||
never guesses, never recommends.
|
||||
- **runner** (light, executes): builds, VM boots, screenshot capture,
|
||||
the scripted `tools/` checks. Headless and unattended, every wait
|
||||
bounded by a timeout. Returns commands, exit codes, wall time, and
|
||||
artifact paths — never marks anything passed or verified; the caller
|
||||
makes the verification claim.
|
||||
|
||||
Use them for pure information-gathering or pure execution instead of
|
||||
pulling bulk (log files, wide scans) into the main context. When a
|
||||
result surprises you, spot-check it yourself before building on it —
|
||||
cheap models are allowed to be wrong about hard things, which is
|
||||
precisely why they're not allowed to make claims.
|
||||
|
||||
## Economy rules
|
||||
|
||||
- **Only delegate when writing the spec is cheaper than doing the
|
||||
work** — a spawned agent starts cold and must re-derive context. A
|
||||
one-file read is cheaper done directly.
|
||||
- **Brief every child cold; point at the spec, don't restate it.** "The
|
||||
spec is in `agent/BACKLOG.md` #NN — implement it" plus only the
|
||||
*constraints* (scope files, branch, no-VM, no-push).
|
||||
- **Match the tier to the task, not the prestige.** An item whose spec
|
||||
is already written (exact files, exact fixes) is standard-tier work;
|
||||
reserve frontier children for genuine multi-step reasoning. This is
|
||||
the single biggest saving.
|
||||
- The strong model writes the spec, reviews the result, and owns the
|
||||
commit.
|
||||
|
||||
## Fanning out parallel work (V0/V1, no VM)
|
||||
|
||||
When several NEXT items are independent and don't need the VM, spread
|
||||
them across worktree-isolated subagents in parallel:
|
||||
|
||||
- **Disjoint file lanes.** Partition items so no two agents touch the
|
||||
same file (map the touched files first). If two items must share a
|
||||
file (README, flake.nix, rofi.nix), give both to one agent or keep
|
||||
one for yourself.
|
||||
- **Isolation + you own landing.** Each agent works in an isolated
|
||||
worktree, commits to its own branch, and **never pushes or touches
|
||||
`main`/`v1`**. You review each diff, cherry-pick onto `main`, and do
|
||||
the bookkeeping — a single landing agent can't race itself. Clean up
|
||||
worktrees and branches after landing.
|
||||
- **Lean on scriptable checks as primary evidence.** Where a
|
||||
deterministic `tools/` check or `checks.*` guard already proves the
|
||||
property, that near-free run *is* the V0/V1 evidence.
|
||||
- **Batch V2 at the end, once.** Delegated visual/behavioural items come
|
||||
back "V2 pending"; collect the landed changes and do **one** VM pass
|
||||
covering all of them — the VM render + screenshot review is the most
|
||||
expensive step in the loop; amortise it.
|
||||
- **Re-verify on `main`, but leanly.** After landing, confirm the
|
||||
agent's V0/V1 on the merged tree with a targeted build, not a full
|
||||
re-run. Trust-but-spot-check scales; blind re-running doesn't.
|
||||
|
||||
The judgment list above still holds: *you* review every diff before it
|
||||
lands.
|
||||
|
||||
## Per-harness model mapping
|
||||
|
||||
| Tier | Claude Code |
|
||||
|------|-------------|
|
||||
| light | `haiku` |
|
||||
| standard | `sonnet` |
|
||||
| frontier | the session's top model (`opus` and up) |
|
||||
|
||||
Other harnesses: add a column when one is actually used on this repo.
|
||||
@@ -1,60 +0,0 @@
|
||||
# Goals — what "done" looks like for Nomarchy
|
||||
|
||||
The north star every loop iteration serves. When two options conflict,
|
||||
the earlier pillar wins. Product *themes* toward a **v1.0** ship (day-2
|
||||
confidence, default identity, release bar) live in
|
||||
[`docs/VISION.md`](../docs/VISION.md) — still subordinate to these pillars.
|
||||
|
||||
## The four pillars (in priority order)
|
||||
|
||||
1. **Rock-stable.** A workstation you never fight. Everything is a NixOS/HM
|
||||
generation — atomic, rollbackable, never partial. `nix flake check` is
|
||||
green at every commit on `main`. Regressions are caught by the VM-test
|
||||
suite (`checks.*`), not by users. `v1` only ever advances after human
|
||||
on-hardware QA.
|
||||
2. **Reproducible, with zero hidden state.** The downstream flake checkout
|
||||
*is* the machine. All user-settable config is menu-writable into the
|
||||
git-tracked state file (`state.json` `settings.*`) — never
|
||||
`~/.local/state`, never `~/.config` side files. Re-cloning your flake
|
||||
reproduces the machine, settings and all.
|
||||
3. **Effortless to configure.** The user never has to learn Nix. Every
|
||||
common knob is reachable from `nomarchy-menu` (SUPER+M); the menu is an
|
||||
ergonomic writer for the flake. Where a toggle can take effect without a
|
||||
rebuild, it must (`--no-switch` + flip the running service).
|
||||
4. **Beautiful.** One JSON themes the entire desktop coherently — Hyprland,
|
||||
Waybar, Kitty, btop, rofi, GTK/Qt, boot splash, greeter. Every new
|
||||
surface follows the palette. Informative, self-gating Waybar modules
|
||||
(they hide when irrelevant). No unthemed corner survives contact with
|
||||
the theme switcher.
|
||||
|
||||
## Quality bars (non-negotiable)
|
||||
|
||||
- **The honesty rule** (docs/TESTING.md): for anything visual, "the Nix
|
||||
evaluates" is not "it renders". Verify at the highest tier you can reach
|
||||
(see LOOP.md's verification ladder) and *state the tier you reached*.
|
||||
Never claim a check you didn't run.
|
||||
- **Parity rule:** any module added to the generated Waybar config must
|
||||
also be added to the summer-day/night `waybar.jsonc` whole-swaps.
|
||||
- **Menu placement:** new menu entries go in the right category submenu
|
||||
(Tools › / System ›), with a direct `SUPER+CTRL+<mnemonic>` bind and
|
||||
self-gating where applicable. The root picker stays six entries.
|
||||
- **Opt-in features** ship a commented example in
|
||||
`templates/downstream/{home,system}.nix`.
|
||||
- **Option surface discipline:** a toggle exists only when there is real
|
||||
config behind it. Bare package installs go in the template's
|
||||
`home.packages` (opt-out = delete the line), never a `nomarchy.apps.*`.
|
||||
|
||||
## Non-goals (do not drift into these)
|
||||
|
||||
- **No binary cache.** Compile-from-source is a deliberate values call;
|
||||
automation targets the config/lock channel, not artifact distribution.
|
||||
- **No second theming pipeline.** The dispatcher owns menu structure; the
|
||||
renderer (rofi) stays swappable. No GTK4 launcher.
|
||||
- **No nixpkgs major bump on `main`.** Lock updates stay within the pinned
|
||||
release branch; a release jump is a deliberate `v2`, hand-edited by the
|
||||
maintainer (the last attempt was discarded over a Hyprland OOM — see
|
||||
agent/MEMORY.md).
|
||||
- **No repo-wide reformat.** The `.nix` files use deliberate aligned
|
||||
hand-formatting; adopting a formatter is an open maintainer decision
|
||||
(BACKLOG.md § Decisions), not a cleanup.
|
||||
- **No multi-DE.** Hyprland is the desktop.
|
||||
@@ -1,657 +0,0 @@
|
||||
# Hardware queue — V3 checks only a human can run
|
||||
|
||||
Everything shipped at V1/V2 whose final verification needs real hardware.
|
||||
Agents **append** (newest at the bottom of a section) with exact steps;
|
||||
Bernardo runs them and either checks off (`[x]` + date + verdict) or files
|
||||
the failure as a NOW bug in BACKLOG.md. Checked-off entries are **pruned**
|
||||
on the next sync sweep (LOOP.md §5) — outcomes live in the journal/ROADMAP;
|
||||
git history is the archive. Machines: the **AMD dev box** (Ryzen AI
|
||||
laptop: AMD + fingerprint + NPU), the **Latitude 5310/5410** (Intel QA),
|
||||
the **T14s** (webcam case).
|
||||
|
||||
## Suggested order (sweep 2026-07-15)
|
||||
|
||||
Run these first when you have a laptop session (AMD dev box unless noted):
|
||||
|
||||
| # | Item | Why first |
|
||||
|---|------|-----------|
|
||||
| 1 | **#115** suspend-then-hibernate | Just shipped; quick path is minutes (battery vs AC + `nomarchy-suspend`) |
|
||||
| 2 | **#127** the brick's *cause* (reopened) | Wake path fixed + fully verified 2026-07-16; cause unknown — start at the dead Ctrl+Alt+F3, not the wake path |
|
||||
| 3 | **Docking recovery round 8** + headphone jack-follow | Same dock session; relogin once, then undock ×5 + jack |
|
||||
| 4 | **#104** airplane mode | Fast radio smoke after `nomarchy-home` |
|
||||
| 5 | **#101** charge-limit USB-C burst | Dock plug/unplug storm; pairs with #3 |
|
||||
| 6 | **Parallel fingerprint** hyprlock/greeter residual | sudo path already PASSED 2026-07-14 |
|
||||
| 7 | **#95** Kitty-only on Acer | Validates terminal stack on oldest GPU |
|
||||
| 8 | **#123** / **#124** install bake + flake pin | Next full reinstall window |
|
||||
|
||||
Everything else below stays open; order is convenience, not a gate.
|
||||
|
||||
## Any machine (dev box is fine)
|
||||
|
||||
- [ ] **Theme pairs V3 — the four new themes on real GL** — after pulling
|
||||
the pair commits: `nomarchy-state-sync apply <slug>` for each of
|
||||
boreal-dawn, executive-ivory, kiln, kiln-clay. Pass = all of:
|
||||
(1) the bar renders true — boreal-dawn's frosted pills actually blur
|
||||
what's behind them (softGL VM can't prove blur), kiln's plank shows
|
||||
its copper edge, no font-fallback boxes (GeistMono / JetBrainsMono /
|
||||
CaskaydiaCove); (2) wallpaper paints at native res, no banding on the
|
||||
kiln ember gradient; (3) Kitty + btop under each theme — btop colors
|
||||
match (the VM never started the GL terminal; btop.theme is
|
||||
file-asserted only); (4) boreal-dawn app launcher (SUPER+Space) lays
|
||||
out its icon grid; (5) an nm-applet/tray menu is readable under all
|
||||
four (the `*`-reset counter-rules). If a VM-rendered preview looks
|
||||
off in the theme-picker grid next to the hardware-captured ones,
|
||||
recapture that preview on hardware into themes/<slug>/preview.png.
|
||||
- [ ] **Light-theme contrast refits (2026-07-18 report)** — under any
|
||||
light theme: (1) with two workspaces open, the inactive number is
|
||||
readable on the bar (subtext, not the washed-out grey); (2) hover a
|
||||
notification popup — background stays the theme's light card with a
|
||||
faint tint, text readable (the swaync default's dark
|
||||
.notification-default-action hover is now overridden); close
|
||||
button matches the palette. VM-verified with a hover probe under
|
||||
summer-day; this is the on-hardware confirmation of the report.
|
||||
- [ ] **Auto-theme pair flip on hardware** — set
|
||||
`settings.autoTheme.{day,night}` to one of the new pairs (e.g.
|
||||
kiln-clay / kiln), set sunset (or sunrise) a few minutes ahead via
|
||||
Look & Feel › Auto theme (the time edit itself rebuilds — since the
|
||||
exact-time revision the timer fires AT the configured minute, no
|
||||
15-min poll), let the timer flip once each way. Pass = (1)
|
||||
`systemctl --user list-timers nomarchy-auto-theme` shows the two
|
||||
configured times as the trigger; (2) the switch lands within ~a
|
||||
minute of the configured time; (3) the whole desktop switches in
|
||||
one generation, and nothing leaks from the previous theme — fonts,
|
||||
rounding, terminal opacity all reset (the per-theme appearance
|
||||
block), wallpaper follows; (4) with autoCommit on, `git -C
|
||||
~/.nomarchy log` shows an `apply theme` commit and state.json is
|
||||
not left dirty (the 0.5.1 pathspec fix).
|
||||
- [ ] **#148 dock-intent enforcement without auto-profiles (dev box, docked)** —
|
||||
after pulling the #148 commit: **log out and back in first** — the
|
||||
watcher is `exec-once`, so `nomarchy-home` alone leaves the old one
|
||||
running. Then, docked clamshell (lid closed, panel off, dockMode set
|
||||
from the Display menu or a prior auto-dock), run `nomarchy-home` or a
|
||||
bare `hyprctl reload`. **Pass:** eDP-1 re-lights for at most ~1–2 s and
|
||||
goes back off by itself; `journalctl -t nomarchy-display-watch
|
||||
--since -5min` shows `dock-intent=true … action=re-dock`; a "Docked —"
|
||||
toast appears. Same relogin answers #149's question (does the Waybar
|
||||
unit still race Hyprland IPC on a cold relogin without linger?).
|
||||
**Fail:** the panel stays lit inside the shut lid (the #148 symptom).
|
||||
- [ ] **#115 suspend-then-hibernate (laptop with hibernate/resume wired)** —
|
||||
after system rebuild: Preferences shows **Suspend then hibernate (on)**
|
||||
(hidden if `CanHibernate=no`). Confirm:
|
||||
`grep -E 'HibernateDelaySec|HibernateOnACPower' /etc/systemd/sleep.conf`
|
||||
→ `1h` / false; `grep HandleLidSwitch /etc/systemd/logind.conf` →
|
||||
undocked s2h, ExternalPower=suspend, Docked=ignore.
|
||||
**Quick path:** on battery, `nomarchy-suspend` then
|
||||
`systemctl list-jobs` / journal should show suspend-then-hibernate
|
||||
(not plain suspend). On AC, same command → plain suspend.
|
||||
**Full path (optional, long):** battery, lid close or idle past 15m,
|
||||
leave ~1h+, open → single LUKS unlock (encrypted) or lock screen
|
||||
(unencrypted), session intact. Toggle **off** + rebuild → plain
|
||||
suspend only. **Pass:** battery s2h + 1h hibernate resume works;
|
||||
AC never plans hibernate; no row without hibernate support.
|
||||
- [ ] **#104 airplane mode radios (any laptop with Wi-Fi + BT)** —
|
||||
after `nomarchy-home` + waybar restart: System › **Airplane mode (off)**
|
||||
(or SUPER+CTRL+R). **Pass on:** Wi-Fi and Bluetooth both drop (nm-applet /
|
||||
blueman confirm); Waybar shows a plane glyph; click glyph or menu again
|
||||
restores **only** the radios that were on before. If Wi-Fi was already
|
||||
off, it must stay off after disengage. Glyph must **not** appear when
|
||||
airplane is off.
|
||||
- [ ] **#96 battery-limit row on threshold-less firmware (Acer M5-481T)** —
|
||||
on the Acer (live or installed session with the commit carrying this
|
||||
entry): open System (`SUPER+CTRL+I`). **Pass:** a "Battery limit" row
|
||||
IS listed (battery present); picking it shows the notification
|
||||
explaining the firmware exposes no charge-stop control (not a raw
|
||||
error, and the row never silently disappears). On the T14s the row
|
||||
must still open the working preset picker.
|
||||
- [ ] **#101 charge-limit service survives USB-C power-event bursts**
|
||||
(round 2) — round 1 FAILED on TuringMachine 2026-07-13 18:16: a
|
||||
*spaced* storm (each run finished before the next AC event) landed 5
|
||||
successful starts in 10s → `start-limit-hit`, unit marked failed,
|
||||
doctor badge — although every run succeeded. Fixed by exempting the
|
||||
unit from start rate limiting (`StartLimitIntervalSec=0`) + a
|
||||
spaced-storm guard in `checks.battery-charge-limit` (the commit
|
||||
carrying this entry). **Re-check:** after pulling/rebuilding this
|
||||
commit, `sudo systemctl reset-failed nomarchy-battery-charge-limit
|
||||
.service`, then plug/unplug the powered USB-C dock several times in
|
||||
quick succession. **Pass:** `systemctl is-failed
|
||||
nomarchy-battery-charge-limit.service` stays false, `Result=success`,
|
||||
BAT0's `charge_control_end_threshold` still matches the limit, and the
|
||||
journal has no new `start-limit-hit`/`Failed with result` lines.
|
||||
- [ ] **Docking recovery round 8 (undock panel restore, 2026-07-14)** —
|
||||
round 7 **FAILED** on the AMD dev box (Bernardo, 5–6 consecutive
|
||||
unplugs, none recovered). Its whole diagnosis was wrong, and the way it
|
||||
was wrong is the lesson: round 6 read `result=enable-timeout` ×2 +
|
||||
`result=ok` ×1 as an intermittent race and retried the keyword. But the
|
||||
keyword is **inert, not raced** — with ZERO enabled outputs (panel
|
||||
disabled by the dock, external gone) Hyprland 0.55.4 accepts
|
||||
`keyword monitor` (prints `ok`, exits 0) and never flushes it until a
|
||||
DRM event arrives. Retrying an inert command 25×/poll for 6 polls just
|
||||
bought 30s of black screen. Every `result=ok` in that journal was
|
||||
**Bernardo plugging the cable back in** — his hotplug flushed the queued
|
||||
rule and the next poll took the credit. Probed live 2026-07-14: keyword
|
||||
inert across 4s and 5s in two runs, `dispatch forcerendererreload` also
|
||||
inert, and only `hyprctl reload` worked — 99ms and 289ms, cable out.
|
||||
The transition now escalates to `reload` when the keyword proves inert,
|
||||
re-asserts the rule, and restores per-device keyboard layouts (a reload
|
||||
drops runtime `device[…]:kb_layout` keywords).
|
||||
**Already proven on hardware** (2026-07-14, two real unplugs invoking
|
||||
the fixed `nomarchy-display-transition undock eDP-1` directly with the
|
||||
watcher paused): panel on and workspaces 1–3 home in 1.8s, journal
|
||||
`keyword=inert escalate=reload` → `enable=via-reload` → `result=ok`; and
|
||||
the keyboard-restore path with a Logitech K400 whose receiver is in the
|
||||
laptop — it kept its remembered `us` across the undock, while a control
|
||||
`hyprctl reload` with no restore dropped it to the session's `gb`
|
||||
(so `restore_keyboards` is load-bearing, not insurance).
|
||||
**What is NOT proven, and is this round:** the *watcher-driven* path
|
||||
(the running watcher calls the transition by baked store path, so it
|
||||
only picks the fix up at relogin — the round-5 trap), and repetition.
|
||||
**Re-check:** relogin (mandatory — see below), dock, then unplug the
|
||||
cable **at least five times**, plus once with the lid shut.
|
||||
**Pass:** the panel comes back every time with workspaces intact, and
|
||||
`journalctl --user
|
||||
-t nomarchy-display-transition -t nomarchy-display-watch --since
|
||||
'-10 min'` shows `result=ok` for each undock with **no**
|
||||
`result=enable-failed`. `keyword=inert escalate=reload` on every undock
|
||||
is EXPECTED — that is the fix firing, not a fault. A `result=ok`
|
||||
**without** a preceding `keyword=inert` line means the keyword flushed
|
||||
on its own and the reload was never needed: interesting, worth
|
||||
reporting, still a pass.
|
||||
- [ ] **Headphone jack-follow (last open count from rounds 4–6,
|
||||
2026-07-14)** — rounds 4–6 opened five counts on the AMD dev box; four
|
||||
are now **confirmed fixed on hardware** by round 6 (Bernardo): wallpaper
|
||||
on the external, automatic dock mode, audio reaching the monitor, and
|
||||
the external keyboard prompting **exactly once** (was four times). The
|
||||
undock panel is round 7 above. This is the one count **never tested**:
|
||||
plugging headphones had stopped moving audio to them, because a pinned
|
||||
`default.configured.audio.sink` outranks the priority rules, which kills
|
||||
jack-follow on UCM cards where the headphones are their own sink (fixed
|
||||
in ce480f3's lineage, unverified). **Re-check:** docked and undocked,
|
||||
plug and unplug headphones — audio must move to them and back each time,
|
||||
and `journalctl --user -t nomarchy-dock-audio` must show `trigger=jack
|
||||
selected=…` on each plug. Worth folding into the round 7 session: the
|
||||
dock is already in hand and the two don't interfere.
|
||||
|
||||
**Historical detail for the four settled counts** (kept only until
|
||||
round 7 closes; the fixes themselves are in the log):
|
||||
— round 4 was run on the AMD dev box and **failed on four counts**
|
||||
(Bernardo): no wallpaper on the external monitor, no automatic dock
|
||||
mode (System › Display had to be used by hand), audio staying on the
|
||||
laptop speakers while docked, and one dock + one external keyboard
|
||||
asking for a layout **four times**. Separately, plugging headphones had
|
||||
stopped moving audio to them. Causes and fixes in the commit carrying
|
||||
this entry: the display watcher's `socat -T 1` closed Hyprland's IPC
|
||||
socket after a second of idle, so a dock plugged into an idle desktop
|
||||
lost `monitoradded` in the reconnect gap (wallpaper + dock mode + audio
|
||||
all hang off that one event); Hyprland lists every key-capable evdev
|
||||
node as a "keyboard"; and a pinned `default.configured.audio.sink`
|
||||
outranks the priority rules, which kills jack-follow on UCM cards where
|
||||
the headphones are their own sink. **Re-run round 4 below in full**,
|
||||
plus: (a) leave the desktop untouched for ~30s, then plug the dock —
|
||||
wallpaper, dock mode, and audio must all follow with no manual step;
|
||||
(b) the external keyboard must prompt **exactly once**, and never for
|
||||
the monitor or the lid/power buttons; (c) docked and undocked, plug and
|
||||
unplug headphones — audio must move to them and back each time.
|
||||
`journalctl --user -t nomarchy-display-watch` should show
|
||||
`outputs-changed added=…`, and `-t nomarchy-dock-audio`
|
||||
`trigger=jack selected=…` on a headphone plug.
|
||||
|
||||
**Round 5 was itself run and failed (2026-07-14); this entry now covers
|
||||
round 6.** Two of the four reports were never actually testing the fix:
|
||||
both watchers are Hyprland `exec-once`, so `nomarchy-home` swaps the
|
||||
config but leaves the *old* processes running — only `nomarchy-dock-audio`
|
||||
(a user service) had picked up new code. **Relogin is mandatory before
|
||||
testing either watcher**; confirm with `tr '\0' '\n' < /proc/$(pgrep -f
|
||||
bin/nomarchy-display-profile-watch)/cmdline | sed -n 2p` and grep the
|
||||
script for `outputs-changed`. The other two were real and are fixed in
|
||||
the commit carrying this entry: automatic dock mode did not exist at all
|
||||
(the watcher auto-*undocked* but only ever matched display profiles on
|
||||
plug, and this box has none saved with `displayProfileAuto` unset, so
|
||||
nothing happened — `Dock mode` was interactive-only), and the BenQ's
|
||||
audio was unreachable because WirePlumber had `alsa_card.pci-0000_c3_00.1`
|
||||
pinned to `pro-audio`, whose raw `pro-output-N` sinks have no ports and
|
||||
no routing. The BenQ's own USB card is input-only (all profiles
|
||||
`sinks: 0`), so DisplayPort is the only path to its speakers.
|
||||
**Also check:** no display profile is saved, yet plugging the BenQ must
|
||||
now dock by itself; and `journalctl --user -t nomarchy-dock-audio` shows
|
||||
`repaired-card=alsa_card.pci-0000_c3_00.1 profile=HiFi` on the first
|
||||
plug, after which audio reaches the monitor's speakers. A saved+matching
|
||||
display profile must still win over auto-dock.
|
||||
- [ ] **Docking recovery round 4 (#100, closed-lid BenQ sequence)** — after
|
||||
updating to the commit carrying this entry, run `nomarchy-home` and
|
||||
relogin. With the lid open, put visible windows on at least workspaces
|
||||
1, 2, and 3 and start audio; plug the BenQ GW2790QT. **Pass on plug:**
|
||||
wallpaper appears, audio toasts and moves to a BenQ/GPU HDMI sink, and
|
||||
`journalctl --user -t nomarchy-dock-audio -n 10` shows
|
||||
`trigger=monitoradded` plus `selected=…` (not only a card event). Open
|
||||
System › Display › **Dock mode**. **Pass on dock:** every workspace and
|
||||
window is on the BenQ, focus follows, eDP is disabled in `hyprctl
|
||||
monitors all -j`, and `systemd-inhibit --list` shows Nomarchy holding
|
||||
`handle-lid-switch` for “Safe dock/undock display transition”; there is
|
||||
no dangling laptop workspace and no application exits. Close the lid;
|
||||
audio/session must stay awake. While still docked, manually choose the
|
||||
speakers and change volume/mute: it must stay on speakers. Unplug the
|
||||
BenQ **with the lid still closed**, wait 10 seconds, then open it.
|
||||
**Pass on undock:** no suspend/crash/hard reset, eDP is already active,
|
||||
workspaces 1/2/3 and their windows are intact on it, and the inhibitor
|
||||
disappears only after lid-open. `journalctl -b --since '-5 min'` must
|
||||
contain no cable-removal `systemd-logind: Suspending...`. Finally close
|
||||
the undocked lid normally: the laptop must suspend, proving policy was
|
||||
restored. Wake it, replug the BenQ: that fresh plug must move audio back
|
||||
to HDMI (manual speakers do not survive a physical replug). If any step
|
||||
fails, capture `journalctl --user -t nomarchy-display-watch -t
|
||||
nomarchy-display-transition -t nomarchy-dock-audio --since '-10 min'`,
|
||||
`systemd-inhibit --list`, `hyprctl monitors all -j`, `hyprctl workspaces
|
||||
-j`, and `pactl --format=json list sinks` before changing state again.
|
||||
- [ ] **Parallel fingerprint-or-password on the real reader** (AMD dev
|
||||
box, 2026-07-12) — **sudo path PASSED 2026-07-14** (Bernardo, dev box,
|
||||
gen 422, `fingerprint.pam = true`, parallel default): `sudo -k true`
|
||||
showed ONE prompt, the typed password worked, touching the sensor
|
||||
instead worked, and wrong-finger ×3 fell back to password.
|
||||
**Still open:** hyprlock and the greeter must accept both factors and
|
||||
not wedge on a leftover prompt after a fingerprint win (known cosmetic
|
||||
quirk of the hack — pthread_cancel'd prompt); `fingerprint.parallel =
|
||||
false` must restore the old sequential behavior.
|
||||
**Do NOT "check password still works with `systemctl stop fprintd`"** —
|
||||
that check was in this item and it is a trap that cannot fail. fprintd
|
||||
is D-Bus activated, so PAM asks D-Bus and D-Bus starts it straight back
|
||||
up: Bernardo ran it and *the fingerprint still authenticated*, which is
|
||||
the tell that the daemon never stayed down. It proves nothing about a
|
||||
dead reader. The property it was reaching for is structural anyway —
|
||||
password is `auth sufficient` at order 11700, independent of the fprintd
|
||||
rule at 11400, so no fprintd failure can gate it (verify by eval:
|
||||
`nix eval --raw <flake>#nixosConfigurations.<host>.config.security.pam
|
||||
.services.sudo.text`), and checks.hardware-toggles asserts that shape in
|
||||
the VM with no reader at all. If someone genuinely wants it on hardware
|
||||
it needs `systemctl mask` + stop, and masking a working machine's
|
||||
authentication daemon to re-prove an evaluated invariant is a bad trade.
|
||||
- [ ] **#55 fingerprint enroll on real reader** — with
|
||||
`nomarchy.hardware.fingerprint.enable` and a physical reader: System ›
|
||||
Fingerprint › Enroll a finger; List shows it; Verify succeeds.
|
||||
Enroll/List/Verify verified on hardware 2026-07-14 (T14s); the
|
||||
remaining unknown is a reader-less machine's self-gate.
|
||||
- [ ] **fingerprint + auto-login toggles on hardware** (this commit) — on the
|
||||
T14s, after removing the pinning lines from `~/.nomarchy/system.nix`
|
||||
(`greeter.autoLogin`, `hardware.fingerprint.pam`):
|
||||
1. System › Fingerprint shows **Fingerprint (on)** (state already true).
|
||||
Toggle it off → terminal opens, sudo system rebuild + home switch →
|
||||
toast "Fingerprint off". Expect: sudo now refuses the finger and asks
|
||||
only for a password; the lock screen (SUPER+CTRL+L) shows no
|
||||
"or scan your finger" line. Toggle back on → both return.
|
||||
2. With fingerprint OFF and no finger enrolled, the toggle must REFUSE
|
||||
with "Enroll a finger first" and write nothing.
|
||||
3. System › Auto-login shows **(on)**. Toggle off → sudo rebuild →
|
||||
reboot → tuigreet asks, and accepts password OR finger (fingerprint
|
||||
on). Toggle back on → reboot → straight to the session, no prompt.
|
||||
Expected throughout: the two are independent, and auto-login on means
|
||||
no boot prompt regardless of the fingerprint switch.
|
||||
- [ ] **#60 non-BAT* battery name (if available)** — on a machine whose
|
||||
system battery is **not** named `BAT*` (e.g. `CMB0`): confirm
|
||||
charge-limit oneshot writes the threshold, System › Battery limit
|
||||
and Power profile rows appear, Waybar power-profile module shows,
|
||||
doctor charge-limit section runs. On `BAT0`-only machines this is
|
||||
a no-op (charge-limit re-apply passed on `BAT0` — Latitude
|
||||
2026-07-10).
|
||||
- [ ] **btop theme fidelity (#52 residual)** — softGL theme-shot cannot
|
||||
open a real terminal UI, so the hand `btop.theme` assets were only
|
||||
guest-file asserted (main_bg / inactive_fg keys) + desktop/bar
|
||||
rendered for rose-pine, everforest, summer-night, vantablack,
|
||||
catppuccin. On a real session: `nomarchy-state-sync apply <slug>`
|
||||
then `btop` and confirm backgrounds/text match the theme (esp.
|
||||
rose-pine Dawn light bg `#faf4ed`, vantablack near-black `#0d0d0d`,
|
||||
catppuccin Mocha `#1E1E2E`). One pass cycling those five is enough.
|
||||
- [ ] **SSH_AUTH_SOCK for GUI clients** — after relogin, launch a GUI git
|
||||
client (or `rofi`-launched terminal-less app) and confirm it reaches
|
||||
gpg-agent's SSH socket without an interactive shell parent.
|
||||
- [ ] **Notification inhibited-state glyph** (item 28 color-only sweep,
|
||||
iteration #69) — have an app hold a notification inhibitor (e.g. run
|
||||
a fullscreen video, or `busctl --user call org.freedesktop.Notifications
|
||||
… Inhibit`); the Waybar bell must switch to the muted **bell-off**
|
||||
(same glyph as DND), NOT the normal bell /. Then release it → the
|
||||
bell returns. Repeat on a whole-swap bar (summer-day/night use their
|
||||
own bell-off glyph, executive-slate/boreal use ). The glyph itself
|
||||
already renders (DND uses it); this only confirms swaync emits the
|
||||
`inhibited-*` class and the bar routes it.
|
||||
- [ ] **Keyboard layout cycle bind** — with a comma layout (e.g.
|
||||
`nomarchy.keyboard.layout = "us,de"`), SUPER+SHIFT+K cycles the
|
||||
focused keyboard's layout, the Waybar `` indicator follows, and
|
||||
the row shows in the SUPER+? cheatsheet. — 2026-07-04 attempt:
|
||||
no comma layout was configured, so the bind wasn't rendered (the
|
||||
gate working as designed, not a failure) — retest after setting a
|
||||
comma layout + rebuild + relogin. The SUPER+? no-op found en
|
||||
route was a real bug → BACKLOG item 26 (fix shipped; SUPER+?
|
||||
cheatsheet PASS on Latitude 2026-07-10).
|
||||
- [ ] **swaync readable on summer-day** (item 25 fix) — on summer-day
|
||||
after `home-update`: `notify-send "title" "body text"` shows
|
||||
readable body text; open the control centre (SUPER+N), hover a
|
||||
notification row and check the Clear button — all text legible
|
||||
(body/buttons now @text on tinted chips, not subtext/surface).
|
||||
- [ ] **Bar + rofi legible on flexoki-light** (item 27 fix) — switch to
|
||||
flexoki-light: every Waybar module readable (window title, tray
|
||||
row, dimmed inactive workspaces / muted volume visible-but-dim),
|
||||
rofi inputbar/alternate rows show text. Spot-check one dark theme
|
||||
(e.g. tokyo-night) for no visual regression in the same spots.
|
||||
- [ ] **Stub-bar themes got the generated bar** (item 28, 90a5104) —
|
||||
switch to catppuccin, lumon, nord and retro-82: each now shows
|
||||
the generated styled bar in its own palette (workspace pill,
|
||||
padded right cluster) instead of the old raw default Waybar.
|
||||
Their rofi menus should look unchanged.
|
||||
- [ ] **Back-audit spot-check** (item 24) — the keybinds cheatsheet
|
||||
(SUPER+? / root menu › Keybindings) now ends in ↩ Back and
|
||||
returns to the root picker; spot a couple of submenus (Display,
|
||||
VPN › Tailscale) still Back correctly after the audit pass.
|
||||
- [ ] **Per-device keyboard menu + hotplug restore (re-verify after in-flake
|
||||
graduation)** — update to the current `main`, run `nomarchy-home`, and
|
||||
relogin with an external keyboard already connected. Open System ›
|
||||
Keyboard: both the built-in and external boards must be listed with
|
||||
their current layouts. Pick a different layout for the external board;
|
||||
it must apply only there and persist in `settings.keyboard.devices`.
|
||||
Unplug/replug it: the saved layout must restore without another prompt
|
||||
and graduate into a `device{}` block on the next rebuild. Finally plug
|
||||
a previously unseen keyboard in after login: the automatic picker must
|
||||
appear once and remember that choice too.
|
||||
- [ ] **Snapshots restore + rollback exercise** (residual — the
|
||||
GUI/polkit half passed on hardware 2026-07-04, a47aa3a): in
|
||||
`sudo nomarchy-snapshots`, restore a single file (`undochange`)
|
||||
and walk a root-config rollback up to (or through) the
|
||||
typed-`yes` gate.
|
||||
- [ ] **Rollback menu (item 9b)** — after a couple of theme changes,
|
||||
menu › System › Rollback: recent desktop generations listed
|
||||
(newest marked current); picking an older one opens a terminal,
|
||||
activates it, and the theme visibly reverts; picking the newer one
|
||||
again rolls forward. The two System rows: Snapshots opens the
|
||||
snapshot flow, "boot an older generation (how)" fires an
|
||||
instruction notification.
|
||||
- [ ] **Open-a-file smoke (viewers + mime defaults, item 8)** — after
|
||||
`nomarchy-home`: from yazi/Thunar, open a PDF (→ zathura, themed to
|
||||
the palette), an image (→ imv, NOT GIMP), a video (→ mpv);
|
||||
`xdg-open .` on a directory → Thunar; a link → **Chromium**
|
||||
(`chromium-browser.desktop` — template default as of 2026-07-10).
|
||||
- [ ] **Update awareness** — with `nomarchy.updates.enable`, let the timer
|
||||
fire (or start the unit): indicator appears only when inputs are
|
||||
behind, notification only on count growth, click opens the upgrade
|
||||
flow.
|
||||
- [ ] **VPN menu live paths** — import a real WireGuard `.conf` and an
|
||||
`.ovpn` via System → VPN, toggle up/down (● / ○ state), and the
|
||||
Tailscale block: up/down + exit-node without sudo (operator grant).
|
||||
Exit-node rows now show "hostname — Country City" (2026-07-11,
|
||||
header-offset parsing): confirm Mullvad-style nodes carry their
|
||||
location, a locationless self-hosted node stays a bare hostname,
|
||||
and picking either still sets the node (`tailscale status` shows it).
|
||||
- [ ] **Printer menu** — with `nomarchy.services.printing`, System →
|
||||
Printers opens system-config-printer; add a printer, test page.
|
||||
- [ ] **GRUB UEFI ISO theme render** — boot the ISO on UEFI hardware:
|
||||
composed splash background, palette menu in the lower third, accent
|
||||
timeout bar.
|
||||
- [ ] **Visual theme pass** — live ISO: all six identity themes (bars) +
|
||||
the four authored rofi `.rasi` (nord/retro-82/lumon/kanagawa) look
|
||||
right, not just parse.
|
||||
- [ ] **Display profiles, slice a (item 15)** — declare two
|
||||
`nomarchy.displayProfiles` (e.g. docked disables eDP-1 + arranges
|
||||
the externals; undocked re-enables it), `home-update`, then:
|
||||
System › Display › Profiles lists them (● marks active); applying
|
||||
is instant (no rebuild) and survives a relogin (the baked
|
||||
overlay); "Base layout" restores; a menu resolution pick made
|
||||
earlier must NOT re-enable a profile-disabled panel after the
|
||||
next rebuild.
|
||||
- [ ] **Display profiles workspace pins, slice c (item 15)** — give the
|
||||
docked profile `workspaces = { "1" = "<ext>"; "9" = "eDP-1"; }`:
|
||||
applying the profile moves open workspaces 1/9 to those outputs
|
||||
immediately, new visits land there too, and the pins survive a
|
||||
relogin (baked `workspace` rules). Switching to a profile without
|
||||
pins leaves workspaces where they are (stale session pins are
|
||||
expected until reload/rebuild — noted in the applier).
|
||||
- [ ] **Display profiles auto-switch, slice b (item 15)** — same setup,
|
||||
then menu › Profiles › Auto-switch on: plugging the dock/monitor
|
||||
applies the matching profile within ~3s (toast names it);
|
||||
unplugging switches back; Auto-switch off stops that; with two
|
||||
profiles naming the same outputs, no flapping (ties do nothing).
|
||||
- [ ] **P2 retunes eyeball (item 28b)** — spot-check on top of the P1
|
||||
entry below: gruvbox/nord/lumon/everforest/retro-82/white dimmed
|
||||
text (inactive workspaces, muted volume) now visible-but-dim;
|
||||
latte + rose-pine(dawn) + summer-day warn/battery tint reads on
|
||||
the light bases; flexoki-light statuses are the deeper canonical
|
||||
600s; latte selected-row/alt-accent pink darker. Anything that
|
||||
lost its "dim" feel → reopen 28b.
|
||||
- [ ] **Retuned palettes eyeball (item 28b P1)** — switch through
|
||||
summer-day, flexoki-light, kanagawa, miasma: chips/menus/toasts
|
||||
now draw on a *raised* surface (summer-day's was slate-on-cream,
|
||||
kanagawa's near-black, miasma's pure black), secondary text
|
||||
(tooltips, fastfetch labels) is visible on summer-day +
|
||||
flexoki-light, kanagawa floats are lighter than the bg (upstream
|
||||
sumiInk4). Anything that reads worse than before → reopen 28b.
|
||||
- [ ] **Doctor bar tripwire (LATER item)** — with everything healthy
|
||||
the bar shows nothing; `systemctl --user start doomed`-style
|
||||
induced failure → within ~5 min a red appears (tooltip lists
|
||||
the ✖ lines), clicking opens the sheet in a terminal;
|
||||
reset-failed → it disappears on the next poll. Also visible on
|
||||
both summer bars.
|
||||
- [ ] **OCR region (LATER item)** — Tools › Capture › "OCR region →
|
||||
clipboard": select a region with visible text → toast reports
|
||||
the word count and `wl-paste` yields the text; Esc in slurp
|
||||
cancels silently; a text-free region toasts "No text recognized"
|
||||
without clobbering the clipboard.
|
||||
- [ ] **Look & Feel submenu (item 19)** — root menu: "Look & Feel"
|
||||
(replacing Theme, root still six rows) → Theme grid opens, Next
|
||||
wallpaper cycles instantly, Night light toggles (and is GONE
|
||||
from System). SUPER+T / SUPER+SHIFT+T direct binds unchanged.
|
||||
- [ ] **Launch-or-focus (item 17)** — uncomment the template's
|
||||
`nomarchy.launchOrFocus` firefox example (with firefox
|
||||
installed), `home-update` + reload: SUPER+B launches firefox
|
||||
when closed, FOCUSES the existing window when open (also from
|
||||
another workspace); with firefox removed the bind fires a
|
||||
"not installed" toast; the row shows under SUPER+?.
|
||||
- [ ] **Themed greeter + console (item 16)** — after `sys-rebuild` +
|
||||
logout: tuigreet renders in theme colors (dark container, accent
|
||||
border, themed prompt/time) and a raw tty (CTRL+ALT+F2) shows the
|
||||
theme's ANSI palette; on a LUKS machine the passphrase prompt
|
||||
follows too. Then switch theme + `sys-rebuild` + logout → the
|
||||
greeter follows the new palette. (tuigreet can't run under the
|
||||
VM harness — see MEMORY.md — so rendering is hardware-tier.)
|
||||
- [ ] **Color picker (item 13, final slice)** — SUPER+CTRL+P (or Tools ›
|
||||
Color picker): hyprpicker's zoom loupe appears; click a pixel →
|
||||
toast shows the hex and `wl-paste` yields it; Esc cancels with no
|
||||
toast. Row present in the SUPER+? cheatsheet.
|
||||
- [ ] **Low-battery toasts (item 13 slice)** — on battery, drain past
|
||||
25%: one "Battery low" toast; past 10%: a critical "Battery
|
||||
critical" toast that stays up until dismissed (swaync); plug in,
|
||||
drain again → it re-notifies; no repeat toasts while it just keeps
|
||||
draining. (Crossing logic VM-verified — this checks the real
|
||||
swaync rendering in a session.)
|
||||
|
||||
- [ ] **Audio opens in Amberol** (iteration #60, item 37) — after
|
||||
`nomarchy-home`: double-click an mp3/flac/ogg (or `xdg-open song.mp3`)
|
||||
→ it opens in Amberol, not mpv; video files still open in mpv.
|
||||
(`xdg-mime query default audio/mpeg` → io.bassi.Amberol.desktop.)
|
||||
- [ ] **rofi menu polish** (iteration #58, item 39 + #56/#57 34/40) —
|
||||
after `nomarchy-home`: (a) menu › System › Power profile shows a
|
||||
colored icon per profile (performance/balanced/power-saver), not
|
||||
just text — icons *render* (not blank); (b) every submenu's ↩ Back
|
||||
shows a single arrow, no double; (c) on this single-monitor box,
|
||||
System › Display → pick a resolution → Back returns to System
|
||||
(not back into the same resolution list).
|
||||
- [ ] **Screenshot annotation (satty)** (iteration #73) — after `nomarchy-home`:
|
||||
hit SUPER+SHIFT+Print (or Tools ▸ Capture ▸ Annotate region) → a region
|
||||
select (slurp) appears, then the `satty` UI opens in fullscreen with the
|
||||
screenshot loaded. Check that the UI draws on the current theme palette
|
||||
(tools colored properly) and hitting save places the screenshot in
|
||||
`~/Pictures/Screenshots/` while hitting copy places it in the clipboard.
|
||||
- [ ] **#76 no-swap Hibernate notify** (#76 itself is closed — the
|
||||
hibernate→resume power-cycle **PASSED on TuringMachine 2026-07-12**:
|
||||
Bernardo ran the full hibernate → power off → single LUKS unlock →
|
||||
session-restored cycle "flawlessly"; this notify check is the one
|
||||
remaining leftover) — on a **Nomarchy** machine installed
|
||||
with `swap = 0` (or temporarily `sudo swapoff -a` on one): `nomarchy-menu
|
||||
→ Power → Hibernate` must surface a desktop notification ("Couldn't
|
||||
hibernate — likely no swap is configured. See docs/MIGRATION.md →
|
||||
Enabling hibernation."), **not** a silent no-op. (`swapon` after, if you
|
||||
swapoff'd for the test.)
|
||||
- [ ] **Night-light geo mode (2026-07-11)** — in `home.nix` set
|
||||
`nomarchy.nightlight = { enable = true; latitude = "<lat>";
|
||||
longitude = "<long>"; }` (your real coordinates), `nomarchy-home`,
|
||||
relogin. Then: `systemctl --user status wlsunset` is running (and
|
||||
`hyprsunset` is NOT); after local sunset the screen visibly warms
|
||||
(or test by setting coordinates where it's currently night); the
|
||||
Waybar moon + menu toggle still flip it instantly and an *off*
|
||||
survives relogin (the ExecCondition gate on the swapped unit —
|
||||
the eval check proves the wiring, not the runtime gate). **Pass** =
|
||||
warm shift at the location's night + toggle/persistence intact.
|
||||
- [ ] **#103 live-ISO baseline apps actually launch** (this commit) — the
|
||||
half no headless check can reach: `checks.live-baseline-apps` proves the
|
||||
binaries and `.desktop` entries are in the exact HM generation the ISO
|
||||
ships, but not that a GUI app opens on real hardware (the agent may not
|
||||
drive a graphical VM, and the live ISO has no SSH to script one). On the
|
||||
**Acer M5-481T** (the machine that found this — its GPU is the oldest
|
||||
shipped, so it is the honest test), boot the new live ISO and, from the
|
||||
launcher only — no terminal, that's the point: open **Chromium**,
|
||||
**LibreOffice** (Writer), **Text Editor**, **Amberol**, **Snapshot**.
|
||||
**Pass** = all five are *listed in the launcher* and each opens a window.
|
||||
Also: a `.html` file opens in Chromium (the HTTPS mime default now names
|
||||
a browser that is present — #94's exact bug), and a `.txt` file opens in
|
||||
**Text Editor** (`text/plain` falls through to
|
||||
`org.gnome.TextEditor.desktop` after #119; vscode remains preferred on
|
||||
template installs). Firefox is deliberately absent; its absence is
|
||||
correct, not a miss.
|
||||
- [ ] **#123 install bake: first boot fully themed without manual HM switch**
|
||||
— re-install from a main ISO (this commit+) on **either** the Acer
|
||||
M5-481T or the Dell XPS 9350. **Pass:** install ends with "Desktop
|
||||
pre-activated", no `dconf-CRITICAL` in
|
||||
`/var/log/nomarchy-hm-preactivate.log`, first graphical login has
|
||||
Stylix GTK + nm-applet **without** hand `home-manager switch`.
|
||||
- [ ] **#95 Kitty-only terminal on Acer M5-481T** — after rebuild/install
|
||||
from main: SUPER+Return opens **themed Kitty**; System › Doctor opens
|
||||
a floating classed Kitty window. No Ghostty on PATH required.
|
||||
- [ ] **#124 flake pin after main-ISO install** — installed machine’s
|
||||
`~/.nomarchy/flake.nix` has `?ref=main` (not lagging v1). **Pass:**
|
||||
`nomarchy-rebuild` does not die with
|
||||
`The option 'nomarchy.hardware' does not exist`.
|
||||
- [ ] **Relogin session recovery (2026-07-18 incident)** — after pulling
|
||||
the session-recovery commit and rebuilding: from a full Hyprland
|
||||
session (bar up, a browser open so easyeffects/tray are busy), log
|
||||
out (SUPER+SHIFT+E) and log straight back in at the greeter — do
|
||||
**not** reboot between. **Pass:** `nomarchy-doctor` shows *no failed
|
||||
user units*; `systemctl --user status cliphist swaync
|
||||
xdg-desktop-portal-hyprland swayosd` are all `active (running)`; the
|
||||
bar, notifications, and clipboard history (SUPER+V) work. This is
|
||||
the V3 close for the stale `graphical-session.target` /
|
||||
`start-limit-hit` relogin breakage (BACKLOG #149 note).
|
||||
- [ ] **#150 hypridle wake path now uses the FULL undock** — after pulling
|
||||
the #150 commit + `nomarchy-home`: the hyprland-side transition/keyboard
|
||||
tools are byte-identical (proven by store-path equality), so only the
|
||||
hypridle wake behaviour changed. Two checks: **(a) suspend/resume while
|
||||
docked** (lid closed, external on) — suspend, resume; pass = you land
|
||||
back docked on the external with the panel still off and remembered
|
||||
external-keyboard layouts intact (the wake now runs the full undock,
|
||||
which `restore_keyboards` after any reload; the old mini did not). **(b)
|
||||
zero-output rescue** — the #127 case (dock mode, external gone/black,
|
||||
eDP disabled): pass = the internal panel comes back lit, workspaces move
|
||||
to it, and `~/nomarchy-display-dump-*.txt` is written iff it stays dark.
|
||||
Watch for a regression: the full undock clears dock-intent, so confirm a
|
||||
*transient* zero-output blip on resume while genuinely still docked does
|
||||
not leave you undocked with the laptop panel on (the external re-add
|
||||
should re-dock via the watcher — verify it does).
|
||||
|
||||
## AMD dev box only
|
||||
- [ ] **#118 smartd still runs where drives DO have SMART** (this commit) — the
|
||||
half a VM cannot answer: QEMU exposes no SMART, so `checks.smartd-gate`
|
||||
proves the skip but has to drive the *with-device* path through a stub.
|
||||
On the dev box (real NVMe), after `nomarchy-rebuild` + reboot:
|
||||
`systemctl status smartd` is **active/running**, and
|
||||
`systemctl show -p ExecCondition --value smartd` names
|
||||
`smartd-any-smart-device`. **Pass** = smartd is running, exactly as
|
||||
before this commit — i.e. the gate skips nothing on real hardware.
|
||||
**Fail** = inactive/skipped, which would mean the gate is silently
|
||||
disabling drive-health monitoring: revert it, don't tune it.
|
||||
Cheap bonus while you are there: `nomarchy-doctor` reports no failed
|
||||
units (the red-icon symptom that started #118).
|
||||
- [ ] **AMD runtime bits** — VA-API (`vainfo` → radeonsi), amd-pstate EPP
|
||||
active and PPD switching governors; opt-ins: ROCm (`rocminfo`, a GPU
|
||||
PyTorch/Ollama smoke) and the XDNA NPU driver loading.
|
||||
- [ ] **Fingerprint** — `fprintd-enroll` + (opt-in PAM) login/sudo.
|
||||
- [ ] **#137/#145 splash on a real docked boot** — the one thing the render rig
|
||||
cannot do: its fake heads exist from the start, so the canvas never
|
||||
resizes, which *is* the bug. **Docked**, reboot: pass = the logo is centred
|
||||
on **both** panels at boot **and** at shutdown (it was off-centre on the
|
||||
external), and the LUKS prompt shows the padlock, the entry, and beneath it
|
||||
a keyboard icon + your layout (`us`). Then reboot **undocked** — unchanged.
|
||||
Rendering is already proven (`tools/plymouth-preview.sh`, both heads +
|
||||
`ask-for-password`), so what is unproven is only a canvas that changes
|
||||
under a real DRM boot. If the splash is ever blank, the passphrase prompt
|
||||
is invisible but still live — type it blind, or pick the previous
|
||||
generation in the boot menu, which carries the old theme.
|
||||
- [ ] **#142 dock mode survives a rebuild** — **relogin first** (the watcher is
|
||||
`exec-once`; `nomarchy-home` alone leaves the old one running and you
|
||||
would be testing nothing — round 6's lesson). Then, docked: run
|
||||
`nomarchy-home`. Pass = the laptop panel stays off, no workspace lands on
|
||||
it, no menu trip; `journalctl --user -t nomarchy-display-watch` shows
|
||||
`dock-intent=true panel=eDP-1 state=re-enabled action=re-dock` (a rebuild
|
||||
re-lights the panel and the watcher takes it back within ~1s, so a brief
|
||||
flicker is expected — the panel staying *up* is the failure).
|
||||
Then the three gates that must NOT fire, since this code can disable a
|
||||
panel: (a) Menu ▸ Display ▸ **Screen on** → the panel stays on (intent
|
||||
cleared; if it snaps off again within a second, that is the bug);
|
||||
(b) **undock** → panel returns and stays; (c) boot/relogin **undocked**
|
||||
with `settings.display.dockMode` still `true` in state.json → panel stays
|
||||
on (no external ⇒ never re-dock). (c) is the one that matters: it is the
|
||||
#127 brick if it is wrong. Gates are unit-tested against stubs, but only
|
||||
hardware proves the wiring.
|
||||
- [ ] **#138 dock audio does not break a running browser** — the plug event
|
||||
itself is the only thing V2 could not do (the tool was driven by hand;
|
||||
Hyprland calls the same entry point). **Relogin first** — the watcher is
|
||||
`exec-once`, so `nomarchy-home` alone leaves the old process running and
|
||||
you would be testing nothing (round 6's lesson). Then, with Chromium
|
||||
**already open** on a Meet call: dock/undock. Pass = output still follows
|
||||
to the monitor, **Meet keeps its mic and speakers without restarting the
|
||||
browser**, and `journalctl --user -t nomarchy-dock-audio` shows
|
||||
`selected=…` with **no** `action=graph-restart-fallback`, while
|
||||
`systemctl --user show -p MainPID --value pipewire.service` is unchanged
|
||||
across the plug. If a `graph-restart-fallback` line does appear, rung 3
|
||||
fired on a healthy plug — that is a bug, not the recovery working: file
|
||||
it with the surrounding journal.
|
||||
- [ ] **System ▸ Firmware menu on real LVFS hardware** (item #43,
|
||||
`nomarchy-menu firmware`) — on a machine whose firmware/SSD/dock is
|
||||
on LVFS: open Menu ▸ System ▸ **Firmware**; confirm the terminal runs
|
||||
`fwupdmgr refresh` → lists real `get-updates` → the y/N confirm gates
|
||||
correctly → `fwupdmgr update` applies and prints the reboot note when a
|
||||
capsule needs one. Verify **no** flash happens on "N"/cancel. (VM only
|
||||
proves the menu row + flow renders; a real capsule write is
|
||||
hardware-only.)
|
||||
|
||||
## Acer Aspire M5-481T only (1366×768 — the narrow-panel case)
|
||||
- [ ] **#139 sheets on a small panel** — Waybar clock → calendar, System ▸
|
||||
Doctor, and the Waybar updates click. Pass = each opens floating and
|
||||
centred at roughly 60%×65% / 55%×70% / 45%×50% of *that* screen (they are
|
||||
computed from the focused monitor now, so this is checking the fallback
|
||||
path and the font, not the arithmetic), fully on-screen, none clipped by
|
||||
the bar. Dev box (2560×1440) already measured exact.
|
||||
- [ ] **#131 menu width on the real narrow panel** — open Menu ▸ Recovery.
|
||||
Pass = every label complete (no ellipsis) and the picker visibly *not*
|
||||
hogging the screen (≤65%, the cap). The geometry was already reproduced
|
||||
pixel-exactly on the dev box (888px + JetBrainsMono 14 = what 65% of 1366
|
||||
produces) and passed, so what is genuinely unproven here is only the
|
||||
Acer's own fontconfig/DPI resolving `ch` the same way — try it under a
|
||||
**JetBrainsMono 14 theme** (summer-day/night, kanagawa), the widest case.
|
||||
|
||||
- [ ] **Theme-switch bar round-trip (2026-07-19 waybar-poke + gtk-pin fix)** —
|
||||
after `nomarchy-pull` + `nomarchy-home`, switch dark → light → dark
|
||||
(e.g. boreal → kiln-clay → kiln). Pass = the bar visibly restarts on
|
||||
each switch and the workspace digits land per theme (dark-on-accent
|
||||
active pill, dimmed inactives on dark themes) with no color carried
|
||||
over from the previous theme; then start/stop a screen recording and
|
||||
toggle airplane mode — the supervisor must survive both pokes
|
||||
(`pgrep -lx nomarchy-waybar` still lists it).
|
||||
|
||||
## Latitude 5310 / 5410 only
|
||||
- [ ] **v1 QA batch on-hardware pass** (583708d batch was QEMU-verified) —
|
||||
general smoke before the next `main → v1` promotion (broader than
|
||||
the 2026-07-10 session; include theme/ISO/greeter leftovers).
|
||||
|
||||
## T14s only
|
||||
- [ ] **Webcam IR-hide end-to-end on Nomarchy** — installer detects the
|
||||
RGB+IR pair, bakes `hardware.camera.hideIrSensor`; `wpctl status`
|
||||
shows one colour source; an app picker lists one camera; Howdy-style
|
||||
direct `/dev/video2` reads still work.
|
||||
- [ ] **Portal/Flatpak libcamera IR (b)/(c)** — after #71 docs: on hardware,
|
||||
confirm a Flatpak/portal picker still lists the internal IR node;
|
||||
only then investigate (b) WirePlumber libcamera GREY-only monitor
|
||||
rule or (c) libcamera/udev-layer hide. Do not ship either without a
|
||||
live dual-sensor check (see HARDWARE.md §7 / ROADMAP § Webcam).
|
||||
File diff suppressed because it is too large
Load Diff
1984
agent/JOURNAL.md
1984
agent/JOURNAL.md
File diff suppressed because it is too large
Load Diff
161
agent/LOOP.md
161
agent/LOOP.md
@@ -1,161 +0,0 @@
|
||||
# The loop — autonomous iteration protocol
|
||||
|
||||
How an AI agent works on Nomarchy unattended. One **iteration** = pick one
|
||||
task, do it, verify it, commit it, record it. The protocol is
|
||||
harness- and vendor-agnostic; the same iteration works under any of:
|
||||
|
||||
- **An interactive self-paced loop** in any agent harness (e.g. `/loop`
|
||||
in Claude Code) — the agent iterates until stopped.
|
||||
- **Headless** (a one-shot CLI invocation, cron/systemd-timer) — one
|
||||
invocation runs one iteration (or a small fixed number) and exits.
|
||||
- **A fresh manual session** — a human says "do a loop iteration"; the
|
||||
files below carry all the state, so any session can pick up where the
|
||||
last left off.
|
||||
|
||||
All loop state lives in this directory, git-tracked. There is no state
|
||||
outside the checkout (the distro's own philosophy, applied to its agents).
|
||||
|
||||
## The files
|
||||
|
||||
| File | Role | Who writes it |
|
||||
|---|---|---|
|
||||
| `GOALS.md` | North star + quality bars + non-goals | Human (agents propose edits) |
|
||||
| `BACKLOG.md` | Prioritized task queue (NOW/NEXT/LATER/PROPOSED/DECISIONS) | Both — see its header rules |
|
||||
| `JOURNAL.md` | Append-only iteration log | Agents |
|
||||
| `MEMORY.md` | Curated durable lessons/gotchas | Agents (curated, not append-only) |
|
||||
| `HARDWARE-QUEUE.md` | Pending on-hardware checks only Bernardo can run | Agents append, human checks off |
|
||||
| `CONVENTIONS.md` | Repo/design conventions to follow while coding | Human (agents propose edits) |
|
||||
|
||||
Instructions live next to the state: `VERIFICATION.md` (enforcement),
|
||||
`DELEGATION.md` (tiers/roles/economy), `GOALS.md`, `THEME-DESIGN.md`.
|
||||
|
||||
## Model & token economy
|
||||
|
||||
Spend expensive tokens on judgment, not mechanics. Tiers, roles, and the
|
||||
full delegation rules are in **`DELEGATION.md`**; the loop-specific
|
||||
habits:
|
||||
|
||||
- **Plan and reason on the strong model.** Orientation, task selection,
|
||||
design, debugging, Nix eval semantics, verification judgment, and
|
||||
anything that would land in a commit unreviewed stay with the
|
||||
frontier-tier model running the loop.
|
||||
- **Delegate mechanical subtasks down.** When a subtask is fully
|
||||
specified and needs no design judgment — grep/audit sweeps,
|
||||
README-option-table reconciliation, a repeated edit applied across
|
||||
files, summarizing long logs or check output — hand it to a
|
||||
light/standard-tier subagent per `DELEGATION.md`. The strong model
|
||||
writes the spec, reviews the result, and owns the commit.
|
||||
- **Read narrowly.** Step 0's list is the whole orientation read (the
|
||||
*last 3–5 entries* of the journal, never the full file). Read large
|
||||
files by section, don't re-read what's already in context, and tail
|
||||
build/check logs instead of dumping them.
|
||||
- **Write tersely.** Journal entries follow the template and no more;
|
||||
commit bodies state what/why/tier, not a narrative.
|
||||
- **Headless runners** may run whole low-stakes iterations (QA sweeps,
|
||||
docs-drift passes) on a cheaper model; iterations touching
|
||||
`modules/` or `pkgs/` behavior keep the strong model.
|
||||
|
||||
## One iteration, step by step
|
||||
|
||||
### 0. Orient
|
||||
1. Read `GOALS.md`, `CONVENTIONS.md`, `MEMORY.md`, the **last 3–5 entries**
|
||||
of `JOURNAL.md`, and `BACKLOG.md`. If the top task is product-shaped
|
||||
(UX, release bar, day-2 confidence), also read the matching section of
|
||||
**`docs/VISION.md`** — do not invent work from VISION; only execute
|
||||
BACKLOG items. Map of docs vs agent state: `docs/README.md`,
|
||||
`agent/README.md`.
|
||||
2. `git pull --ff-only` (skip silently if offline). Confirm you are on
|
||||
`main` with a clean tree. **A dirty tree you didn't create → stop and
|
||||
report; never stash or discard someone else's work.**
|
||||
3. Sanity baseline: if the last journal entry reports a red
|
||||
`nix flake check`, or you have any reason to suspect breakage, run
|
||||
`nix flake check --no-build` first. **A red baseline preempts the
|
||||
backlog — fixing it *is* this iteration's task.**
|
||||
|
||||
### 1. Pick exactly one task
|
||||
- Take the **topmost actionable** item: NOW before NEXT; never LATER
|
||||
unless NOW and NEXT are empty or all blocked.
|
||||
- *Actionable* means: not `[blocked:hw]` (those wait in
|
||||
`HARDWARE-QUEUE.md`), not `[human]` (decisions), and small enough to
|
||||
finish + verify in one iteration. If the top item is too big, **split
|
||||
it in BACKLOG.md** (that edit is part of the iteration) and take the
|
||||
first slice.
|
||||
- Never implement anything from **PROPOSED** — those await human triage.
|
||||
- If nothing is actionable, do a **QA sweep** instead: run the full check
|
||||
suite, hunt drift (README option tables vs the live `nomarchy.*`
|
||||
surface, template drift, dead code), deepen a VM test, or research and
|
||||
write up a PROPOSED item. An iteration that only improves the backlog
|
||||
is a valid iteration. If even that yields nothing, journal it and stop
|
||||
— do not manufacture churn.
|
||||
|
||||
### 2. Work
|
||||
- Keep the diff focused on the task. Unrelated fixes you trip over become
|
||||
PROPOSED/NOW entries, not scope creep.
|
||||
- Mechanical, fully-specified sub-steps go to a cheaper model
|
||||
(see *Model & token economy* above); design and review stay here.
|
||||
- Follow `CONVENTIONS.md`. Match the surrounding hand-formatting; never
|
||||
run a formatter.
|
||||
- New gotcha discovered the hard way → one line in `MEMORY.md` now, while
|
||||
it's fresh.
|
||||
|
||||
### 3. Verify — the ladder
|
||||
Climb as high as the change warrants and your environment allows; **record
|
||||
the tier reached** in the commit body and journal entry.
|
||||
|
||||
| Tier | What | When required |
|
||||
|---|---|---|
|
||||
| **V0** | `nix flake check --no-build` (+ `bash -n` / `py_compile` for scripts) | Every change, no exceptions |
|
||||
| **V1** | Build the touched output: `system.build.toplevel`, the HM generation, the ISO, or the package | Anything beyond docs/comments |
|
||||
| **V2** | VM: a `checks.*` runNixOSTest (add one if the change is guardable), or boot `tools/test-live-iso.sh` / `tools/test-install.sh` | Behavioral changes — services, boot, installer, session |
|
||||
| **V3** | Real hardware | Cannot be done by the agent → append to `HARDWARE-QUEUE.md` with exact test steps |
|
||||
|
||||
The honesty rule governs: a visual/interactive change verified only to V1
|
||||
is **not done** — it ships as "V1-verified, V2/V3 pending" with the pending
|
||||
check queued. Prefer *adding a permanent `checks.*` test* over a one-off
|
||||
manual VM poke when the behavior is testable headlessly (see MEMORY.md for
|
||||
the reusable recipes).
|
||||
|
||||
### 4. Commit + push
|
||||
- Style: match the log — `feat(scope): …`, `fix(scope): …`,
|
||||
`test(scope): …`, `docs(scope): …`. Body explains what/why + the
|
||||
verification tier reached and what remains.
|
||||
- Include the `agent/` bookkeeping updates (backlog/journal/memory/queue)
|
||||
**in the same commit** as the change they describe.
|
||||
- Commit directly on `main` and `git push` (Bernardo's standing workflow).
|
||||
- **Never:** force-push; touch the `v1` branch or any branch/tag you
|
||||
didn't create; commit secrets or binaries; run `nix flake update`
|
||||
unless the task is explicitly a lock bump; delete themes, wallpapers,
|
||||
or user-facing assets without the backlog saying so.
|
||||
|
||||
### 5. Record
|
||||
1. Mark the task in `BACKLOG.md` (move to its ✓ line or delete, per that
|
||||
file's rules).
|
||||
2. Append a `JOURNAL.md` entry (template in that file).
|
||||
3. Queue any V3 checks in `HARDWARE-QUEUE.md`.
|
||||
4. **Sync sweep.** Grep the item's number and feature name across
|
||||
`agent/` and `docs/` and update or delete every cross-reference the
|
||||
ship made stale: the PROPOSED pitch that spawned it (and BACKLOG's
|
||||
v1.0 pointer), HARDWARE-QUEUE entries it supersedes, ROADMAP/README/
|
||||
docs mentions of the old behavior. While there, prune checked-off
|
||||
`[x]` HARDWARE-QUEUE entries whose outcome is already recorded
|
||||
(journal/ROADMAP — git history is the archive). A shipped item must
|
||||
leave no stale pointer behind; the sweep rides in the same commit.
|
||||
|
||||
### 6. Pace (self-paced runners only)
|
||||
Under `/loop`, continue to the next iteration while tasks remain
|
||||
actionable and checks stay green. Stop the loop when: nothing is
|
||||
actionable, the same task has failed twice (journal the failure analysis
|
||||
and mark the item `[stuck]`), or a `[human]` decision blocks everything
|
||||
remaining.
|
||||
|
||||
## Stop-and-escalate conditions (any runner)
|
||||
|
||||
Write a journal entry + a BACKLOG note, then stop, when:
|
||||
- A fix would require touching `v1`, force-pushing, or a nixpkgs release
|
||||
jump.
|
||||
- The working tree contains uncommitted work you didn't create.
|
||||
- A task turns out to need a design decision Bernardo hasn't made → move
|
||||
it to **Decisions** in BACKLOG.md with the options laid out.
|
||||
- Two consecutive iterations failed on the same task (`[stuck]`).
|
||||
- Anything would delete or rewrite user data, git history, or the state
|
||||
file schema in a non-backward-compatible way.
|
||||
219
agent/MEMORY.md
219
agent/MEMORY.md
@@ -1,219 +0,0 @@
|
||||
# Memory — durable lessons, learned the hard way
|
||||
|
||||
Curated, not append-only: one line per fact, newest at the top of its
|
||||
section; delete entries that stop being true. Details usually live in a
|
||||
docs/ROADMAP.md decision record — pointer given as (§ item). Add a fact
|
||||
here the moment a debugging session teaches you something a future
|
||||
iteration would otherwise rediscover.
|
||||
|
||||
## Testing & VM recipes
|
||||
- **Doctor float V2:** `THEME=<slug> nix build --impure -f tools/doctor-float.nix`
|
||||
— softGL Hyprland, `nomarchy-menu doctor`, asserts
|
||||
`class=com.nomarchy.doctor` + `floating` + centered midpoints +
|
||||
screenshots (Kitty `--class=…`). SoftGL may still struggle with a GPU
|
||||
terminal; size can ignore percent windowrules if client geometry wins.
|
||||
- **theme-shot softGL may not start Kitty** — `btop.png` is best-effort
|
||||
(usually identical to desktop). Guest asserts on
|
||||
`~/.config/btop/themes/nomarchy.theme` prove baking; the TUI look is
|
||||
hardware/GL tier (HARDWARE-QUEUE).
|
||||
- **Waybar `custom/doctor` tripwire:** status helper must invoke
|
||||
`nomarchy-doctor` by **absolute store path** (waybar's env can miss
|
||||
system PATH → `command -v … || exit 0` self-hides forever); empty
|
||||
`"text"` also self-hides; strip ANSI before packing the tooltip; use
|
||||
signal 10 + `format = "{}"`. theme-shot asserts class:bad + glyph and
|
||||
pokes RTMIN+10 before the desktop shot.
|
||||
- **tuigreet dies silently under runNixOSTest** (even bare, no theme
|
||||
flag: greetd sits as "(greetd)" with no child, nothing in the
|
||||
journal — its stderr goes to the VT) — nixpkgs' own greetd test uses
|
||||
agreety instead. Greeter *rendering* is interactive-ISO/hardware
|
||||
tier; don't burn another session on a checks.greeter VM test.
|
||||
- In VM tests `pgrep -f PATTERN` can match the test backdoor's own
|
||||
`bash -c` wrapper (the pattern is in its cmdline) — use `pgrep -x`
|
||||
or a `[t]uigreet`-style bracket pattern.
|
||||
- **Don't default a "timer/session" feature to V3 — most of it is VM-testable.**
|
||||
A scheduled/session behaviour usually decomposes into a *generic* step
|
||||
already covered elsewhere (e.g. `home-manager switch`, exercised by every
|
||||
theme apply) and a *specific* decision (which theme, when). Stub the generic
|
||||
step (`NOMARCHY_REBUILD=<marker>` for theme-sync) and **simulate time by
|
||||
moving the VM clock** (`date -s`, `timedatectl set-ntp false`, `time.timeZone
|
||||
= "UTC"`), then assert the decision + state change headlessly. `checks.auto-theme`
|
||||
does exactly this for #79's sunset/sunrise. Only the literal
|
||||
timer-fires-on-`OnCalendar` is truly on-hardware, and `systemd-analyze
|
||||
calendar` validates that schedule. (I first mis-framed #79 as V3 — it's V2.)
|
||||
- **`writeShellScriptBin` scripts run `set -euo pipefail`** (nomarchy-doctor,
|
||||
the menu, lifecycle CLIs). So a **no-match `grep` inside `$(…)`** (grep exits
|
||||
1 → command-sub fails → abort) and a **standalone `cond && action`** (false
|
||||
cond → abort) both kill the script mid-run — the tell is output that stops
|
||||
before the final/verdict line with no error. Guard: `… | grep … || true`
|
||||
inside `$()`, `cmd 2>/dev/null || echo 0` for captures, and `if` instead of
|
||||
`&& action`. (#77 doctor hibernate section; caught by the checks.doctor VM
|
||||
test on first run.)
|
||||
- A checks.* fixture CANNOT be a writeText/toFile state file read at
|
||||
eval time ("path … is not valid" — flake check's eval store won't
|
||||
realise it): extract the logic into a pure importable file and
|
||||
unit-test THAT (monitor-rules.nix / checks.display-profiles is the
|
||||
pattern).
|
||||
- **Hibernation reference (Latitude / Newton, BACKLOG #76):** LUKS whole
|
||||
root BTRFS; `@swap` → `/swap`; file `/swap/swapfile`;
|
||||
`boot.resumeDevice` = LUKS root UUID; `resume_offset` from
|
||||
`btrfs inspect-internal map-swapfile -r`. Swap is encrypted with root
|
||||
(not a cleartext partition). No zram on that box yet — zram is additive
|
||||
for live pressure only. Installer already creates this when swapSize>0.
|
||||
- CI (`.gitea/workflows/check.yml`) is **eval-tier only** (standing
|
||||
decision 2026-07-10): act_runner docker-compose on the Gitea VPS; no
|
||||
KVM there. Full VM suite is BACKLOG **FUTURE #20**, not NEXT — needs a
|
||||
separate nix+/dev/kvm runner. Container gotchas are in the workflow
|
||||
header (single-user Nix + nixbld users, `sandbox=false` for Stylix
|
||||
IFD, Nix pinned 2.31.5 vs lazy-trees, no JS actions past node20).
|
||||
- The Gitea instance is **1.25.4** — `on: schedule` workflows are
|
||||
supported; bump.yml assumes the Actions token can push to `main`
|
||||
(standard Gitea behaviour, but unconfirmed until the first run lands).
|
||||
- The git server is **Gitea** (gitea/act_runner via docker-compose), NOT
|
||||
Forgejo — workflows are read from `.gitea/workflows/` (or `.github/`),
|
||||
never `.forgejo/workflows/` (a whole push cycle was lost to that).
|
||||
- Reusable headless VM harness: `checks.*` via runNixOSTest — existing
|
||||
examples to crib from: `distro-id` (boots + `switch-to-configuration
|
||||
dry-activate`), `hardware-toggles` (kernel cmdline/PAM assertions),
|
||||
`battery-charge-limit` (fake Mains adapter via `test_power`, real udev
|
||||
event burst while the oneshot is active; clean inactive result plus an
|
||||
`InvocationID` change proves coalesced AC re-apply). AC udev hooks for a
|
||||
settling oneshot must use `start`, never `restart`: USB-C docks emit event
|
||||
bursts and restarts SIGTERM the in-flight pass into `start-limit-hit`.
|
||||
- Themed-desktop screenshots work headlessly: software-GL Hyprland
|
||||
(`LIBGL_ALWAYS_SOFTWARE` on virtio-gpu) + `machine.screenshot()` QMP
|
||||
dump — prototyped 2026-06-19, kept as the fallback for theme previews
|
||||
(§ Visual theme picker).
|
||||
- Hyprland needs guest GL (`virtio-vga-gl`, `gl=on`) in interactive QEMU
|
||||
or the session won't start; black screen ≈ missing GL
|
||||
(docs/TESTING.md § gotchas). Kitty is the sole terminal (no Ghostty).
|
||||
- No KVM = slow, not broken; don't read slowness as failure.
|
||||
|
||||
## Known-broken / watchlist
|
||||
- **btrfs-assistant "segfault" was unprivileged-only** (re-diagnosed
|
||||
2026-07-04): libbtrfsutil's unprivileged subvolume iteration crashes on
|
||||
btrfs-progs 6.17.1 (upstream-fixed after); **as root it works**, and the
|
||||
pkexec launcher runs it as root. The real distro bug was **no polkit
|
||||
agent in the session** (every pkexec failed silently) — hyprpolkitagent
|
||||
now ships (hyprland.nix exec-once). `checks.snapshot-gui` guards the
|
||||
root path. Lesson: before "app X is broken", check WHO it runs as — and
|
||||
whether polkit prompts can render at all (§ Snapshot browse/restore).
|
||||
- **NixOS release bump is a trap:** the discarded attempt
|
||||
(branch deleted 2026-06-22) hit a Hyprland OOM blocker; a redo is a
|
||||
deliberate `v2`, never part of routine lock bumps.
|
||||
- `state.json` is git-tracked inside an 86 MB flake tree, so every
|
||||
state write re-copies the source before eval — the wallpapers-artifact
|
||||
split (BACKLOG LATER) is the decided fix (§ Faster switches).
|
||||
- **Friendly theme-state load** (`modules/state-read.nix`, #66):
|
||||
`builtins.tryEval` does **not** catch `readFile`/`fromJSON` failures —
|
||||
gate with `pathExists` + empty/non-object checks before `fromJSON`.
|
||||
Subtle JSON syntax errors still surface from nlohmann (line/col);
|
||||
field schema stays in `theme.nix`. mkFlake must `builtins.seq` the
|
||||
check onto the whole return set or lazy attr access skips it.
|
||||
|
||||
## Design invariants
|
||||
- **Dock transitions are ordered safety operations (#100):** dock in one
|
||||
`hyprctl --batch` (external on → every internal workspace moved → focus
|
||||
external → internal off); undock enables internal *before* moving anything.
|
||||
The Hyprland watcher, not a shell-pipeline subshell, owns a low-level
|
||||
`handle-lid-switch` inhibitor until the lid is physically open; startup
|
||||
cleans a validated stale process group. HDMI availability may appear only
|
||||
as `change:sink`, so fresh `monitoradded` is the intent boundary for the
|
||||
settled PipeWire/WirePlumber reprobe + sink pick; generic audio changes must
|
||||
not override a manual in-dock speaker choice.
|
||||
- **Waybar status is never color-only** (item 28 sweep, iteration #69):
|
||||
every status module must distinguish its states by SHAPE (glyph) or
|
||||
presence (self-hide), never color alone — good/warn/bad collapse under
|
||||
color-blindness. When adding a state, give it a distinct glyph or gate
|
||||
the module on it; a new `class` that only recolors an existing glyph is
|
||||
a regression. Suppressed notification states (DND *and* app-inhibited)
|
||||
all use the bell-off glyph + @muted.
|
||||
- **Identity themes are not traffic lights** (#69): white, vantablack,
|
||||
lumon, hackerman, matte-black, miasma — monochrome / mono-hue / earthy
|
||||
status by design. `audit-theme-design.py` tags their hue/CVD/ANSI-family
|
||||
findings `[identity]`; do not "fix" them into R/Y/G.
|
||||
- **Import hierarchy ≠ ANSI** (#70): `import-palettes.py` must not set
|
||||
surface==overlay when color0==color8; light color0 is often ANSI black
|
||||
(not a chip). Roles are first-class — never bulk-reimport shipped JSON
|
||||
without a hierarchy pass.
|
||||
|
||||
## Gotchas (cost a debugging session once)
|
||||
- **#127 forensics/method lessons (three wrong diagnoses' worth):** drive the
|
||||
real code path or measure nothing (a hand-run `dpms off` has no wake path
|
||||
watching it, so "input didn't wake it" was guaranteed); do not read output
|
||||
lists as lid positions — `/proc/acpi/button/lid/*/state` is the evidence
|
||||
(a reload can light a panel inside a shut lid, #148); and anchor forensics
|
||||
on the right boot — an *unclean boot end* (journald corruption + dirty bit
|
||||
on the next boot) is itself the incident marker, and `--list-boots` comes
|
||||
before any grep.
|
||||
- **OVMF exposes /dev/fd0 as TYPE=disk (#112):** disk picker must exclude
|
||||
`/dev/fd*` and tiny sizes; sort largest-first or blind Enter picks floppy.
|
||||
- **Live ISO offline theme switch (#113):** only the *default/pinned* HM
|
||||
generation is offline-safe; other presets may try to build the world —
|
||||
document the contract and fail with a network-oriented message.
|
||||
- **Installer HM pre-activate needs XDG_RUNTIME_DIR (+ session bus) (#123):**
|
||||
`runuser … activate` inside `nixos-enter` has no user session. Without
|
||||
`mkdir -p /run/user/$UID` owned by the install user and
|
||||
`XDG_RUNTIME_DIR` (prefer `dbus-run-session -- activate`), dconf dies
|
||||
with Permission denied and the bake aborts mid-way — first boot looks
|
||||
unthemed / missing nm-applet even though the system installed.
|
||||
- **ISO filename is `image.baseName` (#125):** volumeID alone does not rename
|
||||
`result/iso/…`; force `image.baseName` to `nomarchy-…` or the artifact
|
||||
stays `nixos-live-….iso`.
|
||||
- **Install flake ref must match the ISO branch (#124):** while `v1` lags
|
||||
`main`, seed `?ref=main` (and compose-lock original) from main-built ISOs;
|
||||
`NOMARCHY_FLAKE_URL` must actually rewrite `inputs.nomarchy.url` (was set
|
||||
but unused until #124).
|
||||
- Gum `filter` returns unmatched typed text by default; catalog-only pickers
|
||||
require `--strict` plus an independent exact-membership validation boundary.
|
||||
- Waybar `layer: top` renders above **even real-fullscreen windows** — the
|
||||
bar draws over a fullscreen video. `layer: bottom` lets the fullscreen
|
||||
surface cover it while the exclusive zone still reserves the bar's space
|
||||
in normal tiling (trade-off: floating windows can now overlap the bar
|
||||
strip). Set in both waybar.nix and every whole-swap jsonc (item 30).
|
||||
- Hyprland binds match the exact modmask: a shifted keysym (`question`)
|
||||
needs SHIFT in `mods` or the bind never fires — the keypress falls
|
||||
through to the focused window (§ item 26; caught on hardware, invisible
|
||||
to eval-tier tests).
|
||||
- Never kill a Wayland session-lock client (hyprlock): its crash
|
||||
failsafe drops to a tty instead of unlocking (§ Hibernate
|
||||
double-unlock).
|
||||
- rofi `element-icon size` is one value = a square cell; `WxH` silently
|
||||
collapses and non-square icons letterbox — pre-crop images square at
|
||||
build (§ Visual theme picker).
|
||||
- WirePlumber 0.5 monitor rules can only early-match `device.api`;
|
||||
`device.product.name` etc. bind *after* the rule runs — surgical
|
||||
libcamera scoping is impossible (§ Webcam).
|
||||
- `hyprctl switchxkblayout` is a *global* layout flip; per-device isolation
|
||||
needs `device[<name>]:kb_layout` keywords (§ Keyboard layouts).
|
||||
- Waybar's clock captures the timezone at construction — a zone change
|
||||
needs SIGUSR2 (watcher in `timezone.nix`) (§ Automatic timezone).
|
||||
- Waybar `persistent_workspaces` (underscore) is dead syntax silently
|
||||
ignored; the hyphen form is honoured and renders phantom workspaces
|
||||
(§ Waybar shows non-existent workspaces).
|
||||
- GTK4/libadwaita/Qt6 read light/dark from the portal's
|
||||
`org.freedesktop.appearance color-scheme` (dconf), not Stylix polarity
|
||||
(§ GTK/Qt ignore the theme's mode).
|
||||
- Update order matters downstream: `nomarchy-pull` (lock) then
|
||||
`nomarchy-rebuild` then `nomarchy-home`, or desktop changes are silently
|
||||
skipped against the old
|
||||
lock (README § 3).
|
||||
- Hyprland 0.55 renames `stayfocused` → `stay_focused` (and similar
|
||||
underscore effects); `stayfocused 1` is `invalid field type` at parse
|
||||
(§ polkit workspace rules, 2026-07-10 hardware).
|
||||
- Never gate a safety listener behind the optional feature it also serves:
|
||||
the display menu existed with no profiles while its blackout rescue did
|
||||
not. Rofi defaults to mouse-pointer output (`monitor=-5`), which is stale
|
||||
in clamshell mode; use focused output (`-1`) for keyboard-launched UI.
|
||||
- Hyprland 0.53 rewrote window rules: `windowrulev2` is a hard error and
|
||||
the old rule-first `float, class:^…$` no longer parses — both surface a
|
||||
red config-error banner on the default desktop. Hyprlang legacy form is
|
||||
now `<effect> <value>, match:<prop> ^…$` (e.g. `float 1, match:class ^…$`);
|
||||
effects carry a value, matchers take `match:` (§ windowrule migration).
|
||||
- grub `loadfont`s every `.pf2` in a theme dir — reuse a bundled DejaVu
|
||||
rather than shipping fonts (§ Distro branding).
|
||||
- Agent instructions live vendor-neutrally in `agent/` (VERIFICATION,
|
||||
DELEGATION, THEME-DESIGN; entry AGENTS.md) — `.claude/` is a thin
|
||||
adapter (permissions + subagent defs only; skills were removed
|
||||
2026-07-11). Never `git add -A` blindly: check `git status --short`
|
||||
for genuine strangers first (`settings.local.json`, harness-dropped
|
||||
files) and commit with explicit pathspecs (§ loop hygiene).
|
||||
@@ -1,54 +0,0 @@
|
||||
# Agent instructions + loop state
|
||||
|
||||
Everything an AI agent needs to work on Nomarchy, vendor-neutral and
|
||||
git-tracked. Protocol: **[LOOP.md](LOOP.md)**. Entry point for every
|
||||
harness: repo-root **[AGENTS.md](../AGENTS.md)**.
|
||||
|
||||
## Instructions (how to work)
|
||||
|
||||
| File | Who writes | Role |
|
||||
|------|------------|------|
|
||||
| [LOOP.md](LOOP.md) | Human | One-iteration protocol (orient → pick → work → verify → commit → record) + the V0–V3 ladder |
|
||||
| [VERIFICATION.md](VERIFICATION.md) | Human (agents propose) | Enforcement: preflight, honesty rules, visual protocol, hardware-blocked checks, reporting |
|
||||
| [DELEGATION.md](DELEGATION.md) | Human (agents propose) | Capability tiers, scout/runner roles, token economy, parallel fan-out |
|
||||
| [GOALS.md](GOALS.md) | Human (agents propose) | Pillars, quality bars, non-goals |
|
||||
| [CONVENTIONS.md](CONVENTIONS.md) | Human (agents propose) | How to write code/menu/state while shipping |
|
||||
| [THEME-DESIGN.md](THEME-DESIGN.md) | Human (agents propose) | Theme/visual design instructions |
|
||||
|
||||
## State (what's happening)
|
||||
|
||||
| File | Who writes | Role |
|
||||
|------|------------|------|
|
||||
| [BACKLOG.md](BACKLOG.md) | Both | **Prioritized queue** — only executable work list |
|
||||
| [JOURNAL.md](JOURNAL.md) | Agents | Append-only iteration log (read last 3–5 entries; older → [JOURNAL-ARCHIVE.md](JOURNAL-ARCHIVE.md)) |
|
||||
| [MEMORY.md](MEMORY.md) | Agents | Curated durable gotchas |
|
||||
| [HARDWARE-QUEUE.md](HARDWARE-QUEUE.md) | Agents append, human checks | On-hardware V3 tests only Bernardo can run |
|
||||
|
||||
## Product / design docs (not a queue)
|
||||
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| [../docs/VISION.md](../docs/VISION.md) | v1.0 product themes — agents slice into BACKLOG PROPOSED |
|
||||
| [../docs/ROADMAP.md](../docs/ROADMAP.md) | Design history + shipped log |
|
||||
| [../docs/README.md](../docs/README.md) | Full docs map |
|
||||
|
||||
## Harness adapters (vendor-specific, thin)
|
||||
|
||||
Shared content never lives in an adapter — adapters only register/route
|
||||
into the files above, in whatever format their harness requires.
|
||||
|
||||
| Path | Harness | Role |
|
||||
|------|---------|------|
|
||||
| [../AGENTS.md](../AGENTS.md) | any | Entry point (`CLAUDE.md` is a symlink to it) |
|
||||
| [../.claude/settings.json](../.claude/settings.json) | Claude Code | Tool permissions |
|
||||
| [../.claude/agents/](../.claude/agents/) | Claude Code | `nomarchy-scout` / `nomarchy-runner` role defs (contracts in [DELEGATION.md](DELEGATION.md)) |
|
||||
|
||||
Do **not** put backlog items, vision text, or policy under an adapter
|
||||
directory — it is not shared with other agent runners.
|
||||
|
||||
## Rules of thumb
|
||||
|
||||
1. **Execute** from BACKLOG only (NOW → NEXT; never PROPOSED without human triage).
|
||||
2. **Orient** with GOALS + CONVENTIONS + MEMORY + last journal + BACKLOG; when the task is product-shaped, also read the relevant **VISION §**.
|
||||
3. **Record** lasting design in ROADMAP ✓ when something ships that future humans should know; delete the BACKLOG line.
|
||||
4. **v1 branch** is human-only — never advance from an agent session.
|
||||
@@ -1,49 +0,0 @@
|
||||
# Theme design — instructions for visual work
|
||||
|
||||
For creating, updating, refining, or troubleshooting Nomarchy themes and
|
||||
visual design. Act as an experienced UI/UX designer and Linux ricing
|
||||
expert for Wayland environments: themes here must be cohesive,
|
||||
intentional, and visually striking — aesthetics are a load-bearing
|
||||
feature of this distro (`agent/GOALS.md` pillar 4).
|
||||
|
||||
## Context & architecture
|
||||
|
||||
All theme data lives in `themes/` at the repo root — one JSON palette per
|
||||
theme plus per-app assets (whole-swap `waybar.jsonc`/CSS, btop themes,
|
||||
wallpapers). Before generating any new configuration, read the existing
|
||||
files there: learn how current themes are structured, how the syntax is
|
||||
formatted for each application, and how they integrate into the NixOS/HM
|
||||
modules. Always match the established pattern — there is no second
|
||||
theming pipeline (`agent/GOALS.md` non-goals), so a new visual surface
|
||||
consumes the palette from the state JSON, never a side file.
|
||||
|
||||
## Design responsibilities
|
||||
|
||||
1. **Holistic design:** a theme spans Hyprland (borders, shadows,
|
||||
animations), Waybar, Kitty, btop, fastfetch, rofi, and wallpaper —
|
||||
one coherent identity, no unthemed corner.
|
||||
2. **Color theory:** create or adapt advanced palettes. Draw inspiration
|
||||
from established aesthetics (Everforest, Nord, Gruvbox) or r/unixporn
|
||||
trends, but innovate. Ensure harmony between background, foreground,
|
||||
accents, and warning/error colors.
|
||||
3. **Typography & iconography:** pair UI and monospace fonts and icon
|
||||
themes to the specific vibe of the palette.
|
||||
4. **Accessibility:** high contrast for text readability; status
|
||||
information is never color-only (see `agent/MEMORY.md` design
|
||||
invariants — glyph/shape carries state, and identity themes are
|
||||
deliberately not traffic-lights).
|
||||
|
||||
## Process
|
||||
|
||||
1. Read `themes/` to understand the current structure.
|
||||
2. State the "vibe", the primary palette (hex codes), and typography
|
||||
choices of the proposed design before implementing.
|
||||
3. Implement by generating or updating files within the `themes/`
|
||||
structure, following `agent/CONVENTIONS.md` (Waybar parity rule:
|
||||
whole-swap `waybar.jsonc` files stay in sync with the generated
|
||||
config).
|
||||
4. Verify per `agent/VERIFICATION.md` §3 — scripted checks first
|
||||
(`tools/check-theme-contrast.py`, `tools/audit-theme-design.py`), then
|
||||
headless before/after screenshots you actually view, under at least
|
||||
two themes. New themes are imported via `tools/import-palettes.py` and
|
||||
round-tripped through the theme switcher.
|
||||
@@ -1,181 +0,0 @@
|
||||
# Verification — the enforcement rules
|
||||
|
||||
Nomarchy's promise to its user: a rock-stable, fully functional, beautiful
|
||||
workstation that is reproducible, easy to recover, and never requires the
|
||||
user to become a Nix expert. Every rule below exists to protect that
|
||||
promise. A change that works but degrades stability, aesthetics, or
|
||||
user-simplicity is a regression, not a feature.
|
||||
|
||||
This document is an **enforcer**, not the workflow itself: the ladder and
|
||||
iteration protocol live in `agent/LOOP.md`, VM instructions and gotchas in
|
||||
`docs/TESTING.md`. It applies to **every** change to this repo — features,
|
||||
fixes, theming, module changes, lock bumps, docs, backlog grooming.
|
||||
"Small" or "obvious" changes are exactly where verification gets skipped,
|
||||
so they trigger it too. If this file and those docs ever disagree, fix the
|
||||
discrepancy in the same or a follow-up commit so they can't disagree
|
||||
twice.
|
||||
|
||||
## 1. Preflight (once per session)
|
||||
|
||||
Before starting work, establish what verification tier this environment
|
||||
can reach, so you never promise verification you can't deliver:
|
||||
|
||||
1. Linux x86_64 host? `/dev/kvm` present and readable?
|
||||
2. Enough free disk for an image/ISO build (multi-GB)?
|
||||
3. Network access for a cold Nix store?
|
||||
|
||||
If the environment cannot reach V2 (no KVM, no disk, etc.): say so
|
||||
immediately, do the V0/V1 work honestly, mark the change **"V2 pending"**
|
||||
exactly as you would mark a hardware-blocked change "V3 pending" (§4),
|
||||
and stop short of claiming the change is done. Never simulate, guess, or
|
||||
describe what a VM test "would" show.
|
||||
|
||||
## 2. The verification ladder (enforcement rules)
|
||||
|
||||
Climb the V0–V3 ladder as defined in `agent/LOOP.md`. Four non-negotiable
|
||||
enforcement rules on top:
|
||||
|
||||
1. **V2 is mandatory for anything user-visible.** If a user of the
|
||||
installed system could perceive the change — behavior, layout, colors,
|
||||
keybinds, timing, error messages — it must be exercised in the local
|
||||
VM before commit. Docs-only, comment-only, or agent-notes changes may
|
||||
stop at the tier LOOP.md assigns them; user-visible changes may not.
|
||||
2. **Every "done" report names the tier reached and shows the evidence.**
|
||||
Evidence means: the command run and its relevant output, the checklist
|
||||
items exercised, and for visual work the screenshots viewed (§3). "It
|
||||
builds" is a V1 claim, not a V2 claim. Never let a report imply a
|
||||
higher tier than was actually reached.
|
||||
3. **A failed or flaky test is a result, not an obstacle.** Distinguish
|
||||
real failures from environment flakes using the known-gotchas section
|
||||
of `docs/TESTING.md` (e.g. no-KVM slowness, missing guest GL). If you
|
||||
cannot confidently classify a failure, report it as unresolved — do
|
||||
not retry until green and report only the green run.
|
||||
4. **VM runs are headless and unattended.** Use the repo's headless
|
||||
harness — `tools/test-live-iso.sh` and `tools/test-install.sh` for
|
||||
boot/install runs, `tools/vm/qmp.py` for programmatic VM control and
|
||||
`tools/vm/vncshot.py` for screen capture — never a graphical VM window
|
||||
or any flow that needs a human at the console. The human is not part
|
||||
of the test loop: do not pause mid-run to ask them to look at the VM,
|
||||
click something, or confirm what is on screen. A run must complete on
|
||||
its own and leave auditable artifacts behind (logs, serial console
|
||||
output, exit codes, screenshots), with every wait bounded by a timeout
|
||||
so a hang becomes a recorded failure instead of a stalled session.
|
||||
Prefer scripted assertions (process up, file exists, service/D-Bus
|
||||
state, the checks in `tools/`) over eyeballing; where judgment is
|
||||
genuinely needed — visual quality — *you* view the captured
|
||||
screenshots (§3), not the human. The human reviews evidence in the
|
||||
final report, never the live run.
|
||||
|
||||
### Regression scope after a change
|
||||
|
||||
Re-running the full checklist for every change wastes VM time; running
|
||||
nothing invites regressions. Default rule:
|
||||
|
||||
- Always: the session-sanity items (boot to session, bar renders).
|
||||
- Plus: every checklist item touching the layer you changed.
|
||||
- Plus: the theming end-to-end item whenever theming plumbing changed,
|
||||
even indirectly (palette generation, symlinks, reload hooks).
|
||||
- Lock bumps and toolchain changes: run the full checklist — their blast
|
||||
radius is unknowable by construction.
|
||||
|
||||
## 3. Visual verification protocol
|
||||
|
||||
Visual quality is a core feature of Nomarchy, so "it probably looks fine"
|
||||
is never verification. A visual/UI change is not V2-verified until all of
|
||||
the following are true:
|
||||
|
||||
1. **Before/after screenshots** of the changed surface were captured
|
||||
headlessly — `tools/theme-shot.nix` for reproducible theme renders,
|
||||
`tools/vm/vncshot.py` (driven via `tools/vm/qmp.py`) for captures from
|
||||
a running VM. No VM window, no human interaction. Capture the "before"
|
||||
from the base branch or prior generation, not from memory.
|
||||
2. **Scripted checks first**: run `tools/check-theme-contrast.py` and
|
||||
`tools/audit-theme-design.py` against the affected theme(s) before any
|
||||
eyeballing — machine-checkable legibility/design violations should
|
||||
never survive to the judgment stage. Use `tools/vm/gap-analysis.py`
|
||||
where it applies.
|
||||
3. **Two themes**: repeat the "after" capture under at least two themes,
|
||||
one with a generated palette and one whole-swap theme (e.g.
|
||||
summer-night). These exercise different code paths in the bar/launcher
|
||||
theming; a change that looks right under one can silently break the
|
||||
other.
|
||||
4. **You actually viewed the images** — open the screenshot files and
|
||||
look at them. State concretely what you inspected: alignment, spacing,
|
||||
contrast/legibility against the palette, icon rendering, no clipped or
|
||||
overlapping elements, and that the change looks intentional next to
|
||||
the "before".
|
||||
5. Keep the screenshots in the run's working area and reference their
|
||||
paths in the report, so the human can audit the same evidence.
|
||||
|
||||
If the VM cannot render the surface faithfully (known GL/compositor gaps
|
||||
in the guest — see `docs/TESTING.md`), that specific visual aspect is
|
||||
hardware-blocked: verify everything the VM *can* show, and queue the rest
|
||||
per §4.
|
||||
|
||||
## 4. Hardware-blocked checks
|
||||
|
||||
Some checks genuinely require real hardware (GPU behavior, multi-monitor
|
||||
hotplug, audio devices, power/suspend, firmware). For those:
|
||||
|
||||
1. Add an entry to `agent/HARDWARE-QUEUE.md` with: what changed, **exact**
|
||||
reproduction steps a human can follow verbatim, the expected
|
||||
observation (what "pass" looks like), and the commit hash once known.
|
||||
2. Mark the commit body **"V3 pending: <one-line summary>"**.
|
||||
3. Say it plainly in your report. A hardware-blocked check is not a
|
||||
failure and not something to hide — hiding it is the failure.
|
||||
4. When the human reports back, close the queue entry in the next commit
|
||||
and record the outcome; if it failed on hardware, that's a new bug at
|
||||
the top of the backlog.
|
||||
|
||||
Do not use the hardware queue as an escape hatch: if a check *can* be
|
||||
done in the VM, it must be. "The VM is slow" does not qualify.
|
||||
|
||||
## 5. Maintenance work
|
||||
|
||||
Maintenance follows the same ladder:
|
||||
|
||||
- **Flake lock bumps**: treat as maximum-blast-radius changes. Build,
|
||||
boot the VM, run the full regression checklist, and do a visual
|
||||
spot-check of the session (themes can shift with upstream package
|
||||
changes). Never merge a lock bump on "it evaluates". Since #134 the
|
||||
lock carries **two channels** (the release pin and the
|
||||
`nixos-unstable` pin feeding `unstable.*`) — a bump moves both, and
|
||||
this checklist applies to both.
|
||||
- **Theme imports / new themes**: import via `tools/import-palettes.py`,
|
||||
then the full §3 visual protocol; additionally verify the theme-switch
|
||||
round trip (into the new theme and back out).
|
||||
- **Docs drift**: run `tools/check-option-docs.py` after any change that
|
||||
adds or modifies options, and fix drift in the same commit as the code
|
||||
change that created it. Doc-only fixes are V0 — but verify any command
|
||||
you document by actually running it.
|
||||
- **Backlog grooming / agent-notes**: V0; keep entries consistent with
|
||||
the conventions in `agent/`.
|
||||
|
||||
## 6. Guarding the philosophy
|
||||
|
||||
Before committing, check the change against the distro's promises
|
||||
(`agent/GOALS.md` is the full statement):
|
||||
|
||||
- **User is not a Nix expert.** If the change requires the user to write
|
||||
or read Nix to use the feature day-to-day, redesign it. Configuration
|
||||
the user touches must stay in the simple, documented surface the repo
|
||||
defines.
|
||||
- **Rock-stable and recoverable.** Prefer boring, reproducible mechanisms
|
||||
over clever ones. Any change that could break boot or the session must
|
||||
have an obvious rollback story (NixOS generations count, but say so).
|
||||
- **Aesthetics are load-bearing.** A functionally correct but visually
|
||||
regressive change fails review by definition — that's what §3 is for.
|
||||
|
||||
When a requested change conflicts with these promises, stop and raise the
|
||||
conflict instead of implementing it quietly.
|
||||
|
||||
## 7. Reporting format
|
||||
|
||||
End every unit of work with a short report containing:
|
||||
|
||||
1. What changed (one paragraph, plain language).
|
||||
2. Verification tier reached, with evidence (commands + key output,
|
||||
checklist items run, screenshot paths viewed).
|
||||
3. Anything pending: "V2 pending" (environment) or "V3 pending"
|
||||
(hardware, with queue entry reference).
|
||||
4. Follow-ups added to the backlog, if any.
|
||||
109
bin/utils/nomarchy-docs-keybindings
Executable file
109
bin/utils/nomarchy-docs-keybindings
Executable file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# nomarchy-docs-keybindings
|
||||
#
|
||||
# Regenerates docs/KEYBINDINGS.md from the Hyprland binding files. Run from the
|
||||
# repo root or anywhere — paths are resolved relative to this script.
|
||||
#
|
||||
# nomarchy-docs-keybindings # write to stdout
|
||||
# nomarchy-docs-keybindings --out docs/KEYBINDINGS.md
|
||||
#
|
||||
# Source files in render order. Each entry is "<repo-relative path>|<title>".
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
|
||||
sources=(
|
||||
"core/home/config/nomarchy/default/hypr/bindings/utilities.conf|Utilities"
|
||||
"core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf|Tiling"
|
||||
"core/home/config/nomarchy/default/hypr/bindings/clipboard.conf|Clipboard"
|
||||
"core/home/config/nomarchy/default/hypr/bindings/media.conf|Media keys"
|
||||
"features/desktop/hyprland/config/bindings.conf|Apps & web shortcuts"
|
||||
)
|
||||
|
||||
prettify_key() {
|
||||
case "$1" in
|
||||
code:10) echo "1" ;; code:11) echo "2" ;; code:12) echo "3" ;;
|
||||
code:13) echo "4" ;; code:14) echo "5" ;; code:15) echo "6" ;;
|
||||
code:16) echo "7" ;; code:17) echo "8" ;; code:18) echo "9" ;;
|
||||
code:19) echo "0" ;;
|
||||
XF86AudioRaiseVolume) echo "Volume Up" ;;
|
||||
XF86AudioLowerVolume) echo "Volume Down" ;;
|
||||
XF86AudioMute) echo "Mute" ;;
|
||||
XF86AudioMicMute) echo "Mic Mute" ;;
|
||||
XF86AudioPlay) echo "Play/Pause" ;;
|
||||
XF86AudioStop) echo "Stop" ;;
|
||||
XF86AudioNext) echo "Next Track" ;;
|
||||
XF86AudioPrev) echo "Previous Track" ;;
|
||||
XF86MonBrightnessUp) echo "Brightness Up" ;;
|
||||
XF86MonBrightnessDown) echo "Brightness Down" ;;
|
||||
XF86KbdBrightnessUp) echo "Kbd Brightness Up" ;;
|
||||
XF86KbdBrightnessDown) echo "Kbd Brightness Down" ;;
|
||||
XF86KbdLightOnOff) echo "Kbd Backlight" ;;
|
||||
*) echo "$1" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
trim() { sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//'; }
|
||||
|
||||
render_section() {
|
||||
local file="$1" title="$2"
|
||||
[[ ! -f "$repo_root/$file" ]] && return
|
||||
local rows
|
||||
rows=$(grep -E '^[[:space:]]*bind[a-z]*[[:space:]]*=' "$repo_root/$file" || true)
|
||||
[[ -z "$rows" ]] && return
|
||||
|
||||
local body=""
|
||||
while IFS= read -r line; do
|
||||
# Strip the "bindXXX =" prefix.
|
||||
local rhs="${line#*=}"
|
||||
local mods key desc
|
||||
IFS=',' read -r mods key desc _ <<<"$rhs"
|
||||
mods=$(printf '%s' "${mods:-}" | trim)
|
||||
key=$(printf '%s' "${key:-}" | trim)
|
||||
desc=$(printf '%s' "${desc:-}" | trim)
|
||||
[[ -z "$desc" ]] && continue # skip non-descriptive bindings
|
||||
[[ -z "$mods" ]] && mods="—"
|
||||
key=$(prettify_key "$key")
|
||||
body+=$(printf '| %s | %s | %s |\n' "$mods" "$key" "$desc")
|
||||
body+=$'\n'
|
||||
done <<<"$rows"
|
||||
|
||||
[[ -z "$body" ]] && return
|
||||
|
||||
printf '\n## %s\n\n' "$title"
|
||||
printf '_Source: `%s`_\n\n' "$file"
|
||||
printf '| Modifiers | Key | Action |\n'
|
||||
printf '| --- | --- | --- |\n'
|
||||
printf '%s' "$body"
|
||||
}
|
||||
|
||||
main() {
|
||||
cat <<'HEADER'
|
||||
# Nomarchy Keybindings
|
||||
|
||||
Auto-generated from the Hyprland binding files. **Do not edit by hand.**
|
||||
Re-run the generator after changing any `bindings/*.conf`:
|
||||
|
||||
```bash
|
||||
./bin/utils/nomarchy-docs-keybindings --out docs/KEYBINDINGS.md
|
||||
```
|
||||
|
||||
`SUPER` is the Meta / Win key. `code:NN` keys (X11 digit keycodes) are
|
||||
shown as the digit they correspond to. Media keys (`XF86Audio*`,
|
||||
`XF86MonBrightness*`, …) are prettified.
|
||||
HEADER
|
||||
for entry in "${sources[@]}"; do
|
||||
render_section "${entry%|*}" "${entry#*|}"
|
||||
done
|
||||
}
|
||||
|
||||
out=""
|
||||
if [[ "${1:-}" == "--out" ]]; then
|
||||
out="${2:?--out needs a path}"; shift 2
|
||||
fi
|
||||
if [[ -n "$out" ]]; then
|
||||
main >"$out"
|
||||
else
|
||||
main
|
||||
fi
|
||||
276
bin/utils/nomarchy-docs-scripts
Executable file
276
bin/utils/nomarchy-docs-scripts
Executable file
@@ -0,0 +1,276 @@
|
||||
#!/usr/bin/env bash
|
||||
# Generator tolerates "no matches" exit codes from grep | sort.
|
||||
# pipefail and -e off; -u stays.
|
||||
set -u
|
||||
|
||||
# nomarchy-docs-scripts
|
||||
#
|
||||
# Regenerates docs/SCRIPTS.md from the repo state. Produces:
|
||||
# 1. Header + status legend + regen instructions.
|
||||
# 2. Table of every nomarchy-* script (location, callers, status).
|
||||
# 3. Table of every menu entry in features/scripts/utils/nomarchy-menu
|
||||
# (submenu, label, target command, status).
|
||||
# 4. Snapshot list of orphaned references (called somewhere, no script).
|
||||
#
|
||||
# Status heuristic in Phase A:
|
||||
# kept — file exists AND is called from at least one *.nix / *.conf /
|
||||
# shell file outside its own directory.
|
||||
# unused? — file exists but no caller found. Phase B decides delete-dead
|
||||
# vs intentional public API.
|
||||
# missing — referenced but no file. Phase B decides port-from-omarchy
|
||||
# vs delete-dead vs stub-with-notify.
|
||||
#
|
||||
# nomarchy-docs-scripts # write to stdout
|
||||
# nomarchy-docs-scripts --out docs/SCRIPTS.md
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "$repo_root"
|
||||
|
||||
# --- Inventory -------------------------------------------------------------
|
||||
|
||||
# Where scripts live, in render order.
|
||||
declare -A loc_label=(
|
||||
["features/scripts/utils"]="features/scripts/utils"
|
||||
["core/system/scripts"]="core/system/scripts"
|
||||
["themes/engine/scripts"]="themes/engine/scripts"
|
||||
)
|
||||
script_dirs=(features/scripts/utils core/system/scripts themes/engine/scripts)
|
||||
|
||||
# Build name → location map (associative array of basename → repo-relative dir).
|
||||
declare -A script_loc
|
||||
for dir in "${script_dirs[@]}"; do
|
||||
[[ -d "$dir" ]] || continue
|
||||
while IFS= read -r f; do
|
||||
script_loc["$(basename "$f")"]="$dir"
|
||||
done < <(find "$dir" -maxdepth 1 -type f -name 'nomarchy-*')
|
||||
done
|
||||
|
||||
# Find every nomarchy-* token referenced anywhere outside the script dirs.
|
||||
# (We exclude the script files themselves so they don't list themselves as
|
||||
# their own caller.)
|
||||
# File types we search for references. *.md catches docs and README;
|
||||
# branding/hook/extension files have varied or no extensions.
|
||||
# *.lua catches elephant providers; *.ini catches mako on-button-* hooks;
|
||||
# *.desktop catches MimeType-registered URL handlers.
|
||||
grep_includes=(
|
||||
--include='*.nix' --include='*.conf' --include='*.sh' --include='*.md'
|
||||
--include='nomarchy-*' --include='*.jsonc' --include='*.json'
|
||||
--include='*.toml' --include='*.ini' --include='*.lua'
|
||||
--include='*.desktop' --include='*.txt' --include='*.sample'
|
||||
)
|
||||
search_dirs=(core features themes installer hosts bin lib README.md)
|
||||
|
||||
# Files whose mentions of nomarchy-* are documentation about the scripts,
|
||||
# not real callers. Excluded from caller discovery so they don't promote
|
||||
# every script to `kept`.
|
||||
self_refs=(docs/SCRIPTS.md docs/ROADMAP.md docs/AGENT.md)
|
||||
|
||||
ref_files_per_cmd() {
|
||||
local cmd="$1"
|
||||
local self_pattern
|
||||
self_pattern=$(IFS='|'; echo "${self_refs[*]}")
|
||||
grep -rlE "\\b${cmd}\\b" \
|
||||
"${grep_includes[@]}" \
|
||||
"${search_dirs[@]}" 2>/dev/null \
|
||||
| grep -vE "^(features/scripts/utils|core/system/scripts|themes/engine/scripts)/${cmd}$" \
|
||||
| grep -vE "^(${self_pattern})$" \
|
||||
| sort -u
|
||||
}
|
||||
|
||||
# All distinct nomarchy-* tokens we see anywhere in the repo.
|
||||
# Final char must be alphanumeric — dropping trailing-dash matches like
|
||||
# `nomarchy-pkg-` that come from glob references (`for c in nomarchy-pkg-*`).
|
||||
# Restrict to grep_includes so binaries / tmpfiles don't pollute the set.
|
||||
# The first `grep -vE` drops lines where `nomarchy-*` is a derivation /
|
||||
# tmp file / sudoers basename / systemd unit / flake output / docker
|
||||
# container identifier rather than a shell invocation.
|
||||
# The second `grep -vE` is a token-level safety net for prefix-only
|
||||
# tokens left over from wildcards/expansions (e.g. `nomarchy-pkg-*`).
|
||||
all_refs=$(grep -rhE 'nomarchy-[a-z0-9]([a-z0-9-]*[a-z0-9])?' \
|
||||
"${grep_includes[@]}" \
|
||||
"${search_dirs[@]}" 2>/dev/null \
|
||||
| grep -vE \
|
||||
-e '(pname|name)[[:space:]]*=[[:space:]]*"nomarchy-' \
|
||||
-e '/tmp/nomarchy-' \
|
||||
-e '/etc/sudoers\.d/[^"[:space:]]*nomarchy-' \
|
||||
-e 'nixosConfigurations\.nomarchy-' \
|
||||
-e 'packages\.[^.]+\.nomarchy-' \
|
||||
-e '\./result/bin/run-nomarchy-' \
|
||||
-e 'mktemp[[:space:]]+[^|]*-t[[:space:]]+nomarchy-' \
|
||||
-e '(TIMER_NAME|NOPASSWD_FILE|UNIT_NAME)=.*nomarchy-' \
|
||||
-e 'docker[[:space:]]+[^|]*nomarchy-' \
|
||||
| grep -oE 'nomarchy-[a-z0-9]([a-z0-9-]*[a-z0-9])?' \
|
||||
| grep -vE '^(nomarchy-launch|nomarchy-brightness|nomarchy-cmd|nomarchy-pkg|nomarchy-restart|nomarchy-toggle|nomarchy-theme|nomarchy-webapp-handler|nomarchy-font-selector|nomarchy-theme-selector|nomarchy-wallpaper-selector|nomarchy-setup|nomarchy-refresh|nomarchy-scripts|nomarchy-system-scripts|nomarchy-theme-engine-scripts)$' \
|
||||
| grep -vE '^(nomarchy-plymouth|nomarchy-sddm-theme|nomarchy-live|nomarchy-rev|nomarchy-windows)$' \
|
||||
| sort -u)
|
||||
# The second denylist covers identifiers whose ambiguity survives the line
|
||||
# filter: `nomarchy-plymouth` / `nomarchy-sddm-theme` are Nix derivation
|
||||
# names referenced as bare idents in `[...]` lists, `nomarchy-live` is an
|
||||
# ISO label that shows up in comments, `nomarchy-rev` is `/etc/nomarchy-rev`
|
||||
# (written by the ISO), and `nomarchy-windows` is a docker container name
|
||||
# in compose heredocs.
|
||||
|
||||
# --- Render: header --------------------------------------------------------
|
||||
|
||||
main() {
|
||||
cat <<'HEADER'
|
||||
# Nomarchy Script & Menu Audit
|
||||
|
||||
Auto-generated table for [Pillar 3 of the roadmap](ROADMAP.md#3-pillar-script--menu-audit).
|
||||
**Do not edit by hand.** Regenerate after script or menu changes:
|
||||
|
||||
```bash
|
||||
./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md
|
||||
```
|
||||
|
||||
The status column uses a Phase A heuristic — `kept` / `unused?` / `missing`.
|
||||
Phase B (per-batch PRs) refines those into `port-from-omarchy`,
|
||||
`delete-dead`, or `stub-with-notify` and updates the rows.
|
||||
|
||||
## Status legend
|
||||
|
||||
- `kept` — script exists and is called from somewhere outside its own directory.
|
||||
- `unused?` — script exists but no caller was found. Could be dead, could be
|
||||
intentional public API. Phase B triage decides.
|
||||
- `missing` — referenced from code but no script file exists. Phase B triage
|
||||
decides whether to port from Omarchy upstream, delete the caller, or stub
|
||||
with `notify-send`.
|
||||
- `port-from-omarchy` — Phase B verdict: lift the upstream Omarchy script,
|
||||
rewrite for NixOS paths.
|
||||
- `delete-dead` — Phase B verdict: remove and update callers.
|
||||
- `stub-with-notify` — Phase B verdict: temporary `notify-send` stub.
|
||||
|
||||
HEADER
|
||||
|
||||
# --- Render: scripts table ----------------------------------------------
|
||||
printf '## Scripts (%d)\n\n' "${#script_loc[@]}"
|
||||
printf '| Script | Location | Callers | Status | Notes |\n'
|
||||
printf '| --- | --- | --- | --- | --- |\n'
|
||||
|
||||
# Sort scripts by name.
|
||||
for name in $(printf '%s\n' "${!script_loc[@]}" | sort); do
|
||||
local dir="${script_loc[$name]}"
|
||||
local callers status callers_str
|
||||
callers=$(ref_files_per_cmd "$name")
|
||||
if [[ -z "$callers" ]]; then
|
||||
status='`unused?`'
|
||||
callers_str='—'
|
||||
else
|
||||
status='`kept`'
|
||||
# Trim caller list to 2 entries + count.
|
||||
local count
|
||||
count=$(printf '%s\n' "$callers" | wc -l)
|
||||
if (( count > 2 )); then
|
||||
callers_str=$(printf '%s\n' "$callers" | head -2 | paste -sd, -)
|
||||
callers_str="$callers_str, +$((count - 2)) more"
|
||||
else
|
||||
callers_str=$(printf '%s\n' "$callers" | paste -sd, -)
|
||||
fi
|
||||
fi
|
||||
printf '| `%s` | `%s` | %s | %s | |\n' \
|
||||
"$name" "$dir" "$callers_str" "$status"
|
||||
done
|
||||
echo
|
||||
|
||||
# --- Render: missing references -----------------------------------------
|
||||
printf '## Missing references\n\n'
|
||||
printf 'Tokens grepped from `core/`, `features/`, `themes/`, `installer/`, `hosts/`, `bin/`, `lib/` that have no matching script file.\n\n'
|
||||
printf '| Token | Referenced in | Status |\n'
|
||||
printf '| --- | --- | --- |\n'
|
||||
while IFS= read -r token; do
|
||||
[[ -z "$token" ]] && continue
|
||||
[[ -n "${script_loc[$token]:-}" ]] && continue
|
||||
local refs
|
||||
self_pattern=$(IFS='|'; echo "${self_refs[*]}")
|
||||
refs=$(grep -rlE "\\b${token}\\b" \
|
||||
"${grep_includes[@]}" \
|
||||
"${search_dirs[@]}" 2>/dev/null \
|
||||
| grep -vE "^(${self_pattern})$" \
|
||||
| sort -u)
|
||||
[[ -z "$refs" ]] && continue
|
||||
local count refs_str
|
||||
count=$(printf '%s\n' "$refs" | wc -l)
|
||||
if (( count > 2 )); then
|
||||
refs_str=$(printf '%s\n' "$refs" | head -2 | paste -sd, -)
|
||||
refs_str="$refs_str, +$((count - 2)) more"
|
||||
else
|
||||
refs_str=$(printf '%s\n' "$refs" | paste -sd, -)
|
||||
fi
|
||||
printf '| `%s` | %s | `missing` |\n' "$token" "$refs_str"
|
||||
done <<<"$all_refs"
|
||||
echo
|
||||
|
||||
# --- Render: menu items -------------------------------------------------
|
||||
printf '## Menu items\n\n'
|
||||
printf 'Walked from `features/scripts/utils/nomarchy-menu`. Each `case` arm in a `show_*_menu` function becomes one row.\n\n'
|
||||
printf '| Submenu | Entry | Calls | Status |\n'
|
||||
printf '| --- | --- | --- | --- |\n'
|
||||
|
||||
awk '
|
||||
/^show_[a-z_]+_menu\(\) {/ { sub(/\(\) {/, ""); current=$1; in_func=1; next }
|
||||
/^[a-z_]+\(\) {/ && !/^show_/ { current=""; in_func=0; next }
|
||||
/^}$/ { current=""; in_func=0; next }
|
||||
!in_func { next }
|
||||
/^ case \$\(menu / {
|
||||
# extract the menu title between the first pair of double quotes
|
||||
match($0, /menu "[^"]+" "[^"]+"/);
|
||||
if (RSTART == 0) next;
|
||||
title=substr($0, RSTART, RLENGTH);
|
||||
# second quoted string is the option list
|
||||
n=split(title, parts, "\"");
|
||||
title=parts[2];
|
||||
options=parts[4];
|
||||
# split options on \n
|
||||
split(options, opts, "\\\\n");
|
||||
pending_submenu=current;
|
||||
pending_title=title;
|
||||
for (i=1;i<=length(opts);i++) pending_opts[i]=opts[i];
|
||||
pending_count=length(opts);
|
||||
next
|
||||
}
|
||||
/^ \*[A-Za-z]/ {
|
||||
# case arm — extract pattern between the first * and the closing )
|
||||
match($0, /\*[^)]*\)/);
|
||||
if (RSTART == 0) next;
|
||||
arm=substr($0, RSTART, RLENGTH);
|
||||
gsub(/[*)]/, "", arm);
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", arm);
|
||||
# action follows the )
|
||||
rest=substr($0, RSTART+RLENGTH);
|
||||
sub(/^[[:space:]]+/, "", rest);
|
||||
sub(/[[:space:]]*;;[[:space:]]*$/, "", rest);
|
||||
# match the first nomarchy-* token in the action
|
||||
cmd=""
|
||||
if (match(rest, /nomarchy-[a-z0-9-]+/)) {
|
||||
cmd=substr(rest, RSTART, RLENGTH);
|
||||
}
|
||||
printf "%s|%s|%s\n", pending_submenu, arm, cmd;
|
||||
}
|
||||
' features/scripts/utils/nomarchy-menu > /tmp/nomarchy-menu-rows.$$
|
||||
|
||||
while IFS='|' read -r submenu entry cmd; do
|
||||
[[ -z "$entry" ]] && continue
|
||||
[[ "$entry" =~ ^\) ]] && continue
|
||||
status='`kept`'
|
||||
if [[ -n "$cmd" ]]; then
|
||||
if [[ -z "${script_loc[$cmd]:-}" ]]; then
|
||||
status='`missing`'
|
||||
fi
|
||||
else
|
||||
cmd='_(inline)_'
|
||||
fi
|
||||
printf '| `%s` | %s | `%s` | %s |\n' "$submenu" "$entry" "$cmd" "$status"
|
||||
done < /tmp/nomarchy-menu-rows.$$
|
||||
rm -f /tmp/nomarchy-menu-rows.$$
|
||||
echo
|
||||
}
|
||||
|
||||
out=""
|
||||
if [[ "${1:-}" == "--out" ]]; then
|
||||
out="${2:?--out needs a path}"; shift 2
|
||||
fi
|
||||
if [[ -n "$out" ]]; then
|
||||
main >"$out"
|
||||
else
|
||||
main
|
||||
fi
|
||||
16
core/branding/about.txt
Normal file
16
core/branding/about.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
# About Nomarchy
|
||||
|
||||
Nomarchy is a highly curated, NixOS-based distribution designed for power users.
|
||||
It features a customized Hyprland desktop environment with a declarative
|
||||
theming engine and a suite of integrated utility scripts.
|
||||
|
||||
Built on a foundation of:
|
||||
- NixOS (Linux)
|
||||
- Hyprland (Window Manager)
|
||||
- Waybar (Status Bar)
|
||||
- Walker (Application Launcher & Menu)
|
||||
- Stylix (Theming Engine)
|
||||
|
||||
Version: 2026.05.04
|
||||
Docs: https://github.com/nomarchy/nomarchy/docs
|
||||
Manual: nomarchy-manual (Command)
|
||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
56
core/branding/icon.svg
Normal file
56
core/branding/icon.svg
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="210mm"
|
||||
height="297mm"
|
||||
viewBox="0 0 210 297"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
|
||||
sodipodi:docname="icon.svg"
|
||||
inkscape:export-filename="logo.svg"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="0.82803284"
|
||||
inkscape:cx="215.57116"
|
||||
inkscape:cy="452.27675"
|
||||
inkscape:window-width="1025"
|
||||
inkscape:window-height="1012"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="layer1" /><defs
|
||||
id="defs1" /><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><g
|
||||
id="g4"
|
||||
transform="translate(3.1953242,-22.686801)"
|
||||
style="fill:#000088;fill-opacity:1"><path
|
||||
style="fill:#000088;stroke-width:0.264583;fill-opacity:1"
|
||||
d="M 25.136891,85.823024 25.557592,210.77125 88.452409,174.38061 67.417351,160.49747 57.110174,166.38729 V 105.80633 Z"
|
||||
id="path1" /><path
|
||||
style="fill:#000088;stroke-width:0.264583;fill-opacity:1"
|
||||
d="M 67.728991,112.41131 182.54178,185.60757 153.16137,202.85259 67.830432,148.17947 Z"
|
||||
id="path2" /><path
|
||||
style="fill:#000088;stroke-width:0.264583;fill-opacity:1"
|
||||
d="M 139.74857,145.88014 140.00405,110.4959 54.800856,56.333749 25.675926,73.32329 Z"
|
||||
id="path3" /><path
|
||||
style="fill:#000088;stroke-width:0.264583;fill-opacity:1"
|
||||
d="M 182.2863,172.70573 V 48.286069 l -62.59305,36.406166 20.82177,13.668277 10.21927,-5.74834 0.12774,60.165978 z"
|
||||
id="path4" /></g></g></svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
34
core/branding/icon.txt
Normal file
34
core/branding/icon.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
${2},,,
|
||||
${1},, ${2},,,,,,
|
||||
${1},,,,,,,, ${2},,,,,,,,,,
|
||||
${1},,,,,,,,,,,,,, ${2}.,,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,,,,,,,,,,,,,. ${2},,,,,,,,,,,,,,,,,,,
|
||||
${1}.,,,,,,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,,,,,,,,,,,,,
|
||||
${1}, ${1},,,,,,,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,,,,,,,,,,,,,,
|
||||
${1},,, ${1},,,,,,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,,,,,,,,,,,,,,
|
||||
${1},,,,,, ${1}.,,,,,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,,,,,,,,,,
|
||||
${1},,,,,,,,,. ${1},,,,,,,,,,,,,,,,,,,,,,,,, ${2},,, ${2}.,,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,, ${1}.,,,,,,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,,,,,,,. ${2},,,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,,, ${1},, ${1},,,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,,, ${1},,,,, ${1},,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,,, ${1},,,,,,,, ${1},,,,,,,,,,,,,,,, ${2},,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,,, ${1},,,,,,,,,, ${1},,,,,,,,,,,,, ${2},,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,,, ${1},,,,,,,,,,,,,, ${1},,,,,,,,,, ${2},,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,, ${1},,,,,,, ${2},,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,, ${1},,,, ${2},,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,,,,, ${1},, ${2},,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,,, ${1},,, ${1},,,,,,,,,,,,,,,,,,,,,,,,, ${2},,,,,,,,,
|
||||
${1},,,,,,,,,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,,,,,,, ${2}.,,,,,
|
||||
${1},,,,,,,,,,,,,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,,,,,,,, ${2},,,
|
||||
${1},,,,,,,,,,,,,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,,,,,,,,, ${1},,,,,,,,,,,,,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,,,,,, ${1}.,,,,,,,,,,,,,,,,,,,
|
||||
${1},,,,,,,,,,,,, ${1},,,,,,,,,,,,,,
|
||||
${1},,,,,,,,,, ${1},,,,,,,,,.
|
||||
${1},,,,,, ${1},,
|
||||
${1},,,,
|
||||
BIN
core/branding/logo.png
Normal file
BIN
core/branding/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
34
core/branding/logo.txt
Normal file
34
core/branding/logo.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
,,,
|
||||
,, ,,,,,,
|
||||
,,,,,,,, ,,,,,,,,,,
|
||||
,,,,,,,,,,,,,, .,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,. ,,,,,,,,,,,,,,,,,,,
|
||||
.,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,
|
||||
, ,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,, ,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,, .,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,. ,,,,,,,,,,,,,,,,,,,,,,,,, ,,, .,,,,,,,,,,,,
|
||||
,,,,,,,,,,,, .,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,. ,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,, ,, ,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,, ,,,,, ,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,, ,,,,,,,, ,,,,,,,,,,,,,,,, ,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,, ,,,,,,,,,, ,,,,,,,,,,,,, ,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,, ,,,,,,,,,,,,,, ,,,,,,,,,, ,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,, ,,,,,,,,,,,,,,,, ,,,,,,, ,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,, ,,,, ,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,, ,, ,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,, ,,, ,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,, .,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,, ,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,, .,,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,, ,,,,,,,,,,,,,,
|
||||
,,,,,,,,,, ,,,,,,,,,.
|
||||
,,,,,, ,,
|
||||
,,,,
|
||||
11
core/branding/screensaver.txt
Normal file
11
core/branding/screensaver.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
# Nomarchy Screensaver Configuration
|
||||
|
||||
Nomarchy uses hyprlock for locking and hypridle for idle management.
|
||||
To configure the screensaver/lock screen visuals, edit:
|
||||
~/.config/hypr/hyprlock.conf
|
||||
|
||||
To configure idle timeouts and actions, edit:
|
||||
~/.config/hypr/hypridle.conf
|
||||
|
||||
You can also toggle the screensaver/idle management via the Nomarchy Menu:
|
||||
Trigger > Toggle > Idle Lock
|
||||
5
core/default.nix
Normal file
5
core/default.nix
Normal file
@@ -0,0 +1,5 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
imports = [ ./system ];
|
||||
}
|
||||
39
core/home/bash.nix
Normal file
39
core/home/bash.nix
Normal file
@@ -0,0 +1,39 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
{
|
||||
programs.bash = {
|
||||
enable = true;
|
||||
|
||||
bashrcExtra = ''
|
||||
if [[ -f ~/.config/nomarchy/default/bash/rc ]]; then
|
||||
source ~/.config/nomarchy/default/bash/rc
|
||||
fi
|
||||
'';
|
||||
|
||||
shellAliases = lib.mkDefault {
|
||||
# File system
|
||||
lsa = "ls -a";
|
||||
|
||||
# Directories
|
||||
".." = "cd ..";
|
||||
"..." = "cd ../..";
|
||||
"...." = "cd ../../..";
|
||||
|
||||
# Tools
|
||||
c = "opencode";
|
||||
d = "docker";
|
||||
r = "rails";
|
||||
t = "tmux attach || tmux new -s Work";
|
||||
|
||||
# Git
|
||||
g = "git";
|
||||
gcm = "git commit -m";
|
||||
gcam = "git commit -a -m";
|
||||
gcad = "git commit -a --amend";
|
||||
|
||||
# NixOS commands
|
||||
sys-update = "nomarchy-sys-update";
|
||||
env-update = "nomarchy-env-update";
|
||||
};
|
||||
};
|
||||
}
|
||||
94
core/home/behavior.nix
Normal file
94
core/home/behavior.nix
Normal file
@@ -0,0 +1,94 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
# Behavior Configuration Module
|
||||
#
|
||||
# This module deploys non-visual configuration files (keybindings, input settings,
|
||||
# window rules, etc.) with lib.mkDefault, allowing downstream users to override.
|
||||
#
|
||||
# Visual/theme configs are handled separately by theme-loader.nix and stylix.nix.
|
||||
#
|
||||
# Behavior configs include:
|
||||
# - Keybindings (bindings, media keys, clipboard)
|
||||
# - Input settings (keyboard, mouse, touchpad)
|
||||
# - Window rules and layouts
|
||||
# - Autostart applications
|
||||
# - Environment variables
|
||||
|
||||
let
|
||||
configDir = ./config;
|
||||
overridesDir = "${config.home.homeDirectory}/.config/nomarchy/overrides";
|
||||
|
||||
# Behavior config categories with their source paths
|
||||
behaviorConfigs = {
|
||||
# Hyprland behavior (non-visual)
|
||||
"nomarchy/default/hypr/bindings.conf" = "hypr/bindings.conf";
|
||||
"nomarchy/default/hypr/bindings/media.conf" = "hypr/bindings/media.conf";
|
||||
"nomarchy/default/hypr/bindings/clipboard.conf" = "hypr/bindings/clipboard.conf";
|
||||
"nomarchy/default/hypr/bindings/tiling-v2.conf" = "hypr/bindings/tiling-v2.conf";
|
||||
"nomarchy/default/hypr/bindings/utilities.conf" = "hypr/bindings/utilities.conf";
|
||||
"nomarchy/default/hypr/input.conf" = "hypr/input.conf";
|
||||
"nomarchy/default/hypr/windows.conf" = "hypr/windows.conf";
|
||||
"nomarchy/default/hypr/autostart.conf" = "hypr/autostart.conf";
|
||||
"nomarchy/default/hypr/envs.conf" = "hypr/envs.conf";
|
||||
"nomarchy/default/hypr/looknfeel.conf" = "hypr/looknfeel.conf";
|
||||
|
||||
# App-specific window rules (behavior, not visual)
|
||||
"nomarchy/default/hypr/apps.conf" = "hypr/apps.conf";
|
||||
"nomarchy/default/hypr/apps/qemu.conf" = "hypr/apps/qemu.conf";
|
||||
"nomarchy/default/hypr/apps/steam.conf" = "hypr/apps/steam.conf";
|
||||
"nomarchy/default/hypr/apps/terminals.conf" = "hypr/apps/terminals.conf";
|
||||
"nomarchy/default/hypr/apps/walker.conf" = "hypr/apps/walker.conf";
|
||||
"nomarchy/default/hypr/apps/browser.conf" = "hypr/apps/browser.conf";
|
||||
"nomarchy/default/hypr/apps/1password.conf" = "hypr/apps/1password.conf";
|
||||
"nomarchy/default/hypr/apps/bitwarden.conf" = "hypr/apps/bitwarden.conf";
|
||||
"nomarchy/default/hypr/apps/pip.conf" = "hypr/apps/pip.conf";
|
||||
"nomarchy/default/hypr/apps/system.conf" = "hypr/apps/system.conf";
|
||||
"nomarchy/default/hypr/apps/localsend.conf" = "hypr/apps/localsend.conf";
|
||||
"nomarchy/default/hypr/apps/telegram.conf" = "hypr/apps/telegram.conf";
|
||||
"nomarchy/default/hypr/apps/geforce.conf" = "hypr/apps/geforce.conf";
|
||||
"nomarchy/default/hypr/apps/moonlight.conf" = "hypr/apps/moonlight.conf";
|
||||
"nomarchy/default/hypr/apps/retroarch.conf" = "hypr/apps/retroarch.conf";
|
||||
"nomarchy/default/hypr/apps/webcam-overlay.conf" = "hypr/apps/webcam-overlay.conf";
|
||||
"nomarchy/default/hypr/apps/davinci-resolve.conf" = "hypr/apps/davinci-resolve.conf";
|
||||
"nomarchy/default/hypr/apps/hyprshot.conf" = "hypr/apps/hyprshot.conf";
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
options.nomarchy.behavior = {
|
||||
hyprland = {
|
||||
bindings = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Whether to deploy default Hyprland keybindings.";
|
||||
};
|
||||
input = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Whether to deploy default input settings.";
|
||||
};
|
||||
windowRules = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Whether to deploy default window rules.";
|
||||
};
|
||||
autostart = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Whether to deploy default autostart configuration.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
# Note: The actual config deployment is handled by configs.nix
|
||||
# This module provides the options and documentation for behavior configs
|
||||
# The separation allows users to selectively disable behavior categories
|
||||
|
||||
# Ensure behavior config directories exist in overrides
|
||||
home.activation.createBehaviorOverrideDirs = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
||||
mkdir -p "${overridesDir}/hypr/bindings"
|
||||
mkdir -p "${overridesDir}/hypr/apps"
|
||||
'';
|
||||
};
|
||||
}
|
||||
4
core/home/config/brave-flags.conf
Normal file
4
core/home/config/brave-flags.conf
Normal file
@@ -0,0 +1,4 @@
|
||||
--ozone-platform=wayland
|
||||
--ozone-platform-hint=wayland
|
||||
--enable-features=TouchpadOverscrollHistoryNavigation
|
||||
--load-extension=~/.config/nomarchy/default/chromium/extensions/copy-url
|
||||
4
core/home/config/chromium-flags.conf
Normal file
4
core/home/config/chromium-flags.conf
Normal file
@@ -0,0 +1,4 @@
|
||||
--ozone-platform=wayland
|
||||
--ozone-platform-hint=wayland
|
||||
--enable-features=TouchpadOverscrollHistoryNavigation
|
||||
--load-extension=~/.config/nomarchy/default/chromium/extensions/copy-url
|
||||
137
core/home/config/fastfetch/config.jsonc
Normal file
137
core/home/config/fastfetch/config.jsonc
Normal file
@@ -0,0 +1,137 @@
|
||||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
"logo": {
|
||||
"type": "file",
|
||||
"source": "~/.config/nomarchy/branding/icon.txt",
|
||||
"color": { "1": "green", "2": "blue" },
|
||||
"padding": {
|
||||
"top": 2,
|
||||
"right": 6,
|
||||
"left": 2
|
||||
}
|
||||
},
|
||||
"modules": [
|
||||
"break",
|
||||
{
|
||||
"type": "custom",
|
||||
"format": "\u001b[90m┌──────────────────────Hardware──────────────────────┐"
|
||||
},
|
||||
{
|
||||
"type": "host",
|
||||
"key": " PC",
|
||||
"keyColor": "green"
|
||||
},
|
||||
{
|
||||
"type": "cpu",
|
||||
"key": "│ ├",
|
||||
"showPeCoreCount": true,
|
||||
"keyColor": "green"
|
||||
},
|
||||
{
|
||||
"type": "gpu",
|
||||
"key": "│ ├",
|
||||
"detectionMethod": "pci",
|
||||
"keyColor": "green"
|
||||
},
|
||||
{
|
||||
"type": "display",
|
||||
"key": "│ ├",
|
||||
"keyColor": "green"
|
||||
},
|
||||
{
|
||||
"type": "disk",
|
||||
"key": "│ ├",
|
||||
"keyColor": "green"
|
||||
},
|
||||
{
|
||||
"type": "memory",
|
||||
"key": "│ ├",
|
||||
"keyColor": "green"
|
||||
},
|
||||
{
|
||||
"type": "swap",
|
||||
"key": "└ └ ",
|
||||
"keyColor": "green"
|
||||
},
|
||||
{
|
||||
"type": "custom",
|
||||
"format": "\u001b[90m└────────────────────────────────────────────────────┘"
|
||||
},
|
||||
"break",
|
||||
{
|
||||
"type": "custom",
|
||||
"format": "\u001b[90m┌──────────────────────Software──────────────────────┐"
|
||||
},
|
||||
{
|
||||
"type": "os",
|
||||
"key": "\ue900 OS",
|
||||
"keyColor": "blue"
|
||||
},
|
||||
{
|
||||
"type": "kernel",
|
||||
"key": "│ ├",
|
||||
"keyColor": "blue"
|
||||
},
|
||||
{
|
||||
"type": "wm",
|
||||
"key": "│ ├",
|
||||
"keyColor": "blue"
|
||||
},
|
||||
{
|
||||
"type": "de",
|
||||
"key": " DE",
|
||||
"keyColor": "blue"
|
||||
},
|
||||
{
|
||||
"type": "terminal",
|
||||
"key": "│ ├",
|
||||
"keyColor": "blue"
|
||||
},
|
||||
{
|
||||
"type": "packages",
|
||||
"key": "│ ├",
|
||||
"keyColor": "blue"
|
||||
},
|
||||
{
|
||||
"type": "wmtheme",
|
||||
"key": "│ ├",
|
||||
"keyColor": "blue"
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"key": "│ ├",
|
||||
"keyColor": "blue",
|
||||
"text": "theme=$(cat ~/.config/nomarchy/current/theme.name); echo -e \"$theme \\e[38m●\\e[37m●\\e[36m●\\e[35m●\\e[34m●\\e[33m●\\e[32m●\\e[31m●\""
|
||||
},
|
||||
{
|
||||
"type": "terminalfont",
|
||||
"key": "└ └",
|
||||
"keyColor": "blue"
|
||||
},
|
||||
{
|
||||
"type": "custom",
|
||||
"format": "\u001b[90m└────────────────────────────────────────────────────┘"
|
||||
},
|
||||
"break",
|
||||
{
|
||||
"type": "custom",
|
||||
"format": "\u001b[90m┌────────────────Age / Uptime / Update───────────────┐"
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"key": " OS Age",
|
||||
"keyColor": "magenta",
|
||||
"text": "echo $(( ($(date +%s) - $(stat -c %W /)) / 86400 )) days"
|
||||
},
|
||||
{
|
||||
"type": "uptime",
|
||||
"key": " Uptime",
|
||||
"keyColor": "magenta"
|
||||
},
|
||||
{
|
||||
"type": "custom",
|
||||
"format": "\u001b[90m└────────────────────────────────────────────────────┘"
|
||||
},
|
||||
"break"
|
||||
]
|
||||
}
|
||||
2
core/home/config/fcitx5/conf/clipboard.conf
Normal file
2
core/home/config/fcitx5/conf/clipboard.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
TriggerKey=
|
||||
PastePrimaryKey=
|
||||
1
core/home/config/fcitx5/conf/xcb.conf
Normal file
1
core/home/config/fcitx5/conf/xcb.conf
Normal file
@@ -0,0 +1 @@
|
||||
Allow Overriding System XKB Settings=False
|
||||
79
core/home/config/fontconfig/fonts.conf
Normal file
79
core/home/config/fontconfig/fonts.conf
Normal file
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
||||
<fontconfig>
|
||||
<match target="pattern">
|
||||
<test name="family" qual="any">
|
||||
<string>sans-serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="assign" binding="strong">
|
||||
<string>Liberation Sans</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="family" qual="any">
|
||||
<string>serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="assign" binding="strong">
|
||||
<string>Liberation Serif</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="family" qual="any">
|
||||
<string>monospace</string>
|
||||
</test>
|
||||
<edit name="family" mode="assign" binding="strong">
|
||||
<string>JetBrainsMono Nerd Font</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<alias>
|
||||
<family>system-ui</family>
|
||||
<prefer>
|
||||
<family>Liberation Sans</family>
|
||||
</prefer>
|
||||
</alias>
|
||||
|
||||
<alias>
|
||||
<family>ui-monospace</family>
|
||||
<default>
|
||||
<family>monospace</family>
|
||||
</default>
|
||||
</alias>
|
||||
|
||||
<alias>
|
||||
<family>-apple-system</family>
|
||||
<prefer>
|
||||
<family>Liberation Sans</family>
|
||||
</prefer>
|
||||
</alias>
|
||||
|
||||
<alias>
|
||||
<family>BlinkMacSystemFont</family>
|
||||
<prefer>
|
||||
<family>Liberation Sans</family>
|
||||
</prefer>
|
||||
</alias>
|
||||
|
||||
<alias>
|
||||
<family>sans-serif</family>
|
||||
<accept>
|
||||
<family>Noto Color Emoji</family>
|
||||
</accept>
|
||||
</alias>
|
||||
|
||||
<alias>
|
||||
<family>serif</family>
|
||||
<accept>
|
||||
<family>Noto Color Emoji</family>
|
||||
</accept>
|
||||
</alias>
|
||||
|
||||
<alias>
|
||||
<family>monospace</family>
|
||||
<accept>
|
||||
<family>Noto Color Emoji</family>
|
||||
</accept>
|
||||
</alias>
|
||||
</fontconfig>
|
||||
28
core/home/config/git/config
Normal file
28
core/home/config/git/config
Normal file
@@ -0,0 +1,28 @@
|
||||
# See https://git-scm.com/docs/git-config
|
||||
|
||||
[alias]
|
||||
co = checkout
|
||||
br = branch
|
||||
ci = commit
|
||||
st = status
|
||||
[init]
|
||||
defaultBranch = master
|
||||
[pull]
|
||||
rebase = true # Rebase (instead of merge) on pull
|
||||
[push]
|
||||
autoSetupRemote = true # Automatically set upstream branch on push
|
||||
[diff]
|
||||
algorithm = histogram # Clearer diffs on moved/edited lines
|
||||
colorMoved = plain # Highlight moved blocks in diffs
|
||||
mnemonicPrefix = true # More intuitive refs in diff output
|
||||
[commit]
|
||||
verbose = true # Include diff comment in commit message template
|
||||
[column]
|
||||
ui = auto # Output in columns when possible
|
||||
[branch]
|
||||
sort = -committerdate # Sort branches by most recent commit first
|
||||
[tag]
|
||||
sort = -version:refname # Sort version numbers as you would expect
|
||||
[rerere]
|
||||
enabled = true # Record and reuse conflict resolutions
|
||||
autoupdate = true # Apply stored conflict resolutions automatically
|
||||
13
core/home/config/imv/config
Normal file
13
core/home/config/imv/config
Normal file
@@ -0,0 +1,13 @@
|
||||
[binds]
|
||||
|
||||
# Print the current image file
|
||||
<Ctrl+p> = exec lp "$imv_current_file"
|
||||
|
||||
# Delete the current image and quit the viewer
|
||||
<Ctrl+x> = exec rm "$imv_current_file"; quit
|
||||
|
||||
# Delete the current image and move to the next one
|
||||
<Ctrl+Shift+X> = exec rm "$imv_current_file"; close
|
||||
|
||||
# Rotate the currently open image by 90 degrees
|
||||
<Ctrl+r> = exec mogrify -rotate 90 "$imv_current_file"
|
||||
84
core/home/config/nautilus-python/extensions/localsend.py
Normal file
84
core/home/config/nautilus-python/extensions/localsend.py
Normal file
@@ -0,0 +1,84 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from gi import require_version
|
||||
|
||||
require_version("Nautilus", "4.1")
|
||||
|
||||
from gi.repository import GObject, Gio, Nautilus
|
||||
|
||||
|
||||
class SendViaLocalSendAction(GObject.GObject, Nautilus.MenuProvider):
|
||||
def _launch_localsend(self, paths):
|
||||
command = self._resolve_command()
|
||||
if not command:
|
||||
return
|
||||
|
||||
if command[-1] == "@@":
|
||||
command = command + paths + ["@@"]
|
||||
else:
|
||||
command = command + paths
|
||||
|
||||
Gio.Subprocess.new(command, Gio.SubprocessFlags.NONE)
|
||||
|
||||
def _resolve_command(self):
|
||||
localsend = shutil.which("localsend")
|
||||
if localsend:
|
||||
return [localsend, "--headless", "send"]
|
||||
|
||||
flatpak = shutil.which("flatpak")
|
||||
if flatpak and self._has_flatpak_app(flatpak, "org.localsend.localsend_app"):
|
||||
return [
|
||||
flatpak,
|
||||
"run",
|
||||
"--file-forwarding",
|
||||
"org.localsend.localsend_app",
|
||||
"@@",
|
||||
]
|
||||
|
||||
return None
|
||||
|
||||
def _has_flatpak_app(self, flatpak, app_id):
|
||||
process = Gio.Subprocess.new(
|
||||
[flatpak, "info", app_id],
|
||||
Gio.SubprocessFlags.STDOUT_SILENCE | Gio.SubprocessFlags.STDERR_SILENCE,
|
||||
)
|
||||
return process.wait_check()
|
||||
|
||||
def _selected_paths(self, files):
|
||||
paths = []
|
||||
|
||||
for file in files:
|
||||
location = file.get_location()
|
||||
if not location:
|
||||
continue
|
||||
|
||||
path = location.get_path()
|
||||
if path and path not in paths:
|
||||
paths.append(path)
|
||||
|
||||
return paths
|
||||
|
||||
def _make_item(self, paths):
|
||||
label = (
|
||||
"Send via LocalSend" if len(paths) == 1 else "Send selected via LocalSend"
|
||||
)
|
||||
item = Nautilus.MenuItem(
|
||||
name="LocalSendNautilus::send_via_localsend",
|
||||
label=label,
|
||||
icon="localsend",
|
||||
)
|
||||
item.connect("activate", self._on_activate, paths)
|
||||
return item
|
||||
|
||||
def _on_activate(self, _menu, paths):
|
||||
self._launch_localsend(paths)
|
||||
|
||||
def get_file_items(self, *args):
|
||||
files = args[0] if len(args) == 1 else args[1]
|
||||
paths = self._selected_paths(files)
|
||||
|
||||
if not paths or not self._resolve_command():
|
||||
return []
|
||||
|
||||
return [self._make_item(paths)]
|
||||
408
core/home/config/nomarchy-skill/SKILL.md
Normal file
408
core/home/config/nomarchy-skill/SKILL.md
Normal file
@@ -0,0 +1,408 @@
|
||||
---
|
||||
name: nomarchy
|
||||
description: >
|
||||
REQUIRED for end-user customization of Linux desktop, window manager, or system config.
|
||||
Use when editing ~/.config/hypr/, ~/.config/waybar/, ~/.config/walker/,
|
||||
~/.config/alacritty/, ~/.config/kitty/, ~/.config/ghostty/, ~/.config/mako/,
|
||||
or ~/.config/nomarchy/. Triggers: Hyprland, window rules, animations, keybindings,
|
||||
monitors, gaps, borders, blur, opacity, waybar, walker, terminal config, themes,
|
||||
wallpaper, night light, idle, lock screen, screenshots, layer rules, workspace
|
||||
settings, display config, and user-facing nomarchy commands. Excludes Nomarchy
|
||||
source development in ~/.local/share/nomarchy/ and repo-internal workflows.
|
||||
---
|
||||
|
||||
# Nomarchy Skill
|
||||
|
||||
Manage [Nomarchy](https://nomarchy.org/) Linux systems - a beautiful, modern, opinionated Arch Linux distribution with Hyprland.
|
||||
|
||||
This skill is for end-user customization on installed systems.
|
||||
It is not for contributing to Nomarchy source code.
|
||||
|
||||
## When This Skill MUST Be Used
|
||||
|
||||
**ALWAYS invoke this skill for end-user requests involving ANY of these:**
|
||||
|
||||
- Editing ANY file in `~/.config/hypr/` (window rules, animations, keybindings, monitors, etc.)
|
||||
- Editing ANY file in `~/.config/waybar/`, `~/.config/walker/`, `~/.config/mako/`
|
||||
- Editing terminal configs (alacritty, kitty, ghostty)
|
||||
- Editing ANY file in `~/.config/nomarchy/`
|
||||
- Window behavior, animations, opacity, blur, gaps, borders
|
||||
- Layer rules, workspace settings, display/monitor configuration
|
||||
- Themes, wallpapers, fonts, appearance changes
|
||||
- User-facing `nomarchy-*` commands (`nomarchy-theme-*`, `nomarchy-refresh-*`, `nomarchy-restart-*`, etc.)
|
||||
- Screenshots, screen recording, night light, idle behavior, lock screen
|
||||
|
||||
**If you're about to edit a config file in ~/.config/ on this system, STOP and use this skill first.**
|
||||
|
||||
**Do NOT use this skill for Nomarchy development tasks** — editing files in `~/.local/share/nomarchy/` or modifying repo internals.
|
||||
|
||||
## Critical Safety Rules
|
||||
|
||||
**For end-user customization tasks, NEVER modify anything in `~/.local/share/nomarchy/`** - but READING is safe and encouraged.
|
||||
|
||||
This directory contains Nomarchy's source files managed by git. Any changes will be:
|
||||
- Lost on next `nomarchy-update`
|
||||
- Cause conflicts with upstream
|
||||
- Break the system's update mechanism
|
||||
|
||||
```
|
||||
~/.local/share/nomarchy/ # READ-ONLY - NEVER EDIT (reading is OK)
|
||||
├── bin/ # Source scripts (symlinked to PATH)
|
||||
├── config/ # Default config templates
|
||||
├── themes/ # Stock themes
|
||||
├── default/ # System defaults
|
||||
├── migrations/ # Update migrations
|
||||
└── install/ # Installation scripts
|
||||
```
|
||||
|
||||
**Reading `~/.local/share/nomarchy/` is SAFE and useful** - do it freely to:
|
||||
- Understand how nomarchy commands work: `cat $(which nomarchy-theme-set)`
|
||||
- See default configs before customizing: `cat ~/.local/share/nomarchy/config/waybar/config.jsonc`
|
||||
- Check stock theme files to copy for customization
|
||||
- Reference default hyprland settings: `cat ~/.config/nomarchy/default/hypr/*`
|
||||
|
||||
**Always use these safe locations instead:**
|
||||
- `~/.config/` - User configuration (safe to edit)
|
||||
- `~/.config/nomarchy/themes/<custom-name>/` - Custom themes (must be real directories)
|
||||
- `~/.config/nomarchy/hooks/` - Custom automation hooks
|
||||
|
||||
If the request is to develop Nomarchy itself, this skill is out of scope. Follow repository development instructions instead of this skill.
|
||||
|
||||
## System Architecture
|
||||
|
||||
Nomarchy is built on:
|
||||
|
||||
| Component | Purpose | Config Location |
|
||||
|-----------|---------|-----------------|
|
||||
| **Arch Linux** | Base OS | `/etc/`, `~/.config/` |
|
||||
| **Hyprland** | Wayland compositor/WM | `~/.config/hypr/` |
|
||||
| **Waybar** | Status bar | `~/.config/waybar/` |
|
||||
| **Walker** | App launcher | `~/.config/walker/` |
|
||||
| **Alacritty/Kitty/Ghostty** | Terminals | `~/.config/<terminal>/` |
|
||||
| **Mako** | Notifications | `~/.config/mako/` |
|
||||
| **SwayOSD** | On-screen display | `~/.config/swayosd/` |
|
||||
|
||||
## Command Discovery
|
||||
|
||||
Nomarchy provides ~145 commands following `nomarchy-<category>-<action>` pattern.
|
||||
|
||||
```bash
|
||||
# List all nomarchy commands
|
||||
compgen -c | grep -E '^nomarchy-' | sort -u
|
||||
|
||||
# Find commands by category
|
||||
compgen -c | grep -E '^nomarchy-theme'
|
||||
compgen -c | grep -E '^nomarchy-restart'
|
||||
|
||||
# Read a command's source to understand it
|
||||
cat $(which nomarchy-theme-set)
|
||||
```
|
||||
|
||||
### Command Categories
|
||||
|
||||
| Prefix | Purpose | Example |
|
||||
|--------|---------|---------|
|
||||
| `nomarchy-refresh-*` | Reset config to defaults (backs up first) | `nomarchy-refresh-waybar` |
|
||||
| `nomarchy-restart-*` | Restart a service/app | `nomarchy-restart-waybar` |
|
||||
| `nomarchy-toggle-*` | Toggle feature on/off | `nomarchy-toggle-nightlight` |
|
||||
| `nomarchy-theme-*` | Theme management | `nomarchy-theme-set <name>` |
|
||||
| `nomarchy-install-*` | Install optional software | `nomarchy-install-docker-dbs` |
|
||||
| `nomarchy-launch-*` | Launch apps | `nomarchy-launch-browser` |
|
||||
| `nomarchy-cmd-*` | System commands | `nomarchy-cmd-screenshot` |
|
||||
| `nomarchy-pkg-*` | Package management | `nomarchy-pkg-install <pkg>` |
|
||||
| `nomarchy-setup-*` | Initial setup tasks | `nomarchy-setup-fingerprint` |
|
||||
| `nomarchy-update-*` | System updates | `nomarchy-update` |
|
||||
|
||||
## Configuration Locations
|
||||
|
||||
### Hyprland (Window Manager)
|
||||
|
||||
```
|
||||
~/.config/hypr/
|
||||
├── hyprland.conf # Main config (sources others)
|
||||
├── bindings.conf # Keybindings
|
||||
├── monitors.conf # Display configuration
|
||||
├── input.conf # Keyboard/mouse settings
|
||||
├── looknfeel.conf # Appearance (gaps, borders, animations)
|
||||
├── envs.conf # Environment variables
|
||||
├── autostart.conf # Startup applications
|
||||
├── hypridle.conf # Idle behavior (screen off, lock, suspend)
|
||||
├── hyprlock.conf # Lock screen appearance
|
||||
└── hyprsunset.conf # Night light / blue light filter
|
||||
```
|
||||
|
||||
**Key behaviors:**
|
||||
- Hyprland auto-reloads on config save (no restart needed for most changes)
|
||||
- Use `hyprctl reload` to force reload
|
||||
- Use `nomarchy-refresh-hyprland` to reset to defaults
|
||||
|
||||
### Waybar (Status Bar)
|
||||
|
||||
```
|
||||
~/.config/waybar/
|
||||
├── config.jsonc # Bar layout and modules (JSONC format)
|
||||
└── style.css # Styling
|
||||
```
|
||||
|
||||
**Waybar does NOT auto-reload.** You MUST run `nomarchy-restart-waybar` after any config changes.
|
||||
|
||||
**Commands:** `nomarchy-restart-waybar`, `nomarchy-refresh-waybar`, `nomarchy-toggle-waybar`
|
||||
|
||||
### Terminals
|
||||
|
||||
```
|
||||
~/.config/alacritty/alacritty.toml
|
||||
~/.config/kitty/kitty.conf
|
||||
~/.config/ghostty/config
|
||||
```
|
||||
|
||||
**Command:** `nomarchy-restart-terminal`
|
||||
|
||||
### Other Configs
|
||||
|
||||
| App | Location |
|
||||
|-----|----------|
|
||||
| btop | `~/.config/btop/btop.conf` |
|
||||
| fastfetch | `~/.config/fastfetch/config.jsonc` |
|
||||
| lazygit | `~/.config/lazygit/config.yml` |
|
||||
| starship | `~/.config/starship.toml` |
|
||||
| git | `~/.config/git/config` |
|
||||
| walker | `~/.config/walker/config.toml` |
|
||||
|
||||
## Safe Customization Patterns
|
||||
|
||||
### Pattern 1: Edit User Config Directly
|
||||
|
||||
For simple changes, edit files in `~/.config/`:
|
||||
|
||||
```bash
|
||||
# 1. Read current config
|
||||
cat ~/.config/hypr/bindings.conf
|
||||
|
||||
# 2. Backup before changes
|
||||
cp ~/.config/hypr/bindings.conf ~/.config/hypr/bindings.conf.bak.$(date +%s)
|
||||
|
||||
# 3. Make changes with Edit tool
|
||||
|
||||
# 4. Apply changes
|
||||
# - Hyprland: auto-reloads on save (no restart needed)
|
||||
# - Waybar: MUST restart with nomarchy-restart-waybar
|
||||
# - Walker: MUST restart with nomarchy-restart-walker
|
||||
# - Terminals: MUST restart with nomarchy-restart-terminal
|
||||
```
|
||||
|
||||
### Pattern 2: Make a new theme
|
||||
|
||||
1. Create a directory under ~/.config/nomarchy/themes.
|
||||
2. See how an existing theme is done via ~/.local/share/nomarchy/themes/catppuccin.
|
||||
3. Download a matching background (or several) from the internet and put them in ~/.config/nomarchy/themes/[name-of-new-theme]
|
||||
4. When done with the theme, run nomarchy-theme-set "Name of new theme"
|
||||
|
||||
### Pattern 3: Use Hooks for Automation
|
||||
|
||||
Create scripts in `~/.config/nomarchy/hooks/` to run automatically on events:
|
||||
|
||||
```bash
|
||||
# Available hooks (see samples in ~/.config/nomarchy/hooks/):
|
||||
~/.config/nomarchy/hooks/
|
||||
├── theme-set # Runs after theme change (receives theme name as $1)
|
||||
├── font-set # Runs after font change
|
||||
└── post-update # Runs after nomarchy-update
|
||||
```
|
||||
|
||||
Example hook (`~/.config/nomarchy/hooks/theme-set`):
|
||||
```bash
|
||||
#!/bin/bash
|
||||
THEME_NAME=$1
|
||||
echo "Theme changed to: $THEME_NAME"
|
||||
# Add custom actions here
|
||||
```
|
||||
|
||||
### Pattern 4: Reset to Defaults -- ALWAYS SEEK USER CONFIRMATION BEFORE RUNNING
|
||||
|
||||
When customizations go wrong:
|
||||
|
||||
```bash
|
||||
# Reset specific config (creates backup automatically)
|
||||
nomarchy-refresh-waybar
|
||||
nomarchy-refresh-hyprland
|
||||
|
||||
# The refresh command:
|
||||
# 1. Backs up current config with timestamp
|
||||
# 2. Copies default from ~/.local/share/nomarchy/config/
|
||||
# 3. Restarts the component
|
||||
```
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Themes
|
||||
|
||||
```bash
|
||||
nomarchy-theme-list # Show available themes
|
||||
nomarchy-theme-current # Show current theme
|
||||
nomarchy-theme-set <name> # Apply theme (use "Tokyo Night" not "tokyo-night")
|
||||
nomarchy-theme-next # Cycle to next theme
|
||||
nomarchy-theme-bg-next # Cycle wallpaper
|
||||
nomarchy-theme-install <url> # Install from git repo
|
||||
nomarchy-theme-remove <name> # Remove an installed extra theme
|
||||
nomarchy-theme-refresh # Re-apply current theme from templates
|
||||
nomarchy-theme-bg-install # Open backgrounds dir to drop in custom images
|
||||
```
|
||||
|
||||
### Keybindings
|
||||
|
||||
Edit `~/.config/hypr/bindings.conf`. Format:
|
||||
```
|
||||
bind = SUPER, Return, exec, xdg-terminal-exec
|
||||
bind = SUPER, Q, killactive
|
||||
bind = SUPER SHIFT, E, exit
|
||||
```
|
||||
|
||||
View current bindings: `nomarchy-menu-keybindings --print`
|
||||
|
||||
**IMPORTANT: When re-binding an existing key:**
|
||||
|
||||
1. First check existing bindings: `nomarchy-menu-keybindings --print`
|
||||
2. If the key is already bound, you MUST add an `unbind` directive BEFORE your new `bind`
|
||||
3. Inform the user what the key was previously bound to
|
||||
|
||||
Example - rebinding SUPER+F (which is bound to fullscreen by default):
|
||||
```
|
||||
# Unbind existing SUPER+F (was: fullscreen)
|
||||
unbind = SUPER, F
|
||||
# New binding for file manager
|
||||
bind = SUPER, F, exec, nautilus
|
||||
```
|
||||
|
||||
Always tell the user: "Note: SUPER+F was previously bound to fullscreen. I've added an unbind directive to override it."
|
||||
|
||||
### Display/Monitors
|
||||
|
||||
Edit `~/.config/hypr/monitors.conf`. Format:
|
||||
```
|
||||
monitor = eDP-1, 1920x1080@60, 0x0, 1
|
||||
monitor = HDMI-A-1, 2560x1440@144, 1920x0, 1
|
||||
```
|
||||
|
||||
List monitors: `hyprctl monitors`
|
||||
|
||||
### Window Rules
|
||||
|
||||
**CRITICAL: Hyprland window rules syntax changes frequently between versions.**
|
||||
|
||||
Before writing ANY window rules, you MUST fetch the current documentation from the official Hyprland wiki:
|
||||
- https://github.com/hyprwm/hyprland-wiki/blob/main/content/Configuring/Window-Rules.md
|
||||
|
||||
DO NOT rely on cached or memorized window rule syntax. The format has changed multiple times and using outdated syntax will cause errors or unexpected behavior.
|
||||
|
||||
Window rules go in `~/.config/hypr/hyprland.conf` or a sourced file. Always verify the current syntax from the wiki first.
|
||||
|
||||
### Fonts
|
||||
|
||||
```bash
|
||||
nomarchy-font-list # Available fonts
|
||||
nomarchy-font-current # Current font
|
||||
nomarchy-font-set <name> # Change font
|
||||
```
|
||||
|
||||
### System
|
||||
|
||||
```bash
|
||||
nomarchy-update # Full system update
|
||||
nomarchy-version # Show Nomarchy version
|
||||
nomarchy-debug --no-sudo --print # Debug info (ALWAYS use these flags)
|
||||
nomarchy-lock-screen # Lock screen
|
||||
nomarchy-system-shutdown # Shutdown
|
||||
nomarchy-system-reboot # Reboot
|
||||
nomarchy-sudo-passwordless-toggle # Toggle 15-min passwordless sudo
|
||||
nomarchy-sudo-reset # Clear sudo lockout / faillock
|
||||
nomarchy-restart-trackpad # Reload intel_quicki2c (fixes dead THC trackpad)
|
||||
```
|
||||
|
||||
**IMPORTANT:** Always run `nomarchy-debug` with `--no-sudo --print` flags to avoid interactive sudo prompts that will hang the terminal.
|
||||
|
||||
### Custom App Launchers
|
||||
|
||||
```bash
|
||||
nomarchy-webapp-install # Add a web app launcher (interactive)
|
||||
nomarchy-webapp-remove [name...] # Remove web apps (interactive if no name)
|
||||
nomarchy-webapp-remove-all # Bulk-remove every web app
|
||||
nomarchy-tui-install # Add a TUI launcher for a terminal program
|
||||
nomarchy-tui-remove [name...] # Remove TUI launchers
|
||||
nomarchy-tui-remove-all # Bulk-remove every TUI launcher
|
||||
```
|
||||
|
||||
Both families write `.desktop` files into `~/.local/share/applications/` so they appear in the app launcher (walker / rofi).
|
||||
|
||||
### Voice dictation (Voxtype)
|
||||
|
||||
```bash
|
||||
nomarchy-voxtype-install # Install Voxtype + AI model (~150MB)
|
||||
nomarchy-voxtype-remove # Uninstall Voxtype
|
||||
nomarchy-voxtype-status # Running state (also shown in waybar)
|
||||
```
|
||||
|
||||
Toggle dictation with `SUPER+CTRL+X` after install.
|
||||
|
||||
### Virtualization
|
||||
|
||||
```bash
|
||||
nomarchy-windows-vm install # Provision a Windows VM via docker-compose
|
||||
nomarchy-windows-vm launch # Connect to the VM (auto-stop on disconnect)
|
||||
nomarchy-windows-vm launch -k # Connect, keep VM running after disconnect
|
||||
nomarchy-windows-vm stop # Shut the VM down
|
||||
nomarchy-windows-vm status # Show current state
|
||||
```
|
||||
|
||||
Requires KVM (`/dev/kvm`) and Docker (enable via `nomarchy.system.virtualization.docker`).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
```bash
|
||||
# Get debug information (ALWAYS use these flags to avoid interactive prompts)
|
||||
nomarchy-debug --no-sudo --print
|
||||
|
||||
# Upload logs for support
|
||||
nomarchy-upload-log
|
||||
|
||||
# Reset specific config to defaults
|
||||
# Examples: nomarchy-refresh-fastfetch, nomarchy-refresh-hyprland, nomarchy-refresh-waybar
|
||||
nomarchy-refresh-<app>
|
||||
|
||||
# Refresh specific config file
|
||||
# config-file path is relative to ~/.config/
|
||||
# eg. nomarchy-refresh-config hypr/hyprlock.conf will refresh ~/.config/hypr/hyprlock.conf
|
||||
nomarchy-refresh-config <config-file>
|
||||
|
||||
# Full reinstall of configs (nuclear option)
|
||||
nomarchy-reinstall
|
||||
```
|
||||
|
||||
## Decision Framework
|
||||
|
||||
When user requests system changes:
|
||||
|
||||
1. **Is it a stock nomarchy command?** Use it directly
|
||||
2. **Is it a config edit?** Edit in `~/.config/`, never `~/.local/share/nomarchy/`
|
||||
3. **Is it a theme customization?** Create a NEW custom theme directory
|
||||
4. **Is it automation?** Use hooks in `~/.config/nomarchy/hooks/`
|
||||
5. **Is it a package install?** Use `nomarchy-pkg-add` (or `nomarchy-pkg-aur-add` for AUR-only packages)
|
||||
6. **Unsure if command exists?** Search with `compgen -c | grep nomarchy`
|
||||
|
||||
## Out of Scope
|
||||
|
||||
This skill intentionally does not cover Nomarchy source development. Do not use this skill for:
|
||||
- Editing files in `~/.local/share/nomarchy/` (`bin/`, `config/`, `default/`, `themes/`, `migrations/`, etc.)
|
||||
- Creating or editing migrations
|
||||
- Modifying Nomarchy's own source tree
|
||||
|
||||
## Example Requests
|
||||
|
||||
- "Change my theme to catppuccin" -> `nomarchy-theme-set catppuccin`
|
||||
- "Add a keybinding for Super+E to open file manager" -> Check existing bindings first, add `unbind` if needed, then add `bind` in `~/.config/hypr/bindings.conf`
|
||||
- "Configure my external monitor" -> Edit `~/.config/hypr/monitors.conf`
|
||||
- "Make the window gaps smaller" -> Edit `~/.config/hypr/looknfeel.conf`
|
||||
- "Set up night light to turn on at sunset" -> `nomarchy-toggle-nightlight` or edit `~/.config/hypr/hyprsunset.conf`
|
||||
- "Customize the catppuccin theme colors" -> Create `~/.config/nomarchy/themes/catppuccin-custom/` by copying from stock, then edit
|
||||
- "Run a script every time I change themes" -> Create `~/.config/nomarchy/hooks/theme-set`
|
||||
- "Reset waybar to defaults" -> `nomarchy-refresh-waybar`
|
||||
11
core/home/config/nomarchy/default/alacritty/screensaver.toml
Normal file
11
core/home/config/nomarchy/default/alacritty/screensaver.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[colors.primary]
|
||||
background = "0x000000"
|
||||
|
||||
[colors.cursor]
|
||||
cursor = "0x000000"
|
||||
|
||||
[font]
|
||||
size = 18.0
|
||||
|
||||
[window]
|
||||
opacity = 1.0
|
||||
57
core/home/config/nomarchy/default/bash/aliases
Normal file
57
core/home/config/nomarchy/default/bash/aliases
Normal file
@@ -0,0 +1,57 @@
|
||||
# File system
|
||||
if command -v eza &> /dev/null; then
|
||||
alias ls='eza -lh --group-directories-first --icons=auto'
|
||||
alias lsa='ls -a'
|
||||
alias lt='eza --tree --level=2 --long --icons --git'
|
||||
alias lta='lt -a'
|
||||
fi
|
||||
|
||||
if [[ "$TERM" == "xterm-kitty" ]]; then
|
||||
alias ff="fzf --preview 'case \$(file --mime-type -b {}) in image/*) kitty icat --clear --transfer-mode=memory --stdin=no --place=\${FZF_PREVIEW_COLUMNS}x\${FZF_PREVIEW_LINES}@0x0 {} ;; *) bat --style=numbers --color=always {} ;; esac'"
|
||||
else
|
||||
alias ff="fzf --preview 'bat --style=numbers --color=always {}'"
|
||||
fi
|
||||
alias eff='$EDITOR "$(ff)"'
|
||||
sff() { if [ $# -eq 0 ]; then echo "Usage: sff <destination> (e.g. sff host:/tmp/)"; return 1; fi; local file; file=$(find . -type f -printf '%T@\t%p\n' | sort -rn | cut -f2- | ff) && [ -n "$file" ] && scp "$file" "$1"; }
|
||||
|
||||
if command -v zoxide &> /dev/null; then
|
||||
alias cd="zd"
|
||||
zd() {
|
||||
if (( $# == 0 )); then
|
||||
builtin cd ~ || return
|
||||
elif [[ -d $1 ]]; then
|
||||
builtin cd "$1" || return
|
||||
else
|
||||
if ! z "$@"; then
|
||||
echo "Error: Directory not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf "\U000F17A9 "
|
||||
pwd
|
||||
fi
|
||||
}
|
||||
fi
|
||||
|
||||
open() (
|
||||
xdg-open "$@" >/dev/null 2>&1 &
|
||||
)
|
||||
|
||||
# Directories
|
||||
alias ..='cd ..'
|
||||
alias ...='cd ../..'
|
||||
alias ....='cd ../../..'
|
||||
|
||||
# Tools
|
||||
alias c='opencode'
|
||||
alias cx='printf "\033[2J\033[3J\033[H" && claude --allow-dangerously-skip-permissions'
|
||||
alias d='docker'
|
||||
alias r='rails'
|
||||
alias t='tmux attach || tmux new -s Work'
|
||||
n() { if [ "$#" -eq 0 ]; then command nvim . ; else command nvim "$@"; fi; }
|
||||
|
||||
# Git
|
||||
alias g='git'
|
||||
alias gcm='git commit -m'
|
||||
alias gcam='git commit -a -m'
|
||||
alias gcad='git commit -a --amend'
|
||||
6
core/home/config/nomarchy/default/bash/envs
Normal file
6
core/home/config/nomarchy/default/bash/envs
Normal file
@@ -0,0 +1,6 @@
|
||||
# Editor used by CLI
|
||||
export SUDO_EDITOR="$EDITOR"
|
||||
export BAT_THEME=ansi
|
||||
|
||||
# Duplicated from .config/uwsm/env so SSH works too
|
||||
export PATH=$PATH:$HOME/.local/bin
|
||||
3
core/home/config/nomarchy/default/bash/fns/compression
Normal file
3
core/home/config/nomarchy/default/bash/fns/compression
Normal file
@@ -0,0 +1,3 @@
|
||||
# Compression
|
||||
compress() { tar -czf "${1%/}.tar.gz" "${1%/}"; }
|
||||
alias decompress="tar -xzf"
|
||||
59
core/home/config/nomarchy/default/bash/fns/drives
Normal file
59
core/home/config/nomarchy/default/bash/fns/drives
Normal file
@@ -0,0 +1,59 @@
|
||||
# Write iso file to sd card
|
||||
iso2sd() {
|
||||
if (( $# < 1 )); then
|
||||
echo "Usage: iso2sd <input_file> [output_device]"
|
||||
echo "Example: iso2sd ~/Downloads/ubuntu-25.04-desktop-amd64.iso /dev/sda"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local iso="$1"
|
||||
local drive="$2"
|
||||
|
||||
if [[ -z $drive ]]; then
|
||||
local available_sds=$(lsblk -dpno NAME | grep -E '/dev/sd')
|
||||
|
||||
if [[ -z $available_sds ]]; then
|
||||
echo "No SD drives found and no drive specified"
|
||||
return 1
|
||||
fi
|
||||
|
||||
drive=$(nomarchy-drive-select "$available_sds")
|
||||
|
||||
if [[ -z $drive ]]; then
|
||||
echo "No drive selected"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
sudo dd bs=4M status=progress oflag=sync if="$iso" of="$drive"
|
||||
sudo eject "$drive"
|
||||
}
|
||||
|
||||
# Format an entire drive for a single partition using exFAT
|
||||
format-drive() {
|
||||
if (( $# != 2 )); then
|
||||
echo "Usage: format-drive <device> <name>"
|
||||
echo "Example: format-drive /dev/sda 'My Stuff'"
|
||||
echo -e "\nAvailable drives:"
|
||||
lsblk -d -o NAME -n | awk '{print "/dev/"$1}'
|
||||
else
|
||||
echo "WARNING: This will completely erase all data on $1 and label it '$2'."
|
||||
read -rp "Are you sure you want to continue? (y/N): " confirm
|
||||
|
||||
if [[ $confirm =~ ^[Yy]$ ]]; then
|
||||
sudo wipefs -a "$1"
|
||||
sudo dd if=/dev/zero of="$1" bs=1M count=100 status=progress
|
||||
sudo parted -s "$1" mklabel gpt
|
||||
sudo parted -s "$1" mkpart primary 1MiB 100%
|
||||
sudo parted -s "$1" set 1 msftdata on
|
||||
|
||||
partition="$([[ $1 == *"nvme"* ]] && echo "${1}p1" || echo "${1}1")"
|
||||
sudo partprobe "$1" || true
|
||||
sudo udevadm settle || true
|
||||
|
||||
sudo mkfs.exfat -n "$2" "$partition"
|
||||
|
||||
echo "Drive $1 formatted as exFAT and labeled '$2'."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
# SSH Port Forwarding Functions
|
||||
fip() {
|
||||
(( $# < 2 )) && echo "Usage: fip <host> <port1> [port2] ..." && return 1
|
||||
local host="$1"
|
||||
shift
|
||||
for port in "$@"; do
|
||||
ssh -f -N -L "$port:localhost:$port" "$host" && echo "Forwarding localhost:$port -> $host:$port"
|
||||
done
|
||||
}
|
||||
|
||||
dip() {
|
||||
(( $# == 0 )) && echo "Usage: dip <port1> [port2] ..." && return 1
|
||||
for port in "$@"; do
|
||||
pkill -f "ssh.*-L $port:localhost:$port" && echo "Stopped forwarding port $port" || echo "No forwarding on port $port"
|
||||
done
|
||||
}
|
||||
|
||||
lip() {
|
||||
pgrep -af "ssh.*-L [0-9]+:localhost:[0-9]+" || echo "No active forwards"
|
||||
}
|
||||
97
core/home/config/nomarchy/default/bash/fns/tmux
Normal file
97
core/home/config/nomarchy/default/bash/fns/tmux
Normal file
@@ -0,0 +1,97 @@
|
||||
# Create a Tmux Dev Layout with editor, ai, and terminal
|
||||
# Usage: tdl <c|cx|codex|other_ai> [<second_ai>]
|
||||
tdl() {
|
||||
[[ -z $1 ]] && { echo "Usage: tdl <c|cx|codex|other_ai> [<second_ai>]"; return 1; }
|
||||
[[ -z $TMUX ]] && { echo "You must start tmux to use tdl."; return 1; }
|
||||
|
||||
local current_dir="${PWD}"
|
||||
local editor_pane ai_pane ai2_pane
|
||||
local ai="$1"
|
||||
local ai2="$2"
|
||||
|
||||
# Use TMUX_PANE for the pane we're running in (stable even if active window changes)
|
||||
editor_pane="$TMUX_PANE"
|
||||
|
||||
# Name the current window after the base directory name
|
||||
tmux rename-window -t "$editor_pane" "$(basename "$current_dir")"
|
||||
|
||||
# Split window vertically - top 85%, bottom 15% (target editor pane explicitly)
|
||||
tmux split-window -v -p 15 -t "$editor_pane" -c "$current_dir"
|
||||
|
||||
# Split editor pane horizontally - AI on right 30% (capture new pane ID directly)
|
||||
ai_pane=$(tmux split-window -h -p 30 -t "$editor_pane" -c "$current_dir" -P -F '#{pane_id}')
|
||||
|
||||
# If second AI provided, split the AI pane vertically
|
||||
if [[ -n $ai2 ]]; then
|
||||
ai2_pane=$(tmux split-window -v -t "$ai_pane" -c "$current_dir" -P -F '#{pane_id}')
|
||||
tmux send-keys -t "$ai2_pane" "$ai2" C-m
|
||||
fi
|
||||
|
||||
# Run ai in the right pane
|
||||
tmux send-keys -t "$ai_pane" "$ai" C-m
|
||||
|
||||
# Run nvim in the left pane
|
||||
tmux send-keys -t "$editor_pane" "$EDITOR ." C-m
|
||||
|
||||
# Select the nvim pane for focus
|
||||
tmux select-pane -t "$editor_pane"
|
||||
}
|
||||
|
||||
# Create multiple tdl windows with one per subdirectory in the current directory
|
||||
# Usage: tdlm <c|cx|codex|other_ai> [<second_ai>]
|
||||
tdlm() {
|
||||
[[ -z $1 ]] && { echo "Usage: tdlm <c|cx|codex|other_ai> [<second_ai>]"; return 1; }
|
||||
[[ -z $TMUX ]] && { echo "You must start tmux to use tdlm."; return 1; }
|
||||
|
||||
local ai="$1"
|
||||
local ai2="$2"
|
||||
local base_dir="$PWD"
|
||||
local first=true
|
||||
|
||||
# Rename the session to the current directory name (replace dots/colons which tmux disallows)
|
||||
tmux rename-session "$(basename "$base_dir" | tr '.:' '--')"
|
||||
|
||||
for dir in "$base_dir"/*/; do
|
||||
[[ -d $dir ]] || continue
|
||||
local dirpath="${dir%/}"
|
||||
|
||||
if $first; then
|
||||
# Reuse the current window for the first project
|
||||
tmux send-keys -t "$TMUX_PANE" "cd '$dirpath' && tdl $ai $ai2" C-m
|
||||
first=false
|
||||
else
|
||||
local pane_id=$(tmux new-window -c "$dirpath" -P -F '#{pane_id}')
|
||||
tmux send-keys -t "$pane_id" "tdl $ai $ai2" C-m
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Create a multi-pane swarm layout with the same command started in each pane (great for AI)
|
||||
# Usage: tsl <pane_count> <command>
|
||||
tsl() {
|
||||
[[ -z $1 || -z $2 ]] && { echo "Usage: tsl <pane_count> <command>"; return 1; }
|
||||
[[ -z $TMUX ]] && { echo "You must start tmux to use tsl."; return 1; }
|
||||
|
||||
local count="$1"
|
||||
local cmd="$2"
|
||||
local current_dir="${PWD}"
|
||||
local -a panes
|
||||
|
||||
tmux rename-window -t "$TMUX_PANE" "$(basename "$current_dir")"
|
||||
|
||||
panes+=("$TMUX_PANE")
|
||||
|
||||
while (( ${#panes[@]} < count )); do
|
||||
local new_pane
|
||||
local split_target="${panes[-1]}"
|
||||
new_pane=$(tmux split-window -h -t "$split_target" -c "$current_dir" -P -F '#{pane_id}')
|
||||
panes+=("$new_pane")
|
||||
tmux select-layout -t "${panes[0]}" tiled
|
||||
done
|
||||
|
||||
for pane in "${panes[@]}"; do
|
||||
tmux send-keys -t "$pane" "$cmd" C-m
|
||||
done
|
||||
|
||||
tmux select-pane -t "${panes[0]}"
|
||||
}
|
||||
53
core/home/config/nomarchy/default/bash/fns/transcoding
Normal file
53
core/home/config/nomarchy/default/bash/fns/transcoding
Normal file
@@ -0,0 +1,53 @@
|
||||
# Transcode a video to a good-balance 1080p that's great for sharing online
|
||||
transcode-video-1080p() {
|
||||
ffmpeg -i "$1" -vf scale=1920:1080 -c:v libx264 -preset fast -crf 23 -c:a copy "${1%.*}-1080p.mp4"
|
||||
}
|
||||
|
||||
# Transcode a video to a good-balance 4K that's great for sharing online
|
||||
transcode-video-4K() {
|
||||
ffmpeg -i "$1" -c:v libx265 -preset slow -crf 24 -c:a aac -b:a 192k "${1%.*}-optimized.mp4"
|
||||
}
|
||||
|
||||
# Transcode any image to JPG image that's great for shrinking wallpapers
|
||||
img2jpg() {
|
||||
img="$1"
|
||||
shift
|
||||
|
||||
magick "$img" "$@" -quality 85 -strip "${img%.*}-converted.jpg"
|
||||
}
|
||||
|
||||
# Transcode any image to a small JPG (max 1080px wide)
|
||||
img2jpg-small() {
|
||||
img="$1"
|
||||
shift
|
||||
|
||||
magick "$img" "$@" -resize 1080x\> -quality 85 -strip "${img%.*}-small.jpg"
|
||||
}
|
||||
|
||||
# Transcode any image to a 4K JPG (max 2160px wide)
|
||||
img2jpg-medium() {
|
||||
img="$1"
|
||||
shift
|
||||
|
||||
magick "$img" "$@" -resize 2160x\> -quality 85 -strip "${img%.*}-medium.jpg"
|
||||
}
|
||||
|
||||
# Transcode any image to a 6K JPG (max 3160px wide)
|
||||
img2jpg-large() {
|
||||
img="$1"
|
||||
shift
|
||||
|
||||
magick "$img" "$@" -resize 3160x\> -quality 85 -strip "${img%.*}-large.jpg"
|
||||
}
|
||||
|
||||
# Transcode any image to compressed-but-lossless PNG
|
||||
img2png() {
|
||||
img="$1"
|
||||
shift
|
||||
|
||||
magick "$img" "$@" -strip -define png:compression-filter=5 \
|
||||
-define png:compression-level=9 \
|
||||
-define png:compression-strategy=1 \
|
||||
-define png:exclude-chunk=all \
|
||||
"${img%.*}-optimized.png"
|
||||
}
|
||||
36
core/home/config/nomarchy/default/bash/fns/worktrees
Normal file
36
core/home/config/nomarchy/default/bash/fns/worktrees
Normal file
@@ -0,0 +1,36 @@
|
||||
# Create a new worktree and branch from within current git directory.
|
||||
ga() {
|
||||
if [[ -z "$1" ]]; then
|
||||
echo "Usage: ga [branch name]"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local branch="$1"
|
||||
local base="$(basename "$PWD")"
|
||||
local wt_path="../${base}--${branch}"
|
||||
|
||||
git worktree add -b "$branch" "$wt_path"
|
||||
mise trust "$wt_path"
|
||||
cd "$wt_path"
|
||||
}
|
||||
|
||||
# Remove worktree and branch from within active worktree directory.
|
||||
gd() {
|
||||
if gum confirm "Remove worktree and branch?"; then
|
||||
local cwd base branch root worktree
|
||||
|
||||
cwd="$(pwd)"
|
||||
worktree="$(basename "$cwd")"
|
||||
|
||||
# split on first `--`
|
||||
root="${worktree%%--*}"
|
||||
branch="${worktree#*--}"
|
||||
|
||||
# Protect against accidentally nuking a non-worktree directory
|
||||
if [[ "$root" != "$worktree" ]]; then
|
||||
cd "../$root"
|
||||
git worktree remove "$cwd" --force || return 1
|
||||
git branch -D "$branch"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
1
core/home/config/nomarchy/default/bash/functions
Normal file
1
core/home/config/nomarchy/default/bash/functions
Normal file
@@ -0,0 +1 @@
|
||||
for f in $HOME/.config/nomarchy/default/bash/fns/*; do source "$f"; done
|
||||
24
core/home/config/nomarchy/default/bash/init
Normal file
24
core/home/config/nomarchy/default/bash/init
Normal file
@@ -0,0 +1,24 @@
|
||||
if command -v mise &> /dev/null; then
|
||||
eval "$(mise activate bash)"
|
||||
fi
|
||||
|
||||
if command -v starship &> /dev/null; then
|
||||
eval "$(starship init bash)"
|
||||
fi
|
||||
|
||||
if command -v zoxide &> /dev/null; then
|
||||
eval "$(zoxide init bash)"
|
||||
fi
|
||||
|
||||
if command -v try &> /dev/null; then
|
||||
eval "$(SHELL=/bin/bash command try init ~/Work/tries)"
|
||||
fi
|
||||
|
||||
if command -v fzf &> /dev/null; then
|
||||
if [[ -f /usr/share/fzf/completion.bash ]]; then
|
||||
source /usr/share/fzf/completion.bash
|
||||
fi
|
||||
if [[ -f /usr/share/fzf/key-bindings.bash ]]; then
|
||||
source /usr/share/fzf/key-bindings.bash
|
||||
fi
|
||||
fi
|
||||
47
core/home/config/nomarchy/default/bash/inputrc
Normal file
47
core/home/config/nomarchy/default/bash/inputrc
Normal file
@@ -0,0 +1,47 @@
|
||||
set meta-flag on
|
||||
set input-meta on
|
||||
set output-meta on
|
||||
set convert-meta off
|
||||
set completion-ignore-case on
|
||||
set completion-prefix-display-length 2
|
||||
set show-all-if-ambiguous on
|
||||
set show-all-if-unmodified on
|
||||
|
||||
# Arrow keys match what you've typed so far against your command history
|
||||
"\e[A": history-search-backward
|
||||
"\e[B": history-search-forward
|
||||
"\e[C": forward-char
|
||||
"\e[D": backward-char
|
||||
|
||||
# Immediately add a trailing slash when autocompleting symlinks to directories
|
||||
set mark-symlinked-directories on
|
||||
|
||||
# Do not autocomplete hidden files unless the pattern explicitly begins with a dot
|
||||
set match-hidden-files off
|
||||
|
||||
# Show all autocomplete results at once
|
||||
set page-completions off
|
||||
|
||||
# If there are more than 200 possible completions for a word, ask to show them all
|
||||
set completion-query-items 200
|
||||
|
||||
# Show extra file information when completing, like `ls -F` does
|
||||
set visible-stats on
|
||||
|
||||
# Be more intelligent when autocompleting by also looking at the text after
|
||||
# the cursor. For example, when the current line is "cd ~/src/mozil", and
|
||||
# the cursor is on the "z", pressing Tab will not autocomplete it to "cd
|
||||
# ~/src/mozillail", but to "cd ~/src/mozilla". (This is supported by the
|
||||
# Readline used by Bash 4.)
|
||||
set skip-completed-text on
|
||||
|
||||
# Coloring for Bash 4 tab completions.
|
||||
set colored-stats on
|
||||
|
||||
# Cycle forward and backward through completion candidates (tab/shift+tab)
|
||||
# (completion listing and display behavior configured above)
|
||||
TAB: menu-complete
|
||||
"\e[Z": menu-complete-backward
|
||||
|
||||
# On first Tab, complete the common prefix before cycling candidates
|
||||
set menu-complete-display-prefix on
|
||||
6
core/home/config/nomarchy/default/bash/rc
Normal file
6
core/home/config/nomarchy/default/bash/rc
Normal file
@@ -0,0 +1,6 @@
|
||||
source ~/.config/nomarchy/default/bash/envs
|
||||
source ~/.config/nomarchy/default/bash/shell
|
||||
source ~/.config/nomarchy/default/bash/aliases
|
||||
source ~/.config/nomarchy/default/bash/functions
|
||||
source ~/.config/nomarchy/default/bash/init
|
||||
[[ $- == *i* ]] && bind -f ~/.config/nomarchy/default/bash/inputrc
|
||||
13
core/home/config/nomarchy/default/bash/shell
Normal file
13
core/home/config/nomarchy/default/bash/shell
Normal file
@@ -0,0 +1,13 @@
|
||||
# History control
|
||||
shopt -s histappend
|
||||
HISTCONTROL=ignoreboth
|
||||
HISTSIZE=32768
|
||||
HISTFILESIZE="${HISTSIZE}"
|
||||
|
||||
# Autocompletion
|
||||
if [[ ! -v BASH_COMPLETION_VERSINFO && -f /usr/share/bash-completion/bash_completion ]]; then
|
||||
source /usr/share/bash-completion/bash_completion
|
||||
fi
|
||||
|
||||
# Ensure command hashing is off for mise
|
||||
set +h
|
||||
@@ -0,0 +1,21 @@
|
||||
chrome.commands.onCommand.addListener((command) => {
|
||||
if (command === 'copy-url') {
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
||||
const currentTab = tabs[0];
|
||||
|
||||
chrome.scripting.executeScript({
|
||||
target: { tabId: currentTab.id },
|
||||
func: () => {
|
||||
navigator.clipboard.writeText(window.location.href);
|
||||
}
|
||||
}).then(() => {
|
||||
chrome.notifications.create({
|
||||
type: 'basic',
|
||||
iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
|
||||
title: ' URL copied to clipboard',
|
||||
message: ''
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
../../../../icon.png
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Copy URL",
|
||||
"version": "1.0",
|
||||
"description": "Copy current URL to clipboard, this extension is installed by Nomarchy",
|
||||
"permissions": ["activeTab", "scripting", "notifications"],
|
||||
"icons": {
|
||||
"16": "icon.png",
|
||||
"48": "icon.png",
|
||||
"128": "icon.png"
|
||||
},
|
||||
"commands": {
|
||||
"copy-url": {
|
||||
"suggested_key": {"default": "Alt+Shift+L"},
|
||||
"description": "Copy URL"
|
||||
}
|
||||
},
|
||||
"background": {"service_worker": "background.js"}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
Name = "nomarchyBackgroundSelector"
|
||||
NamePretty = "Nomarchy Background Selector"
|
||||
Cache = false
|
||||
HideFromProviderlist = true
|
||||
SearchName = true
|
||||
|
||||
local function ShellEscape(s)
|
||||
return "'" .. s:gsub("'", "'\\''") .. "'"
|
||||
end
|
||||
|
||||
function FormatName(filename)
|
||||
-- Remove leading number and dash
|
||||
local name = filename:gsub("^%d+", ""):gsub("^%-", "")
|
||||
-- Remove extension
|
||||
name = name:gsub("%.[^%.]+$", "")
|
||||
-- Replace dashes with spaces
|
||||
name = name:gsub("-", " ")
|
||||
-- Capitalize each word
|
||||
name = name:gsub("%S+", function(word)
|
||||
return word:sub(1, 1):upper() .. word:sub(2):lower()
|
||||
end)
|
||||
return name
|
||||
end
|
||||
|
||||
function GetEntries()
|
||||
local entries = {}
|
||||
local home = os.getenv("HOME")
|
||||
|
||||
-- Read current theme name
|
||||
local theme_name_file = io.open(home .. "/.config/nomarchy/current/theme.name", "r")
|
||||
local theme_name = theme_name_file and theme_name_file:read("*l") or nil
|
||||
if theme_name_file then
|
||||
theme_name_file:close()
|
||||
end
|
||||
|
||||
-- Directories to search
|
||||
local dirs = {
|
||||
home .. "/.config/nomarchy/current/theme/backgrounds",
|
||||
}
|
||||
if theme_name then
|
||||
table.insert(dirs, home .. "/.config/nomarchy/backgrounds/" .. theme_name)
|
||||
end
|
||||
|
||||
-- Track added files to avoid duplicates
|
||||
local seen = {}
|
||||
|
||||
for _, wallpaper_dir in ipairs(dirs) do
|
||||
local handle = io.popen(
|
||||
"find " .. ShellEscape(wallpaper_dir)
|
||||
.. " -maxdepth 1 -type f \\( -name '*.jpg' -o -name '*.jpeg' -o -name '*.png' -o -name '*.gif' -o -name '*.bmp' -o -name '*.webp' \\) 2>/dev/null | sort"
|
||||
)
|
||||
if handle then
|
||||
for background in handle:lines() do
|
||||
local filename = background:match("([^/]+)$")
|
||||
if filename and not seen[filename] then
|
||||
seen[filename] = true
|
||||
table.insert(entries, {
|
||||
Text = FormatName(filename),
|
||||
Value = background,
|
||||
Actions = {
|
||||
activate = "nomarchy-theme-bg-set " .. ShellEscape(background),
|
||||
},
|
||||
Preview = background,
|
||||
PreviewType = "file",
|
||||
})
|
||||
end
|
||||
end
|
||||
handle:close()
|
||||
end
|
||||
end
|
||||
|
||||
return entries
|
||||
end
|
||||
@@ -0,0 +1,95 @@
|
||||
--
|
||||
-- Dynamic Nomarchy Theme Menu for Elephant/Walker
|
||||
--
|
||||
Name = "nomarchythemes"
|
||||
NamePretty = "Nomarchy Themes"
|
||||
HideFromProviderlist = true
|
||||
|
||||
-- Check if file exists using Lua (no subprocess)
|
||||
local function file_exists(path)
|
||||
local f = io.open(path, "r")
|
||||
if f then
|
||||
f:close()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Get first matching file from directory using ls (single call for fallback)
|
||||
local function first_image_in_dir(dir)
|
||||
local handle = io.popen("ls -1 '" .. dir .. "' 2>/dev/null | head -n 1")
|
||||
if handle then
|
||||
local file = handle:read("*l")
|
||||
handle:close()
|
||||
if file and file ~= "" then
|
||||
return dir .. "/" .. file
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- The main function elephant will call
|
||||
function GetEntries()
|
||||
local entries = {}
|
||||
local user_theme_dir = os.getenv("HOME") .. "/.config/nomarchy/themes"
|
||||
local default_theme_dir = os.getenv("HOME") .. "/.local/share/nomarchy/themes"
|
||||
|
||||
local seen_themes = {}
|
||||
|
||||
-- Helper function to process themes from a directory
|
||||
local function process_themes_from_dir(theme_dir)
|
||||
-- Single find call to get all theme directories
|
||||
local handle = io.popen("find -L '" .. theme_dir .. "' -mindepth 1 -maxdepth 1 -type d 2>/dev/null")
|
||||
if not handle then
|
||||
return
|
||||
end
|
||||
|
||||
for theme_path in handle:lines() do
|
||||
local theme_name = theme_path:match(".*/(.+)$")
|
||||
|
||||
if theme_name and not seen_themes[theme_name] then
|
||||
seen_themes[theme_name] = true
|
||||
|
||||
-- Check for preview images directly (no subprocess)
|
||||
local preview_path = nil
|
||||
local preview_png = theme_path .. "/preview.png"
|
||||
local preview_jpg = theme_path .. "/preview.jpg"
|
||||
|
||||
if file_exists(preview_png) then
|
||||
preview_path = preview_png
|
||||
elseif file_exists(preview_jpg) then
|
||||
preview_path = preview_jpg
|
||||
else
|
||||
-- Fallback: get first image from backgrounds (one ls call)
|
||||
preview_path = first_image_in_dir(theme_path .. "/backgrounds")
|
||||
end
|
||||
|
||||
if preview_path and preview_path ~= "" then
|
||||
local display_name = theme_name:gsub("_", " "):gsub("%-", " ")
|
||||
display_name = display_name:gsub("(%a)([%w_']*)", function(first, rest)
|
||||
return first:upper() .. rest:lower()
|
||||
end)
|
||||
display_name = display_name .. " "
|
||||
|
||||
table.insert(entries, {
|
||||
Text = display_name,
|
||||
Preview = preview_path,
|
||||
PreviewType = "file",
|
||||
Actions = {
|
||||
activate = "nomarchy-theme-set " .. theme_name,
|
||||
},
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
handle:close()
|
||||
end
|
||||
|
||||
-- Process user themes first (they take precedence)
|
||||
process_themes_from_dir(user_theme_dir)
|
||||
-- Then process default themes (only if not already seen)
|
||||
process_themes_from_dir(default_theme_dir)
|
||||
|
||||
return entries
|
||||
end
|
||||
3
core/home/config/nomarchy/default/ghostty/screensaver
Normal file
3
core/home/config/nomarchy/default/ghostty/screensaver
Normal file
@@ -0,0 +1,3 @@
|
||||
window-padding-x = 0
|
||||
window-padding-y = 0
|
||||
window-padding-color = "extend-always"
|
||||
7
core/home/config/nomarchy/default/gpg/dirmngr.conf
Normal file
7
core/home/config/nomarchy/default/gpg/dirmngr.conf
Normal file
@@ -0,0 +1,7 @@
|
||||
keyserver hkps://keyserver.ubuntu.com
|
||||
keyserver hkps://pgp.surfnet.nl
|
||||
keyserver hkps://keys.mailvelope.com
|
||||
keyserver hkps://keyring.debian.org
|
||||
keyserver hkps://pgp.mit.edu
|
||||
|
||||
connect-quick-timeout 4
|
||||
9
core/home/config/nomarchy/default/hypr/apps.conf
Normal file
9
core/home/config/nomarchy/default/hypr/apps.conf
Normal file
@@ -0,0 +1,9 @@
|
||||
# App-specific tweaks
|
||||
source = ~/.config/nomarchy/default/hypr/apps/browser.conf
|
||||
source = ~/.config/nomarchy/default/hypr/apps/hyprshot.conf
|
||||
source = ~/.config/nomarchy/default/hypr/apps/localsend.conf
|
||||
source = ~/.config/nomarchy/default/hypr/apps/retroarch.conf
|
||||
source = ~/.config/nomarchy/default/hypr/apps/telegram.conf
|
||||
source = ~/.config/nomarchy/default/hypr/apps/terminals.conf
|
||||
source = ~/.config/nomarchy/default/hypr/apps/walker.conf
|
||||
source = ~/.config/nomarchy/default/hypr/apps/webcam-overlay.conf
|
||||
@@ -0,0 +1,2 @@
|
||||
windowrulev2 = noscreenshare, class:^(1[p|P]assword)$
|
||||
windowrulev2 = tag +floating-window, class:^(1[p|P]assword)$
|
||||
@@ -0,0 +1,6 @@
|
||||
windowrulev2 = noscreenshare, class:^(Bitwarden)$
|
||||
windowrulev2 = tag +floating-window, class:^(Bitwarden)$
|
||||
|
||||
# Bitwarden Chrome Extension
|
||||
windowrulev2 = noscreenshare, class:chrome-nngceckbapebfimnlniiiahkandclblb-Default
|
||||
windowrulev2 = tag +floating-window, class:chrome-nngceckbapebfimnlniiiahkandclblb-Default
|
||||
16
core/home/config/nomarchy/default/hypr/apps/browser.conf
Normal file
16
core/home/config/nomarchy/default/hypr/apps/browser.conf
Normal file
@@ -0,0 +1,16 @@
|
||||
# Browser types
|
||||
windowrulev2 = tag +chromium-based-browser, class:((google-)?[cC]hrom(e|ium)|[bB]rave-browser|[mM]icrosoft-edge|Vivaldi-stable|helium)
|
||||
windowrulev2 = tag +firefox-based-browser, class:([fF]irefox|zen|librewolf)
|
||||
windowrulev2 = tag -default-opacity, tag:chromium-based-browser
|
||||
windowrulev2 = tag -default-opacity, tag:firefox-based-browser
|
||||
|
||||
# Video apps: remove chromium browser tag so they don't get opacity applied
|
||||
windowrulev2 = tag -chromium-based-browser, class:(chrome-youtube.com__-Default|chrome-app.zoom.us__wc_home-Default)
|
||||
windowrulev2 = tag -default-opacity, class:(chrome-youtube.com__-Default|chrome-app.zoom.us__wc_home-Default)
|
||||
|
||||
# Force chromium-based browsers into a tile to deal with --app bug
|
||||
windowrulev2 = tile, tag:chromium-based-browser
|
||||
|
||||
# Only a subtle opacity change, but not for video sites
|
||||
windowrulev2 = opacity 1.0 0.97, tag:chromium-based-browser
|
||||
windowrulev2 = opacity 1.0 0.97, tag:firefox-based-browser
|
||||
@@ -0,0 +1,2 @@
|
||||
# Focus floating DaVinci Resolve dialog windows
|
||||
windowrulev2 = stayfocused, class:.*[Rr]esolve.*, float:1
|
||||
5
core/home/config/nomarchy/default/hypr/apps/geforce.conf
Normal file
5
core/home/config/nomarchy/default/hypr/apps/geforce.conf
Normal file
@@ -0,0 +1,5 @@
|
||||
windowrule {
|
||||
name = geforce
|
||||
class:= GeForceNOW
|
||||
idle_inhibit = fullscreen
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
# Remove 1px border around hyprshot screenshots
|
||||
layerrule = noanim, namespace:selection
|
||||
@@ -0,0 +1,4 @@
|
||||
# Float LocalSend and fzf file picker
|
||||
windowrulev2 = float, class:(Share|localsend)
|
||||
windowrulev2 = center, class:(Share|localsend)
|
||||
windowrulev2 = size 1100 700, class:localsend
|
||||
@@ -0,0 +1,6 @@
|
||||
windowrule {
|
||||
name = moonlight
|
||||
class:= com.moonlight_stream.Moonlight
|
||||
fullscreen = 1
|
||||
idle_inhibit = fullscreen
|
||||
}
|
||||
10
core/home/config/nomarchy/default/hypr/apps/pip.conf
Normal file
10
core/home/config/nomarchy/default/hypr/apps/pip.conf
Normal file
@@ -0,0 +1,10 @@
|
||||
# Picture-in-picture overlays
|
||||
windowrulev2 = tag +pip, title:(Picture.?in.?[Pp]icture)
|
||||
windowrulev2 = tag -default-opacity, tag:pip
|
||||
windowrulev2 = float, tag:pip
|
||||
windowrulev2 = pin, tag:pip
|
||||
windowrulev2 = size 600 338, tag:pip
|
||||
windowrulev2 = keep_aspect_ratio on, tag:pip
|
||||
windowrulev2 = border_size 0, tag:pip
|
||||
windowrulev2 = opacity 1 1, tag:pip
|
||||
windowrulev2 = move (monitor_w-window_w-40) (monitor_h*0.04), tag:pip
|
||||
2
core/home/config/nomarchy/default/hypr/apps/qemu.conf
Normal file
2
core/home/config/nomarchy/default/hypr/apps/qemu.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
windowrulev2 = tag -default-opacity, class:qemu
|
||||
windowrulev2 = opacity 1 1, class:qemu
|
||||
@@ -0,0 +1,4 @@
|
||||
windowrulev2 = fullscreen, class:com.libretro.RetroArch
|
||||
windowrulev2 = tag -default-opacity, class:com.libretro.RetroArch
|
||||
windowrulev2 = opacity 1 1, class:com.libretro.RetroArch
|
||||
windowrulev2 = idleinhibit, fullscreen, class:com.libretro.RetroArch
|
||||
8
core/home/config/nomarchy/default/hypr/apps/steam.conf
Normal file
8
core/home/config/nomarchy/default/hypr/apps/steam.conf
Normal file
@@ -0,0 +1,8 @@
|
||||
# Float Steam
|
||||
windowrulev2 = float, class:steam
|
||||
windowrulev2 = center, class:steam, title:Steam
|
||||
windowrulev2 = tag -default-opacity, class:steam.*
|
||||
windowrulev2 = opacity 1 1, class:steam.*
|
||||
windowrulev2 = size 1100 700, class:steam, title:Steam
|
||||
windowrulev2 = size 460 800, class:steam, title:Friends List
|
||||
windowrulev2 = idleinhibit, fullscreen, class:steam
|
||||
23
core/home/config/nomarchy/default/hypr/apps/system.conf
Normal file
23
core/home/config/nomarchy/default/hypr/apps/system.conf
Normal file
@@ -0,0 +1,23 @@
|
||||
# Floating windows
|
||||
windowrulev2 = float, tag:floating-window
|
||||
windowrulev2 = center, tag:floating-window
|
||||
windowrulev2 = size 875 600, tag:floating-window
|
||||
|
||||
windowrulev2 = tag +floating-window, class:(org.nomarchy.bluetui|org.nomarchy.impala|org.nomarchy.wiremix|org.nomarchy.btop|org.nomarchy.terminal|org.nomarchy.bash|org.gnome.NautilusPreviewer|org.gnome.Evince|com.gabm.satty|Nomarchy|About|TUI.float|imv|mpv)
|
||||
windowrulev2 = tag +floating-window, class:(xdg-desktop-portal-gtk|sublime_text|DesktopEditors|org.gnome.Nautilus), title:^(Open.*Files?|Open [F|f]older.*|Save.*Files?|Save.*As|Save|All Files|.*wants to [open|save].*|[C|c]hoose.*)
|
||||
windowrulev2 = float, class:org.gnome.Calculator
|
||||
|
||||
# Fullscreen screensaver
|
||||
windowrulev2 = fullscreen, class:org.nomarchy.screensaver
|
||||
windowrulev2 = float, class:org.nomarchy.screensaver
|
||||
windowrulev2 = animation slide, class:org.nomarchy.screensaver
|
||||
|
||||
# No transparency on media windows
|
||||
windowrulev2 = tag -default-opacity, class:^(zoom|vlc|mpv|org.kde.kdenlive|com.obsproject.Studio|com.github.PintaProject.Pinta|imv|org.gnome.NautilusPreviewer)$
|
||||
windowrulev2 = opacity 1 1, class:^(zoom|vlc|mpv|org.kde.kdenlive|com.obsproject.Studio|com.github.PintaProject.Pinta|imv|org.gnome.NautilusPreviewer)$
|
||||
|
||||
# Popped window rounding
|
||||
windowrulev2 = rounding 8, tag:pop
|
||||
|
||||
# Prevent idle while open
|
||||
windowrulev2 = idleinhibit, always, tag:noidle
|
||||
@@ -0,0 +1,2 @@
|
||||
# Prevent Telegram from stealing focus on new messages
|
||||
windowrulev2 = focusonactivate off, class:org.telegram.desktop
|
||||
@@ -0,0 +1,4 @@
|
||||
# Define terminal tag to style them uniformly
|
||||
windowrulev2 = tag +terminal, class:(Alacritty|kitty|com.mitchellh.ghostty)
|
||||
windowrulev2 = tag -default-opacity, tag:terminal
|
||||
windowrulev2 = opacity 0.97 0.9, tag:terminal
|
||||
2
core/home/config/nomarchy/default/hypr/apps/walker.conf
Normal file
2
core/home/config/nomarchy/default/hypr/apps/walker.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
# Application-specific animation
|
||||
layerrule = noanim, namespace:walker
|
||||
@@ -0,0 +1,6 @@
|
||||
# Webcam overlay for screen recording
|
||||
windowrulev2 = float, title:WebcamOverlay
|
||||
windowrulev2 = pin, title:WebcamOverlay
|
||||
windowrulev2 = noinitialfocus, title:WebcamOverlay
|
||||
windowrulev2 = nodim, title:WebcamOverlay
|
||||
windowrulev2 = move (monitor_w-window_w-40) (monitor_h-window_h-40), title:WebcamOverlay
|
||||
17
core/home/config/nomarchy/default/hypr/autostart.conf
Normal file
17
core/home/config/nomarchy/default/hypr/autostart.conf
Normal file
@@ -0,0 +1,17 @@
|
||||
exec-once = uwsm-app -- hypridle
|
||||
exec-once = uwsm-app -- mako
|
||||
exec-once = nomarchy-welcome
|
||||
# exec-once = uwsm-app -- waybar
|
||||
# fcitx5 is autostarted by NixOS's i18n.inputMethod when
|
||||
# nomarchy.system.inputMethod.enable = true; no manual exec-once needed.
|
||||
# swaybg is started as a systemd user service (nomarchy-wallpaper.service)
|
||||
# so failures surface in logs and the wallpaper survives Hyprland restarts.
|
||||
exec-once = uwsm-app -- swayosd-server
|
||||
exec-once = nomarchy-on-boot
|
||||
|
||||
# Slow app launch fix -- set systemd vars
|
||||
exec-once = systemctl --user import-environment $(env | cut -d'=' -f 1)
|
||||
exec-once = dbus-update-activation-environment --systemd --all
|
||||
|
||||
# Network Manager
|
||||
exec-once = uwsm-app 'nm-applet --indicator'
|
||||
16
core/home/config/nomarchy/default/hypr/bindings.conf
Normal file
16
core/home/config/nomarchy/default/hypr/bindings.conf
Normal file
@@ -0,0 +1,16 @@
|
||||
# Deprecated bindings file. New installations include everything directly.
|
||||
|
||||
bindd = SUPER, RETURN, Terminal, exec, $terminal
|
||||
bindd = SUPER, F, File manager, exec, $fileManager
|
||||
bindd = SUPER, B, Web browser, exec, $browser
|
||||
bindd = SUPER, M, Music player, exec, $music
|
||||
bindd = SUPER, N, Neovim, exec, $terminal -e nvim
|
||||
bindd = SUPER, T, Top, exec, $terminal -e btop
|
||||
bindd = SUPER, D, Lazy Docker, exec, $terminal -e lazydocker
|
||||
bindd = SUPER, G, Messenger, exec, $messenger
|
||||
bindd = SUPER, O, Obsidian, exec, obsidian -disable-gpu
|
||||
bindd = SUPER, SLASH, Password manager, exec, $passwordManager
|
||||
|
||||
source = ~/.config/nomarchy/default/hypr/bindings/media.conf
|
||||
source = ~/.config/nomarchy/default/hypr/bindings/tiling-v2.conf
|
||||
source = ~/.config/nomarchy/default/hypr/bindings/utilities.conf
|
||||
@@ -0,0 +1,5 @@
|
||||
# Copy / Paste
|
||||
bindd = SUPER, C, Universal copy, sendshortcut, CTRL, Insert,
|
||||
bindd = SUPER, V, Universal paste, sendshortcut, SHIFT, Insert,
|
||||
bindd = SUPER, X, Universal cut, sendshortcut, CTRL, X,
|
||||
bindd = SUPER CTRL, V, Clipboard manager, exec, nomarchy-launch-walker -m clipboard
|
||||
28
core/home/config/nomarchy/default/hypr/bindings/media.conf
Normal file
28
core/home/config/nomarchy/default/hypr/bindings/media.conf
Normal file
@@ -0,0 +1,28 @@
|
||||
# Only display the OSD on the currently focused monitor
|
||||
$osdclient = swayosd-client --monitor "$(hyprctl monitors -j | jq -r '.[] | select(.focused == true).name')"
|
||||
|
||||
# Laptop multimedia keys for volume and LCD brightness (with OSD)
|
||||
bindeld = ,XF86AudioRaiseVolume, Volume up, exec, $osdclient --output-volume raise
|
||||
bindeld = ,XF86AudioLowerVolume, Volume down, exec, $osdclient --output-volume lower
|
||||
bindeld = ,XF86AudioMute, Mute, exec, $osdclient --output-volume mute-toggle
|
||||
bindeld = ,XF86AudioMicMute, Mute microphone, exec, $osdclient --input-volume mute-toggle
|
||||
bindeld = ,XF86MonBrightnessUp, Brightness up, exec, nomarchy-brightness-display +5%
|
||||
bindeld = ,XF86MonBrightnessDown, Brightness down, exec, nomarchy-brightness-display 5%-
|
||||
bindeld = ,XF86KbdBrightnessUp, Keyboard brightness up, exec, nomarchy-brightness-keyboard up
|
||||
bindeld = ,XF86KbdBrightnessDown, Keyboard brightness down, exec, nomarchy-brightness-keyboard down
|
||||
bindld = ,XF86KbdLightOnOff, Keyboard backlight cycle, exec, nomarchy-brightness-keyboard cycle
|
||||
|
||||
# Precise 1% multimedia adjustments with Alt modifier
|
||||
bindeld = ALT, XF86AudioRaiseVolume, Volume up precise, exec, $osdclient --output-volume +1
|
||||
bindeld = ALT, XF86AudioLowerVolume, Volume down precise, exec, $osdclient --output-volume -1
|
||||
bindeld = ALT, XF86MonBrightnessUp, Brightness up precise, exec, nomarchy-brightness-display +1%
|
||||
bindeld = ALT, XF86MonBrightnessDown, Brightness down precise, exec, nomarchy-brightness-display 1%-
|
||||
|
||||
# Requires playerctl
|
||||
bindld = , XF86AudioNext, Next track, exec, $osdclient --playerctl next
|
||||
bindld = , XF86AudioPause, Pause, exec, $osdclient --playerctl play-pause
|
||||
bindld = , XF86AudioPlay, Play, exec, $osdclient --playerctl play-pause
|
||||
bindld = , XF86AudioPrev, Previous track, exec, $osdclient --playerctl previous
|
||||
|
||||
# Switch audio output with Super + Mute
|
||||
bindld = SUPER, XF86AudioMute, Switch audio output, exec, nomarchy-cmd-audio-switch
|
||||
128
core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf
Normal file
128
core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf
Normal file
@@ -0,0 +1,128 @@
|
||||
# Close windows
|
||||
bindd = SUPER, W, Close window, killactive,
|
||||
bindd = CTRL ALT, DELETE, Close all windows, exec, nomarchy-hyprland-window-close-all
|
||||
|
||||
# Control tiling
|
||||
bindd = SUPER, J, Toggle window split, layoutmsg, togglesplit
|
||||
bindd = SUPER, P, Pseudo window, pseudo, # dwindle
|
||||
bindd = SUPER, T, Toggle window floating/tiling, togglefloating,
|
||||
bindd = SUPER, F, Full screen, fullscreen, 0
|
||||
bindd = SUPER CTRL, F, Tiled full screen, fullscreenstate, 0 2
|
||||
bindd = SUPER ALT, F, Full width, fullscreen, 1
|
||||
bindd = SUPER, O, Pop window out (float & pin), exec, nomarchy-hyprland-window-pop
|
||||
bindd = SUPER, L, Toggle workspace layout, exec, nomarchy-hyprland-workspace-layout-toggle
|
||||
|
||||
# Move focus with SUPER + arrow keys
|
||||
bindd = SUPER, LEFT, Move window focus left, movefocus, l
|
||||
bindd = SUPER, RIGHT, Move window focus right, movefocus, r
|
||||
bindd = SUPER, UP, Move window focus up, movefocus, u
|
||||
bindd = SUPER, DOWN, Move window focus down, movefocus, d
|
||||
|
||||
# Switch workspaces with SUPER + [1-9; 0]
|
||||
bindd = SUPER, code:10, Switch to workspace 1, workspace, 1
|
||||
bindd = SUPER, code:11, Switch to workspace 2, workspace, 2
|
||||
bindd = SUPER, code:12, Switch to workspace 3, workspace, 3
|
||||
bindd = SUPER, code:13, Switch to workspace 4, workspace, 4
|
||||
bindd = SUPER, code:14, Switch to workspace 5, workspace, 5
|
||||
bindd = SUPER, code:15, Switch to workspace 6, workspace, 6
|
||||
bindd = SUPER, code:16, Switch to workspace 7, workspace, 7
|
||||
bindd = SUPER, code:17, Switch to workspace 8, workspace, 8
|
||||
bindd = SUPER, code:18, Switch to workspace 9, workspace, 9
|
||||
bindd = SUPER, code:19, Switch to workspace 10, workspace, 10
|
||||
|
||||
# Move active window to a workspace with SUPER + SHIFT + [1-9; 0]
|
||||
bindd = SUPER SHIFT, code:10, Move window to workspace 1, movetoworkspace, 1
|
||||
bindd = SUPER SHIFT, code:11, Move window to workspace 2, movetoworkspace, 2
|
||||
bindd = SUPER SHIFT, code:12, Move window to workspace 3, movetoworkspace, 3
|
||||
bindd = SUPER SHIFT, code:13, Move window to workspace 4, movetoworkspace, 4
|
||||
bindd = SUPER SHIFT, code:14, Move window to workspace 5, movetoworkspace, 5
|
||||
bindd = SUPER SHIFT, code:15, Move window to workspace 6, movetoworkspace, 6
|
||||
bindd = SUPER SHIFT, code:16, Move window to workspace 7, movetoworkspace, 7
|
||||
bindd = SUPER SHIFT, code:17, Move window to workspace 8, movetoworkspace, 8
|
||||
bindd = SUPER SHIFT, code:18, Move window to workspace 9, movetoworkspace, 9
|
||||
bindd = SUPER SHIFT, code:19, Move window to workspace 10, movetoworkspace, 10
|
||||
|
||||
# Move active window silently to a workspace with SUPER + SHIFT + ALT + [1-9; 0]
|
||||
bindd = SUPER SHIFT ALT, code:10, Move window silently to workspace 1, movetoworkspacesilent, 1
|
||||
bindd = SUPER SHIFT ALT, code:11, Move window silently to workspace 2, movetoworkspacesilent, 2
|
||||
bindd = SUPER SHIFT ALT, code:12, Move window silently to workspace 3, movetoworkspacesilent, 3
|
||||
bindd = SUPER SHIFT ALT, code:13, Move window silently to workspace 4, movetoworkspacesilent, 4
|
||||
bindd = SUPER SHIFT ALT, code:14, Move window silently to workspace 5, movetoworkspacesilent, 5
|
||||
bindd = SUPER SHIFT ALT, code:15, Move window silently to workspace 6, movetoworkspacesilent, 6
|
||||
bindd = SUPER SHIFT ALT, code:16, Move window silently to workspace 7, movetoworkspacesilent, 7
|
||||
bindd = SUPER SHIFT ALT, code:17, Move window silently to workspace 8, movetoworkspacesilent, 8
|
||||
bindd = SUPER SHIFT ALT, code:18, Move window silently to workspace 9, movetoworkspacesilent, 9
|
||||
bindd = SUPER SHIFT ALT, code:19, Move window silently to workspace 10, movetoworkspacesilent, 10
|
||||
|
||||
# Control scratchpad
|
||||
bindd = SUPER, S, Toggle scratchpad, togglespecialworkspace, scratchpad
|
||||
bindd = SUPER ALT, S, Move window to scratchpad, movetoworkspacesilent, special:scratchpad
|
||||
|
||||
# TAB between workspaces
|
||||
bindd = SUPER, TAB, Next workspace, workspace, e+1
|
||||
bindd = SUPER SHIFT, TAB, Previous workspace, workspace, e-1
|
||||
bindd = SUPER CTRL, TAB, Former workspace, workspace, previous
|
||||
|
||||
# Move workspaces to other monitors
|
||||
bindd = SUPER SHIFT ALT, LEFT, Move workspace to left monitor, movecurrentworkspacetomonitor, l
|
||||
bindd = SUPER SHIFT ALT, RIGHT, Move workspace to right monitor, movecurrentworkspacetomonitor, r
|
||||
bindd = SUPER SHIFT ALT, UP, Move workspace to up monitor, movecurrentworkspacetomonitor, u
|
||||
bindd = SUPER SHIFT ALT, DOWN, Move workspace to down monitor, movecurrentworkspacetomonitor, d
|
||||
|
||||
# Swap active window with the one next to it with SUPER + SHIFT + arrow keys
|
||||
bindd = SUPER SHIFT, LEFT, Swap window to the left, swapwindow, l
|
||||
bindd = SUPER SHIFT, RIGHT, Swap window to the right, swapwindow, r
|
||||
bindd = SUPER SHIFT, UP, Swap window up, swapwindow, u
|
||||
bindd = SUPER SHIFT, DOWN, Swap window down, swapwindow, d
|
||||
|
||||
# Cycle through applications on active workspace
|
||||
bindd = ALT, TAB, Cycle to next window, cyclenext
|
||||
bindd = ALT SHIFT, TAB, Cycle to prev window, cyclenext, prev
|
||||
bindd = ALT, TAB, Reveal active window on top, bringactivetotop
|
||||
bindd = ALT SHIFT, TAB, Reveal active window on top, bringactivetotop
|
||||
|
||||
# Resize active window
|
||||
bindd = SUPER, code:20, Expand window left, resizeactive, -100 0 # - key
|
||||
bindd = SUPER, code:21, Shrink window left, resizeactive, 100 0 # = key
|
||||
bindd = SUPER SHIFT, code:20, Shrink window up, resizeactive, 0 -100
|
||||
bindd = SUPER SHIFT, code:21, Expand window down, resizeactive, 0 100
|
||||
|
||||
# Scroll through existing workspaces with SUPER + scroll
|
||||
bindd = SUPER, mouse_down, Scroll active workspace forward, workspace, e+1
|
||||
bindd = SUPER, mouse_up, Scroll active workspace backward, workspace, e-1
|
||||
|
||||
# Move/resize windows with mainMod + LMB/RMB and dragging
|
||||
bindmd = SUPER, mouse:272, Move window, movewindow
|
||||
bindmd = SUPER, mouse:273, Resize window, resizewindow
|
||||
|
||||
# Toggle groups
|
||||
bindd = SUPER, G, Toggle window grouping, togglegroup
|
||||
bindd = SUPER ALT, G, Move active window out of group, moveoutofgroup
|
||||
|
||||
# Join groups
|
||||
bindd = SUPER ALT, LEFT, Move window to group on left, moveintogroup, l
|
||||
bindd = SUPER ALT, RIGHT, Move window to group on right, moveintogroup, r
|
||||
bindd = SUPER ALT, UP, Move window to group on top, moveintogroup, u
|
||||
bindd = SUPER ALT, DOWN, Move window to group on bottom, moveintogroup, d
|
||||
|
||||
# Navigate a single set of grouped windows
|
||||
bindd = SUPER ALT, TAB, Next window in group, changegroupactive, f
|
||||
bindd = SUPER ALT SHIFT, TAB, Previous window in group, changegroupactive, b
|
||||
|
||||
# Window navigation for grouped windows
|
||||
bindd = SUPER CTRL, LEFT, Move grouped window focus left, changegroupactive, b
|
||||
bindd = SUPER CTRL, RIGHT, Move grouped window focus right, changegroupactive, f
|
||||
|
||||
# Scroll through a set of grouped windows with SUPER + ALT + scroll
|
||||
bindd = SUPER ALT, mouse_down, Next window in group, changegroupactive, f
|
||||
bindd = SUPER ALT, mouse_up, Previous window in group, changegroupactive, b
|
||||
|
||||
# Activate window in a group by number
|
||||
bindd = SUPER ALT, code:10, Switch to group window 1, changegroupactive, 1
|
||||
bindd = SUPER ALT, code:11, Switch to group window 2, changegroupactive, 2
|
||||
bindd = SUPER ALT, code:12, Switch to group window 3, changegroupactive, 3
|
||||
bindd = SUPER ALT, code:13, Switch to group window 4, changegroupactive, 4
|
||||
bindd = SUPER ALT, code:14, Switch to group window 5, changegroupactive, 5
|
||||
|
||||
# Cycle monitor scaling
|
||||
bindd = SUPER, Slash, Cycle monitor scaling, exec, nomarchy-hyprland-monitor-scaling-cycle
|
||||
@@ -0,0 +1,62 @@
|
||||
# Menus
|
||||
bindd = SUPER, SPACE, Launch apps, exec, nomarchy-launch-walker
|
||||
bindd = SUPER CTRL, E, Emoji picker, exec, nomarchy-menu symbols
|
||||
bindd = SUPER CTRL, C, Capture menu, exec, nomarchy-menu capture
|
||||
bindd = SUPER CTRL, O, Toggle menu, exec, nomarchy-menu toggle
|
||||
bindd = SUPER ALT, SPACE, Toggle top bar, exec, nomarchy-toggle-waybar
|
||||
bindd = SUPER, ESCAPE, System menu, exec, nomarchy-menu system
|
||||
bindld = , XF86PowerOff, Power menu, exec, nomarchy-menu system
|
||||
bindd = SUPER, K, Show key bindings, exec, nomarchy-menu-keybindings
|
||||
bindd = , XF86Calculator, Calculator, exec, gnome-calculator
|
||||
|
||||
# Aesthetics
|
||||
bindd = SUPER SHIFT, SPACE, Nomarchy menu, exec, nomarchy-menu
|
||||
bindd = SUPER CTRL, SPACE, Theme background menu, exec, nomarchy-menu background
|
||||
bindd = SUPER SHIFT CTRL, SPACE, Theme menu, exec, nomarchy-menu theme
|
||||
bindd = SUPER, BACKSPACE, Toggle window transparency, exec, nomarchy-hyprland-active-window-transparency-toggle
|
||||
bindd = SUPER SHIFT, BACKSPACE, Toggle window gaps, exec, nomarchy-hyprland-window-gaps-toggle
|
||||
bindd = SUPER CTRL, BACKSPACE, Toggle single-window square aspect, exec, nomarchy-hyprland-window-single-square-aspect-toggle
|
||||
|
||||
# Notifications
|
||||
bindd = SUPER, COMMA, Dismiss last notification, exec, makoctl dismiss
|
||||
bindd = SUPER SHIFT, COMMA, Dismiss all notifications, exec, makoctl dismiss --all
|
||||
bindd = SUPER CTRL, COMMA, Toggle silencing notifications, exec, nomarchy-toggle-notification-silencing
|
||||
bindd = SUPER ALT, COMMA, Invoke last notification, exec, makoctl invoke
|
||||
bindd = SUPER SHIFT ALT, COMMA, Restore last notification, exec, makoctl restore
|
||||
|
||||
# Toggles
|
||||
bindd = SUPER CTRL, I, Toggle locking on idle, exec, nomarchy-toggle-idle
|
||||
bindd = SUPER CTRL, N, Toggle nightlight, exec, nomarchy-toggle-nightlight
|
||||
|
||||
# Control Apple Display brightness
|
||||
bindd = CTRL, F1, Apple Display brightness down, exec, nomarchy-brightness-display-apple -5000
|
||||
bindd = CTRL, F2, Apple Display brightness up, exec, nomarchy-brightness-display-apple +5000
|
||||
bindd = SHIFT CTRL, F2, Apple Display full brightness, exec, nomarchy-brightness-display-apple +60000
|
||||
|
||||
# Captures
|
||||
bindd = , PRINT, Screenshot, exec, nomarchy-cmd-screenshot
|
||||
bindd = ALT, PRINT, Screenrecording, exec, nomarchy-menu screenrecord
|
||||
bindd = SUPER, PRINT, Color picker, exec, pkill hyprpicker || hyprpicker -a
|
||||
|
||||
# File sharing
|
||||
bindd = SUPER CTRL, S, Share, exec, nomarchy-menu share
|
||||
|
||||
# Waybar-less information
|
||||
bindd = SUPER CTRL ALT, T, Show time, exec, notify-send -u low " $(date +"%A %H:%M · %d %B %Y · Week %V")"
|
||||
bindd = SUPER CTRL ALT, B, Show battery remaining, exec, notify-send -u low "$(nomarchy-battery-status)"
|
||||
|
||||
# Control panels
|
||||
bindd = SUPER CTRL, A, Audio controls, exec, nomarchy-launch-audio
|
||||
bindd = SUPER CTRL, B, Bluetooth controls, exec, nomarchy-launch-bluetooth
|
||||
bindd = SUPER CTRL, W, Wifi controls, exec, nomarchy-launch-wifi
|
||||
bindd = SUPER CTRL, T, Activity, exec, nomarchy-launch-tui btop
|
||||
|
||||
# Dictation
|
||||
bindd = SUPER CTRL, X, Toggle dictation, exec, voxtype record toggle
|
||||
|
||||
# Zoom
|
||||
bindd = SUPER CTRL, Z, Zoom in, exec, hyprctl keyword cursor:zoom_factor $(hyprctl getoption cursor:zoom_factor -j | jq '.float + 1')
|
||||
bindd = SUPER CTRL ALT, Z, Reset zoom, exec, hyprctl keyword cursor:zoom_factor 1
|
||||
|
||||
# Lock system
|
||||
bindd = SUPER CTRL, L, Lock system, exec, nomarchy-lock-screen
|
||||
12
core/home/config/nomarchy/default/hypr/colors.conf
Normal file
12
core/home/config/nomarchy/default/hypr/colors.conf
Normal file
@@ -0,0 +1,12 @@
|
||||
# Hyprland Color Configuration (Visual)
|
||||
# This file contains only color-related settings.
|
||||
# It's sourced AFTER theme colors are applied, allowing theme-specific overrides.
|
||||
|
||||
# These are placeholder values - actual colors come from the active theme
|
||||
# via ~/.config/nomarchy/current/theme/hyprland.conf
|
||||
|
||||
# Border colors (set by theme-loader based on active theme)
|
||||
# The theme's hyprland.conf will define $activeBorderColor and $inactiveBorderColor
|
||||
|
||||
# Group bar colors (set by theme)
|
||||
# Themes can override group bar colors in their hyprland.conf
|
||||
29
core/home/config/nomarchy/default/hypr/envs.conf
Normal file
29
core/home/config/nomarchy/default/hypr/envs.conf
Normal file
@@ -0,0 +1,29 @@
|
||||
# Cursor size
|
||||
env = XCURSOR_SIZE,24
|
||||
env = HYPRCURSOR_SIZE,24
|
||||
|
||||
# Force all apps to use Wayland
|
||||
env = GDK_BACKEND,wayland,x11,*
|
||||
env = QT_QPA_PLATFORM,wayland;xcb
|
||||
env = QT_STYLE_OVERRIDE,kvantum
|
||||
env = SDL_VIDEODRIVER,wayland,x11
|
||||
env = MOZ_ENABLE_WAYLAND,1
|
||||
env = ELECTRON_OZONE_PLATFORM_HINT,wayland
|
||||
env = OZONE_PLATFORM,wayland
|
||||
env = XDG_SESSION_TYPE,wayland
|
||||
|
||||
# Allow better support for screen sharing (Google Meet, Discord, etc)
|
||||
env = XDG_CURRENT_DESKTOP,Hyprland
|
||||
env = XDG_SESSION_DESKTOP,Hyprland
|
||||
|
||||
xwayland {
|
||||
force_zero_scaling = true
|
||||
}
|
||||
|
||||
# Use XCompose file
|
||||
env = XCOMPOSEFILE,~/.XCompose
|
||||
|
||||
# Don't show update on first launch
|
||||
ecosystem {
|
||||
no_update_news = true
|
||||
}
|
||||
21
core/home/config/nomarchy/default/hypr/input.conf
Normal file
21
core/home/config/nomarchy/default/hypr/input.conf
Normal file
@@ -0,0 +1,21 @@
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#input
|
||||
input {
|
||||
kb_layout = us
|
||||
kb_variant =
|
||||
kb_model =
|
||||
kb_options = compose:caps
|
||||
kb_rules =
|
||||
|
||||
follow_mouse = 1
|
||||
|
||||
sensitivity = 0 # -1.0 - 1.0, 0 means no modification.
|
||||
|
||||
touchpad {
|
||||
natural_scroll = false
|
||||
}
|
||||
}
|
||||
|
||||
misc {
|
||||
key_press_enables_dpms = true # key press will trigger wake
|
||||
mouse_move_enables_dpms = true # mouse move will trigger wake
|
||||
}
|
||||
146
core/home/config/nomarchy/default/hypr/looknfeel.conf
Normal file
146
core/home/config/nomarchy/default/hypr/looknfeel.conf
Normal file
@@ -0,0 +1,146 @@
|
||||
# Refer to https://wiki.hyprland.org/Configuring/Variables/
|
||||
|
||||
# Variables
|
||||
$activeBorderColor = rgba(33ccffee) rgba(00ff99ee) 45deg
|
||||
$inactiveBorderColor = rgba(595959aa)
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#general
|
||||
general {
|
||||
gaps_in = 5
|
||||
gaps_out = 10
|
||||
|
||||
border_size = 2
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#variable-types for info about colors
|
||||
col.active_border = $activeBorderColor
|
||||
col.inactive_border = $inactiveBorderColor
|
||||
|
||||
# Set to true enable resizing windows by clicking and dragging on borders and gaps
|
||||
resize_on_border = false
|
||||
|
||||
# Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on
|
||||
allow_tearing = false
|
||||
|
||||
layout = dwindle
|
||||
}
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#decoration
|
||||
decoration {
|
||||
rounding = 0
|
||||
|
||||
shadow {
|
||||
enabled = true
|
||||
range = 2
|
||||
render_power = 3
|
||||
color = rgba(1a1a1aee)
|
||||
}
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#blur
|
||||
blur {
|
||||
enabled = true
|
||||
size = 2
|
||||
passes = 2
|
||||
special = true
|
||||
brightness = 0.60
|
||||
contrast = 0.75
|
||||
}
|
||||
}
|
||||
|
||||
# https://wiki.hypr.land/Configuring/Variables/#group
|
||||
group {
|
||||
col.border_active = $activeBorderColor
|
||||
col.border_inactive = $inactiveBorderColor
|
||||
col.border_locked_active = -1
|
||||
col.border_locked_inactive = -1
|
||||
|
||||
groupbar {
|
||||
font_size = 12
|
||||
font_family = monospace
|
||||
font_weight_active = ultraheavy
|
||||
font_weight_inactive = normal
|
||||
|
||||
indicator_height = 0
|
||||
indicator_gap = 5
|
||||
height = 22
|
||||
gaps_in = 5
|
||||
gaps_out = 0
|
||||
|
||||
text_color = rgb(ffffff)
|
||||
text_color_inactive = rgba(ffffff90)
|
||||
col.active = rgba(00000040)
|
||||
col.inactive = rgba(00000020)
|
||||
|
||||
gradients = true
|
||||
gradient_rounding = 0
|
||||
gradient_round_only_edges = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#animations
|
||||
animations {
|
||||
enabled = yes
|
||||
|
||||
# Default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more
|
||||
|
||||
bezier = easeOutQuint,0.23,1,0.32,1
|
||||
bezier = easeInOutCubic,0.65,0.05,0.36,1
|
||||
bezier = linear,0,0,1,1
|
||||
bezier = almostLinear,0.5,0.5,0.75,1.0
|
||||
bezier = quick,0.15,0,0.1,1
|
||||
|
||||
animation = global, 1, 10, default
|
||||
animation = border, 1, 5.39, easeOutQuint
|
||||
animation = windows, 1, 4.79, easeOutQuint
|
||||
animation = windowsIn, 1, 4.1, easeOutQuint, popin 87%
|
||||
animation = windowsOut, 1, 1.49, linear, popin 87%
|
||||
animation = fadeIn, 1, 1.73, almostLinear
|
||||
animation = fadeOut, 1, 1.46, almostLinear
|
||||
animation = fade, 1, 3.03, quick
|
||||
animation = layers, 1, 3.81, easeOutQuint
|
||||
animation = layersIn, 1, 4, easeOutQuint, fade
|
||||
animation = layersOut, 1, 1.5, linear, fade
|
||||
animation = fadeLayersIn, 1, 1.79, almostLinear
|
||||
animation = fadeLayersOut, 1, 1.39, almostLinear
|
||||
animation = workspaces, 0, 0, ease
|
||||
animation = specialWorkspace, 1, 4, easeOutQuint, slidevert
|
||||
}
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
|
||||
dwindle {
|
||||
pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
|
||||
preserve_split = true # You probably want this
|
||||
force_split = 2 # Always split on the right
|
||||
}
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Master-Layout/ for more
|
||||
master {
|
||||
new_status = master
|
||||
}
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#misc
|
||||
misc {
|
||||
disable_hyprland_logo = true
|
||||
disable_splash_rendering = true
|
||||
disable_scale_notification = true
|
||||
focus_on_activate = true
|
||||
}
|
||||
|
||||
# https://wiki.hypr.land/Configuring/Variables/#cursor
|
||||
cursor {
|
||||
hide_on_key_press = true
|
||||
warp_on_change_workspace = 1
|
||||
}
|
||||
|
||||
# Auto toggle scratchpad on switching workspace from scratchpad
|
||||
binds {
|
||||
hide_special_on_workspace_change = true
|
||||
}
|
||||
|
||||
# Style Gum confirm to match terminal theme
|
||||
env = GUM_CONFIRM_PROMPT_FOREGROUND,6 # Cyan
|
||||
env = GUM_CONFIRM_SELECTED_FOREGROUND,0 # Black
|
||||
env = GUM_CONFIRM_SELECTED_BACKGROUND,2 # Green
|
||||
env = GUM_CONFIRM_UNSELECTED_FOREGROUND,7 # White
|
||||
env = GUM_CONFIRM_UNSELECTED_BACKGROUND,8 # Dark grey
|
||||
# Dark grey
|
||||
16
core/home/config/nomarchy/default/hypr/plain-bindings.conf
Normal file
16
core/home/config/nomarchy/default/hypr/plain-bindings.conf
Normal file
@@ -0,0 +1,16 @@
|
||||
# Application bindings
|
||||
bindd = SUPER, RETURN, Terminal, exec, uwsm-app -- xdg-terminal-exec --dir="$(nomarchy-cmd-terminal-cwd)"
|
||||
bindd = SUPER SHIFT, RETURN, Browser, exec, nomarchy-launch-browser
|
||||
bindd = SUPER SHIFT, F, File manager, exec, uwsm-app -- nautilus --new-window
|
||||
bindd = SUPER ALT SHIFT, F, File manager (cwd), exec, uwsm-app -- nautilus --new-window "$(nomarchy-cmd-terminal-cwd)"
|
||||
bindd = SUPER SHIFT, B, Browser, exec, nomarchy-launch-browser
|
||||
bindd = SUPER SHIFT ALT, B, Browser (private), exec, nomarchy-launch-browser --private
|
||||
bindd = SUPER SHIFT, N, Editor, exec, nomarchy-launch-editor
|
||||
|
||||
# Add extra bindings
|
||||
# bindd = SUPER SHIFT, A, ChatGPT, exec, nomarchy-launch-webapp "https://chatgpt.com"
|
||||
# bindd = SUPER SHIFT, R, exec, alacritty -e ssh your-server
|
||||
|
||||
# Overwrite existing bindings, like putting Nomarchy Menu on Super + Space
|
||||
# unbind = SUPER, SPACE
|
||||
# bindd = SUPER, SPACE, Nomarchy menu, exec, nomarchy-menu
|
||||
15
core/home/config/nomarchy/default/hypr/windows.conf
Normal file
15
core/home/config/nomarchy/default/hypr/windows.conf
Normal file
@@ -0,0 +1,15 @@
|
||||
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
|
||||
# Hyprland 0.53+ syntax
|
||||
#windowrulev2 = suppressevent maximize, class:.*
|
||||
|
||||
# Tag all windows for default opacity (apps can override with -default-opacity tag)
|
||||
#windowrulev2 = tag +default-opacity, class:.*
|
||||
|
||||
# Fix some dragging issues with XWayland
|
||||
#windowrulev2 = nofocus, class:^$, title:^$, xwayland:1, float:1, fullscreen:0, pin:0
|
||||
|
||||
# App-specific tweaks (may remove default-opacity tag)
|
||||
source = ~/.config/nomarchy/default/hypr/apps.conf
|
||||
|
||||
# Apply default opacity after apps have had a chance to opt out
|
||||
#windowrulev2 = opacity 0.97 0.9, tag:default-opacity
|
||||
34
core/home/config/nomarchy/default/mako/core.ini
Normal file
34
core/home/config/nomarchy/default/mako/core.ini
Normal file
@@ -0,0 +1,34 @@
|
||||
anchor=top-right
|
||||
default-timeout=5000
|
||||
width=420
|
||||
outer-margin=20
|
||||
padding=10,15
|
||||
border-size=2
|
||||
max-icon-size=32
|
||||
font=sans-serif 14px
|
||||
|
||||
[app-name=Spotify]
|
||||
invisible=1
|
||||
|
||||
[mode=do-not-disturb]
|
||||
invisible=true
|
||||
|
||||
[mode=do-not-disturb app-name=notify-send]
|
||||
invisible=false
|
||||
|
||||
[urgency=critical]
|
||||
default-timeout=0
|
||||
layer=overlay
|
||||
|
||||
[summary~="Setup Wi-Fi"]
|
||||
on-button-left=exec sh -c 'nomarchy-notification-dismiss "Setup Wi-Fi"; nomarchy-launch-wifi'
|
||||
|
||||
[summary~="Update System"]
|
||||
on-button-left=exec sh -c 'nomarchy-notification-dismiss "Update System"; nomarchy-launch-floating-terminal-with-presentation nomarchy-update'
|
||||
|
||||
[summary~="Learn Keybindings"]
|
||||
on-button-left=exec sh -c 'nomarchy-notification-dismiss "Learn Keybindings"; nomarchy-menu-keybindings'
|
||||
|
||||
[summary~="Screenshot copied & saved"]
|
||||
max-icon-size=80
|
||||
format=<b>%s</b>\n%b
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user