initial commit

This commit is contained in:
Bernardo Magri
2026-04-01 17:06:01 +01:00
parent 12cdfaeeef
commit 33deeb494b
526 changed files with 12287 additions and 1 deletions

11
bin/nomarchy-battery-capacity Executable file
View File

@@ -0,0 +1,11 @@
#!/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
}'

24
bin/nomarchy-battery-monitor Executable file
View File

@@ -0,0 +1,24 @@
#!/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

13
bin/nomarchy-battery-present Executable file
View File

@@ -0,0 +1,13 @@
#!/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

9
bin/nomarchy-battery-remaining Executable file
View File

@@ -0,0 +1,9 @@
#!/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
}'

View File

@@ -0,0 +1,25 @@
#!/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
}'

28
bin/nomarchy-battery-status Executable file
View File

@@ -0,0 +1,28 @@
#!/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

17
bin/nomarchy-branch-set Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
# Set the branch for Nomarchy's git repository.
if (($# == 0)); then
echo "Usage: nomarchy-branch-set [master|rc|dev]"
exit 1
else
branch="$1"
fi
if [[ $branch != "master" && $branch != "rc" && $branch != "dev" ]]; then
echo "Error: Invalid branch '$branch'. Must be one of: master, rc, dev"
exit 1
fi
git -C $OMARCHY_PATH switch $branch

21
bin/nomarchy-brightness-display Executable file
View File

@@ -0,0 +1,21 @@
#!/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 '%')"

View File

@@ -0,0 +1,12 @@
#!/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

View File

@@ -0,0 +1,42 @@
#!/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"

17
bin/nomarchy-build-iso Executable file
View File

@@ -0,0 +1,17 @@
#!/usr/bin/env bash
# Build the Nomarchy Installer ISO declaratively using the flake.
echo "Building Nomarchy Installer ISO..."
# The output will be a symlink named 'result' in the current directory
nix build .#nixosConfigurations.installerIso.config.system.build.isoImage
if [ $? -eq 0 ]; then
ISO_PATH=$(readlink -f result/iso/*.iso)
echo "Success! ISO built at: $ISO_PATH"
echo "You can now burn this to a USB drive using 'dd' or 'etcher'."
else
echo "Error: ISO build failed."
exit 1
fi

31
bin/nomarchy-channel-set Executable file
View File

@@ -0,0 +1,31 @@
#!/bin/bash
# Set the Nomarchy channel, which dictates what git branch and package repository is used.
#
# Stable uses the master branch, which only sees updates on official releases, and
# the stable package repository, which typically lags the edge by a month to ensure
# better compatibility.
#
# Edge tracks the latest package repository, but still relies on the master branch,
# so new packages which require config changes may cause conflicts or errors.
#
# Dev tracks the active development dev branch, which may include partial or broken updates,
# as well as the latest package repository. This should only be used by Nomarchy developers
# and people with a lot of experience managing Linux systems.
if (($# == 0)); then
echo "Usage: nomarchy-channel-set [stable|rc|edge|dev]"
exit 1
else
channel="$1"
fi
case "$channel" in
"stable") nomarchy-branch-set "master" && nomarchy-refresh-pacman "stable" ;;
"rc") nomarchy-branch-set "rc" && nomarchy-refresh-pacman "rc" ;;
"edge") nomarchy-branch-set "master" && nomarchy-refresh-pacman "edge" ;;
"dev") nomarchy-branch-set "dev" && nomarchy-refresh-pacman "edge" ;;
*) echo "Unknown channel: $channel"; exit 1; ;;
esac
nomarchy-update -y

66
bin/nomarchy-cmd-audio-switch Executable file
View File

@@ -0,0 +1,66 @@
#!/bin/bash
# Switch between audio outputs while preserving the mute status. By default mapped to Super + Mute.
focused_monitor="$(hyprctl monitors -j | jq -r '.[] | select(.focused == true).name')"
sinks=$(pactl -f json list sinks | jq '[.[] | select((.ports | length == 0) or ([.ports[]? | .availability != "not available"] | any))]')
sinks_count=$(echo "$sinks" | jq '. | length')
if (( sinks_count == 0 )); then
swayosd-client \
--monitor "$focused_monitor" \
--custom-message "No audio devices found"
exit 1
fi
current_sink_name=$(pactl get-default-sink)
current_sink_index=$(echo "$sinks" | jq -r --arg name "$current_sink_name" 'map(.name) | index($name)')
if [[ $current_sink_index != "null" ]]; then
next_sink_index=$(((current_sink_index + 1) % sinks_count))
else
next_sink_index=0
fi
next_sink=$(echo "$sinks" | jq -r ".[$next_sink_index]")
next_sink_name=$(echo "$next_sink" | jq -r '.name')
next_sink_description=$(echo "$next_sink" | jq -r '.description')
if [[ $next_sink_description == "(null)" ]] || [[ $next_sink_description == "null" ]] || [[ -z $next_sink_description ]]; then
# For Bluetooth devices, the friendly name is on the Device entry (device.id), not the Sink entry (object.id)
device_id=$(echo "$next_sink" | jq -r '.properties."device.id"')
if [[ $device_id != "null" ]] && [[ -n $device_id ]]; then
next_sink_description=$(wpctl status | grep -E "^\s*│?\s+${device_id}\." | sed -E 's/^.*[0-9]+\.\s+//' | sed -E 's/\s+\[.*$//')
fi
# Fall back to object.id lookup if device.id didn't yield a result
if [[ -z $next_sink_description ]]; then
sink_id=$(echo "$next_sink" | jq -r '.properties."object.id"')
next_sink_description=$(wpctl status | grep -E "\s+\*?\s+${sink_id}\." | sed -E 's/^.*[0-9]+\.\s+//' | sed -E 's/\s+\[.*$//')
fi
fi
next_sink_volume=$(echo "$next_sink" | jq -r \
'.volume | to_entries[0].value.value_percent | sub("%"; "")')
next_sink_is_muted=$(echo "$next_sink" | jq -r '.mute')
if [[ $next_sink_is_muted = "true" ]] || (( next_sink_volume == 0 )); then
icon_state="muted"
elif (( next_sink_volume <= 33 )); then
icon_state="low"
elif (( next_sink_volume <= 66 )); then
icon_state="medium"
else
icon_state="high"
fi
next_sink_volume_icon="sink-volume-${icon_state}-symbolic"
if [[ $next_sink_name != $current_sink_name ]]; then
pactl set-default-sink "$next_sink_name"
fi
swayosd-client \
--monitor "$focused_monitor" \
--custom-message "$next_sink_description" \
--custom-icon "$next_sink_volume_icon"

22
bin/nomarchy-cmd-first-run Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
# Finish the installation of Nomarchy with items that can only be done after logging in.
set -e
FIRST_RUN_MODE=~/.local/state/nomarchy/first-run.mode
if [[ -f $FIRST_RUN_MODE ]]; then
rm -f "$FIRST_RUN_MODE"
bash "$OMARCHY_PATH/install/first-run/battery-monitor.sh"
bash "$OMARCHY_PATH/install/first-run/cleanup-reboot-sudoers.sh"
bash "$OMARCHY_PATH/install/first-run/firewall.sh"
bash "$OMARCHY_PATH/install/first-run/dns-resolver.sh"
bash "$OMARCHY_PATH/install/first-run/gnome-theme.sh"
bash "$OMARCHY_PATH/install/first-run/elephant.sh"
sudo rm -f /etc/sudoers.d/first-run
bash "$OMARCHY_PATH/install/first-run/welcome.sh"
bash "$OMARCHY_PATH/install/first-run/wifi.sh"
fi

11
bin/nomarchy-cmd-missing Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/bash
# Returns true if any of the commands passed in as arguments are missing on the system.
for cmd in "$@"; do
if ! command -v "$cmd" &>/dev/null; then
exit 0
fi
done
exit 1

9
bin/nomarchy-cmd-present Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
# Returns true if all the commands passed in as arguments exit on the system.
for cmd in "$@"; do
command -v "$cmd" &>/dev/null || exit 1
done
exit 0

184
bin/nomarchy-cmd-screenrecord Executable file
View File

@@ -0,0 +1,184 @@
#!/bin/bash
# Start and stop a screenrecording, which will be saved to ~/Videos by default.
# Alternative location can be set via OMARCHY_SCREENRECORD_DIR or XDG_VIDEOS_DIR ENVs.
# Resolution is capped to 4K for monitors above 4K, native otherwise.
# Override via --resolution= (e.g. --resolution=1920x1080, --resolution=0x0 for native).
[[ -f ~/.config/user-dirs.dirs ]] && source ~/.config/user-dirs.dirs
OUTPUT_DIR="${OMARCHY_SCREENRECORD_DIR:-${XDG_VIDEOS_DIR:-$HOME/Videos}}"
if [[ ! -d $OUTPUT_DIR ]]; then
notify-send "Screen recording directory does not exist: $OUTPUT_DIR" -u critical -t 3000
exit 1
fi
DESKTOP_AUDIO="false"
MICROPHONE_AUDIO="false"
WEBCAM="false"
WEBCAM_DEVICE=""
RESOLUTION=""
STOP_RECORDING="false"
RECORDING_FILE="/tmp/nomarchy-screenrecord-filename"
for arg in "$@"; do
case "$arg" in
--with-desktop-audio) DESKTOP_AUDIO="true" ;;
--with-microphone-audio) MICROPHONE_AUDIO="true" ;;
--with-webcam) WEBCAM="true" ;;
--webcam-device=*) WEBCAM_DEVICE="${arg#*=}" ;;
--resolution=*) RESOLUTION="${arg#*=}" ;;
--stop-recording) STOP_RECORDING="true" ;;
esac
done
start_webcam_overlay() {
cleanup_webcam
# Auto-detect first available webcam if none specified
if [[ -z $WEBCAM_DEVICE ]]; then
WEBCAM_DEVICE=$(v4l2-ctl --list-devices 2>/dev/null | grep -m1 "^[[:space:]]*/dev/video" | tr -d '\t')
if [[ -z $WEBCAM_DEVICE ]]; then
notify-send "No webcam devices found" -u critical -t 3000
return 1
fi
fi
# Get monitor scale
local scale=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .scale')
# Target width (base 360px, scaled to monitor)
local target_width=$(awk "BEGIN {printf \"%.0f\", 360 * $scale}")
# Try preferred 16:9 resolutions in order, use first available
local preferred_resolutions=("640x360" "1280x720" "1920x1080")
local video_size_arg=""
local available_formats=$(v4l2-ctl --list-formats-ext -d "$WEBCAM_DEVICE" 2>/dev/null)
for resolution in "${preferred_resolutions[@]}"; do
if echo "$available_formats" | grep -q "$resolution"; then
video_size_arg="-video_size $resolution"
break
fi
done
ffplay -f v4l2 $video_size_arg -framerate 30 "$WEBCAM_DEVICE" \
-vf "crop=iw/2:ih,scale=${target_width}:-1" \
-window_title "WebcamOverlay" \
-noborder \
-fflags nobuffer -flags low_delay \
-probesize 32 -analyzeduration 0 \
-loglevel quiet &
sleep 1
}
cleanup_webcam() {
pkill -f "WebcamOverlay" 2>/dev/null
}
default_resolution() {
local width height
read -r width height < <(hyprctl monitors -j | jq -r '.[] | select(.focused == true) | "\(.width) \(.height)"')
if ((width > 3840 || height > 2160)); then
echo "3840x2160"
else
echo "0x0"
fi
}
start_screenrecording() {
local filename="$OUTPUT_DIR/screenrecording-$(date +'%Y-%m-%d_%H-%M-%S').mp4"
local audio_devices=""
local audio_args=()
[[ $DESKTOP_AUDIO == "true" ]] && audio_devices+="default_output"
if [[ $MICROPHONE_AUDIO == "true" ]]; then
# Merge audio tracks into one - separate tracks only play one at a time in most players
[[ -n $audio_devices ]] && audio_devices+="|"
audio_devices+="default_input"
fi
[[ -n $audio_devices ]] && audio_args+=(-a "$audio_devices" -ac aac)
local resolution="${RESOLUTION:-$(default_resolution)}"
gpu-screen-recorder -w portal -k auto -s "$resolution" -f 60 -fm cfr -fallback-cpu-encoding yes -o "$filename" "${audio_args[@]}" &
local pid=$!
# Wait for recording to actually start (file appears after portal selection)
while kill -0 $pid 2>/dev/null && [[ ! -f $filename ]]; do
sleep 0.2
done
if kill -0 $pid 2>/dev/null; then
echo "$filename" >"$RECORDING_FILE"
toggle_screenrecording_indicator
fi
}
stop_screenrecording() {
pkill -SIGINT -f "^gpu-screen-recorder" # SIGINT required to save video properly
# Wait a maximum of 5 seconds to finish before hard killing
local count=0
while pgrep -f "^gpu-screen-recorder" >/dev/null && ((count < 50)); do
sleep 0.1
count=$((count + 1))
done
toggle_screenrecording_indicator
cleanup_webcam
if pgrep -f "^gpu-screen-recorder" >/dev/null; then
pkill -9 -f "^gpu-screen-recorder"
notify-send "Screen recording error" "Recording process had to be force-killed. Video may be corrupted." -u critical -t 5000
else
trim_first_frame
local filename=$(cat "$RECORDING_FILE" 2>/dev/null)
local preview="${filename%.mp4}-preview.png"
# Generate a preview thumbnail from the first frame
ffmpeg -y -i "$filename" -ss 00:00:00.1 -vframes 1 -q:v 2 "$preview" -loglevel quiet 2>/dev/null
(
ACTION=$(notify-send "Screen recording saved" "Open with Super + Alt + , (or click this)" -t 10000 -i "${preview:-$filename}" -A "default=open")
[[ $ACTION == "default" ]] && mpv "$filename"
rm -f "$preview"
) &
fi
rm -f "$RECORDING_FILE"
}
toggle_screenrecording_indicator() {
pkill -RTMIN+8 waybar
}
screenrecording_active() {
pgrep -f "^gpu-screen-recorder" >/dev/null
}
trim_first_frame() {
local latest
latest=$(cat "$RECORDING_FILE" 2>/dev/null)
if [[ -n $latest && -f $latest ]]; then
local trimmed="${latest%.mp4}-trimmed.mp4"
if ffmpeg -y -ss 0.1 -i "$latest" -c copy "$trimmed" -loglevel quiet 2>/dev/null; then
mv "$trimmed" "$latest"
else
rm -f "$trimmed"
fi
fi
}
if screenrecording_active; then
stop_screenrecording
elif [[ $STOP_RECORDING == "true" ]]; then
exit 1
else
[[ $WEBCAM == "true" ]] && start_webcam_overlay
start_screenrecording || cleanup_webcam
fi

36
bin/nomarchy-cmd-screensaver Executable file
View File

@@ -0,0 +1,36 @@
#!/bin/bash
# Run the Nomarchy screensaver using random effects from TTE.
screensaver_in_focus() {
hyprctl activewindow -j | jq -e '.class == "org.nomarchy.screensaver"' >/dev/null 2>&1
}
exit_screensaver() {
hyprctl keyword cursor:invisible false &>/dev/null || true
pkill -x tte 2>/dev/null
pkill -f org.nomarchy.screensaver 2>/dev/null
exit 0
}
# Exit the screensaver on signals and input from keyboard and mouse
trap exit_screensaver SIGINT SIGTERM SIGHUP SIGQUIT
printf '\033]11;rgb:00/00/00\007' # Set background color to black
hyprctl keyword cursor:invisible true &>/dev/null
tty=$(tty 2>/dev/null)
while true; do
tte -i ~/.config/nomarchy/branding/screensaver.txt \
--frame-rate 120 --canvas-width 0 --canvas-height 0 --reuse-canvas --anchor-canvas c --anchor-text c\
--random-effect --exclude-effects dev_worm \
--no-eol --no-restore-cursor &
while pgrep -t "${tty#/dev/}" -x tte >/dev/null; do
if read -n1 -t 1 || ! screensaver_in_focus; then
exit_screensaver
fi
done
done

133
bin/nomarchy-cmd-screenshot Executable file
View File

@@ -0,0 +1,133 @@
#!/bin/bash
# Take a screenshot of the whole screen, a specific window, or a user-drawn region.
# Saves to ~/Pictures by default, but that can be changed via OMARCHY_SCREENSHOT_DIR or XDG_PICTURES_DIR ENVs.
# Editor defaults to Satty but can be changed via --editor=<name> or OMARCHY_SCREENSHOT_EDITOR env
[[ -f ~/.config/user-dirs.dirs ]] && source ~/.config/user-dirs.dirs
OUTPUT_DIR="${OMARCHY_SCREENSHOT_DIR:-${XDG_PICTURES_DIR:-$HOME/Pictures}}"
if [[ ! -d $OUTPUT_DIR ]]; then
notify-send "Screenshot directory does not exist: $OUTPUT_DIR" -u critical -t 3000
exit 1
fi
pkill slurp && exit 0
SCREENSHOT_EDITOR="${OMARCHY_SCREENSHOT_EDITOR:-satty}"
# Parse --editor flag from any position
ARGS=()
for arg in "$@"; do
if [[ $arg == --editor=* ]]; then
SCREENSHOT_EDITOR="${arg#--editor=}"
else
ARGS+=("$arg")
fi
done
set -- "${ARGS[@]}"
open_editor() {
local filepath="$1"
if [[ $SCREENSHOT_EDITOR == "satty" ]]; then
satty --filename "$filepath" \
--output-filename "$filepath" \
--actions-on-enter save-to-clipboard \
--save-after-copy \
--copy-command 'wl-copy'
else
$SCREENSHOT_EDITOR "$filepath"
fi
}
MODE="${1:-smart}"
PROCESSING="${2:-slurp}"
# accounting for portrait/transformed displays
JQ_MONITOR_GEO='
def format_geo:
.x as $x | .y as $y |
(.width / .scale | floor) as $w |
(.height / .scale | floor) as $h |
.transform as $t |
if $t == 1 or $t == 3 then
"\($x),\($y) \($h)x\($w)"
else
"\($x),\($y) \($w)x\($h)"
end;
'
get_rectangles() {
local active_workspace=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .activeWorkspace.id')
hyprctl monitors -j | jq -r --arg ws "$active_workspace" "${JQ_MONITOR_GEO} .[] | select(.activeWorkspace.id == (\$ws | tonumber)) | format_geo"
hyprctl clients -j | jq -r --arg ws "$active_workspace" '.[] | select(.workspace.id == ($ws | tonumber)) | "\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"'
}
# Select based on mode
case "$MODE" in
region)
hyprpicker -r -z >/dev/null 2>&1 &
PID=$!
sleep .1
SELECTION=$(slurp 2>/dev/null)
kill $PID 2>/dev/null
;;
windows)
hyprpicker -r -z >/dev/null 2>&1 &
PID=$!
sleep .1
SELECTION=$(get_rectangles | slurp -r 2>/dev/null)
kill $PID 2>/dev/null
;;
fullscreen)
SELECTION=$(hyprctl monitors -j | jq -r "${JQ_MONITOR_GEO} .[] | select(.focused == true) | format_geo")
;;
smart | *)
RECTS=$(get_rectangles)
hyprpicker -r -z >/dev/null 2>&1 &
PID=$!
sleep .1
SELECTION=$(echo "$RECTS" | slurp 2>/dev/null)
kill $PID 2>/dev/null
# If the selection area is L * W < 20, we'll assume you were trying to select whichever
# window or output it was inside of to prevent accidental 2px snapshots
if [[ $SELECTION =~ ^([0-9]+),([0-9]+)[[:space:]]([0-9]+)x([0-9]+)$ ]]; then
if ((${BASH_REMATCH[3]} * ${BASH_REMATCH[4]} < 20)); then
click_x="${BASH_REMATCH[1]}"
click_y="${BASH_REMATCH[2]}"
while IFS= read -r rect; do
if [[ $rect =~ ^([0-9]+),([0-9]+)[[:space:]]([0-9]+)x([0-9]+) ]]; then
rect_x="${BASH_REMATCH[1]}"
rect_y="${BASH_REMATCH[2]}"
rect_width="${BASH_REMATCH[3]}"
rect_height="${BASH_REMATCH[4]}"
if ((click_x >= rect_x && click_x < rect_x + rect_width && click_y >= rect_y && click_y < rect_y + rect_height)); then
SELECTION="${rect_x},${rect_y} ${rect_width}x${rect_height}"
break
fi
fi
done <<<"$RECTS"
fi
fi
;;
esac
[[ -z $SELECTION ]] && exit 0
FILENAME="screenshot-$(date +'%Y-%m-%d_%H-%M-%S').png"
FILEPATH="$OUTPUT_DIR/$FILENAME"
if [[ $PROCESSING == "slurp" ]]; then
grim -g "$SELECTION" "$FILEPATH" || exit 1
wl-copy <"$FILEPATH"
(
ACTION=$(notify-send "Screenshot saved to clipboard and file" "Edit with Super + Alt + , (or click this)" -t 10000 -i "$FILEPATH" -A "default=edit")
[[ $ACTION == "default" ]] && open_editor "$FILEPATH"
) &
else
grim -g "$SELECTION" - | wl-copy
fi

