Initial import: Lua config, trimmed omarchy scripts, theme system

This commit is contained in:
2026-06-11 19:06:22 +02:00
commit 835a9aa3b5
536 changed files with 12459 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
omac
Executable
+88
View File
@@ -0,0 +1,88 @@
#!/bin/bash
# Backup script for archypr configuration system
# This script is versioned in the archypr-config git repo
# and should be run from ~/archypr-config/
set -euo pipefail
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BRANCH_FILE="$REPO_DIR/.machine-branch"
MACHINE_NAME="${HOSTNAME:-$(hostname)}"
# Read branch from config, or derive from hostname
if [[ -f $BRANCH_FILE ]]; then
BRANCH=$(cat "$BRANCH_FILE")
else
BRANCH="$MACHINE_NAME"
fi
cd "$REPO_DIR"
# Ensure we're on the right branch
if ! git branch --list "$BRANCH" | grep -q "$BRANCH"; then
git checkout -b "$BRANCH" main 2>/dev/null || git checkout -b "$BRANCH"
echo "Created branch: $BRANCH"
fi
git checkout "$BRANCH" 2>/dev/null || true
# Config directories to sync
CONFIG_DIRS=(
"hypr"
"waybar"
"walker"
"mako"
"swayosd"
"elephant"
"kitty"
"ghostty"
"alacritty"
"btop"
"fastfetch"
"autostart"
)
sync_configs() {
echo "--- Syncing configs..."
for dir in "${CONFIG_DIRS[@]}"; do
local src="$HOME/.config/$dir"
local dst="$REPO_DIR/configs/$dir"
if [[ -d $src ]]; then
mkdir -p "$dst"
rsync -a --delete "$src"/ "$dst"/
echo " Synced: $dir"
fi
done
}
check_and_push() {
if ! git diff --quiet || ! git diff --cached --quiet; then
local timestamp
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
git add -A
git commit -m "Auto-backup $BRANCH $timestamp"
git push -u origin "$BRANCH" 2>&1 || echo "WARNING: Push failed — check connection"
echo "--- Backed up at $timestamp"
else
echo "--- No changes to commit"
fi
}
case "${1:-}" in
restore)
"$REPO_DIR/restore.sh"
;;
check-only)
sync_configs
if ! git diff --quiet || ! git diff --cached --quiet; then
echo "Changes detected"
exit 0
else
echo "No changes"
exit 1
fi
;;
*)
sync_configs
check_and_push
;;
esac
+63
View File
@@ -0,0 +1,63 @@
#!/bin/bash
# Clone and trim omarchy scripts for archypr
# This script copies all omarchy scripts, renames them (removing 'omarchy-' prefix),
# and transforms variable names and paths.
set -euo pipefail
OMARCHY_BIN="/home/oval/.local/share/omarchy/bin"
TARGET_DIR="/home/oval/archypr-config/scripts"
THEMES_SOURCE="/home/oval/.local/share/omarchy/themes"
THEMES_TARGET="/home/oval/archypr-config/themes/themes"
echo "=== Cloning omarchy scripts ==="
mkdir -p "$TARGET_DIR"
count=0
# Process each omarchy script
for script in "$OMARCHY_BIN"/omarchy-*; do
[[ -f "$script" ]] || continue
base_name=$(basename "$script")
new_name="${base_name#omarchy-}"
# Create a temporary file for transformation
tmp_file=$(mktemp)
# Perform transformations on the file content
sed -E \
-e 's/OMARCHY_/ARCHYPR_/g' \
-e 's|~/.config/omarchy|~/.config/archypr|g' \
-e 's|~/.local/share/omarchy|~/.local/share/archypr|g' \
-e 's/\$OMARCHY_PATH/\$ARCHYPR_PATH/g' \
-e 's/"omarchy-/"archypr-/g' \
-e 's/ omarchy-/ archypr-/g' \
-e 's/omarchy-[a-z]/archypr-/g' \
-e 's/# omarchy:/# archypr:/g' \
"$script" > "$tmp_file"
# Move the transformed file to target
mv "$tmp_file" "$TARGET_DIR/$new_name"
chmod +x "$TARGET_DIR/$new_name"
((count++)) || true
done
echo "Cloned $count scripts to $TARGET_DIR"
echo "=== Copying omarchy themes ==="
mkdir -p "$THEMES_TARGET"
for theme_dir in "$THEMES_SOURCE"/*/; do
[[ -d "$theme_dir" ]] || continue
theme_name=$(basename "$theme_dir")
cp -r "$theme_dir" "$THEMES_TARGET/$theme_name"
done
echo "Copied themes to $THEMES_TARGET"
echo "=== Done ==="
echo "Next steps:"
echo " 1. Add ~/archypr-config/scripts to your PATH"
echo " 2. Create ~/archypr-config/.machine-branch with machine name"
echo " 3. Run backup.sh to create initial commit"
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
# Copy omarchy themed templates for archypr theme system
OMARCHY_TEMPLATES="/home/oval/.local/share/omarchy/default/themed"
TARGET="/home/oval/archypr-config/themes/templates"
mkdir -p "$TARGET"
for tpl in "$OMARCHY_TEMPLATES"/*.tpl; do
[[ -f "$tpl" ]] || continue
base_name=$(basename "$tpl" .tpl)
cp "$tpl" "$TARGET/$base_name"
done
echo "Copied templates to $TARGET"
+21
View File
@@ -0,0 +1,21 @@
#!/bin/bash
# Install archypr scripts to local bin
# Adds symlinks to ~/archypr-config/scripts in ~/.local/bin
TARGET_DIR="/home/oval/.local/bin"
ARCHYPR_SCRIPTS="/home/oval/archypr-config/scripts"
mkdir -p "$TARGET_DIR"
echo "Installing archypr scripts to $TARGET_DIR..."
count=0
for script in "$ARCHYPR_SCRIPTS"/*; do
[[ -f "$script" ]] || continue
base_name=$(basename "$script")
ln -sf "$script" "$TARGET_DIR/$base_name"
((count++)) || true
done
echo "Installed $count scripts to $TARGET_DIR"
echo "Make sure ~/.local/bin is in your PATH"
Executable
+66
View File
@@ -0,0 +1,66 @@
#!/bin/bash
# Restore script for archypr configuration system
# Restores configs from git backup to ~/.config/
set -euo pipefail
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BRANCH_FILE="$REPO_DIR/.machine-branch"
MACHINE_NAME="${HOSTNAME:-$(hostname)}"
if [[ -f $BRANCH_FILE ]]; then
BRANCH=$(cat "$BRANCH_FILE")
else
BRANCH="$MACHINE_NAME"
fi
cd "$REPO_DIR"
# Pull latest from remote
echo "--- Pulling latest from origin..."
git fetch origin "$BRANCH" 2>/dev/null || true
git checkout "$BRANCH" 2>/dev/null || git checkout -b "$BRANCH" 2>/dev/null || true
# Backup current configs before restoring
TIMESTAMP=$(date '+%Y%m%d_%H%M%S')
BACKUP_DIR="/tmp/archypr-config-restore-backup-$TIMESTAMP"
mkdir -p "$BACKUP_DIR"
CONFIG_DIRS=(
"hypr"
"waybar"
"walker"
"mako"
"swayosd"
"elephant"
"kitty"
"ghostty"
"alacritty"
"btop"
"fastfetch"
"autostart"
)
echo "--- Backing up current configs to $BACKUP_DIR ..."
for dir in "${CONFIG_DIRS[@]}"; do
local src="$HOME/.config/$dir"
if [[ -d $src ]]; then
cp -a "$src" "$BACKUP_DIR/"
echo " Backed up: $dir"
fi
done
# Restore configs from repo
echo "--- Restoring configs from backup..."
for dir in "${CONFIG_DIRS[@]}"; do
local src="$REPO_DIR/configs/$dir"
local dst="$HOME/.config/$dir"
if [[ -d $src ]]; then
mkdir -p "$(dirname "$dst")"
rsync -a "$src"/ "$dst"/
echo " Restored: $dir"
fi
done
echo "--- Restore complete. Previous configs saved to: $BACKUP_DIR"
echo " To undo: rsync -a $BACKUP_DIR/* ~/.config/"
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
# archypr:summary=Returns true if AC power is connected.
for ac in /sys/class/power_supply/AC* /sys/class/power_supply/ADP*; do
[[ -r $ac/online && $(cat "$ac/online") == "1" ]] && exit 0
done
exit 1
+21
View File
@@ -0,0 +1,21 @@
#!/bin/bash
# archypr:summary=Toggle microphone mute. Drives the hardware mic-mute LED on laptops that expose one.
wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle >/dev/null
if pactl get-source-mute @DEFAULT_SOURCE@ | rg -q 'yes'; then
led=on
osd_message='Microphone muted'
osd_icon='microphone-sensitivity-muted-symbolic'
else
led=off
osd_message='Microphone on'
osd_icon='audio-input-microphone-symbolic'
fi
archypr-rightness-keyboard-mute "$led"
archypr-wayosd-client \
--custom-message "$osd_message" \
--custom-icon "$osd_icon"
+62
View File
@@ -0,0 +1,62 @@
#!/bin/bash
# archypr:summary=Switch between audio outputs while preserving the mute status. By default mapped to Super + Mute.
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
archypr-swayosd-client --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
next_sink_wpid=$(echo "$next_sink" | jq -r '.properties."object.id"')
wpctl set-default "$next_sink_wpid"
fi
archypr-wayosd-client \
--custom-message "$next_sink_description" \
--custom-icon "$next_sink_volume_icon"
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
# archypr:summary=Returns the battery full capacity in Wh (rounded to whole number).
battery_info=$(upower -i $(upower -e | grep BAT))
echo "$battery_info" | awk '/energy-full:/ {
printf "%d", $2
exit
}'
+25
View File
@@ -0,0 +1,25 @@
#!/bin/bash
# archypr:summary=Designed to be run by systemd timer every 30 seconds and alerts if battery is low
# archypr:hidden=true
BATTERY_THRESHOLD=10
NOTIFICATION_FLAG="/run/user/$UID/omarchy_battery_notified"
BATTERY_LEVEL=$(archypr-attery-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
archypr-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
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
# archypr:summary=Returns true if a battery is present on the system.
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
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
# archypr:summary=Returns the battery percentage remaining as an integer.
upower -i $(upower -e | grep BAT) | awk '/percentage/ {
print int($2)
exit
}'
+22
View File
@@ -0,0 +1,22 @@
#!/bin/bash
# archypr:summary=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 ~ /^minute/) {
printf "%dm", int(value)
} else {
hours = int(value)
minutes = int((value - hours) * 60)
if (minutes > 0) {
printf "%dh %dm", hours, minutes
} else {
printf "%dh", hours
}
}
exit
}'
+27
View File
@@ -0,0 +1,27 @@
#!/bin/bash
# archypr:summary=Returns a formatted battery status string with percentage and power draw/charge.
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=$(archypr-attery-remaining-time)
capacity=$(archypr-attery-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
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
# archypr:summary=Set the branch for Omarchy's git repository.
# archypr:args=<master|rc|dev>
if (($# == 0)); then
echo "Usage: archypr-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 $ARCHYPR_PATH switch $branch
+28
View File
@@ -0,0 +1,28 @@
#!/bin/bash
# archypr:summary=Edit, set, or reset About branding
# archypr:group=branding
# archypr:name=about
# archypr:args=<image|text|reset>
# archypr:examples=omarchy branding about image | omarchy branding about text | omarchy branding about reset
set -euo pipefail
case "${1:-}" in
image)
image=$(archypr-enu-file "Logo image" "$HOME" "svg png")
if [[ -n $image ]] && archypr-transcode-ascii "$image" ~/.config/archypr/branding/about.txt --width 54 --height 26 --mode block; then
archypr-launch-about >/dev/null 2>&1
fi
;;
text)
archypr-launch-editor ~/.config/archypr/branding/about.txt >/dev/null 2>&1 && archypr-launch-about >/dev/null 2>&1
;;
reset)
cp "$ARCHYPR_PATH/icon.txt" ~/.config/archypr/branding/about.txt && archypr-launch-about >/dev/null 2>&1
;;
*)
echo "Usage: archypr-branding-about <image|text|reset>" >&2
exit 1
;;
esac
+28
View File
@@ -0,0 +1,28 @@
#!/bin/bash
# archypr:summary=Edit, set, or reset screensaver branding
# archypr:group=branding
# archypr:name=screensaver
# archypr:args=<image|text|reset>
# archypr:examples=omarchy branding screensaver image | omarchy branding screensaver text | omarchy branding screensaver reset
set -euo pipefail
case "${1:-}" in
image)
image=$(archypr-enu-file "Logo image" "$HOME" "svg png")
if [[ -n $image ]] && archypr-transcode-ascii "$image" ~/.config/archypr/branding/screensaver.txt; then
archypr-launch-screensaver force >/dev/null 2>&1
fi
;;
text)
archypr-launch-editor ~/.config/archypr/branding/screensaver.txt >/dev/null 2>&1 && archypr-launch-screensaver force >/dev/null 2>&1
;;
reset)
cp "$ARCHYPR_PATH/logo.txt" ~/.config/archypr/branding/screensaver.txt && archypr-launch-screensaver force >/dev/null 2>&1
;;
*)
echo "Usage: archypr-branding-screensaver <image|text|reset>" >&2
exit 1
;;
esac
+60
View File
@@ -0,0 +1,60 @@
#!/bin/bash
# archypr:summary=Adjust brightness on the most likely display device.
# archypr:args=<+N%|N%-|N%|off|on>
# archypr:examples=omarchy brightness display +5% | omarchy brightness display 5%- | omarchy brightness display 50% | omarchy brightness display off | omarchy brightness display on
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
if [[ $step == "off" ]]; then
hyprctl dispatch dpms off >/dev/null 2>&1
exit 0
elif [[ $step == "on" ]]; then
hyprctl dispatch dpms on >/dev/null 2>&1
exit 0
fi
if archypr-hyprland-monitor-focused-apple; then
archypr-brightness-display-apple "$step"
exit
fi
# Current brightness percentage
current=$(brightnessctl -d "$device" -m | cut -d',' -f4 | tr -d '%')
# Apply non-uniform step size: 1% steps if at or below 5%, otherwise set an
# absolute target percentage to avoid raw backlight rounding causing uneven OSD steps.
if [[ $step == "+5%" ]]; then
if (( current < 5 )); then
(( target = current + 1 ))
else
(( target = current + 5 ))
fi
(( target > 100 )) && target=100
step="$target%"
elif [[ $step == "5%-" ]]; then
if (( current <= 5 )); then
(( target = current - 1 ))
else
(( target = current - 5 ))
fi
(( target < 1 )) && target=1
step="$target%"
fi
# Set the actual brightness of the display device.
brightnessctl -d "$device" set "$step" >/dev/null
# Use SwayOSD to display the new brightness setting.
archypr-wayosd-brightness "$(brightnessctl -d "$device" -m | cut -d',' -f4 | tr -d '%')"
+34
View File
@@ -0,0 +1,34 @@
#!/bin/bash
# archypr:summary=Adjust the brightness on Apple Studio Displays and Apple XDR Displays using asdcontrol.
# archypr:args=<+N%|N%-|N%>
# archypr:examples=omarchy brightness display apple +5% | omarchy brightness display apple 5%- | omarchy brightness display apple 50%
if (( $# == 0 )); then
echo "Adjust Apple Display brightness by passing +5%, 5%-, or 100%"
else
step="$1"
if [[ $step =~ ^([0-9]+)%-$ ]]; then
step="-${BASH_REMATCH[1]}%"
fi
devices=()
for path in /dev/usb/hiddev* /dev/hiddev*; do
[[ -e $path ]] && devices+=("$path")
done
if (( ${#devices[@]} == 0 )); then
echo "No Apple Display HID device found"
exit 1
fi
device="$(sudo asdcontrol --detect "${devices[@]}" | grep -E '^/dev/(usb/)?hiddev' | cut -d: -f1 | head -n1)"
if [[ -z $device ]]; then
echo "No Apple Display HID device found"
exit 1
fi
sudo asdcontrol "$device" -- "$step" >/dev/null
value="$(sudo asdcontrol "$device" | awk -F= '/BRIGHTNESS=/{print $2+0}')"
archypr-swayosd-brightness "$(( value * 100 / 60000 ))"
fi
+55
View File
@@ -0,0 +1,55 @@
#!/bin/bash
# archypr:summary=Adjust keyboard backlight brightness using available steps.
# archypr:args=<up|down|cycle|off|restore>
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
if [[ $direction == "off" ]]; then
brightnessctl -sd "$device" set 0 >/dev/null
exit 0
elif [[ $direction == "restore" ]]; then
brightnessctl -rd "$device" >/dev/null
exit 0
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 10% of max brightness. Keyboards with many levels (e.g. 512)
# need larger steps; keyboards with few levels (e.g. 3) fall back to step=1.
step=$(( max_brightness / 10 ))
(( step < 1 )) && step=1
if [[ $direction == "cycle" ]]; then
new_brightness=$(( current_brightness + step ))
(( new_brightness > max_brightness )) && new_brightness=0
elif [[ $direction == "up" ]]; then
new_brightness=$(( current_brightness + step ))
(( new_brightness > max_brightness )) && new_brightness=$max_brightness
else
new_brightness=$(( current_brightness - step ))
(( 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))
archypr-wayosd-kbd-brightness "$percent"
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
# archypr:summary=Set the mic-mute indicator LED on laptops that expose a platform::micmute LED node.
# archypr:args=<on|off>
if [[ -e /sys/class/leds/platform::micmute/brightness ]]; then
case "$1" in
on) value=1 ;;
off) value=0 ;;
*) echo "Usage: $(basename "$0") <on|off>" >&2; exit 1 ;;
esac
brightnessctl --device="platform::micmute" set "$value" >/dev/null 2>&1 || true
fi
+295
View File
@@ -0,0 +1,295 @@
#!/bin/bash
# archypr:summary=Start or stop screen recording
# archypr:group=capture
# archypr:args=[--with-desktop-audio] [--with-microphone-audio] [--with-webcam] [--webcam-device=<device>] [--resolution=<size>] [--stop-recording]
# archypr:examples=omarchy screenrecord | omarchy capture screenrecord --with-desktop-audio
# archypr:aliases=omarchy screenrecord
#
# Env: ARCHYPR_SCREENRECORD_USE_PORTAL=true skips the built-in slurp picker and
# uses gpu-screen-recorder's xdg-desktop-portal capture backend instead. The
# portal backend was originally added (PR #3401) for HDR-aware capture, support
# for monitors driven by external GPUs, and window capture — enable it if any
# of those matter to you. Off by default because the portal path can fail EGL
# DMA-BUF modifier import on some configurations, leaving recording unable to
# start.
#
# Env: ARCHYPR_SCREENRECORD_DEBUG=true appends gpu-screen-recorder's stderr (and
# the picker target it was launched with) to /tmp/archypr-creenrecord.log so
# users can attach a log when reporting capture failures.
[[ -f ~/.config/user-dirs.dirs ]] && source ~/.config/user-dirs.dirs
OUTPUT_DIR="${ARCHYPR_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/archypr-creenrecord-filename"
LOG_FILE=$([[ ${ARCHYPR_SCREENRECORD_DEBUG:-false} == "true" ]] && echo "/tmp/archypr-creenrecord.log" || echo "/dev/null")
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
}
# Monitor + window rectangles on the focused workspace, in slurp's "X,Y WxH" format.
# Mirrors archypr-capture-screenshot so the picker UX is identical.
get_rectangles() {
local active_workspace=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .activeWorkspace.id')
hyprctl monitors -j | jq -r --arg ws "$active_workspace" '
.[] | select(.activeWorkspace.id == ($ws | tonumber)) |
"\(.x),\(.y) \(.width / .scale | floor)x\(.height / .scale | floor)"'
hyprctl clients -j | jq -r --arg ws "$active_workspace" '
.[] | select(.workspace.id == ($ws | tonumber)) |
"\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"'
}
# Echoes "monitor:NAME" when the selection matches an entire monitor, otherwise
# "region:WxH+X+Y" with physical-pixel coordinates ready for gpu-screen-recorder.
# Returns non-zero if the user cancelled the picker.
select_capture_target() {
local rects=$(get_rectangles)
hyprpicker -r -z >/dev/null 2>&1 &
local picker_pid=$!
sleep .1
local selection=$(echo "$rects" | slurp 2>/dev/null)
kill $picker_pid 2>/dev/null
# X and Y can be negative (Hyprland monitor positions in multi-display layouts);
# widths and heights are always positive.
[[ $selection =~ ^(-?[0-9]+),(-?[0-9]+)[[:space:]]([0-9]+)x([0-9]+)$ ]] || return 1
local sx=${BASH_REMATCH[1]} sy=${BASH_REMATCH[2]}
local sw=${BASH_REMATCH[3]} sh=${BASH_REMATCH[4]}
# A bare click (area < 20px²) snaps to whichever rectangle the click landed
# inside, so users don't end up with accidental 2px recordings.
if ((sw * sh < 20)); then
while IFS= read -r rect; do
[[ $rect =~ ^(-?[0-9]+),(-?[0-9]+)[[:space:]]([0-9]+)x([0-9]+)$ ]] || continue
local rx=${BASH_REMATCH[1]} ry=${BASH_REMATCH[2]}
local rw=${BASH_REMATCH[3]} rh=${BASH_REMATCH[4]}
if ((sx >= rx && sx < rx + rw && sy >= ry && sy < ry + rh)); then
sx=$rx sy=$ry sw=$rw sh=$rh
break
fi
done <<<"$rects"
fi
# When the selection exactly matches a monitor, prefer -w <monitor> over a
# region capture — same kms backend, but no scaling math and full native res.
local monitor=$(hyprctl monitors -j | jq -r --argjson x "$sx" --argjson y "$sy" --argjson w "$sw" --argjson h "$sh" '
.[] | select(.x == $x and .y == $y and (.width / .scale | floor) == $w and (.height / .scale | floor) == $h) | .name' | head -1)
if [[ -n $monitor ]]; then
echo "monitor:$monitor"
return
fi
# gpu-screen-recorder wants region geometry in the compositor's logical
# coordinate space — same space slurp returns — so pass the values through
# untouched. (gsr scales to physical pixels itself based on the monitor.)
echo "region:${sw}x${sh}+${sx}+${sy}"
}
start_screenrecording() {
local capture_args=()
local target
# Opt-in path for HDR, external-GPU monitors, and window capture (all things
# the portal backend supports and the kms backend doesn't). Default flow uses
# slurp + the kms backend, which avoids the EGL DMA-BUF modifier import
# failures the portal path can hit on some configurations.
if [[ ${ARCHYPR_SCREENRECORD_USE_PORTAL:-false} == "true" ]]; then
target="portal"
capture_args=(-w portal -s "${RESOLUTION:-$(default_resolution)}")
else
target=$(select_capture_target) || return 1
case $target in
monitor:*)
capture_args=(-w "${target#monitor:}" -s "${RESOLUTION:-$(default_resolution)}")
;;
region:*)
capture_args=(-w "${target#region:}")
[[ -n $RESOLUTION ]] && capture_args+=(-s "$RESOLUTION")
;;
esac
fi
[[ $WEBCAM == "true" ]] && start_webcam_overlay
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)
echo "===== $(date '+%F %T') args: $* target: $target =====" >>"$LOG_FILE"
gpu-screen-recorder "${capture_args[@]}" -k auto -f 60 -fm cfr -fallback-cpu-encoding yes -o "$filename" "${audio_args[@]}" 2>>"$LOG_FILE" &
local pid=$!
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
finalize_recording
local filename=$(cat "$RECORDING_FILE" 2>/dev/null)
echo "$filename"
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
}
finalize_recording() {
local latest
latest=$(cat "$RECORDING_FILE" 2>/dev/null)
[[ -f $latest ]] || return
# Re-encode only when the first GOP contains discardable warmup packets — stream copy can't
# trim those (it rewinds to the keyframe). Clean recordings stay on the fast stream-copy path.
local video_codec=(-c:v copy)
if ffprobe -v error -select_streams v:0 -read_intervals %+0.2 -show_entries packet=flags -of csv=p=0 "$latest" 2>/dev/null | grep -q D; then
video_codec=(-c:v libx264 -preset veryfast -crf 20)
fi
# Trim the first frame, and normalize audio to -14 LUFS if present, in a single pass
local args=(-y -ss 0.1 -i "$latest" "${video_codec[@]}")
if ffprobe -v error -select_streams a -show_entries stream=codec_type -of csv=p=0 "$latest" 2>/dev/null | grep -q audio; then
# Hard-mute the first 400ms to drop the PipeWire capture-open pop (a near-clipping
# transient around 130-200ms that a gentle fade-in can't attenuate enough), then a
# 50ms fade avoids a click at the boundary before loudnorm normalizes the rest.
args+=(-af "volume=enable='lt(t,0.4)':volume=0,afade=t=in:st=0.4:d=0.05,loudnorm=I=-14:TP=-1.5:LRA=11")
fi
local processed="${latest%.mp4}-processed.mp4"
if ffmpeg "${args[@]}" "$processed" -loglevel quiet 2>/dev/null; then
mv "$processed" "$latest"
else
rm -f "$processed"
fi
}
if screenrecording_active; then
stop_screenrecording
elif [[ $STOP_RECORDING == "true" ]]; then
exit 1
else
start_screenrecording || cleanup_webcam
fi
+147
View File
@@ -0,0 +1,147 @@
#!/bin/bash
# archypr:summary=Take a screenshot
# archypr:group=capture
# archypr:args=[smart|region|windows|fullscreen] [slurp|copy|save] [--editor=<name>]
# archypr:examples=omarchy screenshot | omarchy capture screenshot region
# archypr:aliases=omarchy screenshot
[[ -f ~/.config/user-dirs.dirs ]] && source ~/.config/user-dirs.dirs
OUTPUT_DIR="${ARCHYPR_SCREENSHOT_DIR:-${XDG_PICTURES_DIR:-$HOME/Pictures}}"
if [[ ! -d $OUTPUT_DIR ]]; then
mkdir -p "$OUTPUT_DIR"
notify-send "Created screenshot directory: $OUTPUT_DIR" -u normal -t 2000
fi
pkill slurp && exit 0
SCREENSHOT_EDITOR="${ARCHYPR_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])"'
}
# Keep hyprpicker alive until after grim captures so the screenshot sees the
# frozen overlay rather than live content shifting during teardown.
cleanup_freeze() {
[[ -n $PID ]] && kill $PID 2>/dev/null
}
trap cleanup_freeze EXIT
# Select based on mode
case "$MODE" in
region)
hyprpicker -r -z >/dev/null 2>&1 &
PID=$!
sleep .1
SELECTION=$(slurp 2>/dev/null)
;;
windows)
hyprpicker -r -z >/dev/null 2>&1 &
PID=$!
sleep .1
SELECTION=$(get_rectangles | slurp -r 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)
# 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"
case "$PROCESSING" in
slurp)
grim -g "$SELECTION" "$FILEPATH" || exit 1
echo "$FILEPATH"
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"
) >/dev/null 2>&1 &
;;
copy)
grim -g "$SELECTION" - | wl-copy
;;
save)
grim -g "$SELECTION" "$FILEPATH" || exit 1
echo "$FILEPATH"
;;
esac
+26
View File
@@ -0,0 +1,26 @@
#!/bin/bash
# archypr:summary=Extract text from a screenshot region with OCR
# archypr:group=capture
# archypr:examples=omarchy capture ocr
# Keep hyprpicker alive until after grim captures so the screenshot sees the
# frozen overlay rather than live content shifting during teardown.
cleanup_freeze() {
[[ -n $PID ]] && kill $PID 2>/dev/null
}
trap cleanup_freeze EXIT
hyprpicker -r -z >/dev/null 2>&1 &
PID=$!
sleep .1
SELECTION=$(slurp 2>/dev/null)
[[ -z $SELECTION ]] && exit 0
TEXT=$(grim -g "$SELECTION" - | tesseract stdin stdout --oem 1 --psm 6 -l "${ARCHYPR_OCR_LANGS:-eng}" --dpi 300 -c preserve_interword_spaces=1 2>/dev/null) || exit 1
[[ -z $TEXT ]] && exit 1
printf "%s" "$TEXT" | wl-copy
notify-send "󰴑 Copied text from selection to clipboard"
+22
View File
@@ -0,0 +1,22 @@
#!/bin/bash
# archypr:summary=Set the Omarchy channel, which dictates what git branch and package repository is used.
# archypr:args=<stable|rc|edge|dev>
# archypr:requires-sudo=true
if (($# == 0)); then
echo "Usage: archypr-channel-set [stable|rc|edge|dev]"
exit 1
else
channel="$1"
fi
case "$channel" in
"stable") archypr-branch-set "master" && archypr-refresh-pacman "stable" ;;
"rc") archypr-branch-set "rc" && archypr-refresh-pacman "rc" ;;
"edge") archypr-branch-set "master" && archypr-refresh-pacman "edge" ;;
"dev") archypr-branch-set "dev" && archypr-refresh-pacman "edge" ;;
*) echo "Unknown channel: $channel"; exit 1; ;;
esac
archypr-pdate -y
+11
View File
@@ -0,0 +1,11 @@
#!/bin/bash
# archypr:summary=Check whether any required commands are missing
for cmd in "$@"; do
if ! command -v "$cmd" &>/dev/null; then
exit 0
fi
done
exit 1
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
# archypr:summary=Check whether all required commands are available
for cmd in "$@"; do
command -v "$cmd" &>/dev/null || exit 1
done
exit 0
+21
View File
@@ -0,0 +1,21 @@
#!/bin/bash
# archypr:summary=Print the current working directory of the active terminal window
# archypr:hidden=true
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
+58
View File
@@ -0,0 +1,58 @@
#!/bin/bash
# archypr:summary=Add or remove an EFI boot entry for the Omarchy UKI, allowing the system to boot directly
# archypr:requires-sudo=true
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
existing_entry=$(efibootmgr | grep -E "^Boot[0-9A-Fa-f]+\*? Omarchy([[:space:]]|$)" | head -1)
if [[ -n $existing_entry ]]; then
boot_num=$(echo "$existing_entry" | sed -n 's/^Boot\([0-9A-Fa-f]\+\).*/\1/p')
if gum confirm "Disable direct boot (remove Omarchy EFI entry)?"; then
echo "Removing EFI boot entry $boot_num"
sudo efibootmgr --bootnum "$boot_num" --delete-bootnum >/dev/null
fi
exit 0
else
uki_file=$(find /boot/EFI/Linux/ -name "omarchy*.efi" -printf "%f\n" 2>/dev/null | head -1)
if [[ -z $uki_file ]]; then
echo "Error: No Omarchy 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 "Omarchy" \
--loader "\\EFI\\Linux\\$uki_file"
fi
fi
Executable
+96
View File
@@ -0,0 +1,96 @@
#!/bin/bash
# archypr:summary=Print debugging information
# archypr:args=[--no-sudo] [--print]
# archypr:examples=omarchy debug --print --no-sudo
# archypr:requires-sudo=true
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: archypr-debug [--no-sudo] [--print]"
exit 1
;;
esac
done
LOG_FILE="/tmp/archypr-ebug.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)
Omarchy Branch: $(git -C "$ARCHYPR_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 logs.omarchy.org..."
URL=$(curl -sf -F "file=@$LOG_FILE" https://logs.omarchy.org/)
if (( $? == 0 )) && [[ -n $URL ]]; then
echo "✓ Log uploaded successfully!"
echo "Share this URL:"
echo ""
echo " $URL"
else
echo "Error: Failed to upload log file"
exit 1
fi
;;
"View log")
less "$LOG_FILE"
;;
"Save in current directory")
cp "$LOG_FILE" "./archypr-ebug.log"
echo "✓ Log saved to $(pwd)/archypr-ebug.log"
;;
esac
+40
View File
@@ -0,0 +1,40 @@
#!/bin/bash
# archypr:summary=Set the default browser for Omarchy and XDG handlers
# archypr:args=[chromium|chrome|brave|brave-origin|edge|firefox|zen]
# archypr:examples=omarchy default browser firefox | omarchy default browser brave
if (($# == 0)); then
case "$(xdg-settings get default-web-browser)" in
chromium.desktop) echo "chromium" ;;
google-chrome.desktop) echo "chrome" ;;
brave-browser.desktop) echo "brave" ;;
brave-origin-beta.desktop) echo "brave-origin" ;;
microsoft-edge.desktop) echo "edge" ;;
firefox.desktop) echo "firefox" ;;
zen.desktop) echo "zen" ;;
*) xdg-settings get default-web-browser ;;
esac
exit 0
fi
case "$1" in
chromium) desktop_id="chromium.desktop"; name="Chromium"; glyph="" ;;
chrome) desktop_id="google-chrome.desktop"; name="Chrome"; glyph="󰊯" ;;
brave) desktop_id="brave-browser.desktop"; name="Brave"; glyph="󰖟" ;;
brave-origin) desktop_id="brave-origin-beta.desktop"; name="Brave Origin"; glyph="󰖟" ;;
edge) desktop_id="microsoft-edge.desktop"; name="Edge"; glyph="󰇩" ;;
firefox) desktop_id="firefox.desktop"; name="Firefox"; glyph="󰈹" ;;
zen) desktop_id="zen.desktop"; name="Zen"; glyph="󰰷" ;;
*)
echo "Usage: archypr-default-browser <chromium|chrome|brave|brave-origin|edge|firefox|zen>"
exit 1
;;
esac
xdg-settings set default-web-browser "$desktop_id"
xdg-mime default "$desktop_id" x-scheme-handler/http
xdg-mime default "$desktop_id" x-scheme-handler/https
xdg-mime default "$desktop_id" text/html
notify-send -u low "$glyph $name is now the default browser"
+30
View File
@@ -0,0 +1,30 @@
#!/bin/bash
# archypr:summary=Set the default editor for $EDITOR
# archypr:args=[code|cursor|zed|sublime_text|helix|vim|emacs|nvim]
# archypr:examples=omarchy default editor | omarchy default editor code | omarchy default editor helix
if (($# == 0)); then
sed -n 's/^export EDITOR=//p' ~/.config/uwsm/default | head -n 1
exit 0
fi
case "$1" in
code) editor="code"; name="VSCode"; glyph="" ;;
cursor) editor="cursor"; name="Cursor"; glyph="" ;;
zed | zeditor) editor="zeditor"; name="Zed"; glyph="" ;;
sublime_text) editor="sublime_text"; name="Sublime Text"; glyph="" ;;
helix) editor="helix"; name="Helix"; glyph="" ;;
vim) editor="vim"; name="Vim"; glyph="" ;;
emacs) editor="emacs"; name="Emacs"; glyph="" ;;
nvim) editor="nvim"; name="Neovim"; glyph="" ;;
*)
echo "Usage: archypr-default-editor <code|cursor|zed|sublime_text|helix|vim|emacs|nvim>"
exit 1
;;
esac
sed -i "s/^export EDITOR=.*/export EDITOR=$editor/" ~/.config/uwsm/default
export EDITOR="$editor"
notify-send -u low "$glyph $name is now the default editor" " Effective after logging out"
+36
View File
@@ -0,0 +1,36 @@
#!/bin/bash
# archypr:summary=Set the default terminal used by xdg-terminal-exec
# archypr:args=[alacritty|foot|ghostty|kitty]
# archypr:examples=omarchy default terminal ghostty | omarchy default terminal kitty
if (($# == 0)); then
desktop_id=$(grep -vE '^($|#)' ~/.config/xdg-terminals.list 2>/dev/null | head -n 1)
case "$desktop_id" in
Alacritty.desktop) echo "alacritty" ;;
foot.desktop) echo "foot" ;;
com.mitchellh.ghostty.desktop) echo "ghostty" ;;
kitty.desktop) echo "kitty" ;;
*) echo "$desktop_id" ;;
esac
exit 0
fi
case "$1" in
alacritty) desktop_id="Alacritty.desktop"; name="Alacritty"; glyph="" ;;
foot) desktop_id="foot.desktop"; name="Foot"; glyph="" ;;
ghostty) desktop_id="com.mitchellh.ghostty.desktop"; name="Ghostty"; glyph="" ;;
kitty) desktop_id="kitty.desktop"; name="Kitty"; glyph="" ;;
*)
echo "Usage: archypr-default-terminal <alacritty|foot|ghostty|kitty>"
exit 1
;;
esac
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
notify-send -u low "$glyph $name is now the default terminal"
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
# archypr:summary=Creates a new Omarchy migration named after the unix timestamp of the last commit.
cd ~/.local/share/archypr
migration_file="$HOME/.local/share/omarchy/migrations/$(git log -1 --format=%cd --date=unix).sh"
touch $migration_file
if [[ $1 != "--no-edit" ]]; then
nvim $migration_file
fi
echo $migration_file
+111
View File
@@ -0,0 +1,111 @@
#!/bin/bash
# archypr:summary=Measure Omarchy CLI response times
# archypr:args=[--repeat=<count>]
# archypr:examples=omarchy dev benchmark | omarchy dev benchmark --repeat=10
set -euo pipefail
ARCHYPR_BIN_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
CLI="$ARCHYPR_BIN_DIR/omarchy"
REPEAT=5
show_help() {
cat <<'EOF'
Usage:
omarchy dev benchmark [--repeat=<count>]
Measure response times for common Omarchy CLI surfaces.
Options:
--repeat=<count> Number of times to run each case (default: 5)
EOF
}
now_us() {
local now="${EPOCHREALTIME/./}"
printf '%s' "$now"
}
format_ms() {
local us="$1"
printf '%d.%03d' "$(( us / 1000 ))" "$(( us % 1000 ))"
}
run_case() {
local label="$1"
shift
local total_us=0
local min_us=0
local max_us=0
local elapsed_us=0
local start_us=0
local end_us=0
local status=0
for (( i = 1; i <= REPEAT; i++ )); do
start_us=$(now_us)
if "$@" >/dev/null; then
status=0
else
status=$?
fi
end_us=$(now_us)
if (( status != 0 )); then
printf '%-34s failed (exit %d)\n' "$label" "$status"
return "$status"
fi
elapsed_us=$(( end_us - start_us ))
total_us=$(( total_us + elapsed_us ))
if (( i == 1 || elapsed_us < min_us )); then
min_us=$elapsed_us
fi
if (( elapsed_us > max_us )); then
max_us=$elapsed_us
fi
done
printf '%-34s avg %8s ms min %8s ms max %8s ms\n' \
"$label" \
"$(format_ms "$(( total_us / REPEAT ))")" \
"$(format_ms "$min_us")" \
"$(format_ms "$max_us")"
}
while (( $# > 0 )); do
case "$1" in
--repeat=*)
REPEAT="${1#*=}"
;;
--help | -h)
show_help
exit 0
;;
*)
echo "Unknown option: $1" >&2
show_help >&2
exit 2
;;
esac
shift
done
if [[ ! $REPEAT =~ ^[0-9]+$ ]] || (( REPEAT < 1 )); then
echo "--repeat must be a positive integer" >&2
exit 2
fi
printf 'Omarchy CLI benchmark (%d runs each)\n\n' "$REPEAT"
run_case "omarchy" "$CLI"
run_case "omarchy --help" "$CLI" --help
run_case "omarchy commands" "$CLI" commands
run_case "omarchy commands --json" "$CLI" commands --json
run_case "omarchy commands --all --json" "$CLI" commands --all --json
run_case "omarchy theme set --help" "$CLI" theme set --help
run_case "omarchy screenshot --help" "$CLI" screenshot --help
run_case "omarchy restart --help" "$CLI" restart --help
run_case "omarchy theme current" "$CLI" theme current
+89
View File
@@ -0,0 +1,89 @@
#!/bin/bash
# archypr:summary=Show Omarchy bin metadata fields and defaults
# archypr:args=[--json]
set -euo pipefail
show_json() {
jq -n '{
ok: true,
defaults: {
group: "first filename segment after archypr-",
name: "remaining filename segments with dashes converted to spaces",
route: "omarchy <group> <name>",
binary: "filename",
requires_sudo: false
},
fields: [
{name: "summary", required: true, type: "string", note: "One-line human and agent-facing description."},
{name: "group", required: false, type: "string", note: "Only set when the route group should differ from the filename-derived group."},
{name: "name", required: false, type: "string", note: "Only set when the route name should differ from the filename-derived name. May be empty for root commands."},
{name: "args", required: false, type: "string", note: "Only set when the command accepts arguments."},
{name: "examples", required: false, type: "string", note: "Pipe-separated examples."},
{name: "aliases", required: false, type: "string", note: "Pipe-separated alternate routes, e.g. omarchy screenshot."},
{name: "requires-sudo", required: false, type: "true", default: false, note: "Only include when true."},
{name: "hidden", required: false, type: "true", default: false, note: "Hide from default command listings; visible with --all."}
]
}'
}
show_help() {
cat <<'EOF'
Omarchy bin metadata
Metadata lives in the top comment block of each executable bin/omarchy-* file.
Keep it slim: define only fields that are required or override defaults.
Required:
# archypr:summary=<one-line description>
Inferred defaults:
group first filename segment after archypr-
name remaining filename segments, with dashes converted to spaces
route omarchy <group> <name>
binary filename
requires-sudo false
hidden false
Optional fields:
# archypr:group=<group> route override only
# archypr:name=<name> route override only; may be empty
# archypr:args=<args> only if the command accepts args
# archypr:examples=<cmd> | <cmd> pipe-separated examples
# archypr:aliases=<route> | <route> pipe-separated alternate routes
# archypr:requires-sudo=true only when true
# archypr:hidden=true hide from default command listings
Do not define:
binary inferred from filename
usage derived from route + args
false flags or empty args
Examples:
# archypr:summary=Restart Walker and related user services
# archypr:summary=Take a screenshot
# archypr:group=capture
# archypr:args=[smart|region|windows|fullscreen] [slurp|copy] [--editor=<name>]
# archypr:examples=omarchy screenshot | omarchy capture screenshot region
# archypr:aliases=omarchy screenshot
EOF
}
case "${1:-}" in
--json)
show_json
;;
--help | -h)
show_help
;;
"")
show_help
;;
*)
echo "Unknown option: $1" >&2
show_help >&2
exit 2
;;
esac
+50
View File
@@ -0,0 +1,50 @@
#!/bin/bash
# archypr:summary=Print drive information such as size, model, and mount details
# archypr:args=<drive>
if (($# == 0)); then
echo "Usage: archypr-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"
+24
View File
@@ -0,0 +1,24 @@
#!/bin/bash
# archypr:summary=Set a new encryption password for a drive selected.
# archypr:requires-sudo=true
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="$(archypr-rive-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
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
# archypr:summary=Select a drive from a list with info that includes space and brand. Used by archypr-drive-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+="$(archypr-rive-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}'
+27
View File
@@ -0,0 +1,27 @@
#!/bin/bash
# archypr:summary=Finish the installation of Omarchy with items that can only be done after logging in.
# archypr:requires-sudo=true
set -e
FIRST_RUN_MODE=~/.local/state/omarchy/first-run.mode
if [[ -f $FIRST_RUN_MODE ]]; then
rm -f "$FIRST_RUN_MODE"
bash "$ARCHYPR_PATH/install/first-run/battery-monitor.sh"
bash "$ARCHYPR_PATH/install/first-run/recover-internal-monitor.sh"
bash "$ARCHYPR_PATH/install/first-run/cleanup-reboot-sudoers.sh"
bash "$ARCHYPR_PATH/install/first-run/firewall.sh"
bash "$ARCHYPR_PATH/install/first-run/dns-resolver.sh"
bash "$ARCHYPR_PATH/install/first-run/gnome-theme.sh"
bash "$ARCHYPR_PATH/install/first-run/swayosd.sh"
bash "$ARCHYPR_PATH/install/first-run/gtk-primary-paste.sh"
bash "$ARCHYPR_PATH/install/first-run/elephant.sh"
archypr-hook-install post-update "$ARCHYPR_PATH/install/first-run/install-voxtype.hook"
sudo rm -f /etc/sudoers.d/first-run
bash "$ARCHYPR_PATH/install/first-run/welcome.sh"
bash "$ARCHYPR_PATH/install/first-run/wifi.sh"
fi
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
# archypr:summary=Show current monospace font
# archypr:examples=omarchy font current
grep -oP 'font-family:\s*["'\'']?\K[^;"'\'']+' ~/.config/waybar/style.css | head -n1
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
# archypr:summary=List available monospace fonts
# archypr:examples=omarchy font list | omarchy font set "CaskaydiaMono Nerd Font"
fc-list :spacing=100 -f "%{family[0]}\n" | grep -v -i -E 'emoji|signwriting|omarchy' | sort -u
+55
View File
@@ -0,0 +1,55 @@
#!/bin/bash
# archypr:summary=Set the system monospace font
# archypr:args=<font-name>
# archypr:examples=omarchy font list | omarchy font set "CaskaydiaMono Nerd Font"
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
if [[ -f ~/.config/foot/foot.ini ]]; then
sed -i "s/^font=.*/font=$font_name:size=9/g" ~/.config/foot/foot.ini
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
archypr-restart-waybar
archypr-restart-swayosd
if pgrep -x ghostty; then
notify-send -u low " You must restart Ghostty to see font change"
fi
if pgrep -x foot; then
notify-send -u low " You must restart Foot to see font change"
fi
archypr-hook font-set "$font_name"
else
echo "Font '$font_name' not found."
exit 1
fi
else
echo "Usage: archypr-font-set <font-name>"
fi
+19
View File
@@ -0,0 +1,19 @@
#!/bin/bash
# archypr:summary=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/omarchy_resume.conf ]]; then
exit 0
else
exit 1
fi
+59
View File
@@ -0,0 +1,59 @@
#!/bin/bash
# archypr:summary=Remove hibernation setup including swap and boot resume settings
# archypr:requires-sudo=true
MKINITCPIO_CONF="/etc/mkinitcpio.conf.d/omarchy_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"
+132
View File
@@ -0,0 +1,132 @@
#!/bin/bash
# archypr:summary=Set up hibernation with swap and boot resume configuration
# archypr:requires-sudo=true
# archypr:args=[--force] [--no-rebuild]
FORCE=false
NO_REBUILD=false
for arg in "$@"; do
case "$arg" in
--force) FORCE=true ;;
--no-rebuild) NO_REBUILD=true ;;
esac
done
if [[ ! -f /sys/power/image_size ]]; then
echo -e "Hibernation is not supported on your system" >&2
exit 0
fi
# When --no-rebuild is set, the caller is responsible for the UKI rebuild
# (e.g. running before limine-mkinitcpio-hook is installed during initial
# install), so we only require limine-mkinitcpio when we'd invoke it ourselves.
if ! $NO_REBUILD && ! command -v limine-mkinitcpio &>/dev/null; then
echo "Skipping hibernation setup (requires Limine bootloader)"
exit 0
fi
MKINITCPIO_CONF="/etc/mkinitcpio.conf.d/omarchy_resume.conf"
SWAP_FILE="/swap/swapfile"
RESUME_DROP_IN="/etc/limine-entry-tool.d/resume.conf"
# Check if hibernation is already configured
if [[ -f $MKINITCPIO_CONF ]] && grep -q "^HOOKS+=(resume)$" "$MKINITCPIO_CONF"; then
# Fix empty resume_offset if btrfs map-swapfile failed during initial setup
if [[ -f $RESUME_DROP_IN ]] && grep -q 'resume_offset="$' "$RESUME_DROP_IN" && [[ -f $SWAP_FILE ]]; then
RESUME_OFFSET=$(sudo btrfs inspect-internal map-swapfile -r "$SWAP_FILE" 2>/dev/null)
if [[ -n $RESUME_OFFSET ]]; then
echo "Fixing empty resume_offset ($RESUME_OFFSET)"
sudo sed -i "s/resume_offset=\"$/resume_offset=$RESUME_OFFSET\"/" "$RESUME_DROP_IN"
sudo sed -i "s/resume_offset=\"$/resume_offset=$RESUME_OFFSET\"/" /etc/default/limine
$NO_REBUILD || sudo limine-mkinitcpio
fi
fi
echo "Hibernation is already set up"
exit 0
fi
if ! $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"
# 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 "$ARCHYPR_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.
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")
if [[ -n $RESUME_OFFSET ]]; then
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
else
echo "Warning: Could not determine resume offset for $SWAP_FILE" >&2
fi
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
if ! $NO_REBUILD; then
# limine-mkinitcpio rebuilds initramfs/UKI for all kernels and updates the
# /boot/limine.conf entries via limine-entry-tool. The limine bootloader
# binary on the ESP doesn't change here, so we don't need limine-update
# (which would also re-deploy the binary and rebuild a second time).
echo "Regenerating initramfs..."
sudo limine-mkinitcpio
echo
fi
if ! $FORCE && ! $NO_REBUILD && gum confirm "Reboot to enable hibernation?"; then
archypr-system-reboot
fi
Executable
+28
View File
@@ -0,0 +1,28 @@
#!/bin/bash
# archypr:summary=Run a named hook from ~/.config/archypr/hooks/<name> and ~/.config/archypr/hooks/<name>.d/.
# archypr:args=[name] [args...]
set -e
if (( $# < 1 )); then
echo "Usage: archypr-hook [name] [args...]"
exit 1
fi
HOOK=$1
HOOK_PATH="$HOME/.config/omarchy/hooks/$1"
HOOK_DIR="$HOOK_PATH.d"
shift
if [[ -f $HOOK_PATH ]]; then
bash "$HOOK_PATH" "$@" || echo "Hook failed: $HOOK_PATH"
fi
if [[ -d $HOOK_DIR ]]; then
for hook in "$HOOK_DIR"/*; do
[[ -f $hook ]] || continue
[[ $hook == *.sample ]] && continue
bash "$hook" "$@" || echo "Hook failed: $hook"
done
fi
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
# archypr:summary=Install a hook into ~/.config/archypr/hooks/<type>.d/
# archypr:group=hook
# archypr:name=install
# archypr:args=<type> <file>
# archypr:examples=omarchy hook install post-update ~/my-hook
set -e
if (( $# != 2 )); then
echo "Usage: archypr-hook-install <type> <file>"
exit 1
fi
HOOK_TYPE=$1
HOOK_FILE=$2
HOOK_DIR="$HOME/.config/omarchy/hooks/$HOOK_TYPE.d"
HOOK_NAME=$(basename "$HOOK_FILE")
HOOK_PATH="$HOOK_DIR/$HOOK_NAME"
if [[ ! -f $HOOK_FILE ]]; then
echo "Hook file not found: $HOOK_FILE"
exit 1
fi
mkdir -p "$HOOK_DIR"
cp "$HOOK_FILE" "$HOOK_PATH"
chmod 755 "$HOOK_PATH"
echo "Installed $HOOK_TYPE hook: $HOOK_PATH"
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
# archypr:summary=Detect ASUS ExpertBook B9406 series laptops on Intel Panther Lake.
archypr-w-match "B9406" && archypr-hw-intel-ptl
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
# archypr:summary=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
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
# archypr:summary=Detect ASUS Zenbook UX5406AA series laptops on Intel Panther Lake.
archypr-w-match "ux5406aa" && archypr-hw-intel-ptl
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
# archypr:summary=Match Dell XPS systems with the Synaptics haptic touchpad.
archypr-w-match "XPS" && [[ -e /sys/bus/i2c/devices/i2c-VEN_06CB:00 ]]
+7
View File
@@ -0,0 +1,7 @@
#!/bin/bash
# archypr:summary=Match Dell XPS systems with LG OLED panel on Intel Panther Lake (Xe3) GPU.
archypr-w-match "XPS" \
&& archypr-hw-intel-ptl \
&& test "$(od -An -tx1 -j8 -N2 /sys/class/drm/card*-eDP-*/edid 2>/dev/null | tr -d ' \n')" = "30e4"
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
# archypr:summary=Returns true when an external monitor is physically connected.
for status in /sys/class/drm/card*-*/status; do
[[ "$status" == *-eDP-*/status ]] && continue
[[ "$(<"$status")" == "connected" ]] && exit 0
done
exit 1
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
# archypr:summary=Detect whether the computer is a Framework Laptop 16.
[[ $(cat /sys/class/dmi/id/sys_vendor 2>/dev/null) == "Framework" ]] &&
archypr-hw-match "Laptop 16"
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
# archypr:summary=Detect whether the system has an active hybrid GPU configuration
if command -v supergfxctl &>/dev/null; then
supergfxctl -s 2>/dev/null | grep -qw Hybrid
else
(($(lspci | grep -cE 'VGA|3D|Display') >= 2))
fi
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
# archypr:summary=Detect whether the computer has an Intel CPU.
[[ $(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | tr -d ' ') == "GenuineIntel" ]]
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
# archypr:summary=Detect whether the computer has an Intel Panther Lake GPU.
lspci | grep -iE 'vga|3d|display' | grep -qi 'panther lake'
+7
View File
@@ -0,0 +1,7 @@
#!/bin/bash
# archypr:summary=Match against the computer's DMI product name or product family (case-insensitive).
# archypr:args=<pattern>
grep -qi "$1" /sys/class/dmi/id/product_name 2>/dev/null ||
grep -qi "$1" /sys/class/dmi/id/product_family 2>/dev/null
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
# archypr:summary=Detect whether the computer has an NVIDIA GPU with GSP firmware (Turing or newer).
# GTX 16xx, RTX 20xx-50xx, RTX Pro, Quadro RTX, datacenter A/H/T/L series.
lspci | grep -i 'nvidia' | grep -qE "GTX 16[0-9]{2}|RTX [2-5][0-9]{3}|RTX PRO [0-9]{4}|Quadro RTX|RTX A[0-9]{4}|A[1-9][0-9]{2}|H[1-9][0-9]{2}|T4|L[0-9]+"
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
# archypr:summary=Detect whether the computer has an NVIDIA GPU without GSP firmware (Maxwell/Pascal/Volta).
# GTX 9xx/10xx, GT 10xx, Quadro P/M/GV, MX series, Titan X/Xp/V, Tesla V100.
lspci | grep -i 'nvidia' | grep -qE "GTX (9[0-9]{2}|10[0-9]{2})|GT 10[0-9]{2}|Quadro [PM][0-9]{3,4}|Quadro GV100|MX *[0-9]+|Titan (X|Xp|V)|Tesla V100"
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
# archypr:summary=Clear the internal-monitor-disable toggle if no external display is connected.
TOGGLE="$HOME/.local/state/omarchy/toggles/hypr/internal-monitor-disable.conf"
if [[ -f $TOGGLE ]] && ! archypr-hw-external-monitors; then
rm -f "$TOGGLE"
fi
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
# archypr:summary=Detect whether the computer is a Microsoft Surface device.
[[ $(cat /sys/class/dmi/id/sys_vendor 2>/dev/null) == "Microsoft Corporation" ]] &&
archypr-hw-match "Surface"
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
# archypr:summary=Print the detected Hyprland touchpad or trackpad device name
device=$(hyprctl devices -j | jq -r '[.mice[] | .name | select(test("touchpad|trackpad"; "i"))] | first // empty')
[[ -n $device ]] && echo "$device"
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
# archypr:summary=Print the detected Hyprland touchscreen or tablet device name
device=$(hyprctl devices -j | jq -r '[.touch[]?.name, .tablets[]?.name] | first // empty')
[[ -n $device ]] && echo "$device"
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
# archypr:summary=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 .
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
# archypr:summary=Print the name of the currently focused Hyprland monitor.
hyprctl monitors -j | jq -r '.[] | select(.focused == true).name'
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
# archypr:summary=Return success if the focused Hyprland monitor is an Apple display.
hyprctl monitors -j | jq -e '.[] | select(.focused == true) | select(.make == "Apple Computer Inc" and (.model | test("StudioDisplay|ProDisplayXDR|Studio XDR")))' >/dev/null
+46
View File
@@ -0,0 +1,46 @@
#!/bin/bash
# archypr:summary=Enable, disable, toggle, or recover the internal laptop display
# archypr:args=<on|off|toggle|recover>
TOGGLE="internal-monitor-disable"
TOGGLE_FLAG="$HOME/.local/state/omarchy/toggles/hypr/$TOGGLE.conf"
MIRROR_TOGGLE="internal-monitor-mirror"
# Get internal monitor name dynamically
INTERNAL=$(hyprctl monitors -j | jq -r '.[] | select(.name | contains("eDP")).name' | head -n 1)
enable() {
if archypr-hyprland-toggle-enabled "$TOGGLE"; then
archypr-hyprland-toggle --disabled-notification "󰍹 Laptop display enabled" "$TOGGLE"
fi
}
disable() {
if ! archypr-hw-external-monitors; then
notify-send -u low "󰍹 Can't disable the only active display"
exit 1
fi
if archypr-hyprland-toggle-disabled "$TOGGLE" && archypr-hyprland-toggle-disabled "$MIRROR_TOGGLE"; then
echo "monitor=$INTERNAL,disable" >"$TOGGLE_FLAG"
notify-send -u low "󰍹 Laptop display disabled"
hyprctl reload
fi
}
recover() {
if ! archypr-hw-external-monitors && archypr-hyprland-toggle-enabled "$TOGGLE"; then
archypr-hyprland-toggle "$TOGGLE"
fi
}
case "$1" in
on) enable ;;
off) disable ;;
toggle) if archypr-hyprland-toggle-enabled "$TOGGLE"; then enable; else disable; fi ;;
recover) recover ;;
*)
echo "Usage: $(basename "$0") {on|off|toggle|recover}" >&2
exit 1
;;
esac
+58
View File
@@ -0,0 +1,58 @@
#!/bin/bash
# archypr:summary=Enable, disable, toggle, or recover mirroring the internal display to an external monitor
# archypr:args=<on|off|toggle|recover>
TOGGLE="internal-monitor-mirror"
TOGGLE_FLAG="$HOME/.local/state/omarchy/toggles/hypr/$TOGGLE.conf"
DISABLE_TOGGLE="internal-monitor-disable"
# Get names dynamically
INTERNAL=$(hyprctl monitors -j | jq -r '.[] | select(.name | contains("eDP")).name' | head -n 1)
# Get the first available external monitor
EXTERNAL=$(hyprctl monitors -j | jq -r '.[] | select(.name | contains("eDP") | not).name' | head -n 1)
enable() {
if [[ -z "$EXTERNAL" ]]; then
notify-send -u low "󰍹 No external monitors found for mirror"
exit 1
fi
if [[ -z "$INTERNAL" ]]; then
notify-send -u low "󰍹 No laptop monitor found to mirror"
exit 1
fi
if archypr-hyprland-toggle-enabled "$DISABLE_TOGGLE"; then
archypr-hyprland-toggle "$DISABLE_TOGGLE"
fi
if archypr-hyprland-toggle-disabled "$TOGGLE"; then
echo "monitor=$EXTERNAL, preferred, auto, 1, mirror, $INTERNAL" > "$TOGGLE_FLAG"
notify-send -u low "󰍹 Mirroring enabled ($EXTERNAL)"
hyprctl reload
fi
}
disable() {
if archypr-hyprland-toggle-enabled "$TOGGLE"; then
archypr-hyprland-toggle --disabled-notification "󰍹 Extended mode restored" "$TOGGLE"
fi
}
recover() {
if ! archypr-hw-external-monitors && archypr-hyprland-toggle-enabled "$TOGGLE"; then
archypr-hyprland-toggle "$TOGGLE"
fi
}
case "$1" in
on) enable ;;
off) disable ;;
toggle) if archypr-hyprland-toggle-enabled "$TOGGLE"; then disable; else enable; fi ;;
recover) recover ;;
*)
echo "Usage: $(basename "$0") {on|off|toggle|recover}" >&2
exit 1
;;
esac
+47
View File
@@ -0,0 +1,47 @@
#!/bin/bash
# archypr:summary=Cycle focused Hyprland monitor scaling through 1x, 1.25x, 1.6x, 2x, 3x, and 4x
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.25 → 1.6 → 2 → 3 → 4 → 1 (or reverse with --reverse)
SCALES=(1 1.25 1.6 2 3 4)
# Find the index of the scale closest to the current one (Hyprland may
# snap fractional scales to nearby values, so we can't match exactly)
CURRENT_IDX=$(awk -v s="$CURRENT_SCALE" -v list="${SCALES[*]}" 'BEGIN {
n = split(list, arr, " ")
best = 0; best_diff = 1e9
for (i = 1; i <= n; i++) {
d = s - arr[i]; if (d < 0) d = -d
if (d < best_diff) { best_diff = d; best = i - 1 }
}
print best
}')
if [[ "$1" == "--reverse" ]]; then
NEW_IDX=$(( (CURRENT_IDX - 1 + ${#SCALES[@]}) % ${#SCALES[@]} ))
else
NEW_IDX=$(( (CURRENT_IDX + 1) % ${#SCALES[@]} ))
fi
NEW_SCALE=${SCALES[$NEW_IDX]}
hyprctl keyword monitor "$ACTIVE_MONITOR,${WIDTH}x${HEIGHT}@${REFRESH_RATE},auto,$NEW_SCALE"
# Persist to monitors.conf if the user has a single generic catch-all line
# (ignoring disabled monitors), so the scale survives reboots.
MONITOR_CONF="$HOME/.config/hypr/monitors.conf"
if [[ -f $MONITOR_CONF ]]; then
mapfile -t ACTIVE_LINES < <(grep -E '^[[:space:]]*monitor=' "$MONITOR_CONF" | grep -vE 'disable[[:space:]]*$')
if [[ ${#ACTIVE_LINES[@]} -eq 1 ]] && [[ "${ACTIVE_LINES[0]}" =~ ^monitor=,preferred,auto, ]]; then
sed -i -E "s|^(monitor=,preferred,auto,).*|\\1${NEW_SCALE}|" "$MONITOR_CONF"
fi
fi
notify-send -u low "󰍹 Display scaling set to ${NEW_SCALE}x"
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
# archypr:summary=Watch Hyprland monitor events and recover monitor toggles when a monitor is removed
SOCKET="$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock"
socat -U - "UNIX-CONNECT:$SOCKET" | while read -r event; do
case "$event" in
monitorremoved\>\>*|monitorremovedv2\>\>*)
archypr-hyprland-monitor-internal recover
archypr-hyprland-monitor-internal-mirror recover
;;
esac
done
+32
View File
@@ -0,0 +1,32 @@
#!/bin/bash
# archypr:summary=Toggle permanent Hyprland flags by copying them into a directory that's sourced entirely.
# archypr:args=[--enabled-notification <text>] [--disabled-notification <text>] <flag-name>
ENABLED_NOTIFICATION=""
DISABLED_NOTIFICATION=""
while [[ $# -gt 1 ]]; do
case $1 in
--enabled-notification) ENABLED_NOTIFICATION="$2"; shift 2 ;;
--disabled-notification) DISABLED_NOTIFICATION="$2"; shift 2 ;;
*) break ;;
esac
done
FLAG_NAME="$1"
FLAG="$HOME/.local/state/omarchy/toggles/hypr/$FLAG_NAME.conf"
FLAG_SOURCE="$ARCHYPR_PATH/default/hypr/toggles/$FLAG_NAME.conf"
if [[ -f $FLAG ]]; then
rm $FLAG
[[ -n $DISABLED_NOTIFICATION ]] && notify-send -u low "$DISABLED_NOTIFICATION"
elif [[ -f $FLAG_SOURCE ]]; then
cp $FLAG_SOURCE $FLAG
[[ -n $ENABLED_NOTIFICATION ]] && notify-send -u low "$ENABLED_NOTIFICATION"
else
echo "Flag not found: $FLAG_NAME"
exit 1
fi
hyprctl reload
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
# archypr:summary=Check if a Hyprland toggle is currently disabled (missing).
# archypr:args=<flag-name>
[[ ! -f "$HOME/.local/state/omarchy/toggles/hypr/$1.conf" ]]
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
# archypr:summary=Check if a Hyprland toggle is currently enabled.
# archypr:args=<flag-name>
[[ -f "$HOME/.local/state/omarchy/toggles/hypr/$1.conf" ]]
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
# archypr:summary=Close all open windows
hyprctl clients -j | \
jq -r ".[].address" | \
xargs -I{} hyprctl dispatch closewindow address:{}
# Move to first workspace
hyprctl dispatch workspace 1
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
# archypr:summary=Toggles the window gaps globally between no gaps and the default.
archypr-yprland-toggle window-no-gaps
+34
View File
@@ -0,0 +1,34 @@
#!/bin/bash
# archypr:summary=Toggle to pop-out a tile to stay fixed on a display basis.
# archypr:args=[width height x y]
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
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
# archypr:summary=Toggle single-window square aspect ratio.
archypr-yprland-toggle \
--enabled-notification " Enable single-window square aspect ratio" \
--disabled-notification " Disable single-window square aspect ratio" \
single-window-aspect-ratio
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
# archypr:summary=Toggles transparency for the currently focused window.
hyprctl dispatch setprop "address:$(hyprctl activewindow -j | jq -r '.address')" opaque toggle
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
# archypr:summary=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"
+93
View File
@@ -0,0 +1,93 @@
#!/bin/bash
# archypr:summary=Install a supported browser
# archypr:args=<chrome|brave|brave-origin|edge|firefox|zen>
# archypr:examples=omarchy install browser firefox | omarchy install browser brave
setup_policy_directory() {
sudo mkdir -p "$1"
sudo chmod a+rw "$1"
}
announce_browser_installed() {
echo ""
echo "$1 browser installed. Make it the default via Setup > Defaults > Browser."
}
copy_chromium_flags() {
mkdir -p ~/.config
cp -f "${ARCHYPR_PATH:-$HOME/.local/share/omarchy}/config/chromium-flags.conf" "$1"
}
setup_firefox_preferences() {
local distribution_dir="$1"
setup_policy_directory "$distribution_dir"
sudo cp -f "$ARCHYPR_PATH/default/firefox/policies.json" "$distribution_dir/policies.json"
}
setup_firefox_wayland() {
mkdir -p ~/.config/environment.d
echo "MOZ_ENABLE_WAYLAND=1" > ~/.config/environment.d/archypr-irefox-wayland.conf
}
case $1 in
chrome)
echo "Installing Chrome..."
archypr-pkg-aur-add google-chrome || exit 1
setup_policy_directory /etc/opt/chrome/policies/managed
copy_chromium_flags ~/.config/chrome-flags.conf
archypr-theme-set-browser
announce_browser_installed "Chrome"
;;
edge)
echo "Installing Edge..."
archypr-pkg-aur-add microsoft-edge-stable-bin || exit 1
setup_policy_directory /etc/opt/edge/policies/managed
copy_chromium_flags ~/.config/microsoft-edge-stable-flags.conf
archypr-theme-set-browser
announce_browser_installed "Edge"
;;
brave)
echo "Installing Brave..."
archypr-pkg-aur-add brave-bin || exit 1
setup_policy_directory /etc/brave/policies/managed
copy_chromium_flags ~/.config/brave-flags.conf
archypr-theme-set-browser
announce_browser_installed "Brave"
;;
brave-origin)
echo "Installing Brave Origin..."
archypr-pkg-aur-add brave-origin-beta-bin || exit 1
setup_policy_directory /etc/brave/policies/managed
mkdir -p ~/.config
# FIXME: Use normal chromium flags when Brave Origin wrapper has been fixed
echo "--load-extension=~/.local/share/archypr/default/chromium/extensions/copy-url" > ~/.config/brave-origin-beta-flags.conf
archypr-theme-set-browser
announce_browser_installed "Brave Origin"
;;
firefox)
echo "Installing Firefox..."
archypr-pkg-add firefox || exit 1
setup_firefox_preferences /usr/lib/firefox/distribution
setup_firefox_wayland
announce_browser_installed "Firefox"
;;
zen)
echo "Installing Zen..."
archypr-pkg-aur-add zen-browser-bin || exit 1
setup_firefox_preferences /opt/zen-browser/distribution
setup_firefox_wayland
announce_browser_installed "Zen"
;;
*)
echo "Usage: archypr-install-browser <chrome|brave|brave-origin|edge|firefox|zen>"
exit 1
;;
esac
+16
View File
@@ -0,0 +1,16 @@
#!/bin/bash
# archypr:summary=Allow Chromium to sign in to Google accounts by adding the required OAuth credentials
if [[ -f ~/.config/chromium-flags.conf ]]; then
echo "Installing Chromium Google account support..."
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
+155
View File
@@ -0,0 +1,155 @@
#!/bin/bash
# archypr:summary=Install a supported development environment
# archypr:name=dev-env
# archypr:args=<ruby|node|bun|deno|go|laravel|symfony|php|python|elixir|phoenix|rust|java|zig|ocaml|dotnet|clojure|scala>
# archypr:examples=omarchy install dev-env ruby | omarchy install dev-env node
# archypr:requires-sudo=true
if [[ -z $1 ]]; then
echo "Usage: archypr-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() {
archypr-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"
archypr-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
archypr-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"
archypr-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
+28
View File
@@ -0,0 +1,28 @@
#!/bin/bash
# archypr:summary=Install one of the supported databases in a Docker container with the suitable development options.
# archypr:requires-sudo=true
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
echo "Installing $db..."
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
View File
@@ -0,0 +1,10 @@
#!/bin/bash
# archypr:summary=Install and start the Dropbox service. Must then be authenticated via the web.
echo "Installing all dependencies..."
archypr-kg-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."
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
# archypr:summary=Install and launch Geforce Now.
set -e
echo "Installing GeForce NOW..."
archypr-kg-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 archypr-launch-browser
+28
View File
@@ -0,0 +1,28 @@
#!/bin/bash
# archypr:summary=Install lib32 graphics drivers (Vulkan + NVIDIA) for any detected GPUs.
# archypr:requires-sudo=true
set -e
echo "Installing lib32 graphics drivers..."
PACKAGES=()
declare -A VULKAN_DRIVERS=(
[Intel]=lib32-vulkan-intel
[AMD]=lib32-vulkan-radeon
)
for vendor in "${!VULKAN_DRIVERS[@]}"; do
if lspci | grep -iE "(VGA|Display).*$vendor" >/dev/null; then
PACKAGES+=("${VULKAN_DRIVERS[$vendor]}")
fi
done
if archypr-hw-nvidia-gsp; then
PACKAGES+=(lib32-nvidia-utils)
elif archypr-hw-nvidia-without-gsp; then
PACKAGES+=(lib32-nvidia-580xx-utils)
fi
[[ ${#PACKAGES[@]} -gt 0 ]] && archypr-pkg-add "${PACKAGES[@]}"
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
# archypr:summary=Install Heroic Games Launcher (Epic, GOG, Amazon Prime Gaming) with graphics drivers.
# archypr:requires-sudo=true
set -e
echo "Installing Heroic Games Launcher..."
archypr-kg-add heroic-games-launcher-bin
archypr-nstall-gaming-gpu-lib32
setsid gtk-launch heroic >/dev/null 2>&1 &
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
# archypr:summary=Install Lutris with Wine + DXVK for running Windows games (Battle.net, EA, Ubisoft Connect, etc.)
# archypr:requires-sudo=true
set -e
echo "Installing Lutris..."
archypr-kg-add lutris umu-launcher wine-staging wine-mono wine-gecko winetricks python-protobuf
archypr-nstall-gaming-gpu-lib32
# Lutris ships with `#!/usr/bin/env python3`, which resolves to mise's Python and
# fails to import the lutris module. Pin the shebang to the system Python.
sudo sed -i '/env python3/ c\#!/bin/python3' /usr/bin/lutris
cat <<'EOF'
Lutris will open and auto-fetch its DXVK and VKD3D runtimes in the background
(watch the bottom status bar). Once that finishes, click the + to add or install games.
EOF
setsid lutris >/dev/null 2>&1 &
+11
View File
@@ -0,0 +1,11 @@
#!/bin/bash
# archypr:summary=Install Moonlight (NVIDIA GameStream / Sunshine client) for streaming games to this PC.
# archypr:requires-sudo=true
set -e
echo "Installing Moonlight..."
archypr-kg-add moonlight-qt
setsid gtk-launch com.moonlight_stream.Moonlight.desktop >/dev/null 2>&1 &
+85
View File
@@ -0,0 +1,85 @@
#!/bin/bash
# archypr:summary=Install RetroArch with the full libretro core set plus FBNeo and a ~/Games ROM directory.
set -e
echo "Installing RetroArch..."
archypr-kg-add \
retroarch \
retroarch-assets-glui retroarch-assets-ozone retroarch-assets-xmb \
libretro-beetle-pce libretro-beetle-pce-fast libretro-beetle-psx libretro-beetle-psx-hw libretro-beetle-supergrafx \
libretro-blastem \
libretro-bsnes libretro-bsnes-hd libretro-bsnes2014 \
libretro-core-info \
libretro-desmume libretro-dolphin libretro-flycast \
libretro-gambatte libretro-genesis-plus-gx \
libretro-kronos \
libretro-mame libretro-mame2016 libretro-melonds libretro-mesen libretro-mesen-s libretro-mgba libretro-mupen64plus-next \
libretro-nestopia \
libretro-overlays \
libretro-parallel-n64 libretro-picodrive libretro-play libretro-ppsspp \
libretro-sameboy libretro-scummvm libretro-shaders-slang libretro-snes9x \
libretro-yabause \
libretro-cap32-git libretro-fbneo-git libretro-uae-git \
libretro-vice-x128-git libretro-vice-x64-git libretro-vice-x64dtv-git libretro-vice-x64sc-git \
libretro-vice-xcbm2-git libretro-vice-xcbm5x0-git libretro-vice-xpet-git \
libretro-vice-xplus4-git libretro-vice-xscpu64-git libretro-vice-xvic-git \
libretro-database-git \
retroarch-joypad-autoconfig-git
# Set up ~/Games for BIOS files and ROMs
mkdir -p "$HOME/Games/bios" "$HOME/Games/roms"
CFG="$HOME/.config/retroarch/retroarch.cfg"
mkdir -p "$(dirname "$CFG")"
touch "$CFG"
set_cfg() {
local key=$1 value=$2
if grep -q "^$key = " "$CFG"; then
sed -i "s|^$key = .*|$key = \"$value\"|" "$CFG"
else
echo "$key = \"$value\"" >>"$CFG"
fi
}
set_cfg rgui_browser_directory "$HOME/Games/roms"
set_cfg system_directory "$HOME/Games/bios"
# Point at the cores and assets installed by pacman
set_cfg libretro_directory "/usr/lib/libretro"
set_cfg libretro_info_path "/usr/share/libretro/info"
set_cfg overlay_directory "/usr/share/libretro/overlays"
set_cfg osk_overlay_directory "/usr/share/libretro/overlays/keyboards"
set_cfg video_shader_dir "/usr/share/libretro/shaders/shaders_slang"
set_cfg joypad_autoconfig_dir "/usr/share/libretro/autoconfig"
# Point at the database, cheats, and cursors from libretro-database-git
set_cfg content_database_path "/usr/share/libretro/database/rdb"
set_cfg cheat_database_path "/usr/share/libretro/database/cht"
set_cfg cursor_directory "/usr/share/libretro/database/cursors"
# Vulkan is required for slang shaders and unlocks hardware renderers in beetle-psx-hw, parallel-n64, dolphin
set_cfg video_driver "vulkan"
# XMB is the classic PS3-style menu (vs. ozone/rgui/glui)
set_cfg menu_driver "xmb"
# Default to crt-royale shader for that classic CRT look. The global preset is
# auto-loaded by RetroArch when auto_shaders_enable is true and no per-core/per-game
# preset takes precedence — setting video_shader alone in retroarch.cfg is not enough.
set_cfg video_shader_enable "true"
set_cfg auto_shaders_enable "true"
mkdir -p ~/.config/retroarch/config
echo '#reference "/usr/share/libretro/shaders/shaders_slang/crt/crt-royale.slangp"' \
> ~/.config/retroarch/config/global.slangp
# Hide Images and Video tabs in the main menu sidebar
set_cfg content_show_images "false"
set_cfg content_show_video "false"
echo ""
echo "Put your roms and bios files in ~/Games. Then start RetroArch from the app launcher (Super + Space)."
setsid nautilus "$HOME/Games" >/dev/null 2>&1 &
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
# archypr:summary=Install Steam and graphics drivers selected for this system
# archypr:requires-sudo=true
set -e
echo "Installing Steam..."
archypr-kg-add steam
archypr-nstall-gaming-gpu-lib32
echo ""
echo "Steam will start automatically now. This might take a while..."
setsid gtk-launch steam >/dev/null 2>&1 &
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
# archypr:summary=Install Xbox Cloud Gaming as a web app and launch it.
set -e
echo "Installing Xbox Cloud Gaming..."
archypr-ebapp-install "Xbox Cloud Gaming" "https://www.xbox.com/en-US/play" "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/xbox.png"
setsid archypr-launch-webapp "https://www.xbox.com/en-US/play" >/dev/null 2>&1 &
+37
View File
@@ -0,0 +1,37 @@
#!/bin/bash
# archypr:summary=Install support for using Xbox controllers with Steam/RetroArch/etc.
# archypr:requires-sudo=true
set -e
echo "Installing Xbox controller Bluetooth support..."
# Install xpadneo to ensure controllers work out of the box
archypr-kg-add linux-headers 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
# Ensure user is in the input group (controllers need it)
needs_reboot=false
if ! id -nG "$USER" | grep -qw input; then
sudo usermod -aG input "$USER"
needs_reboot=true
fi
# Swap drivers in the running kernel so a reboot isn't needed otherwise
if lsmod | grep -q '^xpad '; then
sudo modprobe -r xpad 2>/dev/null || needs_reboot=true
fi
if $needs_reboot; then
gum confirm "Reboot needed to finish setup. Reboot now?" && sudo reboot now
exit 0
fi
sudo modprobe hid_xpadneo
echo ""
echo "Now you can pair your Xbox controller with Bluetooth using Super + Ctrl + B."
+28
View File
@@ -0,0 +1,28 @@
#!/bin/bash
# archypr:summary=Install Helix and configure it to use the current Omarchy theme
echo "Installing Helix..."
archypr-kg-add helix
mkdir -p ~/.config/helix/themes
# Symlink the rendered Omarchy theme so Helix tracks the active theme
ln -sf ~/.config/archypr/current/theme/helix.toml ~/.config/helix/themes/omarchy.toml
# Only seed a config.toml if the user does not already have one
if [[ ! -f ~/.config/helix/config.toml ]]; then
cat >~/.config/helix/config.toml <<'EOF'
theme = "omarchy"
EOF
fi
# Ensure the symlink target exists for users whose current theme predates this template
if [[ ! -e ~/.config/archypr/current/theme/helix.toml ]]; then
archypr-theme-refresh
fi
# Arch-based distros ship Helix as 'helix' rather than the upstream 'hx'.
if ! grep -q '^alias hx="helix"' ~/.bashrc 2>/dev/null; then
echo 'alias hx="helix"' >>~/.bashrc
fi
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
# archypr:summary=Install the NordVPN service with optional GUI.
# archypr:requires-sudo=true
echo "Installing NordVPN..."
archypr-kg-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?" && archypr-system-reboot
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
# archypr:summary=Install the ONCE service, enable its background service, and launch the TUI.
# archypr:requires-sudo=true
echo "Installing ONCE..."
archypr-kg-add once-bin
echo "Enabling ONCE background service..."
sudo systemctl enable --now once-background.service
echo -e "\nLaunching ONCE..."
once
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
# archypr:summary=Install the Tailscale mesh VPN service and a web app for the Tailscale Admin Console.
# archypr:requires-sudo=true
echo -e "\nInstalling Tailscale..."
archypr-kg-add tailscale
echo -e "\nStarting Tailscale..."
sudo systemctl enable --now tailscaled.service
sudo tailscale up --accept-routes
archypr-ebapp-install "Tailscale" "https://login.tailscale.com/admin/machines" https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/tailscale-light.png

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