diff --git a/.gitignore b/.gitignore index 8779e4b..194c1b3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ result-* # Ignore automatically generated direnv output .direnv +# Local IDE settings +.claude/ # VM and ISO artifacts *.qcow2 diff --git a/assets/themes/catppuccin-latte/btop.theme b/assets/themes/catppuccin-latte/apps/btop.theme similarity index 100% rename from assets/themes/catppuccin-latte/btop.theme rename to assets/themes/catppuccin-latte/apps/btop.theme diff --git a/assets/themes/catppuccin-latte/neovim.lua b/assets/themes/catppuccin-latte/apps/neovim.lua similarity index 100% rename from assets/themes/catppuccin-latte/neovim.lua rename to assets/themes/catppuccin-latte/apps/neovim.lua diff --git a/assets/themes/catppuccin-latte/vscode.json b/assets/themes/catppuccin-latte/apps/vscode.json similarity index 100% rename from assets/themes/catppuccin-latte/vscode.json rename to assets/themes/catppuccin-latte/apps/vscode.json diff --git a/assets/themes/catppuccin/btop.theme b/assets/themes/catppuccin/apps/btop.theme similarity index 100% rename from assets/themes/catppuccin/btop.theme rename to assets/themes/catppuccin/apps/btop.theme diff --git a/assets/themes/catppuccin/neovim.lua b/assets/themes/catppuccin/apps/neovim.lua similarity index 100% rename from assets/themes/catppuccin/neovim.lua rename to assets/themes/catppuccin/apps/neovim.lua diff --git a/assets/themes/catppuccin/vscode.json b/assets/themes/catppuccin/apps/vscode.json similarity index 100% rename from assets/themes/catppuccin/vscode.json rename to assets/themes/catppuccin/apps/vscode.json diff --git a/assets/themes/catppuccin/waybar.css b/assets/themes/catppuccin/apps/waybar.css similarity index 100% rename from assets/themes/catppuccin/waybar.css rename to assets/themes/catppuccin/apps/waybar.css diff --git a/assets/themes/ethereal/btop.theme b/assets/themes/ethereal/apps/btop.theme similarity index 100% rename from assets/themes/ethereal/btop.theme rename to assets/themes/ethereal/apps/btop.theme diff --git a/assets/themes/ethereal/neovim.lua b/assets/themes/ethereal/apps/neovim.lua similarity index 100% rename from assets/themes/ethereal/neovim.lua rename to assets/themes/ethereal/apps/neovim.lua diff --git a/assets/themes/ethereal/vscode.json b/assets/themes/ethereal/apps/vscode.json similarity index 100% rename from assets/themes/ethereal/vscode.json rename to assets/themes/ethereal/apps/vscode.json diff --git a/assets/themes/everforest/btop.theme b/assets/themes/everforest/apps/btop.theme similarity index 100% rename from assets/themes/everforest/btop.theme rename to assets/themes/everforest/apps/btop.theme diff --git a/assets/themes/everforest/neovim.lua b/assets/themes/everforest/apps/neovim.lua similarity index 100% rename from assets/themes/everforest/neovim.lua rename to assets/themes/everforest/apps/neovim.lua diff --git a/assets/themes/everforest/vscode.json b/assets/themes/everforest/apps/vscode.json similarity index 100% rename from assets/themes/everforest/vscode.json rename to assets/themes/everforest/apps/vscode.json diff --git a/assets/themes/flexoki-light/btop.theme b/assets/themes/flexoki-light/apps/btop.theme similarity index 100% rename from assets/themes/flexoki-light/btop.theme rename to assets/themes/flexoki-light/apps/btop.theme diff --git a/assets/themes/flexoki-light/chromium.theme b/assets/themes/flexoki-light/apps/chromium.theme similarity index 100% rename from assets/themes/flexoki-light/chromium.theme rename to assets/themes/flexoki-light/apps/chromium.theme diff --git a/assets/themes/flexoki-light/neovim.lua b/assets/themes/flexoki-light/apps/neovim.lua similarity index 100% rename from assets/themes/flexoki-light/neovim.lua rename to assets/themes/flexoki-light/apps/neovim.lua diff --git a/assets/themes/flexoki-light/vscode.json b/assets/themes/flexoki-light/apps/vscode.json similarity index 100% rename from assets/themes/flexoki-light/vscode.json rename to assets/themes/flexoki-light/apps/vscode.json diff --git a/assets/themes/gruvbox/btop.theme b/assets/themes/gruvbox/apps/btop.theme similarity index 100% rename from assets/themes/gruvbox/btop.theme rename to assets/themes/gruvbox/apps/btop.theme diff --git a/assets/themes/gruvbox/neovim.lua b/assets/themes/gruvbox/apps/neovim.lua similarity index 100% rename from assets/themes/gruvbox/neovim.lua rename to assets/themes/gruvbox/apps/neovim.lua diff --git a/assets/themes/gruvbox/vscode.json b/assets/themes/gruvbox/apps/vscode.json similarity index 100% rename from assets/themes/gruvbox/vscode.json rename to assets/themes/gruvbox/apps/vscode.json diff --git a/assets/themes/hackerman/btop.theme b/assets/themes/hackerman/apps/btop.theme similarity index 100% rename from assets/themes/hackerman/btop.theme rename to assets/themes/hackerman/apps/btop.theme diff --git a/assets/themes/hackerman/neovim.lua b/assets/themes/hackerman/apps/neovim.lua similarity index 100% rename from assets/themes/hackerman/neovim.lua rename to assets/themes/hackerman/apps/neovim.lua diff --git a/assets/themes/hackerman/vscode.json b/assets/themes/hackerman/apps/vscode.json similarity index 100% rename from assets/themes/hackerman/vscode.json rename to assets/themes/hackerman/apps/vscode.json diff --git a/assets/themes/kanagawa/btop.theme b/assets/themes/kanagawa/apps/btop.theme similarity index 100% rename from assets/themes/kanagawa/btop.theme rename to assets/themes/kanagawa/apps/btop.theme diff --git a/assets/themes/kanagawa/hyprland.conf b/assets/themes/kanagawa/apps/hyprland.conf similarity index 100% rename from assets/themes/kanagawa/hyprland.conf rename to assets/themes/kanagawa/apps/hyprland.conf diff --git a/assets/themes/kanagawa/neovim.lua b/assets/themes/kanagawa/apps/neovim.lua similarity index 100% rename from assets/themes/kanagawa/neovim.lua rename to assets/themes/kanagawa/apps/neovim.lua diff --git a/assets/themes/kanagawa/vscode.json b/assets/themes/kanagawa/apps/vscode.json similarity index 100% rename from assets/themes/kanagawa/vscode.json rename to assets/themes/kanagawa/apps/vscode.json diff --git a/assets/themes/lumon/btop.theme b/assets/themes/lumon/apps/btop.theme similarity index 100% rename from assets/themes/lumon/btop.theme rename to assets/themes/lumon/apps/btop.theme diff --git a/assets/themes/lumon/chromium.theme b/assets/themes/lumon/apps/chromium.theme similarity index 100% rename from assets/themes/lumon/chromium.theme rename to assets/themes/lumon/apps/chromium.theme diff --git a/assets/themes/lumon/hyprland.conf b/assets/themes/lumon/apps/hyprland.conf similarity index 100% rename from assets/themes/lumon/hyprland.conf rename to assets/themes/lumon/apps/hyprland.conf diff --git a/assets/themes/lumon/neovim.lua b/assets/themes/lumon/apps/neovim.lua similarity index 100% rename from assets/themes/lumon/neovim.lua rename to assets/themes/lumon/apps/neovim.lua diff --git a/assets/themes/lumon/swayosd.css b/assets/themes/lumon/apps/swayosd.css similarity index 100% rename from assets/themes/lumon/swayosd.css rename to assets/themes/lumon/apps/swayosd.css diff --git a/assets/themes/lumon/vscode.json b/assets/themes/lumon/apps/vscode.json similarity index 100% rename from assets/themes/lumon/vscode.json rename to assets/themes/lumon/apps/vscode.json diff --git a/assets/themes/lumon/waybar.css b/assets/themes/lumon/apps/waybar.css similarity index 100% rename from assets/themes/lumon/waybar.css rename to assets/themes/lumon/apps/waybar.css diff --git a/assets/themes/matte-black/btop.theme b/assets/themes/matte-black/apps/btop.theme similarity index 100% rename from assets/themes/matte-black/btop.theme rename to assets/themes/matte-black/apps/btop.theme diff --git a/assets/themes/matte-black/neovim.lua b/assets/themes/matte-black/apps/neovim.lua similarity index 100% rename from assets/themes/matte-black/neovim.lua rename to assets/themes/matte-black/apps/neovim.lua diff --git a/assets/themes/matte-black/vscode.json b/assets/themes/matte-black/apps/vscode.json similarity index 100% rename from assets/themes/matte-black/vscode.json rename to assets/themes/matte-black/apps/vscode.json diff --git a/assets/themes/miasma/btop.theme b/assets/themes/miasma/apps/btop.theme similarity index 100% rename from assets/themes/miasma/btop.theme rename to assets/themes/miasma/apps/btop.theme diff --git a/assets/themes/miasma/neovim.lua b/assets/themes/miasma/apps/neovim.lua similarity index 100% rename from assets/themes/miasma/neovim.lua rename to assets/themes/miasma/apps/neovim.lua diff --git a/assets/themes/miasma/vscode.json b/assets/themes/miasma/apps/vscode.json similarity index 100% rename from assets/themes/miasma/vscode.json rename to assets/themes/miasma/apps/vscode.json diff --git a/assets/themes/nord/btop.theme b/assets/themes/nord/apps/btop.theme similarity index 100% rename from assets/themes/nord/btop.theme rename to assets/themes/nord/apps/btop.theme diff --git a/assets/themes/nord/hyprland.conf b/assets/themes/nord/apps/hyprland.conf similarity index 100% rename from assets/themes/nord/hyprland.conf rename to assets/themes/nord/apps/hyprland.conf diff --git a/assets/themes/nord/neovim.lua b/assets/themes/nord/apps/neovim.lua similarity index 100% rename from assets/themes/nord/neovim.lua rename to assets/themes/nord/apps/neovim.lua diff --git a/assets/themes/nord/vscode.json b/assets/themes/nord/apps/vscode.json similarity index 100% rename from assets/themes/nord/vscode.json rename to assets/themes/nord/apps/vscode.json diff --git a/assets/themes/osaka-jade/btop.theme b/assets/themes/osaka-jade/apps/btop.theme similarity index 100% rename from assets/themes/osaka-jade/btop.theme rename to assets/themes/osaka-jade/apps/btop.theme diff --git a/assets/themes/osaka-jade/neovim.lua b/assets/themes/osaka-jade/apps/neovim.lua similarity index 100% rename from assets/themes/osaka-jade/neovim.lua rename to assets/themes/osaka-jade/apps/neovim.lua diff --git a/assets/themes/osaka-jade/vscode.json b/assets/themes/osaka-jade/apps/vscode.json similarity index 100% rename from assets/themes/osaka-jade/vscode.json rename to assets/themes/osaka-jade/apps/vscode.json diff --git a/assets/themes/retro-82/btop.theme b/assets/themes/retro-82/apps/btop.theme similarity index 100% rename from assets/themes/retro-82/btop.theme rename to assets/themes/retro-82/apps/btop.theme diff --git a/assets/themes/retro-82/chromium.theme b/assets/themes/retro-82/apps/chromium.theme similarity index 100% rename from assets/themes/retro-82/chromium.theme rename to assets/themes/retro-82/apps/chromium.theme diff --git a/assets/themes/retro-82/hyprland.conf b/assets/themes/retro-82/apps/hyprland.conf similarity index 100% rename from assets/themes/retro-82/hyprland.conf rename to assets/themes/retro-82/apps/hyprland.conf diff --git a/assets/themes/retro-82/neovim.lua b/assets/themes/retro-82/apps/neovim.lua similarity index 100% rename from assets/themes/retro-82/neovim.lua rename to assets/themes/retro-82/apps/neovim.lua diff --git a/assets/themes/retro-82/swayosd.css b/assets/themes/retro-82/apps/swayosd.css similarity index 100% rename from assets/themes/retro-82/swayosd.css rename to assets/themes/retro-82/apps/swayosd.css diff --git a/assets/themes/retro-82/vscode.json b/assets/themes/retro-82/apps/vscode.json similarity index 100% rename from assets/themes/retro-82/vscode.json rename to assets/themes/retro-82/apps/vscode.json diff --git a/assets/themes/retro-82/waybar.css b/assets/themes/retro-82/apps/waybar.css similarity index 100% rename from assets/themes/retro-82/waybar.css rename to assets/themes/retro-82/apps/waybar.css diff --git a/assets/themes/ristretto/btop.theme b/assets/themes/ristretto/apps/btop.theme similarity index 100% rename from assets/themes/ristretto/btop.theme rename to assets/themes/ristretto/apps/btop.theme diff --git a/assets/themes/ristretto/neovim.lua b/assets/themes/ristretto/apps/neovim.lua similarity index 100% rename from assets/themes/ristretto/neovim.lua rename to assets/themes/ristretto/apps/neovim.lua diff --git a/assets/themes/ristretto/vscode.json b/assets/themes/ristretto/apps/vscode.json similarity index 100% rename from assets/themes/ristretto/vscode.json rename to assets/themes/ristretto/apps/vscode.json diff --git a/assets/themes/rose-pine/btop.theme b/assets/themes/rose-pine/apps/btop.theme similarity index 100% rename from assets/themes/rose-pine/btop.theme rename to assets/themes/rose-pine/apps/btop.theme diff --git a/assets/themes/rose-pine/chromium.theme b/assets/themes/rose-pine/apps/chromium.theme similarity index 100% rename from assets/themes/rose-pine/chromium.theme rename to assets/themes/rose-pine/apps/chromium.theme diff --git a/assets/themes/rose-pine/neovim.lua b/assets/themes/rose-pine/apps/neovim.lua similarity index 100% rename from assets/themes/rose-pine/neovim.lua rename to assets/themes/rose-pine/apps/neovim.lua diff --git a/assets/themes/rose-pine/vscode.json b/assets/themes/rose-pine/apps/vscode.json similarity index 100% rename from assets/themes/rose-pine/vscode.json rename to assets/themes/rose-pine/apps/vscode.json diff --git a/assets/themes/summer-night/btop.theme b/assets/themes/summer-night/apps/btop.theme similarity index 100% rename from assets/themes/summer-night/btop.theme rename to assets/themes/summer-night/apps/btop.theme diff --git a/assets/themes/summer-night/hyprland.conf b/assets/themes/summer-night/apps/hyprland.conf similarity index 100% rename from assets/themes/summer-night/hyprland.conf rename to assets/themes/summer-night/apps/hyprland.conf diff --git a/assets/themes/summer-night/neovim.lua b/assets/themes/summer-night/apps/neovim.lua similarity index 100% rename from assets/themes/summer-night/neovim.lua rename to assets/themes/summer-night/apps/neovim.lua diff --git a/assets/themes/summer-night/vscode.json b/assets/themes/summer-night/apps/vscode.json similarity index 100% rename from assets/themes/summer-night/vscode.json rename to assets/themes/summer-night/apps/vscode.json diff --git a/assets/themes/summer-night/waybar.css b/assets/themes/summer-night/apps/waybar.css similarity index 100% rename from assets/themes/summer-night/waybar.css rename to assets/themes/summer-night/apps/waybar.css diff --git a/assets/themes/summer-night/waybar.json b/assets/themes/summer-night/apps/waybar.json similarity index 100% rename from assets/themes/summer-night/waybar.json rename to assets/themes/summer-night/apps/waybar.json diff --git a/assets/themes/tokyo-night/btop.theme b/assets/themes/tokyo-night/apps/btop.theme similarity index 100% rename from assets/themes/tokyo-night/btop.theme rename to assets/themes/tokyo-night/apps/btop.theme diff --git a/assets/themes/tokyo-night/keyboard.rgb b/assets/themes/tokyo-night/apps/keyboard.rgb similarity index 100% rename from assets/themes/tokyo-night/keyboard.rgb rename to assets/themes/tokyo-night/apps/keyboard.rgb diff --git a/assets/themes/tokyo-night/neovim.lua b/assets/themes/tokyo-night/apps/neovim.lua similarity index 100% rename from assets/themes/tokyo-night/neovim.lua rename to assets/themes/tokyo-night/apps/neovim.lua diff --git a/assets/themes/tokyo-night/vscode.json b/assets/themes/tokyo-night/apps/vscode.json similarity index 100% rename from assets/themes/tokyo-night/vscode.json rename to assets/themes/tokyo-night/apps/vscode.json diff --git a/assets/themes/vantablack/btop.theme b/assets/themes/vantablack/apps/btop.theme similarity index 100% rename from assets/themes/vantablack/btop.theme rename to assets/themes/vantablack/apps/btop.theme diff --git a/assets/themes/vantablack/neovim.lua b/assets/themes/vantablack/apps/neovim.lua similarity index 100% rename from assets/themes/vantablack/neovim.lua rename to assets/themes/vantablack/apps/neovim.lua diff --git a/assets/themes/vantablack/vscode.json b/assets/themes/vantablack/apps/vscode.json similarity index 100% rename from assets/themes/vantablack/vscode.json rename to assets/themes/vantablack/apps/vscode.json diff --git a/assets/themes/white/btop.theme b/assets/themes/white/apps/btop.theme similarity index 100% rename from assets/themes/white/btop.theme rename to assets/themes/white/apps/btop.theme diff --git a/assets/themes/white/neovim.lua b/assets/themes/white/apps/neovim.lua similarity index 100% rename from assets/themes/white/neovim.lua rename to assets/themes/white/apps/neovim.lua diff --git a/assets/themes/white/vscode.json b/assets/themes/white/apps/vscode.json similarity index 100% rename from assets/themes/white/vscode.json rename to assets/themes/white/apps/vscode.json diff --git a/bin/appearance/nomarchy-font-set b/bin/appearance/nomarchy-font-set index 7dd2385..b00964d 100755 --- a/bin/appearance/nomarchy-font-set +++ b/bin/appearance/nomarchy-font-set @@ -10,7 +10,7 @@ if [[ -z $font_name ]]; then exit 1 fi -STATE_DIR="$HOME/.config/home-manager" +STATE_DIR="$HOME/.config/nomarchy" STATE_FILE="$STATE_DIR/state.json" mkdir -p "$STATE_DIR" @@ -18,19 +18,19 @@ mkdir -p "$STATE_DIR" if fc-list | grep -iq "$font_name"; then TMP_JSON=$(mktemp) - jq ".font = \"$font_name\"" "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" + jq --arg font "$font_name" '.font = $font' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" echo "Font set to $font_name declaratively. Applying changes..." env-update - + # Instant feedback for certain apps via IPC if pgrep -x kitty; then pkill -USR1 kitty fi if pgrep -x ghostty; then pkill -SIGUSR2 ghostty - notify-send -u low " You must restart Ghostty to see font change" + notify-send -u low " You must restart Ghostty to see font change" fi - + nomarchy-hook font-set "$font_name" else echo "Font '$font_name' not found." diff --git a/bin/appearance/nomarchy-theme-bg-next b/bin/appearance/nomarchy-theme-bg-next index 1ed4dc9..4f20a78 100755 --- a/bin/appearance/nomarchy-theme-bg-next +++ b/bin/appearance/nomarchy-theme-bg-next @@ -3,7 +3,7 @@ # Cycles through the background images available for the current theme. # Declarative + Hybrid (instant swww) for Nomarchy NixOS. -STATE_DIR="$HOME/.config/home-manager" +STATE_DIR="$HOME/.config/nomarchy" STATE_FILE="$STATE_DIR/state.json" mkdir -p "$STATE_DIR" [[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE" @@ -45,7 +45,7 @@ NEXT_INDEX=$(((INDEX + 1) % TOTAL)) NEW_BG="$BG_DIR/${BACKGROUNDS[$NEXT_INDEX]}" TMP_JSON=$(mktemp) -jq ".wallpaper = \"$NEW_BG\"" "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" +jq --arg wp "$NEW_BG" '.wallpaper = $wp' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" # Instant feedback via swww if pgrep -x swww-daemon >/dev/null; then diff --git a/bin/appearance/nomarchy-theme-set b/bin/appearance/nomarchy-theme-set index 2fe2b6e..bc04ad1 100755 --- a/bin/appearance/nomarchy-theme-set +++ b/bin/appearance/nomarchy-theme-set @@ -10,7 +10,7 @@ if [[ -z $THEME_NAME ]]; then exit 1 fi -STATE_DIR="$HOME/.config/home-manager" +STATE_DIR="$HOME/.config/nomarchy" STATE_FILE="$STATE_DIR/state.json" # Resolve themes directory (Built-in from Nix store via Home Manager, or user extra) @@ -25,17 +25,15 @@ mkdir -p "$STATE_DIR" if [ ! -d "$THEMES_DIR/$THEME_NAME" ] && ! [[ "$THEME_NAME" == "nord" ]]; then echo "Theme '$THEME_NAME' not found in $THEMES_DIR" - # Check if it exists in the palettes file - # (Assuming nomarchy-palettes.nix is imported in Nix) fi TMP_JSON=$(mktemp) -jq ".theme = \"$THEME_NAME\"" "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" +jq --arg theme "$THEME_NAME" '.theme = $theme' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" # Sync to system state if we have permissions (for system-level theming like browser policies) SYSTEM_STATE_FILE="/etc/nixos/state.json" if [ -w "$SYSTEM_STATE_FILE" ] || [ -w "/etc/nixos" ]; then - sudo jq ".theme = \"$THEME_NAME\"" "$SYSTEM_STATE_FILE" > /tmp/system-state.json 2>/dev/null && sudo mv /tmp/system-state.json "$SYSTEM_STATE_FILE" 2>/dev/null || true + sudo jq --arg theme "$THEME_NAME" '.theme = $theme' "$SYSTEM_STATE_FILE" > /tmp/system-state.json 2>/dev/null && sudo mv /tmp/system-state.json "$SYSTEM_STATE_FILE" 2>/dev/null || true fi # Try to find a background for this theme @@ -44,7 +42,7 @@ if [ -d "$BG_DIR" ]; then BG=$(ls "$BG_DIR" | head -n 1) if [ -n "$BG" ]; then TMP_JSON=$(mktemp) - jq ".wallpaper = \"$BG_DIR/$BG\"" "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" + jq --arg wp "$BG_DIR/$BG" '.wallpaper = $wp' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" fi fi diff --git a/bin/appearance/nomarchy-theme-set-vscode b/bin/appearance/nomarchy-theme-set-vscode index a4fb8cd..bb38b63 100755 --- a/bin/appearance/nomarchy-theme-set-vscode +++ b/bin/appearance/nomarchy-theme-set-vscode @@ -4,7 +4,7 @@ # This script only updates the global state.json. # Home Manager (modules/home/vscode.nix) handles the declarative settings injection. -STATE_DIR="$HOME/.config/home-manager" +STATE_DIR="$HOME/.config/nomarchy" STATE_FILE="$STATE_DIR/state.json" mkdir -p "$STATE_DIR" diff --git a/bin/appearance/nomarchy-toggle-nightlight b/bin/appearance/nomarchy-toggle-nightlight index 078d698..f3a1f4f 100755 --- a/bin/appearance/nomarchy-toggle-nightlight +++ b/bin/appearance/nomarchy-toggle-nightlight @@ -3,23 +3,24 @@ # Toggles the nightlight (hyprsunset). # Hybrid: updates state.json and provides instant feedback. -STATE_FILE="$HOME/.config/home-manager/state.json" -mkdir -p "$(dirname "$STATE_FILE")" +STATE_DIR="$HOME/.config/nomarchy" +STATE_FILE="$STATE_DIR/state.json" +mkdir -p "$STATE_DIR" # Initialize if doesn't exist [[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE" -if [[ $NNOMARCHY_TOGGLE_NIGHTLIGHT == "false" ]]; then +if [[ $NOMARCHY_TOGGLE_NIGHTLIGHT == "false" ]]; then NEW_VALUE="true" hyprctl dispatch exec hyprsunset --temperature 4000 - notify-send -u low "󰔎 Nightlight enabled" + notify-send -u low " Nightlight enabled" else NEW_VALUE="false" pkill hyprsunset - notify-send -u low "󰔎 Nightlight disabled" + notify-send -u low " Nightlight disabled" fi TMP_JSON=$(mktemp) -jq ".nightlight = $NEW_VALUE" "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" +jq --argjson val "$NEW_VALUE" '.nightlight = $val' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" echo "Nightlight state set to $NEW_VALUE. Environment will be fully updated on next rebuild." diff --git a/bin/hardware/nomarchy-toggle-hybrid-gpu b/bin/hardware/nomarchy-toggle-hybrid-gpu index 31cb351..53c7311 100755 --- a/bin/hardware/nomarchy-toggle-hybrid-gpu +++ b/bin/hardware/nomarchy-toggle-hybrid-gpu @@ -8,7 +8,7 @@ STATE_FILE="/etc/nixos/state.json" # Check if supergfxd is enabled in config if [[ $(sudo jq -r '.features.hybridGPU // false' "$STATE_FILE") != "true" ]]; then if gum confirm "Hybrid GPU support is not enabled. Enable it now? (Requires sys-update)"; then - sudo jq ".features.hybridGPU = true" "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE" + sudo jq '.features.hybridGPU = true' "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE" echo "Hybrid GPU support enabled in configuration. Applying changes..." sudo sys-update echo "Please run this command again after the update." diff --git a/bin/hardware/nomarchy-wifi-powersave b/bin/hardware/nomarchy-wifi-powersave index 6ecc68c..059580a 100755 --- a/bin/hardware/nomarchy-wifi-powersave +++ b/bin/hardware/nomarchy-wifi-powersave @@ -11,7 +11,7 @@ off) value="false" ;; *) echo "Usage: nomarchy-wifi-powersave "; exit 1 ;; esac -sudo jq ".wifi.powersave = $value" "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE" +sudo jq --argjson val "$value" '.wifi.powersave = $val' "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE" echo "Wifi powersave set to $1. Applying changes..." sudo sys-update diff --git a/bin/system/nomarchy-pkg-add b/bin/system/nomarchy-pkg-add index c1e0643..9273b17 100755 --- a/bin/system/nomarchy-pkg-add +++ b/bin/system/nomarchy-pkg-add @@ -14,13 +14,13 @@ if [ ! -f "$STATE_FILE" ]; then echo "[]" > "$STATE_FILE" fi -if jq -e ". | index(\"$PKG_NAME\")" "$STATE_FILE" >/dev/null; then +if jq -e --arg pkg "$PKG_NAME" '. | index($pkg)' "$STATE_FILE" >/dev/null; then echo "Package $PKG_NAME is already in your user-packages.json" exit 0 fi -# Append package to the JSON array -jq ". + [\"$PKG_NAME\"]" "$STATE_FILE" > "${STATE_FILE}.tmp" && mv "${STATE_FILE}.tmp" "$STATE_FILE" +# Append package to the JSON array safely +jq --arg pkg "$PKG_NAME" '. + [$pkg]' "$STATE_FILE" > "${STATE_FILE}.tmp" && mv "${STATE_FILE}.tmp" "$STATE_FILE" echo "Package $PKG_NAME added declaratively to $STATE_FILE." echo "Applying changes with env-update..." diff --git a/bin/system/nomarchy-pkg-remove b/bin/system/nomarchy-pkg-remove index 0909cc3..b6de996 100755 --- a/bin/system/nomarchy-pkg-remove +++ b/bin/system/nomarchy-pkg-remove @@ -14,13 +14,13 @@ if [ ! -f "$STATE_FILE" ]; then exit 0 fi -if ! jq -e ". | index(\"$PKG_NAME\")" "$STATE_FILE" >/dev/null; then +if ! jq -e --arg pkg "$PKG_NAME" '. | index($pkg)' "$STATE_FILE" >/dev/null; then echo "Package $PKG_NAME is not in your user-packages.json" exit 0 fi -# Remove package from the JSON array -jq ". - [\"$PKG_NAME\"]" "$STATE_FILE" > "${STATE_FILE}.tmp" && mv "${STATE_FILE}.tmp" "$STATE_FILE" +# Remove package from the JSON array safely +jq --arg pkg "$PKG_NAME" '. - [$pkg]' "$STATE_FILE" > "${STATE_FILE}.tmp" && mv "${STATE_FILE}.tmp" "$STATE_FILE" echo "Package $PKG_NAME removed declaratively from $STATE_FILE." echo "Applying changes with env-update..." diff --git a/bin/system/nomarchy-preflight-migration b/bin/system/nomarchy-preflight-migration index 9a9fca1..f27a5ce 100644 --- a/bin/system/nomarchy-preflight-migration +++ b/bin/system/nomarchy-preflight-migration @@ -2,22 +2,32 @@ # Nomarchy Pre-flight State Migration # Migrates legacy state files into the unified state.json before Nix evaluation -STATE_DIR="$HOME/.config/home-manager" +STATE_DIR="$HOME/.config/nomarchy" +OLD_STATE_DIR="$HOME/.config/home-manager" OLD_TOGGLES_DIR="$HOME/.local/state/nomarchy/toggles" -IDLE_STATE_FILE="$STATE_DIR/idle-state.json" -NIGHTLIGHT_STATE_FILE="$STATE_DIR/hyprsunset-state.json" -HYPRLAND_STATE_FILE="$STATE_DIR/hyprland-state.json" -THEME_STATE_FILE="$STATE_DIR/theme-state.nix" -WALLPAPER_STATE_FILE="$STATE_DIR/wallpaper-state.nix" -FONT_STATE_FILE="$STATE_DIR/font-state.nix" +IDLE_STATE_FILE="$OLD_STATE_DIR/idle-state.json" +NIGHTLIGHT_STATE_FILE="$OLD_STATE_DIR/hyprsunset-state.json" +HYPRLAND_STATE_FILE="$OLD_STATE_DIR/hyprland-state.json" +THEME_STATE_FILE="$OLD_STATE_DIR/theme-state.nix" +WALLPAPER_STATE_FILE="$OLD_STATE_DIR/wallpaper-state.nix" +FONT_STATE_FILE="$OLD_STATE_DIR/font-state.nix" +OLD_STATE_FILE="$OLD_STATE_DIR/state.json" NEW_STATE_FILE="$STATE_DIR/state.json" # We expect jq to be in PATH (it's a dependency of nomarchy-scripts) JQ="jq" -mkdir -p "$(dirname "$NEW_STATE_FILE")" +mkdir -p "$STATE_DIR" [[ ! -f $NEW_STATE_FILE ]] && echo "{}" > "$NEW_STATE_FILE" +# 0. Migrate from old home-manager state.json location +if [[ -f "$OLD_STATE_FILE" ]] && [[ "$OLD_STATE_FILE" != "$NEW_STATE_FILE" ]]; then + # Merge old state into new state + TMP_FILE=$(mktemp) + $JQ -s '.[0] * .[1]' "$OLD_STATE_FILE" "$NEW_STATE_FILE" > "$TMP_FILE" && mv "$TMP_FILE" "$NEW_STATE_FILE" + rm "$OLD_STATE_FILE" 2>/dev/null || true +fi + # 1. Migrate .local/state/nomarchy/toggles if [[ -d $OLD_TOGGLES_DIR ]]; then for file in "$OLD_TOGGLES_DIR"/*; do @@ -43,7 +53,7 @@ fi if [[ -f $IDLE_STATE_FILE ]]; then ENABLED=$($JQ '.enabled' "$IDLE_STATE_FILE") if [[ "$ENABLED" == "true" || "$ENABLED" == "false" ]]; then - $JQ ".idle = $ENABLED" "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" + $JQ --argjson val "$ENABLED" '.idle = $val' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" fi rm "$IDLE_STATE_FILE" fi @@ -51,7 +61,7 @@ fi if [[ -f $NIGHTLIGHT_STATE_FILE ]]; then ENABLED=$($JQ '.enabled' "$NIGHTLIGHT_STATE_FILE") TEMP=$($JQ '.temperature' "$NIGHTLIGHT_STATE_FILE") - $JQ ".nightlight = $ENABLED | .nightlightTemperature = $TEMP" "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" + $JQ --argjson enabled "$ENABLED" --argjson temp "$TEMP" '.nightlight = $enabled | .nightlightTemperature = $temp' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" rm "$NIGHTLIGHT_STATE_FILE" fi @@ -59,25 +69,25 @@ if [[ -f $HYPRLAND_STATE_FILE ]]; then GAPS_OUT=$($JQ '.gaps_out' "$HYPRLAND_STATE_FILE") GAPS_IN=$($JQ '.gaps_in' "$HYPRLAND_STATE_FILE") BORDER_SIZE=$($JQ '.border_size' "$HYPRLAND_STATE_FILE") - $JQ ".hyprland = {\"gaps_out\": $GAPS_OUT, \"gaps_in\": $GAPS_IN, \"border_size\": $BORDER_SIZE}" "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" + $JQ --argjson go "$GAPS_OUT" --argjson gi "$GAPS_IN" --argjson bs "$BORDER_SIZE" '.hyprland = {"gaps_out": $go, "gaps_in": $gi, "border_size": $bs}' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" rm "$HYPRLAND_STATE_FILE" fi # 3. Migrate plaintext string state files if [[ -f $THEME_STATE_FILE ]]; then THEME=$(cat "$THEME_STATE_FILE" | tr -d '\n') - $JQ ".theme = \"$THEME\"" "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" + $JQ --arg theme "$THEME" '.theme = $theme' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" rm "$THEME_STATE_FILE" fi if [[ -f $WALLPAPER_STATE_FILE ]]; then WALLPAPER=$(cat "$WALLPAPER_STATE_FILE" | tr -d '\n') - $JQ ".wallpaper = \"$WALLPAPER\"" "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" + $JQ --arg wp "$WALLPAPER" '.wallpaper = $wp' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" rm "$WALLPAPER_STATE_FILE" fi if [[ -f $FONT_STATE_FILE ]]; then FONT=$(cat "$FONT_STATE_FILE" | tr -d '\n') - $JQ ".font = \"$FONT\"" "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" + $JQ --arg font "$FONT" '.font = $font' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" rm "$FONT_STATE_FILE" fi diff --git a/bin/system/nomarchy-setup-dns b/bin/system/nomarchy-setup-dns index b2eb7ac..61809d4 100755 --- a/bin/system/nomarchy-setup-dns +++ b/bin/system/nomarchy-setup-dns @@ -13,7 +13,7 @@ fi case "$dns" in Cloudflare|Google|DHCP) - sudo jq ".dns = \"$dns\"" "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE" + sudo jq --arg dns "$dns" '.dns = $dns' "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE" ;; Custom) @@ -24,10 +24,10 @@ Custom) echo "Error: No DNS servers provided." exit 1 fi - - # Convert to JSON array + + # Convert to JSON array safely dns_array=$(echo "$dns_servers" | jq -R 'split(" ")') - sudo jq ".dns = \"Custom\" | .customDns = $dns_array" "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE" + sudo jq --arg dns "Custom" --argjson servers "$dns_array" '.dns = $dns | .customDns = $servers' "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE" ;; esac diff --git a/bin/system/nomarchy-setup-fido2 b/bin/system/nomarchy-setup-fido2 index 64c28b3..a07421b 100755 --- a/bin/system/nomarchy-setup-fido2 +++ b/bin/system/nomarchy-setup-fido2 @@ -5,13 +5,13 @@ STATE_FILE="/etc/nixos/state.json" if [[ "--remove" == $1 ]]; then - sudo jq ".features.fido2 = false" "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE" + sudo jq '.features.fido2 = false' "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE" echo "FIDO2 support disabled. Applying changes..." sudo sys-update exit 0 fi -sudo jq ".features.fido2 = true" "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE" +sudo jq '.features.fido2 = true' "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE" echo "FIDO2 support enabled. Applying changes..." sudo sys-update diff --git a/bin/system/nomarchy-setup-fingerprint b/bin/system/nomarchy-setup-fingerprint index 92d1f46..d5a2842 100755 --- a/bin/system/nomarchy-setup-fingerprint +++ b/bin/system/nomarchy-setup-fingerprint @@ -5,13 +5,13 @@ STATE_FILE="/etc/nixos/state.json" if [[ "--remove" == $1 ]]; then - sudo jq ".features.fingerprint = false" "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE" + sudo jq '.features.fingerprint = false' "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE" echo "Fingerprint support disabled. Applying changes..." sudo sys-update exit 0 fi -sudo jq ".features.fingerprint = true" "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE" +sudo jq '.features.fingerprint = true' "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE" echo "Fingerprint support enabled. Applying changes..." sudo sys-update diff --git a/bin/system/nomarchy-toggle-idle b/bin/system/nomarchy-toggle-idle index be093e3..064c716 100755 --- a/bin/system/nomarchy-toggle-idle +++ b/bin/system/nomarchy-toggle-idle @@ -3,24 +3,25 @@ # Toggles the idle daemon (hypridle) between enabled and disabled. # Hybrid: updates state.json and provides instant feedback. -STATE_FILE="$HOME/.config/home-manager/state.json" -mkdir -p "$(dirname "$STATE_FILE")" +STATE_DIR="$HOME/.config/nomarchy" +STATE_FILE="$STATE_DIR/state.json" +mkdir -p "$STATE_DIR" # Initialize if doesn't exist [[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE" -if [[ $NNOMARCHY_TOGGLE_IDLE == "false" ]]; then +if [[ $NOMARCHY_TOGGLE_IDLE == "false" ]]; then NEW_VALUE="true" setsid hypridle >/dev/null 2>&1 & - notify-send -u low "󱫖 Now locking computer when idle" + notify-send -u low " Now locking computer when idle" else NEW_VALUE="false" pkill -x hypridle - notify-send -u low "󱫖 Stop locking computer when idle" + notify-send -u low " Stop locking computer when idle" fi TMP_JSON=$(mktemp) -jq ".idle = $NEW_VALUE" "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" +jq --argjson val "$NEW_VALUE" '.idle = $val' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" echo "Idle state set to $NEW_VALUE. Environment will be fully updated on next rebuild." diff --git a/bin/system/nomarchy-toggle-suspend b/bin/system/nomarchy-toggle-suspend index 8f33c48..3d668ac 100755 --- a/bin/system/nomarchy-toggle-suspend +++ b/bin/system/nomarchy-toggle-suspend @@ -3,25 +3,25 @@ # Toggles the suspend menu option availability. # Hybrid: updates state.json and runs env-update for persistence. -STATE_FILE="$HOME/.config/home-manager/state.json" -mkdir -p "$(dirname "$STATE_FILE")" +STATE_DIR="$HOME/.config/nomarchy" +STATE_FILE="$STATE_DIR/state.json" +mkdir -p "$STATE_DIR" # Initialize if doesn't exist [[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE" # Get current state from env or state file -if [[ $NNOMARCHY_TOGGLE_SUSPEND == "false" ]]; then +if [[ $NOMARCHY_TOGGLE_SUSPEND == "false" ]]; then NEW_VALUE="true" - notify-send -u low "󰒲 Suspend now available in system menu" + notify-send -u low " Suspend now available in system menu" else NEW_VALUE="false" - notify-send -u low "󰒲 Suspend removed from system menu" + notify-send -u low " Suspend removed from system menu" fi -# Update JSON using jq -# We use a temporary file to avoid corruption if the shell is interrupted +# Update JSON using jq with --argjson for proper boolean handling TMP_JSON=$(mktemp) -jq ".suspend = $NEW_VALUE" "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" +jq --argjson val "$NEW_VALUE" '.suspend = $val' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" echo "Suspend availability set to $NEW_VALUE. Updating environment..." diff --git a/bin/system/nomarchy-tz-select b/bin/system/nomarchy-tz-select index b5ff3b6..89a33dd 100755 --- a/bin/system/nomarchy-tz-select +++ b/bin/system/nomarchy-tz-select @@ -6,7 +6,7 @@ STATE_FILE="/etc/nixos/state.json" timezone=$(timedatectl list-timezones | gum filter --height 20 --header "Set timezone") || exit 1 -sudo jq ".timezone = \"$timezone\"" "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE" +sudo jq --arg tz "$timezone" '.timezone = $tz' "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE" echo "Timezone is now set to $timezone. Applying changes..." sudo sys-update diff --git a/bin/utils/nomarchy-menu b/bin/utils/nomarchy-menu index 032f97b..550e594 100755 --- a/bin/utils/nomarchy-menu +++ b/bin/utils/nomarchy-menu @@ -247,6 +247,7 @@ show_setup_config_menu() { *Walker*) open_in_editor ~/.config/walker/config.toml && nomarchy-restart-walker ;; *Waybar*) open_in_editor ~/.config/waybar/config.jsonc && nomarchy-restart-waybar ;; *XCompose*) open_in_editor ~/.XCompose && nomarchy-restart-xcompose ;; + *Overrides*) xdg-open ~/.config/nomarchy/overrides/ ;; *) show_setup_menu ;; esac } diff --git a/bin/utils/nomarchy-migrate-state b/bin/utils/nomarchy-migrate-state new file mode 100755 index 0000000..b12fb8a --- /dev/null +++ b/bin/utils/nomarchy-migrate-state @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# Nomarchy State Migration Script +# Migrates state from old locations to unified ~/.config/nomarchy/state.json + +set -e + +NEW_STATE_DIR="$HOME/.config/nomarchy" +NEW_STATE_FILE="$NEW_STATE_DIR/state.json" +OLD_HOME_STATE="$HOME/.config/home-manager/state.json" +OLD_SYSTEM_STATE="/etc/nixos/state.json" + +mkdir -p "$NEW_STATE_DIR" + +# Initialize new state file if it doesn't exist +if [[ ! -f "$NEW_STATE_FILE" ]]; then + echo "{}" > "$NEW_STATE_FILE" +fi + +# Function to safely merge JSON +merge_json() { + local source="$1" + if [[ -f "$source" ]]; then + echo "Migrating from $source..." + TMP_FILE=$(mktemp) + # Merge source into new state (new state values take precedence if conflict) + jq -s '.[0] * .[1]' "$source" "$NEW_STATE_FILE" > "$TMP_FILE" && mv "$TMP_FILE" "$NEW_STATE_FILE" + fi +} + +# Migrate old home-manager state +if [[ -f "$OLD_HOME_STATE" ]] && [[ "$OLD_HOME_STATE" != "$NEW_STATE_FILE" ]]; then + merge_json "$OLD_HOME_STATE" + echo "Old home state migrated. You can remove: $OLD_HOME_STATE" +fi + +# Check if system state exists and user wants to sync it +if [[ -f "$OLD_SYSTEM_STATE" ]]; then + echo "" + echo "System state found at $OLD_SYSTEM_STATE" + echo "Note: System state will continue to be read from /etc/nixos/state.json" + echo " for system-level NixOS configuration." +fi + +# Run the preflight migration for any legacy formats +if command -v nomarchy-preflight-migration &> /dev/null; then + nomarchy-preflight-migration +fi + +echo "" +echo "Migration complete!" +echo "New state location: $NEW_STATE_FILE" +echo "" +echo "Current state:" +jq '.' "$NEW_STATE_FILE" diff --git a/bin/utils/nomarchy-state-write b/bin/utils/nomarchy-state-write new file mode 100755 index 0000000..2f3ba44 --- /dev/null +++ b/bin/utils/nomarchy-state-write @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +# Nomarchy Atomic State Write Helper +# Provides atomic JSON state updates with flock to prevent race conditions +# +# Usage: +# nomarchy-state-write [--type string|bool|number|json] +# nomarchy-state-write --file [--type ...] +# +# Examples: +# nomarchy-state-write theme "nord" +# nomarchy-state-write idle true --type bool +# nomarchy-state-write hyprland '{"gaps_in": 5}' --type json +# nomarchy-state-write --file /etc/nixos/state.json dns "Cloudflare" + +set -e + +# Default state file +STATE_FILE="$HOME/.config/nomarchy/state.json" +VALUE_TYPE="string" + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --file) + STATE_FILE="$2" + shift 2 + ;; + --type) + VALUE_TYPE="$2" + shift 2 + ;; + *) + if [[ -z "$KEY" ]]; then + KEY="$1" + elif [[ -z "$VALUE" ]]; then + VALUE="$1" + fi + shift + ;; + esac +done + +if [[ -z "$KEY" ]] || [[ -z "$VALUE" ]]; then + echo "Usage: nomarchy-state-write [--type string|bool|number|json]" + echo " nomarchy-state-write --file [--type ...]" + exit 1 +fi + +# Ensure directory exists +mkdir -p "$(dirname "$STATE_FILE")" + +# Initialize file if it doesn't exist +[[ ! -f "$STATE_FILE" ]] && echo "{}" > "$STATE_FILE" + +# Create lock file path +LOCK_FILE="${STATE_FILE}.lock" + +# Perform atomic update with flock +( + flock -x 200 + + TMP_FILE=$(mktemp) + trap "rm -f '$TMP_FILE'" EXIT + + case "$VALUE_TYPE" in + string) + jq --arg val "$VALUE" ".$KEY = \$val" "$STATE_FILE" > "$TMP_FILE" + ;; + bool) + if [[ "$VALUE" == "true" ]] || [[ "$VALUE" == "false" ]]; then + jq --argjson val "$VALUE" ".$KEY = \$val" "$STATE_FILE" > "$TMP_FILE" + else + echo "Error: Boolean value must be 'true' or 'false'" + exit 1 + fi + ;; + number) + jq --argjson val "$VALUE" ".$KEY = \$val" "$STATE_FILE" > "$TMP_FILE" + ;; + json) + jq --argjson val "$VALUE" ".$KEY = \$val" "$STATE_FILE" > "$TMP_FILE" + ;; + *) + echo "Error: Unknown type '$VALUE_TYPE'. Use string, bool, number, or json." + exit 1 + ;; + esac + + # Validate JSON before moving + if jq empty "$TMP_FILE" 2>/dev/null; then + mv "$TMP_FILE" "$STATE_FILE" + else + echo "Error: Failed to generate valid JSON" + exit 1 + fi + +) 200>"$LOCK_FILE" + +# Clean up lock file +rm -f "$LOCK_FILE" diff --git a/bin/wm/nomarchy-hyprland-window-gaps-toggle b/bin/wm/nomarchy-hyprland-window-gaps-toggle index d4b575d..e4819ff 100755 --- a/bin/wm/nomarchy-hyprland-window-gaps-toggle +++ b/bin/wm/nomarchy-hyprland-window-gaps-toggle @@ -2,8 +2,9 @@ # Toggles the window gaps globally between no gaps and the default 10/5/2, declaratively and instantly. -STATE_FILE="$HOME/.config/home-manager/state.json" -mkdir -p "$(dirname "$STATE_FILE")" +STATE_DIR="$HOME/.config/nomarchy" +STATE_FILE="$STATE_DIR/state.json" +mkdir -p "$STATE_DIR" if [ ! -f "$STATE_FILE" ]; then echo "{}" > "$STATE_FILE" @@ -24,6 +25,6 @@ else fi TMP_JSON=$(mktemp) -jq ".hyprland = $NEW_STATE" "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" +jq --argjson state "$NEW_STATE" '.hyprland = $state' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" echo "Toggled gaps to $NEW_STATE declaratively." diff --git a/bin/wm/nomarchy-toggle-screensaver b/bin/wm/nomarchy-toggle-screensaver index f59821c..0d34de3 100755 --- a/bin/wm/nomarchy-toggle-screensaver +++ b/bin/wm/nomarchy-toggle-screensaver @@ -3,22 +3,23 @@ # Toggles the screensaver availability. # Hybrid: updates state.json and runs env-update for persistence. -STATE_FILE="$HOME/.config/home-manager/state.json" -mkdir -p "$(dirname "$STATE_FILE")" +STATE_DIR="$HOME/.config/nomarchy" +STATE_FILE="$STATE_DIR/state.json" +mkdir -p "$STATE_DIR" # Initialize if doesn't exist [[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE" -if [[ $NNOMARCHY_TOGGLE_SCREENSAVER == "false" ]]; then +if [[ $NOMARCHY_TOGGLE_SCREENSAVER == "false" ]]; then NEW_VALUE="true" - notify-send -u low "󱄄 Screensaver enabled" + notify-send -u low " Screensaver enabled" else NEW_VALUE="false" - notify-send -u low "󱄄 Screensaver disabled" + notify-send -u low " Screensaver disabled" fi TMP_JSON=$(mktemp) -jq ".screensaver = $NEW_VALUE" "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" +jq --argjson val "$NEW_VALUE" '.screensaver = $val' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" echo "Screensaver state set to $NEW_VALUE. Updating environment..." diff --git a/bin/wm/nomarchy-toggle-waybar b/bin/wm/nomarchy-toggle-waybar index 624526d..ca095f3 100755 --- a/bin/wm/nomarchy-toggle-waybar +++ b/bin/wm/nomarchy-toggle-waybar @@ -3,23 +3,24 @@ # Toggles the waybar top bar. # Hybrid: updates state.json and provides instant feedback. -STATE_FILE="$HOME/.config/home-manager/state.json" -mkdir -p "$(dirname "$STATE_FILE")" +STATE_DIR="$HOME/.config/nomarchy" +STATE_FILE="$STATE_DIR/state.json" +mkdir -p "$STATE_DIR" # Initialize if doesn't exist [[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE" -if [[ $NNOMARCHY_TOGGLE_WAYBAR == "false" ]]; then +if [[ $NOMARCHY_TOGGLE_WAYBAR == "false" ]]; then NEW_VALUE="true" uwsm-app -- waybar >/dev/null 2>&1 & - notify-send -u low "󰍜 Top bar enabled" + notify-send -u low " Top bar enabled" else NEW_VALUE="false" pkill -x waybar - notify-send -u low "󰍜 Top bar disabled" + notify-send -u low " Top bar disabled" fi TMP_JSON=$(mktemp) -jq ".waybar = $NEW_VALUE" "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" +jq --argjson val "$NEW_VALUE" '.waybar = $val' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" echo "Waybar state set to $NEW_VALUE. Environment will be fully updated on next rebuild." diff --git a/config/hypr/nomarchy.conf b/config/hypr/nomarchy.conf index 62cb462..73b36fd 100644 --- a/config/hypr/nomarchy.conf +++ b/config/hypr/nomarchy.conf @@ -1,18 +1,42 @@ # Learn how to configure Hyprland: https://wiki.hyprland.org/Configuring/ -# Use defaults Nomarchy defaults (but don't edit these directly!) -source = ~/.config/nomarchy/default/hypr/autostart.conf +# ============================================================================ +# BEHAVIOR CONFIGS (keybindings, input, rules - non-visual) +# ============================================================================ +# These are the Nomarchy defaults - don't edit these directly! + +# Environment variables +source = ~/.config/nomarchy/default/hypr/envs.conf + +# Keybindings source = ~/.config/nomarchy/default/hypr/bindings/media.conf source = ~/.config/nomarchy/default/hypr/bindings/clipboard.conf source = ~/.config/nomarchy/default/hypr/bindings/tiling-v2.conf source = ~/.config/nomarchy/default/hypr/bindings/utilities.conf -source = ~/.config/nomarchy/default/hypr/envs.conf -source = ~/.config/nomarchy/default/hypr/looknfeel.conf + +# Input settings source = ~/.config/nomarchy/default/hypr/input.conf + +# Window rules and layout source = ~/.config/nomarchy/default/hypr/windows.conf + +# Autostart applications +source = ~/.config/nomarchy/default/hypr/autostart.conf + +# Look and feel (animations, layout behavior) +source = ~/.config/nomarchy/default/hypr/looknfeel.conf + +# ============================================================================ +# VISUAL CONFIGS (colors, theming) +# ============================================================================ +# Theme colors - loaded from the currently active theme source = ~/.config/nomarchy/current/theme/hyprland.conf -# Change your own setup in these files (and overwrite any settings from defaults!) +# ============================================================================ +# USER OVERRIDES +# ============================================================================ +# Your personal overrides - edit these files to customize Nomarchy! + source = ~/.config/hypr/monitors.conf source = ~/.config/hypr/input.conf source = ~/.config/hypr/bindings.conf diff --git a/config/nomarchy/default/hypr/colors.conf b/config/nomarchy/default/hypr/colors.conf new file mode 100644 index 0000000..e296abf --- /dev/null +++ b/config/nomarchy/default/hypr/colors.conf @@ -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 diff --git a/flake.lock b/flake.lock index 761af64..f62ddf2 100644 --- a/flake.lock +++ b/flake.lock @@ -116,11 +116,11 @@ ] }, "locked": { - "lastModified": 1768551400, - "narHash": "sha256-mlHlHW8qLcHm42J1M34HLXLW+Rw8jsLkLdjSvIYlhjw=", + "lastModified": 1775706155, + "narHash": "sha256-h7Rw0vlb0n0Jsk21WJPm7H+1T1bG+PEuxE5cJ2TZl8A=", "owner": "abenz1267", "repo": "elephant", - "rev": "f230c43ae94231c2db754f84a4f31cf76721a28a", + "rev": "376ee71c66db38683daabd57350bf3f6f086eaf8", "type": "github" }, "original": { @@ -132,11 +132,11 @@ "firefox-gnome-theme": { "flake": false, "locked": { - "lastModified": 1764873433, - "narHash": "sha256-1XPewtGMi+9wN9Ispoluxunw/RwozuTRVuuQOmxzt+A=", + "lastModified": 1775176642, + "narHash": "sha256-2veEED0Fg7Fsh81tvVDNYR6SzjqQxa7hbi18Jv4LWpM=", "owner": "rafaelmardojai", "repo": "firefox-gnome-theme", - "rev": "f7ffd917ac0d253dbd6a3bf3da06888f57c69f92", + "rev": "179704030c5286c729b5b0522037d1d51341022c", "type": "github" }, "original": { @@ -153,11 +153,11 @@ ] }, "locked": { - "lastModified": 1767609335, - "narHash": "sha256-feveD98mQpptwrAEggBQKJTYbvwwglSbOv53uCfH9PY=", + "lastModified": 1775087534, + "narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "250481aafeb741edfe23d29195671c19b36b6dca", + "rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b", "type": "github" }, "original": { @@ -185,20 +185,18 @@ "gnome-shell": { "flake": false, "locked": { - "host": "gitlab.gnome.org", "lastModified": 1767737596, "narHash": "sha256-eFujfIUQDgWnSJBablOuG+32hCai192yRdrNHTv0a+s=", "owner": "GNOME", "repo": "gnome-shell", "rev": "ef02db02bf0ff342734d525b5767814770d85b49", - "type": "gitlab" + "type": "github" }, "original": { - "host": "gitlab.gnome.org", "owner": "GNOME", - "ref": "gnome-49", "repo": "gnome-shell", - "type": "gitlab" + "rev": "ef02db02bf0ff342734d525b5767814770d85b49", + "type": "github" } }, "home-manager": { @@ -208,11 +206,11 @@ ] }, "locked": { - "lastModified": 1775077333, - "narHash": "sha256-OXcxobt7lBkh1B8AjwreU+24myhtKpqeLfAeIyNLFY8=", + "lastModified": 1775425411, + "narHash": "sha256-KY6HsebJHEe5nHOWP7ur09mb0drGxYSzE3rQxy62rJo=", "owner": "nix-community", "repo": "home-manager", - "rev": "49ca96b2714c5931e17401eff87f3edd42d2b0f2", + "rev": "0d02ec1d0a05f88ef9e74b516842900c41f0f2fe", "type": "github" }, "original": { @@ -329,11 +327,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1775002709, - "narHash": "sha256-d3Yx83vSrN+2z/loBh4mJpyRqr9aAJqlke4TkpFmRJA=", + "lastModified": 1775811116, + "narHash": "sha256-t+HZK42pB6N+i5RGbuy7Xluez/VvWbembBdvzsc23Ss=", "owner": "nixos", "repo": "nixpkgs", - "rev": "bcd464ccd2a1a7cd09aa2f8d4ffba83b761b1d0e", + "rev": "54170c54449ea4d6725efd30d719c5e505f1c10e", "type": "github" }, "original": { @@ -345,11 +343,11 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1767767207, - "narHash": "sha256-Mj3d3PfwltLmukFal5i3fFt27L6NiKXdBezC1EBuZs4=", + "lastModified": 1775036866, + "narHash": "sha256-ZojAnPuCdy657PbTq5V0Y+AHKhZAIwSIT2cb8UgAz/U=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5912c1772a44e31bf1c63c0390b90501e5026886", + "rev": "6201e203d09599479a3b3450ed24fa81537ebc4e", "type": "github" }, "original": { @@ -361,11 +359,11 @@ }, "nixpkgs_4": { "locked": { - "lastModified": 1768564909, - "narHash": "sha256-Kell/SpJYVkHWMvnhqJz/8DqQg2b6PguxVWOuadbHCc=", + "lastModified": 1775710090, + "narHash": "sha256-ar3rofg+awPB8QXDaFJhJ2jJhu+KqN/PRCXeyuXR76E=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e4bae1bd10c9c57b2cf517953ab70060a828ee6f", + "rev": "4c1018dae018162ec878d42fec712642d214fdfa", "type": "github" }, "original": { @@ -387,11 +385,11 @@ ] }, "locked": { - "lastModified": 1767810917, - "narHash": "sha256-ZKqhk772+v/bujjhla9VABwcvz+hB2IaRyeLT6CFnT0=", + "lastModified": 1775228139, + "narHash": "sha256-ebbeHmg+V7w8050bwQOuhmQHoLOEOfqKzM1KgCTexK4=", "owner": "nix-community", "repo": "NUR", - "rev": "dead29c804adc928d3a69dfe7f9f12d0eec1f1a4", + "rev": "601971b9c89e0304561977f2c28fa25e73aa7132", "type": "github" }, "original": { @@ -424,18 +422,17 @@ "nixpkgs": "nixpkgs_3", "nur": "nur", "systems": "systems", - "tinted-foot": "tinted-foot", "tinted-kitty": "tinted-kitty", "tinted-schemes": "tinted-schemes", "tinted-tmux": "tinted-tmux", "tinted-zed": "tinted-zed" }, "locked": { - "lastModified": 1774897726, - "narHash": "sha256-k/H2/oyex6GEC6uYXYetrboFQeTmX1Ouwv/zaW7b/Z0=", + "lastModified": 1775429060, + "narHash": "sha256-wbFF5cRxQOCzL/wHOKYm21t5AHPH2Lfp0mVPCOAvEoc=", "owner": "danth", "repo": "stylix", - "rev": "9b4a5eb409ceac2dd6ad495c7988e189a418cd30", + "rev": "d27951a6539951d87f75cf0a7cda8a3a24016019", "type": "github" }, "original": { @@ -474,23 +471,6 @@ "type": "github" } }, - "tinted-foot": { - "flake": false, - "locked": { - "lastModified": 1726913040, - "narHash": "sha256-+eDZPkw7efMNUf3/Pv0EmsidqdwNJ1TaOum6k7lngDQ=", - "owner": "tinted-theming", - "repo": "tinted-foot", - "rev": "fd1b924b6c45c3e4465e8a849e67ea82933fcbe4", - "type": "github" - }, - "original": { - "owner": "tinted-theming", - "repo": "tinted-foot", - "rev": "fd1b924b6c45c3e4465e8a849e67ea82933fcbe4", - "type": "github" - } - }, "tinted-kitty": { "flake": false, "locked": { @@ -510,11 +490,11 @@ "tinted-schemes": { "flake": false, "locked": { - "lastModified": 1767710407, - "narHash": "sha256-+W1EB79Jl0/gm4JqmO0Nuc5C7hRdp4vfsV/VdzI+des=", + "lastModified": 1772661346, + "narHash": "sha256-4eu3LqB9tPqe0Vaqxd4wkZiBbthLbpb7llcoE/p5HT0=", "owner": "tinted-theming", "repo": "schemes", - "rev": "2800e2b8ac90f678d7e4acebe4fa253f602e05b2", + "rev": "13b5b0c299982bb361039601e2d72587d6846294", "type": "github" }, "original": { @@ -526,11 +506,11 @@ "tinted-tmux": { "flake": false, "locked": { - "lastModified": 1767489635, - "narHash": "sha256-e6nnFnWXKBCJjCv4QG4bbcouJ6y3yeT70V9MofL32lU=", + "lastModified": 1772934010, + "narHash": "sha256-x+6+4UvaG+RBRQ6UaX+o6DjEg28u4eqhVRM9kpgJGjQ=", "owner": "tinted-theming", "repo": "tinted-tmux", - "rev": "3c32729ccae99be44fe8a125d20be06f8d7d8184", + "rev": "c3529673a5ab6e1b6830f618c45d9ce1bcdd829d", "type": "github" }, "original": { @@ -542,11 +522,11 @@ "tinted-zed": { "flake": false, "locked": { - "lastModified": 1767488740, - "narHash": "sha256-wVOj0qyil8m+ouSsVZcNjl5ZR+1GdOOAooAatQXHbuU=", + "lastModified": 1772909925, + "narHash": "sha256-jx/5+pgYR0noHa3hk2esin18VMbnPSvWPL5bBjfTIAU=", "owner": "tinted-theming", "repo": "base16-zed", - "rev": "11abb0b282ad3786a2aae088d3a01c60916f2e40", + "rev": "b4d3a1b3bcbd090937ef609a0a3b37237af974df", "type": "github" }, "original": { @@ -562,11 +542,11 @@ "systems": "systems_2" }, "locked": { - "lastModified": 1773675699, - "narHash": "sha256-GrormZ2KxchtCLuO90+5fioEQmlUCKBIil0Mzr9w0Iw=", + "lastModified": 1775815794, + "narHash": "sha256-4aYljsM+f09YLERdo+kQjg9lvxVc6yxc+rPRFdgeXxw=", "owner": "abenz1267", "repo": "walker", - "rev": "d2702235710da3d7daf55c912ca7534261cf20f5", + "rev": "a0b5fa92e05c7d52936472c5027265b14e746bae", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index c298923..3149c88 100644 --- a/flake.nix +++ b/flake.nix @@ -32,7 +32,22 @@ }; nixosConfigurations = { + # Minimal TTY installer ISO (new golden path) installerIso = nixpkgs.lib.nixosSystem { + specialArgs = { inherit inputs; }; + modules = [ + { nixpkgs.hostPlatform = "x86_64-linux"; } + "${nixpkgs}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix" + ./hosts/installer-iso.nix + { + system.stateVersion = "25.11"; + networking.hostName = "nomarchy-installer"; + } + ]; + }; + + # Graphical installer ISO (legacy, for users who prefer GUI) + installerIsoGraphical = nixpkgs.lib.nixosSystem { specialArgs = { inherit inputs; }; modules = [ { nixpkgs.hostPlatform = "x86_64-linux"; } @@ -59,6 +74,7 @@ ]; }; + # VM for testing graphical installer installerVm = nixpkgs.lib.nixosSystem { specialArgs = { inherit inputs; }; modules = [ diff --git a/hosts/installer-iso.nix b/hosts/installer-iso.nix new file mode 100644 index 0000000..defa133 --- /dev/null +++ b/hosts/installer-iso.nix @@ -0,0 +1,95 @@ +{ config, pkgs, inputs, lib, ... }: + +# Minimal TTY Installer ISO Configuration +# +# This creates a minimal, text-only installation environment. +# No desktop environment - just TTY with gum-based installer. + +{ + # Base installation media configuration is handled by the module imported in flake.nix + + # Console configuration for a pleasant TTY experience + console = { + font = "ter-v16n"; + packages = [ pkgs.terminus_font ]; + }; + + # Essential packages for installation + environment.systemPackages = with pkgs; [ + # Core utilities + git + vim + curl + wget + + # TUI installer dependencies + gum + + # Disk tools + inputs.disko.packages.${pkgs.stdenv.hostPlatform.system}.disko + parted + btrfs-progs + cryptsetup + + # Network tools + networkmanager + + # System info + lshw + pciutils + usbutils + + # Installer command + (pkgs.writeShellScriptBin "nomarchy-install" '' + exec /etc/install.sh "$@" + '') + ]; + + # Enable NetworkManager for easy network setup + networking.networkmanager.enable = true; + + # Auto-login to TTY as root for installation + services.getty.autologinUser = lib.mkForce "root"; + + # Display welcome message and installer info + environment.etc."motd".text = '' + + ╔══════════════════════════════════════════════════════════════╗ + ║ ║ + ║ NOMARCHY INSTALLER ║ + ║ ║ + ║ Run 'nomarchy-install' to start the installation wizard ║ + ║ ║ + ║ For network setup: nmtui ║ + ║ For manual install: see /etc/nomarchy/ ║ + ║ ║ + ╚══════════════════════════════════════════════════════════════╝ + + ''; + + # Make the installer script available + environment.etc."install.sh" = { + source = ../installer/install.sh; + mode = "0755"; + }; + + # Symlink for easy access (merged into systemPackages above) + # The nomarchy-install script is created by writeShellScriptBin in the main list + + # Include disko configurations + environment.etc."disko-golden.nix".source = ../installer/disko-golden.nix; + + # Include Nomarchy source for installation + environment.etc."nomarchy".source = inputs.self; + + # Disable graphical stuff - this is TTY only + services.xserver.enable = false; + services.displayManager.sddm.enable = lib.mkForce false; + + # Ensure we have a proper shell environment + programs.bash.completion.enable = true; + + # Include documentation + documentation.enable = true; + documentation.man.enable = true; +} diff --git a/hosts/live-iso.nix b/hosts/live-iso.nix index 4ffad70..be78597 100644 --- a/hosts/live-iso.nix +++ b/hosts/live-iso.nix @@ -8,7 +8,7 @@ (pkgs.makeDesktopItem { name = "install-nomarchy"; desktopName = "Install Nomarchy"; - exec = "alacritty -e sudo /etc/install-nomarchy.sh"; + exec = "alacritty -e sudo /etc/install.sh"; terminal = false; categories = [ "System" ]; }) @@ -17,17 +17,13 @@ # Ensure the live environment user has the necessary groups for graphical acceleration and audio users.users.nixos.extraGroups = [ "wheel" "video" "render" "audio" "networkmanager" ]; - environment.etc."install-nomarchy.sh" = { - source = ../installer/install-nomarchy.sh; + environment.etc."install.sh" = { + source = ../installer/install.sh; mode = "0755"; }; - environment.etc."disko-ext4.nix" = { - source = ../installer/disko-ext4.nix; - }; - - environment.etc."disko-btrfs-luks.nix" = { - source = ../installer/disko-btrfs-luks.nix; + environment.etc."disko-golden.nix" = { + source = ../installer/disko-golden.nix; }; environment.etc."nomarchy".source = inputs.self; diff --git a/install.sh b/install.sh deleted file mode 100644 index 4aace50..0000000 --- a/install.sh +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env bash -set -e - -# --- BULLETPROOF WRAPPERS FOR BAREBONES NIXOS --- -# We explicitly invoke nix run with experimental features enabled -# for every external tool so it never relies on global configurations. - -gum() { - local cmd="$1" - # Redirect TTY for interactive commands so it works even if piped via curl - case "$cmd" in - input|confirm|choose|filter|write) - nix run --quiet --extra-experimental-features "nix-command flakes" nixpkgs#gum -- "$@" < /dev/tty - ;; - *) - nix run --quiet --extra-experimental-features "nix-command flakes" nixpkgs#gum -- "$@" - ;; - esac -} - -nix_git() { - nix run --quiet --extra-experimental-features "nix-command flakes" nixpkgs#git -- "$@" -} - -clear - -# --- TITLE SCREEN --- -gum style \ - --foreground 212 --border-foreground 212 --border double \ - --align center --width 60 --margin "1 2" --padding "2 4" \ - "NOMARCHY" "The Omarchy-flavored NixOS" - -gum format "This protocol will bootstrap your **private downstream** environment." -echo "" - -# --- STEP 1: USER INPUT --- -gum style --foreground 212 "👤 User Configuration" -USERNAME=$(gum input --prompt " Username: " --placeholder "e.g., alan") -HOSTNAME=$(gum input --prompt " Hostname: " --placeholder "e.g., nomarchy-sys") - -echo "" -gum format "> **Target Directory:** \`$HOME/.nomarchy\`" -gum format "> **System User:** \`$USERNAME\`" -gum format "> **System Host:** \`$HOSTNAME\`" -echo "" - -# --- STEP 2: DIRECTORY & HARDWARE SCAFFOLDING --- -LOCAL_DIR="$HOME/.nomarchy" - -gum format "### ⚙️ Initializing Architecture" - -mkdir -p "$LOCAL_DIR"/{system,home,secrets} -gum style --foreground 120 " ✔ Created local repository structure" - -# Harvest the Hardware Configuration -sudo cp /etc/nixos/hardware-configuration.nix "$LOCAL_DIR/system/" -sudo chown "$USER:users" "$LOCAL_DIR/system/hardware-configuration.nix" -gum style --foreground 120 " ✔ Imported hardware-configuration.nix" - -# --- STEP 3: FLAKE GENERATION --- -cat < "$LOCAL_DIR/flake.nix" -{ - description = "Private Downstream Machine Config (Nomarchy)"; - - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; - home-manager = { - url = "github:nix-community/home-manager/release-25.11"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - - # The Public Upstream Core - nomarchy.url = "git+https://git.bemagri.xyz/bernardo/Nomarchy.git"; - }; - - outputs = { self, nixpkgs, home-manager, nomarchy, ... }@inputs: - let - system = "x86_64-linux"; - targetUser = "$USERNAME"; - in { - # THE SYSTEM DOMAIN - nixosConfigurations."$HOSTNAME" = nixpkgs.lib.nixosSystem { - inherit system; - specialArgs = { inputs = nomarchy.inputs // inputs; }; - modules = [ - ./system/hardware-configuration.nix - ./system/configuration.nix - nomarchy.nixosModules.system - ]; - }; - - # THE USER DOMAIN - homeConfigurations."$USERNAME" = home-manager.lib.homeManagerConfiguration { - pkgs = nixpkgs.legacyPackages."\${system}"; - extraSpecialArgs = { inputs = nomarchy.inputs // inputs; inherit targetUser; }; - modules = [ - nomarchy.nixosModules.home - ./home/home.nix - ]; - }; - }; -} -EOF - -cat < "$LOCAL_DIR/system/configuration.nix" -{ pkgs, ... }: { - networking.hostName = "$HOSTNAME"; - - # Add your private cryptography and C++ toolchains here later! - environment.systemPackages = with pkgs; [ gcc cmake gdb ]; -} -EOF - -cat < "$LOCAL_DIR/home/home.nix" -{ ... }: { - home.username = "$USERNAME"; - home.homeDirectory = "/home/$USERNAME"; - home.stateVersion = "25.11"; - - home.shellAliases = { - update-sys = "sudo nixos-rebuild switch --flake ~/.nomarchy#$HOSTNAME"; - update-home = "nix run home-manager/release-25.11 -- switch --flake ~/.nomarchy#$USERNAME"; - }; -} -EOF - -gum style --foreground 120 " ✔ Generated unified flake.nix and sub-modules" - -# --- STEP 4: VERSION CONTROL --- -gum spin --spinner dot --title " Initializing Version Control..." -- bash -c " - nix run --quiet --extra-experimental-features 'nix-command flakes' nixpkgs#git -- -C \"$LOCAL_DIR\" init > /dev/null 2>&1 - nix run --quiet --extra-experimental-features 'nix-command flakes' nixpkgs#git -- -C \"$LOCAL_DIR\" add . > /dev/null 2>&1 -" -gum style --foreground 120 " ✔ Version control active" -echo "" - -# --- STEP 5: EXECUTION GATE --- -gum style \ - --foreground 212 --border-foreground 212 --border normal \ - --align center --width 60 --margin "1 2" --padding "1 2" \ - "Ready for Genesis" - -if gum confirm "Build Nomarchy OS now?"; then - echo "" - - gum spin --spinner line --title " Compiling System Engine (Requires Sudo)..." -- \ - sudo nixos-rebuild switch --flake "$LOCAL_DIR#$HOSTNAME" --extra-experimental-features "nix-command flakes" - - gum spin --spinner line --title " Building User Interface..." -- \ - nix run --extra-experimental-features "nix-command flakes" home-manager/release-25.11 -- switch --flake "$LOCAL_DIR#$USERNAME" - - clear - gum style \ - --foreground 120 --border-foreground 120 --border double \ - --align center --width 60 --margin "1 2" --padding "2 4" \ - "Installation Complete" \ - "Welcome to Nomarchy OS." - - gum format "Reboot your machine to enter the new environment." -else - echo "" - gum style --foreground 196 "Deployment aborted." - gum format "Your configuration files are safely stored in \`$LOCAL_DIR\`." - gum format "You can inspect them and run the build manually later." -fi diff --git a/installer/disko-ext4.nix b/installer/disko-ext4.nix deleted file mode 100644 index 33ed287..0000000 --- a/installer/disko-ext4.nix +++ /dev/null @@ -1,36 +0,0 @@ -{ - disko.devices = { - disk = { - main = { - type = "disk"; - device = "@TARGET_DRIVE@"; - content = { - type = "gpt"; - partitions = { - ESP = { - priority = 1; - name = "ESP"; - start = "1M"; - end = "512M"; - type = "EF00"; - content = { - type = "filesystem"; - format = "vfat"; - mountpoint = "/boot"; - mountOptions = [ "umask=0077" ]; - }; - }; - root = { - size = "100%"; - content = { - type = "filesystem"; - format = "ext4"; - mountpoint = "/"; - }; - }; - }; - }; - }; - }; - }; -} \ No newline at end of file diff --git a/installer/disko-golden.nix b/installer/disko-golden.nix new file mode 100644 index 0000000..5bc57dd --- /dev/null +++ b/installer/disko-golden.nix @@ -0,0 +1,95 @@ +# Nomarchy Golden Path Disk Configuration +# +# BTRFS + LUKS2 encryption with subvolumes optimized for: +# - Compression (zstd) +# - SSD optimization (noatime) +# - Impermanence support (root-blank snapshot) +# - Separate subvolumes for home, nix store, logs +# +# Replace @TARGET_DRIVE@ with the target device (e.g., /dev/nvme0n1) + +{ + disko.devices = { + disk = { + main = { + type = "disk"; + device = "@TARGET_DRIVE@"; + content = { + type = "gpt"; + partitions = { + # EFI System Partition + ESP = { + priority = 1; + name = "ESP"; + start = "1M"; + end = "512M"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = [ "umask=0077" ]; + }; + }; + + # LUKS-encrypted root partition + luks = { + size = "100%"; + content = { + type = "luks"; + name = "crypted"; + # Password will be provided interactively or via stdin + settings = { + allowDiscards = true; # Enable TRIM for SSDs + }; + content = { + type = "btrfs"; + extraArgs = [ "-f" ]; # Force creation + subvolumes = { + # Root filesystem + "@" = { + mountpoint = "/"; + mountOptions = [ "compress=zstd" "noatime" ]; + }; + + # Persistent storage (for impermanence) + "@persist" = { + mountpoint = "/persist"; + mountOptions = [ "compress=zstd" "noatime" ]; + }; + + # User home directories + "@home" = { + mountpoint = "/home"; + mountOptions = [ "compress=zstd" "noatime" ]; + }; + + # Nix store (separate for better deduplication) + "@nix" = { + mountpoint = "/nix"; + mountOptions = [ "compress=zstd" "noatime" ]; + }; + + # System logs + "@log" = { + mountpoint = "/var/log"; + mountOptions = [ "compress=zstd" "noatime" ]; + }; + }; + + # Create a read-only snapshot of root for impermanence rollback + postCreateHook = '' + MNTPOINT=$(mktemp -d) + mount -t btrfs /dev/mapper/crypted $MNTPOINT + btrfs subvolume snapshot -r $MNTPOINT/@ $MNTPOINT/root-blank + umount $MNTPOINT + ''; + }; + }; + }; + }; + }; + }; + }; + }; +} diff --git a/installer/install-nomarchy.sh b/installer/install-nomarchy.sh deleted file mode 100644 index 33768b7..0000000 --- a/installer/install-nomarchy.sh +++ /dev/null @@ -1,316 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Nomarchy Installer -# Professionalized with Gum - -# Function to display stylized headers -header() { - clear - gum style \ - --foreground 212 --border-foreground 212 --border double \ - --align center --width 50 --margin "1 2" --padding "2 4" \ - "NOMARCHY" "The Omarchy-flavored NixOS" -} - -# Function to show a section header -section() { - echo - gum style --foreground 10 --bold "» $1" -} - -header - -section "Environment Check" -gum spin --spinner dot --title "Verifying internet connection..." -- sleep 1 -while ! ping -c 1 8.8.8.8 &> /dev/null; do - gum style --foreground 9 "Error: No internet connection detected." - CHOICE=$(gum choose "Open Network Manager (nmtui)" "Retry Connection" "Exit Installer") - if [ "$CHOICE" = "Open Network Manager (nmtui)" ]; then - nmtui - elif [ "$CHOICE" = "Exit Installer" ]; then - echo "Exiting installer." - exit 1 - fi -done -echo "✓ Internet connection verified." - -# 0. Environment Setup -NOMARCHY_REPO="" -if [ -d "/etc/nomarchy" ]; then - NOMARCHY_REPO="/etc/nomarchy" -elif [ -d "$(dirname "$0")/.." ] && [ -f "$(dirname "$0")/../flake.nix" ]; then - NOMARCHY_REPO="$(realpath "$(dirname "$0")/..")" -fi - -if [ -z "$NOMARCHY_REPO" ]; then - gum style --foreground 9 "Error: Nomarchy repository not found." - exit 1 -fi - -# 1. Hardware Detection -section "Hardware Detection" -PRODUCT_NAME=$(cat /sys/class/dmi/id/product_name 2>/dev/null || echo "Unknown") -CPU_VENDOR=$(lscpu | grep "Vendor ID" | awk '{print $3}') -echo "Detected Device: $PRODUCT_NAME" -echo "Detected CPU: $CPU_VENDOR" - -HARDWARE_MODULES="" -NOMARCHY_HW_OPTS="" - -if [[ "$CPU_VENDOR" == "AuthenticAMD" ]]; then - HARDWARE_MODULES="inputs.nixos-hardware.nixosModules.common-cpu-amd" -elif [[ "$CPU_VENDOR" == "GenuineIntel" ]]; then - HARDWARE_MODULES="inputs.nixos-hardware.nixosModules.common-cpu-intel" -fi - -echo "Select your hardware vendor for optimized profiles:" -VENDOR=$(gum choose --header "Hardware Vendor" \ - "acer" "apple" "asus" "dell" "framework" "google" "hp" "lenovo" "microsoft" "msi" "purism" "samsung" "starlabs" "system76" "toshiba" "None/Generic") - -if [ "$VENDOR" != "None/Generic" ]; then - case $VENDOR in - dell) - MODEL=$(gum choose "xps-13-9300" "xps-13-9310" "xps-13-9343" "xps-13-9360" "xps-13-9370" "xps-13-9380" "xps-15-7590" "xps-15-9500" "xps-15-9510" "xps-15-9550" "precision-5510" "precision-5530") - HARDWARE_MODULES="$HARDWARE_MODULES\n inputs.nixos-hardware.nixosModules.dell-$MODEL" - [[ "$MODEL" == *"xps"* ]] && NOMARCHY_HW_OPTS="nomarchy.hardware.isXPS = true;" - ;; - lenovo) - MODEL=$(gum choose "thinkpad-x1-carbon-gen7" "thinkpad-x1-carbon-gen8" "thinkpad-x1-carbon-gen9" "thinkpad-x1-carbon-gen10" "thinkpad-x1-extreme" "thinkpad-t480" "thinkpad-t490" "thinkpad-t14-amd" "thinkpad-t14-intel" "thinkpad-p1-gen3") - HARDWARE_MODULES="$HARDWARE_MODULES\n inputs.nixos-hardware.nixosModules.lenovo-$MODEL" - ;; - framework) - MODEL=$(gum choose "13-7040-amd" "13-intel-12th-gen" "13-intel-13th-gen" "16-7040-amd") - HARDWARE_MODULES="$HARDWARE_MODULES\n inputs.nixos-hardware.nixosModules.framework-$MODEL" - NOMARCHY_HW_OPTS="nomarchy.hardware.isFramework = true;" - ;; - microsoft) - MODEL=$(gum choose "surface-pro-7" "surface-pro-8" "surface-pro-9" "surface-laptop-3" "surface-laptop-4") - HARDWARE_MODULES="$HARDWARE_MODULES\n inputs.nixos-hardware.nixosModules.microsoft-$MODEL" - ;; - apple) - MODEL=$(gum choose "macbook-pro-12-1" "macbook-air-6-2" "t2") - if [ "$MODEL" == "t2" ]; then - NOMARCHY_HW_OPTS="nomarchy.hardware.isT2Mac = true;" - else - HARDWARE_MODULES="$HARDWARE_MODULES\n inputs.nixos-hardware.nixosModules.apple-$MODEL" - fi - ;; - *) - INPUT_MOD=$(gum input --placeholder "Enter nixos-hardware module path") - HARDWARE_MODULES="$HARDWARE_MODULES\n $INPUT_MOD" - ;; - esac -fi - -# 2. Storage Setup -section "Storage & Partitioning" -DRIVES=$(lsblk -d -n -p -o NAME,SIZE | grep -v loop) -TARGET_DRIVE=$(echo "$DRIVES" | gum choose --header "Select target drive" | awk '{print $1}') -if [ -z "$TARGET_DRIVE" ]; then exit 1; fi - -LAYOUT=$(gum choose "Standard Ext4" "Encrypted BTRFS (LUKS2)") -IMPERMANENCE="No" -if [ "$LAYOUT" = "Standard Ext4" ]; then - DISKO_FILE="installer/disko-ext4.nix" -else - DISKO_FILE="installer/disko-btrfs-luks.nix" - echo "Enable Impermanence? (Erase root on every boot)" - IMPERMANENCE=$(gum choose "No" "Yes") -fi - -# 3. User & Localization -section "User & Localization" -USERNAME=$(gum input --placeholder "Enter target username (no spaces)") -if [ -z "$USERNAME" ]; then exit 1; fi - -USER_PASSWORD=$(gum input --password --placeholder "Enter password for $USERNAME") -USER_PASSWORD_CONFIRM=$(gum input --password --placeholder "Confirm password") - -if [ "$USER_PASSWORD" != "$USER_PASSWORD_CONFIRM" ]; then - gum style --foreground 9 "Error: Passwords do not match." - exit 1 -fi - -echo "Select Timezone:" -TIMEZONES=$(timedatectl list-timezones || echo "UTC") -TIMEZONE=$(echo "$TIMEZONES" | gum filter --placeholder "Type to search...") -[ -z "$TIMEZONE" ] && TIMEZONE="UTC" - -echo "Select Keyboard Layout:" -KEYBOARD_LAYOUT=$(gum choose "us" "uk" "de" "fr" "es" "it" "br-abnt2" "latam" "Other...") -if [ "$KEYBOARD_LAYOUT" = "Other..." ]; then - KEYBOARD_LAYOUT=$(gum input --placeholder "Enter layout (e.g. jp)") -fi - -# 4. Software Profiles -section "Software Profiles" -echo "Select additional profiles (Space to toggle):" -PROFILES=$(gum choose --no-limit "Development (VSCode, Docker, DBs)" "Gaming (Steam, Lutris)" "Media Production (OBS, Kdenlive)") - -PROFILE_HOME_PKGS="" -PROFILE_SYSTEM_CONFIG="" - -[[ $PROFILES == *"Development"* ]] && PROFILE_HOME_PKGS="$PROFILE_HOME_PKGS\n vscode\n docker-compose\n dbeaver-bin" && PROFILE_SYSTEM_CONFIG="$PROFILE_SYSTEM_CONFIG\n virtualisation.docker.enable = true;" -[[ $PROFILES == *"Gaming"* ]] && PROFILE_HOME_PKGS="$PROFILE_HOME_PKGS\n lutris\n protonup-qt\n mangohud" && PROFILE_SYSTEM_CONFIG="$PROFILE_SYSTEM_CONFIG\n programs.steam.enable = true;" -[[ $PROFILES == *"Media Production"* ]] && PROFILE_HOME_PKGS="$PROFILE_HOME_PKGS\n obs-studio\n kdenlive\n gimp\n audacity" -[[ "$IMPERMANENCE" == "Yes" ]] && PROFILE_SYSTEM_CONFIG="$PROFILE_SYSTEM_CONFIG\n nomarchy.system.impermanence.enable = true;" - -# 5. Review -section "Review Configuration" -echo "Target Drive: $TARGET_DRIVE" -echo "Layout: $LAYOUT (Impermanence: $IMPERMANENCE)" -echo "User: $USERNAME" -echo "Timezone: $TIMEZONE" -echo "Profiles: $PROFILES" - -if ! gum confirm "Ready to begin installation?"; then - echo "Aborted." - exit 0 -fi - -# 6. Execution -section "Executing Installation" - -# 6.1. Partitioning -gum spin --spinner pulse --title "Partitioning $TARGET_DRIVE..." -- bash -c " -DISKO_NIX=\"$NOMARCHY_REPO/$DISKO_FILE\" -TMP_DISKO=\$(mktemp --suffix=.nix) -sed \"s|@TARGET_DRIVE@|$TARGET_DRIVE|g\" \"\$DISKO_NIX\" > \"\$TMP_DISKO\" -sudo env PATH=\"\$PATH\" disko --mode disko \"\$TMP_DISKO\" -" - -# 6.2. Scaffold Config -gum spin --spinner pulse --title "Generating NixOS configuration..." -- bash -c " -mkdir -p /mnt/etc/nixos/ -cat < /mnt/etc/nixos/login-preference.nix -{ - services.displayManager.autoLogin.enable = true; - services.displayManager.autoLogin.user = \"$USERNAME\"; -} -EOF - -cat < /mnt/etc/nixos/localization.nix -{ - time.timeZone = \"$TIMEZONE\"; - services.xserver.xkb.layout = \"$KEYBOARD_LAYOUT\"; - console.keyMap = \"$KEYBOARD_LAYOUT\"; -} -EOF - -cat < /mnt/etc/nixos/hardware-selection.nix -{ inputs, ... }: -{ - imports = [ - \$(echo -e \"$HARDWARE_MODULES\") - ]; - $NOMARCHY_HW_OPTS -} -EOF - -nixos-generate-config --root /mnt - -cat < /mnt/etc/nixos/system.nix -{ pkgs, ... }: -{ - \$(echo -e \"$PROFILE_SYSTEM_CONFIG\") - - users.users.\"$USERNAME\" = { - isNormalUser = true; - initialPassword = \"$USER_PASSWORD\"; - extraGroups = [ \"networkmanager\" \"wheel\" \"video\" \"audio\" ]; - }; - - system.stateVersion = \"25.11\"; - - environment.systemPackages = with pkgs; []; -} -EOF - -cat < /mnt/etc/nixos/home.nix -{ pkgs, ... }: -{ - home.packages = with pkgs; [ - \$(echo -e \"$PROFILE_HOME_PKGS\") - ]; -} -EOF - -cat < /mnt/etc/nixos/flake.nix -{ - description = \"My Nomarchy - Downstream Configuration\"; - inputs = { - nixpkgs.url = \"github:nixos/nixpkgs/nixos-25.11\"; - nomarchy.url = \"git+https://git.bemagri.xyz/bernardo/Nomarchy.git\"; - nixos-hardware.url = \"github:NixOS/nixos-hardware/master\"; - home-manager = { - url = \"github:nix-community/home-manager/release-25.11\"; - inputs.nixpkgs.follows = \"nixpkgs\"; - }; - }; - outputs = { self, nixpkgs, nomarchy, home-manager, ... }@inputs: { - nixosConfigurations.default = nixpkgs.lib.nixosSystem { - specialArgs = { inputs = nomarchy.inputs // inputs; }; - modules = [ - { nixpkgs.hostPlatform = \"x86_64-linux\"; } - ./hardware-configuration.nix - ./hardware-selection.nix - ./login-preference.nix - ./localization.nix - nomarchy.nixosModules.system - ./system.nix - home-manager.nixosModules.home-manager - { - home-manager.useGlobalPkgs = true; - home-manager.useUserPackages = true; - home-manager.extraSpecialArgs = { inherit inputs; }; - home-manager.users.$USERNAME = { - imports = [ nomarchy.nixosModules.home ./home.nix ]; - home.username = \"$USERNAME\"; - home.homeDirectory = \"/home/$USERNAME\"; - home.stateVersion = \"25.11\"; - }; - } - ]; - }; - }; -} -EOF -" - -# 6.3. Git Init -cd /mnt/etc/nixos/ -git init -git add . -git config user.name "Nomarchy Installer" -git config user.email "installer@nomarchy" -git commit -m "Initial Nomarchy generation" - -# 6.4. Impermanence logic -if [[ "$IMPERMANENCE" == "Yes" ]]; then - echo "Redirecting to persistent storage..." - mkdir -p /mnt/persist/etc - mv /mnt/etc/nixos /mnt/persist/etc/ - mkdir -p /mnt/etc - ln -s /persist/etc/nixos /mnt/etc/nixos -fi - -# 6.5. Install -section "Installing System" -echo "Starting nixos-install. This will take some time..." -sudo nixos-install --flake /mnt/etc/nixos#default --no-root-passwd - -# 7. Finished -header -gum style --foreground 10 --bold "INSTALLATION COMPLETE!" -echo "Nomarchy has been successfully installed on $TARGET_DRIVE." -echo -echo "What's next?" -echo "1. Remove the installation media." -echo "2. Reboot your computer." -echo "3. Log in and run 'nomarchy-update' to finish setup." -echo - -if gum confirm "Reboot now?"; then - reboot -fi diff --git a/installer/install.sh b/installer/install.sh new file mode 100755 index 0000000..feafa1e --- /dev/null +++ b/installer/install.sh @@ -0,0 +1,523 @@ +#!/usr/bin/env bash +set -e + +# Nomarchy TTY Installer +# Golden path: BTRFS + LUKS2 encryption +# +# This is a minimal, single-path installer designed for TTY-only environments. +# For a customized installation, manually set up your disk and use the generated +# flake configuration as a starting point. + +# Colors and styling +RED='\033[0;31m' +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +NC='\033[0m' # No Color +BOLD='\033[1m' + +# Installer state +NOMARCHY_REPO="" +TARGET_DRIVE="" +USERNAME="" +USER_PASSWORD="" +TIMEZONE="UTC" +HARDWARE_MODULES="" +NOMARCHY_HW_OPTS="" +ENABLE_IMPERMANENCE="false" + +# ============================================================================ +# UTILITY FUNCTIONS +# ============================================================================ + +header() { + clear + gum style \ + --foreground 212 --border-foreground 212 --border double \ + --align center --width 60 --margin "1 2" --padding "2 4" \ + "NOMARCHY INSTALLER" "NixOS with Omarchy flavor" + echo "" +} + +section() { + echo "" + gum style --foreground 14 --bold "━━━ $1 ━━━" + echo "" +} + +success() { + gum style --foreground 10 "✓ $1" +} + +error() { + gum style --foreground 9 "✗ $1" +} + +info() { + gum style --foreground 12 "→ $1" +} + +# ============================================================================ +# STEP 1: ENVIRONMENT CHECK +# ============================================================================ + +check_environment() { + section "Environment Check" + + # Check for root + if [[ $EUID -ne 0 ]]; then + error "This installer must be run as root (use sudo)" + exit 1 + fi + success "Running as root" + + # Find Nomarchy repo + if [[ -d "/etc/nomarchy" ]]; then + NOMARCHY_REPO="/etc/nomarchy" + elif [[ -d "$(dirname "$0")/.." ]] && [[ -f "$(dirname "$0")/../flake.nix" ]]; then + NOMARCHY_REPO="$(realpath "$(dirname "$0")/..")" + fi + + if [[ -z "$NOMARCHY_REPO" ]]; then + error "Nomarchy repository not found" + exit 1 + fi + success "Found Nomarchy at $NOMARCHY_REPO" + + # Check internet + gum spin --spinner dot --title "Checking internet connection..." -- sleep 1 + while ! ping -c 1 -W 2 1.1.1.1 &>/dev/null; do + error "No internet connection" + local choice + choice=$(gum choose "Open Network Manager (nmtui)" "Retry" "Exit") + case "$choice" in + *nmtui*) nmtui ;; + *Exit*) exit 1 ;; + esac + done + success "Internet connection verified" +} + +# ============================================================================ +# STEP 2: DISK SELECTION +# ============================================================================ + +select_disk() { + section "Disk Selection" + + info "Available drives:" + echo "" + lsblk -d -n -p -o NAME,SIZE,MODEL | grep -v loop + echo "" + + local drives + drives=$(lsblk -d -n -p -o NAME,SIZE | grep -v loop) + TARGET_DRIVE=$(echo "$drives" | gum choose --header "Select target drive" | awk '{print $1}') + + if [[ -z "$TARGET_DRIVE" ]]; then + error "No drive selected" + exit 1 + fi + + echo "" + gum style --foreground 9 --bold "⚠ WARNING: All data on $TARGET_DRIVE will be DESTROYED!" + echo "" + + if ! gum confirm "Are you sure you want to use $TARGET_DRIVE?"; then + error "Aborted" + exit 1 + fi + + success "Selected: $TARGET_DRIVE" +} + +# ============================================================================ +# STEP 3: LUKS PASSPHRASE +# ============================================================================ + +get_luks_passphrase() { + section "Disk Encryption" + + info "Your disk will be encrypted with LUKS2." + info "Enter a strong passphrase (you'll need this at every boot)." + echo "" + + local pass1 pass2 + while true; do + pass1=$(gum input --password --placeholder "Enter LUKS passphrase") + [[ -z "$pass1" ]] && continue + + pass2=$(gum input --password --placeholder "Confirm passphrase") + + if [[ "$pass1" == "$pass2" ]]; then + USER_PASSWORD="$pass1" + break + else + error "Passphrases do not match. Try again." + fi + done + + success "Encryption passphrase set" +} + +# ============================================================================ +# STEP 4: USER CONFIGURATION +# ============================================================================ + +configure_user() { + section "User Configuration" + + USERNAME=$(gum input --placeholder "Enter username (lowercase, no spaces)") + if [[ -z "$USERNAME" ]] || [[ ! "$USERNAME" =~ ^[a-z][a-z0-9_-]*$ ]]; then + error "Invalid username" + exit 1 + fi + + success "Username: $USERNAME" + + # User password (can be same as LUKS or different) + info "Set a password for your user account" + local pass1 pass2 + while true; do + pass1=$(gum input --password --placeholder "Enter user password") + [[ -z "$pass1" ]] && continue + + pass2=$(gum input --password --placeholder "Confirm user password") + + if [[ "$pass1" == "$pass2" ]]; then + USER_PASSWORD="$pass1" + break + else + error "Passwords do not match. Try again." + fi + done + + success "User password set" +} + +# ============================================================================ +# STEP 5: TIMEZONE +# ============================================================================ + +select_timezone() { + section "Timezone" + + local timezones + timezones=$(timedatectl list-timezones 2>/dev/null || echo "UTC") + TIMEZONE=$(echo "$timezones" | gum filter --placeholder "Search timezone...") + + [[ -z "$TIMEZONE" ]] && TIMEZONE="UTC" + success "Timezone: $TIMEZONE" +} + +# ============================================================================ +# STEP 6: HARDWARE VENDOR +# ============================================================================ + +select_hardware() { + section "Hardware Configuration" + + local product_name cpu_vendor + product_name=$(cat /sys/class/dmi/id/product_name 2>/dev/null || echo "Unknown") + cpu_vendor=$(lscpu 2>/dev/null | grep "Vendor ID" | awk '{print $3}' || echo "Unknown") + + info "Detected: $product_name" + info "CPU: $cpu_vendor" + echo "" + + # Set CPU-specific module + if [[ "$cpu_vendor" == "AuthenticAMD" ]]; then + HARDWARE_MODULES="inputs.nixos-hardware.nixosModules.common-cpu-amd" + elif [[ "$cpu_vendor" == "GenuineIntel" ]]; then + HARDWARE_MODULES="inputs.nixos-hardware.nixosModules.common-cpu-intel" + fi + + local vendor + vendor=$(gum choose --header "Select hardware vendor" \ + "Generic (Auto-detect)" \ + "Framework" \ + "Dell" \ + "Lenovo" \ + "Apple (T2 Mac)" \ + "Microsoft Surface" \ + "Other...") + + case "$vendor" in + *Framework*) + local model + model=$(gum choose "16-7040-amd" "13-7040-amd" "13-intel-13th-gen" "13-intel-12th-gen") + HARDWARE_MODULES="$HARDWARE_MODULES\n inputs.nixos-hardware.nixosModules.framework-$model" + NOMARCHY_HW_OPTS="nomarchy.hardware.isFramework = true;" + ;; + *Dell*) + local model + model=$(gum choose "xps-15-9500" "xps-15-9510" "xps-13-9310" "xps-13-9380" "precision-5530") + HARDWARE_MODULES="$HARDWARE_MODULES\n inputs.nixos-hardware.nixosModules.dell-$model" + [[ "$model" == *"xps"* ]] && NOMARCHY_HW_OPTS="nomarchy.hardware.isXPS = true;" + ;; + *Lenovo*) + local model + model=$(gum choose "thinkpad-x1-carbon-gen10" "thinkpad-t14-amd" "thinkpad-t480" "thinkpad-x1-extreme") + HARDWARE_MODULES="$HARDWARE_MODULES\n inputs.nixos-hardware.nixosModules.lenovo-$model" + ;; + *Apple*) + NOMARCHY_HW_OPTS="nomarchy.hardware.isT2Mac = true;" + ;; + *Surface*) + local model + model=$(gum choose "surface-pro-9" "surface-pro-8" "surface-laptop-4") + HARDWARE_MODULES="$HARDWARE_MODULES\n inputs.nixos-hardware.nixosModules.microsoft-$model" + ;; + *Other*) + info "Enter nixos-hardware module path (or leave empty):" + local custom_mod + custom_mod=$(gum input --placeholder "e.g., inputs.nixos-hardware.nixosModules.asus-zephyrus-ga401") + [[ -n "$custom_mod" ]] && HARDWARE_MODULES="$HARDWARE_MODULES\n $custom_mod" + ;; + esac + + success "Hardware configuration set" +} + +# ============================================================================ +# STEP 7: IMPERMANENCE (OPTIONAL) +# ============================================================================ + +configure_impermanence() { + section "Impermanence (Optional)" + + info "Impermanence erases your root filesystem on every boot." + info "Only explicitly persisted files survive reboots." + info "This provides a clean, reproducible system." + echo "" + + if gum confirm "Enable Impermanence?"; then + ENABLE_IMPERMANENCE="true" + success "Impermanence enabled" + else + info "Impermanence disabled (traditional persistent root)" + fi +} + +# ============================================================================ +# STEP 8: REVIEW & CONFIRM +# ============================================================================ + +review_configuration() { + section "Review Configuration" + + echo " Drive: $TARGET_DRIVE (BTRFS + LUKS2)" + echo " Username: $USERNAME" + echo " Timezone: $TIMEZONE" + echo " Impermanence: $ENABLE_IMPERMANENCE" + echo "" + + gum style --foreground 9 "This will DESTROY all data on $TARGET_DRIVE" + echo "" + + if ! gum confirm "Proceed with installation?"; then + error "Aborted" + exit 1 + fi +} + +# ============================================================================ +# STEP 9: EXECUTION +# ============================================================================ + +execute_installation() { + section "Installing Nomarchy" + + # 9.1 Partition with disko + info "Partitioning disk..." + local escaped_drive disko_file tmp_disko + escaped_drive=$(printf '%s\n' "$TARGET_DRIVE" | sed 's/[[\.*^$()+?{|]/\\&/g') + disko_file="$NOMARCHY_REPO/installer/disko-golden.nix" + tmp_disko=$(mktemp --suffix=.nix) + + sed "s|@TARGET_DRIVE@|$escaped_drive|g" "$disko_file" > "$tmp_disko" + + # For LUKS, we need to provide the passphrase + echo -n "$USER_PASSWORD" | disko --mode disko "$tmp_disko" + success "Disk partitioned" + + # 9.2 Generate hardware config + info "Generating hardware configuration..." + mkdir -p /mnt/etc/nixos + nixos-generate-config --root /mnt + success "Hardware configuration generated" + + # 9.3 Generate flake configuration + info "Creating system configuration..." + generate_flake_config + success "Configuration generated" + + # 9.4 Initialize git repo + info "Initializing git repository..." + ( + cd /mnt/etc/nixos + git init -q + git add . + git config user.name "Nomarchy Installer" + git config user.email "installer@nomarchy" + git commit -qm "Initial Nomarchy configuration" + ) + success "Git repository initialized" + + # 9.5 Handle impermanence + if [[ "$ENABLE_IMPERMANENCE" == "true" ]]; then + info "Setting up impermanence..." + mkdir -p /mnt/persist/etc + mv /mnt/etc/nixos /mnt/persist/etc/ + mkdir -p /mnt/etc + ln -s /persist/etc/nixos /mnt/etc/nixos + success "Impermanence configured" + fi + + # 9.6 Install + info "Running nixos-install (this will take a while)..." + nixos-install --flake /mnt/etc/nixos#default --no-root-passwd + success "Installation complete!" +} + +# ============================================================================ +# GENERATE FLAKE CONFIGURATION +# ============================================================================ + +generate_flake_config() { + local impermanence_opt="" + [[ "$ENABLE_IMPERMANENCE" == "true" ]] && impermanence_opt="nomarchy.system.impermanence.enable = true;" + + # flake.nix + cat > /mnt/etc/nixos/flake.nix << 'FLAKE_EOF' +{ + description = "My Nomarchy Configuration"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; + nomarchy.url = "github:bemagri/nomarchy"; + nixos-hardware.url = "github:NixOS/nixos-hardware/master"; + home-manager = { + url = "github:nix-community/home-manager/release-25.11"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, nixpkgs, nomarchy, home-manager, nixos-hardware, ... }@inputs: { + nixosConfigurations.default = nixpkgs.lib.nixosSystem { + specialArgs = { inputs = nomarchy.inputs // inputs; }; + modules = [ + { nixpkgs.hostPlatform = "x86_64-linux"; } + ./hardware-configuration.nix + ./hardware-selection.nix + nomarchy.nixosModules.system + ./system.nix + home-manager.nixosModules.home-manager + { + home-manager.useGlobalPkgs = true; + home-manager.useUserPackages = true; + home-manager.extraSpecialArgs = { inherit inputs; }; + home-manager.users.@USERNAME@ = { + imports = [ nomarchy.nixosModules.home ./home.nix ]; + home.username = "@USERNAME@"; + home.homeDirectory = "/home/@USERNAME@"; + home.stateVersion = "25.11"; + }; + } + ]; + }; + }; +} +FLAKE_EOF + sed -i "s/@USERNAME@/$USERNAME/g" /mnt/etc/nixos/flake.nix + + # hardware-selection.nix + cat > /mnt/etc/nixos/hardware-selection.nix << EOF +{ inputs, ... }: +{ + imports = [ + $(echo -e "$HARDWARE_MODULES") + ]; + $NOMARCHY_HW_OPTS +} +EOF + + # system.nix + cat > /mnt/etc/nixos/system.nix << EOF +{ pkgs, ... }: +{ + time.timeZone = "$TIMEZONE"; + + $impermanence_opt + + services.displayManager.autoLogin.enable = true; + services.displayManager.autoLogin.user = "$USERNAME"; + + users.users."$USERNAME" = { + isNormalUser = true; + initialPassword = "$USER_PASSWORD"; + extraGroups = [ "networkmanager" "wheel" "video" "audio" "render" ]; + }; + + system.stateVersion = "25.11"; +} +EOF + + # home.nix (empty for user customization) + cat > /mnt/etc/nixos/home.nix << 'EOF' +{ pkgs, ... }: +{ + # Add your personal packages here + home.packages = with pkgs; [ + # example: firefox thunderbird libreoffice + ]; + + # Add your personal home-manager configuration here +} +EOF +} + +# ============================================================================ +# FINISH +# ============================================================================ + +finish() { + header + + gum style --foreground 10 --bold --align center "INSTALLATION COMPLETE!" + echo "" + echo "Nomarchy has been successfully installed." + echo "" + echo "Next steps:" + echo " 1. Remove the installation media" + echo " 2. Reboot your computer" + echo " 3. Log in with username: $USERNAME" + echo " 4. Your configuration is at /etc/nixos/" + echo "" + + if gum confirm "Reboot now?"; then + reboot + fi +} + +# ============================================================================ +# MAIN +# ============================================================================ + +main() { + header + + check_environment + select_disk + get_luks_passphrase + configure_user + select_timezone + select_hardware + configure_impermanence + review_configuration + execute_installation + finish +} + +main "$@" diff --git a/modules/home/alacritty.nix b/modules/home/alacritty.nix index 68c7fa3..6a1e678 100644 --- a/modules/home/alacritty.nix +++ b/modules/home/alacritty.nix @@ -2,8 +2,8 @@ { programs.alacritty = { - enable = true; - settings = { + enable = lib.mkDefault true; + settings = lib.mkDefault { env = { TERM = "xterm-256color"; }; diff --git a/modules/home/bash.nix b/modules/home/bash.nix index 4e49586..7005f0c 100644 --- a/modules/home/bash.nix +++ b/modules/home/bash.nix @@ -3,19 +3,17 @@ { programs.bash = { enable = true; - - # Safely append user's custom RC file after NixOS setup + bashrcExtra = '' if [[ -f ~/.config/nomarchy/default/bash/rc ]]; then source ~/.config/nomarchy/default/bash/rc fi ''; - # Import aliases from the static file logic shellAliases = lib.mkDefault { # File system lsa = "ls -a"; - + # Directories ".." = "cd .."; "..." = "cd ../.."; @@ -32,14 +30,10 @@ gcm = "git commit -m"; gcam = "git commit -a -m"; gcad = "git commit -a --amend"; - - # NixOS specific (inherited from default.nix but keeping here for consistency) + + # NixOS commands sys-update = "sudo nixos-rebuild switch --flake /etc/nixos#default --impure"; env-update = "nomarchy-preflight-migration && home-manager switch --flake /etc/nixos#default --impure"; }; }; - - # Ensure the directory exists in the user's home via xdg.configFile - # This mapping is likely already handled in configs.nix, but we ensure it here - # or in the main config mapping. } diff --git a/modules/home/behavior-configs.nix b/modules/home/behavior-configs.nix new file mode 100644 index 0000000..32dc6b3 --- /dev/null +++ b/modules/home/behavior-configs.nix @@ -0,0 +1,97 @@ +{ 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"; + + # Check if user has an override for a specific config + hasOverride = path: builtins.pathExists "${overridesDir}/${path}"; + + # 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" + ''; + }; +} diff --git a/modules/home/configs.nix b/modules/home/configs.nix index 3590d7a..a6d3a4d 100644 --- a/modules/home/configs.nix +++ b/modules/home/configs.nix @@ -2,54 +2,68 @@ let configDir = ../../config; - - # Read the contents of the upstream config directory - configEntries = builtins.readDir configDir; - + + # Explicit list of config items to manage + # This replaces dynamic builtins.readDir for clarity and faster evaluation + configItems = { + # Directories + btop = "directory"; + chromium = "directory"; + elephant = "directory"; + "environment.d" = "directory"; + fastfetch = "directory"; + fcitx5 = "directory"; + fontconfig = "directory"; + ghostty = "directory"; + git = "directory"; + hypr = "directory"; + "hyprland-preview-share-picker" = "directory"; + imv = "directory"; + kitty = "directory"; + lazygit = "directory"; + nomarchy = "directory"; + opencode = "directory"; + systemd = "directory"; + tmux = "directory"; + Typora = "directory"; + uwsm = "directory"; + wiremix = "directory"; + xournalpp = "directory"; + + # Files + "brave-flags.conf" = "regular"; + "chromium-flags.conf" = "regular"; + "starship.toml" = "regular"; + "xdg-terminals.list" = "regular"; + }; + + # Files/directories handled elsewhere or not intended for ~/.config/ + # - nomarchy.ttf: font file, not a config + # - waybar: handled in waybar.nix + # - walker: handled in walker.nix + # - alacritty: handled in alacritty.nix + # - swayosd: handled in swayosd.nix + # Check for user overrides userConfigDir = config.nomarchy.configOverrides; - userEntries = if userConfigDir != null && builtins.pathExists userConfigDir - then builtins.readDir userConfigDir - else {}; - - # Files to explicitly exclude (handled elsewhere or not intended for ~/.config/) - excludedFiles = [ - "nomarchy.ttf" - "waybar" - "walker" - "alacritty" - "swayosd" - ]; - - # Get all unique names from both sources - allNames = lib.unique (builtins.attrNames configEntries ++ builtins.attrNames userEntries); - - # Filter the entries - validEntries = builtins.filter (name: !(builtins.elem name excludedFiles)) allNames; + hasUserOverrides = userConfigDir != null && builtins.pathExists userConfigDir; # Generate the xdg.configFile attribute set - # If a name exists in userEntries, it takes precedence. - # For directories, we use `recursive = true;` to allow the user to create their own files alongside the read-only defaults. - makeMapping = name: { - inherit name; - value = lib.mkDefault { - source = if userEntries ? ${name} - then "${userConfigDir}/${name}" - else "${configDir}/${name}"; - recursive = (userEntries.${name} or configEntries.${name}) == "directory"; + makeMapping = name: type: + let + hasUserOverride = hasUserOverrides && builtins.pathExists "${userConfigDir}/${name}"; + source = if hasUserOverride then "${userConfigDir}/${name}" else "${configDir}/${name}"; + in { + inherit name; + value = lib.mkDefault { + inherit source; + recursive = type == "directory"; + }; }; - }; + + configMappings = lib.mapAttrs' (name: type: makeMapping name type) configItems; in { - xdg.configFile = (builtins.listToAttrs (map makeMapping validEntries)) // { - "nomarchy" = { - source = ../../config/nomarchy; - recursive = true; - }; - "hypr" = { - source = ../../config/hypr; - recursive = true; - }; - }; + xdg.configFile = configMappings; } diff --git a/modules/home/default.nix b/modules/home/default.nix index 295612d..2b47eb7 100644 --- a/modules/home/default.nix +++ b/modules/home/default.nix @@ -1,10 +1,10 @@ { config, pkgs, inputs, lib, ... }: let - palettes = import ../../assets/themes/nomarchy-palettes.nix; + nomarchyLib = import ../lib { inherit lib; }; userPackagesFile = "${config.home.homeDirectory}/.config/home-manager/user-packages.json"; - userPackages = if builtins.pathExists userPackagesFile then - let + userPackages = if builtins.pathExists userPackagesFile then + let pkgNames = builtins.fromJSON (builtins.readFile userPackagesFile); # Filter to only packages that exist in pkgs to prevent build failures validPkgs = builtins.filter (name: builtins.hasAttr name pkgs) pkgNames; @@ -17,6 +17,9 @@ in inputs.walker.homeManagerModules.default ./options.nix ./state.nix + ./overrides.nix + ./behavior-configs.nix + ./theme-loader.nix ./theme-files.nix ./fonts.nix ./alacritty.nix @@ -35,30 +38,47 @@ in ./bash.nix ]; - colorScheme = lib.mkDefault (palettes.${config.nomarchy.theme} or palettes.nord); + colorScheme = lib.mkDefault (nomarchyLib.getColorScheme config.nomarchy.theme); home.packages = lib.mkDefault (with pkgs; [ + # Core applications firefox xfce.thunar - imv - mpv neovim + + # Media + imv # Image viewer + mpv # Video player + + # Hyprland ecosystem + swww # Wallpaper daemon + mako # Notification daemon + hyprlock # Lock screen + + # Screenshot and clipboard wl-clipboard grim slurp + + # Hardware control brightnessctl playerctl pamixer - mise + + # Utilities jq xmlstarlet + mise + gum # TUI components for scripts + + # Theming yaru-theme bibata-cursors + + # Fonts nerd-fonts.jetbrains-mono nerd-fonts.roboto-mono nerd-fonts.fira-code nerd-fonts.ubuntu-mono ] ++ userPackages); - - # Shell aliases are now managed in bash.nix } diff --git a/modules/home/fonts.nix b/modules/home/fonts.nix index 4cec7d5..f262145 100644 --- a/modules/home/fonts.nix +++ b/modules/home/fonts.nix @@ -1,8 +1,8 @@ -{ ... }: +{ lib, ... }: { config = { - fonts.fontconfig.enable = true; - xdg.dataFile."fonts/nomarchy.ttf".source = ../../config/nomarchy.ttf; + fonts.fontconfig.enable = lib.mkDefault true; + xdg.dataFile."fonts/nomarchy.ttf".source = lib.mkDefault ../../config/nomarchy.ttf; }; } diff --git a/modules/home/hyprland.nix b/modules/home/hyprland.nix index af4fe93..5326a84 100644 --- a/modules/home/hyprland.nix +++ b/modules/home/hyprland.nix @@ -1,20 +1,27 @@ { config, pkgs, lib, ... }: let - activeWallpaper = if config.nomarchy.wallpaper != "" then - config.nomarchy.wallpaper - else "${../../assets/themes/catppuccin/backgrounds/1-totoro.png}"; + nomarchyLib = import ../lib { inherit lib; }; + assetsPath = ../../assets/themes; + + # Use shared wallpaper resolver + activeWallpaper = nomarchyLib.resolveWallpaper { + wallpaperPath = config.nomarchy.wallpaper; + themeName = config.nomarchy.theme; + inherit assetsPath; + }; + hyprlandState = config.nomarchy.hyprland; in { - home.sessionVariables = { + home.sessionVariables = lib.mkDefault { WLR_NO_HARDWARE_CURSORS = "1"; HYPRLAND_LOG_WLR = "1"; }; wayland.windowManager.hyprland = { - enable = true; - settings = { + enable = lib.mkDefault true; + settings = lib.mkDefault { "debug" = { "disable_logs" = false; "enable_stdout_logs" = true; @@ -30,7 +37,7 @@ in "col.inactive_border" = "rgb(${config.colorScheme.palette.base03})"; }; }; - extraConfig = '' + extraConfig = lib.mkDefault '' source = ~/.config/hypr/nomarchy.conf ''; }; diff --git a/modules/home/idle.nix b/modules/home/idle.nix index 5ad96e2..a36c49c 100644 --- a/modules/home/idle.nix +++ b/modules/home/idle.nix @@ -1,9 +1,9 @@ -{ config, ... }: +{ config, lib, ... }: { services.hypridle = { - enable = config.nomarchy.toggles.idle; - settings = { + enable = lib.mkDefault config.nomarchy.toggles.idle; + settings = lib.mkDefault { general = { lock_cmd = "nomarchy-lock-screen"; before_sleep_cmd = "loginctl lock-session"; diff --git a/modules/home/nightlight.nix b/modules/home/nightlight.nix index 142dbd0..124ecef 100644 --- a/modules/home/nightlight.nix +++ b/modules/home/nightlight.nix @@ -1,11 +1,11 @@ -{ config, ... }: +{ config, lib, ... }: let temp = toString (if config.nomarchy.toggles.nightlight then config.nomarchy.nightlightTemperature else 6500); in { services.hyprsunset = { - enable = true; # Always enabled, we control via IPC and state - extraArgs = [ "--temperature" temp ]; + enable = lib.mkDefault true; # Always enabled, we control via IPC and state + extraArgs = lib.mkDefault [ "--temperature" temp ]; }; } diff --git a/modules/home/overrides.nix b/modules/home/overrides.nix new file mode 100644 index 0000000..9f7822a --- /dev/null +++ b/modules/home/overrides.nix @@ -0,0 +1,102 @@ +{ config, lib, ... }: + +# File-based override system for Nomarchy +# +# Users can place config files in ~/.config/nomarchy/overrides/ to completely +# replace upstream defaults. Override priority (highest to lowest): +# 1. User Nix options +# 2. User file overrides (~/.config/nomarchy/overrides/) +# 3. Upstream defaults +# +# Supported override paths: +# - hypr/ - Hyprland configs (bindings.conf, input.conf, etc.) +# - waybar/ - Waybar config and style +# - alacritty/ - Alacritty terminal config +# - walker/ - Walker launcher config +# - kitty/ - Kitty terminal config +# - btop/ - Btop resource monitor config +# - apps/ - Other application configs + +let + overridesDir = "${config.home.homeDirectory}/.config/nomarchy/overrides"; + + # Check if a specific override exists + hasOverride = path: builtins.pathExists "${overridesDir}/${path}"; + + # Get override source if it exists, otherwise use default + getOverrideOrDefault = { path, default }: + if hasOverride path + then "${overridesDir}/${path}" + else default; + +in +{ + options.nomarchy.overrides = { + enable = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to enable file-based override loading from ~/.config/nomarchy/overrides/"; + }; + + paths = lib.mkOption { + type = lib.types.attrsOf lib.types.path; + default = {}; + description = "Override paths discovered at build time. Populated by the override system."; + }; + }; + + config = lib.mkIf config.nomarchy.overrides.enable { + # Create the overrides directory structure if it doesn't exist + home.activation.createOverridesDir = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + mkdir -p "${overridesDir}" + mkdir -p "${overridesDir}/hypr" + mkdir -p "${overridesDir}/waybar" + mkdir -p "${overridesDir}/apps" + ''; + + # Document the override system + xdg.configFile."nomarchy/overrides/README.md".text = lib.mkDefault '' + # Nomarchy Configuration Overrides + + Place files in this directory to override upstream Nomarchy defaults. + + ## Directory Structure + + ``` + overrides/ + ├── hypr/ + │ ├── bindings.conf # Keybindings + │ ├── input.conf # Input settings + │ ├── monitors.conf # Monitor layout + │ ├── rules.conf # Window rules + │ └── autostart.conf # Startup apps + ├── waybar/ + │ ├── config.jsonc # Waybar layout + │ └── style.css # Waybar styling + ├── apps/ + │ ├── alacritty.toml # Terminal behavior + │ └── ... + └── README.md # This file + ``` + + ## Override Priority + + 1. **Nix Options** (highest) - Set in your flake/config + 2. **File Overrides** - Files in this directory + 3. **Upstream Defaults** (lowest) - Nomarchy defaults + + ## Usage + + 1. Copy the file you want to customize from the upstream config + 2. Place it in the appropriate directory here + 3. Edit to your preferences + 4. Run `nixos-rebuild switch` or `home-manager switch` + + ## Tips + + - For keybindings, copy `~/.config/hypr/bindings.conf` to `overrides/hypr/` + - For Waybar styling, copy `~/.config/waybar/style.css` to `overrides/waybar/` + - Changes here persist across Nomarchy updates + ''; + }; +} diff --git a/modules/home/scripts.nix b/modules/home/scripts.nix index 01141ea..bb7743b 100644 --- a/modules/home/scripts.nix +++ b/modules/home/scripts.nix @@ -1,62 +1,151 @@ { config, pkgs, lib, ... }: let - # Core dependencies needed by most Nomarchy scripts - nomarchy-deps = with pkgs; [ - gum - hyprland - procps - libnotify + # Core dependencies needed by most scripts (minimal set) + coreDeps = with pkgs; [ coreutils gnused gnugrep - pciutils findutils - jq - swww - xmlstarlet - wl-clipboard - grim - slurp - brightnessctl - playerctl - pamixer - pulseaudio - wireplumber - swayosd gawk - curl - wget - # Add any others commonly used in bin/ + jq ]; + # Category-specific dependencies + categoryDeps = with pkgs; { + appearance = [ + swww + libnotify + ]; + + apps = [ + hyprland + libnotify + ]; + + hardware = [ + brightnessctl + playerctl + pamixer + pciutils + libnotify + ]; + + system = [ + gum + libnotify + curl + wget + ]; + + utils = [ + gum + hyprland + libnotify + wl-clipboard + grim + slurp + xmlstarlet + curl + wget + ]; + + wm = [ + hyprland + swayosd + libnotify + procps + ]; + }; + + # All dependencies combined (for scripts that might call others) + allDeps = coreDeps ++ (lib.unique (lib.flatten (builtins.attrValues categoryDeps))); + + # Environment variables to inject + envVars = { + NOMARCHY_TOGGLE_SUSPEND = if config.nomarchy.toggles.suspend then "true" else "false"; + NOMARCHY_TOGGLE_SCREENSAVER = if config.nomarchy.toggles.screensaver then "true" else "false"; + NOMARCHY_TOGGLE_IDLE = if config.nomarchy.toggles.idle then "true" else "false"; + NOMARCHY_TOGGLE_NIGHTLIGHT = if config.nomarchy.toggles.nightlight then "true" else "false"; + NOMARCHY_TOGGLE_WAYBAR = if config.nomarchy.toggles.waybar then "true" else "false"; + NOMARCHY_TOGGLE_SKIP_VSCODE_THEME = if config.nomarchy.toggles.skipVsCodeTheme then "true" else "false"; + NOMARCHY_MONOSPACE_FONT = config.nomarchy.fonts.monospace; + }; + + # Build wrapper arguments for environment variables + envWrapperArgs = lib.concatStringsSep " " ( + lib.mapAttrsToList (name: value: "--set ${name} \"${value}\"") envVars + ); + nomarchy-scripts = pkgs.stdenv.mkDerivation { pname = "nomarchy-scripts"; version = "1.0.0"; src = ../../bin; - + nativeBuildInputs = [ pkgs.makeWrapper ]; installPhase = '' mkdir -p $out/bin - find . -type f -exec cp {} $out/bin/ \; + + # Copy scripts preserving directory structure for category detection + for category in appearance apps hardware system utils wm; do + if [ -d "$category" ]; then + for script in "$category"/*; do + if [ -f "$script" ]; then + cp "$script" "$out/bin/" + fi + done + fi + done + chmod +x $out/bin/* patchShebangs $out/bin ''; postFixup = '' - # Wrap every script to ensure dependencies are in PATH and inject configuration + # Wrap scripts with category-specific dependencies for file in $out/bin/*; do if [ -f "$file" ]; then + scriptName=$(basename "$file") + + # Determine category from original source location + category="" + for cat in appearance apps hardware system utils wm; do + if [ -f "$src/$cat/$scriptName" ]; then + category="$cat" + break + fi + done + + # Select dependencies based on category + case "$category" in + appearance) + deps="${lib.makeBinPath (coreDeps ++ categoryDeps.appearance)}" + ;; + apps) + deps="${lib.makeBinPath (coreDeps ++ categoryDeps.apps)}" + ;; + hardware) + deps="${lib.makeBinPath (coreDeps ++ categoryDeps.hardware)}" + ;; + system) + deps="${lib.makeBinPath (coreDeps ++ categoryDeps.system)}" + ;; + utils) + deps="${lib.makeBinPath (coreDeps ++ categoryDeps.utils)}" + ;; + wm) + deps="${lib.makeBinPath (coreDeps ++ categoryDeps.wm)}" + ;; + *) + # Fallback to all deps for unknown categories + deps="${lib.makeBinPath allDeps}" + ;; + esac + wrapProgram "$file" \ - --prefix PATH : ${lib.makeBinPath nomarchy-deps} \ - --set NOMARCHY_TOGGLE_SUSPEND "${if config.nomarchy.toggles.suspend then "true" else "false"}" \ - --set NOMARCHY_TOGGLE_SCREENSAVER "${if config.nomarchy.toggles.screensaver then "true" else "false"}" \ - --set NOMARCHY_TOGGLE_IDLE "${if config.nomarchy.toggles.idle then "true" else "false"}" \ - --set NOMARCHY_TOGGLE_NIGHTLIGHT "${if config.nomarchy.toggles.nightlight then "true" else "false"}" \ - --set NOMARCHY_TOGGLE_WAYBAR "${if config.nomarchy.toggles.waybar then "true" else "false"}" \ - --set NOMARCHY_TOGGLE_SKIP_VSCODE_THEME "${if config.nomarchy.toggles.skipVsCodeTheme then "true" else "false"}" \ - --set NOMARCHY_MONOSPACE_FONT "${config.nomarchy.fonts.monospace}" + --prefix PATH : "$deps" \ + ${envWrapperArgs} fi done ''; diff --git a/modules/home/state.nix b/modules/home/state.nix index cdf8c51..ee3aab9 100644 --- a/modules/home/state.nix +++ b/modules/home/state.nix @@ -1,24 +1,11 @@ -{ config, lib, pkgs, ... }: +{ config, lib, ... }: let - stateDir = "${config.home.homeDirectory}/.config/home-manager"; - - # Helper to read state from a file, with a default - readState = file: default: - if builtins.pathExists "${stateDir}/${file}" then - let - content = builtins.readFile "${stateDir}/${file}"; - cleanContent = lib.removeSuffix "\n" content; - in - if lib.hasSuffix ".json" file then - builtins.fromJSON cleanContent - else - cleanContent - else - default; + nomarchyLib = import ../lib { inherit lib; }; + assetsPath = ../../assets/themes; - # Unified state reading - togglesState = readState "state.json" {}; + # Read unified state from ~/.config/nomarchy/state.json + togglesState = nomarchyLib.readHomeState config.home.homeDirectory; in { config = { @@ -40,15 +27,16 @@ in border_size = togglesState.hyprland.border_size or 2; }; fonts.monospace = togglesState.font or "JetBrainsMono Nerd Font"; - + # Derived properties from the theme directory - isLightMode = builtins.pathExists (../../assets/themes + "/${togglesState.theme or "nord"}/light.mode"); - iconsTheme = let - iconsFile = ../../assets/themes + "/${togglesState.theme or "nord"}/icons.theme"; - in - if builtins.pathExists iconsFile - then lib.removeSuffix "\n" (builtins.readFile iconsFile) - else "Yaru-blue"; + isLightMode = nomarchyLib.isThemeLightMode { + themeName = togglesState.theme or "nord"; + inherit assetsPath; + }; + iconsTheme = nomarchyLib.getIconsTheme { + themeName = togglesState.theme or "nord"; + inherit assetsPath; + }; }; }; } diff --git a/modules/home/stylix.nix b/modules/home/stylix.nix index 63dcffd..d6a11f9 100644 --- a/modules/home/stylix.nix +++ b/modules/home/stylix.nix @@ -1,37 +1,57 @@ { config, pkgs, inputs, lib, ... }: -let - # Re-use our state-based logic - palettes = import ../../assets/themes/nomarchy-palettes.nix; - - activeThemeName = config.nomarchy.theme; - activeWallpaper = if config.nomarchy.wallpaper != "" then - # Use the absolute path from the state file (string format to avoid Nix Store copy) - config.nomarchy.wallpaper - else ../../assets/themes/catppuccin/backgrounds/1-totoro.png; # Fallback path object (copied to store) +# Stylix Integration Module +# +# This module handles base-level theming through Stylix: +# - Color scheme injection from the active theme's palette +# - Cursor configuration +# - Font configuration +# - GTK/GNOME theming +# +# App-specific theming is handled separately: +# - theme-loader.nix: Deploys theme's apps/ configs (btop, neovim, etc.) +# - waybar.nix: Generates waybar CSS from colorScheme +# - hyprland.nix: Handles hyprland border colors +# +# Stylix targets disabled here (we have custom implementations): +# - hyprland: Custom border/rule config +# - waybar: Custom CSS with theme colors - # Map nix-colors palette to a format Stylix expects (attrset of hex strings) - currentPalette = (palettes.${activeThemeName} or palettes.nord).palette; +let + nomarchyLib = import ../lib { inherit lib; }; + assetsPath = ../../assets/themes; + + activeThemeName = config.nomarchy.theme; + + # Use shared wallpaper resolver + activeWallpaper = nomarchyLib.resolveWallpaper { + wallpaperPath = config.nomarchy.wallpaper; + themeName = activeThemeName; + inherit assetsPath; + }; + + # Get palette using shared library + currentPalette = nomarchyLib.getPalette activeThemeName; in { imports = [ inputs.stylix.homeModules.stylix ]; stylix = { - enable = true; - enableReleaseChecks = false; - image = activeWallpaper; - base16Scheme = currentPalette; - - # Use detected light mode state - polarity = if config.nomarchy.isLightMode then "light" else "dark"; + enable = lib.mkDefault true; + enableReleaseChecks = lib.mkDefault false; + image = lib.mkDefault activeWallpaper; + base16Scheme = lib.mkDefault currentPalette; - cursor = { + # Use detected light mode state + polarity = lib.mkDefault (if config.nomarchy.isLightMode then "light" else "dark"); + + cursor = lib.mkDefault { package = config.nomarchy.cursor.package; name = config.nomarchy.cursor.name; size = 24; }; - fonts = { + fonts = lib.mkDefault { monospace = { package = pkgs.nerd-fonts.jetbrains-mono; name = config.nomarchy.fonts.monospace; @@ -57,7 +77,7 @@ in }; # Enable theming for specific targets - targets = { + targets = lib.mkDefault { hyprland.enable = false; # We keep our custom hyprland config for borders/rules waybar.enable = false; # We keep our custom waybar CSS alacritty.enable = true; @@ -69,8 +89,8 @@ in # GTK Icon Theme configuration gtk = { - enable = true; - iconTheme = { + enable = lib.mkDefault true; + iconTheme = lib.mkDefault { package = pkgs.yaru-theme; name = config.nomarchy.iconsTheme; }; diff --git a/modules/home/swayosd.nix b/modules/home/swayosd.nix index 98237ee..6681125 100644 --- a/modules/home/swayosd.nix +++ b/modules/home/swayosd.nix @@ -1,12 +1,12 @@ -{ config, pkgs, ... }: +{ config, pkgs, lib, ... }: { services.swayosd = { - enable = true; - stylePath = "${config.home.homeDirectory}/.config/swayosd/style.css"; + enable = lib.mkDefault true; + stylePath = lib.mkDefault "${config.home.homeDirectory}/.config/swayosd/style.css"; }; - xdg.configFile."swayosd/style.css".text = '' + xdg.configFile."swayosd/style.css".text = lib.mkDefault '' @define-color background-color #${config.colorScheme.palette.base00}; @define-color border-color #${config.colorScheme.palette.base0E}; @define-color label #${config.colorScheme.palette.base05}; diff --git a/modules/home/theme-files.nix b/modules/home/theme-files.nix index 4d512ed..ae9b486 100644 --- a/modules/home/theme-files.nix +++ b/modules/home/theme-files.nix @@ -1,14 +1,31 @@ { config, lib, ... }: +let + themePath = ../../assets/themes + "/${config.nomarchy.theme}"; + themeAppsPath = themePath + "/apps"; + nordAppsPath = ../../assets/themes/nord/apps; + + # Check if theme has apps/hyprland.conf + hasHyprlandConf = builtins.pathExists (themeAppsPath + "/hyprland.conf"); +in { xdg.configFile."nomarchy/current/theme" = { - source = ../../assets/themes/${config.nomarchy.theme}; + source = themePath; recursive = true; }; - + # Ensure theme-specific hyprland config exists, fallback to nord if not - xdg.configFile."nomarchy/current/theme/hyprland.conf" = lib.mkIf (! builtins.pathExists (../../assets/themes + "/${config.nomarchy.theme}/hyprland.conf")) { - source = ../../assets/themes/nord/hyprland.conf; + # Now checking in apps/ subdirectory + xdg.configFile."nomarchy/current/theme/apps/hyprland.conf" = lib.mkIf (!hasHyprlandConf) { + source = nordAppsPath + "/hyprland.conf"; + }; + + # Legacy compatibility: symlink apps/hyprland.conf to root for scripts expecting old path + xdg.configFile."nomarchy/current/theme/hyprland.conf" = { + source = + if hasHyprlandConf + then themeAppsPath + "/hyprland.conf" + else nordAppsPath + "/hyprland.conf"; }; xdg.configFile."nomarchy/current/theme.name".text = config.nomarchy.theme; diff --git a/modules/home/theme-loader.nix b/modules/home/theme-loader.nix new file mode 100644 index 0000000..fc1f13d --- /dev/null +++ b/modules/home/theme-loader.nix @@ -0,0 +1,128 @@ +{ config, lib, pkgs, ... }: + +# Theme Loader Module +# +# This module handles loading and deploying theme-specific application configs. +# It reads the active theme from state and deploys configs from the theme's apps/ +# subdirectory to appropriate locations. +# +# Theme structure expected: +# assets/themes// +# ├── colors.toml # Color palette (required) +# ├── light.mode # Marker for light themes (optional) +# ├── icons.theme # Icon theme name +# ├── backgrounds/ # Wallpapers +# ├── apps/ # App-specific themed configs +# │ ├── alacritty.toml +# │ ├── kitty.conf +# │ ├── waybar.css +# │ ├── hyprland-colors.conf +# │ ├── mako.conf +# │ ├── swayosd.css +# │ ├── btop.theme +# │ ├── vscode.json +# │ └── neovim.lua +# └── preview.png + +let + nomarchyLib = import ../lib { inherit lib; }; + assetsPath = ../../assets/themes; + activeTheme = config.nomarchy.theme; + themePath = assetsPath + "/${activeTheme}"; + themeAppsPath = themePath + "/apps"; + + # Check if a theme has an apps directory + themeHasApps = builtins.pathExists themeAppsPath; + + # Get the color palette for template processing + palette = nomarchyLib.getPalette activeTheme; + + # Helper to check if a file exists in theme + hasThemeFile = name: builtins.pathExists (themePath + "/${name}"); + hasThemeAppFile = name: themeHasApps && builtins.pathExists (themeAppsPath + "/${name}"); + + # All app configs are now in apps/ subdirectory + # Legacy root-level files are no longer supported + +in +{ + options.nomarchy.themeLoader = { + enable = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to enable automatic theme app config loading."; + }; + + apps = { + btop = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to load btop theme from active theme."; + }; + waybar = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to load waybar CSS from active theme."; + }; + mako = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to load mako config from active theme."; + }; + kitty = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to load kitty config from active theme."; + }; + alacritty = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to load alacritty config from active theme."; + }; + }; + }; + + config = lib.mkIf config.nomarchy.themeLoader.enable { + # Deploy btop theme if available in apps/ + xdg.configFile."btop/themes/nomarchy.theme" = lib.mkIf (config.nomarchy.themeLoader.apps.btop && hasThemeAppFile "btop.theme") { + source = lib.mkDefault (themeAppsPath + "/btop.theme"); + }; + + # Note: Waybar CSS is generated inline in waybar.nix using colorScheme + # This loader would be used if themes provide complete waybar.css overrides + # For now, theme waybar.css files are only used for themes that need + # significantly different styling (like retro-82) + + # Create theme info file for scripts + xdg.configFile."nomarchy/theme-loader/current".text = '' + THEME_NAME="${activeTheme}" + THEME_PATH="${toString themePath}" + THEME_HAS_APPS="${if themeHasApps then "true" else "false"}" + IS_LIGHT_MODE="${if config.nomarchy.isLightMode then "true" else "false"}" + ICONS_THEME="${config.nomarchy.iconsTheme}" + ''; + + # Expose palette as shell-sourceable file for scripts + xdg.configFile."nomarchy/theme-loader/palette.sh".text = '' + # Auto-generated palette for ${activeTheme} + # Source this file in scripts to get theme colors + + BASE00="${palette.base00}" + BASE01="${palette.base01}" + BASE02="${palette.base02}" + BASE03="${palette.base03}" + BASE04="${palette.base04}" + BASE05="${palette.base05}" + BASE06="${palette.base06}" + BASE07="${palette.base07}" + BASE08="${palette.base08}" + BASE09="${palette.base09}" + BASE0A="${palette.base0A}" + BASE0B="${palette.base0B}" + BASE0C="${palette.base0C}" + BASE0D="${palette.base0D}" + BASE0E="${palette.base0E}" + BASE0F="${palette.base0F}" + ''; + }; +} diff --git a/modules/home/theme-switcher.nix b/modules/home/theme-switcher.nix index 0ae37db..04075b7 100644 --- a/modules/home/theme-switcher.nix +++ b/modules/home/theme-switcher.nix @@ -1,42 +1,43 @@ -{ config, pkgs, ... }: +{ config, pkgs, lib, ... }: let - palettes = import ../../assets/themes/nomarchy-palettes.nix; - themeNames = builtins.attrNames palettes; - themeList = builtins.concatStringsSep "\\n" themeNames; + nomarchyLib = import ../lib { inherit lib; }; + themeList = builtins.concatStringsSep "\\n" nomarchyLib.themeNames; nomarchy-theme-selector = pkgs.writeShellScriptBin "nomarchy-theme-selector" '' SELECTED_THEME=$(echo -e "${themeList}" | walker --dmenu) - + if [ -n "$SELECTED_THEME" ]; then nomarchy-theme-set "$SELECTED_THEME" fi ''; + nomarchy-font-selector = pkgs.writeShellScriptBin "nomarchy-font-selector" '' - STATE_DIR="${config.home.homeDirectory}/.config/home-manager" + STATE_DIR="${config.home.homeDirectory}/.config/nomarchy" STATE_FILE="$STATE_DIR/state.json" - + mkdir -p "$STATE_DIR" [[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE" - + # Simple list of common nerd fonts, could be expanded FONTS="JetBrainsMono Nerd Font\nRobotoMono Nerd Font\nFiraCode Nerd Font\nUbuntuMono Nerd Font" SELECTED_FONT=$(echo -e "$FONTS" | walker --dmenu) - + if [ -n "$SELECTED_FONT" ]; then TMP_JSON=$(mktemp) - jq ".font = \"$SELECTED_FONT\"" "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" + jq --arg font "$SELECTED_FONT" '.font = $font' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" env-update fi ''; + nomarchy-wallpaper-selector = pkgs.writeShellScriptBin "nomarchy-wallpaper-selector" '' - STATE_DIR="${config.home.homeDirectory}/.config/home-manager" + STATE_DIR="${config.home.homeDirectory}/.config/nomarchy" STATE_FILE="$STATE_DIR/state.json" THEMES_DIR="${config.xdg.dataHome}/nomarchy/themes" - + mkdir -p "$STATE_DIR" [[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE" - + # List all images in all themes backgrounds # We search in the system-wide flake source for distro wallpapers to avoid Nix Store bloat WALLPAPERS=$(find "${config.xdg.dataHome}/nomarchy/themes" -type f \( -name "*.jpg" -o -name "*.png" \) 2>/dev/null) @@ -49,10 +50,10 @@ let WALLPAPERS="$WALLPAPERS\n$(find "${config.home.homeDirectory}/.config/nomarchy/themes" -type f \( -name "*.jpg" -o -name "*.png" \))" fi SELECTED_WP=$(echo -e "$WALLPAPERS" | walker --dmenu) - + if [ -n "$SELECTED_WP" ]; then TMP_JSON=$(mktemp) - jq ".wallpaper = \"$SELECTED_WP\"" "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" + jq --arg wp "$SELECTED_WP" '.wallpaper = $wp' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE" swww img "$SELECTED_WP" --transition-type outer --transition-pos 0.85,0.97 --transition-step 90 & env-update fi diff --git a/modules/home/vscode.nix b/modules/home/vscode.nix index 5ed7a60..7e71c48 100644 --- a/modules/home/vscode.nix +++ b/modules/home/vscode.nix @@ -1,17 +1,53 @@ -{ config, pkgs, ... }: +{ config, pkgs, lib, ... }: let themeConfig = builtins.fromJSON (builtins.readFile (../../assets/themes + "/${config.nomarchy.theme}/vscode.json")); + + # Development extensions that match the system theme + devExtensions = with pkgs.vscode-extensions; [ + # Language support + ms-python.python + rust-lang.rust-analyzer + golang.go + jnoortheen.nix-ide + + # Git integration + eamodio.gitlens + + # Editor enhancements + esbenp.prettier-vscode + dbaeumer.vscode-eslint + bradlc.vscode-tailwindcss + + # Theme extensions (provide color themes matching nomarchy palettes) + catppuccin.catppuccin-vsc + enkia.tokyo-night + arcticicestudio.nord-visual-studio-code + sainnhe.everforest + mvllow.rose-pine + ]; in { - programs.vscode = { - enable = true; - package = pkgs.vscode; - userSettings = { - "update.mode" = "none"; - "workbench.colorTheme" = themeConfig.name; - "window.titleBarStyle" = "custom"; + options.nomarchy.vscode = { + devExtensions = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether to install development extensions for VSCode."; + }; + }; + + config = { + programs.vscode = { + enable = lib.mkDefault true; + package = lib.mkDefault pkgs.vscode; + userSettings = lib.mkDefault { + "update.mode" = "none"; + "workbench.colorTheme" = themeConfig.name; + "window.titleBarStyle" = "custom"; + "editor.fontFamily" = "'${config.nomarchy.fonts.monospace}', 'Droid Sans Mono', monospace"; + "terminal.integrated.fontFamily" = config.nomarchy.fonts.monospace; + }; + extensions = lib.mkIf config.nomarchy.vscode.devExtensions (lib.mkDefault devExtensions); }; - # extensions = with pkgs.vscode-extensions; [ ... ]; }; } diff --git a/modules/home/walker.nix b/modules/home/walker.nix index 1f9375e..dbcab70 100644 --- a/modules/home/walker.nix +++ b/modules/home/walker.nix @@ -1,10 +1,10 @@ -{ config, pkgs, ... }: +{ config, pkgs, lib, ... }: { programs.walker = { - enable = true; - runAsService = true; - config = { + enable = lib.mkDefault true; + runAsService = lib.mkDefault true; + config = lib.mkDefault { theme = "nomarchy"; ui = { anchors = { @@ -14,7 +14,7 @@ selection_wrap = true; hide_action_hints = true; placeholders = { - "default" = { input = " Search..."; list = "No Results"; }; + "default" = { input = " Search..."; list = "No Results"; }; }; keybinds = { quick_activate = []; @@ -38,7 +38,7 @@ ]; }; }; - themes."nomarchy" = { + themes."nomarchy" = lib.mkDefault { style = '' * { color: #${config.colorScheme.palette.base05}; diff --git a/modules/home/waybar.nix b/modules/home/waybar.nix index afca05a..970b9ab 100644 --- a/modules/home/waybar.nix +++ b/modules/home/waybar.nix @@ -1,10 +1,10 @@ -{ config, pkgs, ... }: +{ config, pkgs, lib, ... }: { programs.waybar = { - enable = true; - systemd.enable = true; - style = '' + enable = lib.mkDefault true; + systemd.enable = lib.mkDefault true; + style = lib.mkDefault '' * { background-color: #${config.colorScheme.palette.base00}; color: #${config.colorScheme.palette.base05}; @@ -25,7 +25,7 @@ min-width: 9px; color: #${config.colorScheme.palette.base04}; } - + #workspaces button.focused, #workspaces button.active { color: #${config.colorScheme.palette.base0B}; } @@ -40,10 +40,10 @@ #tray { margin-right: 16px; } #bluetooth { margin-right: 17px; } ''; - settings = [ (builtins.fromJSON (builtins.readFile ../../config/waybar/config.jsonc)) ]; + settings = lib.mkDefault [ (builtins.fromJSON (builtins.readFile ../../config/waybar/config.jsonc)) ]; }; - home.packages = with pkgs; [ + home.packages = lib.mkDefault (with pkgs; [ font-awesome - ]; + ]); } diff --git a/modules/lib/default.nix b/modules/lib/default.nix new file mode 100644 index 0000000..34312b2 --- /dev/null +++ b/modules/lib/default.nix @@ -0,0 +1,96 @@ +# Nomarchy Shared Library +# Centralized utilities to reduce code duplication across modules +{ lib }: + +let + # Import theme palettes once - used by multiple modules + palettes = import ../../assets/themes/nomarchy-palettes.nix; + + # Unified state reading function + # Handles both JSON and plain text files with graceful fallbacks + readState = { file, default }: + if builtins.pathExists file then + let + content = builtins.readFile file; + cleanContent = lib.removeSuffix "\n" content; + in + if lib.hasSuffix ".json" (toString file) then + builtins.fromJSON cleanContent + else + cleanContent + else + default; + + # Read home state from unified location + readHomeState = homeDirectory: + readState { + file = "${homeDirectory}/.config/nomarchy/state.json"; + default = {}; + }; + + # Read system state + readSystemState = + readState { + file = /etc/nixos/state.json; + default = {}; + }; + + # Resolve wallpaper path with fallback + # Returns either the user-specified wallpaper or a default from the theme + resolveWallpaper = { wallpaperPath, themeName, assetsPath }: + if wallpaperPath != "" then + wallpaperPath + else + let + themeBackgrounds = assetsPath + "/${themeName}/backgrounds"; + defaultBackground = assetsPath + "/catppuccin/backgrounds/1-totoro.png"; + in + if builtins.pathExists themeBackgrounds then + let + backgrounds = builtins.attrNames (builtins.readDir themeBackgrounds); + in + if backgrounds != [] then + "${themeBackgrounds}/${builtins.head (builtins.sort (a: b: a < b) backgrounds)}" + else + defaultBackground + else + defaultBackground; + + # Get current palette from theme name + getPalette = themeName: + (palettes.${themeName} or palettes.nord).palette; + + # Get full color scheme from theme name + getColorScheme = themeName: + palettes.${themeName} or palettes.nord; + + # Check if a theme is light mode + isThemeLightMode = { themeName, assetsPath }: + builtins.pathExists (assetsPath + "/${themeName}/light.mode"); + + # Get icons theme for a given theme + getIconsTheme = { themeName, assetsPath, default ? "Yaru-blue" }: + let + iconsFile = assetsPath + "/${themeName}/icons.theme"; + in + if builtins.pathExists iconsFile then + lib.removeSuffix "\n" (builtins.readFile iconsFile) + else + default; + + # Get list of available theme names + themeNames = builtins.attrNames palettes; + +in { + inherit + palettes + readState + readHomeState + readSystemState + resolveWallpaper + getPalette + getColorScheme + isThemeLightMode + getIconsTheme + themeNames; +} diff --git a/modules/lib/state-schema.nix b/modules/lib/state-schema.nix new file mode 100644 index 0000000..e8c375b --- /dev/null +++ b/modules/lib/state-schema.nix @@ -0,0 +1,66 @@ +# Nomarchy State Schema +# Defines the complete state shape with defaults for both home and system state +{ lib }: + +{ + # Home state defaults (user preferences) + home = { + # Theme and appearance + theme = "nord"; + wallpaper = ""; + font = "JetBrainsMono Nerd Font"; + nightlightTemperature = 4000; + + # Feature toggles + suspend = true; + screensaver = true; + idle = true; + nightlight = false; + waybar = true; + skipVsCodeTheme = false; + + # Hyprland window manager settings + hyprland = { + gaps_in = 5; + gaps_out = 10; + border_size = 2; + }; + }; + + # System state defaults (system-level configuration) + system = { + # Theme (can differ from home for system-level theming) + theme = "nord"; + + # Timezone + timezone = "UTC"; + + # DNS configuration + dns = "DHCP"; # Options: "DHCP", "Cloudflare", "Google", "Custom" + customDns = []; + + # Wi-Fi settings + wifi = { + powersave = true; + }; + + # Optional features + features = { + fingerprint = false; + fido2 = false; + hybridGPU = false; + makima = false; + }; + }; + + # Get a value from state with fallback to default + getWithDefault = state: path: default: + let + pathList = lib.splitString "." path; + getValue = obj: remaining: + if remaining == [] then obj + else if builtins.isAttrs obj && builtins.hasAttr (builtins.head remaining) obj + then getValue obj.${builtins.head remaining} (builtins.tail remaining) + else default; + in getValue state pathList; +} diff --git a/modules/system/audio.nix b/modules/system/audio.nix index 5968d73..33f6958 100644 --- a/modules/system/audio.nix +++ b/modules/system/audio.nix @@ -1,12 +1,12 @@ -{ config, pkgs, ... }: +{ config, pkgs, lib, ... }: { - security.rtkit.enable = true; + security.rtkit.enable = lib.mkDefault true; services.pipewire = { - enable = true; - alsa.enable = true; - alsa.support32Bit = true; - pulse.enable = true; - jack.enable = true; + enable = lib.mkDefault true; + alsa.enable = lib.mkDefault true; + alsa.support32Bit = lib.mkDefault true; + pulse.enable = lib.mkDefault true; + jack.enable = lib.mkDefault true; }; } diff --git a/modules/system/bluetooth.nix b/modules/system/bluetooth.nix index 122c2e8..dc57c0d 100644 --- a/modules/system/bluetooth.nix +++ b/modules/system/bluetooth.nix @@ -1,7 +1,7 @@ -{ config, pkgs, ... }: +{ config, pkgs, lib, ... }: { - hardware.bluetooth.enable = true; - hardware.bluetooth.powerOnBoot = true; - services.blueman.enable = true; + hardware.bluetooth.enable = lib.mkDefault true; + hardware.bluetooth.powerOnBoot = lib.mkDefault true; + services.blueman.enable = lib.mkDefault true; } diff --git a/modules/system/browser.nix b/modules/system/browser.nix index 6aa6651..9634ffb 100644 --- a/modules/system/browser.nix +++ b/modules/system/browser.nix @@ -1,22 +1,30 @@ { config, pkgs, lib, ... }: let - palettes = import ../../assets/themes/nomarchy-palettes.nix; + nomarchyLib = import ../lib { inherit lib; }; activeThemeName = config.nomarchy.system.theme; - currentPalette = (palettes.${activeThemeName} or palettes.nord).palette; - + currentPalette = nomarchyLib.getPalette activeThemeName; + # Hex color for browser theme (base00 is background) themeColor = "#${currentPalette.base00}"; - - policy = { + + # Detect light mode from theme name or palette + isLightTheme = nomarchyLib.isThemeLightMode { + themeName = activeThemeName; + assetsPath = ../../assets/themes; + }; + + browserPolicy = { BrowserThemeColor = themeColor; - BrowserColorScheme = if lib.strings.hasInfix "light" activeThemeName then "light" else "dark"; + BrowserColorScheme = if isLightTheme then "light" else "dark"; }; in { # Chromium policies - programs.chromium.extraOpts = policy; - - # Brave policies (Brave on NixOS also respects some chromium policies if set via extraOpts) - # But better to use the specific brave module if available or just the same policy. + programs.chromium.extraOpts = lib.mkDefault browserPolicy; + + # Brave browser policies via managed policy file + environment.etc."brave/policies/managed/nomarchy.json".text = lib.mkDefault ( + builtins.toJSON browserPolicy + ); } diff --git a/modules/system/network.nix b/modules/system/network.nix index 42d58c5..1a8d25a 100644 --- a/modules/system/network.nix +++ b/modules/system/network.nix @@ -4,22 +4,24 @@ let cfg = config.nomarchy.system; in { - networking.networkmanager.enable = true; - - networking.networkmanager.wifi.powersave = cfg.wifi.powersave; + networking.networkmanager.enable = lib.mkDefault true; + + networking.networkmanager.wifi.powersave = lib.mkDefault cfg.wifi.powersave; # DNS Configuration - networking.nameservers = if cfg.dns == "Cloudflare" then [ "1.1.1.1" "1.0.0.1" ] - else if cfg.dns == "Google" then [ "8.8.8.8" "8.8.4.4" ] - else if cfg.dns == "Custom" then cfg.customDns - else []; # DHCP lets NM handle it + networking.nameservers = lib.mkDefault ( + if cfg.dns == "Cloudflare" then [ "1.1.1.1" "1.0.0.1" ] + else if cfg.dns == "Google" then [ "8.8.8.8" "8.8.4.4" ] + else if cfg.dns == "Custom" then cfg.customDns + else [] # DHCP lets NM handle it + ); services.resolved = { - enable = cfg.dns != "DHCP"; - dnssec = "allow-downgrade"; - domains = [ "~." ]; - fallbackDns = [ "9.9.9.9" "149.112.112.112" ]; - extraConfig = '' + enable = lib.mkDefault (cfg.dns != "DHCP"); + dnssec = lib.mkDefault "allow-downgrade"; + domains = lib.mkDefault [ "~." ]; + fallbackDns = lib.mkDefault [ "9.9.9.9" "149.112.112.112" ]; + extraConfig = lib.mkDefault '' DNSOverTLS=opportunistic ''; }; diff --git a/modules/system/plymouth.nix b/modules/system/plymouth.nix index b3b130d..1c8f51a 100644 --- a/modules/system/plymouth.nix +++ b/modules/system/plymouth.nix @@ -1,4 +1,4 @@ -{ config, pkgs, ... }: +{ config, pkgs, lib, ... }: let nomarchy-plymouth = pkgs.stdenv.mkDerivation { @@ -16,15 +16,15 @@ let }; in { - boot.initrd.systemd.enable = true; - boot.initrd.verbose = false; - console.earlySetup = true; - boot.consoleLogLevel = 0; + boot.initrd.systemd.enable = lib.mkDefault true; + boot.initrd.verbose = lib.mkDefault false; + console.earlySetup = lib.mkDefault true; + boot.consoleLogLevel = lib.mkDefault 0; boot.plymouth = { - enable = true; - themePackages = [ nomarchy-plymouth ]; - theme = "nomarchy"; + enable = lib.mkDefault true; + themePackages = lib.mkDefault [ nomarchy-plymouth ]; + theme = lib.mkDefault "nomarchy"; }; - boot.kernelParams = [ "quiet" "splash" "loglevel=3" "rd.systemd.show_status=false" "rd.udev.log_level=3" "udev.log_priority=3" "boot.shell_on_fail" ]; + boot.kernelParams = lib.mkDefault [ "quiet" "splash" "loglevel=3" "rd.systemd.show_status=false" "rd.udev.log_level=3" "udev.log_priority=3" "boot.shell_on_fail" ]; } diff --git a/modules/system/sddm.nix b/modules/system/sddm.nix index 0b1b676..7199b8a 100644 --- a/modules/system/sddm.nix +++ b/modules/system/sddm.nix @@ -18,11 +18,11 @@ let }; in { - services.xserver.enable = true; + services.xserver.enable = lib.mkDefault true; services.displayManager.sddm = { - enable = true; - wayland.enable = true; - theme = "nomarchy"; + enable = lib.mkDefault true; + wayland.enable = lib.mkDefault true; + theme = lib.mkDefault "nomarchy"; }; services.displayManager.defaultSession = lib.mkDefault "hyprland-uwsm"; @@ -32,12 +32,12 @@ in user = lib.mkDefault "nomarchy"; }; - environment.systemPackages = [ nomarchy-sddm-theme ]; + environment.systemPackages = lib.mkDefault [ nomarchy-sddm-theme ]; # Enable Hyprland system-level dependencies programs.hyprland = { - enable = true; - withUWSM = true; + enable = lib.mkDefault true; + withUWSM = lib.mkDefault true; }; - programs.uwsm.enable = true; + programs.uwsm.enable = lib.mkDefault true; } diff --git a/modules/system/state.nix b/modules/system/state.nix index dafb77f..ae7cdf4 100644 --- a/modules/system/state.nix +++ b/modules/system/state.nix @@ -1,16 +1,8 @@ { lib, ... }: let - stateFile = "/etc/nixos/state.json"; - - # Helper to read state from a file, with a default - readState = file: default: - if builtins.pathExists file then - builtins.fromJSON (builtins.readFile file) - else - default; - - systemState = readState stateFile {}; + nomarchyLib = import ../lib { inherit lib; }; + systemState = nomarchyLib.readSystemState; in { config.nomarchy.system = { diff --git a/modules/system/systemd.nix b/modules/system/systemd.nix index 3596690..510c9b7 100644 --- a/modules/system/systemd.nix +++ b/modules/system/systemd.nix @@ -1,9 +1,9 @@ { config, pkgs, lib, ... }: { - systemd.settings.Manager.DefaultTimeoutStopSec = "5s"; - - systemd.services."user@".serviceConfig.TimeoutStopSec = "5s"; + systemd.settings.Manager.DefaultTimeoutStopSec = lib.mkDefault "5s"; + + systemd.services."user@".serviceConfig.TimeoutStopSec = lib.mkDefault "5s"; powerManagement.powerDownCommands = '' # --- force-igpu ---