46
bin/nomarchy-cmd-share Executable file
View File

@@ -0,0 +1,46 @@
#!/bin/bash
# Share clipboard, file, or folder using LocalSend. Bound to Super + Ctrl + S by default.
if (($# == 0)); then
echo "Usage: nomarchy-cmd-share [clipboard|file|folder]"
exit 1
fi
MODE="$1"
shift
if [[ $MODE == "clipboard" ]]; then
TEMP_FILE=$(mktemp --suffix=.txt)
wl-paste >"$TEMP_FILE"
FILES="$TEMP_FILE"
else
if (($# > 0)); then
FILES="$*"
else
if [[ $MODE == "folder" ]]; then
# Pick a single folder from home directory
FILES=$(find "$HOME" -type d 2>/dev/null | fzf)
else
# Pick one or more files from home directory
FILES=$(find "$HOME" -type f 2>/dev/null | fzf --multi)
fi
[[ -z $FILES ]] && exit 0
fi
fi
# Run LocalSend in its own systemd service (detached from terminal)
# Convert newline-separated files to space-separated arguments
if [[ $MODE != "clipboard" ]] && echo "$FILES" | grep -q $'\n'; then
# Multiple files selected - convert newlines to array
readarray -t FILE_ARRAY <<<"$FILES"
systemd-run --user --quiet --collect localsend --headless send "${FILE_ARRAY[@]}"
else
# Single file or clipboard mode
systemd-run --user --quiet --collect localsend --headless send "$FILES"
fi
# Note: Temporary file will remain until system cleanup for clipboard mode
# This ensures the file content is available for the LocalSend GUI
exit 0

22
bin/nomarchy-cmd-terminal-cwd Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
# Returns the current working directory of the active terminal window,
# so a new terminal window can be started in the same directory.
# Go from current active terminal to its child shell process and run cwd there
terminal_pid=$(hyprctl activewindow | awk '/pid:/ {print $2}')
shell_pid=$(pgrep -P "$terminal_pid" | tail -n1)
if [[ -n $shell_pid ]]; then
cwd=$(readlink -f "/proc/$shell_pid/cwd" 2>/dev/null)
shell=$(readlink -f "/proc/$shell_pid/exe" 2>/dev/null)
# Check if $shell is a valid shell and $cwd is a directory.
if grep -qs "$shell" /etc/shells && [[ -d $cwd ]]; then
echo "$cwd"
else
echo "$HOME"
fi
else
echo "$HOME"
fi

45
bin/nomarchy-config-direct-boot Executable file
View File

@@ -0,0 +1,45 @@
#!/bin/bash
# Add an EFI boot entry for the Nomarchy UKI, allowing the system to boot directly
# without a bootloader like Limine. Requires UEFI firmware and a built UKI.
if [[ ! -d /sys/firmware/efi ]]; then
echo "Error: System is not booted in UEFI mode" >&2
exit 1
fi
if ! efibootmgr &>/dev/null; then
echo "Error: efibootmgr is not available or not functional" >&2
exit 1
fi
if cat /sys/class/dmi/id/bios_vendor 2>/dev/null | grep -qi "American Megatrends"; then
echo "Error: American Megatrends firmware may not safely support custom EFI entries" >&2
exit 1
fi
if cat /sys/class/dmi/id/bios_vendor 2>/dev/null | grep -qi "Apple"; then
echo "Error: Apple firmware uses its own boot manager" >&2
exit 1
fi
uki_file=$(find /boot/EFI/Linux/ -name "nomarchy*.efi" -printf "%f\n" 2>/dev/null | head -1)
if [[ -z $uki_file ]]; then
echo "Error: No Nomarchy UKI found in /boot/EFI/Linux/" >&2
exit 1
fi
boot_source=$(findmnt -n -o SOURCE /boot)
disk=$(echo "$boot_source" | sed 's/p\?[0-9]*$//')
part=$(echo "$boot_source" | grep -o 'p\?[0-9]*$' | sed 's/^p//')
if gum confirm "Setup direct boot (so snapshot booting must be done via bios)?"; then
echo "Creating EFI boot entry for $uki_file"
sudo efibootmgr --create \
--disk "$disk" \
--part "$part" \
--label "Nomarchy" \
--loader "\\EFI\\Linux\\$uki_file"
fi

95
bin/nomarchy-debug Executable file
View File

@@ -0,0 +1,95 @@
#!/bin/bash
# Return exhaustive debugging information about the system to help diagnose problems.
NO_SUDO=false
PRINT_ONLY=false
while (( $# > 0 )); do
case "$1" in
--no-sudo)
NO_SUDO=true
shift
;;
--print)
PRINT_ONLY=true
shift
;;
*)
echo "Unknown option: $1"
echo "Usage: nomarchy-debug [--no-sudo] [--print]"
exit 1
;;
esac
done
LOG_FILE="/tmp/nomarchy-debug.log"
if [[ $NO_SUDO = "true" ]]; then
DMESG_OUTPUT="(skipped - --no-sudo flag used)"
else
DMESG_OUTPUT="$(sudo dmesg)"
fi
cat > "$LOG_FILE" <<EOF
Date: $(date)
Hostname: $(hostname)
Nomarchy Branch: $(git -C "$OMARCHY_PATH" branch --show-current 2>/dev/null || echo "unknown")
=========================================
SYSTEM INFORMATION
=========================================
$(inxi -Farz)
=========================================
DMESG
=========================================
$DMESG_OUTPUT
=========================================
JOURNALCTL (CURRENT BOOT, WARNINGS+ERRORS)
=========================================
$(journalctl -b -p 4..1)
=========================================
INSTALLED PACKAGES
=========================================
$({ expac -S '%n %v (%r)' $(pacman -Qqe) 2>/dev/null; comm -13 <(pacman -Sql | sort) <(pacman -Qqe | sort) | xargs -r expac -Q '%n %v (AUR)'; } | sort)
EOF
if [[ $PRINT_ONLY = "true" ]]; then
cat "$LOG_FILE"
exit 0
fi
OPTIONS=("View log" "Save in current directory")
if ping -c 1 8.8.8.8 >/dev/null 2>&1; then
OPTIONS=("Upload log" "${OPTIONS[@]}")
fi
ACTION=$(gum choose "${OPTIONS[@]}")
case "$ACTION" in
"Upload log")
echo "Uploading debug log to 0x0.st..."
URL=$(curl -sF "file=@$LOG_FILE" -Fexpires=24 https://0x0.st)
if (( $? == 0 )) && [[ -n $URL ]]; then
echo "✓ Log uploaded successfully!"
echo "Share this URL:"
echo ""
echo " $URL"
echo ""
echo "This link will expire in 24 hours."
else
echo "Error: Failed to upload log file"
exit 1
fi
;;
"View log")
less "$LOG_FILE"
;;
"Save in current directory")
cp "$LOG_FILE" "./nomarchy-debug.log"
echo "✓ Log saved to $(pwd)/nomarchy-debug.log"
;;
esac

14
bin/nomarchy-dev-add-migration Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/bash
# Creates a new Nomarchy migration named after the unix timestamp of the last commit.
# Only intended for Nomarchy developers.
cd ~/.local/share/nomarchy
migration_file="$HOME/.local/share/nomarchy/migrations/$(git log -1 --format=%cd --date=unix).sh"
touch $migration_file
if [[ $1 != "--no-edit" ]]; then
nvim $migration_file
fi
echo $migration_file

49
bin/nomarchy-drive-info Executable file
View File

@@ -0,0 +1,49 @@
#!/bin/bash
# Returns drive information about a given volumne, like /dev/nvme0, which is used by nomarchy-drive-select.
if (($# == 0)); then
echo "Usage: nomarchy-drive-info [/dev/drive]"
exit 1
else
drive="$1"
fi
# Find the root drive in case we are looking at partitions
root_drive=$(lsblk -no PKNAME "$drive" 2>/dev/null | tail -n1)
if [[ -n $root_drive ]]; then
root_drive="/dev/$root_drive"
else
root_drive="$drive"
fi
# Get basic disk information
size=$(lsblk -dno SIZE "$drive" 2>/dev/null)
vendor=$(lsblk -dno VENDOR "$root_drive" 2>/dev/null | sed 's/ *$//')
model=$(lsblk -dno MODEL "$root_drive" 2>/dev/null | sed 's/ *$//')
# Combine vendor and model, avoiding duplication
label=""
if [[ -n $vendor && -n $model ]]; then
if [[ $model == *$vendor* ]]; then
label="$model"
else
label="$vendor $model"
fi
elif [[ -n $model ]]; then
label="$model"
elif [[ -n $vendor ]]; then
label="$vendor"
fi
# Format display string
display="$drive"
[[ -n $size ]] && display="$display ($size)"
[[ -n $label ]] && display="$display - $label"
# Append compact partition summary
part_summary=$(lsblk -nro TYPE,NAME,FSTYPE,MOUNTPOINT "$root_drive" 2>/dev/null | \
awk '$1=="part" { printf "%s%s%s", s, ($3==""?"unknown":$3), ($4==""?"":"("$4")"); s=", " }')
[[ -n $part_summary ]] && display+=" [$part_summary]"
echo "$display"

18
bin/nomarchy-drive-select Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/bash
# Select a drive from a list with info that includes space and brand. Used by nomarchy-drive-set-password.
if (($# == 0)); then
drives=$(lsblk -dpno NAME | grep -E '/dev/(sd|hd|vd|nvme|mmcblk|xv)')
else
drives="$@"
fi
drives_with_info=""
while IFS= read -r drive; do
[[ -n $drive ]] || continue
drives_with_info+="$(nomarchy-drive-info "$drive")"$'\n'
done <<<"$drives"
selected_drive="$(printf "%s" "$drives_with_info" | gum choose --header "Select drive")" || exit 1
printf "%s\n" "$selected_drive" | awk '{print $1}'

23
bin/nomarchy-drive-set-password Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
# Set a new encryption password for a drive selected.
encrypted_drives=$(blkid -t TYPE=crypto_LUKS -o device)
if [[ -n $encrypted_drives ]]; then
if (( $(wc -l <<<encrypted_drives) == 1 )); then
drive_to_change="$encrypted_drives"
else
drive_to_change="$(nomarchy-drive-select "$encrypted_drives")"
fi
if [[ -n $drive_to_change ]]; then
echo "Changing full-disk encryption password for $drive_to_change"
sudo cryptsetup luksChangeKey --pbkdf argon2id --iter-time 2000 "$drive_to_change"
else
echo "No drive selected."
fi
else
echo "No encrypted drives available."
exit 1
fi

6
bin/nomarchy-font-current Executable file
View File

@@ -0,0 +1,6 @@
#!/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

5
bin/nomarchy-font-list Executable file
View File

@@ -0,0 +1,5 @@
#!/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

46
bin/nomarchy-font-set Executable file
View File

@@ -0,0 +1,46 @@
#!/bin/bash
# Set the system-wide monospace font that should be used by the terminal, hyprlock, waybar, swayosd, etc.
# The font name must be one of the ones returned by nomarchy-font-list.
font_name="$1"
if [[ -n $font_name ]]; then
if fc-list | grep -iq "$font_name"; then
if [[ -f ~/.config/alacritty/alacritty.toml ]]; then
sed -i "s/family = \".*\"/family = \"$font_name\"/g" ~/.config/alacritty/alacritty.toml
fi
if [[ -f ~/.config/kitty/kitty.conf ]]; then
sed -i "s/^font_family .*/font_family $font_name/g" ~/.config/kitty/kitty.conf
pkill -USR1 kitty
fi
if [[ -f ~/.config/ghostty/config ]]; then
sed -i "s/font-family = \".*\"/font-family = \"$font_name\"/g" ~/.config/ghostty/config
pkill -SIGUSR2 ghostty
fi
sed -i "s/font_family = .*/font_family = $font_name/g" ~/.config/hypr/hyprlock.conf
sed -i "s/font-family: .*/font-family: '$font_name';/g" ~/.config/waybar/style.css
sed -i "s/font-family: .*/font-family: '$font_name';/g" ~/.config/swayosd/style.css
xmlstarlet ed -L \
-u '//match[@target="pattern"][test/string="monospace"]/edit[@name="family"]/string' \
-v "$font_name" \
~/.config/fontconfig/fonts.conf
nomarchy-restart-waybar
nomarchy-restart-swayosd
if pgrep -x ghostty; then
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
else
echo "Usage: nomarchy-font-set <font-name>"
fi

95
bin/nomarchy-haptic-touchpad Executable file
View File

@@ -0,0 +1,95 @@
#!/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()

View File

@@ -0,0 +1,18 @@
#!/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

59
bin/nomarchy-hibernation-remove Executable file
View File

@@ -0,0 +1,59 @@
#!/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"

102
bin/nomarchy-hibernation-setup Executable file
View File

@@ -0,0 +1,102 @@
#!/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
# Ensure keyboard backlight doesn't prevent sleep
sudo cp -p "$OMARCHY_PATH/default/systemd/system-sleep/keyboard-backlight" /usr/lib/systemd/system-sleep/
# 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

18
bin/nomarchy-hook Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/bash
# Run a named hook, like post-update (available in ~/.config/nomarchy/hooks/post-update).
set -e
if (( $# < 1 )); then
echo "Usage: nomarchy-hook [name] [args...]"
exit 1
fi
HOOK=$1
HOOK_PATH="$HOME/.config/nomarchy/hooks/$1"
shift
if [[ -f $HOOK_PATH ]]; then
bash "$HOOK_PATH" "$@"
fi

6
bin/nomarchy-hw-asus-rog Executable file
View File

@@ -0,0 +1,6 @@
#!/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

6
bin/nomarchy-hw-framework16 Executable file
View File

@@ -0,0 +1,6 @@
#!/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"

5
bin/nomarchy-hw-intel Executable file
View File

@@ -0,0 +1,5 @@
#!/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" ]]

5
bin/nomarchy-hw-intel-ptl Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
# Detect whether the computer has an Intel Panther Lake GPU.
lspci | grep -iE 'vga|3d|display' | grep -qi 'panther lake'

6
bin/nomarchy-hw-match Executable file
View File

@@ -0,0 +1,6 @@
#!/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

6
bin/nomarchy-hw-surface Executable file
View File

@@ -0,0 +1,6 @@
#!/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"

6
bin/nomarchy-hw-vulkan Executable file
View File

@@ -0,0 +1,6 @@
#!/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 .

View File

@@ -0,0 +1,5 @@
#!/bin/bash
# Toggles transparency for the currently focused window.
hyprctl dispatch setprop "address:$(hyprctl activewindow -j | jq -r '.address')" opaque toggle

View File

@@ -0,0 +1,24 @@
#!/bin/bash
# Get the active monitor (the one with the cursor)
MONITOR_INFO=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true)')
ACTIVE_MONITOR=$(echo "$MONITOR_INFO" | jq -r '.name')
CURRENT_SCALE=$(echo "$MONITOR_INFO" | jq -r '.scale')
WIDTH=$(echo "$MONITOR_INFO" | jq -r '.width')
HEIGHT=$(echo "$MONITOR_INFO" | jq -r '.height')
REFRESH_RATE=$(echo "$MONITOR_INFO" | jq -r '.refreshRate')
# Cycle through scales: 1 → 1.6 → 2 → 3 → 1
CURRENT_INT=$(awk -v s="$CURRENT_SCALE" 'BEGIN { printf "%.0f", s * 10 }')
case "$CURRENT_INT" in
10) NEW_SCALE=1.6 ;;
16) NEW_SCALE=2 ;;
20) NEW_SCALE=3 ;;
*) NEW_SCALE=1 ;;
esac
hyprctl keyword misc:disable_scale_notification true
hyprctl keyword monitor "$ACTIVE_MONITOR,${WIDTH}x${HEIGHT}@${REFRESH_RATE},auto,$NEW_SCALE"
hyprctl keyword misc:disable_scale_notification false
notify-send -u low "󰍹 Display scaling set to ${NEW_SCALE}x"

View File

@@ -0,0 +1,9 @@
#!/bin/bash
# Close all open windows
hyprctl clients -j | \
jq -r ".[].address" | \
xargs -I{} hyprctl dispatch closewindow address:{}
# Move to first workspace
hyprctl dispatch workspace 1

View File

@@ -0,0 +1,15 @@
#!/bin/bash
# Toggles the window gaps globally between no gaps and the default 10/5/2.
gaps=$(hyprctl getoption general:gaps_out -j | jq -r '.custom' | awk '{print $1}')
if [[ $gaps == "0" ]]; then
hyprctl keyword general:gaps_out 10
hyprctl keyword general:gaps_in 5
hyprctl keyword general:border_size 2
else
hyprctl keyword general:gaps_out 0
hyprctl keyword general:gaps_in 0
hyprctl keyword general:border_size 0
fi

View File

@@ -0,0 +1,46 @@
#!/bin/bash
# Toggle to pop-out a tile to stay fixed on a display basis.
# Usage:
# nomarchy-hyprland-window-pop [width height [x y]]
#
# Arguments:
# width Optional. Width of the floating window. Default: 1300
# height Optional. Height of the floating window. Default: 900
# x Optional. X position of the window. Must provide both X and Y to take effect.
# y Optional. Y position of the window. Must provide both X and Y to take effect.
#
# Behavior:
# - If the window is already pinned, it will be unpinned and removed from the pop layer.
# - If the window is not pinned, it will be floated, resized, moved/centered, pinned, brought to top, and popped.
width=${1:-1300}
height=${2:-900}
x=${3:-}
y=${4:-}
active=$(hyprctl activewindow -j)
pinned=$(echo "$active" | jq ".pinned")
addr=$(echo "$active" | jq -r ".address")
if [[ $pinned == "true" ]]; then
hyprctl -q --batch \
"dispatch pin address:$addr;" \
"dispatch togglefloating address:$addr;" \
"dispatch tagwindow -pop address:$addr;"
elif [[ -n $addr ]]; then
hyprctl dispatch togglefloating address:$addr
hyprctl dispatch resizeactive exact $width $height address:$addr
if [[ -n $x && -n $y ]]; then
hyprctl dispatch moveactive $x $y address:$addr
else
hyprctl dispatch centerwindow address:$addr
fi
hyprctl -q --batch \
"dispatch pin address:$addr;" \
"dispatch alterzorder top address:$addr;" \
"dispatch tagwindow +pop address:$addr;"
fi

View File

@@ -0,0 +1,13 @@
#!/bin/bash
# Check current single_window_aspect_ratio setting
CURRENT_VALUE=$(hyprctl getoption "layout:single_window_aspect_ratio" 2>/dev/null | head -1)
# Parse vec2 output: "vec2: [1, 1]" or "vec2: [0, 0]"
if [[ $CURRENT_VALUE == *"[1, 1]"* ]]; then
hyprctl keyword layout:single_window_aspect_ratio "0 0"
notify-send -u low " Disable single-window square aspect ratio"
else
hyprctl keyword layout:single_window_aspect_ratio "1 1"
notify-send -u low " Enable single-window square aspect"
fi

View File

@@ -0,0 +1,14 @@
#!/bin/bash
# Toggle the layout on the current active workspace between dwindle and scrolling
ACTIVE_WORKSPACE=$(hyprctl activeworkspace -j | jq -r '.id')
CURRENT_LAYOUT=$(hyprctl activeworkspace -j | jq -r '.tiledLayout')
case "$CURRENT_LAYOUT" in
dwindle) NEW_LAYOUT=scrolling ;;
*) NEW_LAYOUT=dwindle ;;
esac
hyprctl keyword workspace $ACTIVE_WORKSPACE, layout:$NEW_LAYOUT
notify-send -u low "󱂬 Workspace layout set to $NEW_LAYOUT"

