commit 835a9aa3b5cf08f6f11cb33369b02fe522b184ce Author: Ole Valente Date: Thu Jun 11 19:06:22 2026 +0200 Initial import: Lua config, trimmed omarchy scripts, theme system diff --git a/.machine-branch b/.machine-branch new file mode 100644 index 0000000..915ad17 --- /dev/null +++ b/.machine-branch @@ -0,0 +1 @@ +omac diff --git a/backup.sh b/backup.sh new file mode 100755 index 0000000..1730697 --- /dev/null +++ b/backup.sh @@ -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 diff --git a/clone-omarchy.sh b/clone-omarchy.sh new file mode 100644 index 0000000..e590a1e --- /dev/null +++ b/clone-omarchy.sh @@ -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" diff --git a/copy-templates.sh b/copy-templates.sh new file mode 100644 index 0000000..b494345 --- /dev/null +++ b/copy-templates.sh @@ -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" diff --git a/install-scripts.sh b/install-scripts.sh new file mode 100644 index 0000000..e8688b6 --- /dev/null +++ b/install-scripts.sh @@ -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" diff --git a/restore.sh b/restore.sh new file mode 100755 index 0000000..60d512f --- /dev/null +++ b/restore.sh @@ -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/" diff --git a/scripts/ac-present b/scripts/ac-present new file mode 100755 index 0000000..265d83c --- /dev/null +++ b/scripts/ac-present @@ -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 diff --git a/scripts/audio-input-mute b/scripts/audio-input-mute new file mode 100755 index 0000000..1e57029 --- /dev/null +++ b/scripts/audio-input-mute @@ -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" diff --git a/scripts/audio-output-switch b/scripts/audio-output-switch new file mode 100755 index 0000000..0e7552d --- /dev/null +++ b/scripts/audio-output-switch @@ -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" diff --git a/scripts/battery-capacity b/scripts/battery-capacity new file mode 100755 index 0000000..a391563 --- /dev/null +++ b/scripts/battery-capacity @@ -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 +}' diff --git a/scripts/battery-monitor b/scripts/battery-monitor new file mode 100755 index 0000000..c7cd4cd --- /dev/null +++ b/scripts/battery-monitor @@ -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 diff --git a/scripts/battery-present b/scripts/battery-present new file mode 100755 index 0000000..e30033b --- /dev/null +++ b/scripts/battery-present @@ -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 diff --git a/scripts/battery-remaining b/scripts/battery-remaining new file mode 100755 index 0000000..21b5a90 --- /dev/null +++ b/scripts/battery-remaining @@ -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 +}' diff --git a/scripts/battery-remaining-time b/scripts/battery-remaining-time new file mode 100755 index 0000000..3aef489 --- /dev/null +++ b/scripts/battery-remaining-time @@ -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 +}' diff --git a/scripts/battery-status b/scripts/battery-status new file mode 100755 index 0000000..8092a69 --- /dev/null +++ b/scripts/battery-status @@ -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 diff --git a/scripts/branch-set b/scripts/branch-set new file mode 100755 index 0000000..c1da03e --- /dev/null +++ b/scripts/branch-set @@ -0,0 +1,18 @@ +#!/bin/bash + +# archypr:summary=Set the branch for Omarchy's git repository. +# archypr:args= + +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 diff --git a/scripts/branding-about b/scripts/branding-about new file mode 100755 index 0000000..6b55cfe --- /dev/null +++ b/scripts/branding-about @@ -0,0 +1,28 @@ +#!/bin/bash + +# archypr:summary=Edit, set, or reset About branding +# archypr:group=branding +# archypr:name=about +# archypr:args= +# 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 " >&2 + exit 1 + ;; +esac diff --git a/scripts/branding-screensaver b/scripts/branding-screensaver new file mode 100755 index 0000000..664706d --- /dev/null +++ b/scripts/branding-screensaver @@ -0,0 +1,28 @@ +#!/bin/bash + +# archypr:summary=Edit, set, or reset screensaver branding +# archypr:group=branding +# archypr:name=screensaver +# archypr:args= +# 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 " >&2 + exit 1 + ;; +esac diff --git a/scripts/brightness-display b/scripts/brightness-display new file mode 100755 index 0000000..fd2bf0c --- /dev/null +++ b/scripts/brightness-display @@ -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 '%')" diff --git a/scripts/brightness-display-apple b/scripts/brightness-display-apple new file mode 100755 index 0000000..64ea969 --- /dev/null +++ b/scripts/brightness-display-apple @@ -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 diff --git a/scripts/brightness-keyboard b/scripts/brightness-keyboard new file mode 100755 index 0000000..a780502 --- /dev/null +++ b/scripts/brightness-keyboard @@ -0,0 +1,55 @@ +#!/bin/bash + +# archypr:summary=Adjust keyboard backlight brightness using available steps. +# archypr:args= + +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" diff --git a/scripts/brightness-keyboard-mute b/scripts/brightness-keyboard-mute new file mode 100755 index 0000000..928ab56 --- /dev/null +++ b/scripts/brightness-keyboard-mute @@ -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= + +if [[ -e /sys/class/leds/platform::micmute/brightness ]]; then + case "$1" in + on) value=1 ;; + off) value=0 ;; + *) echo "Usage: $(basename "$0") " >&2; exit 1 ;; + esac + + brightnessctl --device="platform::micmute" set "$value" >/dev/null 2>&1 || true +fi diff --git a/scripts/capture-screenrecording b/scripts/capture-screenrecording new file mode 100755 index 0000000..60f0594 --- /dev/null +++ b/scripts/capture-screenrecording @@ -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=] [--resolution=] [--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 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 diff --git a/scripts/capture-screenshot b/scripts/capture-screenshot new file mode 100755 index 0000000..1d9676b --- /dev/null +++ b/scripts/capture-screenshot @@ -0,0 +1,147 @@ +#!/bin/bash + +# archypr:summary=Take a screenshot +# archypr:group=capture +# archypr:args=[smart|region|windows|fullscreen] [slurp|copy|save] [--editor=] +# 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 diff --git a/scripts/capture-text-extraction b/scripts/capture-text-extraction new file mode 100755 index 0000000..5a7afea --- /dev/null +++ b/scripts/capture-text-extraction @@ -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" diff --git a/scripts/channel-set b/scripts/channel-set new file mode 100755 index 0000000..b27cc66 --- /dev/null +++ b/scripts/channel-set @@ -0,0 +1,22 @@ +#!/bin/bash + +# archypr:summary=Set the Omarchy channel, which dictates what git branch and package repository is used. +# archypr:args= +# 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 diff --git a/scripts/cmd-missing b/scripts/cmd-missing new file mode 100755 index 0000000..b36e38a --- /dev/null +++ b/scripts/cmd-missing @@ -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 diff --git a/scripts/cmd-present b/scripts/cmd-present new file mode 100755 index 0000000..f87f978 --- /dev/null +++ b/scripts/cmd-present @@ -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 diff --git a/scripts/cmd-terminal-cwd b/scripts/cmd-terminal-cwd new file mode 100755 index 0000000..aa28f67 --- /dev/null +++ b/scripts/cmd-terminal-cwd @@ -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 diff --git a/scripts/config-direct-boot b/scripts/config-direct-boot new file mode 100755 index 0000000..fccc787 --- /dev/null +++ b/scripts/config-direct-boot @@ -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 diff --git a/scripts/debug b/scripts/debug new file mode 100755 index 0000000..9c29bac --- /dev/null +++ b/scripts/debug @@ -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" </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 diff --git a/scripts/default-browser b/scripts/default-browser new file mode 100755 index 0000000..07df9b8 --- /dev/null +++ b/scripts/default-browser @@ -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 " + 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" diff --git a/scripts/default-editor b/scripts/default-editor new file mode 100755 index 0000000..5db9c84 --- /dev/null +++ b/scripts/default-editor @@ -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 " + 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" diff --git a/scripts/default-terminal b/scripts/default-terminal new file mode 100755 index 0000000..6426fe2 --- /dev/null +++ b/scripts/default-terminal @@ -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 " + exit 1 + ;; +esac + +cat >~/.config/xdg-terminals.list <] +# 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=] + +Measure response times for common Omarchy CLI surfaces. + +Options: + --repeat= 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 diff --git a/scripts/dev-bin-metadata b/scripts/dev-bin-metadata new file mode 100755 index 0000000..5a55114 --- /dev/null +++ b/scripts/dev-bin-metadata @@ -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 ", + 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= + +Inferred defaults: + group first filename segment after archypr- + name remaining filename segments, with dashes converted to spaces + route omarchy + binary filename + requires-sudo false + hidden false + +Optional fields: + # archypr:group= route override only + # archypr:name= route override only; may be empty + # archypr:args= only if the command accepts args + # archypr:examples= | pipe-separated examples + # archypr:aliases= | 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=] + # 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 diff --git a/scripts/drive-info b/scripts/drive-info new file mode 100755 index 0000000..b0e5439 --- /dev/null +++ b/scripts/drive-info @@ -0,0 +1,50 @@ +#!/bin/bash + +# archypr:summary=Print drive information such as size, model, and mount details +# archypr:args= + +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" diff --git a/scripts/drive-password b/scripts/drive-password new file mode 100755 index 0000000..dbb0577 --- /dev/null +++ b/scripts/drive-password @@ -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 diff --git a/scripts/drive-select b/scripts/drive-select new file mode 100755 index 0000000..55e066b --- /dev/null +++ b/scripts/drive-select @@ -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}' diff --git a/scripts/first-run b/scripts/first-run new file mode 100755 index 0000000..6e970ab --- /dev/null +++ b/scripts/first-run @@ -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 diff --git a/scripts/font-current b/scripts/font-current new file mode 100755 index 0000000..c82441e --- /dev/null +++ b/scripts/font-current @@ -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 diff --git a/scripts/font-list b/scripts/font-list new file mode 100755 index 0000000..86a12cd --- /dev/null +++ b/scripts/font-list @@ -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 diff --git a/scripts/font-set b/scripts/font-set new file mode 100755 index 0000000..1eb9d9c --- /dev/null +++ b/scripts/font-set @@ -0,0 +1,55 @@ +#!/bin/bash + +# archypr:summary=Set the system monospace font +# archypr:args= +# 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 " +fi diff --git a/scripts/hibernation-available b/scripts/hibernation-available new file mode 100755 index 0000000..1be1d6e --- /dev/null +++ b/scripts/hibernation-available @@ -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 diff --git a/scripts/hibernation-remove b/scripts/hibernation-remove new file mode 100755 index 0000000..22cd5d9 --- /dev/null +++ b/scripts/hibernation-remove @@ -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" diff --git a/scripts/hibernation-setup b/scripts/hibernation-setup new file mode 100755 index 0000000..11bce9b --- /dev/null +++ b/scripts/hibernation-setup @@ -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 diff --git a/scripts/hook b/scripts/hook new file mode 100755 index 0000000..2955605 --- /dev/null +++ b/scripts/hook @@ -0,0 +1,28 @@ +#!/bin/bash + +# archypr:summary=Run a named hook from ~/.config/archypr/hooks/ and ~/.config/archypr/hooks/.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 diff --git a/scripts/hook-install b/scripts/hook-install new file mode 100755 index 0000000..4dc36b2 --- /dev/null +++ b/scripts/hook-install @@ -0,0 +1,31 @@ +#!/bin/bash + +# archypr:summary=Install a hook into ~/.config/archypr/hooks/.d/ +# archypr:group=hook +# archypr:name=install +# archypr:args= +# archypr:examples=omarchy hook install post-update ~/my-hook + +set -e + +if (( $# != 2 )); then + echo "Usage: archypr-hook-install " + 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" diff --git a/scripts/hw-asus-expertbook-b9406 b/scripts/hw-asus-expertbook-b9406 new file mode 100755 index 0000000..b3c3aa2 --- /dev/null +++ b/scripts/hw-asus-expertbook-b9406 @@ -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 diff --git a/scripts/hw-asus-rog b/scripts/hw-asus-rog new file mode 100755 index 0000000..26dbead --- /dev/null +++ b/scripts/hw-asus-rog @@ -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 diff --git a/scripts/hw-asus-zenbook-ux5406aa b/scripts/hw-asus-zenbook-ux5406aa new file mode 100755 index 0000000..384393c --- /dev/null +++ b/scripts/hw-asus-zenbook-ux5406aa @@ -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 diff --git a/scripts/hw-dell-xps-haptic-touchpad b/scripts/hw-dell-xps-haptic-touchpad new file mode 100755 index 0000000..849a630 --- /dev/null +++ b/scripts/hw-dell-xps-haptic-touchpad @@ -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 ]] diff --git a/scripts/hw-dell-xps-oled b/scripts/hw-dell-xps-oled new file mode 100755 index 0000000..6409f80 --- /dev/null +++ b/scripts/hw-dell-xps-oled @@ -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" diff --git a/scripts/hw-external-monitors b/scripts/hw-external-monitors new file mode 100755 index 0000000..6ede654 --- /dev/null +++ b/scripts/hw-external-monitors @@ -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 diff --git a/scripts/hw-framework16 b/scripts/hw-framework16 new file mode 100755 index 0000000..25be020 --- /dev/null +++ b/scripts/hw-framework16 @@ -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" diff --git a/scripts/hw-hybrid-gpu b/scripts/hw-hybrid-gpu new file mode 100755 index 0000000..1cd8f44 --- /dev/null +++ b/scripts/hw-hybrid-gpu @@ -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 diff --git a/scripts/hw-intel b/scripts/hw-intel new file mode 100755 index 0000000..a52c330 --- /dev/null +++ b/scripts/hw-intel @@ -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" ]] diff --git a/scripts/hw-intel-ptl b/scripts/hw-intel-ptl new file mode 100755 index 0000000..8d7c697 --- /dev/null +++ b/scripts/hw-intel-ptl @@ -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' diff --git a/scripts/hw-match b/scripts/hw-match new file mode 100755 index 0000000..20abb83 --- /dev/null +++ b/scripts/hw-match @@ -0,0 +1,7 @@ +#!/bin/bash + +# archypr:summary=Match against the computer's DMI product name or product family (case-insensitive). +# archypr:args= + +grep -qi "$1" /sys/class/dmi/id/product_name 2>/dev/null || +grep -qi "$1" /sys/class/dmi/id/product_family 2>/dev/null diff --git a/scripts/hw-nvidia-gsp b/scripts/hw-nvidia-gsp new file mode 100755 index 0000000..9575461 --- /dev/null +++ b/scripts/hw-nvidia-gsp @@ -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]+" diff --git a/scripts/hw-nvidia-without-gsp b/scripts/hw-nvidia-without-gsp new file mode 100755 index 0000000..9d1807e --- /dev/null +++ b/scripts/hw-nvidia-without-gsp @@ -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" diff --git a/scripts/hw-recover-internal-monitor b/scripts/hw-recover-internal-monitor new file mode 100755 index 0000000..790bbf9 --- /dev/null +++ b/scripts/hw-recover-internal-monitor @@ -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 diff --git a/scripts/hw-surface b/scripts/hw-surface new file mode 100755 index 0000000..ae0a2a4 --- /dev/null +++ b/scripts/hw-surface @@ -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" diff --git a/scripts/hw-touchpad b/scripts/hw-touchpad new file mode 100755 index 0000000..79333db --- /dev/null +++ b/scripts/hw-touchpad @@ -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" diff --git a/scripts/hw-touchscreen b/scripts/hw-touchscreen new file mode 100755 index 0000000..cda3432 --- /dev/null +++ b/scripts/hw-touchscreen @@ -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" diff --git a/scripts/hw-vulkan b/scripts/hw-vulkan new file mode 100755 index 0000000..5e96f60 --- /dev/null +++ b/scripts/hw-vulkan @@ -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 . diff --git a/scripts/hyprland-monitor-focused b/scripts/hyprland-monitor-focused new file mode 100755 index 0000000..fc954d6 --- /dev/null +++ b/scripts/hyprland-monitor-focused @@ -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' diff --git a/scripts/hyprland-monitor-focused-apple b/scripts/hyprland-monitor-focused-apple new file mode 100755 index 0000000..d37fc31 --- /dev/null +++ b/scripts/hyprland-monitor-focused-apple @@ -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 diff --git a/scripts/hyprland-monitor-internal b/scripts/hyprland-monitor-internal new file mode 100755 index 0000000..2ac8ca0 --- /dev/null +++ b/scripts/hyprland-monitor-internal @@ -0,0 +1,46 @@ +#!/bin/bash + +# archypr:summary=Enable, disable, toggle, or recover the internal laptop display +# archypr:args= + +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 diff --git a/scripts/hyprland-monitor-internal-mirror b/scripts/hyprland-monitor-internal-mirror new file mode 100755 index 0000000..8c87ac9 --- /dev/null +++ b/scripts/hyprland-monitor-internal-mirror @@ -0,0 +1,58 @@ +#!/bin/bash + +# archypr:summary=Enable, disable, toggle, or recover mirroring the internal display to an external monitor +# archypr:args= + +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 diff --git a/scripts/hyprland-monitor-scaling-cycle b/scripts/hyprland-monitor-scaling-cycle new file mode 100755 index 0000000..59df5b9 --- /dev/null +++ b/scripts/hyprland-monitor-scaling-cycle @@ -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" diff --git a/scripts/hyprland-monitor-watch b/scripts/hyprland-monitor-watch new file mode 100755 index 0000000..f6957fb --- /dev/null +++ b/scripts/hyprland-monitor-watch @@ -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 diff --git a/scripts/hyprland-toggle b/scripts/hyprland-toggle new file mode 100755 index 0000000..73b4dde --- /dev/null +++ b/scripts/hyprland-toggle @@ -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 ] [--disabled-notification ] + +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 diff --git a/scripts/hyprland-toggle-disabled b/scripts/hyprland-toggle-disabled new file mode 100755 index 0000000..eaaceba --- /dev/null +++ b/scripts/hyprland-toggle-disabled @@ -0,0 +1,6 @@ +#!/bin/bash + +# archypr:summary=Check if a Hyprland toggle is currently disabled (missing). +# archypr:args= + +[[ ! -f "$HOME/.local/state/omarchy/toggles/hypr/$1.conf" ]] diff --git a/scripts/hyprland-toggle-enabled b/scripts/hyprland-toggle-enabled new file mode 100755 index 0000000..b239052 --- /dev/null +++ b/scripts/hyprland-toggle-enabled @@ -0,0 +1,6 @@ +#!/bin/bash + +# archypr:summary=Check if a Hyprland toggle is currently enabled. +# archypr:args= + +[[ -f "$HOME/.local/state/omarchy/toggles/hypr/$1.conf" ]] diff --git a/scripts/hyprland-window-close-all b/scripts/hyprland-window-close-all new file mode 100755 index 0000000..68f1571 --- /dev/null +++ b/scripts/hyprland-window-close-all @@ -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 diff --git a/scripts/hyprland-window-gaps-toggle b/scripts/hyprland-window-gaps-toggle new file mode 100755 index 0000000..1a5c511 --- /dev/null +++ b/scripts/hyprland-window-gaps-toggle @@ -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 diff --git a/scripts/hyprland-window-pop b/scripts/hyprland-window-pop new file mode 100755 index 0000000..9173a77 --- /dev/null +++ b/scripts/hyprland-window-pop @@ -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 diff --git a/scripts/hyprland-window-single-square-aspect-toggle b/scripts/hyprland-window-single-square-aspect-toggle new file mode 100755 index 0000000..4021c9e --- /dev/null +++ b/scripts/hyprland-window-single-square-aspect-toggle @@ -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 diff --git a/scripts/hyprland-window-transparency-toggle b/scripts/hyprland-window-transparency-toggle new file mode 100755 index 0000000..0c288f1 --- /dev/null +++ b/scripts/hyprland-window-transparency-toggle @@ -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 diff --git a/scripts/hyprland-workspace-layout-toggle b/scripts/hyprland-workspace-layout-toggle new file mode 100755 index 0000000..413b934 --- /dev/null +++ b/scripts/hyprland-workspace-layout-toggle @@ -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" diff --git a/scripts/install-browser b/scripts/install-browser new file mode 100755 index 0000000..1271ac6 --- /dev/null +++ b/scripts/install-browser @@ -0,0 +1,93 @@ +#!/bin/bash + +# archypr:summary=Install a supported browser +# archypr:args= +# 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 " + exit 1 + ;; +esac diff --git a/scripts/install-chromium-google-account b/scripts/install-chromium-google-account new file mode 100755 index 0000000..f3fd8df --- /dev/null +++ b/scripts/install-chromium-google-account @@ -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 diff --git a/scripts/install-dev-env b/scripts/install-dev-env new file mode 100755 index 0000000..0e83c50 --- /dev/null +++ b/scripts/install-dev-env @@ -0,0 +1,155 @@ +#!/bin/bash + +# archypr:summary=Install a supported development environment +# archypr:name=dev-env +# archypr:args= +# 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 " >&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 diff --git a/scripts/install-docker-dbs b/scripts/install-docker-dbs new file mode 100755 index 0000000..f5b76bc --- /dev/null +++ b/scripts/install-docker-dbs @@ -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 diff --git a/scripts/install-dropbox b/scripts/install-dropbox new file mode 100755 index 0000000..99165ff --- /dev/null +++ b/scripts/install-dropbox @@ -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." diff --git a/scripts/install-gaming-geforce-now b/scripts/install-gaming-geforce-now new file mode 100755 index 0000000..1eed443 --- /dev/null +++ b/scripts/install-gaming-geforce-now @@ -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 diff --git a/scripts/install-gaming-gpu-lib32 b/scripts/install-gaming-gpu-lib32 new file mode 100755 index 0000000..558d224 --- /dev/null +++ b/scripts/install-gaming-gpu-lib32 @@ -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[@]}" diff --git a/scripts/install-gaming-heroic b/scripts/install-gaming-heroic new file mode 100755 index 0000000..6ef52fe --- /dev/null +++ b/scripts/install-gaming-heroic @@ -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 & diff --git a/scripts/install-gaming-lutris b/scripts/install-gaming-lutris new file mode 100755 index 0000000..e4d8ba5 --- /dev/null +++ b/scripts/install-gaming-lutris @@ -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 & diff --git a/scripts/install-gaming-moonlight b/scripts/install-gaming-moonlight new file mode 100755 index 0000000..1e48a53 --- /dev/null +++ b/scripts/install-gaming-moonlight @@ -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 & diff --git a/scripts/install-gaming-retroarch b/scripts/install-gaming-retroarch new file mode 100755 index 0000000..11979ae --- /dev/null +++ b/scripts/install-gaming-retroarch @@ -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 & diff --git a/scripts/install-gaming-steam b/scripts/install-gaming-steam new file mode 100755 index 0000000..b2ee882 --- /dev/null +++ b/scripts/install-gaming-steam @@ -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 & diff --git a/scripts/install-gaming-xbox-cloud b/scripts/install-gaming-xbox-cloud new file mode 100755 index 0000000..7f4fe15 --- /dev/null +++ b/scripts/install-gaming-xbox-cloud @@ -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 & diff --git a/scripts/install-gaming-xbox-controllers b/scripts/install-gaming-xbox-controllers new file mode 100755 index 0000000..302ad10 --- /dev/null +++ b/scripts/install-gaming-xbox-controllers @@ -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." diff --git a/scripts/install-helix b/scripts/install-helix new file mode 100755 index 0000000..d10e20b --- /dev/null +++ b/scripts/install-helix @@ -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 diff --git a/scripts/install-nordvpn b/scripts/install-nordvpn new file mode 100755 index 0000000..72cc90b --- /dev/null +++ b/scripts/install-nordvpn @@ -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 diff --git a/scripts/install-once b/scripts/install-once new file mode 100755 index 0000000..c43c2a3 --- /dev/null +++ b/scripts/install-once @@ -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 diff --git a/scripts/install-tailscale b/scripts/install-tailscale new file mode 100755 index 0000000..4fa8e8b --- /dev/null +++ b/scripts/install-tailscale @@ -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 diff --git a/scripts/install-terminal b/scripts/install-terminal new file mode 100755 index 0000000..d2de3c3 --- /dev/null +++ b/scripts/install-terminal @@ -0,0 +1,52 @@ +#!/bin/bash + +# archypr:summary=Install one of the approved terminals and set it as the default for Omarchy (Super + Return etc). +# archypr:args= +# archypr:requires-sudo=true + +if (($# == 0)); then + echo "Usage: archypr-install-terminal [alacritty|foot|ghostty|kitty]" + exit 1 +fi + +package="$1" + +# Map package name to desktop entry ID +case "$package" in +alacritty) desktop_id="Alacritty.desktop" ;; +foot) desktop_id="foot.desktop" ;; +ghostty) desktop_id="com.mitchellh.ghostty.desktop" ;; +kitty) desktop_id="kitty.desktop" ;; +*) + echo "Unknown terminal: $package" + exit 1 + ;; +esac + +echo "Installing $package..." + +# Install package +if archypr-pkg-add $package; then + # Copy custom desktop entries with X-TerminalArg* keys + if [[ $package == "alacritty" ]]; then + mkdir -p ~/.local/share/applications + cp "$ARCHYPR_PATH/applications/$desktop_id" ~/.local/share/applications/ + elif [[ $package == "foot" ]]; then + mkdir -p ~/.local/share/applications + cp "$ARCHYPR_PATH/default/foot/$desktop_id" ~/.local/share/applications/ + fi + + # Copy default config for optional terminals when missing + if [[ ! -e ~/.config/$package ]]; then + cp -Rpf "$ARCHYPR_PATH/config/$package" ~/.config/ + fi + + # Update xdg-terminals.list to prioritize the proper terminal + cat >~/.config/xdg-terminals.list < ~/.vscode/argv.json << 'EOF' +// This configuration file allows you to pass permanent command line arguments to VS Code. +// Only a subset of arguments is currently supported to reduce the likelihood of breaking +// the installation. +// +// PLEASE DO NOT CHANGE WITHOUT UNDERSTANDING THE IMPACT +// +// NOTE: Changing this file requires a restart of VS Code. +{ + "password-store":"gnome-libsecret" +} +EOF + +# Ensure VSC's own auto-update feature is turned off +printf '{\n "update.mode": "none"\n}\n' > ~/.config/Code/User/settings.json + +# Apply Omarchy theme to VSCode +archypr-heme-set-vscode + +setsid gtk-launch code diff --git a/scripts/install-zed b/scripts/install-zed new file mode 100755 index 0000000..91129ef --- /dev/null +++ b/scripts/install-zed @@ -0,0 +1,11 @@ +#!/bin/bash + +# archypr:summary=Install Zed Editor and configure it with the current Omarchy theme + +echo "Installing Zed Editor..." +archypr-kg-add zed omazed + +# Apply Omarchy theme to Zed +omazed setup + +setsid gtk-launch dev.zed.Zed diff --git a/scripts/launch-about b/scripts/launch-about new file mode 100755 index 0000000..e5b61d7 --- /dev/null +++ b/scripts/launch-about @@ -0,0 +1,5 @@ +#!/bin/bash + +# archypr:summary=Launch the fastfetch TUI that gives information about the current system. + +exec archypr-launch-or-focus-tui "bash -c 'fastfetch; read -n 1 -s'" diff --git a/scripts/launch-audio b/scripts/launch-audio new file mode 100755 index 0000000..54d7b03 --- /dev/null +++ b/scripts/launch-audio @@ -0,0 +1,5 @@ +#!/bin/bash + +# archypr:summary=Launch the Omarchy audio controls TUI (provided by wiremix). + +archypr-aunch-or-focus-tui wiremix diff --git a/scripts/launch-bluetooth b/scripts/launch-bluetooth new file mode 100755 index 0000000..5f2f0bc --- /dev/null +++ b/scripts/launch-bluetooth @@ -0,0 +1,6 @@ +#!/bin/bash + +# archypr:summary=Launch the Omarchy bluetooth controls TUI (provided by bluetui). + +rfkill unblock bluetooth +exec archypr-launch-or-focus-tui bluetui diff --git a/scripts/launch-browser b/scripts/launch-browser new file mode 100755 index 0000000..dbbdd77 --- /dev/null +++ b/scripts/launch-browser @@ -0,0 +1,17 @@ +#!/bin/bash + +# archypr:summary=Launch the default browser as determined by xdg-settings. +# archypr:args=[url] + +default_browser=$(xdg-settings get default-web-browser) +browser_exec=$(sed -n 's/^Exec=\([^ ]*\).*/\1/p' {~/.local,~/.nix-profile,/usr}/share/applications/$default_browser 2>/dev/null | head -1) + +if $browser_exec --help | grep -q MOZ_LOG; then + private_flag="--private-window" +elif [[ $browser_exec =~ edge ]]; then + private_flag="--inprivate" +else + private_flag="--incognito" +fi + +exec setsid uwsm-app -- "$browser_exec" "${@/--private/$private_flag}" diff --git a/scripts/launch-editor b/scripts/launch-editor new file mode 100755 index 0000000..32f4bcd --- /dev/null +++ b/scripts/launch-editor @@ -0,0 +1,15 @@ +#!/bin/bash + +# archypr:summary=Launch the default editor as determined by $EDITOR (set via ~/.config/uwsm/default) (or nvim if missing). +# archypr:args= + +archypr-md-present "$EDITOR" || EDITOR=nvim + +case "$EDITOR" in +nvim | vim | nano | micro | hx | helix | fresh) + exec archypr-launch-tui "$EDITOR" "$@" + ;; +*) + exec setsid uwsm-app -- "$EDITOR" "$@" + ;; +esac diff --git a/scripts/launch-floating-terminal-with-presentation b/scripts/launch-floating-terminal-with-presentation new file mode 100755 index 0000000..37b3e45 --- /dev/null +++ b/scripts/launch-floating-terminal-with-presentation @@ -0,0 +1,7 @@ +#!/bin/bash + +# archypr:summary=Launch a floating terminal with the Omarchy presentation wrapper +# archypr:args= + +cmd="$*" +exec setsid uwsm-app -- xdg-terminal-exec --app-id=org.omarchy.terminal --title=Omarchy -e bash -c "archypr-show-logo; $cmd; if (( \$? != 130 )); then archypr-show-done; fi" diff --git a/scripts/launch-or-focus b/scripts/launch-or-focus new file mode 100755 index 0000000..39f57b3 --- /dev/null +++ b/scripts/launch-or-focus @@ -0,0 +1,19 @@ +#!/bin/bash + +# archypr:summary=Launch an app or focus an existing window matching a pattern +# archypr:args= + +if (($# == 0)); then + echo "Usage: archypr-launch-or-focus [window-pattern] [launch-command]" + exit 1 +fi + +WINDOW_PATTERN="$1" +LAUNCH_COMMAND="${2:-"uwsm-app -- $WINDOW_PATTERN"}" +WINDOW_ADDRESS=$(hyprctl clients -j | jq -r --arg p "$WINDOW_PATTERN" '.[]|select((.class|test("\\b" + $p + "\\b";"i")) or (.title|test("\\b" + $p + "\\b";"i")))|.address' | head -n1) + +if [[ -n $WINDOW_ADDRESS ]]; then + hyprctl dispatch focuswindow "address:$WINDOW_ADDRESS" +else + eval exec setsid $LAUNCH_COMMAND +fi diff --git a/scripts/launch-or-focus-tui b/scripts/launch-or-focus-tui new file mode 100755 index 0000000..0d3209a --- /dev/null +++ b/scripts/launch-or-focus-tui @@ -0,0 +1,9 @@ +#!/bin/bash + +# archypr:summary=Launch a TUI or focus an existing terminal window for it +# archypr:args= [args...] + +APP_ID="org.omarchy.$(basename "$1")" +LAUNCH_COMMAND="archypr-launch-tui $@" + +exec archypr-launch-or-focus "$APP_ID" "$LAUNCH_COMMAND" diff --git a/scripts/launch-or-focus-webapp b/scripts/launch-or-focus-webapp new file mode 100755 index 0000000..23a6ffb --- /dev/null +++ b/scripts/launch-or-focus-webapp @@ -0,0 +1,15 @@ +#!/bin/bash + +# archypr:summary=Launch or focus on a given web app identified by the window-pattern. +# archypr:args= + +if (($# == 0)); then + echo "Usage: archypr-launch-or-focus-webapp [window-pattern] [url-and-flags...]" + exit 1 +fi + +WINDOW_PATTERN="$1" +shift +LAUNCH_COMMAND="archypr-launch-webapp $@" + +exec archypr-launch-or-focus "$WINDOW_PATTERN" "$LAUNCH_COMMAND" diff --git a/scripts/launch-screensaver b/scripts/launch-screensaver new file mode 100755 index 0000000..ac1f48f --- /dev/null +++ b/scripts/launch-screensaver @@ -0,0 +1,59 @@ +#!/bin/bash + +# archypr:summary=Launch the Omarchy screensaver in the default terminal on the system with the correct font configuration. + +if ! command -v tte &>/dev/null; then + exit 1 +fi + +# Exit early if screensave is already running +pgrep -f org.omarchy.screensaver && exit 0 + +# Allow screensaver to be turned off but also force started +if archypr-toggle-enabled screensaver-off && [[ $1 != "force" ]]; then + exit 1 +fi + +# Silently quit Walker on overlay +walker -q + +focused=$(archypr-yprland-monitor-focused) +terminal=$(xdg-terminal-exec --print-id) + +for m in $(hyprctl monitors -j | jq -r '.[] | .name'); do + hyprctl dispatch focusmonitor $m + + case $terminal in + *Alacritty*) + hyprctl dispatch exec -- \ + alacritty --class=org.omarchy.screensaver \ + --config-file ~/.local/share/archypr/default/alacritty/screensaver.toml \ + -e archypr-screensaver + ;; + *ghostty*) + hyprctl dispatch exec -- \ + ghostty --class=org.omarchy.screensaver \ + --config-file=~/.local/share/archypr/default/ghostty/screensaver \ + --font-size=18 \ + -e archypr-screensaver + ;; + *foot*) + hyprctl dispatch exec -- \ + foot --app-id=org.omarchy.screensaver \ + --config="$ARCHYPR_PATH/default/foot/screensaver.ini" \ + -e archypr-screensaver + ;; + *kitty*) + hyprctl dispatch exec -- \ + kitty --class=org.omarchy.screensaver \ + --override font_size=18 \ + --override window_padding_width=0 \ + -e archypr-screensaver + ;; + *) + notify-send -u low "✋ Screensaver only runs in Alacritty, Foot, Ghostty, or Kitty" + ;; + esac +done + +hyprctl dispatch focusmonitor $focused diff --git a/scripts/launch-tui b/scripts/launch-tui new file mode 100755 index 0000000..8323a68 --- /dev/null +++ b/scripts/launch-tui @@ -0,0 +1,6 @@ +#!/bin/bash + +# archypr:summary=Launch a TUI command in the default terminal with Omarchy styling +# archypr:args= [args...] + +exec setsid uwsm-app -- xdg-terminal-exec --app-id=org.omarchy.$(basename $1) -e "$1" "${@:2}" diff --git a/scripts/launch-walker b/scripts/launch-walker new file mode 100755 index 0000000..f49dccc --- /dev/null +++ b/scripts/launch-walker @@ -0,0 +1,14 @@ +#!/bin/bash + +# archypr:summary=Launch Walker and ensure its Elephant data provider is running + +if ! pgrep -x elephant > /dev/null; then + setsid uwsm-app -- elephant & +fi + +# Ensure walker service is running +if ! pgrep -f "walker --gapplication-service" > /dev/null; then + setsid uwsm-app -- env GSK_RENDERER=cairo walker --gapplication-service & +fi + +exec walker --width 644 --maxheight 300 --minheight 300 "$@" diff --git a/scripts/launch-webapp b/scripts/launch-webapp new file mode 100755 index 0000000..9f80437 --- /dev/null +++ b/scripts/launch-webapp @@ -0,0 +1,13 @@ +#!/bin/bash + +# archypr:summary=Launch a URL as a web app in the default supported browser +# archypr:args= + +browser=$(xdg-settings get default-web-browser) + +case $browser in +google-chrome* | brave* | microsoft-edge* | opera* | vivaldi* | helium*) ;; +*) browser="chromium.desktop" ;; +esac + +exec setsid uwsm-app -- $(sed -n 's/^Exec=\([^ ]*\).*/\1/p' {~/.local,~/.nix-profile,/usr}/share/applications/$browser 2>/dev/null | head -1) --app="$1" "${@:2}" diff --git a/scripts/launch-wifi b/scripts/launch-wifi new file mode 100755 index 0000000..6bf079c --- /dev/null +++ b/scripts/launch-wifi @@ -0,0 +1,6 @@ +#!/bin/bash + +# archypr:summary=Launch the Omarchy wifi controls (provided by the Impala TUI). + +rfkill unblock wifi +archypr-aunch-or-focus-tui impala diff --git a/scripts/menu b/scripts/menu new file mode 100755 index 0000000..d7d60eb --- /dev/null +++ b/scripts/menu @@ -0,0 +1,891 @@ +#!/bin/bash + +# archypr:summary=Launch the Omarchy Menu or takes a parameter to jump straight to a submenu. + +# Set to true when going directly to a submenu, so we can exit directly +BACK_TO_EXIT=false + +back_to() { + local parent_menu="$1" + + if [[ $BACK_TO_EXIT == "true" ]]; then + exit 0 + elif [[ -n $parent_menu ]]; then + "$parent_menu" + else + show_main_menu + fi +} + +toggle_existing_menu() { + if pgrep -f "walker.*--dmenu" >/dev/null; then + walker --close >/dev/null 2>&1 + exit 0 + fi +} + +menu() { + local prompt="$1" + local options="$2" + local extra="$3" + local preselect="$4" + + read -r -a args <<<"$extra" + + if [[ -n $preselect ]]; then + local index + index=$(echo -e "$options" | grep -nxF "$preselect" | cut -d: -f1) + if [[ -n $index ]]; then + args+=("-c" "$index") + fi + fi + + echo -e "$options" | archypr-launch-walker --dmenu --width 295 --minheight 1 --maxheight 630 -p "$prompt…" "${args[@]}" 2>/dev/null +} + +terminal() { + xdg-terminal-exec --app-id=org.omarchy.terminal "$@" +} + +present_terminal() { + archypr-launch-floating-terminal-with-presentation $1 +} + +open_in_editor() { + notify-send -u low "Editing config file" "$1" + archypr-launch-editor "$1" +} + +install() { + present_terminal "echo 'Installing $1...'; archypr-pkg-add $2" +} + +install_and_launch() { + present_terminal "echo 'Installing $1...'; archypr-pkg-add $2 && setsid gtk-launch $3" +} + +install_font() { + present_terminal "echo 'Installing $1...'; archypr-pkg-add $2 && sleep 2 && archypr-font-set '$3'" +} + +install_terminal() { + present_terminal "archypr-install-terminal $1" +} + +aur_install() { + present_terminal "echo 'Installing $1 from AUR...'; archypr-pkg-aur-add $2" +} + +aur_install_and_launch() { + present_terminal "echo 'Installing $1 from AUR...'; archypr-pkg-aur-add $2 && setsid gtk-launch $3" +} + +show_learn_menu() { + case $(menu "Learn" " Keybindings\n Omarchy\n Hyprland\n󰣇 Arch\n Neovim\n󱆃 Bash") in + *Keybindings*) archypr-menu-keybindings ;; + *Omarchy*) archypr-launch-webapp "https://learn.omacom.io/2/the-archypr-anual" ;; + *Hyprland*) archypr-launch-webapp "https://wiki.hypr.land/" ;; + *Arch*) archypr-launch-webapp "https://wiki.archlinux.org/title/Main_page" ;; + *Bash*) archypr-launch-webapp "https://devhints.io/bash" ;; + *Neovim*) archypr-launch-webapp "https://www.lazyvim.org/keymaps" ;; + *) show_main_menu ;; + esac +} + +show_trigger_menu() { + case $(menu "Trigger" "󰔛 Reminder\n Capture\n󰧸 Transcode\n Share\n󰔎 Toggle\n Hardware") in + *Reminder*) show_reminder_menu ;; + *Capture*) show_capture_menu ;; + *Transcode*) archypr-transcode || back_to show_trigger_menu ;; + *Share*) show_share_menu ;; + *Toggle*) show_toggle_menu ;; + *Hardware*) show_hardware_menu ;; + *) show_main_menu ;; + esac +} + +show_reminder_menu() { + case $(menu "Reminder" "󰔛 Set one\n󰔛 Show all\n󰔛 Clear all") in + *Set*) show_custom_reminder_input ;; + *"Show all"*) archypr-reminder show ;; + *"Clear all"*) archypr-reminder clear ;; + *) back_to show_trigger_menu ;; + esac +} + +show_custom_reminder_input() { + local minutes + minutes=$(archypr-enu-input "Remind in minutes") + + if [[ $minutes =~ ^[0-9]+$ ]] && ((minutes > 0)); then + show_reminder_message_input "$minutes" + elif [[ -n $minutes ]]; then + archypr-notification-send "󰔛" "Invalid reminder" "Enter the number of minutes" -u critical + show_custom_reminder_input + else + back_to show_reminder_menu + fi +} + +show_reminder_message_input() { + local minutes="$1" + local message + message=$(archypr-enu-input "Reminder message") + + if [[ -n $message ]]; then + archypr-reminder "$minutes" "$message" + else + archypr-reminder "$minutes" + fi +} + +show_capture_menu() { + case $(menu "Capture" " Screenshot\n Screenrecord\n󰴑 Text Extraction\n󰃉 Color") in + *Screenshot*) archypr-capture-screenshot ;; + *Screenrecord*) show_screenrecord_menu ;; + *Text*) archypr-capture-text-extraction ;; + *Color*) pkill hyprpicker || hyprpicker -a ;; + *) back_to show_trigger_menu ;; + esac +} + +get_webcam_list() { + v4l2-ctl --list-devices 2>/dev/null | while IFS= read -r line; do + if [[ $line != $'\t'* && -n $line ]]; then + local name="$line" + IFS= read -r device || break + device=$(echo "$device" | tr -d '\t' | head -1) + [[ -n $device ]] && echo "$device $name" + fi + done +} + +show_webcam_select_menu() { + local devices=$(get_webcam_list) + local count=$(echo "$devices" | grep -c . 2>/dev/null || echo 0) + + if [[ -z $devices ]] || ((count == 0)); then + notify-send "No webcam devices found" -u critical -t 3000 + return 1 + fi + + if ((count == 1)); then + echo "$devices" | awk '{print $1}' + else + menu "Select Webcam" "$devices" | awk '{print $1}' + fi +} + +show_screenrecord_menu() { + archypr-capture-screenrecording --stop-recording && exit 0 + + case $(menu "Screenrecord" " With no audio\n With desktop audio\n With desktop + microphone audio\n With desktop + microphone audio + webcam") in + *"With no audio") archypr-capture-screenrecording ;; + *"With desktop audio") archypr-capture-screenrecording --with-desktop-audio ;; + *"With desktop + microphone audio") archypr-capture-screenrecording --with-desktop-audio --with-microphone-audio ;; + *"With desktop + microphone audio + webcam") + local device=$(show_webcam_select_menu) || { + back_to show_capture_menu + return + } + archypr-capture-screenrecording --with-desktop-audio --with-microphone-audio --with-webcam --webcam-device="$device" + ;; + *) back_to show_capture_menu ;; + esac +} + +show_share_menu() { + case $(menu "Share" " Clipboard\n File \n Folder") in + *Clipboard*) archypr-menu-share clipboard ;; + *File*) terminal bash -c "archypr-menu-share file" ;; + *Folder*) terminal bash -c "archypr-menu-share folder" ;; + *) back_to show_trigger_menu ;; + esac +} + +show_toggle_menu() { + local options="󱄄 Screensaver\n󰔎 Nightlight\n󱫖 Idle Lock\n󰂛 Notifications\n󰍜 Top Bar\n󱂬 Workspace Layout\n Window Gaps\n 1-Window Ratio\n󰍹 Monitor Scaling\n Direct Boot\n󰟵 Passwordless Sudo" + + case $(menu "Toggle" "$options") in + *Screensaver*) archypr-toggle-screensaver ;; + *Nightlight*) archypr-toggle-nightlight ;; + *Idle*) archypr-toggle-idle ;; + *Notifications*) archypr-toggle-notification-silencing ;; + *Bar*) archypr-toggle-waybar ;; + *Layout*) archypr-hyprland-workspace-layout-toggle ;; + *Ratio*) archypr-hyprland-window-single-square-aspect-toggle ;; + *Gaps*) archypr-hyprland-window-gaps-toggle ;; + *Scaling*) archypr-hyprland-monitor-scaling-cycle ;; + *"Direct Boot"*) present_terminal archypr-config-direct-boot ;; + *"Passwordless Sudo"*) present_terminal archypr-sudo-passwordless ;; + *) back_to show_trigger_menu ;; + esac +} + +show_hardware_menu() { + local options="󰛧 Laptop Display\n 󰍹 Mirror Display" + + if archypr-hw-hybrid-gpu; then + options="$options\n Hybrid GPU" + fi + + if archypr-hw-touchpad; then + options="$options\n󰟸 Touchpad" + fi + + if archypr-hw-dell-xps-haptic-touchpad && archypr-cmd-present dell-xps-touchpad-haptics; then + options="$options\n󰌌 Touchpad Haptics" + fi + + if archypr-hw-touchscreen; then + options="$options\n󰆽 Touchscreen" + fi + + case $(menu "Toggle" "$options") in + *Laptop*) archypr-hyprland-monitor-internal toggle ;; + *Mirror*) archypr-hyprland-monitor-internal-mirror toggle ;; + *Haptics*) show_hardware_touchpad_haptics_menu ;; + *Touchpad*) archypr-toggle-touchpad ;; + *Touchscreen*) archypr-toggle-touchscreen ;; + *"Hybrid GPU"*) present_terminal archypr-toggle-hybrid-gpu ;; + *) back_to show_trigger_menu ;; + esac +} + +show_hardware_touchpad_haptics_menu() { + local current=$(dell-xps-touchpad-haptics get) + local selected=$(menu "Touchpad Haptics" "low\nmid\nhigh" "" "$current") + + if [[ -n $selected ]]; then + dell-xps-touchpad-haptics set "$selected" + else + back_to show_hardware_menu + fi +} + +show_style_menu() { + case $(menu "Style" "󰸌 Theme\n󰟵 Unlock\n Font\n Background\n Hyprland\n󱄄 Screensaver\n About") in + *Theme*) show_theme_menu ;; + *Unlock*) archypr-launch-walker -m menus:omarchyunlocks --width 800 --minheight 400 ;; + *Font*) show_font_menu ;; + *Background*) show_background_menu ;; + *Hyprland*) open_in_editor ~/.config/hypr/looknfeel.conf ;; + *Screensaver*) show_screensaver_menu ;; + *About*) show_about_menu ;; + *) show_main_menu ;; + esac +} + +show_about_menu() { + case $(menu "About" " Edit Text\n Set From Image\n Restore Default") in + *Text*) archypr-branding-about text ;; + *Image*) archypr-branding-about image ;; + *Default*) archypr-branding-about reset ;; + *) show_style_menu ;; + esac +} + +show_screensaver_menu() { + case $(menu "Screensaver" " Edit Text\n Set From Image\n Restore Default") in + *Text*) archypr-branding-screensaver text ;; + *Image*) archypr-branding-screensaver image ;; + *Default*) archypr-branding-screensaver reset ;; + *) show_style_menu ;; + esac +} + +show_theme_menu() { + archypr-launch-walker -m menus:omarchythemes --width 800 --minheight 400 +} + +show_background_menu() { + archypr-launch-walker -m menus:omarchyBackgroundSelector --width 800 --minheight 400 +} + +show_font_menu() { + theme=$(menu "Font" "$(archypr-ont-list)" "--width 350" "$(archypr-ont-current)") + if [[ $theme == "CNCLD" || -z $theme ]]; then + back_to show_style_menu + else + archypr-font-set "$theme" + fi +} + +show_setup_menu() { + local options=" Audio\n Wifi\n󰂯 Bluetooth\n󱐋 Power Profile\n System Sleep\n󰍹 Monitors" + [[ -f ~/.config/hypr/bindings.conf ]] && options="$options\n Keybindings" + [[ -f ~/.config/hypr/input.conf ]] && options="$options\n Input" + options="$options\n Defaults\n󰱔 DNS\n Security\n Config" + + case $(menu "Setup" "$options") in + *Audio*) archypr-launch-audio ;; + *Wifi*) archypr-launch-wifi ;; + *Bluetooth*) archypr-launch-bluetooth ;; + *Power*) show_setup_power_menu ;; + *System*) show_setup_system_menu ;; + *Monitors*) open_in_editor ~/.config/hypr/monitors.conf ;; + *Keybindings*) open_in_editor ~/.config/hypr/bindings.conf ;; + *Input*) open_in_editor ~/.config/hypr/input.conf ;; + *Defaults*) show_setup_default_menu ;; + *DNS*) present_terminal archypr-setup-dns ;; + *Security*) show_setup_security_menu ;; + *Config*) show_setup_config_menu ;; + *) show_main_menu ;; + esac +} + +show_setup_power_menu() { + profile=$(menu "Power Profile" "$(archypr-owerprofiles-list)" "" "$(powerprofilesctl get)") + + if [[ $profile == "CNCLD" || -z $profile ]]; then + back_to show_setup_menu + else + powerprofilesctl set "$profile" + fi +} + +show_setup_security_menu() { + case $(menu "Setup" "󰈷 Fingerprint\n Fido2") in + *Fingerprint*) present_terminal archypr-setup-security-fingerprint ;; + *Fido2*) present_terminal archypr-setup-security-fido2 ;; + *) show_setup_menu ;; + esac +} + +show_setup_default_menu() { + case $(menu "Default" " Browser\n Terminal\n Editor") in + *Browser*) show_setup_default_browser_menu ;; + *Terminal*) show_setup_default_terminal_menu ;; + *Editor*) show_setup_default_editor_menu ;; + *) show_setup_menu ;; + esac +} + +browser_desktop_exists() { + [[ -f ~/.local/share/applications/$1 || -f ~/.nix-profile/share/applications/$1 || -f /usr/share/applications/$1 ]] +} + +show_setup_default_browser_menu() { + local options="" + browser_desktop_exists chromium.desktop && options="$options Chromium" + browser_desktop_exists google-chrome.desktop && options="${options:+$options\n}󰊯 Chrome" + browser_desktop_exists brave-browser.desktop && options="${options:+$options\n}󰖟 Brave" + browser_desktop_exists brave-origin-beta.desktop && options="${options:+$options\n}󰖟 Brave Origin" + browser_desktop_exists microsoft-edge.desktop && options="${options:+$options\n}󰇩 Edge" + browser_desktop_exists firefox.desktop && options="${options:+$options\n}󰈹 Firefox" + browser_desktop_exists zen.desktop && options="${options:+$options\n}󰖟 Zen" + + local current="" + case "$(archypr-efault-browser)" in + chromium) current=" Chromium" ;; + chrome) current="󰊯 Chrome" ;; + brave) current="󰖟 Brave" ;; + brave-origin) current="󰖟 Brave Origin" ;; + edge) current="󰇩 Edge" ;; + firefox) current="󰈹 Firefox" ;; + zen) current="󰖟 Zen" ;; + esac + + case $(menu "Default Browser" "$options" "" "$current") in + *Chromium*) archypr-default-browser chromium ;; + *Chrome*) archypr-default-browser chrome ;; + *"Brave Origin"*) archypr-default-browser brave-origin ;; + *Brave*) archypr-default-browser brave ;; + *Edge*) archypr-default-browser edge ;; + *Firefox*) archypr-default-browser firefox ;; + *Zen*) archypr-default-browser zen ;; + *) show_setup_default_menu ;; + esac +} + +show_setup_default_terminal_menu() { + local options="" + archypr-cmd-present alacritty && options="$options Alacritty" + archypr-cmd-present foot && options="${options:+$options\n} Foot" + archypr-cmd-present ghostty && options="${options:+$options\n} Ghostty" + archypr-cmd-present kitty && options="${options:+$options\n} Kitty" + + local current="" + case "$(archypr-efault-terminal)" in + alacritty) current=" Alacritty" ;; + foot) current=" Foot" ;; + ghostty) current=" Ghostty" ;; + kitty) current=" Kitty" ;; + esac + + case $(menu "Default Terminal" "$options" "" "$current") in + *Alacritty*) archypr-default-terminal alacritty ;; + *Foot*) archypr-default-terminal foot ;; + *Ghostty*) archypr-default-terminal ghostty ;; + *Kitty*) archypr-default-terminal kitty ;; + *) show_setup_default_menu ;; + esac +} + +show_setup_default_editor_menu() { + local options="" + archypr-cmd-present nvim && options="$options Neovim" + archypr-cmd-present code && options="${options:+$options\n} VSCode" + archypr-cmd-present cursor && options="${options:+$options\n} Cursor" + archypr-cmd-present zeditor && options="${options:+$options\n} Zed" + archypr-cmd-present sublime_text && options="${options:+$options\n} Sublime Text" + archypr-cmd-present helix && options="${options:+$options\n} Helix" + archypr-cmd-present vim && options="${options:+$options\n} Vim" + archypr-cmd-present emacs && options="${options:+$options\n} Emacs" + + local current="" + case "$(archypr-efault-editor)" in + nvim) current=" Neovim" ;; + code) current=" VSCode" ;; + cursor) current=" Cursor" ;; + zed | zeditor) current=" Zed" ;; + sublime_text) current=" Sublime Text" ;; + helix) current=" Helix" ;; + vim) current=" Vim" ;; + emacs) current=" Emacs" ;; + esac + + case $(menu "Default Editor" "$options" "" "$current") in + *Neovim*) archypr-default-editor nvim ;; + *VSCode*) archypr-default-editor code ;; + *Cursor*) archypr-default-editor cursor ;; + *Zed*) archypr-default-editor zed ;; + *Sublime*) archypr-default-editor sublime_text ;; + *Helix*) archypr-default-editor helix ;; + *Vim*) archypr-default-editor vim ;; + *Emacs*) archypr-default-editor emacs ;; + *) show_setup_default_menu ;; + esac +} + +show_setup_config_menu() { + case $(menu "Setup" " Hyprland\n Hypridle\n Hyprlock\n Hyprsunset\n Swayosd\n󰌧 Walker\n󰍜 Waybar\n󰞅 XCompose") in + *Hyprland*) open_in_editor ~/.config/hypr/hyprland.conf ;; + *Hypridle*) open_in_editor ~/.config/hypr/hypridle.conf && archypr-restart-hypridle ;; + *Hyprlock*) open_in_editor ~/.config/hypr/hyprlock.conf ;; + *Hyprsunset*) open_in_editor ~/.config/hypr/hyprsunset.conf && archypr-restart-hyprsunset ;; + *Swayosd*) open_in_editor ~/.config/swayosd/config.toml && archypr-restart-swayosd ;; + *Walker*) open_in_editor ~/.config/walker/config.toml && archypr-restart-walker ;; + *Waybar*) open_in_editor ~/.config/waybar/config.jsonc && archypr-restart-waybar ;; + *XCompose*) open_in_editor ~/.XCompose && archypr-restart-xcompose ;; + *) show_setup_menu ;; + esac +} + +show_setup_system_menu() { + local options="" + + if archypr-toggle-enabled suspend-off; then + options="$options󰒲 Enable Suspend" + else + options="$options󰒲 Disable Suspend" + fi + + if archypr-hibernation-available; then + options="$options\n󰤁 Disable Hibernate" + else + options="$options\n󰤁 Enable Hibernate" + fi + + case $(menu "System" "$options") in + *Suspend*) archypr-toggle-suspend ;; + *"Enable Hibernate"*) present_terminal archypr-hibernation-setup ;; + *"Disable Hibernate"*) present_terminal archypr-hibernation-remove ;; + *) show_setup_menu ;; + esac +} + +show_install_menu() { + case $(menu "Install" "󰣇 Package\n󰣇 AUR\n Web App\n TUI\n Service\n Style\n󰵮 Development\n Editor\n Terminal\n Browser\n󱚤 AI\n Gaming\n󰍲 Windows") in + *Package*) terminal archypr-pkg-install ;; + *AUR*) terminal archypr-pkg-aur-install ;; + *Web*) present_terminal archypr-webapp-install ;; + *TUI*) present_terminal archypr-tui-install ;; + *Service*) show_install_service_menu ;; + *Style*) show_install_style_menu ;; + *Development*) show_install_development_menu ;; + *Editor*) show_install_editor_menu ;; + *Terminal*) show_install_terminal_menu ;; + *Browser*) show_install_browser_menu ;; + *Gaming*) show_install_gaming_menu ;; + *AI*) show_install_ai_menu ;; + *Windows*) present_terminal "archypr-windows-vm install" ;; + *) show_main_menu ;; + esac +} + +show_install_browser_menu() { + case $(menu "Install" " Chrome\n Edge\n Brave\n Brave Origin\n Firefox\n󰖟 Zen") in + *Chrome*) present_terminal "archypr-install-browser chrome" ;; + *Edge*) present_terminal "archypr-install-browser edge" ;; + *"Brave Origin"*) present_terminal "archypr-install-browser brave-origin" ;; + *Brave*) present_terminal "archypr-install-browser brave" ;; + *Firefox*) present_terminal "archypr-install-browser firefox" ;; + *Zen*) present_terminal "archypr-install-browser zen" ;; + *) show_install_menu ;; + esac +} + +show_install_service_menu() { + case $(menu "Install" " Dropbox\n Tailscale\n󱇱 NordVPN [AUR]\n󰏖 ONCE\n󰟵 Bitwarden\n Chromium Account") in + *Dropbox*) present_terminal archypr-install-dropbox ;; + *Tailscale*) present_terminal archypr-install-tailscale ;; + *NordVPN*) present_terminal archypr-install-nordvpn ;; + *ONCE*) present_terminal archypr-install-once ;; + *Bitwarden*) install_and_launch "Bitwarden" "bitwarden bitwarden-cli" "bitwarden" ;; + *Chromium*) present_terminal archypr-install-chromium-google-account ;; + *) show_install_menu ;; + esac +} + +show_install_editor_menu() { + case $(menu "Install" " VSCode\n Cursor\n Zed\n Sublime Text\n Helix\n Vim\n Emacs") in + *VSCode*) present_terminal archypr-install-vscode ;; + *Cursor*) install_and_launch "Cursor" "cursor-bin" "cursor" ;; + *Zed*) present_terminal archypr-install-zed ;; + *Sublime*) install_and_launch "Sublime Text" "sublime-text-4" "sublime_text" ;; + *Helix*) present_terminal archypr-install-helix ;; + *Vim*) install "Vim" "vim" ;; + *Emacs*) install "Emacs" "emacs-wayland" && systemctl --user enable --now emacs.service ;; + *) show_install_menu ;; + esac +} + +show_install_terminal_menu() { + case $(menu "Install" " Alacritty\n Foot\n Ghostty\n Kitty") in + *Alacritty*) install_terminal "alacritty" ;; + *Foot*) install_terminal "foot" ;; + *Ghostty*) install_terminal "ghostty" ;; + *Kitty*) install_terminal "kitty" ;; + *) show_install_menu ;; + esac +} + +show_install_ai_menu() { + ollama_pkg=$( + (archypr-md-present nvidia-smi && echo ollama-cuda) || + (archypr-md-present rocminfo && echo ollama-rocm) || + echo ollama + ) + + case $(menu "Install" " Dictation\n󱚤 LM Studio\n󱚤 Ollama\n󱚤 Crush") in + *Dictation*) present_terminal archypr-voxtype-install ;; + *Studio*) install "LM Studio" "lmstudio-bin" ;; + *Ollama*) install "Ollama" $ollama_pkg ;; + *Crush*) install "Crush" "crush-bin" ;; + *) show_install_menu ;; + esac +} + +show_install_gaming_menu() { + case $(menu "Install" " Steam\n RetroArch\n󰍳 Minecraft\n󰢹 NVIDIA GeForce NOW\n Xbox Cloud Gaming\n󰂯 Xbox Controller\n󰍹 Moonlight (GameStream)\n Lutris (Battle.net)\n󱓟 Heroic (Epic Games)") in + *Steam*) present_terminal archypr-install-gaming-steam ;; + *RetroArch*) present_terminal archypr-install-gaming-retroarch ;; + *Minecraft*) install_and_launch "Minecraft" "minecraft-launcher" "minecraft-launcher" ;; + *GeForce*) present_terminal archypr-install-gaming-geforce-now ;; + *"Xbox Cloud"*) present_terminal archypr-install-gaming-xbox-cloud ;; + *Xbox*) present_terminal archypr-install-gaming-xbox-controllers ;; + *Lutris*) present_terminal archypr-install-gaming-lutris ;; + *Heroic*) present_terminal archypr-install-gaming-heroic ;; + *Moonlight*) present_terminal archypr-install-gaming-moonlight ;; + *) show_install_menu ;; + esac +} + +show_install_style_menu() { + case $(menu "Install" "󰸌 Theme\n Background\n Font") in + *Theme*) present_terminal archypr-theme-install ;; + *Background*) archypr-theme-bg-install ;; + *Font*) show_install_font_menu ;; + *) show_install_menu ;; + esac +} + +show_install_font_menu() { + case $(menu "Install" " Cascadia Mono\n Meslo LG Mono\n Fira Code\n Victor Code\n Bitstream Vera Mono\n Iosevka" "--width 350") in + *Cascadia*) install_font "Cascadia Mono" "ttf-cascadia-mono-nerd" "CaskaydiaMono Nerd Font" ;; + *Meslo*) install_font "Meslo LG Mono" "ttf-meslo-nerd" "MesloLGL Nerd Font" ;; + *Fira*) install_font "Fira Code" "ttf-firacode-nerd" "FiraCode Nerd Font" ;; + *Victor*) install_font "Victor Code" "ttf-victor-mono-nerd" "VictorMono Nerd Font" ;; + *Bitstream*) install_font "Bitstream Vera Code" "ttf-bitstream-vera-mono-nerd" "BitstromWera Nerd Font" ;; + *Iosevka*) install_font "Iosevka" "ttf-iosevka-nerd" "Iosevka Nerd Font Mono" ;; + *) show_install_menu ;; + esac +} + +show_install_development_menu() { + case $(menu "Install" "󰫏 Ruby on Rails\n Docker DB\n JavaScript\n Go\n PHP\n Python\n Elixir\n Zig\n Rust\n Java\n .NET\n OCaml\n Clojure\n Scala") in + *Rails*) present_terminal "archypr-install-dev-env ruby" ;; + *Docker*) present_terminal archypr-install-docker-dbs ;; + *JavaScript*) show_install_javascript_menu ;; + *Go*) present_terminal "archypr-install-dev-env go" ;; + *PHP*) show_install_php_menu ;; + *Python*) present_terminal "archypr-install-dev-env python" ;; + *Elixir*) show_install_elixir_menu ;; + *Zig*) present_terminal "archypr-install-dev-env zig" ;; + *Rust*) present_terminal "archypr-install-dev-env rust" ;; + *Java*) present_terminal "archypr-install-dev-env java" ;; + *NET*) present_terminal "archypr-install-dev-env dotnet" ;; + *OCaml*) present_terminal "archypr-install-dev-env ocaml" ;; + *Clojure*) present_terminal "archypr-install-dev-env clojure" ;; + *Scala*) present_terminal "archypr-install-dev-env scala" ;; + *) show_install_menu ;; + esac +} + +show_install_javascript_menu() { + case $(menu "Install" " Node.js\n Bun\n Deno") in + *Node*) present_terminal "archypr-install-dev-env node" ;; + *Bun*) present_terminal "archypr-install-dev-env bun" ;; + *Deno*) present_terminal "archypr-install-dev-env deno" ;; + *) show_install_development_menu ;; + esac +} + +show_install_php_menu() { + case $(menu "Install" " PHP\n Laravel\n Symfony") in + *PHP*) present_terminal "archypr-install-dev-env php" ;; + *Laravel*) present_terminal "archypr-install-dev-env laravel" ;; + *Symfony*) present_terminal "archypr-install-dev-env symfony" ;; + *) show_install_development_menu ;; + esac +} + +show_install_elixir_menu() { + case $(menu "Install" " Elixir\n Phoenix") in + *Elixir*) present_terminal "archypr-install-dev-env elixir" ;; + *Phoenix*) present_terminal "archypr-install-dev-env phoenix" ;; + *) show_install_development_menu ;; + esac +} + +show_remove_menu() { + case $(menu "Remove" "󰣇 Package\n Web App\n TUI\n󰵮 Development\n󰸌 Theme\n Browser\n Dictation\n Gaming\n󰍲 Windows\n󰏓 Preinstalls\n Security") in + *Package*) terminal archypr-pkg-remove ;; + *Web*) present_terminal archypr-webapp-remove ;; + *TUI*) present_terminal archypr-tui-remove ;; + *Development*) show_remove_development_menu ;; + *Theme*) present_terminal archypr-theme-remove ;; + *Browser*) show_remove_browser_menu ;; + *Dictation*) present_terminal archypr-voxtype-remove ;; + *Gaming*) show_remove_gaming_menu ;; + *Windows*) present_terminal "archypr-windows-vm remove" ;; + *Preinstalls*) present_terminal archypr-remove-preinstalls ;; + *Security*) show_remove_security_menu ;; + *) show_main_menu ;; + esac +} + +show_remove_security_menu() { + case $(menu "Remove" "󰈷 Fingerprint\n Fido2") in + *Fingerprint*) present_terminal archypr-remove-security-fingerprint ;; + *Fido2*) present_terminal archypr-remove-security-fido2 ;; + *) show_remove_menu ;; + esac +} + +show_remove_browser_menu() { + case $(menu "Remove" " Chrome\n Edge\n Brave\n Brave Origin\n Firefox\n Zen") in + *Chrome*) present_terminal "archypr-remove-browser chrome" ;; + *Edge*) present_terminal "archypr-remove-browser edge" ;; + *"Brave Origin"*) present_terminal "archypr-remove-browser brave-origin" ;; + *Brave*) present_terminal "archypr-remove-browser brave" ;; + *Firefox*) present_terminal "archypr-remove-browser firefox" ;; + *Zen*) present_terminal "archypr-remove-browser zen" ;; + *) show_remove_menu ;; + esac +} + +show_remove_gaming_menu() { + case $(menu "Remove" " Steam\n RetroArch\n󰍳 Minecraft\n󰢹 NVIDIA GeForce NOW\n Xbox Cloud Gaming\n󰖺 Xbox Controller (󰂯)\n󰍹 Moonlight (GameStream)\n Lutris (Battle.net)\n󱓟 Heroic (Epic Games)") in + *Steam*) present_terminal archypr-remove-gaming-steam ;; + *RetroArch*) present_terminal archypr-remove-gaming-retroarch ;; + *Minecraft*) present_terminal archypr-remove-gaming-minecraft ;; + *GeForce*) present_terminal archypr-remove-gaming-geforce-now ;; + *"Xbox Cloud"*) present_terminal archypr-remove-gaming-xbox-cloud ;; + *Xbox*) present_terminal archypr-remove-gaming-xbox-controllers ;; + *Moonlight*) present_terminal archypr-remove-gaming-moonlight ;; + *Lutris*) present_terminal archypr-remove-gaming-lutris ;; + *Heroic*) present_terminal archypr-remove-gaming-heroic ;; + *) show_remove_menu ;; + esac +} + +show_remove_development_menu() { + case $(menu "Remove" "󰫏 Ruby on Rails\n JavaScript\n Go\n PHP\n Python\n Elixir\n Zig\n Rust\n Java\n .NET\n OCaml\n Clojure\n Scala") in + *Rails*) present_terminal "archypr-remove-dev-env ruby" ;; + *JavaScript*) show_remove_javascript_menu ;; + *Go*) present_terminal "archypr-remove-dev-env go" ;; + *PHP*) show_remove_php_menu ;; + *Python*) present_terminal "archypr-remove-dev-env python" ;; + *Elixir*) show_remove_elixir_menu ;; + *Zig*) present_terminal "archypr-remove-dev-env zig" ;; + *Rust*) present_terminal "archypr-remove-dev-env rust" ;; + *Java*) present_terminal "archypr-remove-dev-env java" ;; + *NET*) present_terminal "archypr-remove-dev-env dotnet" ;; + *OCaml*) present_terminal "archypr-remove-dev-env ocaml" ;; + *Clojure*) present_terminal "archypr-remove-dev-env clojure" ;; + *Scala*) present_terminal "archypr-remove-dev-env scala" ;; + *) show_remove_menu ;; + esac +} + +show_remove_javascript_menu() { + case $(menu "Remove" " Node.js\n Bun\n Deno") in + *Node*) present_terminal "archypr-remove-dev-env node" ;; + *Bun*) present_terminal "archypr-remove-dev-env bun" ;; + *Deno*) present_terminal "archypr-remove-dev-env deno" ;; + *) show_remove_development_menu ;; + esac +} + +show_remove_php_menu() { + case $(menu "Remove" " PHP\n Laravel\n Symfony") in + *PHP*) present_terminal "archypr-remove-dev-env php" ;; + *Laravel*) present_terminal "archypr-remove-dev-env laravel" ;; + *Symfony*) present_terminal "archypr-remove-dev-env symfony" ;; + *) show_remove_development_menu ;; + esac +} + +show_remove_elixir_menu() { + case $(menu "Remove" " Elixir\n Phoenix") in + *Elixir*) present_terminal "archypr-remove-dev-env elixir" ;; + *Phoenix*) present_terminal "archypr-remove-dev-env phoenix" ;; + *) show_remove_development_menu ;; + esac +} + +show_update_menu() { + case $(menu "Update" "  Omarchy\n󰔫 Channel\n Config\n󰸌 Extra Themes\n Process\n󰇅 Hardware\n Firmware\n Password\n Timezone\n Time") in + *Omarchy*) present_terminal archypr-update ;; + *Channel*) show_update_channel_menu ;; + *Config*) show_update_config_menu ;; + *Themes*) present_terminal archypr-theme-update ;; + *Process*) show_update_process_menu ;; + *Hardware*) show_update_hardware_menu ;; + *Firmware*) present_terminal archypr-update-firmware ;; + *Timezone*) present_terminal archypr-tz-select ;; + *Time*) present_terminal archypr-update-time ;; + *Password*) show_update_password_menu ;; + *) show_main_menu ;; + esac +} + +show_update_channel_menu() { + case $(menu "Update channel" "🟢 Stable\n🟡 RC\n🟠 Edge\n🔴 Dev") in + *Stable*) present_terminal "archypr-channel-set stable" ;; + *RC*) present_terminal "archypr-channel-set rc" ;; + *Edge*) present_terminal "archypr-channel-set edge" ;; + *Dev*) present_terminal "archypr-channel-set dev" ;; + *) show_update_menu ;; + esac +} +show_update_process_menu() { + case $(menu "Restart" " Hypridle\n Hyprsunset\n󰎟 Mako\n Swayosd\n󰌧 Walker\n󰍜 Waybar") in + *Hypridle*) archypr-restart-hypridle ;; + *Hyprsunset*) archypr-restart-hyprsunset ;; + *Mako*) archypr-restart-mako ;; + *Swayosd*) archypr-restart-swayosd ;; + *Walker*) archypr-restart-walker ;; + *Waybar*) archypr-restart-waybar ;; + *) show_update_menu ;; + esac +} + +show_update_config_menu() { + case $(menu "Use default config" " Hyprland\n Hypridle\n Hyprlock\n Hyprsunset\n󱣴 Plymouth\n Swayosd\n Tmux\n󰌧 Walker\n󰍜 Waybar") in + *Hyprland*) present_terminal archypr-refresh-hyprland ;; + *Hypridle*) present_terminal archypr-refresh-hypridle ;; + *Hyprlock*) present_terminal archypr-refresh-hyprlock ;; + *Hyprsunset*) present_terminal archypr-refresh-hyprsunset ;; + *Plymouth*) present_terminal archypr-refresh-plymouth ;; + *Swayosd*) present_terminal archypr-refresh-swayosd ;; + *Tmux*) present_terminal archypr-refresh-tmux ;; + *Walker*) present_terminal archypr-refresh-walker ;; + *Waybar*) present_terminal archypr-refresh-waybar ;; + *) show_update_menu ;; + esac +} + +show_update_hardware_menu() { + case $(menu "Restart" " Audio\n󱚾 Wi-Fi\n󰂯 Bluetooth\n󰟸 Trackpad") in + *Audio*) present_terminal archypr-restart-pipewire ;; + *Wi-Fi*) present_terminal archypr-restart-wifi ;; + *Bluetooth*) present_terminal archypr-restart-bluetooth ;; + *Trackpad*) present_terminal archypr-restart-trackpad ;; + *) show_update_menu ;; + esac +} + +show_update_password_menu() { + case $(menu "Update Password" " Drive Encryption\n User") in + *Drive*) present_terminal archypr-drive-password ;; + *User*) present_terminal passwd ;; + *) show_update_menu ;; + esac +} + +show_about() { + archypr-launch-about +} + +show_system_menu() { + local options="󱄄 Screensaver\n Lock" + ! archypr-toggle-enabled suspend-off && options="$options\n󰒲 Suspend" + archypr-hibernation-available && options="$options\n󰤁 Hibernate" + options="$options\n󰍃 Logout\n󰜉 Restart\n󰐥 Shutdown" + + case $(menu "System" "$options") in + *Screensaver*) archypr-launch-screensaver force ;; + *Lock*) archypr-system-lock ;; + *Suspend*) systemctl suspend ;; + *Hibernate*) systemctl hibernate ;; + *Logout*) archypr-system-logout ;; + *Restart*) archypr-system-reboot ;; + *Shutdown*) archypr-system-shutdown ;; + *) back_to show_main_menu ;; + esac +} + +show_main_menu() { + go_to_menu "$(menu "Go" "󰀻 Apps\n󰧑 Learn\n󱓞 Trigger\n Style\n Setup\n󰉉 Install\n󰭌 Remove\n Update\n About\n System")" +} + +go_to_menu() { + case "${1,,}" in + *apps*) walker -p "Launch…" ;; + *learn*) show_learn_menu ;; + *trigger*) show_trigger_menu ;; + *toggle*) show_toggle_menu ;; + *hardware*) show_hardware_menu ;; + *share*) show_share_menu ;; + *reminder-set*) show_custom_reminder_input ;; + *reminder*) show_reminder_menu ;; + *background*) show_background_menu ;; + *capture*) show_capture_menu ;; + *style*) show_style_menu ;; + *theme*) show_theme_menu ;; + *screenrecord*) show_screenrecord_menu ;; + *setup*) show_setup_menu ;; + *power*) show_setup_power_menu ;; + *install*) show_install_menu ;; + *remove*) show_remove_menu ;; + *update*) show_update_menu ;; + *about*) show_about ;; + *system*) show_system_menu ;; + esac +} + +# Allow user extensions and overrides +USER_EXTENSIONS="$HOME/.config/omarchy/extensions/menu.sh" +[[ -f $USER_EXTENSIONS ]] && source "$USER_EXTENSIONS" + +toggle_existing_menu + +if [[ -n $1 ]]; then + BACK_TO_EXIT=true + go_to_menu "$1" +else + show_main_menu +fi diff --git a/scripts/menu-file b/scripts/menu-file new file mode 100755 index 0000000..445834c --- /dev/null +++ b/scripts/menu-file @@ -0,0 +1,48 @@ +#!/bin/bash + +# archypr:summary=Pick a file with Walker +# archypr:group=menu +# archypr:name=file +# archypr:args=label paths formats [walker args...] +# archypr:examples=omarchy menu file "Select image" "$HOME/Pictures" "jpg png webp"|archypr-enu-file "Select media" "$HOME/Pictures:$HOME/Videos" "jpg png mp4 mov" --width 800 + +set -euo pipefail + +if (( $# < 3 )); then + echo "Usage: archypr-menu-file