refactor: implement component-based architecture for enhanced maintainability
- Reorganize directory structure into core/, features/, and themes/ - Colocate application Nix logic, configs, scripts, and theme overrides - Implement 'Inversion of Control' for theming: apps now pull theme-specific layouts - Update flake.nix and shared library paths to match the new structure - Document the new Feature-Centric architecture in README.md
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Returns the name of the current monospace font being used by extracting it from the Waybar stylesheet.
|
||||
# This can be changed using nomarchy-font-set.
|
||||
|
||||
grep -oP 'font-family:\s*["'\'']?\K[^;"'\'']+' ~/.config/waybar/style.css | head -n1
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Returns a list of all the monospace fonts available on the system that can be set using nomarchy-font-set.
|
||||
|
||||
fc-list :spacing=100 -f "%{family[0]}\n" | grep -v -i -E 'emoji|signwriting|nomarchy' | sort -u
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Set the system-wide monospace font that should be used by the terminal, hyprlock, waybar, swayosd, etc.
|
||||
# Declarative version for Nomarchy NixOS.
|
||||
|
||||
font_name="$1"
|
||||
|
||||
if [[ -z $font_name ]]; then
|
||||
echo "Usage: nomarchy-font-set <font-name>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
STATE_DIR="$HOME/.config/nomarchy"
|
||||
STATE_FILE="$STATE_DIR/state.json"
|
||||
|
||||
mkdir -p "$STATE_DIR"
|
||||
[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE"
|
||||
|
||||
if fc-list | grep -iq "$font_name"; then
|
||||
TMP_JSON=$(mktemp)
|
||||
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"
|
||||
fi
|
||||
|
||||
nomarchy-hook font-set "$font_name"
|
||||
else
|
||||
echo "Font '$font_name' not found."
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Display a "Done!" message with a spinner and wait for user to press any key.
|
||||
# Used by various install scripts to indicate completion.
|
||||
|
||||
echo
|
||||
gum spin --spinner "globe" --title "Done! Press any key to close..." -- bash -c 'read -n 1 -s'
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Display the Nomarchy logo in the terminal using green color.
|
||||
# Used by various presentation scripts to show branding.
|
||||
|
||||
clear
|
||||
echo -e "\033[32m"
|
||||
cat < ~/.config/nomarchy/branding/logo.txt
|
||||
echo -e "\033[0m"
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
CURRENT_THEME_NAME=$(cat "$HOME/.config/nomarchy/current/theme.name")
|
||||
THEME_USER_BACKGROUNDS="$HOME/.config/nomarchy/backgrounds/$CURRENT_THEME_NAME"
|
||||
|
||||
mkdir -p "$THEME_USER_BACKGROUNDS"
|
||||
nautilus "$THEME_USER_BACKGROUNDS"
|
||||
@@ -1,57 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Cycles through the background images available for the current theme.
|
||||
# Declarative + Hybrid (instant swww) for Nomarchy NixOS.
|
||||
|
||||
STATE_DIR="$HOME/.config/nomarchy"
|
||||
STATE_FILE="$STATE_DIR/state.json"
|
||||
mkdir -p "$STATE_DIR"
|
||||
[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE"
|
||||
|
||||
THEME_NAME=$(jq -r '.theme // "nord"' "$STATE_FILE")
|
||||
|
||||
# Resolve themes directory (Built-in from Nix store via Home Manager, or user extra)
|
||||
if [ -d "$HOME/.config/nomarchy/themes/$THEME_NAME" ]; then
|
||||
THEMES_DIR="$HOME/.config/nomarchy/themes"
|
||||
else
|
||||
THEMES_DIR="$HOME/.local/share/nomarchy/themes"
|
||||
fi
|
||||
|
||||
BG_DIR="$THEMES_DIR/$THEME_NAME/backgrounds"
|
||||
|
||||
if [ ! -d "$BG_DIR" ]; then
|
||||
notify-send "No background directory found for theme $THEME_NAME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mapfile -t BACKGROUNDS < <(ls "$BG_DIR" | sort)
|
||||
TOTAL=${#BACKGROUNDS[@]}
|
||||
|
||||
if (( TOTAL == 0 )); then
|
||||
notify-send "No backgrounds found in $BG_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CURRENT_BG=$(jq -r '.wallpaper' "$STATE_FILE")
|
||||
INDEX=-1
|
||||
for i in "${!BACKGROUNDS[@]}"; do
|
||||
if [[ "$BG_DIR/${BACKGROUNDS[$i]}" == "$CURRENT_BG" ]]; then
|
||||
INDEX=$i
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
NEXT_INDEX=$(((INDEX + 1) % TOTAL))
|
||||
NEW_BG="$BG_DIR/${BACKGROUNDS[$NEXT_INDEX]}"
|
||||
|
||||
TMP_JSON=$(mktemp)
|
||||
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
|
||||
swww img "$NEW_BG" --transition-type outer --transition-pos 0.85,0.97 --transition-step 90
|
||||
else
|
||||
swww init && swww img "$NEW_BG"
|
||||
fi
|
||||
|
||||
echo "Background set to $NEW_BG declaratively."
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Sets the specified image as the current background
|
||||
|
||||
if [[ -z $1 ]]; then
|
||||
echo "Usage: nomarchy-theme-bg-set <path-to-image>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BACKGROUND="$1"
|
||||
CURRENT_BACKGROUND_LINK="$HOME/.config/nomarchy/current/background"
|
||||
|
||||
# Create symlink to the new background
|
||||
ln -nsf "$BACKGROUND" "$CURRENT_BACKGROUND_LINK"
|
||||
|
||||
# Kill existing swaybg and start new one
|
||||
pkill -x swaybg
|
||||
setsid uwsm-app -- swaybg -i "$CURRENT_BACKGROUND_LINK" -m fill >/dev/null 2>&1 &
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
THEME_NAME_PATH="$HOME/.config/nomarchy/current/theme.name"
|
||||
|
||||
if [[ -f $THEME_NAME_PATH ]]; then
|
||||
cat $THEME_NAME_PATH | sed -E 's/(^|-)([a-z])/\1\u\2/g; s/-/ /g'
|
||||
else
|
||||
echo "Unknown"
|
||||
fi
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# nomarchy-theme-install: Install a new theme from a git repo for Nomarchy
|
||||
# Usage: nomarchy-theme-install <git-repo-url>
|
||||
|
||||
if [[ -z $1 ]]; then
|
||||
echo -e "\e[32mSee https://manuals.omamix.org/2/the-nomarchy-manual/90/extra-themes\n\e[0m"
|
||||
REPO_URL=$(gum input --placeholder="Git repo URL for theme" --header="")
|
||||
else
|
||||
REPO_URL="$1"
|
||||
fi
|
||||
|
||||
if [[ -z $REPO_URL ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
THEMES_DIR="$HOME/.config/nomarchy/themes"
|
||||
THEME_NAME=$(basename "$REPO_URL" .git | sed -E 's/^nomarchy-//; s/-theme$//')
|
||||
THEME_PATH="$THEMES_DIR/$THEME_NAME"
|
||||
|
||||
# Remove existing theme if present
|
||||
if [[ -d $THEME_PATH ]]; then
|
||||
rm -rf "$THEME_PATH"
|
||||
fi
|
||||
|
||||
# Clone the repo directly to ~/.config/nomarchy/themes
|
||||
if ! git clone "$REPO_URL" "$THEME_PATH"; then
|
||||
echo "Error: Failed to clone theme repo."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Apply the new theme with nomarchy-theme-set
|
||||
nomarchy-theme-set $THEME_NAME
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
{
|
||||
find ~/.config/nomarchy/themes/ -mindepth 1 -maxdepth 1 \( -type d -o -type l \) -printf '%f\n'
|
||||
find "$NOMARCHY_PATH/assets/themes/" -mindepth 1 -maxdepth 1 -type d -printf '%f\n'
|
||||
} | sort -u | while read -r name; do
|
||||
echo "$name" | sed -E 's/(^|-)([a-z])/\1\u\2/g; s/-/ /g'
|
||||
done
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Refresh the current theme from its templates.
|
||||
|
||||
THEME_NAME_PATH="$HOME/.config/nomarchy/current/theme.name"
|
||||
|
||||
if [[ -f $THEME_NAME_PATH ]]; then
|
||||
nomarchy-theme-set "$(cat $THEME_NAME_PATH)"
|
||||
fi
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# nomarchy-theme-remove: Remove a theme from Nomarchy by name
|
||||
# Usage: nomarchy-theme-remove <theme-name>
|
||||
|
||||
if [[ -z $1 ]]; then
|
||||
mapfile -t extra_themes < <(find ~/.config/nomarchy/themes -mindepth 1 -maxdepth 1 -type d ! -xtype l -printf '%f\n')
|
||||
|
||||
if (( ${#extra_themes[@]} > 0 )); then
|
||||
THEME_NAME=$(printf '%s\n' "${extra_themes[@]}" | sort | gum choose --header="Remove extra theme")
|
||||
else
|
||||
echo "No extra themes installed."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
THEME_NAME="$1"
|
||||
fi
|
||||
|
||||
THEMES_DIR="$HOME/.config/nomarchy/themes"
|
||||
CURRENT_DIR="$HOME/.config/nomarchy/current"
|
||||
THEME_PATH="$THEMES_DIR/$THEME_NAME"
|
||||
|
||||
# Ensure a theme was set
|
||||
if [[ -z $THEME_NAME ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if theme exists before attempting removal
|
||||
if [[ ! -d $THEME_PATH ]]; then
|
||||
echo "Error: Theme '$THEME_NAME' not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Now remove the theme directory for THEME_NAME
|
||||
rm -rf "$THEME_PATH"
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Set the system theme declaratively.
|
||||
# Usage: nomarchy-theme-set <theme-name>
|
||||
|
||||
THEME_NAME="$1"
|
||||
|
||||
if [[ -z $THEME_NAME ]]; then
|
||||
echo "Usage: nomarchy-theme-set <theme-name>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
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)
|
||||
if [ -d "$HOME/.config/nomarchy/themes/$THEME_NAME" ]; then
|
||||
THEMES_DIR="$HOME/.config/nomarchy/themes"
|
||||
else
|
||||
THEMES_DIR="$HOME/.local/share/nomarchy/themes"
|
||||
fi
|
||||
|
||||
mkdir -p "$STATE_DIR"
|
||||
[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE"
|
||||
|
||||
if [ ! -d "$THEMES_DIR/$THEME_NAME" ] && ! [[ "$THEME_NAME" == "nord" ]]; then
|
||||
echo "Theme '$THEME_NAME' not found in $THEMES_DIR"
|
||||
fi
|
||||
|
||||
TMP_JSON=$(mktemp)
|
||||
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 --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
|
||||
BG_DIR="$THEMES_DIR/$THEME_NAME/backgrounds"
|
||||
if [ -d "$BG_DIR" ]; then
|
||||
BG=$(ls "$BG_DIR" | head -n 1)
|
||||
if [ -n "$BG" ]; then
|
||||
TMP_JSON=$(mktemp)
|
||||
jq --arg wp "$BG_DIR/$BG" '.wallpaper = $wp' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Theme set to $THEME_NAME. Applying changes with env-update..."
|
||||
rm -rf "$HOME/.config/nomarchy/current/theme"
|
||||
env-update
|
||||
|
||||
nomarchy-theme-set-templates
|
||||
|
||||
nomarchy-hook theme-set "$THEME_NAME"
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
nomarchy-theme-set-keyboard-asus-rog
|
||||
nomarchy-theme-set-keyboard-f16
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
ASUSCTL_THEME=~/.config/nomarchy/current/theme/keyboard.rgb
|
||||
|
||||
if nomarchy-cmd-present asusctl; then
|
||||
asusctl aura effect static -c $(sed 's/^#//' $ASUSCTL_THEME)
|
||||
fi
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
FRAMEWORK16_THEME=~/.config/nomarchy/current/theme/keyboard.rgb
|
||||
|
||||
if nomarchy-cmd-present qmk_hid && [[ -f $FRAMEWORK16_THEME ]]; then
|
||||
hex=$(cat "$FRAMEWORK16_THEME")
|
||||
hex="${hex#\#}"
|
||||
|
||||
# Convert hex to QMK HSV (0-255 scale) using Python's colorsys
|
||||
read -r h s <<< $(python3 -c "
|
||||
import colorsys
|
||||
r, g, b = int('$hex'[:2],16)/255, int('$hex'[2:4],16)/255, int('$hex'[4:6],16)/255
|
||||
h, s, v = colorsys.rgb_to_hsv(r, g, b)
|
||||
print(int(h * 255), int(s * 255))
|
||||
")
|
||||
|
||||
qmk_hid via --rgb-effect 1 2>/dev/null
|
||||
qmk_hid via --rgb-hue "$h" 2>/dev/null
|
||||
qmk_hid via --rgb-saturation "$s" 2>/dev/null
|
||||
qmk_hid via --rgb-brightness 100 2>/dev/null
|
||||
qmk_hid via --save 2>/dev/null
|
||||
fi
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Sync Nomarchy theme to all Obsidian vaults
|
||||
|
||||
CURRENT_THEME_DIR="$HOME/.config/nomarchy/current/theme"
|
||||
|
||||
[[ -f $CURRENT_THEME_DIR/obsidian.css ]] || exit 0
|
||||
|
||||
jq -r '.vaults | values[].path' ~/.config/obsidian/obsidian.json 2>/dev/null | while read -r vault_path; do
|
||||
[[ -d $vault_path/.obsidian ]] || continue
|
||||
|
||||
theme_dir="$vault_path/.obsidian/themes/Nomarchy"
|
||||
mkdir -p "$theme_dir"
|
||||
|
||||
[[ -f $theme_dir/manifest.json ]] || cat >"$theme_dir/manifest.json" <<'EOF'
|
||||
{
|
||||
"name": "Nomarchy",
|
||||
"version": "1.0.0",
|
||||
"minAppVersion": "0.16.0",
|
||||
"description": "Automatically syncs with your current Nomarchy system theme colors and fonts",
|
||||
"author": "Nomarchy",
|
||||
"authorUrl": "https://nomarchy.org"
|
||||
}
|
||||
EOF
|
||||
|
||||
cp "$CURRENT_THEME_DIR/obsidian.css" "$theme_dir/theme.css"
|
||||
done
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
TEMPLATES_DIR="$NOMARCHY_PATH/assets/themed"
|
||||
USER_TEMPLATES_DIR="$HOME/.config/nomarchy/themed"
|
||||
NEXT_THEME_DIR="$HOME/.config/nomarchy/current/theme"
|
||||
COLORS_FILE="$NEXT_THEME_DIR/colors.toml"
|
||||
|
||||
# Convert hex color to decimal RGB (e.g., "#1e1e2e" -> "30,30,46")
|
||||
hex_to_rgb() {
|
||||
local hex="${1#\#}"
|
||||
printf "%d,%d,%d" "0x${hex:0:2}" "0x${hex:2:2}" "0x${hex:4:2}"
|
||||
}
|
||||
|
||||
# Only generate dynamic templates for themes with a colors.toml definition
|
||||
if [[ -f $COLORS_FILE ]]; then
|
||||
sed_script=$(mktemp)
|
||||
|
||||
while IFS='=' read -r key value; do
|
||||
key="${key//[\"\' ]/}" # strip quotes and spaces from key
|
||||
[[ $key && $key != \#* ]] || continue # skip empty lines and comments
|
||||
value="${value#*[\"\']}"
|
||||
value="${value%%[\"\']*}" # extract value between quotes (ignores inline comments)
|
||||
|
||||
printf 's|{{ %s }}|%s|g\n' "$key" "$value" # {{ key }} -> value
|
||||
printf 's|{{ %s_strip }}|%s|g\n' "$key" "${value#\#}" # {{ key_strip }} -> value without leading #
|
||||
if [[ $value =~ ^# ]]; then
|
||||
rgb=$(hex_to_rgb "$value")
|
||||
echo "s|{{ ${key}_rgb }}|${rgb}|g"
|
||||
fi
|
||||
done <"$COLORS_FILE" >"$sed_script"
|
||||
|
||||
shopt -s nullglob
|
||||
|
||||
# Process user templates first, then built-in templates (user overrides built-in)
|
||||
for tpl in "$USER_TEMPLATES_DIR"/*.tpl "$TEMPLATES_DIR"/*.tpl; do
|
||||
filename=$(basename "$tpl" .tpl)
|
||||
output_path="$NEXT_THEME_DIR/$filename"
|
||||
|
||||
# Don't overwrite configs already exists in the output directory (copied from theme specific folder)
|
||||
if [[ ! -f $output_path ]]; then
|
||||
sed -f "$sed_script" "$tpl" >"$output_path"
|
||||
fi
|
||||
done
|
||||
|
||||
rm "$sed_script"
|
||||
fi
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Nomarchy VS Code Theme Setter
|
||||
# This script only updates the global state.json.
|
||||
# Home Manager (modules/home/vscode.nix) handles the declarative settings injection.
|
||||
|
||||
STATE_DIR="$HOME/.config/nomarchy"
|
||||
STATE_FILE="$STATE_DIR/state.json"
|
||||
|
||||
mkdir -p "$STATE_DIR"
|
||||
[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE"
|
||||
|
||||
# Theme is already set in state.json by nomarchy-theme-set.
|
||||
# This script is now mostly a placeholder to maintain the same workflow,
|
||||
# triggering an env-update if needed to apply the declarative changes.
|
||||
|
||||
if [[ $NOMARCHY_TOGGLE_SKIP_VSCODE_THEME != "true" ]]; then
|
||||
# We trigger env-update to apply the new VSCode theme declaratively.
|
||||
env-update
|
||||
fi
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
for dir in ~/.config/nomarchy/themes/*/; do
|
||||
if [[ -d $dir ]] && [[ ! -L ${dir%/} ]] && [[ -d $dir/.git ]]; then
|
||||
echo "Updating: $(basename "$dir")"
|
||||
git -C "$dir" pull
|
||||
fi
|
||||
done
|
||||
@@ -1,26 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Toggles the nightlight (hyprsunset).
|
||||
# Hybrid: updates state.json and provides instant feedback.
|
||||
|
||||
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 [[ $NOMARCHY_TOGGLE_NIGHTLIGHT == "false" ]]; then
|
||||
NEW_VALUE="true"
|
||||
hyprctl dispatch exec hyprsunset --temperature 4000
|
||||
notify-send -u low " Nightlight enabled"
|
||||
else
|
||||
NEW_VALUE="false"
|
||||
pkill hyprsunset
|
||||
notify-send -u low " Nightlight disabled"
|
||||
fi
|
||||
|
||||
TMP_JSON=$(mktemp)
|
||||
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."
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the fastfetch TUI that gives information about the current system.
|
||||
|
||||
exec nomarchy-launch-or-focus-tui "bash -c 'fastfetch; read -n 1 -s'"
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the Nomarchy audio controls TUI (provided by wiremix).
|
||||
|
||||
nomarchy-launch-or-focus-tui wiremix
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the Nomarchy bluetooth controls TUI (provided by bluetui).
|
||||
# Also attempts to unblock bluetooth service if rfkill had blocked it.
|
||||
|
||||
rfkill unblock bluetooth
|
||||
exec nomarchy-launch-or-focus-tui bluetui
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the default browser as determined by xdg-settings.
|
||||
# Automatically converts --private into the correct flag for the given browser.
|
||||
|
||||
default_browser=$(xdg-settings get default-web-browser)
|
||||
browser_exec=$(sed -n 's/^Exec=\([^ ]*\).*/\1/p' {~/.local,~/.nix-profile,/usr}/share/applications/$default_browser 2>/dev/null | head -1)
|
||||
|
||||
if $browser_exec --help | grep -q MOZ_LOG; then
|
||||
private_flag="--private-window"
|
||||
elif [[ $browser_exec =~ edge ]]; then
|
||||
private_flag="--inprivate"
|
||||
else
|
||||
private_flag="--incognito"
|
||||
fi
|
||||
|
||||
exec setsid uwsm-app -- "$browser_exec" "${@/--private/$private_flag}"
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the default editor as determined by $EDITOR (set via ~/.config/uwsm/default) (or nvim if missing).
|
||||
# Starts suitable editors in a terminal window and otherwise as a regular application.
|
||||
|
||||
nomarchy-cmd-present "$EDITOR" || EDITOR=nvim
|
||||
|
||||
case "$EDITOR" in
|
||||
nvim | vim | nano | micro | hx | helix | fresh)
|
||||
exec nomarchy-launch-tui "$EDITOR" "$@"
|
||||
;;
|
||||
*)
|
||||
exec setsid uwsm-app -- "$EDITOR" "$@"
|
||||
;;
|
||||
esac
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch a floating terminal with the Nomarchy logo presentation, then execute the command passed in, and finally end with the nomarchy-show-done presentation.
|
||||
# Used by actions such as Update System.
|
||||
|
||||
cmd="$*"
|
||||
exec setsid uwsm-app -- xdg-terminal-exec --app-id=org.nomarchy.terminal --title=Nomarchy -e bash -c "nomarchy-show-logo; $cmd; if (( \$? != 130 )); then nomarchy-show-done; fi"
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch or focus on a given command identified by the passed in window-pattern.
|
||||
# Use by some default bindings, like the one for Spotify, to ensure there is only one instance of the application open.
|
||||
|
||||
if (($# == 0)); then
|
||||
echo "Usage: nomarchy-launch-or-focus [window-pattern] [launch-command]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WINDOW_PATTERN="$1"
|
||||
LAUNCH_COMMAND="${2:-"uwsm-app -- $WINDOW_PATTERN"}"
|
||||
WINDOW_ADDRESS=$(hyprctl clients -j | jq -r --arg p "$WINDOW_PATTERN" '.[]|select((.class|test("\\b" + $p + "\\b";"i")) or (.title|test("\\b" + $p + "\\b";"i")))|.address' | head -n1)
|
||||
|
||||
if [[ -n $WINDOW_ADDRESS ]]; then
|
||||
hyprctl dispatch focuswindow "address:$WINDOW_ADDRESS"
|
||||
else
|
||||
eval exec setsid $LAUNCH_COMMAND
|
||||
fi
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch or focus on a given TUI identified by the passed in as the command.
|
||||
# Use by commands like nomarchy-launch-wifi to ensure there is only one wifi configuration screen open.
|
||||
|
||||
APP_ID="org.nomarchy.$(basename "$1")"
|
||||
LAUNCH_COMMAND="nomarchy-launch-tui $@"
|
||||
|
||||
exec nomarchy-launch-or-focus "$APP_ID" "$LAUNCH_COMMAND"
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch or focus on a given web app identified by the window-pattern.
|
||||
# Use by some default bindings, like the one for WhatsApp, to ensure there is only one instance of the application open.
|
||||
|
||||
if (($# == 0)); then
|
||||
echo "Usage: nomarchy-launch-or-focus-webapp [window-pattern] [url-and-flags...]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WINDOW_PATTERN="$1"
|
||||
shift
|
||||
LAUNCH_COMMAND="nomarchy-launch-webapp $@"
|
||||
|
||||
exec nomarchy-launch-or-focus "$WINDOW_PATTERN" "$LAUNCH_COMMAND"
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the Nomarchy screensaver in the default terminal on the system with the correct font configuration.
|
||||
|
||||
# Exit early if we don't have the tte show
|
||||
if ! command -v tte &>/dev/null; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Exit early if screensave is already running
|
||||
pgrep -f org.nomarchy.screensaver && exit 0
|
||||
|
||||
# Allow screensaver to be turned off but also force started
|
||||
# Skip if screensaver is disabled in configuration
|
||||
if [[ $NNOMARCHY_TOGGLE_SCREENSAVER == "false" ]] && [[ $1 != "force" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
# Silently quit Walker on overlay
|
||||
walker -q
|
||||
|
||||
focused=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true).name')
|
||||
terminal=$(xdg-terminal-exec --print-id)
|
||||
|
||||
for m in $(hyprctl monitors -j | jq -r '.[] | .name'); do
|
||||
hyprctl dispatch focusmonitor $m
|
||||
|
||||
case $terminal in
|
||||
*Alacritty*)
|
||||
hyprctl dispatch exec -- \
|
||||
alacritty --class=org.nomarchy.screensaver \
|
||||
--config-file ~/.local/share/nomarchy/default/alacritty/screensaver.toml \
|
||||
-e nomarchy-cmd-screensaver
|
||||
;;
|
||||
*ghostty*)
|
||||
hyprctl dispatch exec -- \
|
||||
ghostty --class=org.nomarchy.screensaver \
|
||||
--config-file=~/.local/share/nomarchy/default/ghostty/screensaver \
|
||||
--font-size=18 \
|
||||
-e nomarchy-cmd-screensaver
|
||||
;;
|
||||
*kitty*)
|
||||
hyprctl dispatch exec -- \
|
||||
kitty --class=org.nomarchy.screensaver \
|
||||
--override font_size=18 \
|
||||
--override window_padding_width=0 \
|
||||
-e nomarchy-cmd-screensaver
|
||||
;;
|
||||
*)
|
||||
notify-send -u low "✋ Screensaver only runs in Alacritty, Ghostty, or Kitty"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
hyprctl dispatch focusmonitor $focused
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the TUI command passed in as an argument in the default terminal with an org.nomarchy.COMMAND app id for styling.
|
||||
|
||||
exec setsid uwsm-app -- xdg-terminal-exec --app-id=org.nomarchy.$(basename $1) -e "$1" "${@:2}"
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the Walker application launcher while ensuring that it's data provider (called elephant) is running first.
|
||||
|
||||
# Ensure elephant is running before launching walker
|
||||
if ! pgrep -x elephant > /dev/null; then
|
||||
setsid uwsm-app -- elephant &
|
||||
fi
|
||||
|
||||
# Ensure walker service is running
|
||||
if ! pgrep -f "walker --gapplication-service" > /dev/null; then
|
||||
setsid uwsm-app -- walker --gapplication-service &
|
||||
fi
|
||||
|
||||
exec walker --width 644 --maxheight 300 --minheight 300 "$@"
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the passed in URL as a web app in the default browser (or chromium if the default doesn't support --app).
|
||||
|
||||
browser=$(xdg-settings get default-web-browser)
|
||||
|
||||
case $browser in
|
||||
google-chrome* | brave-browser* | microsoft-edge* | opera* | vivaldi* | helium*) ;;
|
||||
*) browser="chromium.desktop" ;;
|
||||
esac
|
||||
|
||||
exec setsid uwsm-app -- $(sed -n 's/^Exec=\([^ ]*\).*/\1/p' {~/.local,~/.nix-profile,/usr}/share/applications/$browser 2>/dev/null | head -1) --app="$1" "${@:2}"
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Launch the Nomarchy wifi controls (provided by the Impala TUI).
|
||||
# Attempts to unblock the wifi service first in case it should be been blocked.
|
||||
|
||||
rfkill unblock wifi
|
||||
nomarchy-launch-or-focus-tui impala
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Restart an application by killing it and relaunching via uwsm.
|
||||
# Usage: nomarchy-restart-app <application-name> [application-args...]
|
||||
|
||||
pkill -x $1
|
||||
setsid uwsm-app -- "$@" >/dev/null 2>&1 &
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Reload btop configuration (used by the Nomarchy theme switching).
|
||||
|
||||
pkill -SIGUSR2 btop
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Reload opencode configuration (used by the Nomarchy theme switching).
|
||||
|
||||
if pgrep -x opencode >/dev/null; then
|
||||
killall -SIGUSR2 opencode
|
||||
fi
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ -f ~/.config/alacritty/alacritty.toml ]]; then
|
||||
touch ~/.config/alacritty/alacritty.toml
|
||||
fi
|
||||
|
||||
if pgrep -x kitty >/dev/null; then
|
||||
killall -SIGUSR1 kitty >/dev/null
|
||||
fi
|
||||
|
||||
if pgrep -x ghostty >/dev/null; then
|
||||
killall -SIGUSR2 ghostty
|
||||
fi
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Restart tmux if running with the latest configuration
|
||||
|
||||
if pgrep -x tmux; then
|
||||
tmux source-file ~/.config/tmux/tmux.conf
|
||||
fi
|
||||
@@ -1,59 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
if (( $# != 4 )); then
|
||||
echo -e "\e[32mLet's create a TUI shortcut you can start with the app launcher.\n\e[0m"
|
||||
APP_NAME=$(gum input --prompt "Name> " --placeholder "My TUI")
|
||||
APP_EXEC=$(gum input --prompt "Launch Command> " --placeholder "lazydocker or bash -c 'dust; read -n 1 -s'")
|
||||
WINDOW_STYLE=$(gum choose --header "Window style" float tile)
|
||||
ICON_URL=$(gum input --prompt "Icon URL> " --placeholder "See https://dashboardicons.com (must use PNG or SVG!)")
|
||||
else
|
||||
APP_NAME="$1"
|
||||
APP_EXEC="$2"
|
||||
WINDOW_STYLE="$3"
|
||||
ICON_URL="$4"
|
||||
fi
|
||||
|
||||
if [[ -z $APP_NAME || -z $APP_EXEC || -z $ICON_URL ]]; then
|
||||
echo "You must set app name, app command, and icon URL!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ICON_DIR="$HOME/.local/share/applications/icons"
|
||||
DESKTOP_FILE="$HOME/.local/share/applications/$APP_NAME.desktop"
|
||||
|
||||
if [[ ! $ICON_URL =~ ^https?:// ]] && [[ -f $ICON_URL ]]; then
|
||||
ICON_PATH="$ICON_URL"
|
||||
else
|
||||
ICON_PATH="$ICON_DIR/$APP_NAME.png"
|
||||
mkdir -p "$ICON_DIR"
|
||||
if ! curl -sL -o "$ICON_PATH" "$ICON_URL"; then
|
||||
echo "Error: Failed to download icon."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $WINDOW_STYLE == "float" ]]; then
|
||||
APP_CLASS="TUI.float"
|
||||
else
|
||||
APP_CLASS="TUI.tile"
|
||||
fi
|
||||
|
||||
cat >"$DESKTOP_FILE" <<EOF
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=$APP_NAME
|
||||
Comment=$APP_NAME
|
||||
Exec=xdg-terminal-exec --app-id=$APP_CLASS -e $APP_EXEC
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=$ICON_PATH
|
||||
StartupNotify=true
|
||||
EOF
|
||||
|
||||
chmod +x "$DESKTOP_FILE"
|
||||
|
||||
if (( $# != 4 )); then
|
||||
echo -e "You can now find $APP_NAME using the app launcher (SUPER + SPACE)\n"
|
||||
fi
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
ICON_DIR="$HOME/.local/share/applications/icons"
|
||||
DESKTOP_DIR="$HOME/.local/share/applications/"
|
||||
|
||||
if (( $# == 0 )); then
|
||||
# Find all TUIs
|
||||
while IFS= read -r -d '' file; do
|
||||
if grep -qE '^Exec=.*(\$TERMINAL|xdg-terminal-exec).*-e' "$file"; then
|
||||
TUIS+=("$(basename "${file%.desktop}")")
|
||||
fi
|
||||
done < <(find "$DESKTOP_DIR" -name '*.desktop' -print0)
|
||||
|
||||
if ((${#TUIS[@]})); then
|
||||
IFS=$'\n' SORTED_TUIS=($(sort <<<"${TUIS[*]}"))
|
||||
unset IFS
|
||||
APP_NAMES_STRING=$(gum choose --no-limit --header "Select TUI to remove..." --selected-prefix="✗ " "${SORTED_TUIS[@]}")
|
||||
# Convert newline-separated string to array
|
||||
APP_NAMES=()
|
||||
while IFS= read -r line; do
|
||||
[[ -n $line ]] && APP_NAMES+=("$line")
|
||||
done <<< "$APP_NAMES_STRING"
|
||||
else
|
||||
echo "No TUIs to remove."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Use array to preserve spaces in app names
|
||||
APP_NAMES=("$@")
|
||||
fi
|
||||
|
||||
if (( ${#APP_NAMES[@]} == 0 )); then
|
||||
echo "You must provide TUI names."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for APP_NAME in "${APP_NAMES[@]}"; do
|
||||
rm -f "$DESKTOP_DIR/$APP_NAME.desktop"
|
||||
rm -f "$ICON_DIR/$APP_NAME.png"
|
||||
echo "Removed $APP_NAME"
|
||||
done
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Remove all TUIs installed via nomarchy-tui-install.
|
||||
# Identifies TUIs by their Exec pattern (xdg-terminal-exec --app-id=TUI.).
|
||||
|
||||
set -e
|
||||
|
||||
APP_DIR="${1:-$HOME/.local/share/applications}"
|
||||
ICON_DIR="$HOME/.local/share/applications/icons"
|
||||
|
||||
echo "Scanning for TUIs in $APP_DIR..."
|
||||
|
||||
tui_desktop_files=()
|
||||
while IFS= read -r -d '' file; do
|
||||
if grep -q "Exec=xdg-terminal-exec --app-id=TUI\." "$file" 2>/dev/null; then
|
||||
tui_desktop_files+=("$file")
|
||||
fi
|
||||
done < <(find "$APP_DIR" -maxdepth 1 -name "*.desktop" -print0 2>/dev/null)
|
||||
|
||||
if (( ${#tui_desktop_files[@]} == 0 )); then
|
||||
echo "No TUIs found."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for file in "${tui_desktop_files[@]}"; do
|
||||
app_name=$(basename "$file" .desktop)
|
||||
echo "Removing TUI: $app_name"
|
||||
rm -f "$file"
|
||||
rm -f "$ICON_DIR/$app_name.png"
|
||||
done
|
||||
|
||||
if command -v update-desktop-database &>/dev/null; then
|
||||
update-desktop-database "$APP_DIR" &>/dev/null || true
|
||||
fi
|
||||
|
||||
echo "TUIs removed successfully."
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Used by Voxtype waybar module to open config on right click
|
||||
|
||||
exec nomarchy-launch-editor ~/.config/voxtype/config.toml
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Install voxtype and configure it for use.
|
||||
|
||||
if gum confirm "Install Voxtype + AI model (~150MB) to enable dictation?"; then
|
||||
nomarchy-pkg-add wtype voxtype-bin
|
||||
|
||||
# Setup voxtype
|
||||
mkdir -p ~/.config/voxtype
|
||||
cp ~/.config/nomarchy/default/voxtype/config.toml ~/.config/voxtype/
|
||||
|
||||
voxtype setup --download --no-post-install
|
||||
if nomarchy-hw-vulkan; then
|
||||
voxtype setup gpu --enable || true
|
||||
fi
|
||||
voxtype setup systemd
|
||||
|
||||
nomarchy-restart-waybar
|
||||
notify-send " Voxtype Dictation Ready" "Press Super + Ctrl + X to toggle dictation.\nEdit ~/.config/voxtype/config.toml for options." -t 10000
|
||||
fi
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
nomarchy-launch-floating-terminal-with-presentation "voxtype setup model"
|
||||
nomarchy-restart-waybar
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Remove voxtype and its configurations.
|
||||
|
||||
if nomarchy-cmd-present voxtype; then
|
||||
echo "Uninstall Voxtype to remove dictation."
|
||||
|
||||
# Remove services
|
||||
systemctl --user stop voxtype.service 2>/dev/null || true
|
||||
rm -f ~/.config/systemd/user/voxtype*
|
||||
systemctl --user daemon-reload
|
||||
|
||||
# Remove packages and configs
|
||||
nomarchy-pkg-drop wtype voxtype-bin
|
||||
rm -rf ~/.config/voxtype
|
||||
rm -rf ~/.local/share/voxtype
|
||||
else
|
||||
echo "Voxtype was not installed."
|
||||
fi
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if nomarchy-cmd-present voxtype; then
|
||||
voxtype status --follow --extended --format json | while read -r line; do
|
||||
echo "$line" | jq -c '. + {alt: .class}'
|
||||
done
|
||||
else
|
||||
echo '{"alt": "", "tooltip": ""}'
|
||||
fi
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/bin/bash
|
||||
url="$1"
|
||||
web_url="https://app.hey.com"
|
||||
|
||||
# Handle mailto: URLs
|
||||
if [[ $url =~ ^mailto: ]]; then
|
||||
email=$(echo "$url" | sed 's/mailto://')
|
||||
web_url="https://app.hey.com/messages/new?to=$email"
|
||||
fi
|
||||
|
||||
exec nomarchy-launch-webapp "$web_url"
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
url="$1"
|
||||
web_url="https://app.zoom.us/wc/home"
|
||||
|
||||
if [[ $url =~ ^zoom(mtg|us):// ]]; then
|
||||
confno=$(echo "$url" | sed -n 's/.*[?&]confno=\([^&]*\).*/\1/p')
|
||||
|
||||
if [[ -n $confno ]]; then
|
||||
pwd=$(echo "$url" | sed -n 's/.*[?&]pwd=\([^&]*\).*/\1/p')
|
||||
|
||||
if [[ -n $pwd ]]; then
|
||||
web_url="https://app.zoom.us/wc/join/$confno?pwd=$pwd"
|
||||
else
|
||||
web_url="https://app.zoom.us/wc/join/$confno"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
exec nomarchy-launch-webapp "$web_url"
|
||||
@@ -1,89 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
ICON_DIR="$HOME/.local/share/applications/icons"
|
||||
|
||||
if (( $# < 3 )); then
|
||||
echo -e "\e[32mLet's create a new web app you can start with the app launcher.\n\e[0m"
|
||||
APP_NAME=$(gum input --prompt "Name> " --placeholder "My favorite web app")
|
||||
APP_URL=$(gum input --prompt "URL> " --placeholder "https://example.com")
|
||||
if [[ ! $APP_URL =~ ^[a-zA-Z][a-zA-Z0-9+.-]*: ]]; then
|
||||
APP_URL="https://$APP_URL"
|
||||
fi
|
||||
|
||||
# Try to fetch favicon automatically first.
|
||||
FAVICON_URL="https://www.google.com/s2/favicons?domain=${APP_URL}&sz=128"
|
||||
mkdir -p "$ICON_DIR"
|
||||
if curl -fsSL -o "$ICON_DIR/$APP_NAME.png" "$FAVICON_URL" && [[ -s $ICON_DIR/$APP_NAME.png ]]; then
|
||||
ICON_REF="$APP_NAME.png"
|
||||
else
|
||||
ICON_REF=$(gum input --prompt "Icon URL> " --placeholder "Could not fetch favicon automatically. Enter PNG icon URL (see https://dashboardicons.com)")
|
||||
fi
|
||||
|
||||
CUSTOM_EXEC=""
|
||||
MIME_TYPES=""
|
||||
INTERACTIVE_MODE=true
|
||||
else
|
||||
APP_NAME="$1"
|
||||
APP_URL="$2"
|
||||
if [[ ! $APP_URL =~ ^[a-zA-Z][a-zA-Z0-9+.-]*: ]]; then
|
||||
APP_URL="https://$APP_URL"
|
||||
fi
|
||||
ICON_REF="$3"
|
||||
CUSTOM_EXEC="$4" # Optional custom exec command
|
||||
MIME_TYPES="$5" # Optional mime types
|
||||
INTERACTIVE_MODE=false
|
||||
fi
|
||||
|
||||
# Ensure valid execution
|
||||
if [[ -z $APP_NAME || -z $APP_URL ]]; then
|
||||
echo "You must set app name and app URL!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Resolve icon from URL or from a local icon name.
|
||||
mkdir -p "$ICON_DIR"
|
||||
|
||||
if [[ -z $ICON_REF ]]; then
|
||||
ICON_REF="https://www.google.com/s2/favicons?domain=${APP_URL}&sz=128"
|
||||
fi
|
||||
|
||||
if [[ $ICON_REF =~ ^https?:// ]]; then
|
||||
ICON_PATH="$ICON_DIR/$APP_NAME.png"
|
||||
if ! curl -fsSL -o "$ICON_PATH" "$ICON_REF" || [[ ! -s $ICON_PATH ]]; then
|
||||
echo "Error: Failed to download icon."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
ICON_PATH="$ICON_DIR/$ICON_REF"
|
||||
fi
|
||||
|
||||
# Use custom exec if provided, otherwise default behavior
|
||||
EXEC_COMMAND="${CUSTOM_EXEC:-nomarchy-launch-webapp $APP_URL}"
|
||||
|
||||
# Create application .desktop file
|
||||
DESKTOP_FILE="$HOME/.local/share/applications/$APP_NAME.desktop"
|
||||
|
||||
cat >"$DESKTOP_FILE" <<EOF
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=$APP_NAME
|
||||
Comment=$APP_NAME
|
||||
Exec=$EXEC_COMMAND
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=$ICON_PATH
|
||||
StartupNotify=true
|
||||
EOF
|
||||
|
||||
# Add mime types if provided
|
||||
if [[ -n $MIME_TYPES ]]; then
|
||||
echo "MimeType=$MIME_TYPES" >>"$DESKTOP_FILE"
|
||||
fi
|
||||
|
||||
chmod +x "$DESKTOP_FILE"
|
||||
|
||||
if [[ $INTERACTIVE_MODE == "true" ]]; then
|
||||
echo -e "You can now find $APP_NAME using the app launcher (SUPER + SPACE)\n"
|
||||
fi
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
ICON_DIR="$HOME/.local/share/applications/icons"
|
||||
DESKTOP_DIR="$HOME/.local/share/applications/"
|
||||
|
||||
if (( $# == 0 )); then
|
||||
# Find all web apps
|
||||
while IFS= read -r -d '' file; do
|
||||
if grep -q '^Exec=.*\(nomarchy-launch-webapp\|nomarchy-webapp-handler\).*' "$file"; then
|
||||
WEB_APPS+=("$(basename "${file%.desktop}")")
|
||||
fi
|
||||
done < <(find "$DESKTOP_DIR" -name '*.desktop' -print0)
|
||||
|
||||
if ((${#WEB_APPS[@]})); then
|
||||
IFS=$'\n' SORTED_WEB_APPS=($(sort <<<"${WEB_APPS[*]}"))
|
||||
unset IFS
|
||||
APP_NAMES_STRING=$(gum choose --no-limit --header "Select web app to remove..." --selected-prefix="✗ " "${SORTED_WEB_APPS[@]}")
|
||||
# Convert newline-separated string to array
|
||||
APP_NAMES=()
|
||||
while IFS= read -r line; do
|
||||
[[ -n $line ]] && APP_NAMES+=("$line")
|
||||
done <<< "$APP_NAMES_STRING"
|
||||
else
|
||||
echo "No web apps to remove."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Use array to preserve spaces in app names
|
||||
APP_NAMES=("$@")
|
||||
fi
|
||||
|
||||
if (( ${#APP_NAMES[@]} == 0 )); then
|
||||
echo "You must select at least one web app to remove."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for APP_NAME in "${APP_NAMES[@]}"; do
|
||||
rm -f "$DESKTOP_DIR/$APP_NAME.desktop"
|
||||
rm -f "$ICON_DIR/$APP_NAME.png"
|
||||
echo "Removed $APP_NAME"
|
||||
done
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Remove all web apps installed via nomarchy-webapp-install.
|
||||
# Identifies web apps by their Exec pattern (nomarchy-launch-webapp or nomarchy-webapp-handler).
|
||||
|
||||
set -e
|
||||
|
||||
APP_DIR="${1:-$HOME/.local/share/applications}"
|
||||
ICON_DIR="$HOME/.local/share/applications/icons"
|
||||
|
||||
echo "Scanning for web apps in $APP_DIR..."
|
||||
|
||||
webapp_desktop_files=()
|
||||
while IFS= read -r -d '' file; do
|
||||
if grep -q "Exec=nomarchy-launch-webapp\|Exec=nomarchy-webapp-handler" "$file" 2>/dev/null; then
|
||||
webapp_desktop_files+=("$file")
|
||||
fi
|
||||
done < <(find "$APP_DIR" -maxdepth 1 -name "*.desktop" -print0 2>/dev/null)
|
||||
|
||||
if (( ${#webapp_desktop_files[@]} == 0 )); then
|
||||
echo "No web apps found."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for file in "${webapp_desktop_files[@]}"; do
|
||||
app_name=$(basename "$file" .desktop)
|
||||
echo "Removing web app: $app_name"
|
||||
rm -f "$file"
|
||||
rm -f "$ICON_DIR/$app_name.png"
|
||||
done
|
||||
|
||||
if command -v update-desktop-database &>/dev/null; then
|
||||
update-desktop-database "$APP_DIR" &>/dev/null || true
|
||||
fi
|
||||
|
||||
echo "Web apps removed successfully."
|
||||
@@ -1,448 +0,0 @@
|
||||
#!/bin/bash
|
||||
COMPOSE_FILE="$HOME/.config/windows/docker-compose.yml"
|
||||
|
||||
check_prerequisites() {
|
||||
local DISK_SIZE_GB=${1:-64}
|
||||
local REQUIRED_SPACE=$((DISK_SIZE_GB + 10)) # Add 10GB for Windows ISO and overhead
|
||||
|
||||
# Check for KVM support
|
||||
if [[ ! -e /dev/kvm ]]; then
|
||||
gum style \
|
||||
--border normal \
|
||||
--padding "1 2" \
|
||||
--margin "1" \
|
||||
"❌ KVM virtualization not available!" \
|
||||
"" \
|
||||
"Please enable virtualization in BIOS or run:" \
|
||||
" sudo modprobe kvm-intel # for Intel CPUs" \
|
||||
" sudo modprobe kvm-amd # for AMD CPUs"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check disk space
|
||||
AVAILABLE_SPACE=$(df "$HOME" | awk 'NR==2 {print int($4/1024/1024)}')
|
||||
if (( AVAILABLE_SPACE < REQUIRED_SPACE )); then
|
||||
echo "❌ Insufficient disk space!"
|
||||
echo " Available: ${AVAILABLE_SPACE}GB"
|
||||
echo " Required: ${REQUIRED_SPACE}GB (${DISK_SIZE_GB}GB disk + 10GB for Windows image)"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
install_windows() {
|
||||
# Set up trap to handle Ctrl+C
|
||||
trap "echo ''; echo 'Installation cancelled by user'; exit 1" INT
|
||||
|
||||
check_prerequisites
|
||||
|
||||
nomarchy-pkg-add freerdp openbsd-netcat gum
|
||||
|
||||
mkdir -p "$HOME/.windows"
|
||||
mkdir -p "$HOME/.config/windows"
|
||||
mkdir -p "$HOME/.local/share/applications/icons"
|
||||
|
||||
# Install Windows VM icon and desktop file
|
||||
if [[ -f $NOMARCHY_PATH/applications/icons/windows.png ]]; then
|
||||
cp "$NOMARCHY_PATH/applications/icons/windows.png" "$HOME/.local/share/applications/icons/windows.png"
|
||||
fi
|
||||
|
||||
cat << EOF | tee "$HOME/.local/share/applications/windows-vm.desktop" > /dev/null
|
||||
[Desktop Entry]
|
||||
Name=Windows
|
||||
Comment=Start Windows VM via Docker and connect with RDP
|
||||
Exec=uwsm app -- nomarchy-windows-vm launch
|
||||
Icon=$HOME/.local/share/applications/icons/windows.png
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=System;Virtualization;
|
||||
EOF
|
||||
|
||||
# Get system resources
|
||||
TOTAL_RAM=$(free -h | awk 'NR==2 {print $2}')
|
||||
TOTAL_RAM_GB=$(awk 'NR==1 {printf "%d", $2/1024/1024}' /proc/meminfo)
|
||||
TOTAL_CORES=$(nproc)
|
||||
|
||||
echo ""
|
||||
echo "System Resources Detected:"
|
||||
echo " Total RAM: $TOTAL_RAM"
|
||||
echo " Total CPU Cores: $TOTAL_CORES"
|
||||
echo ""
|
||||
|
||||
RAM_OPTIONS=""
|
||||
for size in 2 4 8 16 32 64; do
|
||||
if (( size <= TOTAL_RAM_GB )); then
|
||||
RAM_OPTIONS="$RAM_OPTIONS ${size}G"
|
||||
fi
|
||||
done
|
||||
|
||||
SELECTED_RAM=$(echo $RAM_OPTIONS | tr ' ' '\n' | gum choose --selected="4G" --header="How much RAM would you like to allocate to Windows VM?")
|
||||
|
||||
# Check if user cancelled
|
||||
if [[ -z $SELECTED_RAM ]]; then
|
||||
echo "Installation cancelled by user"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SELECTED_CORES=$(gum input --placeholder="Number of CPU cores (1-$TOTAL_CORES)" --value="2" --header="How many CPU cores would you like to allocate to Windows VM?" --char-limit=2)
|
||||
|
||||
# Check if user cancelled (Ctrl+C in gum input returns empty string)
|
||||
if [[ -z $SELECTED_CORES ]]; then
|
||||
echo "Installation cancelled by user"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ $SELECTED_CORES =~ ^[0-9]+$ ]] || (( SELECTED_CORES < 1 )) || (( SELECTED_CORES > TOTAL_CORES )); then
|
||||
echo "Invalid input. Using default: 2 cores"
|
||||
SELECTED_CORES=2
|
||||
fi
|
||||
|
||||
AVAILABLE_SPACE=$(df "$HOME" | awk 'NR==2 {print int($4/1024/1024)}')
|
||||
MAX_DISK_GB=$((AVAILABLE_SPACE - 10)) # Leave 10GB for Windows image
|
||||
|
||||
# Check if we have enough space for minimum
|
||||
if (( MAX_DISK_GB < 32 )); then
|
||||
echo "❌ Insufficient disk space for Windows VM!"
|
||||
echo " Available: ${AVAILABLE_SPACE}GB"
|
||||
echo " Minimum required: 42GB (32GB disk + 10GB for Windows image)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DISK_OPTIONS=""
|
||||
for size in 32 64 128 256 512; do
|
||||
if (( size <= MAX_DISK_GB )); then
|
||||
DISK_OPTIONS="$DISK_OPTIONS ${size}G"
|
||||
fi
|
||||
done
|
||||
|
||||
# Default to 64G if available, otherwise 32G
|
||||
DEFAULT_DISK="64G"
|
||||
if ! echo "$DISK_OPTIONS" | grep -q "64G"; then
|
||||
DEFAULT_DISK="32G"
|
||||
fi
|
||||
|
||||
SELECTED_DISK=$(echo $DISK_OPTIONS | tr ' ' '\n' | gum choose --selected="$DEFAULT_DISK" --header="How much disk space would you like to give Windows VM? (64GB+ recommended)")
|
||||
|
||||
# Check if user cancelled
|
||||
if [[ -z $SELECTED_DISK ]]; then
|
||||
echo "Installation cancelled by user"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract just the number for prerequisite check
|
||||
DISK_SIZE_NUM=$(echo "$SELECTED_DISK" | sed 's/G//')
|
||||
|
||||
# Re-check prerequisites with selected disk size
|
||||
check_prerequisites "$DISK_SIZE_NUM"
|
||||
|
||||
# Prompt for username and password
|
||||
USERNAME=$(gum input --placeholder="Username (Press enter to use default: docker)" --header="Enter Windows username:")
|
||||
if [[ -z $USERNAME ]]; then
|
||||
USERNAME="docker"
|
||||
fi
|
||||
|
||||
PASSWORD=$(gum input --placeholder="Password (Press enter to use default: admin)" --password --header="Enter Windows password:")
|
||||
if [[ -z $PASSWORD ]]; then
|
||||
PASSWORD="admin"
|
||||
PASSWORD_DISPLAY="(default)"
|
||||
else
|
||||
PASSWORD_DISPLAY="(user-defined)"
|
||||
fi
|
||||
|
||||
# Display configuration summary
|
||||
gum style \
|
||||
--border normal \
|
||||
--padding "1 2" \
|
||||
--margin "1" \
|
||||
--align left \
|
||||
--bold \
|
||||
"Windows VM Configuration" \
|
||||
"" \
|
||||
"RAM: $SELECTED_RAM" \
|
||||
"CPU: $SELECTED_CORES cores" \
|
||||
"Disk: $SELECTED_DISK" \
|
||||
"Username: $USERNAME" \
|
||||
"Password: $PASSWORD_DISPLAY"
|
||||
|
||||
# Ask for confirmation
|
||||
echo ""
|
||||
if ! gum confirm "Proceed with this configuration?"; then
|
||||
echo "Installation cancelled by user"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p $HOME/Windows
|
||||
|
||||
# Create docker-compose.yml in user config directory
|
||||
cat << EOF | tee "$COMPOSE_FILE" > /dev/null
|
||||
services:
|
||||
windows:
|
||||
image: dockurr/windows
|
||||
container_name: nomarchy-windows
|
||||
environment:
|
||||
VERSION: "11"
|
||||
RAM_SIZE: "$SELECTED_RAM"
|
||||
CPU_CORES: "$SELECTED_CORES"
|
||||
DISK_SIZE: "$SELECTED_DISK"
|
||||
USERNAME: "$USERNAME"
|
||||
PASSWORD: "$PASSWORD"
|
||||
TZ: "$(timedatectl show -p Timezone --value 2>/dev/null || echo UTC)"
|
||||
ARGUMENTS: "-rtc base=localtime,clock=host,driftfix=slew"
|
||||
devices:
|
||||
- /dev/kvm
|
||||
- /dev/net/tun
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
ports:
|
||||
- 127.0.0.1:8006:8006
|
||||
- 127.0.0.1:3389:3389/tcp
|
||||
- 127.0.0.1:3389:3389/udp
|
||||
volumes:
|
||||
- $HOME/.windows:/storage
|
||||
- $HOME/Windows:/shared
|
||||
restart: unless-stopped
|
||||
stop_grace_period: 2m
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "Starting Windows VM installation..."
|
||||
echo "This will download a Windows 11 image (may take 10-15 minutes)."
|
||||
echo ""
|
||||
echo "Monitor installation progress at: http://127.0.0.1:8006"
|
||||
echo ""
|
||||
|
||||
# Start docker-compose with user's config
|
||||
echo "Starting Windows VM with docker-compose..."
|
||||
if ! docker-compose -f "$COMPOSE_FILE" up -d 2>&1; then
|
||||
echo "❌ Failed to start Windows VM!"
|
||||
echo " Common issues:"
|
||||
echo " - Docker daemon not running: sudo systemctl start docker"
|
||||
echo " - Port already in use: check if another VM is running"
|
||||
echo " - Permission issues: make sure you're in the docker group"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Windows VM is starting up!"
|
||||
echo ""
|
||||
echo "Opening browser to monitor installation..."
|
||||
|
||||
# Open browser to monitor installation
|
||||
sleep 3
|
||||
xdg-open "http://127.0.0.1:8006"
|
||||
|
||||
echo ""
|
||||
echo "Installation is running in the background."
|
||||
echo "You can monitor progress at: http://127.0.0.1:8006"
|
||||
echo ""
|
||||
echo "Once finished, launch 'Windows' via Super + Space"
|
||||
echo ""
|
||||
echo "To stop the VM: nomarchy-windows-vm stop"
|
||||
echo "To change resources: ~/.config/windows/docker-compose.yml"
|
||||
echo ""
|
||||
}
|
||||
|
||||
remove_windows() {
|
||||
if ! gum confirm --default=false "Remove Windows VM and delete all associated data?"; then
|
||||
echo "Removal cancelled by user"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Removing Windows VM..."
|
||||
|
||||
docker-compose -f "$COMPOSE_FILE" down 2>/dev/null || true
|
||||
|
||||
docker rmi dockurr/windows 2>/dev/null || echo "Image already removed or not found"
|
||||
|
||||
rm "$HOME/.local/share/applications/windows-vm.desktop"
|
||||
rm -rf "$HOME/.config/windows"
|
||||
rm -rf "$HOME/.windows"
|
||||
|
||||
echo ""
|
||||
echo "Windows VM removal completed!"
|
||||
}
|
||||
|
||||
launch_windows() {
|
||||
KEEP_ALIVE=false
|
||||
if [[ $1 = "--keep-alive" ]] || [[ $1 = "-k" ]]; then
|
||||
KEEP_ALIVE=true
|
||||
fi
|
||||
|
||||
# Check if config exists
|
||||
if [[ ! -f $COMPOSE_FILE ]]; then
|
||||
echo "Windows VM not configured. Please run: nomarchy-windows-vm install"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract credentials from compose file
|
||||
WIN_USER=$(grep "USERNAME:" "$COMPOSE_FILE" | sed 's/.*USERNAME: "\(.*\)"/\1/')
|
||||
WIN_PASS=$(grep "PASSWORD:" "$COMPOSE_FILE" | sed 's/.*PASSWORD: "\(.*\)"/\1/')
|
||||
|
||||
# Use defaults if not found
|
||||
[[ -z $WIN_USER ]] && WIN_USER="docker"
|
||||
[[ -z $WIN_PASS ]] && WIN_PASS="admin"
|
||||
|
||||
# Check if container is already running
|
||||
CONTAINER_STATUS=$(docker inspect --format='{{.State.Status}}' nomarchy-windows 2>/dev/null)
|
||||
|
||||
if [[ $CONTAINER_STATUS != "running" ]]; then
|
||||
echo "Starting Windows VM..."
|
||||
|
||||
# Send desktop notification
|
||||
notify-send " Starting Windows VM" " This can take 15-30 seconds" -t 15000
|
||||
|
||||
if ! docker-compose -f "$COMPOSE_FILE" up -d 2>&1; then
|
||||
echo "❌ Failed to start Windows VM!"
|
||||
echo " Try checking: nomarchy-windows-vm status"
|
||||
echo " View logs: docker logs nomarchy-windows"
|
||||
notify-send -u critical "Windows VM" "Failed to start Windows VM"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Waiting for Windows VM to start..."
|
||||
WAIT_COUNT=0
|
||||
until docker logs nomarchy-windows 2>&1 | grep -qi "windows started successfully"; do
|
||||
sleep 2
|
||||
WAIT_COUNT=$((WAIT_COUNT + 1))
|
||||
if (( WAIT_COUNT > 60 )); then # 2 minutes timeout
|
||||
echo ""
|
||||
echo "❌ Timeout: Windows VM failed to start within 2 minutes"
|
||||
echo " Check logs: docker logs nomarchy-windows"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Build the connection info
|
||||
if [[ $KEEP_ALIVE = "true" ]]; then
|
||||
LIFECYCLE="VM will keep running after RDP closes
|
||||
To stop: nomarchy-windows-vm stop"
|
||||
else
|
||||
LIFECYCLE="VM will auto-stop when RDP closes"
|
||||
fi
|
||||
|
||||
gum style \
|
||||
--border normal \
|
||||
--padding "1 2" \
|
||||
--margin "1" \
|
||||
--align center \
|
||||
"Connecting to Windows VM" \
|
||||
"" \
|
||||
"$LIFECYCLE"
|
||||
|
||||
# Detect display scale from Hyprland
|
||||
HYPR_SCALE=$(hyprctl monitors -j | jq -r '.[] | select (.focused == true) | .scale')
|
||||
SCALE_PERCENT=$(echo "$HYPR_SCALE" | awk '{print int($1 * 100)}')
|
||||
|
||||
RDP_SCALE=""
|
||||
if (( SCALE_PERCENT >= 170 )); then
|
||||
RDP_SCALE="/scale:180"
|
||||
elif (( SCALE_PERCENT >= 130 )); then
|
||||
RDP_SCALE="/scale:140"
|
||||
fi
|
||||
# If scale is less than 130%, don't set any scale (use default 100)
|
||||
|
||||
# Connect with RDP in fullscreen (auto-detects resolution)
|
||||
xfreerdp3 /u:"$WIN_USER" /p:"$WIN_PASS" /v:127.0.0.1:3389 -grab-keyboard /sound /microphone /clipboard /cert:ignore /title:"Windows VM - Nomarchy" /dynamic-resolution /gfx:AVC444 /floatbar:sticky:off,default:visible,show:fullscreen $RDP_SCALE
|
||||
|
||||
# After RDP closes, stop the container unless --keep-alive was specified
|
||||
if [[ $KEEP_ALIVE = "false" ]]; then
|
||||
echo ""
|
||||
echo "RDP session closed. Stopping Windows VM..."
|
||||
docker-compose -f "$COMPOSE_FILE" down
|
||||
echo "Windows VM stopped."
|
||||
else
|
||||
echo ""
|
||||
echo "RDP session closed. Windows VM is still running."
|
||||
echo "To stop it: nomarchy-windows-vm stop"
|
||||
fi
|
||||
}
|
||||
|
||||
stop_windows() {
|
||||
if [[ ! -f $COMPOSE_FILE ]]; then
|
||||
echo "Windows VM not configured."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Stopping Windows VM..."
|
||||
docker-compose -f "$COMPOSE_FILE" down
|
||||
echo "Windows VM stopped."
|
||||
}
|
||||
|
||||
status_windows() {
|
||||
if [[ ! -f $COMPOSE_FILE ]]; then
|
||||
echo "Windows VM not configured."
|
||||
echo "To set up: nomarchy-windows-vm install"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CONTAINER_STATUS=$(docker inspect --format='{{.State.Status}}' nomarchy-windows 2>/dev/null)
|
||||
|
||||
if [[ -z $CONTAINER_STATUS ]]; then
|
||||
echo "Windows VM container not found."
|
||||
echo "To start: nomarchy-windows-vm launch"
|
||||
elif [[ $CONTAINER_STATUS = "running" ]]; then
|
||||
gum style \
|
||||
--border normal \
|
||||
--padding "1 2" \
|
||||
--margin "1" \
|
||||
--align left \
|
||||
"Windows VM Status: RUNNING" \
|
||||
"" \
|
||||
"Web interface: http://127.0.0.1:8006" \
|
||||
"RDP available: port 3389" \
|
||||
"" \
|
||||
"To connect: nomarchy-windows-vm launch" \
|
||||
"To stop: nomarchy-windows-vm stop"
|
||||
else
|
||||
echo "Windows VM is stopped (status: $CONTAINER_STATUS)"
|
||||
echo "To start: nomarchy-windows-vm launch"
|
||||
fi
|
||||
}
|
||||
|
||||
show_usage() {
|
||||
echo "Usage: nomarchy-windows-vm [command] [options]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " install Install and configure Windows VM"
|
||||
echo " remove Remove Windows VM and optionally its data"
|
||||
echo " launch [options] Start Windows VM (if needed) and connect via RDP"
|
||||
echo " Options:"
|
||||
echo " --keep-alive, -k Keep VM running after RDP closes"
|
||||
echo " stop Stop the running Windows VM"
|
||||
echo " status Show current VM status"
|
||||
echo " help Show this help message"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " nomarchy-windows-vm install # Set up Windows VM for first time"
|
||||
echo " nomarchy-windows-vm launch # Connect to VM (auto-stop on exit)"
|
||||
echo " nomarchy-windows-vm launch -k # Connect to VM (keep running)"
|
||||
echo " nomarchy-windows-vm stop # Shut down the VM"
|
||||
}
|
||||
|
||||
# Main command dispatcher
|
||||
case "$1" in
|
||||
install)
|
||||
install_windows
|
||||
;;
|
||||
remove)
|
||||
remove_windows
|
||||
;;
|
||||
launch|start)
|
||||
launch_windows "$2"
|
||||
;;
|
||||
stop|down)
|
||||
stop_windows
|
||||
;;
|
||||
status)
|
||||
status_windows
|
||||
;;
|
||||
help|--help|-h|"")
|
||||
show_usage
|
||||
;;
|
||||
*)
|
||||
echo "Unknown command: $1" >&2
|
||||
echo "" >&2
|
||||
show_usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Returns the battery full capacity in Wh (rounded to whole number).
|
||||
# Used by nomarchy-battery-status for displaying battery capacity.
|
||||
|
||||
battery_info=$(upower -i $(upower -e | grep BAT))
|
||||
|
||||
echo "$battery_info" | awk '/energy-full:/ {
|
||||
printf "%d", $2
|
||||
exit
|
||||
}'
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Designed to be run by systemd timer every 30 seconds and alerts if battery is low
|
||||
|
||||
BATTERY_THRESHOLD=10
|
||||
NOTIFICATION_FLAG="/run/user/$UID/nomarchy_battery_notified"
|
||||
BATTERY_LEVEL=$(nomarchy-battery-remaining)
|
||||
BATTERY_STATE=$(upower -i $(upower -e | grep 'BAT') | grep -E "state" | awk '{print $2}')
|
||||
|
||||
send_notification() {
|
||||
notify-send -u critical " Time to recharge!" "Battery is down to ${1}%" -i battery-caution -t 30000
|
||||
nomarchy-hook battery-low "$1"
|
||||
}
|
||||
|
||||
if [[ -n $BATTERY_LEVEL && $BATTERY_LEVEL =~ ^[0-9]+$ ]]; then
|
||||
if [[ $BATTERY_STATE == "discharging" ]] && (( BATTERY_LEVEL <= BATTERY_THRESHOLD )); then
|
||||
if [[ ! -f $NOTIFICATION_FLAG ]]; then
|
||||
send_notification $BATTERY_LEVEL
|
||||
touch $NOTIFICATION_FLAG
|
||||
fi
|
||||
else
|
||||
rm -f $NOTIFICATION_FLAG
|
||||
fi
|
||||
fi
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Returns true if a battery is present on the system.
|
||||
# Used by the battery monitor and other battery-related checks.
|
||||
|
||||
for bat in /sys/class/power_supply/BAT*; do
|
||||
[[ -r $bat/present ]] &&
|
||||
[[ $(cat $bat/present) == "1" ]] &&
|
||||
[[ $(cat $bat/type) == "Battery" ]] &&
|
||||
exit 0
|
||||
done
|
||||
|
||||
exit 1
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Returns the battery percentage remaining as an integer.
|
||||
# Used by the battery monitor and the Ctrl + Shift + Super + B hotkey.
|
||||
|
||||
upower -i $(upower -e | grep BAT) | awk '/percentage/ {
|
||||
print int($2)
|
||||
exit
|
||||
}'
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Returns the battery time remaining (to empty or full) in a compact format.
|
||||
|
||||
battery_info=$(upower -i $(upower -e | grep BAT))
|
||||
|
||||
echo "$battery_info" | awk '/time to (empty|full)/ {
|
||||
value = $4
|
||||
unit = $5
|
||||
if (unit == "minutes") {
|
||||
hours = int(value / 60)
|
||||
minutes = int(value % 60)
|
||||
} else {
|
||||
hours = int(value)
|
||||
minutes = int((value - hours) * 60)
|
||||
}
|
||||
if (hours > 0 && minutes > 0) {
|
||||
printf "%dh %dm", hours, minutes
|
||||
} else if (hours > 0) {
|
||||
printf "%dh", hours
|
||||
} else {
|
||||
printf "%dm", minutes
|
||||
}
|
||||
exit
|
||||
}'
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Returns a formatted battery status string with percentage and power draw/charge.
|
||||
# Used by the battery notification hotkey (Ctrl + Shift + Super + B).
|
||||
|
||||
battery_info=$(upower -i $(upower -e | grep BAT))
|
||||
|
||||
percentage=$(echo "$battery_info" | awk '/percentage/ {
|
||||
print int($2)
|
||||
exit
|
||||
}')
|
||||
|
||||
power_rate=$(echo "$battery_info" | awk '/energy-rate/ {
|
||||
rounded = sprintf("%.1f", $2)
|
||||
sub(/\.0$/, "", rounded)
|
||||
print rounded
|
||||
exit
|
||||
}')
|
||||
|
||||
state=$(echo "$battery_info" | awk '/state/ { print $2; exit }')
|
||||
time_remaining=$(nomarchy-battery-remaining-time)
|
||||
capacity=$(nomarchy-battery-capacity)
|
||||
|
||||
if [[ $state == "charging" ]]; then
|
||||
echo " Battery ${percentage}% · ${time_remaining} to full · ${power_rate}W / ${capacity}Wh"
|
||||
else
|
||||
echo " Battery ${percentage}% · ${time_remaining} left · ${power_rate}W / ${capacity}Wh"
|
||||
fi
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Adjust brightness on the most likely display device.
|
||||
# Usage: nomarchy-brightness-display <step>
|
||||
|
||||
step="${1:-+5%}"
|
||||
|
||||
# Start with the first possible output, then refine to the most likely given an order heuristic.
|
||||
device="$(ls -1 /sys/class/backlight 2>/dev/null | head -n1)"
|
||||
for candidate in amdgpu_bl* intel_backlight acpi_video*; do
|
||||
if [[ -e /sys/class/backlight/$candidate ]]; then
|
||||
device="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Set the actual brightness of the display device.
|
||||
brightnessctl -d "$device" set "$step" >/dev/null
|
||||
|
||||
# Use SwayOSD to display the new brightness setting.
|
||||
nomarchy-swayosd-brightness "$(brightnessctl -d "$device" -m | cut -d',' -f4 | tr -d '%')"
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Adjust the brightness on Apple Studio Displays and Apple XDR Displays using asdcontrol.
|
||||
|
||||
if (( $# == 0 )); then
|
||||
echo "Adjust Apple Display Brightness by passing +5000 or -5000 (or any range from 0-60000)"
|
||||
else
|
||||
device="$(sudo asdcontrol --detect /dev/usb/hiddev* | grep ^/dev/usb/hiddev | cut -d: -f1)"
|
||||
sudo asdcontrol "$device" -- "$1" >/dev/null
|
||||
value="$(sudo asdcontrol "$device" | awk -F= '/BRIGHTNESS=/{print $2+0}')"
|
||||
nomarchy-swayosd-brightness "$(( value * 100 / 60000 ))"
|
||||
fi
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Adjust keyboard backlight brightness using available steps.
|
||||
# Usage: nomarchy-brightness-keyboard <up|down|cycle>
|
||||
|
||||
direction="${1:-up}"
|
||||
|
||||
# Find keyboard backlight device (look for *kbd_backlight* pattern in leds class).
|
||||
device=""
|
||||
for candidate in /sys/class/leds/*kbd_backlight*; do
|
||||
if [[ -e $candidate ]]; then
|
||||
device="$(basename "$candidate")"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -z $device ]]; then
|
||||
echo "No keyboard backlight device found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get current and max brightness to determine step size.
|
||||
max_brightness="$(brightnessctl -d "$device" max)"
|
||||
current_brightness="$(brightnessctl -d "$device" get)"
|
||||
|
||||
# Calculate step as one unit (keyboards typically have discrete levels like 0-3).
|
||||
if [[ $direction == "cycle" ]]; then
|
||||
new_brightness=$(( (current_brightness + 1) % (max_brightness + 1) ))
|
||||
elif [[ $direction == "up" ]]; then
|
||||
new_brightness=$((current_brightness + 1))
|
||||
(( new_brightness > max_brightness )) && new_brightness=$max_brightness
|
||||
else
|
||||
new_brightness=$((current_brightness - 1))
|
||||
(( new_brightness < 0 )) && new_brightness=0
|
||||
fi
|
||||
|
||||
# Set the new brightness.
|
||||
brightnessctl -d "$device" set "$new_brightness" >/dev/null
|
||||
|
||||
# Use SwayOSD to display the new brightness setting.
|
||||
percent=$((new_brightness * 100 / max_brightness))
|
||||
nomarchy-swayosd-kbd-brightness "$percent"
|
||||
@@ -1,95 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""Haptic feedback daemon for Synaptics touchpads with Manual Trigger.
|
||||
|
||||
Monitors touchpad button press events and sends haptic pulses via HID
|
||||
feature reports. Required because the kernel's HID haptic subsystem only
|
||||
supports Auto Trigger with waveform enumeration, not the simpler Manual
|
||||
Trigger protocol used by these Synaptics touchpads.
|
||||
"""
|
||||
|
||||
import fcntl, glob, os, struct, sys
|
||||
|
||||
VENDOR = "06CB"
|
||||
PRODUCT = "D01A"
|
||||
REPORT_ID = 0x37
|
||||
INTENSITY = 40 # 0-100
|
||||
|
||||
# input_event: struct timeval (16 bytes on 64-bit) + type(H) + code(H) + value(i)
|
||||
EVENT_FORMAT = "llHHi"
|
||||
EVENT_SIZE = struct.calcsize(EVENT_FORMAT)
|
||||
EV_KEY = 0x01
|
||||
BTN_LEFT = 272
|
||||
BTN_RIGHT = 273
|
||||
BTN_MIDDLE = 274
|
||||
|
||||
# ioctl: HIDIOCSFEATURE(len) = _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len)
|
||||
def HIDIOCSFEATURE(length):
|
||||
return 0xC0000000 | (length << 16) | (ord("H") << 8) | 0x06
|
||||
|
||||
|
||||
def find_hidraw():
|
||||
for path in sorted(glob.glob("/sys/class/hidraw/hidraw*")):
|
||||
uevent = os.path.join(path, "device", "uevent")
|
||||
try:
|
||||
with open(uevent) as f:
|
||||
content = f.read().upper()
|
||||
if f"0000{VENDOR}" in content and f"0000{PRODUCT}" in content:
|
||||
return os.path.join("/dev", os.path.basename(path))
|
||||
except OSError:
|
||||
continue
|
||||
return None
|
||||
|
||||
|
||||
def find_touchpad_event():
|
||||
for path in sorted(glob.glob("/sys/class/input/event*/device/name")):
|
||||
try:
|
||||
with open(path) as f:
|
||||
name = f.read().strip().upper()
|
||||
if VENDOR in name and PRODUCT in name and "TOUCHPAD" in name:
|
||||
event = path.split("/")[-3]
|
||||
return os.path.join("/dev/input", event)
|
||||
except OSError:
|
||||
continue
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
hidraw = find_hidraw()
|
||||
if not hidraw:
|
||||
print("No Synaptics haptic touchpad hidraw device found", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
event = find_touchpad_event()
|
||||
if not event:
|
||||
print("No Synaptics haptic touchpad input device found", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Haptic touchpad: hidraw={hidraw} input={event} intensity={INTENSITY}", flush=True)
|
||||
|
||||
haptic_report = struct.pack("BB", REPORT_ID, INTENSITY)
|
||||
ioctl_req = HIDIOCSFEATURE(len(haptic_report))
|
||||
|
||||
hidraw_fd = os.open(hidraw, os.O_RDWR)
|
||||
event_fd = os.open(event, os.O_RDONLY)
|
||||
|
||||
try:
|
||||
while True:
|
||||
data = os.read(event_fd, EVENT_SIZE)
|
||||
if len(data) < EVENT_SIZE:
|
||||
continue
|
||||
_, _, ev_type, code, value = struct.unpack(EVENT_FORMAT, data)
|
||||
if ev_type == EV_KEY and code in (BTN_LEFT, BTN_RIGHT, BTN_MIDDLE) and value == 1:
|
||||
try:
|
||||
fcntl.ioctl(hidraw_fd, ioctl_req, haptic_report)
|
||||
except OSError:
|
||||
pass
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
os.close(event_fd)
|
||||
os.close(hidraw_fd)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Detect whether the computer is an Asus ROG machine.
|
||||
|
||||
[[ $(cat /sys/class/dmi/id/sys_vendor 2>/dev/null) == "ASUSTeK COMPUTER INC." ]] &&
|
||||
grep -q "ROG" /sys/class/dmi/id/product_family 2>/dev/null
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Detect whether the computer is a Framework Laptop 16.
|
||||
|
||||
[[ $(cat /sys/class/dmi/id/sys_vendor 2>/dev/null) == "Framework" ]] &&
|
||||
nomarchy-hw-match "Laptop 16"
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Detect whether the computer has an Intel CPU.
|
||||
|
||||
[[ $(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | tr -d ' ') == "GenuineIntel" ]]
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Detect whether the computer has an Intel Panther Lake GPU.
|
||||
|
||||
lspci | grep -iE 'vga|3d|display' | grep -qi 'panther lake'
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Match against the computer's DMI product name (case-insensitive).
|
||||
# Usage: nomarchy-hw-match "XPS"
|
||||
|
||||
grep -qi "$1" /sys/class/dmi/id/product_name 2>/dev/null
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Detect whether the computer is a Microsoft Surface device.
|
||||
|
||||
[[ $(cat /sys/class/dmi/id/sys_vendor 2>/dev/null) == "Microsoft Corporation" ]] &&
|
||||
nomarchy-hw-match "Surface"
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Detect whether Vulkan is available.
|
||||
|
||||
[[ -d /usr/share/vulkan/icd.d ]] &&
|
||||
find /usr/share/vulkan/icd.d -maxdepth 1 -name "*.json" -print -quit | grep -q .
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Returns a list of all the available power profiles on the system.
|
||||
# Used by the Nomarchy Menu under Setup > Power Profile.
|
||||
|
||||
powerprofilesctl list |
|
||||
awk '/^\s*[* ]\s*[a-zA-Z0-9\-]+:$/ { gsub(/^[*[:space:]]+|:$/,""); print }' |
|
||||
tac
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Restart makima - key remapping service for remapping Copilot key to Nomarchy Menu
|
||||
|
||||
sudo systemctl restart makima
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Reload the intel_quicki2c driver to fix a dead trackpad.
|
||||
# The THC (Touch Host Controller) can fail to initialize interrupts
|
||||
# during boot or after suspend, leaving the trackpad registered but
|
||||
# not delivering events.
|
||||
|
||||
sudo modprobe -r intel_quicki2c && sudo modprobe intel_quicki2c
|
||||
@@ -1,47 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Toggle dedicated vs integrated GPU mode via supergfxd (for hybrid gpu laptops, like Asus G14).
|
||||
# Declarative enablement + Runtime mode switching for Nomarchy NixOS.
|
||||
|
||||
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"
|
||||
echo "Hybrid GPU support enabled in configuration. Applying changes..."
|
||||
sudo sys-update
|
||||
echo "Please run this command again after the update."
|
||||
exit 0
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v supergfxctl &> /dev/null; then
|
||||
echo "supergfxctl not found. Is the system updated?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
gpu_mode=$(supergfxctl -g)
|
||||
echo "Current GPU mode: $gpu_mode"
|
||||
|
||||
case "$gpu_mode" in
|
||||
"Integrated")
|
||||
if gum confirm "Switch to Hybrid mode (enables dGPU) and reboot?"; then
|
||||
supergfxctl -m Hybrid
|
||||
echo "Switching to Hybrid mode..."
|
||||
nomarchy-system-reboot
|
||||
fi
|
||||
;;
|
||||
"Hybrid")
|
||||
if gum confirm "Switch to Integrated mode (disables dGPU) and reboot?"; then
|
||||
supergfxctl -m Integrated
|
||||
echo "Switching to Integrated mode..."
|
||||
nomarchy-system-reboot
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Hybrid GPU in unknown mode: $gpu_mode. Try 'supergfxctl -m Hybrid' manually."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Toggles wifi power saving declaratively.
|
||||
# Usage: nomarchy-wifi-powersave <on|off>
|
||||
|
||||
STATE_FILE="/etc/nixos/state.json"
|
||||
|
||||
case "$1" in
|
||||
on) value="true" ;;
|
||||
off) value="false" ;;
|
||||
*) echo "Usage: nomarchy-wifi-powersave <on|off>"; exit 1 ;;
|
||||
esac
|
||||
|
||||
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
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Check if hibernation is supported
|
||||
if [[ ! -f /sys/power/image_size ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Sum all swap sizes (excluding zram)
|
||||
SWAPSIZE_KB=$(awk '!/Filename|zram/ {sum += $3} END {print sum+0}' /proc/swaps)
|
||||
SWAPSIZE=$(( 1024 * ${SWAPSIZE_KB:-0} ))
|
||||
|
||||
HIBERNATION_IMAGE_SIZE=$(cat /sys/power/image_size)
|
||||
|
||||
if (( SWAPSIZE > HIBERNATION_IMAGE_SIZE )) && [[ -f /etc/mkinitcpio.conf.d/nomarchy_resume.conf ]]; then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,59 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Removes hibernation setup: disables swap, removes swapfile, removes fstab entry,
|
||||
# removes resume hook, and removes suspend-then-hibernate configuration.
|
||||
|
||||
MKINITCPIO_CONF="/etc/mkinitcpio.conf.d/nomarchy_resume.conf"
|
||||
|
||||
# Check if hibernation is configured
|
||||
if [[ ! -f $MKINITCPIO_CONF ]] || ! grep -q "^HOOKS+=(resume)$" "$MKINITCPIO_CONF"; then
|
||||
echo "Hibernation is not set up"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! gum confirm "Remove hibernation setup?"; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
SWAP_SUBVOLUME="/swap"
|
||||
SWAP_FILE="$SWAP_SUBVOLUME/swapfile"
|
||||
|
||||
# Disable swap if active
|
||||
if swapon --show | grep -q "$SWAP_FILE"; then
|
||||
echo "Disabling swap on $SWAP_FILE"
|
||||
sudo swapoff "$SWAP_FILE"
|
||||
fi
|
||||
|
||||
# Remove swapfile
|
||||
if [[ -f $SWAP_FILE ]]; then
|
||||
echo "Removing swapfile"
|
||||
sudo rm "$SWAP_FILE"
|
||||
fi
|
||||
|
||||
# Remove swap subvolume
|
||||
if sudo btrfs subvolume show "$SWAP_SUBVOLUME" &>/dev/null; then
|
||||
echo "Removing Btrfs subvolume $SWAP_SUBVOLUME"
|
||||
sudo btrfs subvolume delete "$SWAP_SUBVOLUME"
|
||||
fi
|
||||
|
||||
# Remove fstab entry
|
||||
if grep -Fq "$SWAP_FILE" /etc/fstab; then
|
||||
echo "Removing swapfile from /etc/fstab"
|
||||
sudo cp -a /etc/fstab "/etc/fstab.$(date +%Y%m%d%H%M%S).back"
|
||||
sudo sed -i "\|$SWAP_FILE|d" /etc/fstab
|
||||
sudo sed -i '/^# Btrfs swapfile for system hibernation$/d' /etc/fstab
|
||||
fi
|
||||
|
||||
# Remove suspend-then-hibernate configuration
|
||||
echo "Removing suspend-then-hibernate configuration"
|
||||
sudo rm -f /etc/systemd/logind.conf.d/lid.conf
|
||||
sudo rm -f /etc/systemd/sleep.conf.d/hibernate.conf
|
||||
|
||||
# Remove mkinitcpio resume hook
|
||||
echo "Removing resume hook"
|
||||
sudo rm "$MKINITCPIO_CONF"
|
||||
|
||||
echo "Regenerating initramfs..."
|
||||
sudo limine-mkinitcpio
|
||||
|
||||
echo "Hibernation removed"
|
||||
@@ -1,99 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Creates a swap file in the btrfs subvolume, adds the swap file to /etc/fstab,
|
||||
# adds a resume hook to mkinitcpio, and configures suspend-then-hibernate.
|
||||
|
||||
if [[ ! -f /sys/power/image_size ]]; then
|
||||
echo -e "Hibernation is not supported on your system" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! command -v limine-mkinitcpio &>/dev/null; then
|
||||
echo "Skipping hibernation setup (requires Limine bootloader)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
MKINITCPIO_CONF="/etc/mkinitcpio.conf.d/nomarchy_resume.conf"
|
||||
|
||||
# Check if hibernation is already configured
|
||||
if [[ -f $MKINITCPIO_CONF ]] && grep -q "^HOOKS+=(resume)$" "$MKINITCPIO_CONF"; then
|
||||
echo "Hibernation is already set up"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ $1 != "--force" ]]; then
|
||||
MEM_TOTAL_HUMAN=$(free --human | awk '/Mem/ {print $2}')
|
||||
if ! gum confirm "Use $MEM_TOTAL_HUMAN on boot drive to make hibernation available?"; then
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
SWAP_SUBVOLUME="/swap"
|
||||
SWAP_FILE="$SWAP_SUBVOLUME/swapfile"
|
||||
|
||||
# Create btrfs subvolume for swap
|
||||
if ! sudo btrfs subvolume show "$SWAP_SUBVOLUME" &>/dev/null; then
|
||||
echo "Creating Btrfs subvolume"
|
||||
sudo btrfs subvolume create "$SWAP_SUBVOLUME"
|
||||
sudo chattr +C "$SWAP_SUBVOLUME"
|
||||
fi
|
||||
|
||||
# Create swapfile
|
||||
if ! sudo swaplabel "$SWAP_FILE" &>/dev/null; then
|
||||
echo "Creating swapfile in Btrfs subvolume"
|
||||
MEM_TOTAL_KB="$(awk '/MemTotal/ {print $2}' /proc/meminfo)k"
|
||||
sudo btrfs filesystem mkswapfile -s "$MEM_TOTAL_KB" "$SWAP_FILE"
|
||||
fi
|
||||
|
||||
# Add swapfile to fstab
|
||||
if ! grep -Fq "$SWAP_FILE" /etc/fstab; then
|
||||
echo "Adding swapfile to /etc/fstab"
|
||||
sudo cp -a /etc/fstab "/etc/fstab.$(date +%Y%m%d%H%M%S).back"
|
||||
printf "\n# Btrfs swapfile for system hibernation\n%s none swap defaults,pri=0 0 0\n" "$SWAP_FILE" | sudo tee -a /etc/fstab >/dev/null
|
||||
fi
|
||||
|
||||
# Enable swap
|
||||
if ! swapon --show | grep -q "$SWAP_FILE"; then
|
||||
echo "Enabling swap on $SWAP_FILE"
|
||||
sudo swapon -p 0 "$SWAP_FILE"
|
||||
fi
|
||||
|
||||
# Add resume hook to mkinitcpio
|
||||
sudo mkdir -p /etc/mkinitcpio.conf.d
|
||||
echo "Adding resume hook to $MKINITCPIO_CONF"
|
||||
echo "HOOKS+=(resume)" | sudo tee "$MKINITCPIO_CONF" >/dev/null
|
||||
|
||||
# Add resume= kernel parameters so the initramfs resume hook knows where to find the
|
||||
# hibernation image. Without these, resume happens late (after GPU drivers load) and fails.
|
||||
RESUME_DROP_IN="/etc/limine-entry-tool.d/resume.conf"
|
||||
if [[ ! -f $RESUME_DROP_IN ]]; then
|
||||
echo "Adding resume kernel parameters"
|
||||
sudo swapon -p 0 "$SWAP_FILE" 2>/dev/null
|
||||
RESUME_DEVICE=$(findmnt -no SOURCE -T "$SWAP_FILE" | sed 's/\[.*\]//')
|
||||
RESUME_OFFSET=$(sudo btrfs inspect-internal map-swapfile -r "$SWAP_FILE")
|
||||
sudo mkdir -p /etc/limine-entry-tool.d
|
||||
echo "KERNEL_CMDLINE[default]+=\" resume=$RESUME_DEVICE resume_offset=$RESUME_OFFSET\"" | sudo tee "$RESUME_DROP_IN" >/dev/null
|
||||
sudo tee -a /etc/default/limine < "$RESUME_DROP_IN" >/dev/null
|
||||
fi
|
||||
|
||||
# Use ACPI alarm for RTC wakeup on s2idle systems (needed for suspend-then-hibernate)
|
||||
if grep -q "\[s2idle\]" /sys/power/mem_sleep 2>/dev/null; then
|
||||
LIMINE_DROP_IN="/etc/limine-entry-tool.d/rtc-alarm.conf"
|
||||
if [[ ! -f $LIMINE_DROP_IN ]]; then
|
||||
echo "Enabling ACPI RTC alarm for s2idle suspend"
|
||||
sudo mkdir -p /etc/limine-entry-tool.d
|
||||
echo 'KERNEL_CMDLINE[default]+=" rtc_cmos.use_acpi_alarm=1"' | sudo tee "$LIMINE_DROP_IN" >/dev/null
|
||||
sudo tee -a /etc/default/limine < "$LIMINE_DROP_IN" >/dev/null
|
||||
fi
|
||||
fi
|
||||
|
||||
# Regenerate initramfs and boot entry
|
||||
echo "Regenerating initramfs..."
|
||||
sudo limine-mkinitcpio
|
||||
sudo limine-update
|
||||
|
||||
echo
|
||||
|
||||
if [[ $1 != "--force" ]] && gum confirm "Reboot to enable hibernation?"; then
|
||||
nomarchy-system-reboot
|
||||
fi
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
PKG_NAME="$1"
|
||||
|
||||
if [ -z "$PKG_NAME" ]; then
|
||||
echo "Usage: nomarchy-pkg-add <package-name>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
STATE_FILE="$HOME/.config/home-manager/user-packages.json"
|
||||
mkdir -p "$(dirname "$STATE_FILE")"
|
||||
|
||||
if [ ! -f "$STATE_FILE" ]; then
|
||||
echo "[]" > "$STATE_FILE"
|
||||
fi
|
||||
|
||||
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 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..."
|
||||
env-update
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
PKG_NAME="$1"
|
||||
|
||||
if [ -z "$PKG_NAME" ]; then
|
||||
echo "Usage: nomarchy-pkg-remove <package-name>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
STATE_FILE="$HOME/.config/home-manager/user-packages.json"
|
||||
|
||||
if [ ! -f "$STATE_FILE" ]; then
|
||||
echo "No packages managed by nomarchy-pkg yet."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
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 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..."
|
||||
env-update
|
||||
@@ -1,93 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Nomarchy Pre-flight State Migration
|
||||
# Migrates legacy state files into the unified state.json before Nix evaluation
|
||||
|
||||
STATE_DIR="$HOME/.config/nomarchy"
|
||||
OLD_STATE_DIR="$HOME/.config/home-manager"
|
||||
OLD_TOGGLES_DIR="$HOME/.local/state/nomarchy/toggles"
|
||||
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 "$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
|
||||
[[ -e "$file" ]] || continue
|
||||
filename=$(basename "$file")
|
||||
case "$filename" in
|
||||
suspend-off)
|
||||
$JQ '.suspend = false' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE"
|
||||
;;
|
||||
screensaver-off)
|
||||
$JQ '.screensaver = false' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE"
|
||||
;;
|
||||
skip-vscode-theme-changes)
|
||||
$JQ '.skipVsCodeTheme = true' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE"
|
||||
;;
|
||||
esac
|
||||
rm "$file"
|
||||
done
|
||||
rmdir "$OLD_TOGGLES_DIR" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 2. Migrate existing JSON state files
|
||||
if [[ -f $IDLE_STATE_FILE ]]; then
|
||||
ENABLED=$($JQ '.enabled' "$IDLE_STATE_FILE")
|
||||
if [[ "$ENABLED" == "true" || "$ENABLED" == "false" ]]; then
|
||||
$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
|
||||
|
||||
if [[ -f $NIGHTLIGHT_STATE_FILE ]]; then
|
||||
ENABLED=$($JQ '.enabled' "$NIGHTLIGHT_STATE_FILE")
|
||||
TEMP=$($JQ '.temperature' "$NIGHTLIGHT_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
|
||||
|
||||
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 --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 --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 --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 --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
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Unblock and restart the bluetooth service.
|
||||
|
||||
echo -e "Unblocking bluetooth...\n"
|
||||
rfkill unblock bluetooth
|
||||
rfkill list bluetooth
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Restart the PipeWire audio service to fix audio issues or apply new configuration.
|
||||
|
||||
echo -e "Restarting pipewire audio service...\n"
|
||||
systemctl --user restart pipewire.service
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Unblock and restart the Wi-Fi service.
|
||||
|
||||
echo -e "Unblocking wifi...\n"
|
||||
rfkill unblock wifi
|
||||
rfkill list wifi
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Restart the XCompose input method service (fcitx5) to apply new compose key settings.
|
||||
|
||||
nomarchy-restart-app fcitx5 --disable notificationitem
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Configure DNS declaratively for Nomarchy NixOS.
|
||||
# Hybrid: updates /etc/nixos/state.json and runs sys-update.
|
||||
|
||||
STATE_FILE="/etc/nixos/state.json"
|
||||
|
||||
if [[ -z $1 ]]; then
|
||||
dns=$(gum choose --height 6 --header "Select DNS provider" Cloudflare Google DHCP Custom)
|
||||
else
|
||||
dns=$1
|
||||
fi
|
||||
|
||||
case "$dns" in
|
||||
Cloudflare|Google|DHCP)
|
||||
sudo jq --arg dns "$dns" '.dns = $dns' "$STATE_FILE" > /tmp/state.json && sudo mv /tmp/state.json "$STATE_FILE"
|
||||
;;
|
||||
|
||||
Custom)
|
||||
echo "Enter your DNS servers (space-separated, e.g. '192.168.1.1 1.1.1.1'):"
|
||||
read -r dns_servers
|
||||
|
||||
if [[ -z $dns_servers ]]; then
|
||||
echo "Error: No DNS servers provided."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Convert to JSON array safely
|
||||
dns_array=$(echo "$dns_servers" | jq -R 'split(" ")')
|
||||
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
|
||||
|
||||
echo "DNS configured to $dns. Applying changes..."
|
||||
sudo sys-update
|
||||
@@ -1,26 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Configure FIDO2 support declaratively for Nomarchy NixOS.
|
||||
|
||||
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"
|
||||
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"
|
||||
echo "FIDO2 support enabled. Applying changes..."
|
||||
sudo sys-update
|
||||
|
||||
# Enrollment is still an imperative action
|
||||
if command -v pamu2fcfg &> /dev/null; then
|
||||
echo "Let's register your FIDO2 key now."
|
||||
mkdir -p ~/.config/Yubico
|
||||
pamu2fcfg > ~/.config/Yubico/u2f_keys
|
||||
echo "FIDO2 key registered."
|
||||
else
|
||||
echo "pamu2fcfg not found. It will be available after the next reboot or sys-update."
|
||||
fi
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Configure fingerprint support declaratively for Nomarchy NixOS.
|
||||
|
||||
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"
|
||||
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"
|
||||
echo "Fingerprint support enabled. Applying changes..."
|
||||
sudo sys-update
|
||||
|
||||
# Enrollment is still an imperative action
|
||||
if command -v fprintd-enroll &> /dev/null; then
|
||||
echo "Let's enroll your fingerprint now."
|
||||
fprintd-enroll
|
||||
echo "Fingerprint enrolled."
|
||||
else
|
||||
echo "fprintd not found. It will be available after the next reboot or sys-update."
|
||||
fi
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Prompt for sudo once and keep the credential alive in the background.
|
||||
# Source this script so the trap applies to the calling shell:
|
||||
# source nomarchy-sudo-keepalive
|
||||
|
||||
sudo -v
|
||||
while true; do sudo -n true; sleep 60; done 2>/dev/null &
|
||||
SUDO_KEEPALIVE_PID=$!
|
||||
trap "kill $SUDO_KEEPALIVE_PID 2>/dev/null" EXIT
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Toggle passwordless sudo for the current user.
|
||||
# First run: enables passwordless sudo for 15 minutes (after confirmation).
|
||||
# Second run: disables it early.
|
||||
|
||||
NOPASSWD_FILE="/etc/sudoers.d/99-nomarchy-nopasswd-${USER}"
|
||||
TIMER_NAME="nomarchy-nopasswd-expire-${USER}"
|
||||
|
||||
# Safety: if the file exists but the timer doesn't (e.g. after reboot), clean up
|
||||
if sudo test -f "$NOPASSWD_FILE" && ! systemctl is-active "${TIMER_NAME}.timer" &>/dev/null; then
|
||||
sudo rm "$NOPASSWD_FILE"
|
||||
fi
|
||||
|
||||
# Check for the file directly — sudo -n can stay cached or be granted by other rules
|
||||
if sudo test -f "$NOPASSWD_FILE"; then
|
||||
sudo rm "$NOPASSWD_FILE"
|
||||
sudo systemctl stop "${TIMER_NAME}.timer" 2>/dev/null
|
||||
echo "Passwordless sudo has been DISABLED. Sudo will require a password again."
|
||||
else
|
||||
echo ""
|
||||
echo "⚠️ WARNING: This will allow ANY process running as your user to"
|
||||
echo "execute ANY command as root WITHOUT a password for 15 minutes."
|
||||
echo ""
|
||||
echo "This is useful for AI agents that need to run sudo commands,"
|
||||
echo "but it significantly weakens the security of your system."
|
||||
echo "Anyone or anything with access to your user account gets full root."
|
||||
echo ""
|
||||
echo "Passwordless sudo will automatically disable after 15 minutes."
|
||||
echo "Run this command again to disable it early."
|
||||
echo ""
|
||||
|
||||
if gum confirm "Enable passwordless sudo for 15 minutes? This is a significant security risk!"; then
|
||||
echo "${USER} ALL=(ALL) NOPASSWD: ALL" | sudo tee "$NOPASSWD_FILE" > /dev/null
|
||||
sudo chmod 440 "$NOPASSWD_FILE"
|
||||
sudo systemd-run --on-active=15m --timer-property=AccuracySec=1s --unit="$TIMER_NAME" \
|
||||
rm "$NOPASSWD_FILE"
|
||||
echo "Passwordless sudo has been ENABLED. It will automatically disable in 15 minutes."
|
||||
echo "Note: if you restart before then, run nomarchy-sudo-passwordless-toggle again to disable it."
|
||||
else
|
||||
echo "Aborted. No changes made."
|
||||
fi
|
||||
fi
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Reset the sudo lockout/faillock for the current user.
|
||||
# This clears any failed authentication attempts that may have locked the user out.
|
||||
|
||||
# Resetting sudo lockout for user
|
||||
su -c "faillock --reset --user $USER"
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Logout command that first closes all application windows (thus giving them a chance to save state),
|
||||
# then stops the session, returning to the SDDM login screen.
|
||||
|
||||
# Schedule the session stop after closing windows (detached from terminal)
|
||||
nohup bash -c "sleep 2 && uwsm stop" >/dev/null 2>&1 &
|
||||
|
||||
# Now close all windows
|
||||
nomarchy-hyprland-window-close-all
|
||||
sleep 1 # Allow apps like Chrome to shutdown correctly
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Reboot command that first closes all application windows (thus giving them a chance to save state).
|
||||
# This is particularly helpful for applications like Chromium that otherwise won't shutdown cleanly.
|
||||
|
||||
nomarchy-state clear re*-required
|
||||
|
||||
# Schedule the reboot to happen after closing windows (detached from terminal)
|
||||
nohup bash -c "sleep 2 && systemctl reboot --no-wall" >/dev/null 2>&1 &
|
||||
|
||||
# Now close all windows
|
||||
nomarchy-hyprland-window-close-all
|
||||
sleep 1 # Allow apps like Chrome to shutdown correctly
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Shutdown command that first closes all application windows (thus giving them a chance to save state).
|
||||
# This is particularly helpful for applications like Chromium that otherwise won't shutdown cleanly.
|
||||
|
||||
nomarchy-state clear re*-required
|
||||
|
||||
# Schedule the shutdown to happen after closing windows (detached from terminal)
|
||||
nohup bash -c "sleep 2 && systemctl poweroff --no-wall" >/dev/null 2>&1 &
|
||||
|
||||
# Now close all windows
|
||||
nomarchy-hyprland-window-close-all
|
||||
sleep 1 # Allow apps like Chrome to shutdown correctly
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Toggles the idle daemon (hypridle) between enabled and disabled.
|
||||
# Hybrid: updates state.json and provides instant feedback.
|
||||
|
||||
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 [[ $NOMARCHY_TOGGLE_IDLE == "false" ]]; then
|
||||
NEW_VALUE="true"
|
||||
setsid hypridle >/dev/null 2>&1 &
|
||||
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"
|
||||
fi
|
||||
|
||||
TMP_JSON=$(mktemp)
|
||||
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."
|
||||
|
||||
pkill -RTMIN+9 waybar # Signal waybar if needed
|
||||
@@ -1,29 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Toggles the suspend menu option availability.
|
||||
# Hybrid: updates state.json and runs env-update for persistence.
|
||||
|
||||
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 [[ $NOMARCHY_TOGGLE_SUSPEND == "false" ]]; then
|
||||
NEW_VALUE="true"
|
||||
notify-send -u low " Suspend now available in system menu"
|
||||
else
|
||||
NEW_VALUE="false"
|
||||
notify-send -u low " Suspend removed from system menu"
|
||||
fi
|
||||
|
||||
# Update JSON using jq with --argjson for proper boolean handling
|
||||
TMP_JSON=$(mktemp)
|
||||
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..."
|
||||
|
||||
# Run env-update to apply changes to the menu
|
||||
env-update
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Select system timezone declaratively for Nomarchy NixOS.
|
||||
|
||||
STATE_FILE="/etc/nixos/state.json"
|
||||
|
||||
timezone=$(timedatectl list-timezones | gum filter --height 20 --header "Set timezone") || exit 1
|
||||
|
||||
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
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Nomarchy Update Script
|
||||
# 1. Updates the flake inputs in /etc/nixos
|
||||
# 2. Applies system-wide NixOS changes
|
||||
# 3. Applies user-level Home Manager changes
|
||||
|
||||
set -e
|
||||
|
||||
REPO_DIR="/etc/nixos"
|
||||
|
||||
if [ ! -d "$REPO_DIR" ]; then
|
||||
echo "Error: $REPO_DIR not found. Are you running on a Nomarchy system?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "--- Starting Nomarchy Update ---"
|
||||
|
||||
# 1. Update Flake Lock
|
||||
echo "Updating flake inputs..."
|
||||
sudo nix --extra-experimental-features "nix-command flakes" flake update --flake "$REPO_DIR"
|
||||
|
||||
# 2. Rebuild System
|
||||
echo "Applying system-level updates..."
|
||||
sudo nixos-rebuild switch --flake "$REPO_DIR#default" --impure
|
||||
|
||||
# 3. Rebuild Home Environment
|
||||
echo "Applying user-level updates..."
|
||||
home-manager switch --flake "$REPO_DIR#default" --impure
|
||||
|
||||
# 4. Commit changes if it's a git repo
|
||||
if [ -d "$REPO_DIR/.git" ]; then
|
||||
echo "Committing update to local history..."
|
||||
sudo git -C "$REPO_DIR" add flake.lock
|
||||
sudo git -C "$REPO_DIR" commit -m "chore: update system (flake.lock)" || echo "No lockfile changes to commit."
|
||||
fi
|
||||
|
||||
echo "--- Nomarchy Update Complete ---"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user