View File

@@ -0,0 +1,16 @@
#!/bin/bash
# Allow Chromium to sign in to Google accounts by adding the correct
# oauth client id and secret to ~/.config/chromium-flags.conf.
if [[ -f ~/.config/chromium-flags.conf ]]; then
CONF=~/.config/chromium-flags.conf
grep -qxF -- "--oauth2-client-id=77185425430.apps.googleusercontent.com" "$CONF" ||
echo "--oauth2-client-id=77185425430.apps.googleusercontent.com" >>"$CONF"
grep -qxF -- "--oauth2-client-secret=OTJgUOQcT7lO7GsGZq2G4IlT" "$CONF" ||
echo "--oauth2-client-secret=OTJgUOQcT7lO7GsGZq2G4IlT" >>"$CONF"
echo "Now you can login to your Google Account in Chromium."
fi

151
bin/nomarchy-install-dev-env Executable file
View File

@@ -0,0 +1,151 @@
#!/bin/bash
# Install one of the supported development environments. Usually called via Install > Development > * in the Nomarchy Menu.
if [[ -z $1 ]]; then
echo "Usage: nomarchy-install-dev-env <ruby|node|bun|deno|go|laravel|symfony|php|python|elixir|phoenix|rust|java|zig|ocaml|dotnet|clojure|scala>" >&2
exit 1
fi
install_php() {
nomarchy-pkg-add php composer php-sqlite xdebug
# Install Path for Composer
if [[ :$PATH: != *:$HOME/.config/composer/vendor/bin:* ]]; then
echo 'export PATH="$HOME/.config/composer/vendor/bin:$PATH"' >>"$HOME/.bashrc"
source "$HOME/.bashrc"
echo "Added Composer global bin directory to PATH."
else
echo "Composer global bin directory already in PATH."
fi
# Enable some extensions
local php_ini_path="/etc/php/php.ini"
local extensions_to_enable=(
"bcmath"
"intl"
"iconv"
"openssl"
"pdo_sqlite"
"pdo_mysql"
)
# Enable Xdebug
sudo sed -i \
-e 's/^;zend_extension=xdebug.so/zend_extension=xdebug.so/' \
-e 's/^;xdebug.mode=debug/xdebug.mode=debug/' \
/etc/php/conf.d/xdebug.ini
for ext in "${extensions_to_enable[@]}"; do
sudo sed -i "s/^;extension=${ext}/extension=${ext}/" "$php_ini_path"
done
}
install_node() {
echo -e "Installing Node.js...\n"
mise use --global node
}
case "$1" in
ruby)
echo -e "Installing Ruby on Rails...\n"
nomarchy-pkg-add libyaml
mise settings add ruby.compile false
mise settings add idiomatic_version_file_enable_tools ruby
mise use --global ruby@latest
echo "gem: --no-document" >~/.gemrc
mise x ruby -- gem install rails --no-document
echo -e "\nYou can now run: rails new myproject"
;;
node)
install_node
;;
bun)
echo -e "Installing Bun...\n"
mise use -g bun@latest
;;
deno)
echo -e "Installing Deno...\n"
mise use -g deno@latest
;;
go)
echo -e "Installing Go...\n"
mise use --global go@latest
;;
php)
echo -e "Installing PHP...\n"
install_php
;;
laravel)
echo -e "Installing PHP and Laravel...\n"
install_php
install_node
composer global require laravel/installer
echo -e "\nYou can now run: laravel new myproject"
;;
symfony)
echo -e "Installing PHP and Symfony...\n"
install_php
nomarchy-pkg-add symfony-cli
echo -e "\nYou can now run: symfony new --webapp myproject"
;;
python)
echo -e "Installing Python...\n"
mise use --global python@latest
echo -e "\nInstalling uv...\n"
curl -fsSL https://astral.sh/uv/install.sh | sh
;;
elixir)
echo -e "Installing Elixir...\n"
mise use --global erlang@latest
mise use --global elixir@latest
mise x elixir -- mix local.hex --force
;;
phoenix)
echo -e "Installing Phoenix Framework...\n"
# Ensure Erlang/Elixir first
mise use --global erlang@latest
mise use --global elixir@latest
# Hex & Rebar
mise x elixir -- mix local.hex --force
mise x elixir -- mix local.rebar --force
# Phoenix project (phx_new)
mise x elixir -- mix archive.install hex phx_new --force
echo -e "\nYou can now run: mix phx.new my_app"
;;
rust)
echo -e "Installing Rust...\n"
bash -c "$(curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs)" -- -y
;;
java)
echo -e "Installing Java...\n"
mise use --global java@latest
;;
zig)
echo -e "Installing Zig...\n"
mise use --global zig@latest
mise use -g zls@latest
;;
ocaml)
echo -e "Installing OCaml...\n"
bash -c "$(curl -fsSL https://raw.githubusercontent.com/ocaml/opam/master/shell/install.sh)"
opam init --yes
eval "$(opam env)"
opam install ocaml-lsp-server odoc ocamlformat utop --yes
;;
dotnet)
echo -e "Installing .NET...\n"
mise use --global dotnet@latest
;;
clojure)
echo -e "Installing Clojure...\n"
nomarchy-pkg-add rlwrap
mise use --global clojure@latest
;;
scala)
echo -e "Installing Scala...\n"
mise use --global java@latest
mise use --global scala@latest
mise use --global scala-cli@latest
;;
esac

27
bin/nomarchy-install-docker-dbs Executable file
View File

@@ -0,0 +1,27 @@
#!/bin/bash
# Install one of the supported databases in a Docker container with the suitable development options.
# Usually called via Install > Development > Docker DB from the Nomarchy Menu.
options=("MySQL" "PostgreSQL" "Redis" "MongoDB" "MariaDB" "MSSQL")
if (( $# == 0 )); then
choices=$(printf "%s\n" "${options[@]}" | gum choose --header "Select database (return to install, esc to cancel)") || main_menu
else
choices="$@"
fi
if [[ -n $choices ]]; then
for db in $choices; do
case $db in
MySQL) sudo docker run -d --restart unless-stopped -p "127.0.0.1:3306:3306" --name=mysql8 -e MYSQL_ROOT_PASSWORD= -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql:8.4 ;;
PostgreSQL) sudo docker run -d --restart unless-stopped -p "127.0.0.1:5432:5432" --name=postgres18 -e POSTGRES_HOST_AUTH_METHOD=trust postgres:18 ;;
MariaDB) sudo docker run -d --restart unless-stopped -p "127.0.0.1:3306:3306" --name=mariadb11 -e MARIADB_ROOT_PASSWORD= -e MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=true mariadb:11.8 ;;
Redis) sudo docker run -d --restart unless-stopped -p "127.0.0.1:6379:6379" --name=redis redis:7 ;;
MongoDB) sudo docker run -d --restart unless-stopped -p "127.0.0.1:27017:27017" --name mongodb -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD=admin123 mongo:noble ;;
MSSQL) sudo docker run -d --restart unless-stopped -p "127.0.0.1:1433:1433" --name mssql -e MSSQL_PID=Developer -e ACCEPT_EULA=Y -e "MSSQL_SA_PASSWORD=@dmin123" mcr.microsoft.com/mssql/server:2022-CU12-ubuntu-22.04 ;;
esac
done
else
echo "No databases selected for installation."
fi

10
bin/nomarchy-install-dropbox Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/bash
# Install and start the Dropbox service. Must then be authenticated via the web.
echo "Installing all dependencies..."
nomarchy-pkg-add dropbox dropbox-cli libappindicator-gtk3 python-gpgme nautilus-dropbox
echo "Starting Dropbox..."
uwsm-app -- dropbox-cli start &>/dev/null &
echo "See Dropbox icon behind  hover tray in top right and right-click for setup."

View File

@@ -0,0 +1,17 @@
#!/bin/bash
# Install and launch Geforce Now.
set -e
nomarchy-pkg-add flatpak
cd /tmp
# Download and run GeForce NOW
curl -LO https://international.download.nvidia.com/GFNLinux/GeForceNOWSetup.bin
chmod +x GeForceNOWSetup.bin
./GeForceNOWSetup.bin
# Ensure a separate browser process not started by GFN is available.
# If not, it seems like GFN has a tendency to hang on login.
setsid nomarchy-launch-browser

17
bin/nomarchy-install-nordvpn Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
# Install the NordVPN service with optional GUI.
echo "Installing NordVPN..."
nomarchy-pkg-aur-add nordvpn-bin
echo "Enabling NordVPN daemon..."
sudo systemctl enable --now nordvpnd
echo "Adding user to nordvpn group..."
sudo usermod -aG nordvpn "$USER"
echo -e "\nNordVPN installed! After reboot, run 'nordvpn login' to authenticate."
echo
gum confirm "Reboot now to make NordVPN usable?" && nomarchy-system-reboot

9
bin/nomarchy-install-steam Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
# Install and launch Steam after first letting the user pick the correct grahics card drivers.
set -e
echo "Now pick dependencies matching your graphics card"
sudo pacman -S steam
setsid gtk-launch steam >/dev/null 2>&1 &

10
bin/nomarchy-install-tailscale Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/bash
# Install the Tailscale mesh VPN service and a web app for the Tailscale Admin Console.
curl -fsSL https://tailscale.com/install.sh | sh
echo -e "\nStarting Tailscale..."
sudo tailscale up --accept-routes
nomarchy-webapp-install "Tailscale" "https://login.tailscale.com/admin/machines" https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/tailscale-light.png

39
bin/nomarchy-install-terminal Executable file
View File

@@ -0,0 +1,39 @@
#!/bin/bash
# Install one of the approved terminals and set it as the default for Nomarchy (Super + Return etc).
if (($# == 0)); then
echo "Usage: nomarchy-install-terminal [alacritty|ghostty|kitty]"
exit 1
fi
package="$1"
# Map package name to desktop entry ID
case "$package" in
alacritty) desktop_id="Alacritty.desktop" ;;
ghostty) desktop_id="com.mitchellh.ghostty.desktop" ;;
kitty) desktop_id="kitty.desktop" ;;
*)
echo "Unknown terminal: $package"
exit 1
;;
esac
# Install package
if nomarchy-pkg-add $package; then
# Copy custom desktop entry for alacritty with X-TerminalArg* keys
if [[ $package == "alacritty" ]]; then
mkdir -p ~/.local/share/applications
cp $OMARCHY_PATH/applications/Alacritty.desktop ~/.local/share/applications/
fi
# Update xdg-terminals.list to prioritize the proper terminal
cat >~/.config/xdg-terminals.list <<EOF
# Terminal emulator preference order for xdg-terminal-exec
# The first found and valid terminal will be used
$desktop_id
EOF
else
echo "Failed to install $package"
fi

29
bin/nomarchy-install-vscode Executable file
View File

@@ -0,0 +1,29 @@
#!/bin/bash
# Install VSCode and configure it to use the gnome-libsecret password store, not to update automatically, and to use the current Nomarchy theme.
echo "Installing VSCode..."
nomarchy-pkg-add visual-studio-code-bin
mkdir -p ~/.vscode ~/.config/Code/User
cat > ~/.vscode/argv.json << 'EOF'
// This configuration file allows you to pass permanent command line arguments to VS Code.
// Only a subset of arguments is currently supported to reduce the likelihood of breaking
// the installation.
//
// PLEASE DO NOT CHANGE WITHOUT UNDERSTANDING THE IMPACT
//
// NOTE: Changing this file requires a restart of VS Code.
{
"password-store":"gnome-libsecret"
}
EOF
# Ensure VSC's own auto-update feature is turned off
printf '{\n "update.mode": "none"\n}\n' > ~/.config/Code/User/settings.json
# Apply Nomarchy theme to VSCode
nomarchy-theme-set-vscode
setsid gtk-launch code

View File

@@ -0,0 +1,19 @@
#!/bin/bash
# Install support for using Xbox controllers with Steam/RetroArch/etc.
set -e
# Install xpadneo to ensure controllers work out of the box
nomarchy-pkg-add linux-headers
nomarchy-pkg-aur-add xpadneo-dkms
# Prevent xpad/xpadneo driver conflict
echo blacklist xpad | sudo tee /etc/modprobe.d/blacklist-xpad.conf >/dev/null
echo hid_xpadneo | sudo tee /etc/modules-load.d/xpadneo.conf >/dev/null
# Give user access to game controllers
sudo usermod -a -G input $USER
# Modules need to be loaded
gum confirm "Install requires reboot. Ready?" && sudo reboot now

5
bin/nomarchy-launch-about Executable file
View File

@@ -0,0 +1,5 @@
#!/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'"

5
bin/nomarchy-launch-audio Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
# Launch the Nomarchy audio controls TUI (provided by wiremix).
nomarchy-launch-or-focus-tui wiremix

7
bin/nomarchy-launch-bluetooth Executable file
View File

@@ -0,0 +1,7 @@
#!/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

17
bin/nomarchy-launch-browser Executable file
View File

@@ -0,0 +1,17 @@
#!/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}"

15
bin/nomarchy-launch-editor Executable file
View File

@@ -0,0 +1,15 @@
#!/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

View File

@@ -0,0 +1,7 @@
#!/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"

19
bin/nomarchy-launch-or-focus Executable file
View File

@@ -0,0 +1,19 @@
#!/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

View File

@@ -0,0 +1,9 @@
#!/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"

View File

@@ -0,0 +1,15 @@
#!/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"

54
bin/nomarchy-launch-screensaver Executable file
View File

@@ -0,0 +1,54 @@
#!/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
if [[ -f ~/.local/state/nomarchy/toggles/screensaver-off ]] && [[ $1 != "force" ]]; then
exit 1
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

5
bin/nomarchy-launch-tui Executable file
View File

@@ -0,0 +1,5 @@
#!/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}"

15
bin/nomarchy-launch-walker Executable file
View File

@@ -0,0 +1,15 @@
#!/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 "$@"

12
bin/nomarchy-launch-webapp Executable file
View File

@@ -0,0 +1,12 @@
#!/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}"

7
bin/nomarchy-launch-wifi Executable file
View File

@@ -0,0 +1,7 @@
#!/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

17
bin/nomarchy-lock-screen Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
# Locks the system using hyprlock, but not before ensuring 1password has also been locked, and the screensaver stopped.
# Lock the screen
pidof hyprlock || hyprlock &
# Set keyboard layout to default (first layout)
hyprctl switchxkblayout all 0 > /dev/null 2>&1
# Ensure 1password is locked
if pgrep -x "1password" >/dev/null; then
1password --lock &
fi
# Avoid running screensaver when locked
pkill -f org.nomarchy.screensaver

633
bin/nomarchy-menu Executable file
View File

@@ -0,0 +1,633 @@
#!/bin/bash
# Launch the Nomarchy Menu or takes a parameter to jump straight to a submenu.
export PATH="$HOME/.local/share/nomarchy/bin:$PATH"
# Set to true when going directly to a submenu, so we can exit directly
BACK_TO_EXIT=false
back_to() {
local parent_menu="$1"
if [[ $BACK_TO_EXIT == "true" ]]; then
exit 0
elif [[ -n $parent_menu ]]; then
"$parent_menu"
else
show_main_menu
fi
}
toggle_existing_menu() {
if pgrep -f "walker.*--dmenu" >/dev/null; then
walker --close >/dev/null 2>&1
exit 0
fi
}
menu() {
local prompt="$1"
local options="$2"
local extra="$3"
local preselect="$4"
read -r -a args <<<"$extra"
if [[ -n $preselect ]]; then
local index
index=$(echo -e "$options" | grep -nxF "$preselect" | cut -d: -f1)
if [[ -n $index ]]; then
args+=("-c" "$index")
fi
fi
echo -e "$options" | nomarchy-launch-walker --dmenu --width 295 --minheight 1 --maxheight 630 -p "$prompt…" "${args[@]}" 2>/dev/null
}
terminal() {
xdg-terminal-exec --app-id=org.nomarchy.terminal "$@"
}
present_terminal() {
nomarchy-launch-floating-terminal-with-presentation $1
}
open_in_editor() {
notify-send -u low "Editing config file" "$1"
nomarchy-launch-editor "$1"
}
install() {
present_terminal "echo 'Installing $1...'; nomarchy-pkg-add $2"
}
install_and_launch() {
present_terminal "echo 'Installing $1...'; nomarchy-pkg-add $2 && setsid gtk-launch $3"
}
install_font() {
present_terminal "echo 'Installing $1...'; nomarchy-pkg-add $2 && sleep 2 && nomarchy-font-set '$3'"
}
install_terminal() {
present_terminal "nomarchy-install-terminal $1"
}
aur_install() {
present_terminal "echo 'Installing $1 from AUR...'; nomarchy-pkg-aur-add $2"
}
aur_install_and_launch() {
present_terminal "echo 'Installing $1 from AUR...'; nomarchy-pkg-aur-add $2 && setsid gtk-launch $3"
}
show_learn_menu() {
case $(menu "Learn" " Keybindings\n Nomarchy\n Hyprland\n󰣇 Arch\n Neovim\n󱆃 Bash") in
*Keybindings*) nomarchy-menu-keybindings ;;
*Nomarchy*) nomarchy-launch-webapp "https://learn.omacom.io/2/the-nomarchy-manual" ;;
*Hyprland*) nomarchy-launch-webapp "https://wiki.hypr.land/" ;;
*Arch*) nomarchy-launch-webapp "https://wiki.archlinux.org/title/Main_page" ;;
*Bash*) nomarchy-launch-webapp "https://devhints.io/bash" ;;
*Neovim*) nomarchy-launch-webapp "https://www.lazyvim.org/keymaps" ;;
*) show_main_menu ;;
esac
}
show_trigger_menu() {
case $(menu "Trigger" " Capture\n Share\n󰔎 Toggle\n Hardware") in
*Capture*) show_capture_menu ;;
*Share*) show_share_menu ;;
*Toggle*) show_toggle_menu ;;
*Hardware*) show_hardware_menu ;;
*) show_main_menu ;;
esac
}
show_capture_menu() {
case $(menu "Capture" " Screenshot\n Screenrecord\n󰃉 Color") in
*Screenshot*) nomarchy-cmd-screenshot ;;
*Screenrecord*) show_screenrecord_menu ;;
*Color*) pkill hyprpicker || hyprpicker -a ;;
*) back_to show_trigger_menu ;;
esac
}
get_webcam_list() {
v4l2-ctl --list-devices 2>/dev/null | while IFS= read -r line; do
if [[ $line != $'\t'* && -n $line ]]; then
local name="$line"
IFS= read -r device || break
device=$(echo "$device" | tr -d '\t' | head -1)
[[ -n $device ]] && echo "$device $name"
fi
done
}
show_webcam_select_menu() {
local devices=$(get_webcam_list)
local count=$(echo "$devices" | grep -c . 2>/dev/null || echo 0)
if [[ -z $devices ]] || ((count == 0)); then
notify-send "No webcam devices found" -u critical -t 3000
return 1
fi
if ((count == 1)); then
echo "$devices" | awk '{print $1}'
else
menu "Select Webcam" "$devices" | awk '{print $1}'
fi
}
show_screenrecord_menu() {
nomarchy-cmd-screenrecord --stop-recording && exit 0
case $(menu "Screenrecord" " With no audio\n With desktop audio\n With desktop + microphone audio\n With desktop + microphone audio + webcam") in
*"With no audio") nomarchy-cmd-screenrecord ;;
*"With desktop audio") nomarchy-cmd-screenrecord --with-desktop-audio ;;
*"With desktop + microphone audio") nomarchy-cmd-screenrecord --with-desktop-audio --with-microphone-audio ;;
*"With desktop + microphone audio + webcam")
local device=$(show_webcam_select_menu) || {
back_to show_capture_menu
return
}
nomarchy-cmd-screenrecord --with-desktop-audio --with-microphone-audio --with-webcam --webcam-device="$device"
;;
*) back_to show_capture_menu ;;
esac
}
show_share_menu() {
case $(menu "Share" " Clipboard\n File \n Folder") in
*Clipboard*) nomarchy-cmd-share clipboard ;;
*File*) terminal bash -c "nomarchy-cmd-share file" ;;
*Folder*) terminal bash -c "nomarchy-cmd-share folder" ;;
*) back_to show_trigger_menu ;;
esac
}
show_toggle_menu() {
case $(menu "Toggle" "󱄄 Screensaver\n󰔎 Nightlight\n󱫖 Idle Lock\n󰍜 Top Bar\n󱂬 Workspace Layout\n Window Gaps\n 1-Window Ratio\n󰍹 Display Scaling") in
*Screensaver*) nomarchy-toggle-screensaver ;;
*Nightlight*) nomarchy-toggle-nightlight ;;
*Idle*) nomarchy-toggle-idle ;;
*Bar*) nomarchy-toggle-waybar ;;
*Layout*) nomarchy-hyprland-workspace-layout-toggle ;;
*Ratio*) nomarchy-hyprland-window-single-square-aspect-toggle ;;
*Gaps*) nomarchy-hyprland-window-gaps-toggle ;;
*Scaling*) nomarchy-hyprland-monitor-scaling-cycle ;;
*) back_to show_trigger_menu ;;
esac
}
show_hardware_menu() {
case $(menu "Toggle" " Hybrid GPU") in
*"Hybrid GPU"*) present_terminal nomarchy-toggle-hybrid-gpu ;;
*) show_trigger_menu ;;
esac
}
show_style_menu() {
case $(menu "Style" "󰸌 Theme\n Font\n Background\n Hyprland\n󱄄 Screensaver\n About") in
*Theme*) show_theme_menu ;;
*Font*) show_font_menu ;;
*Background*) show_background_menu ;;
*Hyprland*) open_in_editor ~/.config/hypr/looknfeel.conf ;;
*Screensaver*) open_in_editor ~/.config/nomarchy/branding/screensaver.txt ;;
*About*) open_in_editor ~/.config/nomarchy/branding/about.txt ;;
*) show_main_menu ;;
esac
}
show_theme_menu() {
nomarchy-launch-walker -m menus:nomarchythemes --width 800 --minheight 400
}
show_background_menu() {
nomarchy-launch-walker -m menus:nomarchyBackgroundSelector --width 800 --minheight 400
}
show_font_menu() {
theme=$(menu "Font" "$(nomarchy-font-list)" "--width 350" "$(nomarchy-font-current)")
if [[ $theme == "CNCLD" || -z $theme ]]; then
back_to show_style_menu
else
nomarchy-font-set "$theme"
fi
}
show_setup_menu() {
local options=" Audio\n Wifi\n󰂯 Bluetooth\n󱐋 Power Profile\n System Sleep\n󰍹 Monitors"
[[ -f ~/.config/hypr/bindings.conf ]] && options="$options\n Keybindings"
options="$options\n Key Remapping"
[[ -f ~/.config/hypr/input.conf ]] && options="$options\n Input"
options="$options\n󰱔 DNS\n Security\n Config"
case $(menu "Setup" "$options") in
*Audio*) nomarchy-launch-audio ;;
*Wifi*) nomarchy-launch-wifi ;;
*Bluetooth*) nomarchy-launch-bluetooth ;;
*Power*) show_setup_power_menu ;;
*System*) show_setup_system_menu ;;
*Monitors*) open_in_editor ~/.config/hypr/monitors.conf ;;
*Keybindings*) open_in_editor ~/.config/hypr/bindings.conf ;;
*Input*) open_in_editor ~/.config/hypr/input.conf ;;
*Key\ Remapping*) nomarchy-setup-makima && open_in_editor "$HOME/.config/makima/AT Translated Set 2 keyboard.toml" && nomarchy-restart-makima ;;
*DNS*) present_terminal nomarchy-setup-dns ;;
*Security*) show_setup_security_menu ;;
*Config*) show_setup_config_menu ;;
*) show_main_menu ;;
esac
}
show_setup_power_menu() {
profile=$(menu "Power Profile" "$(nomarchy-powerprofiles-list)" "" "$(powerprofilesctl get)")
if [[ $profile == "CNCLD" || -z $profile ]]; then
back_to show_setup_menu
else
powerprofilesctl set "$profile"
fi
}
show_setup_security_menu() {
case $(menu "Setup" "󰈷 Fingerprint\n Fido2") in
*Fingerprint*) present_terminal nomarchy-setup-fingerprint ;;
*Fido2*) present_terminal nomarchy-setup-fido2 ;;
*) show_setup_menu ;;
esac
}
show_setup_config_menu() {
case $(menu "Setup" " Defaults\n Hyprland\n Hypridle\n Hyprlock\n Hyprsunset\n Swayosd\n󰌧 Walker\n󰍜 Waybar\n󰞅 XCompose") in
*Defaults*) open_in_editor ~/.config/uwsm/default ;;
*Hyprland*) open_in_editor ~/.config/hypr/hyprland.conf ;;
*Hypridle*) open_in_editor ~/.config/hypr/hypridle.conf && nomarchy-restart-hypridle ;;
*Hyprlock*) open_in_editor ~/.config/hypr/hyprlock.conf ;;
*Hyprsunset*) open_in_editor ~/.config/hypr/hyprsunset.conf && nomarchy-restart-hyprsunset ;;
*Swayosd*) open_in_editor ~/.config/swayosd/config.toml && nomarchy-restart-swayosd ;;
*Walker*) open_in_editor ~/.config/walker/config.toml && nomarchy-restart-walker ;;
*Waybar*) open_in_editor ~/.config/waybar/config.jsonc && nomarchy-restart-waybar ;;
*XCompose*) open_in_editor ~/.XCompose && nomarchy-restart-xcompose ;;
*) show_setup_menu ;;
esac
}
show_setup_system_menu() {
local options=""
if [[ -f ~/.local/state/nomarchy/toggles/suspend-off ]]; then
options="$options󰒲 Enable Suspend"
else
options="$options󰒲 Disable Suspend"
fi
if nomarchy-hibernation-available; then
options="$options\n󰤁 Disable Hibernate"
else
options="$options\n󰤁 Enable Hibernate"
fi
case $(menu "System" "$options") in
*Suspend*) nomarchy-toggle-suspend ;;
*"Enable Hibernate"*) present_terminal nomarchy-hibernation-setup ;;
*"Disable Hibernate"*) present_terminal nomarchy-hibernation-remove ;;
*) show_setup_menu ;;
esac
}
show_install_menu() {
case $(menu "Install" "󰣇 Package\n󰣇 AUR\n Web App\n TUI\n Service\n Style\n󰵮 Development\n Editor\n Terminal\n󱚤 AI\n󰍲 Windows\n Gaming") in
*Package*) terminal nomarchy-pkg-install ;;
*AUR*) terminal nomarchy-pkg-aur-install ;;
*Web*) present_terminal nomarchy-webapp-install ;;
*TUI*) present_terminal nomarchy-tui-install ;;
*Service*) show_install_service_menu ;;
*Style*) show_install_style_menu ;;
*Development*) show_install_development_menu ;;
*Editor*) show_install_editor_menu ;;
*Terminal*) show_install_terminal_menu ;;
*AI*) show_install_ai_menu ;;
*Windows*) present_terminal "nomarchy-windows-vm install" ;;
*Gaming*) show_install_gaming_menu ;;
*) show_main_menu ;;
esac
}
show_install_service_menu() {
case $(menu "Install" " Dropbox\n Tailscale\n󱇱 NordVPN [AUR]\n󰟵 Bitwarden\n Chromium Account") in
*Dropbox*) present_terminal nomarchy-install-dropbox ;;
*Tailscale*) present_terminal nomarchy-install-tailscale ;;
*NordVPN*) present_terminal nomarchy-install-nordvpn ;;
*Bitwarden*) install_and_launch "Bitwarden" "bitwarden bitwarden-cli" "bitwarden" ;;
*Chromium*) present_terminal nomarchy-install-chromium-google-account ;;
*) show_install_menu ;;
esac
}
show_install_editor_menu() {
case $(menu "Install" " VSCode\n Cursor\n Zed\n Sublime Text\n Helix\n Emacs") in
*VSCode*) present_terminal nomarchy-install-vscode ;;
*Cursor*) install_and_launch "Cursor" "cursor-bin" "cursor" ;;
*Zed*) install_and_launch "Zed" "zed" "dev.zed.Zed" ;;
*Sublime*) install_and_launch "Sublime Text" "sublime-text-4" "sublime_text" ;;
*Helix*) install "Helix" "helix" ;;
*Emacs*) install "Emacs" "emacs-wayland" && systemctl --user enable --now emacs.service ;;
*) show_install_menu ;;
esac
}
show_install_terminal_menu() {
case $(menu "Install" " Alacritty\n Ghostty\n Kitty") in
*Alacritty*) install_terminal "alacritty" ;;
*Ghostty*) install_terminal "ghostty" ;;
*Kitty*) install_terminal "kitty" ;;
*) show_install_menu ;;
esac
}
show_install_ai_menu() {
ollama_pkg=$(
(command -v nvidia-smi &>/dev/null && echo ollama-cuda) ||
(command -v rocminfo &>/dev/null && echo ollama-rocm) ||
echo ollama
)
case $(menu "Install" " Dictation\n󱚤 LM Studio\n󱚤 Ollama\n󱚤 Crush") in
*Dictation*) present_terminal nomarchy-voxtype-install ;;
*Studio*) install "LM Studio" "lmstudio-bin" ;;
*Ollama*) install "Ollama" $ollama_pkg ;;
*Crush*) install "Crush" "crush-bin" ;;
*) show_install_menu ;;
esac
}
show_install_gaming_menu() {
case $(menu "Install" " Steam\n󰢹 NVIDIA GeForce NOW\n RetroArch [AUR]\n󰍳 Minecraft\n󰖺 Xbox Controller [AUR]") in
*Steam*) present_terminal nomarchy-install-steam ;;
*GeForce*) present_terminal nomarchy-install-geforce-now ;;
*RetroArch*) aur_install_and_launch "RetroArch" "retroarch retroarch-assets libretro libretro-fbneo" "com.libretro.RetroArch.desktop" ;;
*Minecraft*) install_and_launch "Minecraft" "minecraft-launcher" "minecraft-launcher" ;;
*Xbox*) present_terminal nomarchy-install-xbox-controllers ;;
*) show_install_menu ;;
esac
}
show_install_style_menu() {
case $(menu "Install" "󰸌 Theme\n Background\n Font") in
*Theme*) present_terminal nomarchy-theme-install ;;
*Background*) nomarchy-theme-bg-install ;;
*Font*) show_install_font_menu ;;
*) show_install_menu ;;
esac
}
show_install_font_menu() {
case $(menu "Install" " Cascadia Mono\n Meslo LG Mono\n Fira Code\n Victor Code\n Bistream Vera Mono\n Iosevka" "--width 350") in
*Cascadia*) install_font "Cascadia Mono" "ttf-cascadia-mono-nerd" "CaskaydiaMono Nerd Font" ;;
*Meslo*) install_font "Meslo LG Mono" "ttf-meslo-nerd" "MesloLGL Nerd Font" ;;
*Fira*) install_font "Fira Code" "ttf-firacode-nerd" "FiraCode Nerd Font" ;;
*Victor*) install_font "Victor Code" "ttf-victor-mono-nerd" "VictorMono Nerd Font" ;;
*Bistream*) install_font "Bistream Vera Code" "ttf-bitstream-vera-mono-nerd" "BitstromWera Nerd Font" ;;
*Iosevka*) install_font "Iosevka" "ttf-iosevka-nerd" "Iosevka Nerd Font Mono" ;;
*) show_install_menu ;;
esac
}
show_install_development_menu() {
case $(menu "Install" "󰫏 Ruby on Rails\n Docker DB\n JavaScript\n Go\n PHP\n Python\n Elixir\n Zig\n Rust\n Java\n .NET\n OCaml\n Clojure\n Scala") in
*Rails*) present_terminal "nomarchy-install-dev-env ruby" ;;
*Docker*) present_terminal nomarchy-install-docker-dbs ;;
*JavaScript*) show_install_javascript_menu ;;
*Go*) present_terminal "nomarchy-install-dev-env go" ;;
*PHP*) show_install_php_menu ;;
*Python*) present_terminal "nomarchy-install-dev-env python" ;;
*Elixir*) show_install_elixir_menu ;;
*Zig*) present_terminal "nomarchy-install-dev-env zig" ;;
*Rust*) present_terminal "nomarchy-install-dev-env rust" ;;
*Java*) present_terminal "nomarchy-install-dev-env java" ;;
*NET*) present_terminal "nomarchy-install-dev-env dotnet" ;;
*OCaml*) present_terminal "nomarchy-install-dev-env ocaml" ;;
*Clojure*) present_terminal "nomarchy-install-dev-env clojure" ;;
*Scala*) present_terminal "nomarchy-install-dev-env scala" ;;
*) show_install_menu ;;
esac
}
show_install_javascript_menu() {
case $(menu "Install" " Node.js\n Bun\n Deno") in
*Node*) present_terminal "nomarchy-install-dev-env node" ;;
*Bun*) present_terminal "nomarchy-install-dev-env bun" ;;
*Deno*) present_terminal "nomarchy-install-dev-env deno" ;;
*) show_install_development_menu ;;
esac
}
show_install_php_menu() {
case $(menu "Install" " PHP\n Laravel\n Symfony") in
*PHP*) present_terminal "nomarchy-install-dev-env php" ;;
*Laravel*) present_terminal "nomarchy-install-dev-env laravel" ;;
*Symfony*) present_terminal "nomarchy-install-dev-env symfony" ;;
*) show_install_development_menu ;;
esac
}
show_install_elixir_menu() {
case $(menu "Install" " Elixir\n Phoenix") in
*Elixir*) present_terminal "nomarchy-install-dev-env elixir" ;;
*Phoenix*) present_terminal "nomarchy-install-dev-env phoenix" ;;
*) show_install_development_menu ;;
esac
}
show_remove_menu() {
case $(menu "Remove" "󰣇 Package\n Web App\n TUI\n󰵮 Development\n󰏓 Preinstalls\n Dictation\n󰸌 Theme\n󰍲 Windows\n󰈷 Fingerprint\n Fido2") in
*Package*) terminal nomarchy-pkg-remove ;;
*Web*) present_terminal nomarchy-webapp-remove ;;
*TUI*) present_terminal nomarchy-tui-remove ;;
*Development*) show_remove_development_menu ;;
*Preinstalls*) present_terminal nomarchy-remove-preinstalls ;;
*Dictation*) present_terminal nomarchy-voxtype-remove ;;
*Theme*) present_terminal nomarchy-theme-remove ;;
*Windows*) present_terminal "nomarchy-windows-vm remove" ;;
*Fingerprint*) present_terminal "nomarchy-setup-fingerprint --remove" ;;
*Fido2*) present_terminal "nomarchy-setup-fido2 --remove" ;;
*) show_main_menu ;;
esac
}
show_remove_development_menu() {
case $(menu "Remove" "󰫏 Ruby on Rails\n JavaScript\n Go\n PHP\n Python\n Elixir\n Zig\n Rust\n Java\n .NET\n OCaml\n Clojure\n Scala") in
*Rails*) present_terminal "nomarchy-remove-dev-env ruby" ;;
*JavaScript*) show_remove_javascript_menu ;;
*Go*) present_terminal "nomarchy-remove-dev-env go" ;;
*PHP*) show_remove_php_menu ;;
*Python*) present_terminal "nomarchy-remove-dev-env python" ;;
*Elixir*) show_remove_elixir_menu ;;
*Zig*) present_terminal "nomarchy-remove-dev-env zig" ;;
*Rust*) present_terminal "nomarchy-remove-dev-env rust" ;;
*Java*) present_terminal "nomarchy-remove-dev-env java" ;;
*NET*) present_terminal "nomarchy-remove-dev-env dotnet" ;;
*OCaml*) present_terminal "nomarchy-remove-dev-env ocaml" ;;
*Clojure*) present_terminal "nomarchy-remove-dev-env clojure" ;;
*Scala*) present_terminal "nomarchy-remove-dev-env scala" ;;
*) show_remove_menu ;;
esac
}
show_remove_javascript_menu() {
case $(menu "Remove" " Node.js\n Bun\n Deno") in
*Node*) present_terminal "nomarchy-remove-dev-env node" ;;
*Bun*) present_terminal "nomarchy-remove-dev-env bun" ;;
*Deno*) present_terminal "nomarchy-remove-dev-env deno" ;;
*) show_remove_development_menu ;;
esac
}
show_remove_php_menu() {
case $(menu "Remove" " PHP\n Laravel\n Symfony") in
*PHP*) present_terminal "nomarchy-remove-dev-env php" ;;
*Laravel*) present_terminal "nomarchy-remove-dev-env laravel" ;;
*Symfony*) present_terminal "nomarchy-remove-dev-env symfony" ;;
*) show_remove_development_menu ;;
esac
}
show_remove_elixir_menu() {
case $(menu "Remove" " Elixir\n Phoenix") in
*Elixir*) present_terminal "nomarchy-remove-dev-env elixir" ;;
*Phoenix*) present_terminal "nomarchy-remove-dev-env phoenix" ;;
*) show_remove_development_menu ;;
esac
}
show_update_menu() {
case $(menu "Update" "  Nomarchy\n󰔫 Channel\n Config\n󰸌 Extra Themes\n Process\n󰇅 Hardware\n Firmware\n Password\n Timezone\n Time") in
*Nomarchy*) present_terminal nomarchy-update ;;
*Channel*) show_update_channel_menu ;;
*Config*) show_update_config_menu ;;
*Themes*) present_terminal nomarchy-theme-update ;;
*Process*) show_update_process_menu ;;
*Hardware*) show_update_hardware_menu ;;
*Firmware*) present_terminal nomarchy-update-firmware ;;
*Timezone*) present_terminal nomarchy-tz-select ;;
*Time*) present_terminal nomarchy-update-time ;;
*Password*) show_update_password_menu ;;
*) show_main_menu ;;
esac
}
show_update_channel_menu() {
case $(menu "Update channel" "🟢 Stable\n🟡 RC\n🟠 Edge\n🔴 Dev") in
*Stable*) present_terminal "nomarchy-channel-set stable" ;;
*RC*) present_terminal "nomarchy-channel-set rc" ;;
*Edge*) present_terminal "nomarchy-channel-set edge" ;;
*Dev*) present_terminal "nomarchy-channel-set dev" ;;
*) show_update_menu ;;
esac
}
show_update_process_menu() {
case $(menu "Restart" " Hypridle\n Hyprsunset\n Swayosd\n󰌧 Walker\n󰍜 Waybar") in
*Hypridle*) nomarchy-restart-hypridle ;;
*Hyprsunset*) nomarchy-restart-hyprsunset ;;
*Swayosd*) nomarchy-restart-swayosd ;;
*Walker*) nomarchy-restart-walker ;;
*Waybar*) nomarchy-restart-waybar ;;
*) show_update_menu ;;
esac
}
show_update_config_menu() {
case $(menu "Use default config" " Hyprland\n Hypridle\n Hyprlock\n Hyprsunset\n󱣴 Plymouth\n Swayosd\n Tmux\n󰌧 Walker\n󰍜 Waybar") in
*Hyprland*) present_terminal nomarchy-refresh-hyprland ;;
*Hypridle*) present_terminal nomarchy-refresh-hypridle ;;
*Hyprlock*) present_terminal nomarchy-refresh-hyprlock ;;
*Hyprsunset*) present_terminal nomarchy-refresh-hyprsunset ;;
*Plymouth*) present_terminal nomarchy-refresh-plymouth ;;
*Swayosd*) present_terminal nomarchy-refresh-swayosd ;;
*Tmux*) present_terminal nomarchy-refresh-tmux ;;
*Walker*) present_terminal nomarchy-refresh-walker ;;
*Waybar*) present_terminal nomarchy-refresh-waybar ;;
*) show_update_menu ;;
esac
}
show_update_hardware_menu() {
case $(menu "Restart" " Audio\n󱚾 Wi-Fi\n󰂯 Bluetooth") in
*Audio*) present_terminal nomarchy-restart-pipewire ;;
*Wi-Fi*) present_terminal nomarchy-restart-wifi ;;
*Bluetooth*) present_terminal nomarchy-restart-bluetooth ;;
*) show_update_menu ;;
esac
}
show_update_password_menu() {
case $(menu "Update Password" " Drive Encryption\n User") in
*Drive*) present_terminal nomarchy-drive-set-password ;;
*User*) present_terminal passwd ;;
*) show_update_menu ;;
esac
}
show_about() {
nomarchy-launch-about
}
show_system_menu() {
local options="󱄄 Screensaver\n Lock"
[[ ! -f ~/.local/state/nomarchy/toggles/suspend-off ]] && options="$options\n󰒲 Suspend"
nomarchy-hibernation-available && options="$options\n󰤁 Hibernate"
options="$options\n󰍃 Logout\n󰜉 Restart\n󰐥 Shutdown"
case $(menu "System" "$options") in
*Screensaver*) nomarchy-launch-screensaver force ;;
*Lock*) nomarchy-lock-screen ;;
*Suspend*) systemctl suspend ;;
*Hibernate*) systemctl hibernate ;;
*Logout*) nomarchy-system-logout ;;
*Restart*) nomarchy-system-reboot ;;
*Shutdown*) nomarchy-system-shutdown ;;
*) back_to show_main_menu ;;
esac
}
show_main_menu() {
go_to_menu "$(menu "Go" "󰀻 Apps\n󰧑 Learn\n󱓞 Trigger\n Style\n Setup\n󰉉 Install\n󰭌 Remove\n Update\n About\n System")"
}
go_to_menu() {
case "${1,,}" in
*apps*) walker -p "Launch…" ;;
*learn*) show_learn_menu ;;
*trigger*) show_trigger_menu ;;
*toggle*) show_toggle_menu ;;
*share*) show_share_menu ;;
*background*) show_background_menu ;;
*capture*) show_capture_menu ;;
*style*) show_style_menu ;;
*theme*) show_theme_menu ;;
*screenrecord*) show_screenrecord_menu ;;
*setup*) show_setup_menu ;;
*power*) show_setup_power_menu ;;
*install*) show_install_menu ;;
*remove*) show_remove_menu ;;
*update*) show_update_menu ;;
*about*) show_about ;;
*system*) show_system_menu ;;
esac
}
# Allow user extensions and overrides
USER_EXTENSIONS="$HOME/.config/nomarchy/extensions/menu.sh"
[[ -f $USER_EXTENSIONS ]] && source "$USER_EXTENSIONS"
toggle_existing_menu
if [[ -n $1 ]]; then
BACK_TO_EXIT=true
go_to_menu "$1"
else
show_main_menu
fi

247
bin/nomarchy-menu-keybindings Executable file
View File

@@ -0,0 +1,247 @@
#!/bin/bash
# Display Hyprland keybindings defined in your configuration using walker for an interactive search menu.
declare -A KEYCODE_SYM_MAP
build_keymap_cache() {
local keymap
keymap="$(xkbcli compile-keymap)" || {
echo "Failed to compile keymap" >&2
return 1
}
while IFS=, read -r code sym; do
[[ -z $code || -z $sym ]] && continue
KEYCODE_SYM_MAP["$code"]="$sym"
done < <(
awk '
BEGIN { sec = "" }
/xkb_keycodes/ { sec = "codes"; next }
/xkb_symbols/ { sec = "syms"; next }
sec == "codes" {
if (match($0, /<([A-Za-z0-9_]+)>\s*=\s*([0-9]+)\s*;/, m)) code_by_name[m[1]] = m[2]
}
sec == "syms" {
if (match($0, /key\s*<([A-Za-z0-9_]+)>\s*\{\s*\[\s*([^, \]]+)/, m)) sym_by_name[m[1]] = m[2]
}
END {
for (k in code_by_name) {
c = code_by_name[k]
s = sym_by_name[k]
if (c != "" && s != "" && s != "NoSymbol") print c "," s
}
}
' <<<"$keymap"
)
}
lookup_keycode_cached() {
printf '%s\n' "${KEYCODE_SYM_MAP[$1]}"
}
parse_keycodes() {
local start end elapsed
[[ ${DEBUG:-0} == "1" ]] && start=$(date +%s.%N)
while IFS= read -r line; do
if [[ $line =~ code:([0-9]+) ]]; then
code="${BASH_REMATCH[1]}"
symbol=$(lookup_keycode_cached "$code" "$XKB_KEYMAP_CACHE")
echo "${line/code:${code}/$symbol}"
elif [[ $line =~ mouse:([0-9]+) ]]; then
code="${BASH_REMATCH[1]}"
case "$code" in
272) symbol="LEFT MOUSE BUTTON" ;;
273) symbol="RIGHT MOUSE BUTTON" ;;
274) symbol="MIDDLE MOUSE BUTTON" ;;
*) symbol="mouse:${code}" ;;
esac
echo "${line/mouse:${code}/$symbol}"
else
echo "$line"
fi
done
if [[ $DEBUG == "1" ]]; then
end=$(date +%s.%N)
# fall back to awk if bc is missing
if command -v bc >/dev/null 2>&1; then
elapsed=$(echo "$end - $start" | bc)
else
elapsed=$(awk -v s="$start" -v e="$end" 'BEGIN{printf "%.6f", (e - s)}')
fi
echo "[DEBUG] parse_keycodes elapsed: ${elapsed}s" >&2
fi
}
# Fetch dynamic keybindings from Hyprland
#
# Also do some pre-processing:
# - Remove standard Nomarchy bin path prefix
# - Remove uwsm prefix
# - Map numeric modifier key mask to a textual rendition
# - Output comma-separated values that the parser can understand
dynamic_bindings() {
hyprctl -j binds |
jq -r '.[] | {modmask, key, keycode, description, dispatcher, arg} | "\(.modmask),\(.key)@\(.keycode),\(.description),\(.dispatcher),\(.arg)"' |
sed -r \
-e 's/null//' \
-e 's,~/.local/share/nomarchy/bin/,,' \
-e 's,uwsm app -- ,,' \
-e 's,uwsm-app -- ,,' \
-e 's/@0//' \
-e 's/,@/,code:/' \
-e 's/^0,/,/' \
-e 's/^1,/SHIFT,/' \
-e 's/^4,/CTRL,/' \
-e 's/^5,/SHIFT CTRL,/' \
-e 's/^8,/ALT,/' \
-e 's/^9,/SHIFT ALT,/' \
-e 's/^12,/CTRL ALT,/' \
-e 's/^13,/SHIFT CTRL ALT,/' \
-e 's/^64,/SUPER,/' \
-e 's/^65,/SUPER SHIFT,/' \
-e 's/^68,/SUPER CTRL,/' \
-e 's/^69,/SUPER SHIFT CTRL,/' \
-e 's/^72,/SUPER ALT,/' \
-e 's/^73,/SUPER SHIFT ALT,/' \
-e 's/^76,/SUPER CTRL ALT,/' \
-e 's/^77,/SUPER SHIFT CTRL ALT,/'
}
# Hardcoded bindings, like the copy-url extension and such
static_bindings() {
echo "SHIFT ALT,L,Copy URL from Web App,extension,copy-url"
}
# Parse and format keybindings
#
# `awk` does the heavy lifting:
# - Set the field separator to a comma ','.
# - Joins the key combination (e.g., "SUPER + Q").
# - Joins the command that the key executes.
# - Prints everything in a nicely aligned format.
parse_bindings() {
awk -F, '
{
# Combine the modifier and key (first two fields)
key_combo = $1 " + " $2;
# Clean up: strip leading "+" if present, trim spaces
gsub(/^[ \t]*\+?[ \t]*/, "", key_combo);
gsub(/[ \t]+$/, "", key_combo);
# Use description, if set
action = $3;
if (action == "") {
# Reconstruct the command from the remaining fields
for (i = 4; i <= NF; i++) {
action = action $i (i < NF ? "," : "");
}
# Clean up trailing commas, remove leading "exec, ", and trim
sub(/,$/, "", action);
gsub(/(^|,)[[:space:]]*exec[[:space:]]*,?/, "", action);
gsub(/^[ \t]+|[ \t]+$/, "", action);
gsub(/[ \t]+/, " ", key_combo); # Collapse multiple spaces to one
# Escape XML entities
gsub(/&/, "\\&amp;", action);
gsub(/</, "\\&lt;", action);
gsub(/>/, "\\&gt;", action);
gsub(/"/, "\\&quot;", action);
gsub(/'"'"'/, "\\&apos;", action);
}
if (action != "") {
printf "%-35s → %s\n", key_combo, action;
}
}'
}
prioritize_entries() {
awk '
{
line = $0
prio = 50
if (match(line, /Terminal/)) prio = 0
if (match(line, /Tmux/)) prio = 1
if (match(line, /Browser/) && !match(line, /Browser[[:space:]]*\(/) && !match(line, /SUPER SHIFT.*\+.*B.*→.*Browser/)) prio = 2
if (match(line, /File manager/) && !match(line, /File manager \(cwd\)/)) prio = 3
if (match(line, /Launch apps/)) prio = 4
if (match(line, /Nomarchy menu/)) prio = 5
if (match(line, /System menu/)) prio = 6
if (match(line, /Theme menu/)) prio = 7
if (match(line, /Full screen/)) prio = 8
if (match(line, /Full width/)) prio = 9
if (match(line, /Close window/)) prio = 10
if (match(line, /Close all windows/)) prio = 11
if (match(line, /Lock system/)) prio = 12
if (match(line, /Toggle window floating/)) prio = 13
if (match(line, /Toggle window split/)) prio = 14
if (match(line, /Pop window/)) prio = 15
if (match(line, /Universal/)) prio = 16
if (match(line, /Clipboard/)) prio = 17
if (match(line, /Audio controls/)) prio = 18
if (match(line, /Bluetooth controls/)) prio = 19
if (match(line, /Wifi controls/)) prio = 20
if (match(line, /Emoji picker/)) prio = 21
if (match(line, /Color picker/)) prio = 22
if (match(line, /Screenshot/)) prio = 23
if (match(line, /Screenrecording/)) prio = 24
if (match(line, /SUPER SHIFT.*\+.*B.*→.*Browser/)) prio = 25
if (match(line, /File manager \(cwd\)/)) prio = 26
if (match(line, /(Switch|Next|Former|Previous).*workspace/)) prio = 27
if (match(line, /Move window to workspace/)) prio = 28
if (match(line, /Move window silently to workspace/)) prio = 29
if (match(line, /Swap window/)) prio = 30
if (match(line, /Move window focus/)) prio = 31
if (match(line, /Move window$/)) prio = 32
if (match(line, /Resize window/)) prio = 33
if (match(line, /Expand window/)) prio = 34
if (match(line, /Shrink window/)) prio = 35
if (match(line, /scratchpad/)) prio = 36
if (match(line, /notification/)) prio = 37
if (match(line, /Toggle window transparency/)) prio = 38
if (match(line, /Toggle workspace gaps/)) prio = 39
if (match(line, /Toggle nightlight/)) prio = 40
if (match(line, /Toggle locking/)) prio = 41
if (match(line, /group/)) prio = 94
if (match(line, /Scroll active workspace/)) prio = 95
if (match(line, /Cycle to/)) prio = 96
if (match(line, /Reveal active/)) prio = 97
if (match(line, /Apple Display/)) prio = 98
if (match(line, /XF86/)) prio = 99
# print "priority<TAB>line"
printf "%d\t%s\n", prio, line
}' |
sort -k1,1n -k2,2 |
cut -f2-
}
output_keybindings() {
build_keymap_cache
{
dynamic_bindings
static_bindings
} |
sort -u |
parse_keycodes |
parse_bindings |
prioritize_entries
}
if [[ $1 == "--print" || $1 == "-p" ]]; then
output_keybindings
else
monitor_height=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .height')
menu_height=$((monitor_height * 40 / 100))
output_keybindings |
walker --dmenu -p 'Keybindings' --width 800 --height "$menu_height"
fi

29
bin/nomarchy-migrate Executable file
View File

@@ -0,0 +1,29 @@
#!/bin/bash
# Run all pending migrations to bring the system in line with the installed version.
# Where we store an empty file for each migration that has already been performed.
STATE_DIR="$HOME/.local/state/nomarchy/migrations"
mkdir -p "$STATE_DIR"
# Skipped migrations are tracked separately
mkdir -p "$STATE_DIR/skipped"
# Run any pending migrations
for file in ~/.local/share/nomarchy/migrations/*.sh; do
filename=$(basename "$file")
if [[ ! -f $STATE_DIR/$filename && ! -f $STATE_DIR/skipped/$filename ]]; then
echo -e "\e[32m\nRunning migration (${filename%.sh})\e[0m"
if bash $file; then
touch "$STATE_DIR/$filename"
else
if gum confirm "Migration ${filename%.sh} failed. Skip and continue?"; then
touch "$STATE_DIR/skipped/$filename"
else
exit 1
fi
fi
fi
done

View File

@@ -0,0 +1,15 @@
#!/bin/bash
# Dismiss a mako notification on the basis of its summary. Used by the first-run notifications to dismiss them after clicking for action.
if (($# == 0)); then
echo "Usage: nomarchy-notification-dismiss <summary>"
exit 1
fi
# Find the first notification whose 'summary' matches the regex in $1
notification_id=$(makoctl list | grep -F "$1" | head -n1 | sed -E 's/^Notification ([0-9]+):.*/\1/')
if [[ -n $notification_id ]]; then
makoctl dismiss -n $notification_id
fi

24
bin/nomarchy-npx-install Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
# Install an npx wrapper for a given npm package.
# Usage: nomarchy-npx-install <package> [command-name]
#
# If command-name is omitted, it defaults to the package name.
# Example: nomarchy-npx-install opencode-ai opencode
if [[ -z $1 ]]; then
echo "Usage: nomarchy-npx-install <package> [command-name]"
exit 1
fi
package=$1
command=${2:-$1}
mkdir -p "$HOME/.local/bin"
cat > "$HOME/.local/bin/$command" <<EOF
#!/bin/bash
exec npx --yes $package "\$@"
EOF
chmod +x "$HOME/.local/bin/$command"

17
bin/nomarchy-pkg-add Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
# Add the named packages to the system if they're missing. Returns false if it couldn't be done.
if nomarchy-pkg-missing "$@"; then
sudo pacman -S --noconfirm --needed "$@" || exit 1
fi
for pkg in "$@"; do
# Secondary check to handle states where pacman doesn't actually register an error
if ! pacman -Q "$pkg" &>/dev/null; then
echo -e "\033[31mError: Package '$pkg' did not install\033[0m" >&2
exit 1
fi
done
exit 0

View File

@@ -0,0 +1,7 @@
#!/bin/bash
# Returns true if the AUR is up and available.
# Used by nomarchy-update-system-pkgs to ensure the AUR is available before updating packages from it.
curl -sf --connect-timeout 30 --retry 3 --retry-delay 3 -A "nomarchy-update" \
"https://aur.archlinux.org/rpc/?v=5&type=info&arg=base" >/dev/null

17
bin/nomarchy-pkg-aur-add Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
# Add the named packages to the system from the AUR if they're missing. Returns false if it couldn't be done.
if nomarchy-pkg-missing "$@"; then
yay -S --noconfirm --needed "$@" || exit 1
fi
for pkg in "$@"; do
# Secondary check to handle states where pacman doesn't actually register an error
if ! pacman -Q "$pkg" &>/dev/null; then
echo -e "\033[31mError: Package '$pkg' did not install\033[0m" >&2
exit 1
fi
done
exit 0

28
bin/nomarchy-pkg-aur-install Executable file
View File

@@ -0,0 +1,28 @@
#!/bin/bash
# Show a fuzzy-finder TUI for picking new AUR packages to install.
fzf_args=(
--multi
--preview 'yay -Siia {1}'
--preview-label='alt-p: toggle description, alt-b/B: toggle PKGBUILD, alt-j/k: scroll, tab: multi-select'
--preview-label-pos='bottom'
--preview-window 'down:65%:wrap'
--bind 'alt-p:toggle-preview'
--bind 'alt-d:preview-half-page-down,alt-u:preview-half-page-up'
--bind 'alt-k:preview-up,alt-j:preview-down'
--bind 'alt-b:change-preview:yay -Gpa {1} | tail -n +5'
--bind 'alt-B:change-preview:yay -Siia {1}'
--color 'pointer:green,marker:green'
)
pkg_names=$(yay -Slqa | fzf "${fzf_args[@]}")
if [[ -n $pkg_names ]]; then
# Add aur/ prefix to each package name and convert to space-separated for yay
source nomarchy-sudo-keepalive
echo "$pkg_names" | sed 's/^/aur\//' | tr '\n' ' ' | xargs yay -S --noconfirm
sudo updatedb
nomarchy-show-done
fi

9
bin/nomarchy-pkg-drop Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
# Remove all the named packages from the system if they're installed (otherwise ignore).
for pkg in "$@"; do
if pacman -Q "$pkg" &>/dev/null; then
sudo pacman -Rns --noconfirm "$pkg"
fi
done

25
bin/nomarchy-pkg-install Executable file
View File

@@ -0,0 +1,25 @@
#!/bin/bash
# Show a fuzzy-finder TUI for picking new Arch and OPR packages to install.
fzf_args=(
--multi
--preview 'pacman -Sii {1}'
--preview-label='alt-p: toggle description, alt-j/k: scroll, tab: multi-select'
--preview-label-pos='bottom'
--preview-window 'down:65%:wrap'
--bind 'alt-p:toggle-preview'
--bind 'alt-d:preview-half-page-down,alt-u:preview-half-page-up'
--bind 'alt-k:preview-up,alt-j:preview-down'
--color 'pointer:green,marker:green'
)
pkg_names=$(pacman -Slq | fzf "${fzf_args[@]}")
if [[ -n $pkg_names ]]; then
source nomarchy-sudo-keepalive
# Convert newline-separated selections to space-separated for pacman
echo "$pkg_names" | tr '\n' ' ' | xargs sudo pacman -S --noconfirm
nomarchy-show-done
fi

11
bin/nomarchy-pkg-missing Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/bash
# Returns true if any of the named packages are missing from the system (or false if they're all there).
for pkg in "$@"; do
if ! pacman -Q "$pkg" &>/dev/null; then
exit 0
fi
done
exit 1

9
bin/nomarchy-pkg-present Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
# Returns true if all of the named packages are installed on the system (or false if any of them are missing).
for pkg in "$@"; do
pacman -Q "$pkg" &>/dev/null || exit 1
done
exit 0

23
bin/nomarchy-pkg-remove Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
# Show a fuzzy-finder TUI for picking packages installed on the system to be removed.
fzf_args=(
--multi
--preview 'yay -Qi {1}'
--preview-label='alt-p: toggle description, alt-j/k: scroll, tab: multi-select'
--preview-label-pos='bottom'
--preview-window 'down:65%:wrap'
--bind 'alt-p:toggle-preview'
--bind 'alt-d:preview-half-page-down,alt-u:preview-half-page-up'
--bind 'alt-k:preview-up,alt-j:preview-down'
--color 'pointer:red,marker:red'
)
pkg_names=$(yay -Qqe | fzf "${fzf_args[@]}")
if [[ -n $pkg_names ]]; then
# Convert newline-separated selections to space-separated for yay
echo "$pkg_names" | tr '\n' ' ' | xargs sudo pacman -Rns --noconfirm
nomarchy-show-done
fi

View File

@@ -0,0 +1,8 @@
#!/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

View File

@@ -0,0 +1,20 @@
#!/bin/bash
# Ensure all default .desktop, web apps, and TUIs are installed.
# Copy and sync icon files
mkdir -p ~/.local/share/icons/hicolor/48x48/apps/
cp ~/.local/share/nomarchy/applications/icons/*.png ~/.local/share/icons/hicolor/48x48/apps/
gtk-update-icon-cache ~/.local/share/icons/hicolor &>/dev/null
# Copy .desktop declarations
mkdir -p ~/.local/share/applications
cp ~/.local/share/nomarchy/applications/*.desktop ~/.local/share/applications/
cp ~/.local/share/nomarchy/applications/hidden/*.desktop ~/.local/share/applications/
# Refresh the webapps and TUIs
bash $OMARCHY_PATH/install/packaging/icons.sh
bash $OMARCHY_PATH/install/packaging/webapps.sh
bash $OMARCHY_PATH/install/packaging/tuis.sh
update-desktop-database ~/.local/share/applications

21
bin/nomarchy-refresh-chromium Executable file
View File

@@ -0,0 +1,21 @@
#!/bin/bash
# Refresh the ~/.config/chromium-flags.conf file from the Nomarchy defaults.
CONFIG_FILE="$HOME/.config/chromium-flags.conf"
INSTALL_GOOGLE_ACCOUNTS=false
# Check if google accounts were installed
if [[ -f $CONFIG_FILE ]] && \
grep -q -- "--oauth2-client-id" "$CONFIG_FILE" && \
grep -q -- "--oauth2-client-secret" "$CONFIG_FILE"; then
INSTALL_GOOGLE_ACCOUNTS=true
fi
# Refresh the Chromium configuration
nomarchy-refresh-config chromium-flags.conf
# Re-install Google accounts if previously configured
if [[ $INSTALL_GOOGLE_ACCOUNTS == "true" ]]; then
nomarchy-install-chromium-google-account
fi

42
bin/nomarchy-refresh-config Executable file
View File

@@ -0,0 +1,42 @@
#!/bin/bash
# Copies the named config from ~/.local/share/nomarchy/config/X/Y/Z -> ~/.config/X/Y/Z.
# If the config already exists, a backup of the existing will be taken as .bak.TIMESTAMP.
config_file=$1
if [[ -z $config_file ]]; then
cat <<USAGE
Usage: $0 [config_file]
Must provide a file path from the .config directory to be refreshed.
To copy ~/.local/share/nomarchy/config/hypr/hyprlock.conf to ~/.config/hypr/hyprlock.conf
$0 hypr/hyprlock.conf
USAGE
exit 1
fi
# Backup the destination file (with timestamp) to avoid clobbering (Ex: hyprlock.conf.bak.1753817951)
user_config_file="${HOME}/.config/$config_file"
default_config_file="${HOME}/.local/share/nomarchy/config/$config_file"
backup_config_file="$user_config_file.bak.$(date +%s)"
if [[ -f $user_config_file ]]; then
# Create preliminary backup
cp -f "$user_config_file" "$backup_config_file" 2>/dev/null
# Replace config with new default
cp -f "$default_config_file" "$user_config_file" 2>/dev/null
# Compare and delete/inform accordingly
if cmp -s "$user_config_file" "$backup_config_file"; then
rm "$backup_config_file"
else
echo -e "\e[31mReplaced $user_config_file with new Nomarchy default.\nSaved backup as ${backup_config_file}.\n\n\e[32mChanges:\e[0m"
diff "$user_config_file" "$backup_config_file" || true
fi
else
# Config file did not exist already
cp -f "$default_config_file" "$user_config_file" 2>/dev/null
fi

5
bin/nomarchy-refresh-fastfetch Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
# Overwrite the user config for fastfetch with the Nomarchy default.
nomarchy-refresh-config fastfetch/config.jsonc

6
bin/nomarchy-refresh-hypridle Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
# Overwrite the user config for hypridle with the Nomarchy default and restart the service.
nomarchy-refresh-config hypr/hypridle.conf
nomarchy-restart-hypridle

9
bin/nomarchy-refresh-hyprland Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
# Overwrite all the user configs in ~/.config/hypr with the Nomarchy defaults.
nomarchy-refresh-config hypr/autostart.conf
nomarchy-refresh-config hypr/bindings.conf
nomarchy-refresh-config hypr/input.conf
nomarchy-refresh-config hypr/looknfeel.conf
nomarchy-refresh-config hypr/hyprland.conf

5
bin/nomarchy-refresh-hyprlock Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
# Overwrite the user config for hyprlock with the Nomarchy default.
nomarchy-refresh-config hypr/hyprlock.conf

View File

@@ -0,0 +1,6 @@
#!/bin/bash
# Overwrite the user config for hyprsunset with the Nomarchy default and restart the service.
#
nomarchy-refresh-config hypr/hyprsunset.conf
nomarchy-restart-hyprsunset

16
bin/nomarchy-refresh-limine Executable file
View File

@@ -0,0 +1,16 @@
#!/bin/bash
# Overwrite the user config for the Limine bootloader and rebuild it.
if [[ -f /boot/EFI/Linux/nomarchy_linux.efi ]] && [[ -f /boot/EFI/Linux/$(cat /etc/machine-id)_linux.efi ]]; then
echo "Cleanup extra UKI"
sudo rm -f /boot/EFI/Linux/$(cat /etc/machine-id)_linux.efi
fi
echo "Resetting limine config"
sudo mv /boot/limine.conf /boot/limine.conf.bak
sudo cp ~/.local/share/nomarchy/default/limine/limine.conf /boot/limine.conf
sudo limine-update
sudo limine-snapper-sync

24
bin/nomarchy-refresh-pacman Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
# Overwrite the package configuration for /etc/pacman with the Nomarchy default of using its dedicated mirrors and repositories, then update all packages.
# This is used after switching between Nomarchy release channels to ensure the right packages for the right channel are available.
# Take backup of existing files
sudo cp -f /etc/pacman.conf /etc/pacman.conf.bak
sudo cp -f /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.bak
channel="${1:-stable}"
if [[ $channel != "stable" && $channel != "rc" && $channel != "edge" ]]; then
echo "Error: Invalid channel '$channel'. Must be one of: stable, rc, edge"
exit 1
fi
echo "Setting channel to $channel"
echo
sudo cp -f "$OMARCHY_PATH/default/pacman/pacman-$channel.conf" /etc/pacman.conf
sudo cp -f "$OMARCHY_PATH/default/pacman/mirrorlist-$channel" /etc/pacman.d/mirrorlist
# Reset all package DBs and then update
sudo pacman -Syyuu --noconfirm

Some files were not shown because too many files have changed in this diff Show More