summaryrefslogtreecommitdiff
path: root/ar/.local
diff options
context:
space:
mode:
Diffstat (limited to 'ar/.local')
-rwxr-xr-xar/.local/bin/arkenfox-auto-update26
-rwxr-xr-xar/.local/bin/bash-preexec380
-rwxr-xr-xar/.local/bin/bookmarks164
-rwxr-xr-xar/.local/bin/booksplit45
-rwxr-xr-xar/.local/bin/browse85
-rwxr-xr-xar/.local/bin/browserprofile102
-rwxr-xr-xar/.local/bin/clonerepo61
-rwxr-xr-xar/.local/bin/compiler104
-rwxr-xr-xar/.local/bin/concatvideo56
-rwxr-xr-xar/.local/bin/createsh104
-rw-r--r--ar/.local/bin/cron/README.md11
-rwxr-xr-xar/.local/bin/cron/checkup16
-rwxr-xr-xar/.local/bin/cron/crontog26
-rwxr-xr-xar/.local/bin/cron/mediaup34
-rwxr-xr-xar/.local/bin/cron/newsup15
-rwxr-xr-xar/.local/bin/cutvideo41
-rwxr-xr-xar/.local/bin/displayselect92
-rwxr-xr-xar/.local/bin/dmenubrowse44
-rwxr-xr-xar/.local/bin/dmenudelmusic44
-rwxr-xr-xar/.local/bin/dmenuhandler24
-rwxr-xr-xar/.local/bin/dmenuman5
-rwxr-xr-xar/.local/bin/dmenumountcifs19
-rwxr-xr-xar/.local/bin/dmenupass6
-rwxr-xr-xar/.local/bin/dmenurecord129
-rwxr-xr-xar/.local/bin/dmenusmbadd58
-rwxr-xr-xar/.local/bin/dmenusmbdel55
-rwxr-xr-xar/.local/bin/dmenuunicode18
-rwxr-xr-xar/.local/bin/dmenuupgrade115
-rwxr-xr-xar/.local/bin/dvdburn44
-rwxr-xr-xar/.local/bin/ecrypt35
-rwxr-xr-xar/.local/bin/emojiupdate65
-rwxr-xr-xar/.local/bin/ethwifi16
-rwxr-xr-xar/.local/bin/extractkeys142
-rwxr-xr-xar/.local/bin/fzffiles45
-rwxr-xr-xar/.local/bin/fzffns74
-rwxr-xr-xar/.local/bin/fzfpass89
-rwxr-xr-xar/.local/bin/getbib14
-rwxr-xr-xar/.local/bin/getcomproot9
-rwxr-xr-xar/.local/bin/getkeys6
-rwxr-xr-xar/.local/bin/gitfiles72
-rwxr-xr-xar/.local/bin/gitopenbranch24
-rwxr-xr-xar/.local/bin/gitstagedfiles11
-rwxr-xr-xar/.local/bin/gitupdate91
-rwxr-xr-xar/.local/bin/gpt26
-rwxr-xr-xar/.local/bin/gracefulkill25
-rwxr-xr-xar/.local/bin/ifinstalled12
-rwxr-xr-xar/.local/bin/iwaf96
-rwxr-xr-xar/.local/bin/lastnvim77
-rwxr-xr-xar/.local/bin/lfub24
-rwxr-xr-xar/.local/bin/linkhandler31
-rwxr-xr-xar/.local/bin/maimpick23
-rwxr-xr-xar/.local/bin/mbackup14
-rwxr-xr-xar/.local/bin/monitorbright24
-rwxr-xr-xar/.local/bin/mounter181
-rwxr-xr-xar/.local/bin/mpdmenu111
-rwxr-xr-xar/.local/bin/mpvplay189
-rwxr-xr-xar/.local/bin/noisereduce81
-rwxr-xr-xar/.local/bin/openfiles34
-rwxr-xr-xar/.local/bin/opensessions37
-rwxr-xr-xar/.local/bin/opentasktui27
-rwxr-xr-xar/.local/bin/opout16
-rwxr-xr-xar/.local/bin/otp54
-rwxr-xr-xar/.local/bin/ovpn22
-rwxr-xr-xar/.local/bin/pacerror27
-rwxr-xr-xar/.local/bin/partlabel85
-rwxr-xr-xar/.local/bin/passmenu238
-rwxr-xr-xar/.local/bin/pauseallmpv10
-rwxr-xr-xar/.local/bin/peertubetorrent9
-rwxr-xr-xar/.local/bin/podentr7
-rwxr-xr-xar/.local/bin/qndl108
-rwxr-xr-xar/.local/bin/queueandnotify14
-rwxr-xr-xar/.local/bin/rbackup185
-rwxr-xr-xar/.local/bin/remapd8
-rwxr-xr-xar/.local/bin/remaps66
-rwxr-xr-xar/.local/bin/restartnvim25
-rwxr-xr-xar/.local/bin/rgafiles120
-rwxr-xr-xar/.local/bin/rotdir12
-rwxr-xr-xar/.local/bin/rssadd18
-rwxr-xr-xar/.local/bin/schedule22
-rwxr-xr-xar/.local/bin/screenshotactivewindow37
-rwxr-xr-xar/.local/bin/sd22
-rwxr-xr-xar/.local/bin/sessionizer36
-rwxr-xr-xar/.local/bin/setbg71
-rwxr-xr-xar/.local/bin/setlock23
-rwxr-xr-xar/.local/bin/setmonitor35
-rwxr-xr-xar/.local/bin/shortcuts86
-rwxr-xr-xar/.local/bin/slider132
-rwxr-xr-xar/.local/bin/sshadd31
-rwxr-xr-xar/.local/bin/statusbar/sb-battery68
-rwxr-xr-xar/.local/bin/statusbar/sb-bghitness19
-rwxr-xr-xar/.local/bin/statusbar/sb-brightness11
-rwxr-xr-xar/.local/bin/statusbar/sb-clock77
-rwxr-xr-xar/.local/bin/statusbar/sb-cpu12
-rwxr-xr-xar/.local/bin/statusbar/sb-cpubars44
-rwxr-xr-xar/.local/bin/statusbar/sb-disk27
-rwxr-xr-xar/.local/bin/statusbar/sb-ecrypt12
-rwxr-xr-xar/.local/bin/statusbar/sb-forecast402
-rwxr-xr-xar/.local/bin/statusbar/sb-help-icon22
-rwxr-xr-xar/.local/bin/statusbar/sb-internet42
-rwxr-xr-xar/.local/bin/statusbar/sb-iplocate15
-rwxr-xr-xar/.local/bin/statusbar/sb-keyboard35
-rwxr-xr-xar/.local/bin/statusbar/sb-mailbox23
-rwxr-xr-xar/.local/bin/statusbar/sb-memory18
-rwxr-xr-xar/.local/bin/statusbar/sb-mpdup8
-rwxr-xr-xar/.local/bin/statusbar/sb-music78
-rwxr-xr-xar/.local/bin/statusbar/sb-nettraf29
-rwxr-xr-xar/.local/bin/statusbar/sb-news17
-rwxr-xr-xar/.local/bin/statusbar/sb-packages37
-rwxr-xr-xar/.local/bin/statusbar/sb-popupgrade9
-rwxr-xr-xar/.local/bin/statusbar/sb-price67
-rwxr-xr-xar/.local/bin/statusbar/sb-queues40
-rwxr-xr-xar/.local/bin/statusbar/sb-repos130
-rwxr-xr-xar/.local/bin/statusbar/sb-tasks84
-rwxr-xr-xar/.local/bin/statusbar/sb-torrent33
-rwxr-xr-xar/.local/bin/statusbar/sb-volume43
-rwxr-xr-xar/.local/bin/stw87
-rwxr-xr-xar/.local/bin/syncdot46
-rwxr-xr-xar/.local/bin/synctime22
-rwxr-xr-xar/.local/bin/sysact26
-rwxr-xr-xar/.local/bin/tablet39
-rwxr-xr-xar/.local/bin/tag49
-rwxr-xr-xar/.local/bin/task/taskwarrior-tui/annotate-with-new-note41
-rwxr-xr-xar/.local/bin/task/taskwarrior-tui/annotate-with-note41
-rwxr-xr-xar/.local/bin/task/taskwarrior-tui/cycle-priority35
-rwxr-xr-xar/.local/bin/task/taskwarrior-tui/cycle-tmux-projects31
-rwxr-xr-xar/.local/bin/task/taskwarrior-tui/decrease-priority15
-rwxr-xr-xar/.local/bin/task/taskwarrior-tui/git-issue-sync185
-rwxr-xr-xar/.local/bin/task/taskwarrior-tui/increase-priority60
-rwxr-xr-xar/.local/bin/task/taskwarrior-tui/lib-task-interop82
-rwxr-xr-xar/.local/bin/task/taskwarrior-tui/task-switch-context23
-rwxr-xr-xar/.local/bin/task/taskwarrior-tui/taskopen-annotation11
-rwxr-xr-xar/.local/bin/task/taskwarrior-tui/taskopen-line51
-rwxr-xr-xar/.local/bin/task/taskwarrior-tui/tasks-sync11
-rwxr-xr-xar/.local/bin/task/taskwarrior-tui/toggle-review-label13
-rwxr-xr-xar/.local/bin/td-toggle11
-rwxr-xr-xar/.local/bin/texclear8
-rwxr-xr-xar/.local/bin/timer14
-rwxr-xr-xar/.local/bin/timer_14
-rwxr-xr-xar/.local/bin/timezones21
-rwxr-xr-xar/.local/bin/tmuxcreate42
-rwxr-xr-xar/.local/bin/tmuxopen193
-rwxr-xr-xar/.local/bin/tmuxtogglebar6
-rwxr-xr-xar/.local/bin/tmuxtoggleterm11
-rwxr-xr-xar/.local/bin/tordone10
-rwxr-xr-xar/.local/bin/torwrap8
-rwxr-xr-xar/.local/bin/transadd29
-rwxr-xr-xar/.local/bin/tutorialvids26
-rwxr-xr-xar/.local/bin/unewsboat16
-rwxr-xr-xar/.local/bin/unix26
-rwxr-xr-xar/.local/bin/unmounter44
-rwxr-xr-xar/.local/bin/vipy42
-rwxr-xr-xar/.local/bin/wallset295
-rwxr-xr-xar/.local/bin/weath46
-rwxr-xr-xar/.local/bin/whereami24
-rwxr-xr-xar/.local/bin/xdg-terminal-exec3
-rwxr-xr-xar/.local/bin/xinputconf39
156 files changed, 8145 insertions, 0 deletions
diff --git a/ar/.local/bin/arkenfox-auto-update b/ar/.local/bin/arkenfox-auto-update
new file mode 100755
index 0000000..8e1ea7d
--- /dev/null
+++ b/ar/.local/bin/arkenfox-auto-update
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+# A wrapper for the arkenfox-updater that runs it on all pre-existing Arkenfox
+# user.js files on the machine.
+
+# On installation of LARBS, this file is copied to /usr/local/lib/ where it is
+# run by a pacman hook set up. The user should not have to run this manually.
+
+# Search for all Firefox and Librewolf profiles using Arkenfox.
+profiles="$(grep -sH "arkenfox user.js" \
+ /home/*/.librewolf/*.default/user.js \
+ /home/*/.mozilla/firefox/*.default/user.js)"
+
+IFS='
+'
+
+# Update each found profile.
+for profile in $profiles; do
+ userjs=${profile%%/user.js*}
+ user=$(stat -c '%U' "$userjs") || continue
+
+ su -l "$user" -c "arkenfox-updater -c -p $userjs -s"
+done
+
+# RTMIN: pkill
+# PROJECT: pkill
diff --git a/ar/.local/bin/bash-preexec b/ar/.local/bin/bash-preexec
new file mode 100755
index 0000000..7222568
--- /dev/null
+++ b/ar/.local/bin/bash-preexec
@@ -0,0 +1,380 @@
+#!/bin/bash
+
+# bash-preexec.sh -- Bash support for ZSH-like 'preexec' and 'precmd' functions.
+# https://github.com/rcaloras/bash-preexec
+#
+#
+# 'preexec' functions are executed before each interactive command is
+# executed, with the interactive command as its argument. The 'precmd'
+# function is executed before each prompt is displayed.
+#
+# Author: Ryan Caloras (ryan@bashhub.com)
+# Forked from Original Author: Glyph Lefkowitz
+#
+# V0.5.0
+#
+
+# General Usage:
+#
+# 1. Source this file at the end of your bash profile so as not to interfere
+# with anything else that's using PROMPT_COMMAND.
+#
+# 2. Add any precmd or preexec functions by appending them to their arrays:
+# e.g.
+# precmd_functions+=(my_precmd_function)
+# precmd_functions+=(some_other_precmd_function)
+#
+# preexec_functions+=(my_preexec_function)
+#
+# 3. Consider changing anything using the DEBUG trap or PROMPT_COMMAND
+# to use preexec and precmd instead. Preexisting usages will be
+# preserved, but doing so manually may be less surprising.
+#
+# Note: This module requires two Bash features which you must not otherwise be
+# using: the "DEBUG" trap, and the "PROMPT_COMMAND" variable. If you override
+# either of these after bash-preexec has been installed it will most likely break.
+
+# Tell shellcheck what kind of file this is.
+# shellcheck shell=bash
+
+# Make sure this is bash that's running and return otherwise.
+# Use POSIX syntax for this line:
+if [ -z "${BASH_VERSION-}" ]; then
+ return 1
+fi
+
+# We only support Bash 3.1+.
+# Note: BASH_VERSINFO is first available in Bash-2.0.
+if [[ -z "${BASH_VERSINFO-}" ]] || ((BASH_VERSINFO[0] < 3 || (BASH_VERSINFO[0] == 3 && BASH_VERSINFO[1] < 1))); then
+ return 1
+fi
+
+# Avoid duplicate inclusion
+if [[ -n "${bash_preexec_imported:-}" || -n "${__bp_imported:-}" ]]; then
+ return 0
+fi
+bash_preexec_imported="defined"
+
+# WARNING: This variable is no longer used and should not be relied upon.
+# Use ${bash_preexec_imported} instead.
+# shellcheck disable=SC2034
+__bp_imported="${bash_preexec_imported}"
+
+# Should be available to each precmd and preexec
+# functions, should they want it. $? and $_ are available as $? and $_, but
+# $PIPESTATUS is available only in a copy, $BP_PIPESTATUS.
+# TODO: Figure out how to restore PIPESTATUS before each precmd or preexec
+# function.
+__bp_last_ret_value="$?"
+BP_PIPESTATUS=("${PIPESTATUS[@]}")
+__bp_last_argument_prev_command="$_"
+
+__bp_inside_precmd=0
+__bp_inside_preexec=0
+
+# Initial PROMPT_COMMAND string that is removed from PROMPT_COMMAND post __bp_install
+__bp_install_string=$'__bp_trap_string="$(trap -p DEBUG)"\ntrap - DEBUG\n__bp_install'
+
+# Fails if any of the given variables are readonly
+# Reference https://stackoverflow.com/a/4441178
+__bp_require_not_readonly() {
+ local var
+ for var; do
+ if ! (unset "$var" 2>/dev/null); then
+ echo "bash-preexec requires write access to ${var}" >&2
+ return 1
+ fi
+ done
+}
+
+# Remove ignorespace and or replace ignoreboth from HISTCONTROL
+# so we can accurately invoke preexec with a command from our
+# history even if it starts with a space.
+__bp_adjust_histcontrol() {
+ local histcontrol
+ histcontrol="${HISTCONTROL:-}"
+ histcontrol="${histcontrol//ignorespace/}"
+ # Replace ignoreboth with ignoredups
+ if [[ "$histcontrol" == *"ignoreboth"* ]]; then
+ histcontrol="ignoredups:${histcontrol//ignoreboth/}"
+ fi
+ export HISTCONTROL="$histcontrol"
+}
+
+# This variable describes whether we are currently in "interactive mode";
+# i.e. whether this shell has just executed a prompt and is waiting for user
+# input. It documents whether the current command invoked by the trace hook is
+# run interactively by the user; it's set immediately after the prompt hook,
+# and unset as soon as the trace hook is run.
+__bp_preexec_interactive_mode=""
+
+# These arrays are used to add functions to be run before, or after, prompts.
+declare -a precmd_functions
+declare -a preexec_functions
+
+# Trims leading and trailing whitespace from $2 and writes it to the variable
+# name passed as $1
+__bp_trim_whitespace() {
+ local var=${1:?} text=${2:-}
+ text="${text#"${text%%[![:space:]]*}"}" # remove leading whitespace characters
+ text="${text%"${text##*[![:space:]]}"}" # remove trailing whitespace characters
+ printf -v "$var" '%s' "$text"
+}
+
+# Trims whitespace and removes any leading or trailing semicolons from $2 and
+# writes the resulting string to the variable name passed as $1. Used for
+# manipulating substrings in PROMPT_COMMAND
+__bp_sanitize_string() {
+ local var=${1:?} text=${2:-} sanitized
+ __bp_trim_whitespace sanitized "$text"
+ sanitized=${sanitized%;}
+ sanitized=${sanitized#;}
+ __bp_trim_whitespace sanitized "$sanitized"
+ printf -v "$var" '%s' "$sanitized"
+}
+
+# This function is installed as part of the PROMPT_COMMAND;
+# It sets a variable to indicate that the prompt was just displayed,
+# to allow the DEBUG trap to know that the next command is likely interactive.
+__bp_interactive_mode() {
+ __bp_preexec_interactive_mode="on"
+}
+
+# This function is installed as part of the PROMPT_COMMAND.
+# It will invoke any functions defined in the precmd_functions array.
+__bp_precmd_invoke_cmd() {
+ # Save the returned value from our last command, and from each process in
+ # its pipeline. Note: this MUST be the first thing done in this function.
+ # BP_PIPESTATUS may be unused, ignore
+ # shellcheck disable=SC2034
+
+ __bp_last_ret_value="$?" BP_PIPESTATUS=("${PIPESTATUS[@]}")
+
+ # Don't invoke precmds if we are inside an execution of an "original
+ # prompt command" by another precmd execution loop. This avoids infinite
+ # recursion.
+ if ((__bp_inside_precmd > 0)); then
+ return
+ fi
+ local __bp_inside_precmd=1
+
+ # Invoke every function defined in our function array.
+ local precmd_function
+ for precmd_function in "${precmd_functions[@]}"; do
+
+ # Only execute this function if it actually exists.
+ # Test existence of functions with: declare -[Ff]
+ if type -t "$precmd_function" 1>/dev/null; then
+ __bp_set_ret_value "$__bp_last_ret_value" "$__bp_last_argument_prev_command"
+ # Quote our function invocation to prevent issues with IFS
+ "$precmd_function"
+ fi
+ done
+
+ __bp_set_ret_value "$__bp_last_ret_value"
+}
+
+# Sets a return value in $?. We may want to get access to the $? variable in our
+# precmd functions. This is available for instance in zsh. We can simulate it in bash
+# by setting the value here.
+__bp_set_ret_value() {
+ return ${1:+"$1"}
+}
+
+__bp_in_prompt_command() {
+
+ local prompt_command_array IFS=$'\n;'
+ read -rd '' -a prompt_command_array <<<"${PROMPT_COMMAND[*]:-}"
+
+ local trimmed_arg
+ __bp_trim_whitespace trimmed_arg "${1:-}"
+
+ local command trimmed_command
+ for command in "${prompt_command_array[@]:-}"; do
+ __bp_trim_whitespace trimmed_command "$command"
+ if [[ "$trimmed_command" == "$trimmed_arg" ]]; then
+ return 0
+ fi
+ done
+
+ return 1
+}
+
+# This function is installed as the DEBUG trap. It is invoked before each
+# interactive prompt display. Its purpose is to inspect the current
+# environment to attempt to detect if the current command is being invoked
+# interactively, and invoke 'preexec' if so.
+__bp_preexec_invoke_exec() {
+
+ # Save the contents of $_ so that it can be restored later on.
+ # https://stackoverflow.com/questions/40944532/bash-preserve-in-a-debug-trap#40944702
+ __bp_last_argument_prev_command="${1:-}"
+ # Don't invoke preexecs if we are inside of another preexec.
+ if ((__bp_inside_preexec > 0)); then
+ return
+ fi
+ local __bp_inside_preexec=1
+
+ # Checks if the file descriptor is not standard out (i.e. '1')
+ # __bp_delay_install checks if we're in test. Needed for bats to run.
+ # Prevents preexec from being invoked for functions in PS1
+ if [[ ! -t 1 && -z "${__bp_delay_install:-}" ]]; then
+ return
+ fi
+
+ if [[ -n "${COMP_POINT:-}" || -n "${READLINE_POINT:-}" ]]; then
+ # We're in the middle of a completer or a keybinding set up by "bind
+ # -x". This obviously can't be an interactively issued command.
+ return
+ fi
+ if [[ -z "${__bp_preexec_interactive_mode:-}" ]]; then
+ # We're doing something related to displaying the prompt. Let the
+ # prompt set the title instead of me.
+ return
+ else
+ # If we're in a subshell, then the prompt won't be re-displayed to put
+ # us back into interactive mode, so let's not set the variable back.
+ # In other words, if you have a subshell like
+ # (sleep 1; sleep 2)
+ # You want to see the 'sleep 2' as a set_command_title as well.
+ if [[ 0 -eq "${BASH_SUBSHELL:-}" ]]; then
+ __bp_preexec_interactive_mode=""
+ fi
+ fi
+
+ if __bp_in_prompt_command "${BASH_COMMAND:-}"; then
+ # If we're executing something inside our prompt_command then we don't
+ # want to call preexec. Bash prior to 3.1 can't detect this at all :/
+ __bp_preexec_interactive_mode=""
+ return
+ fi
+
+ local this_command
+ this_command=$(
+ export LC_ALL=C
+ HISTTIMEFORMAT='' builtin history 1 | sed '1 s/^ *[0-9][0-9]*[* ] //'
+ )
+
+ # Sanity check to make sure we have something to invoke our function with.
+ if [[ -z "$this_command" ]]; then
+ return
+ fi
+
+ # Invoke every function defined in our function array.
+ local preexec_function
+ local preexec_function_ret_value
+ local preexec_ret_value=0
+ for preexec_function in "${preexec_functions[@]:-}"; do
+
+ # Only execute each function if it actually exists.
+ # Test existence of function with: declare -[fF]
+ if type -t "$preexec_function" 1>/dev/null; then
+ __bp_set_ret_value "${__bp_last_ret_value:-}"
+ # Quote our function invocation to prevent issues with IFS
+ "$preexec_function" "$this_command"
+ preexec_function_ret_value="$?"
+ if [[ "$preexec_function_ret_value" != 0 ]]; then
+ preexec_ret_value="$preexec_function_ret_value"
+ fi
+ fi
+ done
+
+ # Restore the last argument of the last executed command, and set the return
+ # value of the DEBUG trap to be the return code of the last preexec function
+ # to return an error.
+ # If `extdebug` is enabled a non-zero return value from any preexec function
+ # will cause the user's command not to execute.
+ # Run `shopt -s extdebug` to enable
+ __bp_set_ret_value "$preexec_ret_value" "$__bp_last_argument_prev_command"
+}
+
+__bp_install() {
+ # Exit if we already have this installed.
+ if [[ "${PROMPT_COMMAND[*]:-}" == *"__bp_precmd_invoke_cmd"* ]]; then
+ return 1
+ fi
+
+ trap '__bp_preexec_invoke_exec "$_"' DEBUG
+
+ # Preserve any prior DEBUG trap as a preexec function
+ local prior_trap
+ # we can't easily do this with variable expansion. Leaving as sed command.
+ # shellcheck disable=SC2001
+ prior_trap=$(sed "s/[^']*'\(.*\)'[^']*/\1/" <<<"${__bp_trap_string:-}")
+ unset __bp_trap_string
+ if [[ -n "$prior_trap" ]]; then
+ eval '__bp_original_debug_trap() {
+ '"$prior_trap"'
+ }'
+ preexec_functions+=(__bp_original_debug_trap)
+ fi
+
+ # Adjust our HISTCONTROL Variable if needed.
+ __bp_adjust_histcontrol
+
+ # Issue #25. Setting debug trap for subshells causes sessions to exit for
+ # backgrounded subshell commands (e.g. (pwd)& ). Believe this is a bug in Bash.
+ #
+ # Disabling this by default. It can be enabled by setting this variable.
+ if [[ -n "${__bp_enable_subshells:-}" ]]; then
+
+ # Set so debug trap will work be invoked in subshells.
+ set -o functrace >/dev/null 2>&1
+ shopt -s extdebug >/dev/null 2>&1
+ fi
+
+ local existing_prompt_command
+ # Remove setting our trap install string and sanitize the existing prompt command string
+ existing_prompt_command="${PROMPT_COMMAND:-}"
+ # Edge case of appending to PROMPT_COMMAND
+ existing_prompt_command="${existing_prompt_command//$__bp_install_string/:}" # no-op
+ existing_prompt_command="${existing_prompt_command//$'\n':$'\n'/$'\n'}" # remove known-token only
+ existing_prompt_command="${existing_prompt_command//$'\n':;/$'\n'}" # remove known-token only
+ __bp_sanitize_string existing_prompt_command "$existing_prompt_command"
+ if [[ "${existing_prompt_command:-:}" == ":" ]]; then
+ existing_prompt_command=
+ fi
+
+ # Install our hooks in PROMPT_COMMAND to allow our trap to know when we've
+ # actually entered something.
+ PROMPT_COMMAND='__bp_precmd_invoke_cmd'
+ PROMPT_COMMAND+=${existing_prompt_command:+$'\n'$existing_prompt_command}
+ if ((BASH_VERSINFO[0] > 5 || (BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 1))); then
+ PROMPT_COMMAND+=('__bp_interactive_mode')
+ else
+ # shellcheck disable=SC2179 # PROMPT_COMMAND is not an array in bash <= 5.0
+ PROMPT_COMMAND+=$'\n__bp_interactive_mode'
+ fi
+
+ # Add two functions to our arrays for convenience
+ # of definition.
+ precmd_functions+=(precmd)
+ preexec_functions+=(preexec)
+
+ # Invoke our two functions manually that were added to $PROMPT_COMMAND
+ __bp_precmd_invoke_cmd
+ __bp_interactive_mode
+}
+
+# Sets an installation string as part of our PROMPT_COMMAND to install
+# after our session has started. This allows bash-preexec to be included
+# at any point in our bash profile.
+__bp_install_after_session_init() {
+ # bash-preexec needs to modify these variables in order to work correctly
+ # if it can't, just stop the installation
+ __bp_require_not_readonly PROMPT_COMMAND HISTCONTROL HISTTIMEFORMAT || return
+
+ local sanitized_prompt_command
+ __bp_sanitize_string sanitized_prompt_command "${PROMPT_COMMAND:-}"
+ if [[ -n "$sanitized_prompt_command" ]]; then
+ # shellcheck disable=SC2178 # PROMPT_COMMAND is not an array in bash <= 5.0
+ PROMPT_COMMAND=${sanitized_prompt_command}$'\n'
+ fi
+ # shellcheck disable=SC2179 # PROMPT_COMMAND is not an array in bash <= 5.0
+ PROMPT_COMMAND+=${__bp_install_string}
+}
+
+# Run our install so long as we're not delaying it.
+if [[ -z "${__bp_delay_install:-}" ]]; then
+ __bp_install_after_session_init
+fi
diff --git a/ar/.local/bin/bookmarks b/ar/.local/bin/bookmarks
new file mode 100755
index 0000000..5e81c07
--- /dev/null
+++ b/ar/.local/bin/bookmarks
@@ -0,0 +1,164 @@
+#!/bin/sh
+
+usage() {
+ echo "Open bookmarks, URLs, or browser history in a program."
+ echo ""
+ echo "Usage: ${0##*/} [OPTIONS]"
+ echo ""
+ echo "Options:"
+ echo " -h : Show this message"
+ echo " -b : Open a browser bookmark"
+ echo " -c : Copy a URL from snippets/URLs to the clipboard"
+ echo " -o : Get a URL from snippets/URLs and open it in a new browser window"
+ echo " -p : Get a URL from snippets/URLs and open it in a private browser window"
+ echo " -s : Open browser history"
+ echo " -t : Get a URL from snippets/URLs and type it using xdotool"
+ echo " -v : Open a browser bookmark in private browser window"
+ echo ""
+ echo "Programs:"
+ echo " browser : System default browser"
+ echo " links : A text WWW browser, similar to lynx"
+ echo " lynx : A text browser for World Wide Web"
+ echo ""
+ echo "Examples:"
+ echo " ${0##*/} -b # Opens a browser bookmark in a program"
+ echo " ${0##*/} -c # Copies a URL from snippets/URLs to the clipboard"
+ echo " ${0##*/} -o # Opens a URL from snippets/URLs in a new browser window"
+ echo " ${0##*/} -p # Opens a URL in a private browser window"
+ echo " ${0##*/} -s # Opens browser history in a program"
+ echo " ${0##*/} -v # Opens browser boomark in private browser window"
+}
+
+open() {
+ available_tools=""
+ command -v xdg-open 2>/dev/null | grep -v "alias" -q && available_tools="$available_tools xdg-open"
+ command -v open 2>/dev/null | grep -v "alias" -q && available_tools="$available_tools open"
+ command -v links 2>/dev/null | grep -v "alias" -q && available_tools="$available_tools links"
+ command -v lynx 2>/dev/null | grep -v "alias" -q && available_tools="$available_tools lynx"
+ available_tools=$(printf "%s" "$available_tools" | awk '{$1=$1; print}' | tr ' ' '\n')
+ if [ -z "$available_tools" ]; then
+ printf "No browser found\n" >&2
+ exit 1
+ fi
+
+ opentool=$(printf "%s\n" "$available_tools" | dmenu -i -p "Choose an open tool:")
+
+ # Set the selected tool to the variable 'open'
+ case "$opentool" in
+ xdg-open) xdg-open "$1" ;;
+ open)
+ case "$(uname -s)" in
+ Darwin)
+ open "$1"
+ ;;
+ *)
+ xdg-open "$1"
+ ;;
+ esac
+ ;;
+ links) setsid -f "$TERMINAL" -e links "$1" ;;
+ lynx) setsid -f "$TERMINAL" -e lynx "$1" ;;
+ *) echo "Invalid selection" >&2 && exit 1 ;;
+ esac
+}
+
+browsercheck() {
+ if pidof "$BROWSER" >/dev/null; then
+ notify-send "❌ Failed to $1 from '$BROWSER'." "'$BROWSER' is locked. Check if '$BROWSER' is running."
+ exit 1
+ fi
+}
+
+openinbrowser() {
+ # Extract only the default part of the profile name
+ case $BROWSER in
+ firefox)
+ profiles_ini_path="$HOME/.mozilla/firefox/profiles.ini"
+ profile=$(awk '/\[Install/ {found=1} found && /^Default=/ {split($0, arr, "."); print arr[2]; exit}' "$profiles_ini_path")
+ profile_dir=$(find ~/.mozilla/firefox -type d -name "*.$profile*" | head -n 1)
+ ;;
+ librewolf)
+ profiles_ini_path="$HOME/.librewolf/profiles.ini"
+ profile=$(awk '/\[Install/ {found=1} found && /^Default=/ {split($0, arr, "."); print arr[2]; exit}' "$profiles_ini_path")
+ profile_dir=$(find ~/.librewolf -type d -name "*.$profile*" | head -n 1)
+ ;;
+ *) echo "Default browser path is needed." && exit ;;
+ esac
+
+ db_path="$profile_dir/places.sqlite"
+ tmp_dir="${XDG_CACHE_HOME:-$HOME/.cache}/mozilla/firefox/$USER.$profile"
+ tmp_file="$tmp_dir/$1.sqlite"
+ mkdir -p "$tmp_dir"
+ cp -f "$db_path" "$tmp_file"
+
+ type dmenu >/dev/null 2>&1 &&
+ selection="dmenu -i -l 20 -p \"Choose a $1 to open:\"" ||
+ selection="fzf-tmux --reverse --cycle --ansi --delimiter='|' --with-nth=1..-2"
+
+ case "$1" in
+ *bookmark*)
+ sqlite_query="
+ SELECT b.title || ' | ' || p.url AS bookmark
+ FROM moz_bookmarks b
+ JOIN moz_places p ON b.fk = p.id
+ WHERE b.type = 1 AND p.url LIKE 'http%' AND b.title NOT NULL
+ ORDER BY b.dateAdded DESC;
+ "
+ ;;
+ *history*)
+ cols=$((${COLUMNS:-90} / 3))
+ sqlite_query="
+ SELECT substr(p.title, 1, $cols) || ' | ' || p.url
+ FROM moz_places p
+ JOIN moz_historyvisits hv ON hv.place_id = p.id
+ ORDER BY hv.visit_date DESC LIMIT 100;
+ "
+ ;;
+ esac
+ choice=$(sqlite3 "$tmp_file" "$sqlite_query" | eval "$selection" | cut -d'|' -f2 | sed 's|.*\(https://\)|\1|' | xargs)
+ if [ -n "$choice" ]; then
+ if echo "$1" | grep -q "private"; then
+ "$BROWSER" --private-window "$choice"
+ else
+ open "$choice"
+ fi
+ else
+ exit
+ fi
+}
+
+geturls() {
+ [ -f ~/.local/share/thesiah/urls ] &&
+ URLS=$(cat ~/.local/share/thesiah/snippets ~/.local/share/thesiah/urls) ||
+ URLS=$(cat ~/.local/share/thesiah/snippets)
+ CHOICE=$(echo "$URLS" | grep -v -e '^#' -e '^$' | awk -F'"' '{print $2}' | dmenu -i -l 50 -p "Choose a URL $1:")
+ [ -z "$CHOICE" ] && exit
+ URL=$(echo "$URLS" | grep -v -e '^#' -e '^$' | grep "\"$CHOICE\"" | awk '{print $1}')
+}
+
+copytoclipboard() {
+ if command -v xclip >/dev/null 2>&1; then
+ printf "%s" "$URL" | xclip -selection clipboard
+ elif command -v xsel >/dev/null 2>&1; then
+ printf "%s" "$URL" | xsel --clipboard --input
+ else
+ echo "Clipboard utility not found. Install xclip or xsel." >&2
+ exit 1
+ fi
+ notify-send "'$CHOICE' copied in clipbaord" "$URL"
+}
+
+[ $# -eq 0 ] && usage && exit 1
+
+while getopts "bchopstv" opt; do
+ case $opt in
+ b) browsercheck "open bookmark" && openinbrowser "bookmark" ;;
+ c) geturls "to copy" && copytoclipboard ;;
+ o) geturls "to open in $BROWSER" && "$BROWSER" --new-window "$URL" ;;
+ p) geturls "to open in private $BROWSER" && "$BROWSER" --private-window "$URL" ;;
+ s) browsercheck "open history" && openinbrowser "history" ;;
+ t) geturls "to type under cursor" && xdotool type "$URL" ;;
+ v) browsercheck "open bookmark" && openinbrowser "private bookmark" ;;
+ h | *) usage && exit 0 ;;
+ esac
+done
diff --git a/ar/.local/bin/booksplit b/ar/.local/bin/booksplit
new file mode 100755
index 0000000..bdf637a
--- /dev/null
+++ b/ar/.local/bin/booksplit
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+# Requires ffmpeg
+
+[ ! -f "$2" ] && printf "The first file should be the audio, the second should be the timecodes.\\n" && exit
+
+echo "Enter the album/book title:"
+read -r booktitle
+echo "Enter the artist/author:"
+read -r author
+echo "Enter the publication year:"
+read -r year
+
+inputaudio="$1"
+ext="${1##*.}"
+
+# Get a safe file name from the book.
+escbook="$(echo "$booktitle" | iconv -c -f UTF-8 -t ASCII//TRANSLIT | tr -d '[:punct:]' | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | sed "s/-\+/-/g;s/\(^-\|-\$\)//g")"
+
+! mkdir -p "$escbook" &&
+ echo "Do you have write access in this directory?" &&
+ exit 1
+
+# Get the total number of tracks from the number of lines.
+total="$(wc -l <"$2")"
+
+cmd="ffmpeg -i \"$inputaudio\" -nostdin -y"
+
+while read -r x; do
+ end="$(echo "$x" | cut -d' ' -f1)"
+ file="$escbook/$(printf "%.2d" "$track")-$esctitle.$ext"
+ if [ -n "$start" ]; then
+ cmd="$cmd -metadata artist=\"$author\" -metadata title=\"$title\" -metadata album=\"$booktitle\" -metadata year=\"$year\" -metadata track=\"$track\" -metadata total=\"$total\" -ss \"$start\" -to \"$end\" -vn -c:a copy \"$file\" "
+ fi
+ title="$(echo "$x" | cut -d' ' -f2-)"
+ esctitle="$(echo "$title" | iconv -c -f UTF-8 -t ASCII//TRANSLIT | tr -d '[:punct:]' | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | sed "s/-\+/-/g;s/\(^-\|-\$\)//g")"
+ track="$((track + 1))"
+ start="$end"
+done <"$2"
+
+# Last track must be added out of the loop.
+file="$escbook/$(printf "%.2d" "$track")-$esctitle.$ext"
+cmd="$cmd -metadata artist=\"$author\" -metadata title=\"$title\" -metadata album=\"$booktitle\" -metadata year=\"$year\" -metadata track=\"$track\" -ss \"$start\" -vn -c copy \"$file\""
+
+eval "$cmd"
diff --git a/ar/.local/bin/browse b/ar/.local/bin/browse
new file mode 100755
index 0000000..56c36d9
--- /dev/null
+++ b/ar/.local/bin/browse
@@ -0,0 +1,85 @@
+#!/bin/sh
+
+# Usage message
+usage() {
+ echo "Searches query in a terminal or browser"
+ echo ""
+ echo "Usage: ${0##*/} [-d | --ddgr | ddgr] [-h | --help | help] [<search_engine>] <search_query>"
+ echo ""
+ echo "Options:"
+ echo " -d, --ddgr, ddgr : Use ddgr to search and open the result in a terminal"
+ echo " -h, --help, help : Display this help message"
+ echo " <search_engine> : (Optional) Search engine to use (google, bing, yahoo, duckduckgo, youtube)"
+ echo " <search_query> : The search term or query to use"
+}
+
+# Set default values
+SEARCH_TOOL="web"
+SEARCH_ENGINE="searx"
+
+# Determine the open command based on the operating system
+case "$(uname -s)" in
+Darwin)
+ open_cmd='open'
+ ;;
+*)
+ open_cmd='xdg-open'
+ ;;
+esac
+
+# Check the first argument for flags or help using case
+case "$1" in
+-d | --ddgr | ddgr)
+ # Check if ddgr is installed (only needed for ddgr options)
+ if ! command -v ddgr >/dev/null 2>&1; then
+ echo "Error: ddgr is not installed." >&2
+ exit 1
+ fi
+ SEARCH_TOOL="ddgr"
+ shift # Remove this argument from the list
+ ;;
+-h | --help | help)
+ usage && exit 0
+ ;;
+bing | duckduckgo | google | yahoo | youtube)
+ SEARCH_ENGINE="$1"
+ shift # Remove the search engine from the list
+ ;;
+esac
+
+# Store the remaining arguments as the search query
+SEARCH_QUERY="$*"
+
+# Ensure a search query is provided; if not, show usage
+[ -z "$SEARCH_QUERY" ] && usage && exit 1
+
+# Execute the corresponding search tool using case
+case $SEARCH_TOOL in
+ddgr)
+ # Run DuckDuckGo search in the terminal
+ setsid -f "$TERMINAL" -e ddgr "$SEARCH_QUERY"
+ ;;
+web)
+ # Construct the URL based on the search engine
+ case "$SEARCH_ENGINE" in
+ bing | google | yahoo | youtube)
+ base_url="https://www.${SEARCH_ENGINE}.com/search?q="
+ ;;
+ duckduckgo)
+ base_url="https://duckduckgo.com/?q="
+ ;;
+ searx | *)
+ base_url="https://www.searx.thesiah.xyz/search?q="
+ ;;
+ esac
+
+ # Encode the search query
+ SEARCH_QUERY_ENCODED=$(echo "$SEARCH_QUERY" | sed 's/ /+/g')
+
+ # Open the search URL in the default browser
+ $open_cmd "${base_url}${SEARCH_QUERY_ENCODED}"
+ ;;
+*)
+ usage && exit 1
+ ;;
+esac
diff --git a/ar/.local/bin/browserprofile b/ar/.local/bin/browserprofile
new file mode 100755
index 0000000..ca49f02
--- /dev/null
+++ b/ar/.local/bin/browserprofile
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+# Define the profile paths
+WORK_PROFILE="si.work"
+HOME_PROFILE="si.default"
+
+usage() {
+ echo "Update the default profile in profiles.ini for Firefox or Librewolf."
+ echo ""
+ echo "Usage: ${0##*/} <browser> [<profile_type>]"
+ echo ""
+ echo "Options:"
+ echo " -h : Show this help message."
+ echo " <browser> : The browser to configure."
+ echo " Accepted values:"
+ echo " -f/firefox"
+ echo " -l/librewolf"
+ echo " <profile_type> : (Optional) If not specified, the default profile will be used."
+ echo " Accepted values:"
+ echo " work: Sets the work profile ($WORK_PROFILE)"
+ echo " default: Sets the home profile ($HOME_PROFILE)"
+ echo ""
+ echo "Examples:"
+ echo " ${0##*/} -f -w # Set the work profile for Firefox"
+ echo " ${0##*/} -l -d # Set the default profile for Librewolf"
+ echo " ${0##*/} -f -d # Set the default profile for Firefox"
+}
+
+update_profiles_ini() {
+ case "$1" in
+ -f | --firefox | firefox) profiles_ini_path="$HOME/.mozilla/firefox/profiles.ini" ;;
+ -l | --librewolf | librewolf) profiles_ini_path="$HOME/.librewolf/profiles.ini" ;;
+ *)
+ echo "Invalid browser type. Please use 'firefox' or 'librewolf'."
+ return 1
+ ;;
+ esac
+
+ profile_to_set=$2
+
+ # Backup current profiles.ini
+ cp "$profiles_ini_path" "$profiles_ini_path.bak"
+
+ # Update the profiles.ini
+ awk -v profile="$profile_to_set" '
+ /^\[Install/ {
+ print
+ found=1
+ next
+ }
+ found && /^Default=/ {
+ sub(/=.*/, "=" profile)
+ print
+ next
+ }
+ {
+ print
+ }' "$profiles_ini_path" >"$profiles_ini_path.tmp" && mv "$profiles_ini_path.tmp" "$profiles_ini_path"
+
+ echo "Updated profiles.ini to use profile: $profile_to_set"
+}
+
+# Main function
+update_profile() {
+ browser=$1
+ profile_type=$2
+
+ # Check if a browser is provided
+ if [ -z "$browser" ]; then
+ usage && exit 1
+ fi
+
+ # Convert profile_type to lowercase for case-insensitive comparison
+ if [ -n "$profile_type" ]; then
+ profile_type=$(echo "$profile_type" | tr '[:upper:]' '[:lower:]')
+ else
+ # Set to "default" if profile_type is not given
+ profile_type="default"
+ fi
+
+ # Set the profile based on the input
+ case "$profile_type" in
+ -w | --work | work)
+ update_profiles_ini "$browser" "$WORK_PROFILE"
+ ;;
+ -d | --default | default)
+ update_profiles_ini "$browser" "$HOME_PROFILE"
+ ;;
+ *)
+ echo "Invalid profile type. Please use 'work' or 'default'."
+ return 1
+ ;;
+ esac
+}
+
+# Check for help flag
+if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
+ usage && exit 0
+fi
+
+# Execute the main function with arguments passed to the script
+update_profile "$1" "$2"
diff --git a/ar/.local/bin/clonerepo b/ar/.local/bin/clonerepo
new file mode 100755
index 0000000..cc1e23f
--- /dev/null
+++ b/ar/.local/bin/clonerepo
@@ -0,0 +1,61 @@
+#!/bin/sh
+
+usage() {
+ echo "Clone a git repo in XDG_PUBLICSHARE_DIR if it's set or ~/Public"
+ echo ""
+ echo "Usage: ${0##*/} [-b <branch>] [-d <destination>] [-n <name>] <url>"
+ echo ""
+ echo "Options:"
+ echo " -h : Show this help message"
+ echo " -b <branch> : Specify the branch to clone"
+ echo " -d <destination> : Specify the destination of directory"
+ echo " -n <name> : Specify the directory name to clone into"
+ echo ""
+ echo "Example:"
+ echo " ${0##*/} -b master <url> # Clone master branch"
+ echo " ${0##*/} -d ~/.local/bin <url> # Clone the url into ~/.local/bin"
+ echo " ${0##*/} -n myrepo <url> # Clone url named with myrepo"
+ exit 1
+}
+
+# Default values
+path="${XDG_PUBLICSHARE_DIR:-$HOME/Public}"
+
+# Parse options
+while getopts ":b:d:n:" opt; do
+ case $opt in
+ b) branch="$OPTARG" ;;
+ d) path="$OPTARG" ;;
+ n) dirname="$OPTARG" ;;
+ *) usage ;;
+ esac
+done
+
+shift $((OPTIND - 1))
+
+[ -z "$1" ] && usage
+
+repo="$1"
+
+# Validate the URL (supports HTTPS and SSH Git URLs)
+if ! echo "$repo" | grep -Eq '^(https://github\.com/[^/]+/[^/]+(\.git)?|git@github\.com:[^/]+/[^/]+(\.git)?)$'; then
+ echo "Error: Invalid Git URL."
+ exit 1
+fi
+
+# Extract the base URL for the repository (removes any trailing .git)
+url="$(echo "$repo" | grep -Eo 'https://github.com/[^/]+/[^/]+' | sed 's/\.git$//')"
+
+# Determine the directory name for cloning
+if [ -n "$dirname" ]; then
+ dest="$path/$dirname"
+else
+ dest="$path/$(basename "$url")"
+fi
+
+# Determine the clone command
+if [ -n "$branch" ]; then
+ git clone --branch "$branch" "$url" "$dest"
+else
+ git clone "$url" "$dest"
+fi
diff --git a/ar/.local/bin/compiler b/ar/.local/bin/compiler
new file mode 100755
index 0000000..32004c2
--- /dev/null
+++ b/ar/.local/bin/compiler
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+# This script will compile or run another finishing operation on a document. I
+# have this script run via Vim.
+# # tex files: Compiles to pdf, including bibliography if necessary
+# md files: Compiles to pdf via pandoc
+# rmd files: Compiles via R Markdown
+# c files: Compiles via whatever compiler is set to cc. Usually gcc.
+# Use make if Makefile exists.
+# py files: runs via python command
+# go files: compiles and runs with "go run"
+# config.h files: (For suckless utils) recompiles and installs program.
+# all others: run `sent` to show a presentation
+
+file=$(readlink -f "$1")
+dir=$(dirname "$file")
+base="${file%.*}"
+
+cd "$dir" || exit
+
+Ifinstalled() {
+ command -v "$1" >/dev/null || { notify-send "📦 <b>$1</b> must be installed for this function." && exit 1; }
+}
+
+textype() {
+ command="pdflatex"
+ errorfmt="-file-line-error"
+ # ( sed 5q "$file" | grep -i -q 'xelatex' ) && command="xelatex"
+ secdir="$(dirname "$dir")"
+ cd "$secdir"
+ if [ -f "${secdir}/Notes.tex" ]; then
+ echo "${secdir}/Notes.tex"
+ $command $errorfmt --output-directory="$secdir" "${secdir}/Notes.tex"
+ exit
+ fi
+ $command $errorfmt --output-directory="$dir" "$base"
+ grep -i addbibresource "$file" >/dev/null &&
+ biber --input-directory "$dir" "$base" &&
+ $command $errorfmt --output-directory="$dir" "$base" &&
+ $command $errorfmt --output-directory="$dir" "$base"
+}
+
+pandoccmd() {
+ # Ifinstalled pdflatex && pandoc -V geometry:margin=4cm -f markdown-implicit_figures "$1" -o "${2}.pdf"
+ Ifinstalled groff && pandoc "${1}" -t ms --pdf-engine-opt=-p -o "${2}.pdf"
+}
+
+pandocorg() { pandoccmd "$file" "$base"; }
+
+compilec() {
+ if [ -f "${dir}/Makefile" ]; then
+ make
+ else
+ cc "$file" -o "$base" && "$base"
+ fi
+}
+
+case "$file" in
+*\.[0-9]) preconv "$file" | refer -PS -e | groff -mandoc -T pdf >"$base".pdf ;;
+*\.apl) apl -f "$file" ;;
+*\.c) compilec ;;
+*config.h) make && sudo make install ;;
+*\.cpp) g++ "$file" -o "$base" && "$base" ;;
+*\.cs) mcs "$file" && mono "$base".exe ;;
+*\.docx | *\.doc)
+ Ifinstalled libreoffice && lowriter --convert-to pdf "$file" && exit
+ Ifinstalled pandoc && pandoccmd "$file" "$base" && exit
+ ;;
+*\.dot | *\.gv) dot -Tsvg "$file" | convert svg:- "$base".eps ;;
+*\.h) compilec ;;
+*\.html) refreshbrowser ;;
+*\.fnl)
+ echo "fennel --compile '$file' > '$base'.lua"
+ fennel --compile "$file" >"$base".lua
+ ;;
+*\.go) go run "$file" ;;
+*\.java) javac "$file" && echo "${base##*/}" | xargs java ;;
+*\.js) node "$file" ;;
+*\.m) octave "$file" ;;
+*\.md) [ -x "$(command -v lowdown)" ] &&
+ lowdown --parse-no-intraemph "${file}" -Tms | groff -mpdfmark -ms -kept -T pdf >"${base}.pdf" ||
+ [ -x "$(command -v groffdown)" ] &&
+ groffdown -i "${file}" | groff -T pdf >"${base}.pdf" ||
+ pandoc -t ms --highlight-style="kate" -s -o "${base}.pdf" "${file}" ;;
+*\.me) groff -Gktes -b -w w -me -T ps "$file" | ps2pdf - >"$base".pdf ;;
+*\.mm) groff -Gktes -b -w w -mm -mpic -T ps "$file" | ps2pdf - >"$base".pdf ;;
+*\.mom) pdfroff -pktes -b -wall -mom -mpdfmark "$file" >"$base".pdf ;;
+*\.ms | *\.groff) preconv "$file" | groff -Tpdf -ktesp -G -ms >"$base".pdf ;;
+*\.org) Ifinstalled pandoc && pandocorg "$file" "$base" && exit ;;
+*\.present) groff -p -e -t -mm -mpresent "$file" | presentps -l | ps2pdf - >"$base".pdf ;;
+*\.ps) ps2pdf "$file" ;;
+*\.py) python "$file" ;;
+*\.[rR]md) Rscript -e "rmarkdown::render('$file', quiet=TRUE)" ;;
+*\.r) R -f "$file" ;;
+*\.rkt) racket "$file" ;;
+*\.rs) cargo build ;;
+*\.sass) sassc -a "$file" "$base".css ;;
+*\.scad) openscad -o "$base".stl "$file" ;;
+*\.sent) setsid -f sent "$file" 2>/dev/null & ;;
+*\.tex) textype "$file" ;;
+*\.tcl) tclsh "$file" ;;
+*\.vim*) vint "$file" ;;
+*) chmod +x "$file" && sed 1q "$file" | grep "^#!/" | sed "s/^#!//" | xargs -r -I % "$file" ;;
+esac
diff --git a/ar/.local/bin/concatvideo b/ar/.local/bin/concatvideo
new file mode 100755
index 0000000..93a2060
--- /dev/null
+++ b/ar/.local/bin/concatvideo
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+usage() {
+ echo "Combine multiple video files that share the same pattern and extension"
+ echo "into a single video file."
+ echo ""
+ echo "Usage: ${0##*/} <base filename>"
+ echo ""
+ echo "Arguments:"
+ echo " <base filename>: The name of one of the video files to use as a pattern."
+ echo " For example, if your files are named video_cut1.mp4,"
+ echo " video_cut2.mp4, you can provide video_cut1.mp4."
+ echo ""
+ echo "Example:"
+ echo " ${0##*/} video_cut.mp4"
+ echo " This will combine all files matching video_cut*.mp4 into a single file."
+ exit 1
+}
+
+# Check if the correct number of arguments are provided
+if [ $# -ne 1 ]; then
+ usage
+fi
+
+# Extract the pattern and extension from the input filename
+input_file="$1"
+pattern="${input_file%.*}" # This removes the extension
+extension="${input_file##*.}" # This extracts the extension
+
+# Find all video files matching the generated pattern and extension
+video_files=$(ls "${pattern}"*."${extension}" 2>/dev/null)
+
+# Check if any files are found
+if [ -z "$video_files" ]; then
+ echo "No video files found with the pattern '${pattern}*.${extension}'."
+ exit 1
+fi
+
+# Create a temporary file list for ffmpeg
+file_list=$(mktemp)
+
+# Populate the file list with the found video files, properly quoting each full path
+for video in "${pattern}"*."${extension}"; do
+ full_path=$(realpath "$video")
+ echo "file '$full_path'" >>"$file_list"
+done
+
+# Combine the videos into a single file using ffmpeg
+output_file="${pattern}_combine.${extension}"
+ffmpeg -f concat -safe 0 -i "$file_list" -c copy "$output_file" 2>/dev/null
+
+# Clean up the temporary file
+cat "$file_list"
+rm -f "$file_list"
+
+echo "All videos combined into '$output_file'."
diff --git a/ar/.local/bin/createsh b/ar/.local/bin/createsh
new file mode 100755
index 0000000..53c2a32
--- /dev/null
+++ b/ar/.local/bin/createsh
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+# Function to display help
+usage() {
+ echo "Generate a shell script in sh, bash, or zsh"
+ echo ""
+ echo "Usage: ${0##*/} [OPTION] [FILENAME]"
+ echo ""
+ echo "Options:"
+ echo " -h, --help : Show this help message."
+ echo " -b, --bash : Create a Bash script (bash)."
+ echo " -d, --dash : Create a POSIX-compliant shell script (sh)."
+ echo " -z, --zsh : Create a Zsh script (zsh)."
+ echo ""
+ echo "Example:"
+ echo " ${0##*/} -d sambacreate # Create a POSIX-compliant shell script named 'sambacreate'"
+}
+
+# Default shell to POSIX if no option is provided
+shell="sh"
+script_name=""
+
+# Parse options
+while [ "$1" ]; do
+ case "$1" in
+ -d | --dash)
+ shell="sh"
+ ;;
+ -b | --bash)
+ shell="bash"
+ ;;
+ -z | --zsh)
+ shell="zsh"
+ ;;
+ -h | --help)
+ usage
+ exit 0
+ ;;
+ -*)
+ echo "Error: Invalid option '$1'. Use -h or --help for usage information."
+ exit 1
+ ;;
+ *)
+ if [ -z "$script_name" ]; then
+ script_name="$1"
+ else
+ echo "Error: Multiple filenames provided. Use -h or --help for usage information."
+ exit 1
+ fi
+ ;;
+ esac
+ shift
+done
+
+# Validate script name
+if [ -z "$script_name" ]; then
+ echo "Error: No filename provided. Use -h or --help for usage information."
+ exit 1
+fi
+
+# Check if the script already exists
+script_path="$HOME/.local/bin"
+if [ -f "$script_path/$script_name" ]; then
+ echo "Error: File '$script_path/$script_name' already exists."
+ exit 1
+fi
+
+# Create the new script file
+mkdir -p "$script_path"
+
+echo "#!/bin/$shell
+
+# Help function
+usage() {
+ echo \"Usage: \${0##*/} [OPTION] ????\"
+ echo \"????\"
+ echo \"\"
+ echo \"Options:\"
+ echo \" -h, --help : Show this help message\"
+ echo \"\"
+ echo \"Example:\"
+ echo \" \${0##*/} -? ???? # ????\"
+}
+
+# Main function
+$script_name() {
+ case \"\$1\" in
+ -h | --help)
+ usage
+ exit 0
+ ;;
+ *)
+ echo \"\${0##*/}\"
+ ;;
+ esac
+}
+
+# Call the main function with arguments
+$script_name \"\$@\"" >"$script_path/$script_name"
+
+# Make the script executable
+chmod +x "$script_path/$script_name"
+
+echo "'$script_name' created successfully at $script_path."
diff --git a/ar/.local/bin/cron/README.md b/ar/.local/bin/cron/README.md
new file mode 100644
index 0000000..fa0c354
--- /dev/null
+++ b/ar/.local/bin/cron/README.md
@@ -0,0 +1,11 @@
+# Important Note
+
+These cronjobs have components that require information about your current display to display notifications correctly.
+
+When you add them as cronjobs, I recommend you precede the command with commands as those below:
+
+```
+export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u $USER)/bus; export DISPLAY=:0; . $HOME/.zprofile; then_command_goes_here
+```
+
+This ensures that notifications will display, xdotool commands will function and environmental variables will work as well.
diff --git a/ar/.local/bin/cron/checkup b/ar/.local/bin/cron/checkup
new file mode 100755
index 0000000..dfdfc4a
--- /dev/null
+++ b/ar/.local/bin/cron/checkup
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+# Syncs repositories and downloads updates, meant to be run as a cronjob.
+
+notify-send "📦 Repository Sync" "Checking for package updates..."
+
+sudo pacman -Syyuw --noconfirm || notify-send "⛔ Error downloading updates.
+
+Check your internet connection, if pacman is already running, or run update manually to see errors."
+pkill -RTMIN+16 "${STATUSBAR:-dwmblocks}"
+
+if pacman -Qu | grep -v "\[ignored\]"; then
+ notify-send "🎁 Repository Sync" "Updates available. Click statusbar icon (📦) or run sb-popupgrade in terminal for update."
+else
+ notify-send "📦 Repository Sync" "Sync complete. No new packages for update."
+fi
diff --git a/ar/.local/bin/cron/crontog b/ar/.local/bin/cron/crontog
new file mode 100755
index 0000000..9ebab9c
--- /dev/null
+++ b/ar/.local/bin/cron/crontog
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+# Toggles all cronjobs off/on.
+# Stores disabled crontabs in ~/.config/crons until restored.
+
+CRON_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/crons"
+
+# Check if there are any active cronjobs
+if crontab -l 2>/dev/null | grep -q '^[^#[:space:]]'; then
+ # If active cronjobs are found, save and disable them
+ ln -sf "${XDG_DOTFILES_DIR:-${HOME}/.dotfiles}/$(whereami)/.config/crons" "${XDG_CONFIG_HOME:-${HOME}/.config}/crons"
+ crontab -r
+ notify-send "⏰ Cronjobs saved and disabled."
+else
+ # If no active cronjobs are found, try re-enabling from saved file
+ if [ -f "$CRON_FILE" ]; then
+ crontab - <"$CRON_FILE"
+ rm "$CRON_FILE"
+ notify-send "🕓 Cronjobs re-enabled."
+ else
+ notify-send "🕰️ No saved cronjobs to re-enable."
+ fi
+fi
+
+# Notify status bar to update
+pkill -RTMIN+3 "${STATUSBAR:-dwmblocks}"
diff --git a/ar/.local/bin/cron/mediaup b/ar/.local/bin/cron/mediaup
new file mode 100755
index 0000000..85935a2
--- /dev/null
+++ b/ar/.local/bin/cron/mediaup
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+timestamp_file="${HOME}/.cache/ytdlpupdate"
+current_time=$(date +%s)
+
+# Create cache directory if it doesn't exist
+mkdir -p "${HOME}/.cache"
+
+# Check if the timestamp file exists and is less than 24 hours old
+if [ -f "$timestamp_file" ] && [ "$(cat "$timestamp_file")" -gt "$((current_time - 86400))" ]; then
+ exit 0
+fi
+
+# Check if pipx is available, install if not
+if ! command -v pipx >/dev/null 2>&1; then
+ python3 -m pip install --user pipx || exit 1
+ python3 -m pipx ensurepath || exit 1
+ if [ -f "${ZDOTDIR:-${HOME}/.config/zsh}/.zshrc" ]; then
+ # shellcheck source=${ZDOTDIR:-${HOME}/.config/zsh}/.zshrc
+ . "${ZDOTDIR:-${HOME}/.config/zsh}/.zshrc"
+ elif [ -f "${HOME}/.zshrc" ]; then
+ # shellcheck source=${HOME}/.zshrc
+ . "${HOME}/.zshrc"
+ fi
+fi
+
+# Check if yt-dlp is installed via pipx, install or upgrade it
+if ! pipx list | grep -q yt-dlp; then
+ pipx install yt-dlp || exit 1
+else
+ pipx upgrade yt-dlp || exit 1
+fi
+
+echo "$current_time" >"$timestamp_file"
diff --git a/ar/.local/bin/cron/newsup b/ar/.local/bin/cron/newsup
new file mode 100755
index 0000000..346ec75
--- /dev/null
+++ b/ar/.local/bin/cron/newsup
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+# Set as a cron job to check for new RSS entries for newsboat.
+# If newsboat is open, sends it an "R" key to refresh.
+
+/usr/bin/notify-send "📰 Updating RSS feeds..."
+
+pgrep -f newsboat$ && /usr/bin/xdotool key --window "$(/usr/bin/xdotool search --name "^newsboat$")" R && exit
+
+echo 🔃 >/tmp/newsupdate
+pkill -RTMIN+19 "${STATUSBAR:-dwmblocks}"
+/usr/bin/newsboat -x reload
+rm -f /tmp/newsupdate
+pkill -RTMIN+19 "${STATUSBAR:-dwmblocks}"
+/usr/bin/notify-send "📰 RSS feed update complete."
diff --git a/ar/.local/bin/cutvideo b/ar/.local/bin/cutvideo
new file mode 100755
index 0000000..3220008
--- /dev/null
+++ b/ar/.local/bin/cutvideo
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+usage() {
+ echo "Crop a video file using ffmpeg."
+ echo ""
+ echo "Usage: cutvideo [file_name] [position] [duration]"
+ echo ""
+ echo "Arguments:"
+ echo " file_name: The name of the video file (e.g., video.mp4)."
+ echo " position: The start position in the format HH:MM:SS (e.g., 00:00:00)."
+ echo " duration: The duration in seconds from the start position (e.g., 10)."
+ echo ""
+ echo "Example:"
+ echo " cutvideo ~/Video/video.mp4 00:01:00 10"
+ echo " This will create a 10-second cut starting at 00:01:00 in the video.mp4 file."
+ exit 1
+}
+
+[ -z "$1" ] && echo "Target file missing" && usage
+[ -z "$2" ] && echo "Target position missing" && usage
+[ -z "$3" ] && echo "Target duration missing" && usage
+
+file="$1"
+filename="${file%%.*}"
+ext="${file##*.}"
+num=1
+
+# Find a unique filename by incrementing num
+if [ -f "${filename}_cut.${ext}" ]; then
+ while [ -f "${filename}_cut_$(printf "%02d" "$num").${ext}" ]; do
+ num=$((num + 1))
+ done
+ new_filename="${filename}_cut_$(printf "%02d" "$num").${ext}"
+else
+ new_filename="${filename}_cut.${ext}"
+fi
+
+# Perform the cut using ffmpeg
+ffmpeg -hide_banner -ss "$2" -to "$3" -i "$file" -c copy "$new_filename"
+
+echo "Created file: $new_filename"
diff --git a/ar/.local/bin/displayselect b/ar/.local/bin/displayselect
new file mode 100755
index 0000000..38fc942
--- /dev/null
+++ b/ar/.local/bin/displayselect
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+# A UI for detecting and selecting all displays. Probes xrandr for connected
+# displays and lets user select one to use. User may also select "manual
+# selection" which opens arandr.
+
+twoscreen() { # If multi-monitor is selected and there are two screens.
+ mirror=$(printf "no\\nyes" | dmenu -i -p "Mirror displays?")
+ # Mirror displays using native resolution of external display and a scaled
+ # version for the internal display
+ if [ "$mirror" = "yes" ]; then
+ external=$(echo "$screens" | dmenu -i -p "Optimize resolution for:")
+ internal=$(echo "$screens" | grep -v "$external")
+
+ res_external=$(xrandr --query | sed -n "/^$external/,/\+/p" |
+ tail -n 1 | awk '{print $1}')
+ res_internal=$(xrandr --query | sed -n "/^$internal/,/\+/p" |
+ tail -n 1 | awk '{print $1}')
+
+ res_ext_x=$(echo "$res_external" | sed 's/x.*//')
+ res_ext_y=$(echo "$res_external" | sed 's/.*x//')
+ res_int_x=$(echo "$res_internal" | sed 's/x.*//')
+ res_int_y=$(echo "$res_internal" | sed 's/.*x//')
+
+ scale_x=$(echo "$res_ext_x / $res_int_x" | bc -l)
+ scale_y=$(echo "$res_ext_y / $res_int_y" | bc -l)
+
+ xrandr --output "$external" --auto --scale 1.0x1.0 \
+ --output "$internal" --auto --same-as "$external" \
+ --scale "$scale_x"x"$scale_y"
+ else
+ primary=$(echo "$screens" | dmenu -i -p "Select primary display:")
+ secondary=$(echo "$screens" | grep -v ^"$primary"$)
+ direction=$(printf "left\\nright" | dmenu -i -p "What side of $primary should $secondary be on?")
+ xrandr --output "$primary" --auto --scale 1.0x1.0 --output "$secondary" --"$direction"-of "$primary" --auto --scale 1.0x1.0
+ fi
+}
+
+morescreen() { # If multi-monitor is selected and there are more than two screens.
+ primary=$(echo "$screens" | dmenu -i -p "Select primary display:")
+ secondary=$(echo "$screens" | grep -v ^"$primary"$ | dmenu -i -p "Select secondary display:")
+ direction=$(printf "left\\nright" | dmenu -i -p "What side of $primary should $secondary be on?")
+ tertiary=$(echo "$screens" | grep -v ^"$primary"$ | grep -v ^"$secondary"$ | dmenu -i -p "Select third display:")
+ xrandr --output "$primary" --auto --output "$secondary" --"$direction"-of "$primary" --auto --output "$tertiary" --"$(printf "left\\nright" | grep -v "$direction")"-of "$primary" --auto
+}
+
+multimon() { # Multi-monitor handler.
+ case "$(echo "$screens" | wc -l)" in
+ 2) twoscreen ;;
+ *) morescreen ;;
+ esac
+}
+
+onescreen() { # If only one output available or chosen.
+ xrandr --output "$1" --auto --scale 1.0x1.0 $(echo "$allposs" | grep -v "\b$1" | awk '{print "--output", $1, "--off"}' | paste -sd ' ' -)
+}
+
+postrun() { # Stuff to run to clean up.
+ setbg # Fix background if screen size/arangement has changed.
+ {
+ killall dunst
+ setsid -f dunst
+ } >/dev/null 2>&1 # Restart dunst to ensure proper location on screen
+}
+
+# Get all possible displays
+allposs=$(xrandr -q | grep "connected")
+
+# Get all connected screens.
+screens=$(echo "$allposs" | awk '/ connected/ {print $1}')
+
+# If there's only one screen
+[ "$(echo "$screens" | wc -l)" -lt 2 ] &&
+ {
+ onescreen "$screens"
+ postrun
+ notify-send "💻 Only one screen detected." "Using it in its optimal settings..."
+ exit
+ }
+
+# Get user choice including multi-monitor and manual selection:
+chosen=$(printf "%s\\nmulti-monitor\\nmanual selection" "$screens" | dmenu -i -p "Select display arangement:") &&
+ case "$chosen" in
+ "manual selection")
+ arandr
+ exit
+ ;;
+ "multi-monitor") multimon ;;
+ *) onescreen "$chosen" ;;
+ esac
+
+postrun
diff --git a/ar/.local/bin/dmenubrowse b/ar/.local/bin/dmenubrowse
new file mode 100755
index 0000000..a7446ac
--- /dev/null
+++ b/ar/.local/bin/dmenubrowse
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+# Use dmenu to choose a search option
+SEARCH_TOOL=$(printf "DuckDuckGo\nSearx\nWebsite\nYouTube" | dmenu -i -p "Which option?")
+
+# Exit if no option is selected
+[ -z "$SEARCH_TOOL" ] && exit 1
+
+# Determine the command to execute based on the search tool
+case "$SEARCH_TOOL" in
+"DuckDuckGo")
+ # For DuckDuckGo, run ddgr in the terminal
+ TOOL="$TERMINAL -e browse -d"
+ ;;
+"Searx")
+ # Searx can be run directly in the browser
+ TOOL="browse"
+ ;;
+"Website")
+ # Ask the user for the website
+ SITE=$(dmenu -i -p "Which site?")
+
+ # Exit if no site is provided
+ [ -z "$SITE" ] && exit 1
+
+ # For website searches, run ddgr in the terminal with the website option
+ TOOL="$TERMINAL -e browse -w $SITE"
+ ;;
+"YouTube")
+ TOOL="browse -y"
+ ;;
+*)
+ TOOL="browse"
+ ;;
+esac
+
+# Get the search query from the user
+SEARCH_QUERY=$(echo | dmenu -i -p "Search: ")
+
+# Exit if no search query is provided
+[ -z "$SEARCH_QUERY" ] && exit 1
+
+# Execute the command
+$TOOL "$SEARCH_QUERY"
diff --git a/ar/.local/bin/dmenudelmusic b/ar/.local/bin/dmenudelmusic
new file mode 100755
index 0000000..2efbafa
--- /dev/null
+++ b/ar/.local/bin/dmenudelmusic
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+music_dir="${XDG_MUSIC_DIR:-${HOME}/Music}"
+music_txt="${music_dir}/.music.txt"
+playlist_dir="${XDG_CONFIG_HOME:-${HOME}/.config}/mpd/playlists"
+
+# Using POSIX-compliant methods for file selection
+selected_filename=$(find "$music_dir" -type f | awk -F/ '{print $NF}' | dmenu -i -l 20 -p "Select a file to delete:") || exit 1
+
+selected_file="$music_dir/$selected_filename"
+
+# Extracting YouTube video ID without using -P in grep
+video_id=$(strings "$selected_file" | grep 'watch?v=' | sed 's/.*watch?v=\([a-zA-Z0-9_-]*\).*/\1/' | head -1) || {
+ notify-send "❌ No YouTube video ID found in file: $selected_filename"
+ exit 1
+}
+
+# Confirmation dialog without using echo -e
+confirm=$(printf "Yes\nNo" | dmenu -i -p "Delete $selected_filename and update mpc?")
+
+[ "$confirm" = "Yes" ] || {
+ notify-send "❌ Operation cancelled."
+ exit 0
+}
+
+# More portable sed command without -i and updating mpc
+if grep -v "$video_id" "$music_txt" >"${music_txt}.tmp" && mv "${music_txt}.tmp" "$music_txt"; then
+ # Search and remove the filename from playlists
+ for playlist in "$playlist_dir"/*.m3u; do
+ [ -e "$playlist" ] || continue
+ if grep -q "$selected_filename" "$playlist"; then
+ grep -v "$selected_filename" "$playlist" > "${playlist}.tmp" && mv "${playlist}.tmp" "$playlist"
+ # Remove empty lines
+ sed -i '/^$/d' "$playlist"
+ fi
+ done
+
+ # Delete the music file
+ rm "$selected_file"
+ mpc update >/dev/null
+ notify-send " Success to delete:" "$selected_filename"
+else
+ notify-send "❌ An error occurred."
+fi
diff --git a/ar/.local/bin/dmenuhandler b/ar/.local/bin/dmenuhandler
new file mode 100755
index 0000000..02e015e
--- /dev/null
+++ b/ar/.local/bin/dmenuhandler
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+# Feed this script a link and it will give dmenu
+# some choice programs to use to open it.
+feed="${1:-$(true | dmenu -p 'Paste URL or file path')}"
+
+case "$(printf "copy url\\nnsxiv\\nsetbg\\nPDF\\nbrowser\\nlynx\\nvim\\nmpv\\nmpv loop\\nmpv float\\nqueue download\\nqueue yt-dlp\\nqueue yt-dlp audio" | dmenu -i -p "Open it with?")" in
+"copy url") echo "$feed" | xclip -selection clipboard ;;
+mpv) setsid -f mpv -quiet "$feed" >/dev/null 2>&1 ;;
+"mpv loop") setsid -f mpv -quiet --loop "$feed" >/dev/null 2>&1 ;;
+"mpv float") setsid -f "$TERMINAL" -e mpv --geometry=+0-0 --autofit=30% --title="mpvfloat" "$feed" >/dev/null 2>&1 ;;
+"queue yt-dlp") qndl "$feed" >/dev/null 2>&1 ;;
+"queue yt-dlp audio") qndl "$feed" 'yt-dlp -o "%(title)s.%(ext)s" -f bestaudio --embed-metadata --restrict-filenames' ;;
+"queue download") qndl "$feed" 'curl -LO' >/dev/null 2>&1 ;;
+PDF) curl -sL "$feed" >"/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" && zathura "/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" >/dev/null 2>&1 ;;
+nsxiv) curl -sL "$feed" >"/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" && nsxiv -a "/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" >/dev/null 2>&1 ;;
+vim) curl -sL "$feed" >"/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" && setsid -f "$TERMINAL" -e "$EDITOR" "/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" >/dev/null 2>&1 ;;
+setbg)
+ curl -L "$feed" >"$XDG_CACHE_HOME/pic"
+ xwallpaper --zoom "$XDG_CACHE_HOME/pic" >/dev/null 2>&1
+ ;;
+browser) setsid -f "$BROWSER" "$feed" >/dev/null 2>&1 ;;
+lynx) lynx "$feed" >/dev/null 2>&1 ;;
+esac
diff --git a/ar/.local/bin/dmenuman b/ar/.local/bin/dmenuman
new file mode 100755
index 0000000..d571e60
--- /dev/null
+++ b/ar/.local/bin/dmenuman
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+cmd=$(echo | dmenu -p "Enter command for man page:")
+
+[ -n "$cmd" ] && man_path=$(man -w "$cmd" 2>/dev/null) && [ -n "$man_path" ] && setsid -f "${TERMINAL:-st}" -e man "$cmd"
diff --git a/ar/.local/bin/dmenumountcifs b/ar/.local/bin/dmenumountcifs
new file mode 100755
index 0000000..46c2b57
--- /dev/null
+++ b/ar/.local/bin/dmenumountcifs
@@ -0,0 +1,19 @@
+#!/bin/sh
+# Gives a dmenu prompt to mount unmounted local NAS shares for read/write.
+# Requirements - "%wheel ALL=(ALL) NOPASSWD: ALL"
+#
+# Browse for mDNS/DNS-SD services using the Avahi daemon...
+srvname=$(avahi-browse _smb._tcp -t | awk '{print $4}' | dmenu -i -p "Which NAS?") || exit 1
+notify-send "Searching for network shares..." "Please wait..."
+# Choose share disk...
+share=$(smbclient -L "$srvname" -N | grep Disk | awk '{print $1}' | dmenu -i -p "Mount which share?") || exit 1
+# Format URL...
+share2mnt=//"$srvname".local/"$share"
+
+sharemount() {
+ mounted=$(mount -v | grep "$share2mnt") || ([ ! -d /mnt/"$share" ] && sudo mkdir /mnt/"$share")
+ [ -z "$mounted" ] && sudo mount -t cifs "$share2mnt" -o user=nobody,password="",noperm /mnt/"$share" && notify-send "Netshare $share mounted" && exit 0
+ notify-send "Netshare $share already mounted"; exit 1
+}
+
+sharemount
diff --git a/ar/.local/bin/dmenupass b/ar/.local/bin/dmenupass
new file mode 100755
index 0000000..2c14e6f
--- /dev/null
+++ b/ar/.local/bin/dmenupass
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+# This script is the SUDO_ASKPASS variable, meaning that it will be used as a
+# password prompt if needed.
+
+dmenu -fn Monospace-18 -P -p "$1" <&- && echo
diff --git a/ar/.local/bin/dmenurecord b/ar/.local/bin/dmenurecord
new file mode 100755
index 0000000..4aef54f
--- /dev/null
+++ b/ar/.local/bin/dmenurecord
@@ -0,0 +1,129 @@
+#!/bin/sh
+
+usage() {
+ echo "Asks for recording type via dmenu."
+ echo "If there is already a running instance, user will be prompted to end it. "
+ echo ""
+ echo "Usage: ${0##*/} [-h] [audio] [kill] [video] [screencast]"
+ echo ""
+ echo "Options:"
+ echo " - audio : Records only audio"
+ echo " - kill : Kills existing recording"
+ echo " - video : Records only screen"
+ echo " - screencast : Records both audio and screen"
+}
+
+getdim() { xrandr | grep -oP '(?<=current ).*(?=,)' | tr -d ' '; }
+
+updateicon() {
+ echo "$1" >/tmp/recordingicon
+ pkill -RTMIN+31 "${STATUSBAR:-dwmblocks}"
+}
+
+killrecording() {
+ recpid="$(cat /tmp/recordingpid)"
+ kill -15 "$recpid"
+ rm -f /tmp/recordingpid
+ updateicon ""
+ pkill -RTMIN+31 "${STATUSBAR:-dwmblocks}"
+}
+
+screencast() {
+ ffmpeg -y \
+ -f x11grab \
+ -framerate 30 \
+ -s "$(getdim)" \
+ -i "$DISPLAY" \
+ -r 24 \
+ -use_wallclock_as_timestamps 1 \
+ -f alsa -thread_queue_size 1024 -i default \
+ -c:v h264 \
+ -crf 0 -preset ultrafast -c:a aac \
+ "$HOME/screencast-$(date '+%y%m%d-%H%M-%S').mp4" &
+ echo $! >/tmp/recordingpid
+ updateicon "⏺️🎙️"
+}
+
+video() {
+ ffmpeg \
+ -f x11grab \
+ -framerate 30 \
+ -s "$(getdim)" \
+ -i "$DISPLAY" \
+ -c:v libx264 -qp 0 -r 30 \
+ "$HOME/video-$(date '+%y%m%d-%H%M-%S').mkv" &
+ echo $! >/tmp/recordingpid
+ updateicon "⏺️"
+}
+
+webcamhidef() {
+ ffmpeg \
+ -f v4l2 \
+ -i /dev/video0 \
+ -video_size 1920x1080 \
+ "$HOME/webcam-$(date '+%y%m%d-%H%M-%S').mkv" &
+ echo $! >/tmp/recordingpid
+ updateicon "🎥"
+}
+
+webcam() {
+ ffmpeg \
+ -f v4l2 \
+ -i /dev/video0 \
+ -video_size 640x480 \
+ "$HOME/webcam-$(date '+%y%m%d-%H%M-%S').mkv" &
+ echo $! >/tmp/recordingpid
+ updateicon "🎥"
+}
+
+audio() {
+ ffmpeg \
+ -f alsa -i default \
+ -c:a flac \
+ "$HOME/audio-$(date '+%y%m%d-%H%M-%S').flac" &
+ echo $! >/tmp/recordingpid
+ updateicon "🎙️"
+}
+
+askrecording() {
+ choice=$(printf "screencast\\nvideo\\nvideo selected\\naudio\\nwebcam\\nwebcam (hi-def)" | dmenu -i -p "Select recording style:")
+ case "$choice" in
+ screencast) screencast ;;
+ audio) audio ;;
+ video) video ;;
+ *selected) videoselected ;;
+ webcam) webcam ;;
+ "webcam (hi-def)") webcamhidef ;;
+ esac
+}
+
+asktoend() {
+ response=$(printf "No\\nYes" | dmenu -i -p "Recording still active. End recording?") &&
+ [ "$response" = "Yes" ] && killrecording
+}
+
+videoselected() {
+ slop -f "%x %y %w %h" >/tmp/slop
+ read -r X Y W H </tmp/slop
+ rm /tmp/slop
+
+ ffmpeg \
+ -f x11grab \
+ -framerate 30 \
+ -video_size "$W"x"$H" \
+ -i :0.0+"$X,$Y" \
+ -c:v libx264 -qp 0 -r 30 \
+ "$HOME/box-$(date '+%y%m%d-%H%M-%S').mkv" &
+ echo $! >/tmp/recordingpid
+ updateicon "⏺️"
+}
+
+case "$1" in
+-h | --help | help) usage && exit 0 ;;
+audio) audio ;;
+kill) killrecording ;;
+screencast) screencast ;;
+video) video ;;
+*selected) videoselected ;;
+*) ([ -f /tmp/recordingpid ] && asktoend && exit) || askrecording ;;
+esac
diff --git a/ar/.local/bin/dmenusmbadd b/ar/.local/bin/dmenusmbadd
new file mode 100755
index 0000000..d1b76cd
--- /dev/null
+++ b/ar/.local/bin/dmenusmbadd
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+samba_conf="/etc/samba/smb.conf"
+base_path="/media /mnt /home/$USER"
+os="$(grep '^ID=' /etc/os-release | cut -d= -f2)"
+
+restartinit() {
+ case "$(basename "$(readlink -f /sbin/init)" | sed 's/-init//g')" in
+ *systemd*)
+ sudo systemctl restart smb >/dev/null/ 2>&1 && sudo systemctl restart nmb >/dev/null/ 2>&1
+ ;;
+ *runit*)
+ sudo sv restart smbd >/dev/null 2>&1 && sudo sv restart nmbd >/dev/null 2>&1
+ ;;
+ esac
+}
+
+[ -f "$samba_conf" ] || { sudo touch "$samba_conf" && smbpasswd -a "$USER"; }
+if [ "$#" -eq 1 ]; then
+ folder_path="$1"
+ folder_name=$(basename "$folder_path" | tr '[:upper:]' '[:lower:]')
+ if [ "$folder_path" = "/media/$USER" ]; then
+ folder_name="media"
+ fi
+else
+ folder_name=$(echo | dmenu -p "Enter the name of the folder to share:" | tr '[:upper:]' '[:lower:]') || exit 1
+ [ -z "$folder_name" ] && notify-send "📁No folder name provided." && exit 1
+
+ if [ "$folder_name" = "media" ]; then
+ target_name="$USER"
+ else
+ target_name="$folder_name"
+ fi
+
+ folder_path=$(for path in $base_path; do
+ find "$path" -type d -iname "$target_name" -print 2>/dev/null
+ done | sort -r | dmenu -l 10 -p "Select the folder to share:")
+ [ -z "$folder_path" ] && notify-send "📁Folder not found." && exit 1
+fi
+
+[ -d "$folder_path" ] || exit 1
+
+if grep -qF "[$USER-$os-$folder_name]" "$samba_conf"; then
+ notify-send "📁The folder '$target_name' is already shared."
+ exit 0
+fi
+
+echo | sudo tee -a "$samba_conf" >/dev/null
+sudo tee -a "$samba_conf" >/dev/null <<EOF && restartinit || exit 1
+[$USER-$os-$folder_name]
+ path = $folder_path
+ writable = yes
+ browsable = yes
+ guest ok = yes
+ create mask = 0755
+EOF
+
+notify-send "📁'$USER-$os-$folder_name' starts sharing." "path: '$folder_path'"
diff --git a/ar/.local/bin/dmenusmbdel b/ar/.local/bin/dmenusmbdel
new file mode 100755
index 0000000..5769695
--- /dev/null
+++ b/ar/.local/bin/dmenusmbdel
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+samba_conf="/etc/samba/smb.conf"
+dmenu_command=$(command -v dmenu)
+
+# Check if Samba configuration file exists
+[ -f "$samba_conf" ] || {
+ echo "Error: Samba configuration file not found at $samba_conf"
+ exit 1
+}
+
+# Extract share names and their paths for selection, with alignment
+shares=$(awk '
+/^\[.*\]/ {
+ share_name = $0
+ gsub(/[\[\]]/, "", share_name)
+}
+/^ *path *=/ {
+ sub(/^ *path *= */, "", $0)
+ printf "%-40s %s\n", share_name, $0
+}
+' "$samba_conf")
+
+# Check if dmenu is installed and available
+if [ -n "$dmenu_command" ]; then
+ selected=$(printf "%s\n" "$shares" | "$dmenu_command" -l 10 -p "Select a shared folder to disable:")
+
+ # Exit if no selection was made
+ [ -z "$selected" ] && exit 0
+
+ # Extract share name from the selected entry (before the spaces)
+ selected_share=$(echo "$selected" | awk '{print $1}')
+
+ # Confirm with the user
+ confirm=$(printf "Yes\nNo\n" | "$dmenu_command" -p "Disable sharing for $selected_share?")
+ if [ "$confirm" = "Yes" ]; then
+ # Remove only the selected share block and its preceding empty line
+ sed -n -e "/^$/N;/^\n\[${selected_share}\]/,/^ *create mask = 0755$/!p" "$samba_conf" | sudo tee "$samba_conf" >/dev/null
+
+ # Restart Samba services
+ case "$(basename "$(readlink -f /sbin/init)" | sed 's/-init//g')" in
+ *systemd*)
+ sudo systemctl restart smb >/dev/null 2>&1 && sudo systemctl restart nmb >/dev/null 2>&1
+ ;;
+ *runit*)
+ sudo sv restart smbd >/dev/null 2>&1 && sudo sv restart nmbd >/dev/null 2>&1
+ ;;
+ esac
+
+ notify-send "✂️ Disabled sharing for '$selected_share'"
+ fi
+else
+ # Print share names if dmenu is not installed
+ printf "%s\n" "$shares"
+fi
diff --git a/ar/.local/bin/dmenuunicode b/ar/.local/bin/dmenuunicode
new file mode 100755
index 0000000..eb5ff4c
--- /dev/null
+++ b/ar/.local/bin/dmenuunicode
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# The famous "get a menu of emojis to copy" script.
+
+# Get user selection via dmenu from emoji file.
+chosen=$(cut -d ';' -f1 ~/.local/share/thesiah/chars/* | dmenu -i -l 30 | sed "s/ .*//")
+
+# Exit if none chosen.
+[ -z "$chosen" ] && exit
+
+# If you run this command with an argument, it will automatically insert the
+# character. Otherwise, show a message that the emoji has been copied.
+if [ -n "$1" ]; then
+ xdotool type "$chosen"
+else
+ printf "%s" "$chosen" | xclip -selection clipboard
+ notify-send "'$chosen' copied to clipboard." &
+fi
diff --git a/ar/.local/bin/dmenuupgrade b/ar/.local/bin/dmenuupgrade
new file mode 100755
index 0000000..b43ff4c
--- /dev/null
+++ b/ar/.local/bin/dmenuupgrade
@@ -0,0 +1,115 @@
+#!/bin/sh
+
+# Get list of pacman and yay upgradable packages
+pacman_updates=$(pacman -Qu)
+yay_updates=$(yay -Qu)
+
+# Count the number of upgradable packages, filtering yay's output to count only AUR packages
+pacman_count=$(printf "%s" "$pacman_updates" | grep -c '^\S')
+aur_count=$(printf "%s" "$yay_updates" | grep -c '^\[AUR\]')
+
+# Display the upgrade options with package counts
+selection=$(printf "All\nPacman(%d)\nAUR(%d)" "$pacman_count" "$aur_count" | dmenu -i -p "Upgrade Option:")
+
+case "$selection" in
+"All")
+ if [ "$(printf "No\nYes" | dmenu -i -p 'Proceed with upgrade for all packages?')" = "Yes" ]; then
+ notify-send "📦 Upgrading all packages..."
+ yay -Syu --noconfirm
+ pkill -RTMIN+16 "${STATUSBAR:-dwmblocks}"
+ notify-send "✅ Upgrade of all packages completed."
+ else
+ notify-send "❌ Upgrade cancelled."
+ exit 0
+ fi
+ ;;
+"Pacman($pacman_count)")
+ # Show all upgradable pacman packages with "Upgrade All" option
+ selection=$(printf "Upgrade All\n%s" "$pacman_updates" | dmenu -i -l 10 -p "Pacman: Upgrade or Open URL:")
+ case "$selection" in
+ "Upgrade All")
+ if [ "$(printf "No\nYes" | dmenu -i -p 'Proceed with pacman upgrade?')" = "Yes" ]; then
+ notify-send "📦 Upgrading pacman packages..."
+ printf "%s" "$pacman_updates" | awk '{print $1}' | xargs sudo pacman -S --noconfirm
+ pkill -RTMIN+16 "${STATUSBAR:-dwmblocks}"
+ notify-send "✅ Pacman packages upgrade completed."
+ else
+ notify-send "❌ Upgrade cancelled."
+ exit 0
+ fi
+ ;;
+ *)
+ # Individual package selected: Prompt to upgrade or open URL
+ action=$(printf "Upgrade\nOpen URL" | dmenu -i -p "Package: $selection")
+ case "$action" in
+ "Upgrade")
+ if [ "$(printf "No\nYes" | dmenu -i -p 'Proceed with upgrade for this package?')" = "Yes" ]; then
+ notify-send "📦 Upgrading package: $selection..."
+ sudo pacman -S --noconfirm "$(printf "%s" "$selection" | awk '{print $1}')"
+ pkill -RTMIN+16 "${STATUSBAR:-dwmblocks}"
+ notify-send "✅ Upgrade completed for package: $selection."
+ else
+ notify-send "❌ Upgrade cancelled."
+ exit 0
+ fi
+ ;;
+ "Open URL")
+ if [ "$(printf "No\nYes" | dmenu -i -p 'Open URL?')" = "Yes" ]; then
+ xdg-open "https://archlinux.org/packages/?q=$(printf "%s" "$selection" | awk '{print $1}')" >/dev/null 2>&1 &
+ else
+ exit
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+"AUR($aur_count)")
+ # Show all upgradable AUR packages with "Upgrade All" option
+ aur_updates=$(printf "%s" "$yay_updates" | grep '^\[AUR\]')
+ selection=$(printf "Upgrade All\n%s" "$aur_updates" | dmenu -i -l 10 -p "AUR: Upgrade or Open URL:")
+ case "$selection" in
+ "Upgrade All")
+ if [ "$(printf "No\nYes" | dmenu -i -p 'Proceed with AUR upgrade?')" = "Yes" ]; then
+ notify-send "📦 Upgrading AUR packages..."
+ yay -Syu --aur --noconfirm
+ pkill -RTMIN+16 "${STATUSBAR:-dwmblocks}"
+ notify-send "✅ AUR packages upgrade completed."
+ else
+ notify-send "❌ Upgrade cancelled."
+ exit 0
+ fi
+ ;;
+ *)
+ # Individual AUR package selected: Prompt to upgrade or open URL
+ action=$(printf "Upgrade\nOpen URL" | dmenu -i -p "Package: $selection")
+ case "$action" in
+ "Upgrade")
+ if [ "$(printf "No\nYes" | dmenu -i -p 'Proceed with upgrade for this package?')" = "Yes" ]; then
+ notify-send "📦 Upgrading AUR package: $selection..."
+ yay -S --noconfirm "$(printf "%s" "$selection" | awk '{print $2}')"
+ pkill -RTMIN+16 "${STATUSBAR:-dwmblocks}"
+ notify-send "✅ Upgrade completed for AUR package: $selection."
+ else
+ notify-send "❌ Upgrade cancelled."
+ exit 0
+ fi
+ ;;
+ "Open URL")
+ if [ "$(printf "No\nYes" | dmenu -i -p 'Open URL?')" = "Yes" ]; then
+ xdg-open "https://aur.archlinux.org/packages/?O=0&K=$(printf "%s" "$selection" | awk '{print $2}')" >/dev/null 2>&1 &
+ else
+ exit
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+*)
+ notify-send "❌ Invalid selection."
+ exit 0
+ ;;
+esac
+
+remaps
diff --git a/ar/.local/bin/dvdburn b/ar/.local/bin/dvdburn
new file mode 100755
index 0000000..0e78b83
--- /dev/null
+++ b/ar/.local/bin/dvdburn
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+# Check if input file is provided
+if [ $# -eq 0 ]; then
+ echo "Usage: $0 input_video.mp4"
+ exit 1
+fi
+
+# Check if input file exists
+input_file="$1"
+if [ ! -f "$input_file" ]; then
+ echo "Error: Input file '$input_file' not found."
+ exit 1
+fi
+
+# Set default VIDEO_FORMAT if not provided
+VIDEO_FORMAT="${VIDEO_FORMAT:-PAL}"
+
+# Create temporary directory for DVD structure
+tmp_dir=$(mktemp -d)
+echo "Temporary directory created: $tmp_dir"
+
+# Convert MP4 to DVD-compatible MPEG-2 format
+echo "Converting $input_file to DVD-Video format ($VIDEO_FORMAT)..."
+ffmpeg -i "$input_file" -target "$VIDEO_FORMAT"-dvd -vf scale=720:576 -aspect 16:9 "$tmp_dir/video.mpg"
+
+# Create DVD file structure
+echo "Creating DVD file structure..."
+dvdauthor -o "$tmp_dir/dvd" -t "$tmp_dir/video.mpg"
+dvdauthor -o "$tmp_dir/dvd" -T
+
+# Create ISO image from DVD structure
+echo "Creating ISO image..."
+mkisofs -dvd-video -o "$tmp_dir/dvd.iso" "$tmp_dir/dvd/"
+
+# Burn ISO image to DVD
+echo "Burning DVD..."
+wodim -v dev=/dev/sr0 speed=8 -eject "$tmp_dir/dvd.iso"
+
+# Cleanup
+echo "Cleaning up..."
+rm -rf "$tmp_dir"
+
+echo "Done!"
diff --git a/ar/.local/bin/ecrypt b/ar/.local/bin/ecrypt
new file mode 100755
index 0000000..5547dcb
--- /dev/null
+++ b/ar/.local/bin/ecrypt
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+mount_encrypted() {
+ ! mount | grep -q " $1 " && echo "$PASSPHRASE" | sudo mount -t ecryptfs "$1" "$2" \
+ -o ecryptfs_cipher=aes,ecryptfs_key_bytes=32,ecryptfs_passthrough=no,ecryptfs_enable_filename_crypto=yes,ecryptfs_sig=$ECRYPTFS_SIG,ecryptfs_fnek_sig=$FNEK_SIG,passwd=$(printf '%s' "$PASSPHRASE")
+}
+
+attempt_mount() {
+ if mount | grep -q " $2 "; then
+ sudo umount "$2" && notify-send "🔒 Locked: $3" || notify-send "❗ Unable to lock" "Mounted: $3"
+ else
+ ECRYPTFS_SIG=$(pass show encryption/ecryptfs-sig-"$4")
+ FNEK_SIG=$ECRYPTFS_SIG
+ PASSPHRASE=$(pass show encryption/ecryptfs)
+ [ -z "$PASSPHRASE" ] && {
+ notify-send "❌ Failed to retrieve passphrase."
+ exit 1
+ }
+ mount_encrypted "$1" "$2" && notify-send "🔑 Unlocked: $3"
+ fi
+}
+
+targets="$HOME/.secret"
+mounts="$HOME/Private"
+pw="default"
+set -- $mounts # Set positional parameters to mounts
+i=1
+for target in $targets; do
+ mp=$(eval echo "\$$i") # Get the mount point using indirect expansion
+ path=$(basename "$mp") # Extract last directory component
+ pw=$(echo "$pw" | cut -d' ' -f$i) # Get the corresponding passthrough option
+
+ attempt_mount "$target" "$mp" "$path" "$pw"
+ i=$((i + 1))
+done
diff --git a/ar/.local/bin/emojiupdate b/ar/.local/bin/emojiupdate
new file mode 100755
index 0000000..cc2f1c5
--- /dev/null
+++ b/ar/.local/bin/emojiupdate
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+# Define input and output files
+URL="https://unicode.org/Public/emoji/latest/emoji-test.txt"
+INPUT_FILE="${XDG_DATA_HOME:-${HOME}/.local/share}/thesiah/char/emoji_raw"
+TEMP_FILE="${XDG_DATA_HOME:-${HOME}/.local/share}/thesiah/char/emoji_temp"
+OUTPUT_FILE="${XDG_DATA_HOME:-${HOME}/.local/share}/thesiah/char/emoji"
+
+# Create the directory for output files if it doesn't exist
+mkdir -p "$(dirname "$INPUT_FILE")"
+
+# Download the emoji file
+echo "Downloading emoji-test.txt from Unicode..."
+if curl -o "$INPUT_FILE" -L "$URL"; then
+ echo "Download complete! File saved to: $INPUT_FILE"
+else
+ echo "Failed to download emoji"
+ exit 1
+fi
+
+awk '
+ # Skip empty lines and comments
+ /^[[:space:]]*$/ || /^#/ { next }
+
+ # Keep only fully-qualified lines
+ !/(fully-qualified|component)/ { next }
+
+ # Skip lines containing 200D (zero-width joiner)
+ /200D/ { next }
+
+ # Skip lines containing components
+ $2 ~ /1F3F[BCDEF]/ { next }
+
+ # Print valid lines
+ { print }
+' "$INPUT_FILE" >"$TEMP_FILE"
+
+# Second stage: Extract emoji and description
+awk -F'#' '
+ {
+ if (NF >= 2) {
+ full_data = $2 # Extract the emoji and description (after #)
+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", full_data) # Trim spaces around the entire field
+
+ split(full_data, parts, " ") # Split into parts by spaces
+ emoji = parts[1] # First part is the emoji
+
+ # Reconstruct description from parts[3] onward
+ description = ""
+ for (i = 3; i <= length(parts); i++) {
+ description = description parts[i] " "
+ }
+
+ # Remove excessive internal spaces and trim description
+ gsub(/[[:space:]]+/, " ", description)
+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", description)
+
+ # Print emoji and description
+ print emoji, description
+ }
+ }
+' "$TEMP_FILE" >"$OUTPUT_FILE"
+
+rm -rf "$INPUT_FILE" "$TEMP_FILE"
+echo "Processing complete! File saved to: $OUTPUT_FILE"
diff --git a/ar/.local/bin/ethwifi b/ar/.local/bin/ethwifi
new file mode 100755
index 0000000..1f76ace
--- /dev/null
+++ b/ar/.local/bin/ethwifi
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+check_ethernet() {
+ interfaces=$(ip link | awk '/^[0-9]+: e/ {print substr($2, 1, length($2)-1)}')
+ for iface in $interfaces; do
+ ip link show "$iface" | grep -q 'state UP' && return 0
+ done
+ return 1
+}
+
+check_ethernet && nmcli radio wifi off && notify-send "📡 wifi: OFF" || {
+ nmcli radio wifi on
+ notify-send "📡 wifi: ON"
+}
+
+pkill -RTMIN+7 "${STATUSBAR:-dwmblocks}"
diff --git a/ar/.local/bin/extractkeys b/ar/.local/bin/extractkeys
new file mode 100755
index 0000000..4a8d82d
--- /dev/null
+++ b/ar/.local/bin/extractkeys
@@ -0,0 +1,142 @@
+#!/bin/bash
+
+# Define paths and phrases
+path="${XDG_SOURCES_HOME:-${HOME}/.local/src}/suckless/dwm/thesiah"
+readme_file="${XDG_DATA_HOME:-${HOME}/.local/share}/thesiah/thesiah.mom"
+output_file="$path.mom"
+temp_file_before="$path.tmp.before"
+temp_file_after="$path.tmp.after"
+start_phrase="When config.h in DWM and ST is saved, key bindings will be extracted and updated below."
+end_phrase=".HEADING 2 \"Other buttons\""
+
+[ -f "$path.mom" ] && source_file="$path.mom" || source_file="$path-default.mom"
+
+# Extract the section before the end marker
+sed "/$end_phrase/,\$d" "$source_file" >"$temp_file_before"
+
+# Extract the section from the end marker to the end of the file
+sed -n "/$end_phrase/,\$p" "$source_file" >"$temp_file_after"
+
+# Remove the contents between the start and end phrases, including the markers themselves
+sed -i "/$start_phrase/,/$end_phrase/d" "$temp_file_before"
+
+# Verify that the section has been removed
+echo "Section between markers removed. Check $temp_file_before for correctness."
+
+# Re-append the start marker since it was deleted
+echo "$start_phrase" >>"$temp_file_before"
+
+# Define your configuration files
+mapfile -t config_files <<EOF
+${XDG_SOURCES_HOME:-${HOME}/.local/src}/suckless/dwm/config.h
+${XDG_SOURCES_HOME:-${HOME}/.local/src}/suckless/st/config.h
+EOF
+
+# Process each configuration file and append content
+for file_path in "${config_files[@]}"; do
+ project_name=$(basename "$(dirname "$file_path")")
+ project_name=${project_name^^}
+
+ echo ".HEADING 3 \"$project_name\"" >>"$temp_file_before"
+ if [ "$project_name" == "DWM" ]; then
+ printf ".PP\nTHESIAH's window manager. I do not use a desktop environment.\n" >>"$temp_file_before"
+ elif [ "$project_name" == "ST" ]; then
+ printf ".PP\nTHESIAH's default terminal in C. It is light, configurable, and fast.\n" >>"$temp_file_before"
+ fi
+ echo ".LI" >>"$temp_file_before"
+
+ awk 'BEGIN {flag=0} /static[[:space:]]+(const[[:space:]]+)?(Key|Shortcut|Command|Button)[[:space:]]+(keys|cmdkeys|shortcuts|commands|buttons)[[:space:]]*\[\][[:space:]]*=[[:space:]]*{/ {flag=1} /\};/ {flag=0} flag' "$file_path" | while read -r line; do
+ if [[ "$line" =~ \/\*.*\*\/ || "$line" =~ .*\"\\.* || "$line" =~ ^$ || "$line" =~ STACKKEYS || "$line" =~ TAGKEYS || "$line" =~ static\ Key\ cmdkeys || "$line" =~ ^\#.* ]]; then
+ continue
+ fi
+
+ if [[ "$line" =~ ^\/\/.* ]]; then
+ echo ".LIST OFF" >>"$temp_file_before"
+ line=$(echo "$line" | sed -e 's/\/\/\s*//g' | awk '{for (i=1; i<=NF; i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2)); print}')
+ output_line=".HEADING 5 \"$line\""
+ echo "$output_line" >>"$temp_file_before"
+ echo ".LI" >>"$temp_file_before"
+ continue
+ fi
+
+ if [[ "$line" =~ static\ const|static\ Shortcut ]]; then
+ echo ".LIST OFF" >>"$temp_file_before"
+ output_line=".HEADING 4 \"INSERT MODE\""
+ echo "$output_line" >>"$temp_file_before"
+ echo ".LI" >>"$temp_file_before"
+ continue
+ fi
+
+ if [[ "$line" =~ static\ Command ]]; then
+ echo ".LIST OFF" >>"$temp_file_before"
+ output_line=".HEADING 4 \"COMMAND MODE\""
+ echo "$output_line" >>"$temp_file_before"
+ echo ".LI" >>"$temp_file_before"
+ continue
+ fi
+
+ line=$(echo "$line" | sed -e "s/^{ \(.*\) },/\1/g;s/{ \([^,]*\), \([^,]*\), \([^,]*\), \([^}]*\) }/\1 \2 \3 \4/g")
+ modkey=$(echo "$line" | awk -F',' '{print $1}' | sed "s/\b0//g;s/MODKEY\(\d*\)/MOD\1/g;s/\([Shift|Contrl]\)Mask/\1/g;s/Control/Ctrl/g;s/TERMMOD/MOD+Shift/g;s/|/+/g;s/ //g;s/XK_ANY_MOD/ANY MOD/g")
+ key=$(echo "$line" | awk -F',' '{print $2}' | sed "s/\b0//g;s/XF86XK_//g;s/XK_//g;s/\s*\(.*\)_R$/Right_\1/g;s/\s*\(.*\)_L$/Left_\1/g;s/MODKEY\(\d*\)/MOD\1/g;s/\([Shift|Contrl]\)Mask/\1/g;s/Control/Ctrl/g;s/|/+/g;s/ //g")
+ func=$(echo "$line" | awk -F',' '{print $3}' | grep -v "spawn" | sed "s/ //g")
+ args=$(echo "$line" | cut -d',' -f4- | sed -E 's/.*\.v\s*=\s*\(const\s*char\s*\*\[\]\)\s*\{\s*([^}]*)\s*\}.*/\1/g;
+ s/.*\.v\s*=\s*\(int\s*\[\]\)\s*\{\s*([^}]*)\s*\}.*/\1/g;
+ s/.*SHCMD\((.*)\).*/\1/g;
+ s/.*\{\s*\.\w*\s*=\s*(.*)\s*\}.*/\1/g;
+ s/\{0\}//g;
+ s/\"//g;
+ s/\,//g;
+ s/NULL//g;
+ s/TERMINAL\s*-e\s*//g;
+ s/&layouts\[0\]/"[]="/g;
+ s/&layouts\[1\]/"[M]"/g;
+ s/&layouts\[2\]/"|||"/g;
+ s/&layouts\[3\]/"[@]"/g;
+ s/&layouts\[4\]/"[\\\\]"/g;
+ s/&layouts\[5\]/"H[]"/g;
+ s/&layouts\[6\]/"TTT"/g;
+ s/&layouts\[7\]/"==="/g;
+ s/&layouts\[8\]/"HHH"/g;
+ s/&layouts\[9\]/"###"/g;
+ s/&layouts\[10\]/"---"/g;
+ s/&layouts\[11\]/":::"/g;
+ s/&layouts\[12\]/"|M|"/g;
+ s/&layouts\[13\]/">M>"/g;
+ s/^\s*//g;
+ /^\s*0\s*$/d
+ ')
+
+ # args=$(echo "$line" | cut -d',' -f4- | sed "s/\.v = (const char \*\[\])//g;s/{\s*\.v = (int \[\])//g;s/SHCMD(\(.*\))/\1/g;s/\.. = //g")
+
+ if [[ -z "$modkey" ]]; then
+ if [[ -z $func ]]; then
+ output_line="\\f(CW${key}\\fP \\(en ${args}"
+ else
+ output_line="\\f(CW${key}\\fP \\(en ${func} ${args}"
+ fi
+ else
+ if [[ -z $func ]]; then
+ output_line="\\f(CW${modkey}+${key}\\fP \\(en ${args}"
+ else
+ output_line="\\f(CW${modkey}+${key}\\fP \\(en ${func} ${args}"
+ fi
+ fi
+
+ echo ".ITEM" >>"$temp_file_before"
+ echo "$output_line" >>"$temp_file_before"
+ done
+
+ echo ".LIST OFF" >>"$temp_file_before"
+done
+
+# Append the latter part of the document that follows the end marker
+cat "$temp_file_after" >>"$temp_file_before"
+
+# Replace the original file with the updated content
+mv -f "$temp_file_before" "$output_file"
+cp -f "$output_file" "$readme_file"
+
+# Clean up the temporary after-section file
+rm -f "$temp_file_after"
+
+notify-send "👍 Operation completed successfully."
diff --git a/ar/.local/bin/fzffiles b/ar/.local/bin/fzffiles
new file mode 100755
index 0000000..f6708c1
--- /dev/null
+++ b/ar/.local/bin/fzffiles
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+# The set -e option instructs sh to immediately exit if any command has a non-zero exit status
+set -e
+
+# Set new line and tab for word splitting
+IFS='
+'
+
+# Get the list of selected files with key bindings for specific paths
+files=$(fzf-tmux \
+ --header "^a pwd ^b public ^d .dotfiles ^f configs ^g git ^h home ^k desktop ^r scripts ^s suckless ^u staged files ^v private ^/ help" \
+ --preview 'if [ -d {} ]; then exa --color=always --long --all --header --icons --git {}; elif [ -L {} ]; then file -h {}; else bat --color=always --style=header,grid --line-range=:500 {}; fi' \
+ --reverse \
+ --query="$1" \
+ --multi \
+ --exit-0 \
+ --prompt " 💡 " \
+ --bind "ctrl-a:change-prompt( ⚡ )+reload(fd -H -L -t f -E .Trash -E .git -E .cache . $PWD)" \
+ --bind "ctrl-b:change-prompt( 🌎 )+reload(fd -H -L -t f -E .Trash -E .git -E .cache . ${XDG_PUBLICSHARE_DIR:-${HOME}/Public})" \
+ --bind "ctrl-d:change-prompt( ⚙️ )+reload(fd -H -L -t f -E .Trash -E .git -E .cache . ${XDG_DOTFILES_DIR:-${HOME}/.dotfiles})" \
+ --bind "ctrl-f:change-prompt( 🗂️ )+reload(fd -H -L -t f -E .Trash -E .git -E .cache . ${XDG_CONFIG_HOME:-${HOME}/.config})" \
+ --bind "ctrl-g:change-prompt(  )+reload(fd -H -L -t f -E .Trash -E .git -E .cache . $HOME/Private/repos $HOME/Public/repos)" \
+ --bind "ctrl-h:change-prompt( 🏠 )+reload(fd -H -L -t f -E .Trash -E .git -E .cache . $HOME)" \
+ --bind "ctrl-k:change-prompt( 🖥️ )+reload(fd -H -L -t f -E .Trash -E .git -E .cache . ${XDG_DESKTOP_DIR:-${HOME}/Desktop})" \
+ --bind "ctrl-r:change-prompt( 👟 )+reload(fd -H -L -t f -E .Trash -E .git -E .cache -E zsh . ${XDG_BIN_HOME:-${HOME}/.local/bin})" \
+ --bind "ctrl-s:change-prompt( 🛠 )+reload(find ${XDG_SOURCES_HOME:-${HOME}/.local/src}/suckless -maxdepth 2 -type f -not -path '*/.git/*')" \
+ --bind "ctrl-u:change-prompt( 📝 )+reload(if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then git -C $(git rev-parse --show-toplevel 2>/dev/null) status -s | awk -v pwd=\"$PWD\" '{print pwd \"\/\" \$2}' | grep -v '^$'; else echo 'This is not a git repository.'; fi)" \
+ --bind "ctrl-v:change-prompt( 🔒 )+reload(fd -H -L -t f -E .Trash -E .git -E .cache . $HOME/Private)" \
+ --bind 'ctrl-/:change-prompt( ❓ )+reload(echo "^a all
+^b public
+^c configs
+^d .dotfiles
+^g git
+^k desktop
+^r scripts
+^s suckless
+^u staged files
+^v private
+^/ help")')
+
+# Check if any files were selected, and exit if not
+[ -z "$files" ] && exit 0
+
+openfiles "$files"
diff --git a/ar/.local/bin/fzffns b/ar/.local/bin/fzffns
new file mode 100755
index 0000000..c919723
--- /dev/null
+++ b/ar/.local/bin/fzffns
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+# Print all functions and comments in a readable format
+# Set the filename of the script containing the functions
+file="${ZDOTDIR:-${XDG_CONFIG_HOME:-${HOME}/.config}/zsh}/scripts.zsh"
+
+# Initialize an empty variable to hold functions, aliases, and comments
+functions=""
+
+# Parse the file for function names, aliases, and comments
+while IFS= read -r line || [ -n "$line" ]; do
+ case "$line" in
+ \#*)
+ if [ "$(printf '%s' "$line" | cut -c 2)" != "#" ] && [ "$(printf '%s' "$line" | cut -c 2)" != "!" ]; then
+ # Remove the '#' from the comment line
+ comment=$(printf '%s' "$line" | sed 's/^# //')
+
+ # Read the next line to check for alias or function definition
+ IFS= read -r next_line || break
+
+ # Check if it's an alias definition
+ if echo "$next_line" | grep -q '^alias '; then
+ alias_name=$(echo "$next_line" | sed -n 's/^alias \([a-zA-Z0-9_]*\)=.*$/\1/p')
+
+ # Read another line to get the function definition
+ IFS= read -r func_line || break
+ f_name=$(printf '%s' "$func_line" | sed -n 's/^function \([^(]*\)().*$/\1/p')
+
+ if [ -n "$f_name" ]; then
+ functions="$functions$f_name|$alias_name|$comment\n"
+ fi
+
+ # Check if it's a function definition
+ elif echo "$next_line" | grep -q '^function '; then
+ f_name=$(printf '%s' "$next_line" | sed -n 's/^function \([^(]*\)().*$/\1/p')
+ if [ -n "$f_name" ]; then
+ functions="$functions$f_name||$comment\n"
+ fi
+ fi
+ fi
+ ;;
+ esac
+done <"$file"
+
+# Sort the functions alphabetically by name
+sorted=$(printf '%b' "$functions" | sort)
+
+# Print out the sorted functions with aliases and comments in a readable format
+formatted=$(printf '%b' "$sorted" | while IFS='|' read -r f_name alias_name comment; do
+ if [ -n "$alias_name" ]; then
+ printf 'fn: %-30s - %s (alias: %s)\n' "$f_name" "$comment" "$alias_name"
+ else
+ printf 'fn: %-30s - %s\n' "$f_name" "$comment"
+ fi
+done)
+
+# Use fzf to select a function
+selected=$(printf '%b' "$formatted" | fzf-tmux --header "Select a function:" --reverse)
+
+# Check if a function was selected
+if [ -n "$selected" ]; then
+ # Extract the function name
+ f_name=$(echo "$selected" | cut -d ' ' -f 2 | sed 's/[[:space:]]\+//g')
+
+ # Get the line number of the function definition
+ line_number=$(grep -n "^function $f_name(" "$file" | cut -d ':' -f 1)
+
+ # Open the function in nvim at the specific line number
+ if [ -n "$line_number" ]; then
+ nvim "+$line_number" "$file"
+ else
+ echo "Function '$f_name' not found in $file."
+ fi
+fi
diff --git a/ar/.local/bin/fzfpass b/ar/.local/bin/fzfpass
new file mode 100755
index 0000000..13cb6a6
--- /dev/null
+++ b/ar/.local/bin/fzfpass
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+set -e
+
+usage() {
+ echo "Find pass gpg files in Password-Store using fzf."
+ echo ""
+ echo "Usage: ${0##*/} [-h|--help]"
+ echo ""
+ echo "Options:"
+ echo " -h, --help : Show this message"
+ echo ""
+ echo "Shortcuts:"
+ echo " return - echo password and copy to clipboard (wayland only)"
+ echo " ctrl+a - new password (named as per the prompt)"
+ echo " ctrl+e - edit selected password"
+ echo " ctrl+g - regenerate selected password"
+ echo " ctrl+r - rename selected password"
+ echo " ctrl+x - delete selected password"
+ echo " tab - tab complete"
+ echo " esc/ctrl+c - exit"
+ exit 0
+}
+
+if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
+ usage
+fi
+
+PASSDIR=${PASSWORD_STORE_DIR:-$HOME/.local/share/.password-store}
+cd "$PASSDIR"
+
+# Unlock the password for this session
+pass show "$(tree -Ffi | grep '.gpg' | sed 's/.gpg$//g' | sed 's/^..//' | head -n 1)" >/dev/null
+
+# Main fzf session
+PASSFILE=$(
+ tree -Ffi | grep '.gpg' | sed 's/.gpg$//g' | sed 's/^..//' |
+ fzf-tmux \
+ --header="🔑 Password Manager" \
+ --reverse \
+ --no-mouse \
+ --preview="pass {}" \
+ --header="🔑 ^a: Generate | ^e: Edit | ^g: Generate (Insertion) | ^r: Rename | ^x: Delete | tab: Replace" \
+ --bind="ctrl-a:execute(if [ -z {q} ]; then read -p \"Can't generate empty password. Press enter to continue...\"; else pass generate -n {q} < /dev/tty > /dev/tty 2>&1 && pass edit {q} < /dev/tty > /dev/tty 2>&1; fi)+reload(tree -Ffi | grep '.gpg' | sed 's/.gpg$//g')" \
+ --bind="ctrl-e:execute(pass edit {} < /dev/tty > /dev/tty 2>&1)+reload(tree -Ffi | grep '.gpg' | sed 's/.gpg$//g')" \
+ --bind="ctrl-r:execute(bash -c '
+echo -n \"Enter new name for {}: \" > /dev/tty;
+read new_name < /dev/tty;
+if [ -n \"\$new_name\" ]; then
+ pass mv {} \"\$new_name\" || echo \"Rename failed.\" > /dev/tty;
+else
+ echo \"No name entered. Rename aborted.\" > /dev/tty;
+fi' < /dev/tty > /dev/tty 2>&1)+reload(tree -Ffi | grep '.gpg' | sed 's/.gpg$//g')" \
+ --bind="ctrl-g:execute(if [ -z {} ]; then read -p \"Can't generate empty password. Press enter to continue...\"; else pass generate -in {} < /dev/tty > /dev/tty 2>&1 && pass edit {} < /dev/tty > /dev/tty 2>&1; fi)+reload(tree -Ffi | grep '.gpg' | sed 's/.gpg$//g')" \
+ --bind="ctrl-x:execute(pass rm {} < /dev/tty > /dev/tty 2>&1)+reload(tree -Ffi | grep '.gpg' | sed 's/.gpg$//g')" \
+ --bind="tab:replace-query"
+)
+
+PASSDATA="$(pass "$PASSFILE")"
+PASSWORD="$(echo "$PASSDATA" | head -n 1)"
+SHOW_PASSDATA=false
+
+if [ "$1" = "-i" ]; then
+ SHOW_PASSDATA=true
+ shift
+fi
+
+if [ "$SHOW_PASSDATA" = true ]; then
+ PASSDATA="$(pass "$PASSFILE")"
+ echo "$PASSDATA"
+else
+ PASSWORD="$(pass show "$PASSFILE" | head -n 1)"
+ echo "$PASSWORD"
+
+ if [ -n "$PASSWORD" ]; then
+ case "$(uname)" in
+ Darwin*)
+ echo "$PASSWORD" | pbcopy # Use pbcopy on macOS
+ ;;
+ Linux*)
+ echo "$PASSWORD" | xclip -selection clipboard # Use xclip on Linux
+ ;;
+ *)
+ echo "Unsupported operating system"
+ ;;
+ esac
+ sleep 0.1
+ fi
+fi
diff --git a/ar/.local/bin/getbib b/ar/.local/bin/getbib
new file mode 100755
index 0000000..121dd6e
--- /dev/null
+++ b/ar/.local/bin/getbib
@@ -0,0 +1,14 @@
+#!/bin/sh
+[ -z "$1" ] && echo "Give either a pdf file or a DOI as an argument." && exit
+
+if [ -f "$1" ]; then
+ # Try to get DOI from pdfinfo or pdftotext output.
+ doi=$(pdfinfo "$1" | grep -io "doi:.*") ||
+ doi=$(pdftotext "$1" 2>/dev/null - | sed -n '/[dD][oO][iI]:/{s/.*[dD][oO][iI]:\s*\(\S\+[[:alnum:]]\).*/\1/p;q}') ||
+ exit 1
+else
+ doi="$1"
+fi
+
+# Check crossref.org for the bib citation.
+curl -s "https://api.crossref.org/works/$doi/transform/application/x-bibtex" -w "\\n"
diff --git a/ar/.local/bin/getcomproot b/ar/.local/bin/getcomproot
new file mode 100755
index 0000000..dbee348
--- /dev/null
+++ b/ar/.local/bin/getcomproot
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+# A helper script for LaTeX/groff files used by `compiler` and `opout`.
+# The user can add the root file of a larger project as a comment as below:
+# % root = mainfile.tex
+# And the compiler script will run on that instead of the opened file.
+
+texroot="$(sed -n 's/^\s*%.*root\s*=\s*\(\S\+\).*/\1/p' "${1}")"
+[ -f "${texroot}" ] && readlink -f "${texroot}" || exit "1"
diff --git a/ar/.local/bin/getkeys b/ar/.local/bin/getkeys
new file mode 100755
index 0000000..79a7872
--- /dev/null
+++ b/ar/.local/bin/getkeys
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+# Print available keys from thesiah
+cat "${XDG_DATA_HOME:-${HOME}/.local/share}"/thesiah/keys/"$1" 2>/dev/null && exit
+echo "Run command with one of the following arguments for info about that program:"
+ls "${XDG_DATA_HOME:-${HOME}/.local/share}"/thesiah/keys
diff --git a/ar/.local/bin/gitfiles b/ar/.local/bin/gitfiles
new file mode 100755
index 0000000..510b032
--- /dev/null
+++ b/ar/.local/bin/gitfiles
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+# Exit immediately if any command fails
+set -e
+
+get_full_file_list() {
+ echo "$existing_files" | awk '!seen[$0]++'
+}
+
+handle_fzf_error() {
+ if [ $? -eq 130 ]; then
+ # If fzf-tmux was interrupted by Ctrl+C (exit code 130), exit gracefully
+ exit 0
+ else
+ # Otherwise, re-raise the error
+ return $?
+ fi
+}
+
+# Get the repository root path and change to the repo root directory
+repo_root=$(git rev-parse --show-toplevel 2>/dev/null)
+cd "$repo_root" || {
+ echo "Failed to change to repository root directory"
+ exit 1
+}
+
+# Determine the base branch (main or master)
+if git show-ref --quiet refs/heads/main; then
+ base_branch=main
+elif git show-ref --quiet refs/heads/master; then
+ base_branch=master
+else
+ base_branch=main
+fi
+
+if [ "$(git remote | head -n 1)" = "origin" ]; then
+ remote="origin"
+elif [ "$(git remote | head -n 1)" = "home" ]; then
+ remote="home"
+else
+ remote="origin"
+fi
+
+merge_base=$(git merge-base HEAD "$base_branch")
+file_list=$(git log --pretty=format: --name-only -n 30 | grep . | awk '!seen[$0]++' | head -n 30)
+
+# Generate the file list and verify each file path
+existing_files=$(echo "$file_list" | while IFS= read -r file; do
+ [ -f "$file" ] && echo "$repo_root/$file"
+done)
+
+# Use fzf-tmux to select from the sorted list
+selected_files=$(get_full_file_list | fzf-tmux \
+ --header "^a: all, ^e: edited, ^f: current branch ^r: recent, ^s: staged, ^u: unpushed" \
+ --preview "bat --color=always {}" \
+ --reverse \
+ --multi \
+ --select-1 \
+ --exit-0 \
+ --bind "ctrl-a:reload(git ls-tree -r HEAD --name-only || handle_fzf_error)" \
+ --bind "ctrl-e:reload(git diff --name-only || handle_fzf_error)" \
+ --bind "ctrl-f:reload(git diff-tree --no-commit-id --name-only -r $merge_base..HEAD || handle_fzf_error)" \
+ --bind "ctrl-r:reload(echo \"$existing_files\" | awk '!seen[\$0]++' || handle_fzf_error)" \
+ --bind "ctrl-s:reload(git diff --cached --name-only || handle_fzf_error)" \
+ --bind "ctrl-u:reload(git diff --name-only $remote/$base_branch..HEAD || handle_fzf_error)" \
+ --bind "change:top" ||
+ handle_fzf_error)
+
+# Check if any files were selected, and exit if not
+[ -z "$selected_files" ] && exit 0
+
+openfiles "$selected_files"
diff --git a/ar/.local/bin/gitopenbranch b/ar/.local/bin/gitopenbranch
new file mode 100755
index 0000000..56c8d12
--- /dev/null
+++ b/ar/.local/bin/gitopenbranch
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+if ! command -v gh >/dev/null 2>&1; then
+ echo "Error: GitHub CLI (gh) is not installed." >&2
+ exit 1
+fi
+
+case "$(uname -s)" in
+Darwin)
+ open_cmd='open'
+ ;;
+*)
+ open_cmd='xdg-open'
+ ;;
+esac
+
+# Check if inside a git repository
+if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
+ current_branch=$(git rev-parse --abbrev-ref HEAD)
+ gh repo view --branch "$current_branch" --web || $open_cmd "$(gh repo view --branch "$current_branch" --json url -q '.url')"
+else
+ echo "Not a git repository."
+ exit 1
+fi
diff --git a/ar/.local/bin/gitstagedfiles b/ar/.local/bin/gitstagedfiles
new file mode 100755
index 0000000..1cdd902
--- /dev/null
+++ b/ar/.local/bin/gitstagedfiles
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# Use fzf to select files and store them in a string with newline as a separator
+IFS='
+'
+files=$(git status -s | awk '$1 != "D" {print $2}' | grep -v '^$' | fzf-tmux --preview "bat --color=always {}" --reverse --multi --select-1 --exit-0)
+
+# Check if any files were selected, and exit if not
+[ -z "$files" ] && exit 0
+
+openfiles "$files"
diff --git a/ar/.local/bin/gitupdate b/ar/.local/bin/gitupdate
new file mode 100755
index 0000000..a214950
--- /dev/null
+++ b/ar/.local/bin/gitupdate
@@ -0,0 +1,91 @@
+#!/bin/sh
+
+set -eu
+
+pidof transmission-daemon >/dev/null && echo "Turn off transmission-daemon first!" && exit 1
+
+# Check if inside a Git repository
+! git rev-parse --is-inside-work-tree >/dev/null 2>&1 && echo "Not a git repository." && exit 1
+
+# Function to generate a message for each status line
+generate_message() {
+ status=$1
+ file=$2
+ if echo "$file" | grep -q '/'; then
+ file_name="${file##*/}"
+ path_name="${file%/*}"
+ dir_name="${path_name##*/}"
+ dir_file_name="$dir_name/$file_name"
+ else
+ dir_file_name="$file"
+ fi
+ case "$status" in
+ " M" | "M ") # Modified
+ echo "modified $dir_file_name"
+ ;;
+ "??") # Untracked (new) files
+ echo "created $dir_file_name"
+ ;;
+ " D" | "D ") # Deleted from the index
+ echo "deleted $dir_file_name"
+ ;;
+ *) # Catch-all for other statuses
+ echo "updated $dir_file_name"
+ ;;
+ esac
+}
+
+# Check for changes in the working directory
+changes=$(git status --porcelain)
+
+# Check for unpushed commits
+unpushed=$(git cherry -v | wc -l)
+
+[ -z "$changes" ] && [ "$unpushed" -eq 0 ] && exit
+
+# Generate default commit message if there are changes
+if [ -n "$changes" ] && [ "$unpushed" -eq 0 ]; then
+ num_changes=$(echo "$changes" | wc -l)
+ if [ "$num_changes" -gt 5 ]; then
+ default="updates"
+ else
+ default=$(echo "$changes" | while IFS= read -r line; do
+ status=$(echo "$line" | cut -c1-2)
+ file=$(echo "$line" | cut -c4-)
+ generate_message "$status" "$file"
+ done | tr '\n' ',' | sed 's/,$//;s/,/, /g;s/"$//')
+ fi
+ message="${1:-$default}"
+
+ # Add and commit changes
+ git add --all .
+ git commit -m "$message"
+else
+ message=$(git cherry -v | awk '{$1=$2=""; print $0}' | sed 's/^ *//' | tail -n 1)
+fi
+
+# Get the current Git branch name
+branch=$(git symbolic-ref -q HEAD | sed -e 's|^refs/heads/||')
+
+# Save current directory and change to the Git repo root
+repo_root=$(git rev-parse --show-toplevel || echo ".")
+cd "$repo_root"
+
+# Pull and rebase before pushing
+git pull --rebase origin "$branch"
+
+# Check if the 'home' remote exists
+if git remote | grep -q "^home$"; then
+ # Push to both 'home' and 'origin'
+ git push home "$branch" && git push origin "$branch"
+else
+ # Push only to 'origin'
+ git push origin "$branch"
+fi
+
+printf "\n[repo] %s(%s): %s\n" "$(basename "$repo_root")" "$branch" "$message"
+
+# Return to the original directory
+cd - >/dev/null
+
+command -v dwmblocks >/dev/null 2>&1 && pkill -RTMIN+18 "${STATUSBAR:-dwmblocks}"
diff --git a/ar/.local/bin/gpt b/ar/.local/bin/gpt
new file mode 100755
index 0000000..393916a
--- /dev/null
+++ b/ar/.local/bin/gpt
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+set -e
+
+# Check if input is piped
+if [ -t 0 ]; then
+ input="$1"
+else
+ input=$(cat | sed -E "s/\x1b\[[0-9;]*m//g")
+fi
+
+# Use a temporary file for the processed content
+tmpfile=$(mktemp /tmp/nvim_buffer_cleaned.XXXXXX)
+
+# Save the input to the temporary file
+echo "$input" >"$tmpfile"
+
+# Process the input and open Neovim directly, ensuring it doesn't suspend
+nvim \
+ -c "GpChatNew" \
+ -c "call append(line('\$')-1, readfile('$tmpfile'))" \
+ -c "normal! Gdd" \
+ -c "startinsert"
+
+# Remove the temporary file after usage
+rm "$tmpfile"
diff --git a/ar/.local/bin/gracefulkill b/ar/.local/bin/gracefulkill
new file mode 100755
index 0000000..918ab79
--- /dev/null
+++ b/ar/.local/bin/gracefulkill
@@ -0,0 +1,25 @@
+#!/bin/bash
+set -euo pipefail
+
+changedVimFocused() {
+ title=$(getFocusedTitle)
+ [[ $title == *VIM\ \+\" ]]
+}
+
+getFocusedConId() {
+ i3-msg -t get_tree | jq 'recurse | objects | select(.type=="con" and .focused==true) | .id'
+}
+
+getFocusedTitle() {
+ id=$(getFocusedConId)
+ i3-msg -t get_tree | jq "recurse | objects | select(.id == $id) | .name"
+}
+
+killFocusedContainer() {
+ id=$(getFocusedConId)
+ i3-msg "[con_id=\"$id\"]" kill
+}
+
+if ! changedVimFocused; then
+ killFocusedContainer
+fi
diff --git a/ar/.local/bin/ifinstalled b/ar/.local/bin/ifinstalled
new file mode 100755
index 0000000..d0bd26b
--- /dev/null
+++ b/ar/.local/bin/ifinstalled
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# Some optional functions in THESIAH require programs not installed by default. I
+# use this little script to check to see if a command exists and if it doesn't
+# it informs the user that they need that command to continue. This is used in
+# various other scripts for clarity's sake.
+
+for x in "$@"; do
+ if ! which "$x" >/dev/null 2>&1 && ! pacman -Qq "$x" >/dev/null 2>&1; then
+ notify-send "📦 $x" "must be installed for this function." && exit 1
+ fi
+done
diff --git a/ar/.local/bin/iwaf b/ar/.local/bin/iwaf
new file mode 100755
index 0000000..7e3aad3
--- /dev/null
+++ b/ar/.local/bin/iwaf
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+_check_wine() {
+ WINE_EXE="$1"
+
+ ls "${WINE_EXE}" >/dev/null 2>/dev/null
+}
+
+_exec_wine() {
+ WINE_EXE="$1"
+ WINE_EXE2="${WINE_EXE%/*}"
+ WINE_EXE2="${WINE_EXE2}/SOOPStreamer.exe"
+
+ echo 'Executing Soop...'
+ wine "${WINE_EXE}"
+ # wine "${WINE_EXE2}"
+}
+
+_install_wine() {
+ # Source URL for the SOOPStreamer installer
+ SRC_URL='https://creatorup.sooplive.co.kr/SOOPStreamer_installer.exe'
+
+ # Create a temporary file for the installer
+ DST_FILE="$(mktemp)"
+ # Ensure the temporary file is removed after the script finishes
+ trap 'rm -f "$DST_FILE"' EXIT
+
+ # Set a temporary HOME for the Wine process to avoid conflicts
+ HOME_ORIGIN="$HOME"
+ HOME_PATCH="$WINEPREFIX/tmp"
+ export HOME="$HOME_PATCH"
+
+ # Download the installer
+ echo 'Downloading SOOP Streamer installer...'
+ curl -s "$SRC_URL" -o "$DST_FILE" || {
+ echo "Failed to download installer!"
+ exit 1
+ }
+
+ # Initialize Wine environment with win32 architecture
+ echo 'Initializing Wine environment...'
+ wineboot >/dev/null 2>&1
+
+ # Check if winetricks is installed, if not, install it
+ if ! command -v winetricks >/dev/null; then
+ sudo pacman --noconfirm --needed -S winetricks || {
+ echo "Failed to install winetricks!"
+ exit 1
+ }
+ fi
+
+ # Install necessary libraries via winetricks
+ echo "Installing required libraries via winetricks..."
+ if ! winetricks -q mfc42 vcrun2008; then
+ echo "Failed to install required libraries!"
+ exit 1
+ fi
+
+ # Run the SOOP Streamer installer without sudo
+ echo 'Running the SOOP Streamer installer...'
+ wine "$DST_FILE" /S >/dev/null 2>&1 && success=true || success=false
+
+ # Clean up temporary Wine HOME directory
+ echo 'Cleaning up...'
+ rm -rf "$HOME_PATCH"
+
+ # Restore original HOME and WINEARCH variables
+ export HOME="$HOME_ORIGIN"
+
+ # Check success status and print the result
+ if $success; then
+ echo 'Installation completed successfully.'
+ else
+ echo 'Installation failed!'
+ fi
+}
+
+# Define a main function
+main() {
+ # Configure environment variables
+ export LANG='en_US.UTF-8'
+ export LC_ALL='en_US.UTF-8'
+ export WINEARCH='win32'
+ export WINEPREFIX="${WINEPREFIX:-${XDG_DATA_HOME:-${HOME}/.local/share}/wine}/soop"
+
+ # Install
+ WINE_EXE="${WINEPREFIX}/drive_c/users/$(whoami)/AppData/Local/SOOP/SOOPPackage.exe"
+ if ! _check_wine "${WINE_EXE}"; then
+ _install_wine "${WINE_EXE}"
+ fi
+
+ # Exec
+ _exec_wine "${WINE_EXE}"
+}
+
+main
diff --git a/ar/.local/bin/lastnvim b/ar/.local/bin/lastnvim
new file mode 100755
index 0000000..b1ab6c9
--- /dev/null
+++ b/ar/.local/bin/lastnvim
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+# Display help message
+usage() {
+ echo "Open the most recent file or the list of old files in fzf edited by nvim."
+ echo ""
+ echo "Usage: ${0##*/} [OPTION]"
+ echo ""
+ echo "Options:"
+ echo " : Open the most recent old file in Neovim."
+ echo " -h, --help : Show this help message."
+ echo " -l, --list : Show all recent files in Neovim using fzf."
+ echo ""
+ echo "Examples:"
+ echo " ${0##*/} # Open the most recent file."
+ echo " ${0##*/} -l # Show all recent files in fzf and select to open."
+ exit 0
+}
+
+# List and handle oldfiles
+list_oldfiles() {
+ # Fetch the oldfiles list from Neovim
+ oldfiles=$(nvim -u NONE --headless +'lua io.write(table.concat(vim.v.oldfiles, "\n") .. "\n")' +qa)
+
+ # Exit if no oldfiles are found
+ [ -z "$oldfiles" ] && {
+ echo "No recent files found in Neovim." >&2
+ exit 1
+ }
+
+ case "$1" in
+ -h | --help)
+ usage
+ ;;
+ -l | --list)
+ # Filter valid files
+ valid_files=$(echo "$oldfiles" | while IFS= read -r file; do
+ [ -f "$file" ] && printf "%s\n" "$file"
+ done)
+
+ # Exit if no valid files exist
+ [ -z "$valid_files" ] && {
+ echo "No valid files found." >&2
+ exit 1
+ }
+
+ # Use fzf to select files
+ selected_files=$(echo "$valid_files" |
+ fzf-tmux \
+ --multi \
+ --preview 'bat -n --color=always --line-range=:500 {} 2>/dev/null || echo "Error previewing file"' \
+ --height=70% \
+ --reverse)
+
+ # Exit if no files were selected
+ [ -z "$selected_files" ] && exit 1
+
+ # Open selected files in Neovim
+ openfiles "$selected_files"
+ ;;
+ *)
+ # Open the most recent file
+ for file in $oldfiles; do
+ if [ -f "$file" ]; then
+ openfiles "$file"
+ exit 0
+ fi
+ done
+
+ echo "No valid recent files found." >&2
+ exit 1
+ ;;
+ esac
+}
+
+# Parse command-line arguments
+list_oldfiles "$@"
diff --git a/ar/.local/bin/lfub b/ar/.local/bin/lfub
new file mode 100755
index 0000000..b743989
--- /dev/null
+++ b/ar/.local/bin/lfub
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+# This is a wrapper script for lf that allows it to create image previews with
+# ueberzug. This works in concert with the lf configuration file and the
+# lf-cleaner script.
+
+set -e
+
+cleanup() {
+ exec 3>&-
+ rm "$FIFO_UEBERZUG"
+}
+
+if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ]; then
+ lf "$@"
+else
+ [ ! -d "$HOME/.cache/lf" ] && mkdir -p "$HOME/.cache/lf"
+ export FIFO_UEBERZUG="$HOME/.cache/lf/ueberzug-$$"
+ mkfifo "$FIFO_UEBERZUG"
+ ueberzug layer -s -p json <"$FIFO_UEBERZUG" &
+ exec 3>"$FIFO_UEBERZUG"
+ trap cleanup HUP INT QUIT TERM PWR EXIT
+ lf "$@" 3>&-
+fi
diff --git a/ar/.local/bin/linkhandler b/ar/.local/bin/linkhandler
new file mode 100755
index 0000000..7dccafc
--- /dev/null
+++ b/ar/.local/bin/linkhandler
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# Feed script a url or file location.
+# If an image, it will view in nsxiv,
+# if a video or gif, it will view in mpv
+# if a music file or pdf, it will download,
+# otherwise it opens link in browser.
+
+if [ -z "$1" ]; then
+ url="$(xclip -o)"
+else
+ url="$1"
+fi
+
+case "$url" in
+*mkv | *webm | *mp4 | *youtube.com/watch* | *youtube.com/playlist* | *youtube.com/shorts* | *youtu.be* | *hooktube.com* | *bitchute.com* | *videos.lukesmith.xyz* | *odysee.com*)
+ setsid -f mpv -quiet "$url" >/dev/null 2>&1
+ ;;
+*png | *jpg | *jpe | *jpeg | *gif | *webp)
+ curl -sL "$url" >"/tmp/$(echo "$url" | sed "s/.*\///;s/%20/ /g")" && nsxiv -a "/tmp/$(echo "$url" | sed "s/.*\///;s/%20/ /g")" >/dev/null 2>&1 &
+ ;;
+*pdf | *cbz | *cbr)
+ curl -sL "$url" >"/tmp/$(echo "$url" | sed "s/.*\///;s/%20/ /g")" && zathura "/tmp/$(echo "$url" | sed "s/.*\///;s/%20/ /g")" >/dev/null 2>&1 &
+ ;;
+*mp3 | *flac | *opus | *mp3?source*)
+ qndl "$url" 'curl -LO' >/dev/null 2>&1
+ ;;
+*)
+ [ -f "$url" ] && setsid -f "$TERMINAL" -e "$EDITOR" "$url" >/dev/null 2>&1 || setsid -f "$BROWSER" "$url" >/dev/null 2>&1
+ ;;
+esac
diff --git a/ar/.local/bin/maimpick b/ar/.local/bin/maimpick
new file mode 100755
index 0000000..06d9092
--- /dev/null
+++ b/ar/.local/bin/maimpick
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+# This is bound to Shift+PrintScreen by default, requires maim. It lets you
+# choose the kind of screenshot to take, including copying the image or even
+# highlighting an area to copy. scrotcucks on suicidewatch right now.
+
+# variables
+output_dir="${XDG_PICTURES_DIR:-${HOME}/Pictures}/screenshots/"
+output="$(date '+%y%m%d-%H%M-%S').png"
+xclip_cmd="xclip -sel clip -t image/png"
+ocr_cmd="xclip -sel clip"
+
+[ -d "$output_dir" ] || mkdir -p "$output_dir"
+
+case "$(printf "a selected area\\ncurrent window\\nfull screen\\na selected area (copy)\\ncurrent window (copy)\\nfull screen (copy)\\ncopy selected image to text" | dmenu -l 7 -i -p "Screenshot which area?")" in
+"a selected area") maim -u -s "$output_dir"pic-selected-"${output}" ;;
+"current window") maim -B -q -d 0.2 -i "$(xdotool getactivewindow)" "$output_dir"pic-window-"${output}" ;;
+"full screen") maim -q -d 0.2 "$output_dir"pic-full-"${output}" ;;
+"a selected area (copy)") maim -u -s | ${xclip_cmd} ;;
+"current window (copy)") maim -q -d 0.2 -i "$(xdotool getactivewindow)" | ${xclip_cmd} ;;
+"full screen (copy)") maim -q -d 0.2 | ${xclip_cmd} ;;
+"copy selected image to text") tmpfile=$(mktemp /tmp/ocr-XXXXXX.png) && maim -u -s >"$tmpfile" && tesseract "$tmpfile" - -l eng | ${ocr_cmd} && rm "$tmpfile" ;;
+esac
diff --git a/ar/.local/bin/mbackup b/ar/.local/bin/mbackup
new file mode 100755
index 0000000..71856b1
--- /dev/null
+++ b/ar/.local/bin/mbackup
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+batch_file="${XDG_MUSIC_DIR:-${HOME}/Music}/.music.txt"
+backup="/tmp/music_backup.txt"
+[ -f "$batch_file" ] || exit 1
+echo "Backing up music files..."
+[ -f "$backup" ] && rm -f "$backup" >/dev/null 2>&1
+while read -r line; do
+ video_id="${line#* }"
+ echo "https://www.youtube.com/watch?v=$video_id" >>"$backup"
+done <"$batch_file"
+yt-dlp --continue --embed-metadata --ignore-errors --no-force-overwrites --verbose --audio-format best --audio-quality 0 --batch-file "$backup" --extract-audio --recode-video mp3 --output "${XDG_MUSIC_DIR:-${HOME}/Music}/%(artist)s - %(title)s.%(ext)s" >/dev/null 2>&1
+rm -f "$backup" >/dev/null 2>&1
+echo "Done!"
diff --git a/ar/.local/bin/monitorbright b/ar/.local/bin/monitorbright
new file mode 100755
index 0000000..2de2e5d
--- /dev/null
+++ b/ar/.local/bin/monitorbright
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+monitor=$(xrandr --query | grep -i '\sconnected' | grep '[0-9]x[0-9]' | grep -i 'primary' | cut -d ' ' -f1)
+[ -z "$monitor" ] && monitor=$(xrandr --query | grep -i '\sconnected' | grep '[0-9]x[0-9]' | cut -d ' ' -f1)
+case "$monitor" in
+*DP* | *HDMI*)
+ current_brightness=$(xrandr --verbose | grep -i "^$monitor connected" -A5 | grep -i "Brightness:" | cut -d ' ' -f2)
+ [ -z "$current_brightness" ] && exit 1
+ if [ "$#" -eq 2 ]; then
+ scale_change=$(echo "$2 / 100" | bc -l)
+ case "$1" in
+ "-inc") new_brightness=$(echo "$current_brightness + $scale_change" | bc -l) ;;
+ "-dec") new_brightness=$(echo "$current_brightness - $scale_change" | bc -l) ;;
+ *) echo "Invalid argument $1. Use -inc or -dec." && exit 1 ;;
+ esac
+ new_brightness=$(echo "if ($new_brightness > 1) 1 else if ($new_brightness < 0) 0 else $new_brightness" | bc -l)
+ xrandr --output "$monitor" --brightness "$new_brightness"
+ current_brightness=$(echo "$new_brightness * 100" | bc -l)
+ else
+ current_brightness=$(echo "$current_brightness * 100" | bc -l)
+ fi
+ printf "🪟%.0f%%\n" "$current_brightness"
+ ;;
+esac
diff --git a/ar/.local/bin/mounter b/ar/.local/bin/mounter
new file mode 100755
index 0000000..6f294d2
--- /dev/null
+++ b/ar/.local/bin/mounter
@@ -0,0 +1,181 @@
+#!/bin/bash
+
+# Mounts Android Phones and USB drives (encrypted or not). This script will
+# replace the older `dmenumount` which had extra steps and couldn't handle
+# encrypted drives.
+# TODO: Try decrypt for drives in crtypttab
+# TODO: Add some support for connecting iPhones (although they are annoying).
+
+IFS='
+'
+# Function for escaping cell-phone names.
+escape() { echo "$@" | iconv -cf UTF-8 -t ASCII//TRANSLIT | tr -d '[:punct:]' | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | sed "s/-\+/-/g;s/\(^-\|-\$\)//g"; }
+
+# Function for prompting user for a mountpoint.
+getmount() {
+ [ -z "$1" ] && mp="$(find "/media/$USER" /mnt -mindepth 1 -maxdepth 1 -type d 2>/dev/null | dmenu -i -p "Mount this drive where?")" || mp="$1"
+ test -n "$mp"
+ if [ ! -d "$mp" ]; then
+ mkdiryn=$(printf "No\\nYes" | dmenu -i -p "$mp does not exist. Create it?")
+ [ "$mkdiryn" = "Yes" ] && (mkdir -p "$mp" 2>/dev/null || sudo -A mkdir -p "$mp")
+ fi
+}
+
+attemptmount() {
+ # Attempt to mount without a mountpoint, to see if drive is in fstab.
+ mplabel=$(sudo lsblk -no "label" "$chosen")
+ mp="/media/$USER/$mplabel"
+ [ -n "$mplabel" ] || return
+ if [ ! -d "$mp" ] && [ ! -d "/mnt/$mplabel" ]; then
+ getmount "$mp" && sudo -A mount "$chosen" "$mp" >/dev/null 2>&1 || return 1
+ elif [ -d "$mp" ] && [ ! -d "/mnt/$mplabel" ]; then
+ sudo -A mount "$chosen" "$mp" >/dev/null 2>&1 || return 1
+ elif [ -d "/mnt/$mplabel" ]; then
+ getmount "/mnt/$mplabel" && sudo -A mount "$chosen" "/mnt/$mplabel" >/dev/null 2>&1 || return 1
+ else
+ sudo -A mount "$chosen" >/dev/null 2>&1 || return 1
+ fi
+}
+
+# Check for phones.
+phones="$(simple-mtpfs -l 2>/dev/null | sed "s/^/📱/")"
+mountedphones="$(grep "simple-mtpfs" /etc/mtab)"
+# If there are already mounted phones, remove them from the list of mountables.
+[ -n "$mountedphones" ] && phones="$(for phone in $phones; do
+ for mounted in $mountedphones; do
+ escphone="$(escape "$phone")"
+ [[ "$mounted" =~ $escphone ]] && break 1
+ done && continue 1
+ echo "$phone"
+done)"
+
+# Check for drives.
+lsblkoutput="$(sudo lsblk -rpo "uuid,name,type,size,label,mountpoint,fstype")"
+# Get all LUKS drives
+allluks="$(echo "$lsblkoutput" | grep crypto_LUKS)"
+# Get a list of the LUKS drive UUIDs already decrypted.
+decrypted="$(find /dev/disk/by-id/dm-uuid-CRYPT-LUKS2-* | sed "s|.*LUKS2-||;s|-.*||")"
+# Functioning for formatting drives correctly for dmenu:
+filter() { sed "s/ /:/g" | awk -F':' '$7==""{printf "%s%s (%s) %s\n",$1,$3,$5,$6}'; }
+
+# Get only LUKS drives that are not decrypted.
+unopenedluks="$(for drive in $allluks; do
+ uuid="${drive%% *}"
+ uuid="${uuid//-/}" # This is a bashism.
+ [ -n "$decrypted" ] && for open in $decrypted; do
+ [ "$uuid" = "$open" ] && break 1
+ done && continue 1
+ echo "🔒 $drive"
+done | filter)"
+
+# Get all normal, non-encrypted or decrypted partitions that are not mounted.
+normalparts="$(echo "$lsblkoutput" | grep -v crypto_LUKS | grep 'part\|rom\|crypt' | sed "s/^/💾 /" | filter)"
+
+# Get all available IP addresses with open Samba shares in the wlan0 subnet, excluding eth0 IP
+smbips="$(sudo arp-scan --interface=wlan0 --localnet | grep -vEi '(EFM Networks|locally administered|DUP:)' | awk '/^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/{print $1}')"
+
+# Get currently mounted CIFS shares
+mountedsmbs="$(grep 'cifs' /etc/mtab)"
+
+for smbip in $smbips; do
+ smb=$(
+ smbclient -L "$smbip" -U% -A /dev/stdin <<EOF 2>/dev/null | awk '/Disk/ {print $1}' | grep -vEi '(ADMIN|\w)\$'
+username=$(whoami)
+password=$(pass show default/default)
+EOF
+ )
+ win=$(
+ smbclient -L "$smbip" -U% -A /dev/stdin <<EOF 2>/dev/null | awk '/Disk/ {print $1}' | grep -vEi '(ADMIN|\w)\$'
+username=$(whoami)
+password=$(pass show default/windows)
+EOF
+ )
+ while IFS= read -r share; do
+ if ! echo "$smbshares" | grep -q "$share"; then
+ smbshares+="//$smbip/$share"$'\n'
+ fi
+ done <<<"$smb"
+ while IFS= read -r share; do
+ if ! echo "$smbshares" | grep -q "$share"; then
+ smbshares+="//$smbip/$share"$'\n'
+ fi
+ done <<<"$win"
+done
+
+smbshares="$(echo "$smbshares" | sed '/^$/d')"
+
+[ -n "$smbshares" ] && smbs="$(echo "$smbshares" | while IFS= read -r smb; do
+ [ -n "$mountedsmbs" ] && for mountedsmb in $mountedsmbs; do
+ mountedsmb="${mountedsmb%% *}"
+ [[ "$mountedsmb" =~ $smb ]] && break 1
+ done && continue 1
+ echo "📡 $smb"
+done)"
+
+# Add all to one variable. If no mountable drives found, exit.
+alldrives="$(echo "$phones
+$unopenedluks
+$normalparts
+$smbs" | sed "/^$/d;s/ *$//")"
+
+# Quit the script if a sequential command fails.
+set -e
+test -n "$alldrives" || { echo "No mountable drives" && exit; }
+
+# Feed all found drives to dmenu and get user choice.
+chosen="$(echo "$alldrives" | dmenu -p "Mount which drive?" -i)"
+case "$chosen" in
+💾*)
+ chosen="${chosen%% *}"
+ chosen="${chosen:1}" # This is a bashism.
+ parttype="$(echo "$lsblkoutput" | grep "$chosen")"
+ attemptmount || {
+ getmount
+ case "${parttype##* }" in
+ vfat) sudo -A mount -t vfat "$chosen" "$mp" -o rw,umask=0000 ;;
+ btrfs) sudo -A mount "$chosen" "$mp" ;;
+ *) sudo -A mount "$chosen" "$mp" -o uid="$(id -u)",gid="$(id -g)" ;;
+ esac
+ }
+ notify-send "💾 Drive Mounted." "$chosen mounted to $mp."
+ ;;
+🔒*)
+ chosen="${chosen%% *}"
+ chosen="${chosen:1}" # This is a bashism.
+ # Number the drive.
+ while [ -e "/dev/mapper/usb$num" ]; do
+ num=$((num + 1))
+ done
+ pass show default/default | sudo cryptsetup open "$chosen" "usb$num" || ${TERMINAL:-st} -n floatterm -g 60x1 -e sudo cryptsetup open "$chosen" "usb$num"
+ # Check if now decrypted.
+ test -b "/dev/mapper/usb$num"
+ chosen="/dev/mapper/usb$num"
+ attemptmount || {
+ getmount
+ sudo -A mount "/dev/mapper/usb$num" "$mp" -o uid="$(id -u)",gid="$(id -g)" || sudo -A mount "/dev/mapper/usb$num" "$mp"
+ }
+ notify-send "🔓 Decrypted drive Mounted." "$chosen decrypted and mounted to $mp."
+ ;;
+📱*)
+ notify-send "❗ Note" "Remember to allow file access on your phone now."
+ getmount
+ number="${chosen%%:*}"
+ number="${chosen:1}" # This is a bashism.
+ sudo -A simple-mtpfs -o allow_other -o fsname="simple-mtpfs-$(escape "$chosen")" --device "$number" "$mp"
+ notify-send "🤖 Android Mounted." "Android device mounted to $mp."
+ ;;
+📡*)
+ chosen="${chosen##* }"
+ path="${chosen##*/}"
+ if getmount "/media/$USER/$path"; then
+ sudo -A mount -t cifs -o username="$(whoami)",password="$(pass show default/default)" "$chosen" "$mp" 2>/dev/null ||
+ sudo -A mount -t cifs -o username="$(whoami)",password="$(pass show default/windows)" "$chosen" "$mp" 2>/dev/null
+ else
+ notify-send "❌ Failed to mount samba" "$chosen
+Check if $(whoami) is added to samba user list."
+ sudo rm -rf "$mp"
+ exit
+ fi
+ notify-send "📡 Samba successfully mounted to" "$mp"
+ ;;
+esac
diff --git a/ar/.local/bin/mpdmenu b/ar/.local/bin/mpdmenu
new file mode 100755
index 0000000..4febe9d
--- /dev/null
+++ b/ar/.local/bin/mpdmenu
@@ -0,0 +1,111 @@
+#!/bin/bash
+
+all_name='[ALL]'
+PLAYLIST_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/mpd/playlists"
+
+# Functions
+d_artist() {
+ mpc list artist | sort -f | dmenu -p artist "${dmenu_args[@]}" || exit
+}
+
+d_album() {
+ local artist="$1"
+ local albums
+
+ mapfile -t albums < <(mpc list album artist "$artist")
+ if ((${#albums[@]} > 1)); then
+ {
+ printf '%s\n' "$all_name"
+ printf '%s\n' "${albums[@]}" | sort -f
+ } | dmenu -p album "${dmenu_args[@]}" || exit
+ else
+ # We only have one album, so just use that.
+ printf '%s\n' "${albums[0]}"
+ fi
+}
+
+d_title() {
+ titles=$(mpc list title | sort -f)
+ [[ -z "$titles" ]] && exit
+ echo "$titles" | dmenu -p "Select title:" "${dmenu_args[@]}"
+}
+
+d_playlist() {
+ playlists=$(find "$PLAYLIST_DIR" \( -type f -o -type l \) -name "*.m3u" -exec basename {} .m3u \;)
+
+ selected_playlist=$(echo "$playlists" | sort | dmenu -i -p "Select Playlist:" "${dmenu_args[@]}")
+
+ printf '%s' "$selected_playlist"
+}
+
+d_queue() {
+ format="%position% %title%"
+ extra_format="(%artist% - %album%)"
+
+ # If all tracks are from the same artist and album, no need to display that
+ num_extras=$(mpc playlist -f "$extra_format" | sort | uniq | wc -l)
+ ((num_extras == 1)) || format+=" $extra_format"
+
+ track=$(mpc playlist -f "$format" | dmenu -p track "${dmenu_args[@]}")
+ printf '%s' "${track%% *}"
+}
+
+# Parse arguments (if any)
+i=2
+for arg in "$@"; do
+ if [[ $arg == :: ]]; then
+ dmenu_args=("${@:$i}")
+ break
+ fi
+
+ case "$arg" in
+ -l) mode="library" ;;
+ -p) mode="playlist" ;;
+ -q) mode="queue" ;;
+ esac
+
+ ((i + 1))
+done
+
+# Main Menu
+[[ -z "$mode" ]] && mode=$(echo -e "library\nplaylist\nqueue" | dmenu -p "Choose mode:" "${dmenu_args[@]}")
+
+# Mode Handling
+case "$mode" in
+"library")
+ search_type=$(echo -e "artist\ntitle" | dmenu -p "Search by:" "${dmenu_args[@]}")
+ case "$search_type" in
+ "artist")
+ artist=$(d_artist)
+ album=$(d_album "$artist")
+ mpc clear
+ if [[ $album == "$all_name" ]]; then
+ mpc find artist "$artist" | sort | mpc add
+ else
+ mpc find artist "$artist" album "$album" | sort | mpc add
+ fi
+ ;;
+ "title")
+ title=$(d_title)
+ mpc clear
+ mpc find title "$title" | sort | mpc add
+ ;;
+ *) exit ;;
+ esac
+ mpc random on
+ mpc play 2>/dev/null
+ ;;
+"playlist")
+ mpc clear
+ mpc load "$(d_playlist)" 2>/dev/null || exit
+ mpc random on
+ mpc play 2>/dev/null
+ ;;
+"queue")
+ ! mpc playlist | grep -q '.' && exit
+ mpc play "$(d_queue)" 2>/dev/null || exit
+ ;;
+*) exit ;;
+esac
+
+sleep 0.5 && mpc volume 50
diff --git a/ar/.local/bin/mpvplay b/ar/.local/bin/mpvplay
new file mode 100755
index 0000000..967f47f
--- /dev/null
+++ b/ar/.local/bin/mpvplay
@@ -0,0 +1,189 @@
+#!/bin/sh
+
+MOUNT_SCRIPT="${XDG_SCRIPTS_HOME:-${HOME}/.local/bin}/ecrypt"
+DB_PATH="$HOME/.local/share/history/mpv.sqlite"
+
+check_mount() { findmnt "$HOME/Private" >/dev/null || $MOUNT_SCRIPT; }
+
+check_unmount() { findmnt "$HOME/Private" >/dev/null && $MOUNT_SCRIPT; }
+
+play_url() {
+ url=$(xclip -selection clipboard -o)
+ [ -n "$url" ] && echo "$url" | grep -E '^https?://' >/dev/null || return 1
+ notify-send "📽️ Playing video from URL:" "$url"
+ mpv "$url" || exit
+}
+
+play_media() {
+ if echo "$1" | grep -q ".*\.m3u$"; then
+ playlist_file="${1#--playlist=}"
+ if grep -q "/home/$USER/Private" "$playlist_file"; then
+ mpv "$@" && check_unmount || exit
+ else
+ $MOUNT_SCRIPT && mpv "$@" || exit
+ fi
+ elif echo "$1" | grep -q "/home/$USER/Private"; then
+ mpv "$@" && check_unmount || exit
+ else
+ $MOUNT_SCRIPT && mpv "$@" || exit
+ fi
+}
+
+play_playlist() { play_media "--playlist=$1"; }
+
+tmp_playlist() {
+ playlistdir="$HOME/.config/mpv/playlists"
+ [ -d "$playlistdir" ] || mkdir -p "$playlistdir"
+ tmplist="$playlistdir/tmplist.m3u"
+ [ -f "$tmplist" ] && rm -rf "$tmplist"
+ find "$1" -maxdepth 1 -type f \( -iname "*.mp4" -o -iname "*.mkv" -o -iname "*.mov" -o -iname "*.flv" -o -iname "*.wmv" -o -iname "*.webm" -o -iname "*.mpeg" -o -iname "*.mpg" -o -iname "*.avi" -o -iname "*.ts" -o -iname "*.3gp" -o -iname "*.rmvb" \) \
+ -exec echo {} \; >>"$tmplist"
+ play_playlist "$tmplist"
+ rm -rf "$tmplist"
+}
+
+list_and_play() {
+ dir=$1
+ CHOICE=$(printf "List files\nEnter filenames" | dmenu -i -p "Choose an option:")
+ case "$CHOICE" in
+ "Enter filenames")
+ search_term=$(echo | dmenu -i -p "File names:")
+ [ -z "$search_term" ] && echo "Invalid search term \"$search_term\"" && exit
+ notify-send "🔎 Finding videos named with '$search_term'.."
+ files=$(find "$dir" -type f \( -iname "*.mp4" -o -iname "*.mkv" -o -iname "*.mov" -o -iname "*.flv" -o -iname "*.wmv" -o -iname "*.webm" -o -iname "*.mpeg" -o -iname "*.mpg" -o -iname "*.avi" -o -iname "*.ts" -o -iname "*.3gp" -o -iname "*.rmvb" \) -iname "*$search_term*" | sort)
+ [ -z "$files" ] && echo "No files named with \"$search_term\"." && exit
+ tmpplaylist=$(mktemp /tmp/mpv_playlist_XXXXXX.m3u)
+ echo "$files" | while read -r file; do
+ echo "$file"
+ done >"$tmpplaylist"
+ play_playlist "$tmpplaylist"
+ rm -rf "$tmpplaylist"
+ ;;
+ "List files")
+ files_with_paths=$(find "$dir" -mindepth 1 -maxdepth 1 -type f \( -iname "*.mp4" -o -iname "*.mkv" -o -iname "*.mov" -o -iname "*.flv" -o -iname "*.wmv" -o -iname "*.webm" -o -iname "*.mpeg" -o -iname "*.mpg" -o -iname "*.avi" -o -iname "*.ts" -o -iname "*.3gp" -o -iname "*.rmvb" \) | sort)
+ SELECTED_FILE=$(printf "All files\n%s" "$files_with_paths" | sed 's!.*/!!' | dmenu -i -l 21 -p "Select a file:")
+ [ -z "$SELECTED_FILE" ] && echo "No file selected." && exit
+ [ "$SELECTED_FILE" = "All files" ] && tmp_playlist "$dir" && return
+ FULL_PATH="$(echo "$files_with_paths" | grep -F "$SELECTED_FILE")"
+ [ -f "$FULL_PATH" ] && play_media "$FULL_PATH" && return
+ ;;
+ *) return ;;
+ esac
+}
+
+history_play() {
+ # Check if the database exists
+ if [ ! -f "$DB_PATH" ]; then
+ echo "Error: SQLite database not found at $DB_PATH" >&2
+ exit 1
+ fi
+
+ # Query the database for the latest distinct files by path, formatting time_pos as HH:MM:SS
+ HISTORY=$(
+ sqlite3 "$DB_PATH" <<EOF
+WITH LatestFiles AS (
+ SELECT path, title, time_pos, MAX(date) AS max_date
+ FROM loaded_items
+ GROUP BY path
+),
+FormattedHistory AS (
+ SELECT
+ path,
+ title,
+ CASE
+ WHEN time_pos IS NOT NULL THEN
+ printf('%02d:%02d:%02d',
+ time_pos / 3600,
+ (time_pos % 3600) / 60,
+ time_pos % 60
+ )
+ ELSE '00:00:00'
+ END AS formatted_time,
+ max_date
+ FROM LatestFiles
+)
+SELECT path || ' | ' || title || ' | ' || formatted_time
+FROM FormattedHistory
+ORDER BY max_date DESC;
+EOF
+ )
+
+ # Check if there are any results
+ if [ -z "$HISTORY" ]; then
+ echo "No history items found in the database." >&2
+ exit 1
+ fi
+
+ # Create a temporary file for filtered results
+ TEMP_FILE=$(mktemp)
+
+ # Filter out entries with non-existing files
+ echo "$HISTORY" | while IFS= read -r line; do
+ FILE_PATH=$(printf '%s\n' "$line" | awk -F ' \\| ' '{print $1}')
+ if [ -f "$FILE_PATH" ]; then
+ printf '%s\n' "$line" >>"$TEMP_FILE"
+ fi
+ done
+
+ # Check if there are valid entries after filtering
+ if [ ! -s "$TEMP_FILE" ]; then
+ echo "No valid history items found (all files missing)." >&2
+ rm -f "$TEMP_FILE"
+ exit 1
+ fi
+
+ # Display results in dmenu and get the user's choice
+ CHOSEN=$(dmenu -i -l 20 -p "Choose a file to play:" <"$TEMP_FILE")
+ rm -f "$TEMP_FILE"
+
+ # Check if the user made a selection
+ if [ -z "$CHOSEN" ]; then
+ echo "No file selected." >&2
+ exit 1
+ fi
+
+ # Extract the file path and formatted time position from the selected item
+ FILE_PATH=$(printf '%s\n' "$CHOSEN" | awk -F ' \\| ' '{print $1}')
+ FORMATTED_TIME=$(printf '%s\n' "$CHOSEN" | awk -F ' \\| ' '{print $3}')
+
+ # Convert the formatted time back to seconds for mpv
+ TIME_POS=$(printf '%s\n' "$FORMATTED_TIME" | awk -F: '{print ($1 * 3600) + ($2 * 60) + $3}')
+
+ # Play the file with mpv, resuming from the saved time position
+ if [ "$TIME_POS" -gt 0 ]; then
+ mpv --start="$TIME_POS" "$FILE_PATH"
+ else
+ mpv "$FILE_PATH"
+ fi
+}
+
+CONTENT_CHOICE=$(printf "URL\nLocal Files\nPlaylist\nHistory" | dmenu -i -p "Choose media source:")
+case "$CONTENT_CHOICE" in
+"URL") play_url ;;
+"Playlist")
+ PLAYLIST=$(find "$HOME/.config/mpv/playlists" -maxdepth 1 -type f -name "*.m3u" -exec basename {} .m3u \; | dmenu -i -p "Select a playlist:")
+ [ -z "$PLAYLIST" ] && exit
+ play_playlist "$HOME/.config/mpv/playlists/$PLAYLIST.m3u"
+ ;;
+"Local Files")
+ check_mount
+ printf "%s\n%s\n%s\n%s\n%s\n%s\n" "$HOME/Downloads" "$HOME/Private" "$HOME/Torrents/complete" "$HOME/Videos" "/media/$USER" "/mnt/second" | dmenu -i -p "Choose your initial directory:" | {
+ read -r init_dir
+ [ -z "$init_dir" ] && $MOUNT_SCRIPT && exit
+ SELECTED_DIR="$init_dir"
+ while true; do
+ SUBDIR_OPTIONS="$(find "$SELECTED_DIR" -mindepth 1 -maxdepth 1 -type d ! -name ".*" -printf "%P\n" | sort)"
+ [ -z "$SUBDIR_OPTIONS" ] && list_and_play "$SELECTED_DIR" && break
+ OPTIONS="All files\n$SUBDIR_OPTIONS"
+ SELECTED_RELATIVE_DIR="$(printf "%b" "$OPTIONS" | dmenu -i -p "Select a directory or 'All files':")"
+ [ -z "$SELECTED_RELATIVE_DIR" ] && echo "No relative directory." && exit
+ [ "$SELECTED_RELATIVE_DIR" = "All files" ] && list_and_play "$SELECTED_DIR" && break
+ SELECTED_DIR="$SELECTED_DIR/$SELECTED_RELATIVE_DIR"
+ done
+ }
+ ;;
+"History") history_play ;;
+*) exit ;;
+esac
+
+trap 'check_unmount; exit' EXIT INT
diff --git a/ar/.local/bin/noisereduce b/ar/.local/bin/noisereduce
new file mode 100755
index 0000000..709b257
--- /dev/null
+++ b/ar/.local/bin/noisereduce
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+usage() {
+ printf "Usage : noisereduce <input video file> <output video file>\n"
+ exit 1
+}
+
+# Tests for requirements
+ifinstalled ffmpeg || {
+ echo >&2 "We require 'ffmpeg' but it's not installed."
+ exit 1
+}
+ifinstalled sox || {
+ echo >&2 "We require 'ffmpeg' but it's not installed."
+ exit 1
+}
+
+if [ "$#" -ne 2 ]; then
+ usage
+fi
+
+if [ ! -e "$1" ]; then
+ printf "File not found: %s\n" "$1"
+ exit
+fi
+
+if [ -e "$2" ]; then
+ printf "File %s already exists, overwrite? [y/N]\n: " "$2"
+ read -r yn
+ case $yn in
+ [Yy]*) ;;
+ *) exit ;;
+ esac
+fi
+
+inBasename=$(basename "$1")
+inExt="${inBasename##*.}"
+
+isVideoStr=$(ffprobe -v warning -show_streams "$1" | grep codec_type=video)
+if [ -n "$isVideoStr" ]; then
+ isVideo=1
+ printf "Detected %s as a video file\n" "$inBasename"
+else
+ isVideo=0
+ printf "Detected %s as an audio file\n" "$inBasename"
+fi
+
+printf "Sample noise start time [00:00:00]: "
+read -r sampleStart
+if [ -z "$sampleStart" ]; then sampleStart="00:00:00"; fi
+printf "Sample noise end time [00:00:00.900]: "
+read -r sampleEnd
+if [ -z "$sampleEnd" ]; then sampleEnd="00:00:00.900"; fi
+printf "Noise reduction amount [0.21]: "
+read -r sensitivity
+if [ -z "$sensitivity" ]; then sensitivity="0.21"; fi
+
+tmpVidFile="/tmp/noiseclean_tmpvid.$inExt"
+tmpAudFile="/tmp/noiseclean_tmpaud.wav"
+noiseAudFile="/tmp/noiseclean_noiseaud.wav"
+noiseProfFile="/tmp/noiseclean_noise.prof"
+tmpAudCleanFile="/tmp/noiseclean_tmpaud-clean.wav"
+
+printf "Cleaning noise on %s...\n" "$1"
+
+if [ $isVideo -eq "1" ]; then
+ ffmpeg -v warning -y -i "$1" -qscale:v 0 -vcodec copy -an "$tmpVidFile"
+ ffmpeg -v warning -y -i "$1" -qscale:a 0 "$tmpAudFile"
+else
+ cp "$1" "$tmpAudFile"
+fi
+ffmpeg -v warning -y -i "$1" -vn -ss "$sampleStart" -t "$sampleEnd" "$noiseAudFile"
+sox "$noiseAudFile" -n noiseprof "$noiseProfFile"
+sox "$tmpAudFile" "$tmpAudCleanFile" noisered "$noiseProfFile" "$sensitivity"
+if [ $isVideo -eq "1" ]; then
+ ffmpeg -v warning -y -i "$tmpAudCleanFile" -i "$tmpVidFile" -vcodec copy -qscale:v 0 -qscale:a 0 "$2"
+else
+ cp "$tmpAudCleanFile" "$2"
+fi
+
+printf "Done"
diff --git a/ar/.local/bin/openfiles b/ar/.local/bin/openfiles
new file mode 100755
index 0000000..9aba756
--- /dev/null
+++ b/ar/.local/bin/openfiles
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+if ! command -v nvim >/dev/null 2>&1; then
+ echo "Error: 'nvim' is not installed." >&2
+ exit 1
+fi
+
+IFS='
+'
+
+files=$*
+
+for file in $files; do
+ files_list="$files_list \"$(realpath "$file")\""
+done
+
+eval "set -- $files_list"
+
+count=$#
+
+case "$count" in
+2)
+ ${EDITOR:-nvim} -O +'silent! normal g;' "$@"
+ ;;
+3)
+ ${EDITOR:-nvim} -O "$1" -c 'wincmd j' -c "silent! vsplit $2" -c "silent! split $3"
+ ;;
+4)
+ ${EDITOR:-nvim} -O "$1" -c "silent! vsplit $2" -c "silent! split $3" -c 'wincmd h' -c "silent! split $4"
+ ;;
+*)
+ ${EDITOR:-nvim} "$@"
+ ;;
+esac
diff --git a/ar/.local/bin/opensessions b/ar/.local/bin/opensessions
new file mode 100755
index 0000000..6f9f236
--- /dev/null
+++ b/ar/.local/bin/opensessions
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+# split the selected directories into an array
+dirs="$*"
+
+# filter out any empty selections
+dirs=$(echo "$dirs" | tr -s ' ' '\n' | sed '/^$/d')
+[ -z "$dirs" ] && exit 0
+
+# function to clean and create a valid session name
+get_session_name() {
+ basename "$1" | sed 's/[^a-zA-Z0-9]/_/g'
+}
+
+set -- $dirs
+
+# handle session creation for multiple selected directories
+for dir in $dirs; do
+ if [ -d "$dir" ]; then
+ session_name=$(get_session_name "$dir")
+ if ! tmux has-session -t "$session_name" 2>/dev/null; then
+ tmux new-session -d -s "$session_name" -c "$dir"
+ if git -C "$dir" rev-parse --is-inside-work-tree >/dev/null 2>&1 && [ -n "$(git -C "$dir" status --porcelain)" ]; then
+ tmux send-keys -t "$session_name" "git status --porcelain" C-m
+ fi
+ fi
+ fi
+done
+
+if [ "$#" -gt 0 ]; then
+ first_session=$(get_session_name "$1")
+ if [ -n "$TMUX" ]; then
+ tmux switch-client -t "$first_session"
+ else
+ tmux attach-session -t "$first_session"
+ fi
+fi
diff --git a/ar/.local/bin/opentasktui b/ar/.local/bin/opentasktui
new file mode 100755
index 0000000..fa9ec5c
--- /dev/null
+++ b/ar/.local/bin/opentasktui
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# Capture the current session name
+current_session=$(tmux display-message -p '#S')
+
+# Parse the task description from the argument
+TASK_DESCRIPTION="$1"
+
+# Create a new tmux session named 'new-task-session' and run 'tui -r current'
+tmux new-session -d -s new-task-session "taskwarrior-tui -r current"
+
+# Sleep for a bit to allow tui to load
+sleep 0.1
+
+# Send the keystrokes needed to filter tasks by description to the target pane
+tmux send-keys -t new-task-session:1.1 "/description:$TASK_DESCRIPTION" C-m
+
+# Attach to the new session
+tmux switch-client -t new-task-session
+
+# Wait for the session to be closed, either by the user or some other way
+while tmux has-session -t new-task-session 2>/dev/null; do
+ sleep 0.1
+done
+
+# Switch back to the original session
+tmux switch-client -t "$current_session"
diff --git a/ar/.local/bin/opout b/ar/.local/bin/opout
new file mode 100755
index 0000000..70bc2cb
--- /dev/null
+++ b/ar/.local/bin/opout
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+# opout: "open output": A general handler for opening a file's intended output,
+# usually the pdf of a compiled document. I find this useful especially
+# running from vim.
+
+basename="${1%.*}"
+
+case "${*}" in
+*.tex | *.sil | *.m[dse] | *.[rR]md | *.mom | *.[0-9])
+ target="$(getcomproot "$1" || echo "$1")"
+ setsid -f xdg-open "${target%.*}".pdf >/dev/null 2>&1
+ ;;
+*.html) setsid -f "$BROWSER" "$basename".html >/dev/null 2>&1 ;;
+*.sent) setsid -f sent "$1" >/dev/null 2>&1 ;;
+esac
diff --git a/ar/.local/bin/otp b/ar/.local/bin/otp
new file mode 100755
index 0000000..be80265
--- /dev/null
+++ b/ar/.local/bin/otp
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+# Get a one-time password, or add a OTP secret to your pass-otp store.
+
+# The assumption of this script is that all otp passwords are stored with the
+# suffix `-otp`. This script automatically appends newly added otps as such.
+
+# For OTP passwords to be generated properly, it is important for the local
+# computer to have its time properly synced. This can be done with the command
+# below which requires the package `ntp`.
+
+ifinstalled pass pass-otp || exit 1
+
+dir="${PASSWORD_STORE_DIR}"
+
+choice="$({
+ echo "🆕add"
+ echo "🕙sync-time"
+ ls "$dir"/*-otp.gpg
+} | sed "s/.*\///;s/-otp.gpg//" | dmenu -p "Pick a 2FA:")"
+
+case $choice in
+🆕add)
+ ifinstalled maim zbar || exit 1
+
+ temp=$(mktemp -p "$XDG_RUNTIME_DIR" --suffix=.png)
+ otp="otp-test-script"
+ trap 'rm -f $temp; pass rm -f $otp' HUP INT QUIT TERM PWR EXIT
+
+ notify-send "Scan the image." "Scan the OTP QR code."
+
+ maim -s "$temp" || exit 1
+ info="$(zbarimg -q "$temp")"
+ info="${info#QR-Code:}"
+
+ if echo "$info" | pass otp insert "$otp"; then
+ while true; do
+ export name="$(echo | dmenu -p "Give this One Time Password a one-word name:")"
+ echo "$name" | grep -q -- "^[A-z0-9-]\+$" && break
+ done
+ pass mv "$otp" "$name-otp"
+ notify-send "Successfully added." "$name-otp has been created."
+ else
+ notify-send "No OTP data found." "Try to scan the image again more precisely."
+ fi
+ ;;
+🕙sync-time)
+ ifinstalled ntp || exit 1
+ notify-send -u low "🕙 Synchronizing Time..." "Synching time with remote NTP servers..."
+ updatedata="$(sudo ntpdate pool.ntp.org)" &&
+ notify-send -u low "🕙 Synchronizing Time..." "Done. Time changed by ${updatedata#*offset }"
+ ;;
+*) pass otp -c ${choice}-otp ;;
+esac
diff --git a/ar/.local/bin/ovpn b/ar/.local/bin/ovpn
new file mode 100755
index 0000000..a89c357
--- /dev/null
+++ b/ar/.local/bin/ovpn
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+turnonoffvpn() {
+ case "$1" in
+ *on*)
+ nmcli connection up "$THESIAH_VPN" 2>/dev/null && notify-send "🛰️ THESIAH_VPN: ON" || notify-send "🛰️ Error to connect"
+ ;;
+ *off*)
+ nmcli connection down "$THESIAH_VPN" 2>/dev/null && notify-send "✂️ THESIAH_VPN: OFF" || notify-send "✂️ Error to disconnect"
+
+ ;;
+ esac
+}
+
+[ -n "$(cat /sys/class/net/tun*/operstate 2>/dev/null)" ] && {
+ turnonoffvpn off
+} || {
+ nmcli connection show | grep "$THESIAH_VPN" >/dev/null 2>&1 && turnonoffvpn on || {
+ nmcli connection import type openvpn file ~/.config/openvpn/thesiah.ovpn >/dev/null 2>&1
+ turnonoffvpn on
+ }
+}
diff --git a/ar/.local/bin/pacerror b/ar/.local/bin/pacerror
new file mode 100755
index 0000000..ec45002
--- /dev/null
+++ b/ar/.local/bin/pacerror
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# Remove existing keyring and package database to start fresh
+sudo rm -rf /etc/pacman.d/gnupg /var/lib/pacman/sync
+
+# Reinitialize the pacman keyring
+sudo pacman-key --init
+
+# Install updated keyrings for Arch and Artix
+sudo pacman -Sy archlinux-keyring artix-keyring
+
+# Refresh all keys to ensure they're up-to-date
+sudo pacman-key --refresh-keys
+
+# Populate the keyring with default keys
+sudo pacman-key --populate archlinux artix
+
+# Clear the package cache
+sudo pacman -Scc --noconfirm
+
+# Forcefully refresh the package databases
+sudo pacman -Syy
+
+# Update the system packages
+sudo pacman -Syu
+
+remaps
diff --git a/ar/.local/bin/partlabel b/ar/.local/bin/partlabel
new file mode 100755
index 0000000..fb62fcd
--- /dev/null
+++ b/ar/.local/bin/partlabel
@@ -0,0 +1,85 @@
+#!/bin/sh
+
+command_exists() { command -v "$1" >/dev/null 2>&1; }
+get_fs_type() { sudo blkid -o value -s TYPE "$1" 2>/dev/null; }
+label_ext() { sudo e2label "$1" "$2"; }
+label_fat() { sudo fatlabel "$1" "$2"; }
+label_ntfs() { sudo ntfslabel --force --quiet "$1" "$2"; }
+label_btrfs() { sudo btrfs filesystem label "$1" "$2"; }
+label_luks() { sudo cryptsetup config "$1" --label "$2"; }
+label_parted() { sudo parted "$1" name "$2" "$3"; }
+print_label() { sudo lsblk -o NAME,LABEL; }
+
+echo "Partition Labeling Script"
+for cmd in blkid e2label fatlabel ntfslabel btrfs parted lsblk cryptsetup; do
+ if ! command_exists "$cmd"; then
+ echo "Error: $cmd is not installed or not in PATH."
+ echo "Installing necessary package for $cmd..."
+ case $cmd in
+ blkid | lsblk)
+ sudo pacman -S --noconfirm util-linux
+ ;;
+ e2label)
+ sudo pacman -S --noconfirm e2fsprogs
+ ;;
+ fatlabel)
+ sudo pacman -S --noconfirm dosfstools
+ ;;
+ ntfslabel)
+ sudo pacman -S --noconfirm ntfs-3g
+ ;;
+ btrfs)
+ sudo pacman -S --noconfirm btrfs-progs
+ ;;
+ parted)
+ sudo pacman -S --noconfirm parted
+ ;;
+ cryptsetup)
+ sudo pacman -S --noconfirm cryptsetup
+ ;;
+ *)
+ echo "Unknown command: $cmd"
+ exit 1
+ ;;
+ esac
+ fi
+done
+
+print_label
+echo -n "\nEnter the partition (e.g., /dev/sda1): "
+read partition
+
+fs_type=$(get_fs_type "$partition")
+
+if [ -z "$fs_type" ]; then
+ echo "Unable to determine file system type for $partition. Please ensure the partition exists and has a valid file system."
+ echo "Debugging information:"
+ echo "blkid output for $partition:"
+ sudo blkid "$partition"
+ echo "Raw blkid output:"
+ sudo blkid
+ exit 1
+fi
+
+echo "Detected file system type: $fs_type"
+echo -n "Enter the label: "
+read label
+
+case "$fs_type" in
+ext2 | ext3 | ext4) label_ext "$partition" "$label" ;;
+vfat) label_fat "$partition" "$label" ;;
+ntfs) label_ntfs "$partition" "$label" ;;
+btrfs) label_btrfs "$partition" "$label" ;;
+crypto_LUKS)
+ echo "Labeling LUKS partition using cryptsetup."
+ label_luks "$partition" "$label"
+ ;;
+*)
+ echo "File system type $fs_type is not directly supported by this script. Attempting to label with parted."
+ label_parted "$partition" "$label"
+ ;;
+esac
+
+echo "Partition labeled successfully. Verifying..."
+print_label
+echo "Done."
diff --git a/ar/.local/bin/passmenu2 b/ar/.local/bin/passmenu2
new file mode 100755
index 0000000..00a6b35
--- /dev/null
+++ b/ar/.local/bin/passmenu2
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+shopt -s nullglob globstar
+
+typeit=0
+if [[ $1 == "--type" ]]; then
+ typeit=1
+ shift
+fi
+
+if [[ -n $WAYLAND_DISPLAY ]]; then
+ dmenu_cmd="dmenu-wl -l 20"
+ xdotool="ydotool type --file -"
+elif [[ -n $DISPLAY ]]; then
+ dmenu_cmd="dmenu -l 20"
+ xdotool="xdotool type --clearmodifiers --file -"
+else
+ echo "Error: No Wayland or X11 display detected" >&2
+ exit 1
+fi
+
+prefix=${PASSWORD_STORE_DIR-~/.password-store}
+password_files=("$prefix"/**/*.gpg)
+password_files=("${password_files[@]#"$prefix"/}")
+password_files=("${password_files[@]%.gpg}")
+
+password=$(printf '%s\n' "${password_files[@]}" | $dmenu_cmd "$@")
+
+[[ -n $password ]] || exit
+
+if [[ $typeit -eq 0 ]]; then
+ pass show -c "$password" 2>/dev/null
+else
+ pass show "$password" | {
+ IFS= read -r pass
+ printf %s "$pass"
+ } | $xdotool
+fi
diff --git a/ar/.local/bin/pauseallmpv b/ar/.local/bin/pauseallmpv
new file mode 100755
index 0000000..9b14148
--- /dev/null
+++ b/ar/.local/bin/pauseallmpv
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+# You might notice all mpv commands are aliased to have this input-ipc-server
+# thing. That's just for this particular command, which allows us to pause
+# every single one of them with one command! This is bound to super + shift + p
+# (with other things) by default and is used in some other places.
+
+for i in $(ls /tmp/mpvSockets/*); do
+ echo '{ "command": ["set_property", "pause", true] }' | socat - "$i"
+done
diff --git a/ar/.local/bin/peertubetorrent b/ar/.local/bin/peertubetorrent
new file mode 100755
index 0000000..4d8f630
--- /dev/null
+++ b/ar/.local/bin/peertubetorrent
@@ -0,0 +1,9 @@
+#!/bin/sh
+# torrent peertube videos, requires the transadd script
+# first argument is the video link, second is the quality (360, 480 or 1080)
+# 13/07/20 - Arthur Bais
+
+instance=$(echo "$1" | sed "s|/w.\+||")
+vidid=$(echo "$1" | sed "s|.\+/||")
+link=$(curl -s "$instance/api/v1/videos/$vidid" | grep -o "$instance/download/torrents/.\{37\}$2.torrent")
+transadd "$link"
diff --git a/ar/.local/bin/podentr b/ar/.local/bin/podentr
new file mode 100755
index 0000000..ae72d41
--- /dev/null
+++ b/ar/.local/bin/podentr
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# entr command to run `queueandnotify` when newsboat queue is changed
+
+[ "$(pgrep -x "$(basename "$0")" | wc -l)" -gt 2 ] && exit
+
+echo "${XDG_DATA_HOME:-${HOME}/.local/share}"/newsboat/queue | entr -p queueandnotify 2>/dev/null
diff --git a/ar/.local/bin/qndl b/ar/.local/bin/qndl
new file mode 100755
index 0000000..c9c1be2
--- /dev/null
+++ b/ar/.local/bin/qndl
@@ -0,0 +1,108 @@
+#!/bin/sh
+
+YTDL_CMD_BASE="yt-dlp --continue --embed-metadata --ignore-errors --no-force-overwrites --no-playlist --verbose"
+
+# Process command-line options for download type
+while getopts "mvr" opt; do
+ case $opt in
+ m)
+ DOWNLOAD_TYPE="music"
+ OUTPUT_DIR="${XDG_MUSIC_DIR:-${HOME}/Music}"
+ ARCHIVE_FILE="${XDG_DOTFILES_DIR:-${HOME}/.dotfiles}/default/Music/.music.txt"
+ YTDL_OUTPUT_FORMAT="${OUTPUT_DIR}/%(artist|)s%(artist& - |)s%(title)s.%(ext)s"
+ YTDL_CMD_BASE="$YTDL_CMD_BASE --audio-format best --audio-quality 0 --download-archive \"$ARCHIVE_FILE\" --extract-audio --recode-video mp3"
+ ;;
+ v)
+ DOWNLOAD_TYPE="video"
+ OUTPUT_DIR="${XDG_VIDEOS_DIR:-${HOME}/Videos}"
+ YTDL_OUTPUT_FORMAT="${OUTPUT_DIR}/%(title)s [%(id)s].%(ext)s"
+ VIDEO_EXT=$(printf "best\n60fps\n30fps\nmp4\nmkv" | dmenu -i -p "Choose an encoding (default: 1080p)") || exit
+ case $VIDEO_EXT in
+ best)
+ VIDEO_FORMATS="--format bestvideo+bestaudio/best"
+ VIDEO_OPTIONS=""
+ ;;
+ 60fps)
+ VIDEO_FORMATS='--format "((bv*[fps=60]/bv*)[height<=1080]/(wv*[fps=60]/wv*)) + ba / (b[fps=60]/b)[height<=1080]/(w[fps=60]/w)"'
+ VIDEO_OPTIONS=""
+ ;;
+ 30fps)
+ VIDEO_FORMATS='--format "((bv*[fps=30]/bv*)[height<=1080]/(wv*[fps=30]/wv*)) + ba / (b[fps=30]/b)[height<=1080]/(w[fps=30]/w)"'
+ VIDEO_OPTIONS=""
+ ;;
+ *)
+ VIDEO_FORMATS="--format bestvideo+bestaudio/best"
+ VIDEO_OPTIONS="--recode-video $VIDEO_EXT"
+ ;;
+ esac
+ YTDL_CMD_BASE="$YTDL_CMD_BASE --buffer-size 1M --embed-thumbnail $VIDEO_FORMATS --no-sponsorblock $VIDEO_OPTIONS"
+ ;;
+ r)
+ OUTPUT_DIR="${XDG_MUSIC_DIR:-${HOME}/Music}"
+ ARCHIVE_FILE="${XDG_DOTFILES_DIR:-${HOME}/.dotfiles}/default/Music/.music.txt"
+ YTDL_OUTPUT_FORMAT="${OUTPUT_DIR}/%(artist|)s%(artist& - |)s%(title)s.%(ext)s"
+ YTDL_CMD_BASE="$YTDL_CMD_BASE --audio-format best --audio-quality 0 --extract-audio --recode-video mp3"
+ YTDL_CMD="$YTDL_CMD_BASE --output \"$YTDL_OUTPUT_FORMAT\""
+ [ ! -f "$ARCHIVE_FILE" ] && exit 1
+ while read -r line; do
+ video_id=$(echo "$line" | awk '{print $2}')
+ YTDL_CMD="$YTDL_CMD_BASE --output \"$YTDL_OUTPUT_FORMAT\" \"https://www.youtube.com/watch?v=$video_id\""
+ idnum=$(tsp bash -c "$YTDL_CMD")
+ pkill -RTMIN+21 "${STATUSBAR:-dwmblocks}"
+ done <"$ARCHIVE_FILE"
+ exit 0
+ ;;
+ *)
+ notify-send "⛔ Invalid option: -$OPTARG"
+ exit 1
+ ;;
+ esac
+done
+
+shift $((OPTIND - 1))
+
+# Use the first non-option argument as the URL if provided, else from clipboard
+URL="${1:-$(xclip -selection clipboard -o)}"
+[ -z "$URL" ] && notify-send "⛔ No URL provided and clipboard is empty or does not contain a valid URL." && exit 1
+
+# Validate the URL format
+! echo "$URL" | grep -qE '^https?://[a-zA-Z0-9.-]+(/[a-zA-Z0-9./?&%=_-]*)?$' && notify-send "⛔ Invalid URL format: $URL" && exit 1
+
+# Validate URL accessibility
+! curl --head --silent --fail "$URL" >/dev/null && notify-send "⛔ URL is not accessible: $URL" && exit 1
+
+case $URL in
+*playlist* | *list=*)
+ PL_DOWNLOAD_CHOICE=$(printf "playlist\na content" | dmenu -i -p "Download entire playlist or just this content?")
+ [ "$PL_DOWNLOAD_CHOICE" = "playlist" ] &&
+ YTDL_CMD_BASE=$(echo "$YTDL_CMD_BASE" | sed 's/ --no-playlist//') &&
+ YTDL_CMD_BASE="$YTDL_CMD_BASE --yes-playlist" &&
+ echo 🪏 >/tmp/qplaylist
+ [ "$DOWNLOAD_TYPE" = "video" ] &&
+ SUBDIR=$(yt-dlp --dump-single-json "$URL" --no-playlist | jq -r '.channel' | sed 's/[\/:*?"<>|]/_/g;s/[[:space:]]/-/g') &&
+ mkdir -p "${OUTPUT_DIR}/${SUBDIR}" &&
+ YTDL_OUTPUT_FORMAT="${OUTPUT_DIR}/${SUBDIR}/%(playlist_index)02d_%(title)s [%(id)s].%(ext)s"
+ ;;
+esac
+
+SIMULATION_CMD="yt-dlp --simulate --print filename $URL"
+YTDL_CMD="$YTDL_CMD_BASE --output \"$YTDL_OUTPUT_FORMAT\" \"$URL\""
+
+# Notify and perform simulation to get filename (feedback to user)
+echo "$SIMULATION_CMD" | while IFS= read -r line; do
+ filename=$(basename "$line")
+ notify-send "📥 Queuing $DOWNLOAD_TYPE to download:" "$filename"
+done
+
+# Enqueue the download task with tsp
+FILENAME=$($SIMULATION_CMD 2>/dev/null)
+rm -rf /tmp/qplaylist
+notify-send "⏳ Downloading $DOWNLOAD_TYPE:" "$FILENAME"
+idnum=$(tsp bash -c "$YTDL_CMD")
+pkill -RTMIN+21 "${STATUSBAR:-dwmblocks}"
+
+# Notify upon completion
+tsp -D "$idnum" notify-send "✅ $DOWNLOAD_TYPE download complete:" "$URL"
+
+# Conditionally update the music database if the download type is music
+[ "$DOWNLOAD_TYPE" = "music" ] && tsp -D "$idnum" bash -c "mpc update"
diff --git a/ar/.local/bin/queueandnotify b/ar/.local/bin/queueandnotify
new file mode 100755
index 0000000..0059e93
--- /dev/null
+++ b/ar/.local/bin/queueandnotify
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# Podboat sucks. This script replaces it.
+# It reads the newsboat queue, queuing downloads with taskspooler.
+# It also removes the junk from extensions.
+queuefile="${XDG_DATA_HOME:-${HOME}/.local/share}/newsboat/queue"
+
+while read -r line; do
+ [ -z "$line" ] && continue
+ url="${line%%[ ]*}"
+ qndl "$url" "curl -LO"
+done <"$queuefile"
+
+echo >"$queuefile"
diff --git a/ar/.local/bin/rbackup b/ar/.local/bin/rbackup
new file mode 100755
index 0000000..c78c029
--- /dev/null
+++ b/ar/.local/bin/rbackup
@@ -0,0 +1,185 @@
+#!/bin/sh
+
+# local backup
+backup_path="/mnt/second/backup"
+dot_path="${XDG_DOTFILES_DIR:-${HOME}/.dotfiles}"
+git_path="$HOME/Private/repos"
+pass_path="${PASSWORD_STORE_DIR:-${XDG_DATA_HOME:-${HOME}/.local/share}/.password-store}/exported_keys"
+suck_path="${XDG_SOURCES_HOME:-${HOME}/.local/src}/suckless"
+USER_HOME=$(eval echo ~"$USER")
+
+# targets
+bash_path="${XDG_DOTFILES_DIR:-${HOME}/.dotfiles}/ar/.config/bash"
+shell_path="${XDG_DOTFILES_DIR:-${HOME}/.dotfiles}/ar/.config/shell"
+vim_path="${XDG_DOTFILES_DIR:-${HOME}/.dotfiles}/ar/.config/vim"
+thesiah_path="${THESIAH_WWW:-${HOME}/Private/repos/THESIAH}/public"
+lf_path="${XDG_DOTFILES_DIR:-${HOME}/.dotfiles}/ar/.config/lf"
+
+usage() {
+ echo "Synchronize files and save them in backup path."
+ echo ""
+ echo "Usage: ${0##*/} [OPTIONS]"
+ echo ""
+ echo "Options:"
+ echo " -h, --help Show this help message"
+ echo " -r, --root Sync root files only"
+ echo ""
+ echo "Example:"
+ echo " ${0##*/} # Sync all files to backup path"
+ echo " ${0##*/} --root # Sync root files only"
+ exit 0
+}
+
+error() {
+ printf "%s\n" "$1" >&2
+ exit 1
+}
+
+mount_luks() {
+ if ! mount | grep -q " /mnt/second "; then
+ SIZE_NVME0=$(sudo blockdev --getsize64 /dev/nvme0n1p1)
+ SIZE_NVME1=$(sudo blockdev --getsize64 /dev/nvme1n1p1)
+ [ "$SIZE_NVME1" -lt "$SIZE_NVME0" ] && TARGET_DEVICE="/dev/nvme0n1p1" || TARGET_DEVICE="/dev/nvme1n1p1"
+ ${TERMINAL:-st} -n floatterm -g 60x1 -e sudo cryptsetup open "$TARGET_DEVICE" "second"
+ sudo -A mount "/dev/mapper/second" "/mnt/second" -o uid="$(id -u)",gid="$(id -g)" 2>/dev/null || sudo -A mount "/dev/mapper/second" "/mnt/second"
+ fi
+}
+
+# Using a loop over space-separated strings instead of an array
+sync_files() {
+ for source in "$dot_path" "$git_path" "$pass_path" "$suck_path"; do
+ rsync -vrazPlu --exclude=".music.txt" --delete "$source" "$backup_path/" >/dev/null 2>&1 || {
+ echo "Failed to sync $(basename "$source")"
+ }
+ done
+}
+
+sync_root() {
+ # clean targets
+ sudo rm -rf /root/.config /root/.bash_history /root/.local/share/history
+ sudo mkdir -p /root/.config/bash /root/.config/lf /root/.config/shell /root/.config/vim /root/.local/bin /root/.local/share/history/vim_history /root/.local/state
+
+ # Root configuration synchronization on local system
+ sudo rsync -vrazPlu --delete "$vim_path/vimrc" "/root/.config/vim/" >/dev/null 2>&1
+ sudo rsync -vrazPlu --delete "$lf_path" "/root/.config/" >/dev/null 2>&1
+ sudo mv -f "/root/.config/lf/rooticons" "/root/.config/lf/icons" >/dev/null 2>&1
+ sudo rsync -vrazPlu --delete "$bash_path" "/root/.config/" >/dev/null 2>&1
+ sudo rsync -vrazPlu --delete "$shell_path/inputrc" "/root/.config/shell/" >/dev/null 2>&1
+
+ # load shortcuts
+ shortcuts >/dev/null 2>&1
+
+ # Modify root's Bash and LF configuration to include user-specific settings
+ echo "[ -f \"$USER_HOME/.config/shell/shortcutrc\" ] && source \"$USER_HOME/.config/shell/shortcutrc\"" | sudo tee -a /root/.config/bash/bashrc >/dev/null 2>&1
+ echo "[ -f \"$USER_HOME/.config/shell/zshnameddirrc\" ] && source \"$USER_HOME/.config/shell/zshnameddirrc\"" | sudo tee -a /root/.config/bash/bashrc >/dev/null 2>&1
+ sudo sed -i "s|source[[:space:]]*\"\?~/.config/lf/shortcutrc\"\?|source \"$USER_HOME/.config/lf/shortcutrc\"|" /root/.config/lf/lfrc >/dev/null 2>&1
+ sudo grep -q "source \"\?/root/.config/lf/rootshortcutrc\"\?" /root/.config/lf/lfrc ||
+ sudo sed -i "\|source \"\?$USER_HOME/.config/lf/shortcutrc\"\?|a source \"/root/.config/lf/rootshortcutrc\"" /root/.config/lf/lfrc
+
+ # Final ownership and link adjustments
+ sudo chown -R root:root /root/.config/ >/dev/null 2>&1
+ sudo ln -sf /root/.config/bash/bashrc /root/.bashrc >/dev/null 2>&1
+ sudo ln -sf /root/.config/bash/bash_profile /root/.bash_profile >/dev/null 2>&1
+ sudo ln -sf /root/.config/shell/inputrc /root/.inputrc >/dev/null 2>&1
+ sudo ln -sf /root/.config/vim/vimrc /root/.vimrc >/dev/null 2>&1
+}
+
+sync_server() {
+ # clean targets
+ ssh "$THESIAH_SERVER" "rm -rf /root/.config /var/www/thesiah"
+ ssh "$THESIAH_SERVER" "mkdir -p /root/.config/bash /root/.config/shell /root/.config/vim /root/.local/bin /root/.local/share /root/.local/state /var/www/thesiah"
+
+ # Sync operations with explicit error checking
+ rsync -vrazPlu --delete "$thesiah_path/" "$THESIAH_SERVER:/var/www/thesiah/" >/dev/null 2>&1
+ rsync -vrazPlu --delete "$vim_path/vimrc" "$THESIAH_SERVER:/root/.config/vim/" >/dev/null 2>&1
+ rsync -vrazPlu --delete "$shell_path/inputrc" "$THESIAH_SERVER:/root/.config/shell/" >/dev/null 2>&1
+ sudo cp /root/.config/shell/rootshortcutrc ~/.cache/
+ sudo chown -R "$USER":wheel ~/.cache/rootshortcutrc
+ rsync -vrazPlu --remove-source-files "$HOME/.cache/rootshortcutrc" "$THESIAH_SERVER:/root/.config/shell/" >/dev/null 2>&1
+
+ # Adding custom shortcuts to root's shell configuration on the remote system
+ ssh "$THESIAH_SERVER" "echo 'web=\"cd /var/www && ls -A\" \\' >> /root/.config/shell/rootshortcutrc"
+ ssh "$THESIAH_SERVER" "echo 'wen=\"cd /var/www/nextcloud && ls -A\" \\' >> /root/.config/shell/rootshortcutrc"
+ ssh "$THESIAH_SERVER" "echo 'wep=\"cd /var/www/prosody && ls -A\" \\' >> /root/.config/shell/rootshortcutrc"
+ ssh "$THESIAH_SERVER" "echo 'wet=\"cd /var/www/thesiah && ls -A\" \\' >> /root/.config/shell/rootshortcutrc"
+ ssh "$THESIAH_SERVER" "echo 'gng=\"cd /etc/nginx/sites-available && ls -A\" \\' >> /root/.config/shell/rootshortcutrc"
+
+ # Sync Bash configuration
+ rsync -vrazPlu --delete "$bash_path" "$THESIAH_SERVER:/root/.config/" >/dev/null 2>&1
+ ssh "$THESIAH_SERVER" "chown -R root:root /var/www/thesiah"
+ ssh "$THESIAH_SERVER" "chown -R root:root /root/.config/"
+ ssh "$THESIAH_SERVER" "ln -sf /root/.config/bash/bash_profile /root/.profile"
+ ssh "$THESIAH_SERVER" "source /root/.profile"
+
+ # Sync for Git
+ ssh "$THESIAH_SERVER" "cp -r /root/.config /var/www/git/"
+ ssh "$THESIAH_SERVER" "chown -R git:git /var/www/git/.config/"
+ ssh "$THESIAH_GIT" "ln -sf /var/www/git/.config/bash/bash_profile /var/www/git/.profile"
+ ssh "$THESIAH_GIT" "source /var/www/git/.profile"
+}
+
+sync_nextcloud() {
+ base="$(basename $backup_path)"
+ parent="$(dirname $backup_path)"
+ tmpdir="$(mktemp -d)"
+ cd "$tmpdir" || exit
+ tar -C "$parent" -zcf "$base".tar.gz "$base" >/dev/null 2>&1
+ rsync -vrazPlu --delete "$tmpdir/$base".tar.gz "$THESIAH_SERVER:/var/www/nextcloud/data/si@thesiah.xyz/files/backup/" >/dev/null 2>&1
+ ssh "$THESIAH_SERVER" "chown -R www-data:www-data /var/www/nextcloud/data/si@thesiah.xyz/files/backup" >/dev/null 2>&1
+ ssh "$THESIAH_SERVER" "cd /var/www/nextcloud && sudo -u www-data ./occ files:scan --path="/si@thesiah.xyz/files"" >/dev/null 2>&1
+ rm -r "$tmpdir"
+}
+
+handle_long_option() {
+ case $1 in
+ help)
+ usage
+ ;;
+ root)
+ echo "Sync root files..."
+ sync_root && echo "Success to sync root!" && echo "Done!" || error "Failed to back up root"
+ exit 0
+ ;;
+ *)
+ error "Unknown option: --$1"
+ ;;
+ esac
+}
+
+process_options() {
+ while [ $# -gt 0 ]; do
+ case $1 in
+ -h | --help)
+ usage
+ ;;
+ -r | --root)
+ echo "Sync root files..."
+ sync_root && echo "Success to sync root!" && echo "Done!" || error "Failed to back up root"
+ exit 0
+ ;;
+ --*)
+ handle_long_option "${1#--}"
+ ;;
+ -*)
+ error "Unknown option: $1"
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+ done
+}
+
+# Start script
+echo "Backup starts to $backup_path..."
+process_options "$@"
+
+# Main script logic
+mount_luks && echo "Mount backup drive... " && echo "Success to mount luks drive!" || error "Failed to mount $backup_path"
+[ -d "$backup_path" ] || sudo mkdir -p "$backup_path"
+echo "Sync home files..." && sync_files && echo "Success to sync files!" || error "Failed back up files"
+echo "Sync root files..." && sync_root && echo "Success to sync root!" || error "Failed back up root"
+echo "Sync server files..." && sync_server && echo "Success to sync server!" || error "Failed back up server"
+echo "Sync files to nextcloud..." && sync_nextcloud && echo "Success to sync nextcloud!" || error "Failed back up nextcloud"
+echo "Done!"
diff --git a/ar/.local/bin/remapd b/ar/.local/bin/remapd
new file mode 100755
index 0000000..f669b0c
--- /dev/null
+++ b/ar/.local/bin/remapd
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+# Rerun the remaps script whenever a new input device is added.
+
+while :; do
+ remaps
+ grep -qP -m1 '[^un]bind.+\/[^:]+\(usb\)' <(udevadm monitor -u -t seat -s input -s usb)
+done
diff --git a/ar/.local/bin/remaps b/ar/.local/bin/remaps
new file mode 100755
index 0000000..153ce29
--- /dev/null
+++ b/ar/.local/bin/remaps
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+# This script is called on startup to remap keys.
+# Decrease key repeat delay to 300ms and increase key repeat rate to 50 per second.
+xset r rate 200 50
+# Map the caps lock key to control, and map the menu key to right super.
+xinput list | grep 'id=' | while read -r line; do
+ id=$(echo "$line" | grep -i 'keyboard.*id.*keyboard' | sed 's/.*id=\([0-9]\+\).*/\1/')
+ [ -z "$id" ] || {
+ case "$(echo "$line" | grep -oE '.*id=' | sed 's/ id=.*//')" in
+ *"Lite-On Tech Lenovo USB Travel Keyboard with Ultra Nav"*)
+ setxkbmap -device "$id" -option
+ setxkbmap -device "$id" -option caps:ctrl_modifier,ctrl:swap_lwin_lctl
+ ;;
+ *"Lite-On Technology Corp. ThinkPad USB Keyboard with TrackPoint"*)
+ setxkbmap -device "$id" -option
+ setxkbmap -device "$id" -option caps:ctrl_modifier,altwin:menu_win,altwin:swap_lalt_lwin
+ ;;
+ *"AT Translated Set 2 keyboard"*)
+ setxkbmap -device "$id" -option
+ setxkbmap -device "$id" -option caps:ctrl_modifier,altwin:menu_win,altwin:swap_lalt_lwin
+ ;;
+ *"Soomin Im’s Keyboard"*)
+ setxkbmap -device "$id" -option
+ setxkbmap -device "$id" -option caps:ctrl_modifier
+ ;;
+ *"HHKB"*)
+ setxkbmap -device "$id" -option
+ setxkbmap -device "$id" -option altwin:menu_win
+ ;;
+ *)
+ setxkbmap -device "$id" -option
+ setxkbmap -device "$id" -option caps:ctrl_modifier,altwin:menu_win
+ ;;
+ esac
+ }
+ id=$(echo "$line" | grep -i '.*id.*pointer' | sed 's/.*id=\([0-9]\+\).*/\1/')
+ [ -z "$id" ] || {
+ case "$(echo "$line" | grep -oE '.*id=' | sed 's/ id=.*//')" in
+ *"Apple Inc. Magic Trackpad"*)
+ xinput set-prop "$id" "libinput Tapping Enabled" 0
+ ;;
+ *"SynPS/2 Synaptics TouchPad"*)
+ xinput set-prop "$id" "libinput Tapping Enabled" 0
+ ;;
+ *"Lite-On Tech Lenovo USB Travel Keyboard with Ultra Nav Mouse"*)
+ [ -z "$1" ] && xinput set-prop "$id" "Coordinate Transformation Matrix" 5, 0, 0, 0, 5, 0, 0, 0, 1 || xinput set-prop "$id" "Coordinate Transformation Matrix" $1, 0, 0, 0, $1, 0, 0, 0, 1
+ xinput set-prop "$id" "libinput Scroll Method Enabled" 0, 0, 1
+ ;;
+ *"Logitech USB Receiver"*)
+ [ -z "$1" ] && xinput set-prop "$id" "Coordinate Transformation Matrix" 3, 0, 0, 0, 3, 0, 0, 0, 1 || xinput set-prop "$id" "Coordinate Transformation Matrix" $1, 0, 0, 0, $1, 0, 0, 0, 1
+ ;;
+ *"TPPS/2 IBM TrackPoint"*)
+ [ -z "$1" ] && xinput set-prop "$id" "Coordinate Transformation Matrix" 1, 0, 0, 0, 1, 0, 0, 0, 1 || xinput set-prop "$id" "Coordinate Transformation Matrix" $1, 0, 0, 0, $1, 0, 0, 0, 1
+ ;;
+ *"Lite-On Technology Corp. ThinkPad USB Keyboard with TrackPoint"*)
+ [ -z "$1" ] && xinput set-prop "$id" "Coordinate Transformation Matrix" 3, 0, 0, 0, 3, 0, 0, 0, 1 || xinput set-prop "$id" "Coordinate Transformation Matrix" $1, 0, 0, 0, $1, 0, 0, 0, 1
+ ;;
+ esac
+ }
+done
+# When left control, caps lock, or Super_L is pressed only once, treat it as escape.
+killall xcape 2>/dev/null
+xcape -e 'Caps_Lock=Escape' #;Control_L=Escape' #;Super_L=Escape'
+# Turn off caps lock if on since there is no longer a key for it.
+xset -q | grep -q "Caps Lock:\s*on" && xdotool key Caps_Lock
diff --git a/ar/.local/bin/restartnvim b/ar/.local/bin/restartnvim
new file mode 100755
index 0000000..74d57c3
--- /dev/null
+++ b/ar/.local/bin/restartnvim
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+set -e
+
+# Set new line and tab for word splitting
+IFS="
+ "
+
+# Check if the script is running inside a tmux session
+if [ -z "$TMUX" ]; then
+ echo "This script must be run from inside a tmux session."
+ exit 1
+fi
+
+# Get the current tmux pane ID
+TMUX_PANE=$(tmux display-message -p '#D')
+
+# Send Escape, :wq, and Enter to Neovim in the tmux pane
+tmux send-keys -t "$TMUX_PANE" Escape C-m ':wq' C-m
+
+# Wait to ensure Neovim exits
+sleep 0.5
+
+# Detach the script from Neovim and wait a bit to ensure Neovim exits
+(nohup sh -c "sleep 0.5; tmux send-keys -t \"$TMUX_PANE\" 'nvim -c \"execute \\\"edit \\\" . v:oldfiles[0] | normal '\''0\"' C-m" >/dev/null 2>&1 &)
diff --git a/ar/.local/bin/rgafiles b/ar/.local/bin/rgafiles
new file mode 100755
index 0000000..6088933
--- /dev/null
+++ b/ar/.local/bin/rgafiles
@@ -0,0 +1,120 @@
+#!/bin/sh
+
+# Usage function to display script options
+usage() {
+ echo "Find files using ripgrep and open them in Neovim."
+ echo ""
+ echo "Usage: ${0##*/} [-s] [-i] [-l] [-p] [<tag>] <query>"
+ echo ""
+ echo "Options:"
+ echo " -h : Show this message"
+ echo " -i : Perform a case-insensitive search (default)"
+ echo " -l : List files associated with the given tag"
+ echo " -p : Search for files in the specified project directories using the specified tag (default: PROJECT)"
+ echo " -s : Perform a case-sensitive search"
+ echo " [<tag>] <query> : Optional tag for project mode, followed by the search query"
+ echo ""
+ echo "Examples:"
+ echo " ${0##*/} -p TODO 'KEYWORD' # Search for 'KEYWORD' in files tagged with 'TODO' in the project directories"
+ echo " ${0##*/} -l -p 'KEYWORD' # List files associated with the default 'PROJECT' tag and 'KEYWORD'"
+ echo " ${0##*/} 'KEYWORD' # Open files containing 'KEYWORD' in nvim"
+ exit 0
+}
+
+search_term() {
+ ignore_case_flag="$1"
+ shift
+
+ if ! command -v rga >/dev/null 2>&1; then
+ echo "Error: 'rga' is not installed." >&2
+ exit 1
+ fi
+ if ! command -v xclip >/dev/null 2>&1; then
+ echo "Error: 'xclip' is not installed." >&2
+ exit 1
+ fi
+
+ # Construct the preview command
+ preview_cmd=$(printf "rga %s --pretty --context 10 '%s' {}" "$ignore_case_flag" "$*")
+ rga_output=$(rga --follow --no-ignore --hidden --text --max-count=1 ${ignore_case_flag:+$ignore_case_flag} --files-with-matches --no-messages --glob '!**/.git/*' "$*")
+
+ # Use fzf to select files
+ files=$(echo "$rga_output" | fzf-tmux +m --preview="$preview_cmd" --reverse --multi --select-1 --exit-0) || return 1
+
+ # Check if files are selected
+ if [ -z "$files" ]; then
+ echo "No files selected."
+ return 0
+ fi
+
+ # copy target to the clipboard
+ echo "$@" | xclip -selection clipboard 2>/dev/null
+
+ openfiles "$files"
+
+ # print the file names
+ echo "$rga_output"
+}
+
+# Function to list and/or open all files associated with a given project tag
+list_or_open_project_files() {
+ # Use the provided tag or default to "PROJECT"
+ project_tag="${1:-PROJECT}: $2"
+
+ # Define the project paths as a space-separated string
+ project_paths="$HOME/.dotfiles $HOME/.local/src/suckless $HOME/Public/repos"
+
+ # Use rga to find files containing the project tag across all project paths
+ rga_output=""
+ for path in $project_paths; do
+ if [ -d "$path" ]; then
+ rga_result=$(rga --follow --no-ignore --hidden --text --max-count=1 --files-with-matches --no-messages --glob '!**/.git/*' "$project_tag" "$path")
+ rga_output="$rga_output $rga_result"
+ fi
+ done
+
+ # Remove leading/trailing whitespace
+ rga_output=$(echo "$rga_output" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
+
+ # Check if any files were found
+ if [ -z "$rga_output" ]; then
+ echo "No files found for tag $project_tag."
+ return 0
+ fi
+
+ # If the script was called in list mode, simply print the files
+ if [ "$list_mode" -eq 1 ]; then
+ echo "$rga_output"
+ else
+ # Otherwise, open the files with nvim
+ set -- "$(printf "%s\n" "$rga_output")"
+ openfiles "$@"
+ fi
+}
+
+# Main function to handle options
+ignore_case_flag="--ignore-case" # Default to case-insensitive
+list_mode=0
+project_mode=0
+
+# Parse the options
+while getopts "silph" opt; do
+ case $opt in
+ s) ignore_case_flag="" ;; # Case-sensitive
+ i) ignore_case_flag="--ignore-case" ;; # Case-insensitive
+ l) list_mode=1 ;; # List mode
+ p) project_mode=1 ;; # Project mode
+ h) usage ;;
+ *) ;;
+ esac
+done
+
+shift $((OPTIND - 1))
+
+# Handle project mode search
+if [ "$project_mode" -eq 1 ]; then
+ list_or_open_project_files "$1" "$2"
+else
+ # Otherwise, call the common search function
+ search_term "$ignore_case_flag" "$@"
+fi
diff --git a/ar/.local/bin/rotdir b/ar/.local/bin/rotdir
new file mode 100755
index 0000000..d171f29
--- /dev/null
+++ b/ar/.local/bin/rotdir
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# When I open an image from the file manager in nsxiv (the image viewer), I want
+# to be able to press the next/previous keys to key through the rest of the
+# images in the same directory. This script "rotates" the content of a
+# directory based on the first chosen file, so that if I open the 15th image,
+# if I press next, it will go to the 16th etc. Autistic, I know, but this is
+# one of the reasons that nsxiv is great for being able to read standard input.
+
+[ -z "$1" ] && echo "usage: rotdir regex 2>&1" && exit 1
+base="$(basename "$1")"
+ls "$PWD" | awk -v BASE="$base" 'BEGIN { lines = ""; m = 0; } { if ($0 == BASE) { m = 1; } } { if (!m) { if (lines) { lines = lines"\n"; } lines = lines""$0; } else { print $0; } } END { print lines; }'
diff --git a/ar/.local/bin/rssadd b/ar/.local/bin/rssadd
new file mode 100755
index 0000000..9e68650
--- /dev/null
+++ b/ar/.local/bin/rssadd
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+if echo "$1" | grep -q "https*://\S\+\.[A-Za-z]\+\S*"; then
+ url="$1"
+else
+ url="$(grep -Eom1 '<[^>]+(rel="self"|application/[a-z]+\+xml)[^>]+>' "$1" |
+ grep -o "https?://[^\" ]")"
+
+ echo "$url" | grep -q "https*://\S\+\.[A-Za-z]\+\S*" ||
+ notify-send "That doesn't look like a full URL." && exit 1
+fi
+
+RSSFILE="${XDG_CONFIG_HOME:-${HOME}/.config}/newsboat/urls"
+if awk '{print $1}' "$RSSFILE" | grep "^$url$" >/dev/null; then
+ notify-send "You already have this RSS feed."
+else
+ echo "$url" >>"$RSSFILE" && notify-send "RSS feed added."
+fi
diff --git a/ar/.local/bin/schedule b/ar/.local/bin/schedule
new file mode 100755
index 0000000..c339e2b
--- /dev/null
+++ b/ar/.local/bin/schedule
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+if [ -z "$1" ]; then
+ echo "Usage: ${0##*/} <date-string>"
+ echo " Example: ${0##*/} 'tomorrow 06:00'"
+ exit 2
+fi
+
+set -e
+
+# Calculate the seconds until the given date
+secsUntil=$(expr "$(date +%s -d "$*")" - "$(date +%s)")
+
+# Calculate minutes and hours
+minutesUntil=$(echo "scale=1; $secsUntil/60" | bc)
+hoursUntil=$(echo "scale=2; $secsUntil/3600" | bc)
+
+# Get the formatted date
+date=$(date -d "$*")
+
+# Display the result
+echo "$hoursUntil hours (or $minutesUntil mins) until $date"
diff --git a/ar/.local/bin/screenshotactivewindow b/ar/.local/bin/screenshotactivewindow
new file mode 100755
index 0000000..79365e0
--- /dev/null
+++ b/ar/.local/bin/screenshotactivewindow
@@ -0,0 +1,37 @@
+#!/bin/bash
+set -eo pipefail
+
+pidfile=/tmp/screencast.lock
+
+if [ -e $pidfile ]; then
+ pid=$(cat $pidfile)
+ echo "Already running as ID $pid, now quitting"
+ rm $pidfile
+ kill -15 $pid
+ exit 0
+fi
+
+if [[ "$1" == "" ]]; then
+ echo "Usage: ${0##*/} <window-id>"
+ exit 2
+fi
+
+windowId=$1
+tmpFile=/tmp/screencast-$(date +%s).mkv
+paletteFile=/tmp/palette-$(date +%s).png
+gifFile=/tmp/screencast-$(date +%s).gif
+
+size=$(xdotool getwindowgeometry $windowId | grep Geometry | awk '{print $2}')
+posX=$(xwininfo -id $windowId | grep 'Absolute upper-left X' | awk '{print $4}')
+posY=$(xwininfo -id $windowId | grep 'Absolute upper-left Y' | awk '{print $4}')
+pos="$posX,$posY"
+
+ffmpeg -hide_banner -loglevel info -f x11grab -show_region 1 -video_size $size -i :0+$pos $tmpFile &
+ffPID=$!
+echo $ffPID >$pidfile
+wait $ffPID && echo
+
+ffmpeg -y -i $tmpFile -vf fps=10,palettegen $paletteFile
+ffmpeg -i $tmpFile -i $paletteFile -filter_complex "paletteuse" $gifFile
+
+rm $paletteFile $tmpFile
diff --git a/ar/.local/bin/sd b/ar/.local/bin/sd
new file mode 100755
index 0000000..67b0d5a
--- /dev/null
+++ b/ar/.local/bin/sd
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# Open a terminal window in the same directory as the currently active window.
+
+windowPID=$(xprop -id "$(xprop -root | sed -n "/_NET_ACTIVE_WINDOW/ s/^.*# // p")" | sed -n "/PID/ s/^.*= // p")
+PIDlist=$(pstree -lpATna "$windowPID" | sed -En 's/.*,([0-9]+).*/\1/p' | tac)
+for PID in $PIDlist; do
+ cmdline=$(ps -o args= -p "$PID")
+ process_group_leader=$(ps -o comm= -p "$(ps -o pgid= -p "$PID" | tr -d ' ')")
+ cwd=$(readlink /proc/"$PID"/cwd)
+ # zsh and lf won't be ignored even if it shows ~ or /
+ case "$cmdline" in
+ 'lf -server') continue ;;
+ "${SHELL##*/}" | 'lf' | 'lf '*) break ;;
+ esac
+ # git (and its sub-processes) will show the root of a repository instead of the actual cwd, so they're ignored
+ [ "$process_group_leader" = 'git' ] || [ ! -d "$cwd" ] && continue
+ # This is to ignore programs that show ~ or / instead of the actual working directory
+ [ "$cwd" != "$HOME" ] && [ "$cwd" != '/' ] && break
+done
+[ "$PWD" != "$cwd" ] && [ -d "$cwd" ] && { cd "$cwd" || exit 1; }
+setsid -f "$TERMINAL"
diff --git a/ar/.local/bin/sessionizer b/ar/.local/bin/sessionizer
new file mode 100755
index 0000000..5857dd3
--- /dev/null
+++ b/ar/.local/bin/sessionizer
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+sessionizer() {
+ [ -n "$TMUX" ] && gitpath=$(tmux display-message -p '#{pane_current_path}') || gitpath="$PWD"
+ path="$(
+ sesh list -c -H | fzf-tmux -p 80%,70% \
+ --no-sort --cycle --ignore-case --ansi --border=sharp --multi --border-label "╢ TheSiahxyz ╟" --prompt "💡 " \
+ --header "^a all ^e sesh ^f zoxide ^g git ^t tmux ^u staged files ^x tmux kill M-cr open in editor ^/ help" \
+ --reverse \
+ --bind "ctrl-a:change-prompt(💡 )+reload(sesh list -H)" \
+ --bind "ctrl-e:change-prompt(📑 )+reload(sesh list -c -H)" \
+ --bind "ctrl-f:change-prompt(🔎 )+reload(sesh list -z -H)" \
+ --bind "ctrl-g:change-prompt( )+reload(fd -H -d 1 -t d -E .Trash -E .git -E .cache . $HOME/Private/repos $HOME/Public/repos | sed 's|$HOME|~|g')" \
+ --bind "ctrl-t:change-prompt(🪟 )+reload(sesh list -t)" \
+ --bind "ctrl-v:execute($EDITOR ${0})+abort" \
+ --bind 'ctrl-x:execute(tmux kill-session -t {})+change-prompt(💡 )+reload(sesh list -t)' \
+ --bind "alt-enter:execute($EDITOR {})+abort" \
+ --bind 'ctrl-/:change-prompt(❓ )+reload(echo "^a all
+^e sesh config
+^f zoxide
+^g git
+^t tmux
+^x tmux kill
+M-cr open in editor
+^/ help")' \
+ --preview-window 'right:60%' \
+ --preview 'sesh preview {}'
+ )" 2>/dev/null
+
+ case "$path" in
+ ^*) sessionizer ;;
+ *) sesh connect "$path" >/dev/null 2>&1 && exit ;;
+ esac
+}
+
+sessionizer
diff --git a/ar/.local/bin/setbg b/ar/.local/bin/setbg
new file mode 100755
index 0000000..66f6819
--- /dev/null
+++ b/ar/.local/bin/setbg
@@ -0,0 +1,71 @@
+#!/bin/sh
+
+# This script does the following:
+# Run by itself, set the wallpaper (at X start).
+# If given a file, set that as the new wallpaper.
+# If given a directory, choose random file in it.
+# If wal is installed, also generates a colorscheme.
+
+# Location of link to wallpaper link.
+wallloc="${XDG_DATA_HOME:-${HOME}/.local/share}/wallpapers"
+bgloc="$wallloc/bg"
+vidloc="$wallloc/video/1/filename00001.jpg"
+
+# Configuration files of applications that have their themes changed by pywal.
+dunstconf="${XDG_CONFIG_HOME:-${HOME}/.config}/dunst/dunstrc"
+zathuraconf="${XDG_CONFIG_HOME:-${HOME}/.config}/zathura/zathurarc"
+
+# Give -s as parameter to make notifications silent.
+while getopts "s" o; do
+ case "${o}" in
+ s) silent='1' ;;
+ *) ;;
+ esac
+done
+
+shift $((OPTIND - 1))
+
+[ -d "$wallloc" ] || mkdir -p "$wallloc"
+
+[ "$(ls "$wallloc/video" 2>/dev/null)" ] && {
+ [ -n "$(pgrep -f "wallset")" ] && pgrep -f "wallset" | xargs kill
+ wallset -r 2>/dev/null
+ wallset -q 2>/dev/null
+ rm -rf "$wallloc/video"
+}
+
+[ -f ~/.fehbg ] && rm -f ~/.fehbg
+
+trueloc="$(readlink -f "$1")" &&
+ case "$(file --mime-type -b "$trueloc")" in
+ image/*) ln -sf "$trueloc" "$bgloc" && [ -z "$silent" ] && notify-send -i "$bgloc" "Changing wallpaper..." ;;
+ inode/directory) ln -sf "$(find -L "$trueloc" -iregex '.*.\(jpg\|jpeg\|png\|gif\)' -type f | shuf -n 1)" "$bgloc" && [ -z "$silent" ] && notify-send -i "$bgloc" "Random Wallpaper chosen." ;;
+ video/*)
+ mkdir -p "$wallloc/video" 2>/dev/null
+ if [ ! "${trueloc##*.}" = "mp4" ] || [ "$(($(mediainfo --Output="General;%Duration%" "$trueloc")))" -gt 10000 ]; then
+ notify-send "⌛ Converting video for wallpaper..." && ffmpeg -i "$trueloc" -t 3 "$wallloc/video/bg.mp4" 2>/dev/null
+ else
+ cp "$trueloc" "$wallloc/video/bg.mp4" 2>/dev/null
+ fi
+ trueloc="$wallloc/video/bg.mp4"
+ wallset --video "$trueloc" >/dev/null 2>&1 && [ -z "$silent" ] && notify-send -i "$vidloc" "Changing wallpaper..."
+ exit
+ ;;
+ *)
+ [ -z "$silent" ] && notify-send "🖼️ Error" "Not a valid image or directory."
+ exit 1
+ ;;
+ esac
+
+# If pywal is installed, use it.
+if command -v wal >/dev/null 2>&1; then
+ wal -n -i "$(readlink -f "$bgloc")" -o "${XDG_CONFIG_HOME:-${HOME}/.config}/wal/postrun" >/dev/null 2>&1
+# If pywal is removed, return config files to normal.
+else
+ [ -f "$dunstconf.bak" ] && unlink "$dunstconf" && mv "$dunstconf.bak" "$dunstconf"
+ [ -f "$zathuraconf.bak" ] && unlink "$zathuraconf" && mv "$zathuraconf.bak" "$zathuraconf"
+fi
+
+xwallpaper --zoom "$bgloc"
+# If running, dwm hit the key to refresh the color scheme.
+pidof dwm >/dev/null && xdotool key super+F5
diff --git a/ar/.local/bin/setlock b/ar/.local/bin/setlock
new file mode 100755
index 0000000..611ae5e
--- /dev/null
+++ b/ar/.local/bin/setlock
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+lockloc="${XDG_DATA_HOME:-${HOME}/.local/share}/wallpapers/lock"
+
+# Give -s as parameter to make notifications silent.
+while getopts "s" o; do
+ case "${o}" in
+ s) silent='1' ;;
+ *) ;;
+ esac
+done
+
+shift $((OPTIND - 1))
+
+trueloc="$(readlink -f "$1")" &&
+ case "$(file --mime-type -b "$trueloc")" in
+ image/*) ln -sf "$trueloc" "$lockloc" && [ -z "$silent" ] && notify-send -i "$lockloc" "Changing lock screen..." ;;
+ inode/directory) ln -sf "$(find -L "$trueloc" -iregex '.*.\(jpg\|jpeg\|png\|gif\)' -type f | shuf -n 1)" "$lockloc" && [ -z "$silent" ] && notify-send -i "$lockloc" "Random Lock Screen chosen." ;;
+ *)
+ [ -z "$silent" ] && notify-send "🖼️ Error" "Not a valid image or directory."
+ exit 1
+ ;;
+ esac
diff --git a/ar/.local/bin/setmonitor b/ar/.local/bin/setmonitor
new file mode 100755
index 0000000..66e2284
--- /dev/null
+++ b/ar/.local/bin/setmonitor
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+# Parse connected displays
+default="--mode 1920x1080 --rotate normal --scale 1.0x1.0 --dpi 96"
+
+for connected in $(xrandr -q | grep "\sconnected" | awk '{print $1}'); do
+ case $connected in
+ eDP*) edp="$connected" ;;
+ HDMI*) hdmi="$connected" ;;
+ DP*) dp="$connected" ;;
+ *) display="$connected" ;;
+ esac
+done
+
+# If the lid is closed, turn off the laptop's screen
+if grep -q "disabled" /sys/class/drm/card0-eDP-1/enabled || grep -q "closed" /proc/acpi/button/lid/LID/state; then
+ if [ -n "$hdmi" ] && [ -z "$dp" ] && [ -n "$edp" ]; then
+ xrandr --output "$edp" --off --output "$hdmi" --primary $default
+ elif [ -z "$hdmi" ] && [ -n "$dp" ] && [ -n "$edp" ]; then
+ xrandr --output "$edp" --off --output "$dp" --primary $default
+ else
+ xrandr --output "$edp" --off --output "$display" --primary $default
+ fi
+else
+ # Apply display settings when lid is open
+ if [ -n "$hdmi" ] && [ -z "$dp" ] && [ -n "$edp" ]; then
+ xrandr --output "$edp" --pos 1920x0 $default --output "$hdmi" --primary --pos 0x0 $default
+ elif [ -z "$hdmi" ] && [ -n "$dp" ] && [ -n "$edp" ]; then
+ xrandr --output "$edp" --pos 1920x0 $default --output "$dp" --primary --pos 0x0 $default
+ elif [ -z "$hdmi" ] && [ -z "$dp" ] && [ -n "$edp" ]; then
+ xrandr --output "$edp" --primary $default
+ else
+ xrandr --output "$display" --primary --auto
+ fi
+fi
diff --git a/ar/.local/bin/shortcuts b/ar/.local/bin/shortcuts
new file mode 100755
index 0000000..e7c96c1
--- /dev/null
+++ b/ar/.local/bin/shortcuts
@@ -0,0 +1,86 @@
+#!/bin/sh
+
+bmdirs="${XDG_CONFIG_HOME:-${HOME}/.config}/shell/bm-dirs"
+bmfiles="${XDG_CONFIG_HOME:-${HOME}/.config}/shell/bm-files"
+
+# Output locations. Unactivated progs should go to /dev/null.
+shell_shortcuts="${XDG_CONFIG_HOME:-${HOME}/.config}/shell/shortcutrc"
+shell_env_shortcuts="${XDG_CONFIG_HOME:-$HOME/.config}/shell/shortcutenvrc"
+zsh_named_dirs="${XDG_CONFIG_HOME:-${HOME}/.config}/shell/zshnameddirrc"
+lf_shortcuts="${XDG_CONFIG_HOME:-${HOME}/.config}/lf/shortcutrc"
+vim_shortcuts="${XDG_CONFIG_HOME:-${HOME}/.config}/vim/shortcuts.vim"
+nvim_shortcuts="${XDG_CONFIG_HOME:-${HOME}/.config}/nvim/shortcuts.lua"
+ranger_shortcuts="/dev/null"
+qute_shortcuts="/dev/null"
+fish_shortcuts="/dev/null"
+vifm_shortcuts="/dev/null"
+
+# Remove, prepare files
+rm -f "$lf_shortcuts" "$ranger_shortcuts" "$qute_shortcuts" "$zsh_named_dirs" "$vim_shortcuts" "$nvim_shortcuts" 2>/dev/null
+printf "# vim: filetype=sh\\n" >"$fish_shortcuts"
+printf "# vim: filetype=sh\\nalias " >"$shell_shortcuts"
+printf "# vim: filetype=sh\\n" >"$shell_env_shortcuts"
+printf "\" vim: filetype=vim\\n" >"$vifm_shortcuts"
+
+# Format the `directories` file in the correct syntax and sent it to all three configs.
+eval "echo \"$(cat "$bmdirs")\"" |
+ awk "!/^\s*#/ && !/^\s*\$/ {gsub(\"\\\s*#.*$\",\"\");
+ printf(\"%s=\42cd %s && ls -A\42 \\\\\n\",\$1,\$2) >> \"$shell_shortcuts\" ;
+ printf(\"[ -n \42%s\42 ] && export %s=\42%s\42 \n\",\$1,\$1,\$2) >> \"$shell_env_shortcuts\" ;
+ printf(\"hash -d %s=%s \n\",\$1,\$2) >> \"$zsh_named_dirs\" ;
+ printf(\"abbr %s \42cd %s; and ls -A\42\n\",\$1,\$2) >> \"$fish_shortcuts\" ;
+ printf(\"map g%s :cd %s<CR>\nmap t%s <tab>:cd %s<CR><tab>\nmap M%s <tab>:cd %s<CR><tab>:mo<CR>\nmap Y%s <tab>:cd %s<CR><tab>:co<CR> \n\",\$1,\$2, \$1, \$2, \$1, \$2, \$1, \$2) >> \"$vifm_shortcuts\" ;
+ printf(\"config.bind(';%s', \42set downloads.location.directory %s ;; hint links download\42) \n\",\$1,\$2) >> \"$qute_shortcuts\" ;
+ printf(\"map g%s cd %s\nmap t%s tab_new %s\nmap m%s shell mv -v %%s %s\nmap Y%s shell cp -rv %%s %s \n\",\$1,\$2,\$1,\$2, \$1, \$2, \$1, \$2) >> \"$ranger_shortcuts\" ;
+ printf(\"map %s cd \42%s\42 \n\",\$1,\$2) >> \"$lf_shortcuts\" ;
+ printf(\"cmap ;%s %s\n\",\$1,\$2) >> \"$vim_shortcuts\" ;
+ printf(\"nmap <localleader><localleader>%s :Explore %s<cr>\n\",\$1,\$2) >> \"$vim_shortcuts\" ;
+ printf(\"vim.keymap.set('c', ';%s', '%s<cr>', { noremap = true, silent = true, desc = '%s' })\n\", \$1, \$2, gensub(\"^/home/si/\",\"~/\",\"g\",\$2)) >> \"$nvim_shortcuts\" ;
+ printf(\"vim.keymap.set('n', '<localleader><leader>%s', '<cmd>Explore %s<cr>', { noremap = true, silent = true, desc = '%s' })\n\", \$1, \$2, gensub(\"^/home/si/\",\"~/\",\"g\",\$2)) >> \"$nvim_shortcuts\" ;
+ printf(\"vim.keymap.set('n', '<localleader><localleader>%s', function() require('mini.files').open('%s') end, { noremap = true, silent = true, desc = '%s' })\n\", \$1, \$2, gensub(\"^/home/si/\",\"~/\",\"g\",\$2)) >> \"$nvim_shortcuts\"}"
+
+# Format the `files` file in the correct syntax and sent it to both configs.
+eval "echo \"$(cat "$bmfiles")\"" |
+ awk "!/^\s*#/ && !/^\s*\$/ {gsub(\"\\\s*#.*$\",\"\");
+ printf(\"%s=\42\$EDITOR %s\42 \\\\\n\",\$1,\$2) >> \"$shell_shortcuts\" ;
+ printf(\"[ -n \42%s\42 ] && export %s=\42%s\42 \n\",\$1,\$1,\$2) >> \"$shell_env_shortcuts\" ;
+ printf(\"v%s=\42\$EDITOR2 %s\42 \\\\\n\",\$1,\$2) >> \"$shell_shortcuts\" ;
+ printf(\"hash -d %s=%s \n\",\$1,\$2) >> \"$zsh_named_dirs\" ;
+ printf(\"abbr %s \42\$EDITOR %s\42 \n\",\$1,\$2) >> \"$fish_shortcuts\" ;
+ printf(\"abbr v%s \42\$EDITOR2 %s\42 \n\",\$1,\$2) >> \"$fish_shortcuts\" ;
+ printf(\"map %s :e %s<CR> \n\",\$1,\$2) >> \"$vifm_shortcuts\" ;
+ printf(\"map %s shell \$EDITOR %s \n\",\$1,\$2) >> \"$ranger_shortcuts\" ;
+ printf(\"map v%s shell \$EDITOR2 %s \n\",\$1,\$2) >> \"$ranger_shortcuts\" ;
+ printf(\"map %s \$\$EDITOR \42%s\42 \n\",\$1,\$2) >> \"$lf_shortcuts\" ;
+ printf(\"map v%s \$\$EDITOR2 \42%s\42 \n\",\$1,\$2) >> \"$lf_shortcuts\" ;
+ printf(\"cmap ;%s %s\n\",\$1,\$2) >> \"$vim_shortcuts\" ;
+ printf(\"nmap <localleader><localleader>%s :e %s<cr>\n\",\$1,\$2) >> \"$vim_shortcuts\" ;
+ printf(\"vim.keymap.set('c', ';%s', '%s<cr>', { noremap = true, silent = true, desc = '%s' })\n\", \$1, \$2, gensub(\"^/home/si/\",\"~/\",\"g\",\$2)) >> \"$nvim_shortcuts\" ;
+ printf(\"vim.keymap.set('n', '<localleader><localleader>%s', '<cmd>e %s<cr>', { noremap = true, silent = true, desc = '%s' })\n\", \$1, \$2, gensub(\"^/home/si/\",\"~/\",\"g\",\$2)) >> \"$nvim_shortcuts\"}"
+
+# root
+root_shell_shortcuts="/root/.config/shell/rootshortcutrc"
+root_zsh_named_dirs="/root/.config/shell/rootzshnameddirrc"
+root_lf_shortcuts="/root/.config/lf/rootshortcutrc"
+root_vim_shortcuts="/root/.config/vim/rootshortcuts.vim"
+
+sudo rm -f "$root_zsh_named_dirs" "$root_lf_shortcuts" "$root_vim_shortcuts" 2>/dev/null
+printf "# vim: filetype=sh\\nalias " | sudo tee "$root_shell_shortcuts" 2>/dev/null
+sudo mkdir -p /root/.config/shell/ /root/.config/lf/ /root/.config/vim/
+sudo touch "$root_shell_shortcuts" "$root_zsh_named_dirs" "$root_lf_shortcuts" "$root_vim_shortcuts"
+
+eval "echo \"$(cat "$bmdirs")\"" |
+ sudo awk "!/^\s*#/ && !/^\s*\$/ && /cache|config($|\/bash|\/lf|\/shell|\/vim)|local\/(bin|share|state)$/ {gsub(\"\\\s*#.*$\",\"\"); gsub(\"home/si\", \"root\");
+ printf(\",%s=\42cd %s && ls -A\42 \\\\\n\",\$1,\$2) >> \"$root_shell_shortcuts\" ;
+ printf(\"hash -d ,%s=%s \n\",\$1,\$2) >> \"$root_zsh_named_dirs\" ;
+ printf(\"map ,%s cd \42%s\42 \n\",\$1,\$2) >> \"$root_lf_shortcuts\" ;
+ printf(\"cmap ;%s %s\n\",\$1,\$2) >> \"$root_vim_shortcuts\" ;
+ printf(\"nmap <localleader><localleader>%s :Explore %s<cr>\n\",\$1,\$2) >> \"$root_vim_shortcuts\"}"
+
+eval "echo \"$(cat "$bmfiles")\"" |
+ sudo awk "!/^\s*#/ && !/^\s*\$/ && /config\/(bash|lf|vim)\/.*rc |inputrc|\$EDITOR / {gsub(\"\\\s*#.*$\",\"\"); gsub(\"home/si\", \"root\");
+ printf(\",%s=\42\$EDITOR %s\42 \\\\\n\",\$1,\$2) >> \"$root_shell_shortcuts\" ;
+ printf(\"hash -d ,%s=%s \n\",\$1,\$2) >> \"$root_zsh_named_dirs\" ;
+ printf(\"map ,%s \$\$EDITOR \42%s\42 \n\",\$1,\$2) >> \"$root_lf_shortcuts\" ;
+ printf(\"cmap ;%s %s\n\",\$1,\$2) >> \"$root_vim_shortcuts\" ;
+ printf(\"nmap <localleader><localleader>%s :e %s<cr>\n\",\$1,\$2) >> \"$root_vim_shortcuts\"}"
diff --git a/ar/.local/bin/slider b/ar/.local/bin/slider
new file mode 100755
index 0000000..35bb6a7
--- /dev/null
+++ b/ar/.local/bin/slider
@@ -0,0 +1,132 @@
+#!/bin/sh
+
+# Give a file with images and timecodes and creates a video slideshow of them.
+#
+# Timecodes must be in format 00:00:00.
+#
+# Imagemagick and ffmpeg required.
+
+# Application cache if not stated elsewhere.
+cache="${XDG_CACHE_HOME:-${HOME}/.cache}/slider"
+
+while getopts "hvrpi:c:a:o:d:f:t:e:x:s:" o; do case "${o}" in
+ c) bgc="$OPTARG" ;;
+ t) fgc="$OPTARG" ;;
+ f) font="$OPTARG" ;;
+ i) file="$OPTARG" ;;
+ a) audio="$OPTARG" ;;
+ o) outfile="$OPTARG" ;;
+ d) prepdir="$OPTARG" ;;
+ r) redo="$OPTARG" ;;
+ s) ppt="$OPTARG" ;;
+ e) endtime="$OPTARG" ;;
+ x)
+ res="$OPTARG"
+ echo "$res" | grep -qv "^[0-9]\+x[0-9]\+$" &&
+ echo "Resolution must be dimensions separated by a 'x': 1280x720, etc." &&
+ exit 1
+ ;;
+ p)
+ echo "Purge old build files in $cache? [y/N]"
+ read -r confirm
+ echo "$confirm" | grep -iq "^y$" && rm -rf "$cache" && echo "Done."
+ exit
+ ;;
+ v) verbose=True ;;
+ *) echo "$(basename "$0") usage:
+ -i input timecode list (required)
+ -a audio file
+ -c color of background (use html names, black is default)
+ -t text color for text slides (white is default)
+ -s text font size for text slides (150 is default)
+ -f text font for text slides (sans serif is default)
+ -o output video file
+ -e if no audio given, the time in seconds that the last slide will be shown (5 is default)
+ -x resolution (1920x1080 is default)
+ -d tmp directory
+ -r rerun imagemagick commands even if done previously (in case files or background has changed)
+ -p purge old build files instead of running
+ -v be verbose" && exit 1 ;;
+
+ esac done
+
+# Check that the input file looks like it should.
+{ head -n 1 "$file" 2>/dev/null | grep -q "^00:00:00 "; } || {
+ echo "Give an input file with -i." &&
+ echo "The file should look as this example:
+
+00:00:00 first_image.jpg
+00:00:03 otherdirectory/next_image.jpg
+00:00:09 this_image_starts_at_9_seconds.jpg
+etc...
+
+Timecodes and filenames must be separated by Tabs." &&
+ exit 1
+}
+
+if [ -n "${audio+x}" ]; then
+ # Check that the audio file looks like an actual audio file.
+ case "$(file --dereference --brief --mime-type -- "$audio")" in
+ audio/*) ;;
+ *)
+ echo "That doesn't look like an audio file."
+ exit 1
+ ;;
+ esac
+ totseconds="$(date '+%s' -d $(ffmpeg -i "$audio" 2>&1 | awk '/Duration/ {print $2}' | sed s/,//))"
+fi
+
+prepdir="${prepdir:-$cache/$file}"
+outfile="${outfile:-$file.mp4}"
+prepfile="$prepdir/$file.prep"
+
+[ -n "${verbose+x}" ] && echo "Preparing images... May take a while depending on the number of files."
+mkdir -p "$prepdir"
+
+{
+ while read -r x; do
+ # Get the time from the first column.
+ time="${x%% *}"
+ seconds="$(date '+%s' -d "$time")"
+ # Duration is not used on the first looped item.
+ duration="$((seconds - prevseconds))"
+
+ # Get the filename/text content from the rest.
+ content="${x#* }"
+ base="$(basename "$content")"
+ base="${base%.*}.jpg"
+
+ if [ -f "$content" ]; then
+ # If images have already been made in a previous run, do not recreate
+ # them unless -r was given.
+ { [ ! -f "$prepdir/$base" ] || [ -n "${redo+x}" ]; } &&
+ magick -size "${res:-1920x1080}" canvas:"${bgc:-black}" -gravity center "$content" -resize 1920x1080 -composite "$prepdir/$base"
+ else
+ { [ ! -f "$prepdir/$base" ] || [ -n "${redo+x}" ]; } &&
+ magick -size "${res:-1920x1080}" -background "${bgc:-black}" -fill "${fgc:-white}" -font "${font:-Sans}" -pointsize "${ppt:-150}" -gravity center label:"$content" "$prepdir/$base"
+ fi
+
+ # If the first line, do not write yet.
+ [ "$time" = "00:00:00" ] || echo "file '$prevbase'
+duration $duration"
+
+ # Keep the information required for the next file.
+ prevbase="$base"
+ prevtime="$time"
+ prevseconds="$(date '+%s' -d "$prevtime")"
+ done <"$file"
+ # Do last file which must be given twice as follows
+ endtime="$((totseconds - seconds))"
+ echo "file '$base'
+duration ${endtime:-5}
+file '$base'"
+} >"$prepfile"
+if [ -n "${audio+x}" ]; then
+ ffmpeg -hide_banner -y -f concat -safe 0 -i "$prepfile" -i "$audio" -c:a aac -vsync vfr -c:v libx264 -pix_fmt yuv420p "$outfile"
+else
+ ffmpeg -hide_banner -y -f concat -safe 0 -i "$prepfile" -vsync vfr -c:v libx264 -pix_fmt yuv420p "$outfile"
+fi
+
+# Might also try:
+# -vf "fps=${fps:-24},format=yuv420p" "$outfile"
+# but has given some problems.
diff --git a/ar/.local/bin/sshadd b/ar/.local/bin/sshadd
new file mode 100755
index 0000000..76797ba
--- /dev/null
+++ b/ar/.local/bin/sshadd
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+SSH_KEY_DIR="${PASSWORD_STORE_DIR:-${HOME}/.local/share/.password-store}"
+
+temp_private_keys_list=$(mktemp)
+
+# Ensure that filenames with spaces or other special characters are handled correctly.
+find "$SSH_KEY_DIR" -name "*.pub" | while IFS= read -r pub_file_path; do
+ private_key_path="${pub_file_path%.pub}"
+ if [ -f "$private_key_path" ]; then
+ echo "$(basename "$private_key_path")" >>"$temp_private_keys_list"
+ echo "$private_key_path" >>"$temp_private_keys_list"
+ fi
+done
+
+# Use of dmenu is system-specific and not covered by POSIX standards
+selected_key_name=$(awk 'NR % 2 == 1' "$temp_private_keys_list" | dmenu -i -p "Select SSH key:")
+
+if [ -n "$selected_key_name" ]; then
+ selected_key_path=$(awk -v name="$selected_key_name" '$0 == name { getline; print }' "$temp_private_keys_list")
+
+ if [ -n "$selected_key_path" ]; then
+ export SSH_ASKPASS="$HOME/.local/bin/demupass"
+ setsid ssh-add "$selected_key_path" </dev/null
+ ln -sf "$selected_key_path" "$HOME/.ssh/$(basename "$selected_key_path")"
+ ln -sf "${selected_key_path}.pub" "$HOME/.ssh/$(basename "$selected_key_path").pub"
+ notify-send "🔑 SSH key added:" "$selected_key_name"
+ fi
+fi
+
+rm "$temp_private_keys_list"
diff --git a/ar/.local/bin/statusbar/sb-battery b/ar/.local/bin/statusbar/sb-battery
new file mode 100755
index 0000000..faf3d04
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-battery
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+# Prints all batteries, their percentage remaining and an emoji corresponding
+# to charge status (🔌 for plugged up, 🔋 for discharging on battery, etc.).
+
+# Function to get the battery status icon
+get_status_icon() {
+ case "$1" in
+ Full) echo "⚡" ;;
+ Discharging) echo "🔋" ;;
+ Charging) echo "🔌" ;;
+ "Not charging") echo "🛑" ;;
+ Unknown) echo "♻️" ;;
+ *) echo "" ;;
+ esac
+}
+
+# Function to print battery status
+battery_status() {
+ device=$1
+ capacity=$(cat "$device/capacity" 2>/dev/null)
+ status=$(cat "$device/status" 2>/dev/null)
+ icon=$(get_status_icon "$status")
+ [ -z "$icon" ] && return
+ case $(basename "$device") in
+ BAT?*)
+ [ "$icon" = "🔋" ] && [ "$capacity" -le 25 ] && warn="❗"
+ printf "%s%s%d%%" "$icon" "$warn" "$capacity"
+ ;;
+ hid*)
+ model="$(cat "$device/model_name")"
+ notify-send "$icon $model:" "$capacity%"
+ ;;
+ esac
+ unset warn
+}
+
+devices() {
+ for battery in /sys/class/power_supply/$1; do
+ battery_status "$battery"
+ done && printf "\\n"
+}
+
+bluetooth() {
+ bluedevices=$(upower -e | grep -iv 'line' | grep -iv 'display' | grep -v 'BAT[0-9]' | grep -v 'hid')
+ for bluedevice in $bluedevices; do
+ bluedevice_name=$(upower -i "$bluedevice" | grep "model" | awk -F ': ' '{print $2}' | sed 's/ //g')
+ bluedevice_battery=$(upower -i "$bluedevice" | grep "percentage" | awk -F ': ' '{print $2}' | sed 's/ //g')
+ if [ -n "$bluedevice_battery" ]; then
+ notify-send "🔋 $bluedevice_name:" "$bluedevice_battery"
+ fi
+ done
+}
+
+# Handle mouse click actions
+case "$BLOCK_BUTTON" in
+2) bluetooth && devices "hid*" ;; # Middle click for Bluetooth battery levels
+3) notify-send "🔋 Battery module" "\- 🔋: discharging
+- 🛑: not charging
+- ♻️: stagnant charge
+- 🔌: charging
+- ⚡: fully charged
+- ❗: battery very low!
+- Middle click: bluetooth battery levels via upower" ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+devices "BAT?*"
diff --git a/ar/.local/bin/statusbar/sb-bghitness b/ar/.local/bin/statusbar/sb-bghitness
new file mode 100755
index 0000000..0aabfb6
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-bghitness
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+# current brightness
+curr_brightness=$(cat /sys/class/backlight/*/brightness)
+
+# max_brightness
+max_brightness=$(cat /sys/class/backlight/*/max_brightness)
+
+# brightness percentage
+brightness_per=$((100 * curr_brightness / max_brightness))
+
+case $BLOCK_BUTTON in
+3) notify-send "💡 Brightness module" "\- Shows current brightness level ☀️." ;;
+4) pkexec brillo -A 5 -q ;;
+5) pkexec brillo -U 5 -q ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+echo "💡${brightness_per}%"
diff --git a/ar/.local/bin/statusbar/sb-brightness b/ar/.local/bin/statusbar/sb-brightness
new file mode 100755
index 0000000..909e676
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-brightness
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+case $BLOCK_BUTTON in
+3) notify-send "🔆 Backlight module
+- Scroll up & down changes backlight" ;;
+4) monbright -inc 5 ;;
+5) monbright -dec 5 ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+monitorbright
diff --git a/ar/.local/bin/statusbar/sb-clock b/ar/.local/bin/statusbar/sb-clock
new file mode 100755
index 0000000..e0e8c53
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-clock
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+# Get current hour and minute
+hour=$(date '+%I')
+minute=$(date '+%M')
+calendar="🗓️"
+
+# Determine the icon based on hour and minute
+if crontab -l 2>/dev/null | grep -q '^[^#[:space:]]'; then
+ if [ "$minute" -ge 30 ]; then
+ case "$hour" in
+ "00" | "12") icon="🕧" ;; # 12:30
+ "01" | "13") icon="🕜" ;; # 1:30
+ "02" | "14") icon="🕝" ;; # 2:30
+ "03" | "15") icon="🕞" ;; # 3:30
+ "04" | "16") icon="🕟" ;; # 4:30
+ "05" | "17") icon="🕠" ;; # 5:30
+ "06" | "18") icon="🕡" ;; # 6:30
+ "07" | "19") icon="🕢" ;; # 7:30
+ "08" | "20") icon="🕣" ;; # 8:30
+ "09" | "21") icon="🕤" ;; # 9:30
+ "10" | "22") icon="🕥" ;; # 10:30
+ "11" | "23") icon="🕦" ;; # 11:30
+ esac
+ else
+ case "$hour" in
+ "00" | "12") icon="🕛" ;; # 12:00
+ "01" | "13") icon="🕐" ;; # 1:00
+ "02" | "14") icon="🕑" ;; # 2:00
+ "03" | "15") icon="🕒" ;; # 3:00
+ "04" | "16") icon="🕓" ;; # 4:00
+ "05" | "17") icon="🕔" ;; # 5:00
+ "06" | "18") icon="🕕" ;; # 6:00
+ "07" | "19") icon="🕖" ;; # 7:00
+ "08" | "20") icon="🕗" ;; # 8:00
+ "09" | "21") icon="🕘" ;; # 9:00
+ "10" | "22") icon="🕙" ;; # 10:00
+ "11" | "23") icon="🕚" ;; # 11:00
+ esac
+ fi
+else
+ icon="⏰"
+fi
+
+# Shows the current moon phase.
+moonfile="${XDG_DATA_HOME:-${HOME}/.local/share}/moonphase"
+
+[ -s "$moonfile" ] && [ "$(stat -c %y "$moonfile" 2>/dev/null | cut -d' ' -f1)" = "$(date '+%Y-%m-%d')" ] ||
+ { curl -sf "wttr.in/?format=%m" >"$moonfile" || exit 1; }
+
+moon="$(cat "$moonfile")"
+
+case $BLOCK_BUTTON in
+1)
+ notify-send "This Month" "$(cal | sed "s/\<$(date +'%B' | tr -d ' ')\>/<b><span color='blue'>&<\/span><\/b>/;s/\<$(date +'%Y' | sed 's/ //g')\>/<b><span color='blue'>&<\/span><\/b>/;s/\<$(date +'%B' | sed 's/ //g')\>/<b><span color='blue'>&<\/span><\/b>/;s/\<$(date +'%e' | sed 's/ //g')\>/<b><span color='red'>&<\/span><\/b>/")" && notify-send "Appointments" "$(calcurse -d3)"
+ ;;
+2) setsid -f "$TERMINAL" -e calcurse ;;
+3)
+ notify-send "📅 Time/date module" "\- Left click to show upcoming appointments for the next three days via \`calcurse -d3\` and show the month via \`cal\`
+- Left click also displays the current time in other cities.
+- Middle click opens calcurse if installed"
+ notify-send "🌜 Moon phase module" "Displays current moon phase
+- 🌑: New
+- 🌒: Waxing Crescent
+- 🌓: First Quarter
+- 🌔: Waxing Gibbous
+- 🌕: Full
+- 🌖: Waning Gibbous
+- 🌗: Last Quarter
+- 🌘: Waning Crescent"
+ ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+7) timezones ;;
+esac
+
+# Output the formatted date and time
+date "+${moon-$calendar}%a,%d $icon%H:%M"
diff --git a/ar/.local/bin/statusbar/sb-cpu b/ar/.local/bin/statusbar/sb-cpu
new file mode 100755
index 0000000..9dfbb54
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-cpu
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+case $BLOCK_BUTTON in
+1) notify-send "🖥 CPU hogs" "$(ps axch -o cmd:15,%cpu --sort=-%cpu | head)\\n(100% per core)" ;;
+2) setsid -f "$TERMINAL" -e htop ;;
+3) notify-send "🖥 CPU module " "\- Shows CPU temperature
+- Left click to show intensive processes
+- Middle click to open htop" ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+sensors | awk '/Core 0/ {printf "🍉%d°\n", $3}'
diff --git a/ar/.local/bin/statusbar/sb-cpubars b/ar/.local/bin/statusbar/sb-cpubars
new file mode 100755
index 0000000..ce677ef
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-cpubars
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+# Module showing CPU load as a changing bars.
+# Just like in polybar.
+# Each bar represents amount of load on one core since
+# last run.
+
+# Cache in tmpfs to improve speed and reduce SSD load
+cache=/tmp/cpubarscache
+
+case $BLOCK_BUTTON in
+2) setsid -f "$TERMINAL" -e htop ;;
+3) notify-send "🪨 CPU load module" "Each bar represents one CPU core" ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+# id total idle
+stats=$(awk '/cpu[0-9]+/ {printf "%d %d %d\n", substr($1,4), ($2 + $3 + $4 + $5), $5 }' /proc/stat)
+[ ! -f $cache ] && echo "$stats" >"$cache"
+old=$(cat "$cache")
+printf "🪨"
+echo "$stats" | while read -r row; do
+ id=${row%% *}
+ rest=${row#* }
+ total=${rest%% *}
+ idle=${rest##* }
+
+ case "$(echo "$old" | awk '{if ($1 == id)
+ printf "%d\n", (1 - (idle - $3) / (total - $2))*100 /12.5}' \
+ id="$id" total="$total" idle="$idle")" in
+
+ "0") printf "▁" ;;
+ "1") printf "▂" ;;
+ "2") printf "▃" ;;
+ "3") printf "▄" ;;
+ "4") printf "▅" ;;
+ "5") printf "▆" ;;
+ "6") printf "▇" ;;
+ "7") printf "█" ;;
+ "8") printf "█" ;;
+ esac
+done
+printf "\\n"
+echo "$stats" >"$cache"
diff --git a/ar/.local/bin/statusbar/sb-disk b/ar/.local/bin/statusbar/sb-disk
new file mode 100755
index 0000000..3c92f00
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-disk
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# Status bar module for disk space
+# $1 should be drive mountpoint, otherwise assumed /.
+
+location=${1:-/}
+
+[ -d "$location" ] || exit
+
+case "$location" in
+"/home"*) icon="🏠" ;;
+"/mnt"*) icon="💾" ;;
+*) icon="💻" ;;
+esac
+
+usage=$(df -h "$location" | awk ' /[0-9]/ {print $3 "/" $2}')
+
+case $BLOCK_BUTTON in
+1) notify-send "💽 Disk space" "$(df -h --output=target,used,size)" ;;
+2) notify-send "💽 Disk usage" "$icon: $usage" ;;
+3) notify-send "💽 Disk module" "\- Shows used hard drive space
+- Left click to show all disk info
+- Middle click to show disk usage" ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+printf "%s%s\n" "$icon" "$(df -hP "$location" | awk ' /[0-9]/ {print $5}')"
diff --git a/ar/.local/bin/statusbar/sb-ecrypt b/ar/.local/bin/statusbar/sb-ecrypt
new file mode 100755
index 0000000..24c4cb3
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-ecrypt
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+is_mounted() { mount | grep -q "$HOME/Private"; }
+
+case $BLOCK_BUTTON in
+1) "${XDG_SCRIPTS_HOME:-${HOME}/.local/bin}/ecrypt" ;;
+3) notify-send "🔒 Encrypted Media Folder " "\- Shows mount status of Media
+- Left click to toggle mount" ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+is_mounted "$MOUNT_POINT" && echo "🔑" || echo "🔒"
diff --git a/ar/.local/bin/statusbar/sb-forecast b/ar/.local/bin/statusbar/sb-forecast
new file mode 100755
index 0000000..9966c61
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-forecast
@@ -0,0 +1,402 @@
+#!/bin/sh
+
+# Displays today's snow chance (🏂), precipication chance (☔), humidity (💧), wind speed (🎐), and current (feel like) temperature (🌞).
+# Usually intended for the statusbar.
+
+LOCATION=$(curl -s http://ip-api.com/json | jq -r '[.regionName, .countryCode] | join(",")')
+
+url="${WTTRURL:-wttr.in}"
+weatherreport="${XDG_CACHE_HOME:-${HOME}/.cache}/weatherreport"
+weatherreportjson="${XDG_CACHE_HOME:-${HOME}/.cache}/weatherreport.json"
+
+error() {
+ notify-send -u critical "⛔ Failed to update 'weather$1'"
+ exit 1
+}
+
+# Get a weather report from 'wttr.in' and save it locally.
+getweatherreport() {
+ (timeout --signal=1 10s curl -sf "$url/$LOCATION" >"$weatherreport" &&
+ printf "\nUpdated: %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" >>"$weatherreport") ||
+ error "report"
+}
+
+getweatherreportjson() {
+ timeout --signal=1 10s curl -sf "$url/$LOCATION?format=j1" >"$weatherreportjson" || error "reportjson"
+}
+
+# Forecast should be updated every 3 hours, but set 3600 to check for reliability.
+checkforecast() {
+ [ -s "$1" ] && [ "$(($(date +%s) - $(stat -c %Y "$1")))" -le 3600 ]
+}
+
+get_current_hour() { date +%H | sed 's/^0*//'; }
+
+get_nearest_hourly() {
+ current_hour=$(get_current_hour)
+ hour_index=$((current_hour / 3))
+ jq ".weather[0].hourly[$hour_index]" "$weatherreportjson"
+}
+
+getprecip() { get_nearest_hourly | jq -r '.chanceofrain'; }
+
+gethighprecip() { jq -r '.weather[0].hourly[].chanceofrain' "$weatherreportjson" | sort -rn | head -1; }
+
+getsnow() { get_nearest_hourly | jq -r '.chanceofsnow'; }
+
+getwind() { get_nearest_hourly | jq -r '.windspeedKmph'; }
+
+gettemp() { get_nearest_hourly | jq -r '.tempC'; }
+
+getfeelslike() { get_nearest_hourly | jq -r '.FeelsLikeC'; }
+
+getlowtemp() { jq -r '.weather[0].hourly[].tempC' "$weatherreportjson" | sort -n | head -1; }
+
+gethightemp() { jq -r '.weather[0].hourly[].tempC' "$weatherreportjson" | sort -rn | head -1; }
+
+gethumidity() {
+ humidity=$(get_nearest_hourly | jq -r '.humidity')
+ case "$humidity" in
+ [0-9] | [1-2][0-9]) echo "🏜️: $humidity%" ;;
+ [3-4][0-9]) echo "🌲: $humidity%" ;;
+ [5-6][0-9]) echo "💧: $humidity%" ;;
+ [7-8][0-9]) echo "💦: $humidity%" ;;
+ 9[0-9] | 100) echo "🌊: $humidity%" ;;
+ esac
+}
+
+getdesc() { get_nearest_hourly | jq -r '.weatherDesc[0].value' | sed 's/ $//'; }
+
+showweather() {
+ case "$(gettemp)" in
+ -[0-9]* | 0) icon="🥶" ;; # Temperature <= 0
+ [1-9] | [1-2][0-9]) icon="🌞" ;; # Temperature 1–29
+ 3* | [4-9]*) icon="🥵" ;; # Temperature >= 30
+ esac
+ [ -n "$(gettemp)" ] && echo "$icon$(gettemp)°"
+}
+
+todayweather() {
+ printf "🌈 Today's weather: %s\n🏂: %s%%\n☔: %s(%s)m⧸s\n%s\n🎐: %sm/s\n🌞: %s°(%s°)\n🥶: %s°\n🥵: %s°\n" \
+ "$(getdesc)" "$(getsnow)" "$(getprecip)" "$(gethighprecip)" "$(gethumidity)" "$(getwind)" "$(gettemp)" "$(getfeelslike)" "$(getlowtemp)" "$(gethightemp)"
+}
+
+# Show a Doppler RADAR of a user's preferred location.
+
+secs=600 # Download a new doppler radar if one hasn't been downloaded in $secs seconds.
+radarloc="${XDG_CACHE_HOME:-${HOME}/.cache}/radar"
+doppler="${XDG_CACHE_HOME:-${HOME}/.cache}/doppler.gif"
+
+pickloc() {
+ chosen="$(echo "US: CONUS: Continental United States
+US: Northeast
+US: Southeast
+US: PacNorthWest
+US: PacSouthWest
+US: UpperMissVly
+US: SouthMissVly
+US: SouthPlains
+US: NorthRockies
+US: SouthRockies
+US: Alaska
+US: Carib
+US: Hawaii
+US: CentGrLakes
+US: Conus-Large
+US: KABR: Aberdeen, SD
+US: KBIS: Bismarck, ND
+US: KFTG: Denver/Boulder, CO
+US: KDMX: Des Moines, IA
+US: KDTX: Detroit, MI
+US: KDDC: Dodge City, KS
+US: KDLH: Duluth, MN
+US: KCYS: Cheyenne, WY
+US: KLOT: Chicago, IL
+US: KGLD: Goodland, KS
+US: KUEX: Hastings, NE
+US: KGJX: Grand Junction, CO
+US: KGRR: Grand Rapids, MI
+US: KMVX: Fargo/Grand Forks, ND
+US: KGRB: Green Bay, WI
+US: KIND: Indianapolis, IN
+US: KJKL: Jackson, KY
+US: KARX: La Crosse, WI
+US: KILX: Lincoln/Central Illinois, IL
+US: KLVX: Louisville, KY
+US: KMQT: Marquette
+US: KMKX: Milwaukee, WI
+US: KMPX: Minneapolis, MN
+US: KAPX: Gaylord/Alpena, MI
+US: KLNX: North Platte, NE
+US: KIWX: N. Webster/Northern, IN
+US: KOAX: Omaha, NE
+US: KPAH: Paducah, KY
+US: KEAX: Pleasant Hill, MO
+US: KPUX: Pueblo, CO
+US: KDVN: Quad Cities, IA
+US: KUDX: Rapid City, SD
+US: KRIW: Riverton, WY
+US: KSGF: Springfield, MO
+US: KLSX: St. LOUIS, MO
+US: KFSD: Sioux Falls, SD
+US: KTWX: Topeka, KS
+US: KICT: Wichita, KS
+US: KVWX: Paducah, KY
+US: ICAO: Responsible Wfo
+US: KLTX: WILMINGTON, NC
+US: KCCX: State College/Central, PA
+US: KLWX: Sterling, VA
+US: KFCX: Blacksburg/Roanoke, VA
+US: KRAX: Raleigh/Durham, NC
+US: KGYX: Portland, ME
+US: KDIX: Mt Holly/Philadelphia, PA
+US: KPBZ: Pittsburgh, PA
+US: KAKQ: Wakefield, VA
+US: KMHX: Morehead City, NC
+US: KGSP: Greer/Greenville/Sprtbg, SC
+US: KILN: Wilmington/Cincinnati, OH
+US: KCLE: Cleveland, OH
+US: KCAE: Columbia, SC
+US: KBGM: Binghamton, NY
+US: KENX: Albany, NY
+US: KBUF: Buffalo, NY
+US: KCXX: Burlington, VT
+US: KCBW: Caribou, ME
+US: KBOX: Boston /Taunton, MA
+US: KOKX: New York City, NY
+US: KCLX: Charleston, SC
+US: KRLX: Charleston, WV
+US: ICAO: Responsible WFO
+US: KBRO: Brownsville, TX
+US: KABX: Albuquerque, NM
+US: KAMA: Amarillo, TX
+US: KFFC: Peachtree City/Atlanta, GA
+US: KEWX: Austin/Sanantonio, TX
+US: KBMX: Birmingham, AL
+US: KCRP: Corpus Christi, TX
+US: KFWS: Dallas / Ft. Worth, TX
+US: KEPZ: El Paso, TX
+US: KHGX: Houston/ Galveston, TX
+US: KJAX: Jacksonville, FL
+US: KBYX: Key West, FL
+US: KMRX: Morristown/knoxville, TN
+US: KLBB: Lubbock, TX
+US: KLZK: Little Rock, AR
+US: KLCH: Lake Charles, LA
+US: KOHX: Nashville, TN
+US: KMLB: Melbourne, FL
+US: KNQA: Memphis, TN
+US: KAMX: Miami, FL
+US: KMAF: Midland/odessa, TX
+US: KTLX: Norman, OK
+US: KHTX: Huntsville, AL
+US: KMOB: Mobile, AL
+US: KTLH: Tallahassee, FL
+US: KTBW: Tampa Bay Area, FL
+US: KSJT: San Angelo, TX
+US: KINX: Tulsa, OK
+US: KSRX: Tulsa, OK
+US: KLIX: New Orleans/slidell, LA
+US: KDGX: Jackson, MS
+US: KSHV: Shreveport, LA
+US: ICAO: Responsible WFO
+US: KLGX: Seattle / Tacoma, WA
+US: KOTX: Spokane, WA
+US: KEMX: Tucson, AZ
+US: KYUX: Phoenix, AZ
+US: KNKX: San Diego, CA
+US: KMUX: Monterey/san Francisco, CA
+US: KHNX: San Joaquin/hanford, CA
+US: KSOX: San Diego, CA
+US: KATX: Seattle / Tacoma, WA
+US: KIWA: Phoenix, AZ
+US: KRTX: Portland, OR
+US: KSFX: Pocatello, ID
+US: KRGX: Reno, NV
+US: KDAX: Sacramento, CA
+US: KMTX: Salt Lake City, UT
+US: KPDT: Pendleton, OR
+US: KMSX: Missoula, MT
+US: KESX: Las Vegas, NV
+US: KVTX: Los Angeles, CA
+US: KMAX: Medford, OR
+US: KFSX: Flagstaff, AZ
+US: KGGW: Glasgow, MT
+US: KLRX: Elko, NV
+US: KBHX: Eureka, CA
+US: KTFX: Great Falls, MT
+US: KCBX: Boise, ID
+US: KBLX: Billings, MT
+US: KICX: Salt Lake City, UT
+US: ICAO: Responsible Wfo W/ MSCF
+US: PABC: Anchorage, AK
+US: PAPD: Fairbanks, AK
+US: PHKM: Honolulu, HI
+US: PAHG: Anchorage, AK
+US: PAKC: Anchorage, AK
+US: PAIH: Anchorage, AK
+US: PHMO: Honolulu, HI
+US: PAEC: Fairbanks, AK
+US: TJUA: San Juan, PR
+US: PACG: Juneau, AK
+US: PHKI: Honolulu, HI
+US: PHWA: Honolulu, HI
+US: ICAO: Responsible Wfo W/ MSCF
+US: KFDR: Norman, OK
+US: PGUA: Guam
+US: KBBX: Sacramento, CA
+US: KFDX: Albuquerque, NM
+US: KGWX: Jackson, MS
+US: KDOX: Wakefield, VA
+US: KDYX: San Angelo, TX
+US: KEYX: Las Vegas, NV
+US: KEVX: Mobile, AL
+US: KHPX: Paducah, KY
+US: KTYX: Burlington, VT
+US: KGRK: Dallas / Ft. Worth, TX
+US: KPOE: Lake Charles, LA
+US: KEOX: Tallahassee, FL
+US: KHDX: El Paso, TX
+US: KDFX: San Antonio, TX
+US: KMXX: Birmingham, AL
+US: KMBX: Bismarck, ND
+US: KVAX: Jacksonville, FL
+US: KJGX: Peachtree City/atlanta, GA
+US: KVNX: Norman, OK
+US: KVBX: Vandenberg Afb: Orcutt, CA
+EU: Europe
+EU: GB: Great Brittain
+EU: SCAN: Scandinavia. Norway, Sweden And Denmark
+EU: ALPS: The Alps
+EU: NL: The Netherlands
+EU: DE: Germany
+EU: SP: Spain
+EU: FR: France
+EU: IT: Italy
+EU: PL: Poland
+EU: GR: Greece
+EU: TU: Turkey
+EU: RU: Russia
+EU: BA: Bahrain
+EU: BC: Botswana
+EU: SE: Republic of Seychelles
+EU: HU: Hungary
+EU: UK: Ukraine
+AF: AF: Africa
+AF: WA: West Africa
+AF: ZA: South Africa
+AF: DZ: Algeria
+AF: CE: Canary Islands
+AF: NG: Nigeria
+AF: TD: Chad
+AF: CG: Democratic Republic of Congo
+AF: EG: Egypt
+AF: ET: Ethiopia
+AF: CM: Cameroon
+AF: IS: Israel
+AF: LY: Libya
+AF: MG: Madagascar
+AF: MO: Morocco
+AF: BW: Namibia
+AF: SA: Saudi Arabia
+AF: SO: Somalia
+AF: SD: Sudan
+AF: TZ: Tanzania
+AF: TN: Tunisia
+AF: ZM: Zambia
+AF: KE: Kenya
+AF: AO: Angola
+DE: BAW: Baden-Württemberg
+DE: BAY: Bavaria
+DE: BBB: Berlin
+DE: BBB: Brandenburg
+DE: HES: Hesse
+DE: MVP: Mecklenburg-Western Pomerania
+DE: NIB: Lower Saxony
+DE: NIB: Bremen
+DE: NRW: North Rhine-Westphalia
+DE: RPS: Rhineland-Palatinate
+DE: RPS: Saarland
+DE: SAC: Saxony
+DE: SAA: Saxony-Anhalt
+DE: SHH: Schleswig-Holstein
+DE: SHH: Hamburg
+DE: THU: Thuringia" | dmenu -r -i -l 50 -p "Select a radar to use as default:" | tr "[:lower:]" "[:upper:]")"
+
+ # Ensure user did not escape.
+ [ -z "$chosen" ] && exit 1
+
+ # Set continent code and radar code.
+ continentcode=${chosen%%:*}
+ radarcode=${chosen#* } radarcode=${radarcode%:*}
+
+ # Print codes to $radarloc file.
+ printf "%s,%s\\n" "$continentcode" "$radarcode" >"$radarloc"
+}
+
+getdoppler() {
+ cont=$(cut -c -2 "$radarloc")
+ loc=$(cut -c 4- "$radarloc")
+ notify-send "🌦️ Doppler RADAR" "Pulling most recent Doppler RADAR for $loc."
+ case "$cont" in
+ "US") curl -sL "https://radar.weather.gov/ridge/standard/${loc}_loop.gif" >"$doppler" ;;
+ "EU") curl -sL "https://api.sat24.com/animated/${loc}/rainTMC/2/" >"$doppler" ;;
+ "AF") curl -sL "https://api.sat24.com/animated/${loc}/rain/2/" >"$doppler" ;;
+ "DE")
+ loc="$(echo "$loc" | tr "[:upper:]" "[:lower:]")"
+ curl -sL "https://www.dwd.de/DWD/wetter/radar/radfilm_${loc}_akt.gif" >"$doppler"
+ ;;
+ esac
+}
+
+showdoppler() { setsid -f mpv --no-osc --loop=inf --no-terminal "$doppler"; }
+
+case $BLOCK_BUTTON in
+1)
+ [ "$MANPAGER" = "less -s" ] && pager=true || pager=false
+ [ "$pager" = "false" ] && {
+ export MANPAGER='less -s'
+ export LESS="R"
+ export LESS_TERMCAP_mb="$(printf '%b' '')"
+ export LESS_TERMCAP_md="$(printf '%b' '')"
+ export LESS_TERMCAP_me="$(printf '%b' '')"
+ export LESS_TERMCAP_so="$(printf '%b' '')"
+ export LESS_TERMCAP_se="$(printf '%b' '')"
+ export LESS_TERMCAP_us="$(printf '%b' '')"
+ export LESS_TERMCAP_ue="$(printf '%b' '')"
+ export LESSOPEN="| /usr/bin/highlight -O ansi %s 2>/dev/null"
+ }
+ setsid -f "$TERMINAL" -e less -Sf "${XDG_CACHE_HOME:-${HOME}/.cache}/weatherreport"
+ [ "$pager" = "false" ] && {
+ export MANPAGER="sh -c 'col -bx | bat -l man -p'"
+ export MANROFFOPT="-c"
+ }
+ ;;
+2)
+ [ ! -f "$radarloc" ] && pickloc && getdoppler
+ [ $(($(date '+%s') - $(stat -c %Y "$doppler"))) -gt "$secs" ] && getdoppler
+ showdoppler
+ ;;
+3)
+ notify-send "🌈 Weather module (updates every 3 hours)" "\- Left click for full forecast
+- Shift + left click to update forecast
+🏂: Chance of snow (hidden if it's 0)
+☔: Chance of rain
+💧: Humidity (icon changes depending on the level)
+🎐: Wind speed
+🌞: Current (feel like) temperature
+🥶: Daily lowest temperature
+🥵: Daily highest temperature"
+ notify-send "$(todayweather)"
+ notify-send "🗺️ Doppler RADAR module" "\- Middle click for local Doppler RADAR
+- Shift + middle click to update RADAR location
+After $secs seconds, new clicks will also automatically update the doppler RADAR"
+ ;;
+6) getweatherreport && getweatherreportjson && notify-send "🌈 Updated forecast" ;;
+7) pickloc && getdoppler && showdoppler ;;
+8) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+checkforecast "$weatherreport" || { getweatherreport && pkill -RTMIN+15 "${STATUSBAR:-dwmblocks}" && sleep 3; }
+checkforecast "$weatherreportjson" || { getweatherreportjson && pkill -RTMIN+15 "${STATUSBAR:-dwmblocks}" && sleep 3; }
+showweather
diff --git a/ar/.local/bin/statusbar/sb-help-icon b/ar/.local/bin/statusbar/sb-help-icon
new file mode 100755
index 0000000..2717758
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-help-icon
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# The clickable help menu. Middle click to restart wm.
+
+# If dwm is running, use dwm's readme and restart.
+pidof dwm >/dev/null &&
+ READMEFILE=/usr/local/share/dwm/thesiah.mom
+restartwm() { pkill -HUP dwm; } ||
+ restartwm() { i3 restart; }
+
+case $BLOCK_BUTTON in
+1) groff -mom "${READMEFILE:-${XDG_DATA_HOME:-${HOME}/.local/share}/thesiah/thesiah.mom}" -Tpdf | zathura - ;;
+2) restartwm ;;
+3) notify-send "❓ Help module" "\- Left click to open THESIAH guide
+- Middle click to refresh window manager
+- Shift + Middle click to run system action
+- Shift + Right clict to lock screen" ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+7) sysact ;;
+8) slock ;;
+esac
+echo "❓"
diff --git a/ar/.local/bin/statusbar/sb-internet b/ar/.local/bin/statusbar/sb-internet
new file mode 100755
index 0000000..3d4ff4e
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-internet
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+# Show wifi 🛜 and percent strength or 📡 if none.
+# Show 🌐 if connected to ethernet or ❎ if none.
+# Show 🛰️ if a vpn connection is active
+
+case $BLOCK_BUTTON in
+1)
+ "$TERMINAL" -e nmtui
+ pkill -RTMIN+7 dwmblocks
+ ;;
+3) notify-send "🌐 Internet module" "\- Left click to connect
+❌: wifi disabled
+📡: no wifi connection
+🛜: wifi connection with quality
+❎: no ethernet
+🌐: ethernet working
+🛰️: vpn is active
+" ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+# Wifi
+if [ "$(cat /sys/class/net/w*/operstate 2>/dev/null)" = 'up' ]; then
+ wifiicon="$(awk '/^\s*w/ { print "🛜" int($3 * 100 / 70) "%" }' /proc/net/wireless)"
+elif [ "$(cat /sys/class/net/w*/operstate 2>/dev/null)" = 'down' ]; then
+ [ "$(cat /sys/class/net/w*/flags 2>/dev/null)" = '0x1003' ] && wifiicon="📡" || wifiicon="❌"
+fi
+
+# Ethernet
+[ "$(cat /sys/class/net/e*/operstate 2>/dev/null)" = 'up' ] && ethericon="🌐" || ethericon="❎"
+
+# TUN
+[ -n "$(cat /sys/class/net/tun*/operstate 2>/dev/null)" ] && tunicon=" 🛰️"
+
+if [ "$(cat /sys/class/net/w*/operstate 2>/dev/null)" = 'up' ] && [ ! "$(cat /sys/class/net/e*/operstate 2>/dev/null)" = 'up' ]; then
+ printf "%s%s\n" "$wifiicon" "$tunicon"
+elif [ ! "$(cat /sys/class/net/w*/operstate 2>/dev/null)" = 'up' ] && [ "$(cat /sys/class/net/e*/operstate 2>/dev/null)" = 'up' ]; then
+ printf "%s%s\n" "$ethericon" "$tunicon"
+else
+ printf "%s%s%s\n" "$wifiicon" " $ethericon" "$tunicon"
+fi
diff --git a/ar/.local/bin/statusbar/sb-iplocate b/ar/.local/bin/statusbar/sb-iplocate
new file mode 100755
index 0000000..d84445e
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-iplocate
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+# Gets your public ip address checks which country you are in and
+# displays that information in the statusbar
+#
+# https://www.maketecheasier.com/ip-address-geolocation-lookups-linux/
+
+set -e
+
+ifinstalled "geoip"
+addr="$(geoiplookup "$(curl -sfm 1 ifconfig.me 2>/dev/null)")"
+name="${addr##*, }"
+flag="$(grep "flag: $name" "${XDG_DATA_HOME:-${HOME}/.local/share}/larbs/emoji")"
+flag="${flag%% *}"
+printf "%s %s\\n" "$flag" "$name"
diff --git a/ar/.local/bin/statusbar/sb-keyboard b/ar/.local/bin/statusbar/sb-keyboard
new file mode 100755
index 0000000..6329020
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-keyboard
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+# works on any init system
+# requirements: dmenu, xorg-setxkbmap
+kb="$(setxkbmap -query | grep -oP 'layout:\s*\K\w+')" || exit 1
+
+case $BLOCK_BUTTON in
+1) fcitx5-remote -t && kill -44 "$(pidof "${STATUSBAR:-dwmblocks}")" ;;
+2)
+ kb_choice="$(awk '/! layout/{flag=1; next} /! variant/{flag=0} flag {print $2, "- " $1}' /usr/share/X11/xkb/rules/base.lst | dmenu -l 15)"
+ [ -z "$kb_choice" ] && exit 0
+ kb="$(echo "$kb_choice" | awk '{print $3}')"
+ setxkbmap "$kb"
+ pkill -RTMIN+10 "${STATUSBAR:-dwmblocks}"
+ ;;
+3) notify-send "⌨️ Input Method module" "\- Shows current input method (defalt US)
+- Left click to switch language (EN/KO)
+- Middle click to change keyboard" ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+if [ "$kb" = "us" ] || [ "$kb" = "kr" ]; then
+ if [ "$(fcitx5-remote)" -eq 1 ]; then
+ echo "⌨️US"
+ elif [ "$(fcitx5-remote)" -eq 2 ]; then
+ case "$(fcitx5-remote -n)" in
+ *ko* | *Korean* | *hangul*) echo "⌨️KO" ;;
+ *) echo "⌨️$(setxkbmap -query | grep 'layout:' | sed 's/layout:\s*\(\S*\)/\1/g')" ;;
+ esac
+ else
+ echo "⌨️??"
+ fi
+else
+ echo "⌨️$kb"
+fi
diff --git a/ar/.local/bin/statusbar/sb-mailbox b/ar/.local/bin/statusbar/sb-mailbox
new file mode 100755
index 0000000..4496efc
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-mailbox
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+# Displays number of unread mail and an loading icon if updating.
+# When clicked, brings up `neomutt`.
+
+case $BLOCK_BUTTON in
+1)
+ setsid -w -f "$TERMINAL" -e neomutt
+ pkill -RTMIN+20 "${STATUSBAR:-dwmblocks}"
+ ;;
+2) setsid -f mw -Y >/dev/null ;;
+3) notify-send "📬 Mail module" "\- Shows unread mail
+- Shows 🔃 if syncing mail
+- Left click opens neomutt
+- Middle click syncs mail" ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+unread="$(find "${XDG_DATA_HOME:-${HOME}/.local/share}"/mail/*/[Ii][Nn][Bb][Oo][Xx]/new/* -type f | wc -l 2>/dev/null)"
+
+pidof mbsync >/dev/null 2>&1 && icon="🔃"
+
+[ "$unread" = "0" ] && [ "$icon" = "" ] || echo "📬$unread$icon"
diff --git a/ar/.local/bin/statusbar/sb-memory b/ar/.local/bin/statusbar/sb-memory
new file mode 100755
index 0000000..bc7085a
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-memory
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+case $BLOCK_BUTTON in
+1) notify-send "🐏 Memory hogs" "$(
+ ps axch -o cmd:15,%mem --sort=-%mem | head
+ echo
+ free --mebi | sed -n '2{p;q}' | awk '{printf ("👟%2.2fGB/%2.2fGB\n", ( $3 / 1024), ($2 / 1024))}'
+)" ;;
+2) setsid -f "$TERMINAL" -e htop ;;
+3) notify-send "🐏 Memory module" "\- Shows Memory used/total
+- Left click to show memory hogs
+- Middle click to open htop" ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+[ $(xrandr | grep "\*" | awk '{print $1}' | sed 's/x[0-9]*//g') -lt 1920 ] &&
+ free --mebi | sed -n '2{p;q}' | awk '{printf ("🐏%d%%\n", ($3/$2)*100+0.5 )}' ||
+ free --mebi | sed -n '2{p;q}' | awk '{printf ("🐏%dGB/%dGB", $3/1000+0.5,$2/1000+0.5)}'
diff --git a/ar/.local/bin/statusbar/sb-mpdup b/ar/.local/bin/statusbar/sb-mpdup
new file mode 100755
index 0000000..90e9c7e
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-mpdup
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+# This loop will update the mpd statusbar module whenever a command changes the
+# music player's status. mpd must be running on X's start for this to work.
+
+while :; do
+ mpc idle >/dev/null && kill -57 "$(pidof "${STATUSBAR:-dwmblocks}")" || break
+done
diff --git a/ar/.local/bin/statusbar/sb-music b/ar/.local/bin/statusbar/sb-music
new file mode 100755
index 0000000..e47fb06
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-music
@@ -0,0 +1,78 @@
+#!/bin/bash
+
+truncate_string() {
+ local input="$1"
+ local max_length="$2"
+ if [ "${#input}" -gt "$max_length" ]; then
+ echo "${input:0:$((max_length - 2))}.."
+ else
+ echo "$input"
+ fi
+}
+
+filter() {
+ if ps -C mpd >/dev/null 2>&1; then
+ screen_width=$(xrandr | grep '\*' | awk '{print $1}' | cut -d'x' -f1)
+ artist=$(mpc current -f %artist%)
+ title=$(mpc current -f %title%)
+
+ if [ "$screen_width" -le 2048 ]; then
+ max_length=$((screen_width / 100))
+ artist=$(truncate_string "$artist" "$max_length")
+ title=$(truncate_string "$title" "$max_length")
+ else
+ artist="$(mpc current -f %artist%)"
+ title="$(mpc current -f %title%)"
+ fi
+
+ case "$(mpc status %state%)" in
+ "playing") prefix="🎵" ;;
+ "paused") prefix="⏸" ;;
+ *) return ;;
+ esac
+
+ indicators=""
+ [ "$(mpc status %single%)" = "on" ] && indicators="${indicators}🔀"
+ [ "$(mpc status %random%)" = "on" ] && indicators="${indicators}🔂"
+ [ "$(mpc status %repeat%)" = "on" ] && indicators="${indicators}🔁"
+
+ sig=$(grep "${0##*/}" "${XDG_SOURCES_HOME:-${HOME}/.local/src}/suckless/dwmblocks/config.h" | awk -F',' '{print $3}')
+ case $sig in
+ *0*)
+ echo "$prefix$artist - $title$([ -n "$indicators" ] && echo "$indicators")"
+ ;;
+ *1*)
+ echo "$prefix$artist - $title $(mpc status %currenttime%)/$(mpc status %totaltime%)$([ -n "$indicators" ] && echo "$indicators")"
+ ;;
+ esac
+ fi
+}
+
+[ "$(grep "${0##*/}" "${XDG_SOURCES_HOME:-${HOME}/.local/src}/suckless/dwmblocks/config.h" | awk -F',' '{print $3}')" -eq 0 ] && {
+ pidof -x sb-mpdup >/dev/null 2>&1 || sb-mpdup >/dev/null 2>&1 &
+}
+
+# Handling interaction based on button press
+case $BLOCK_BUTTON in
+1) # left click, opens ncmpcpp
+ mpc status | filter
+ setsid -f "$TERMINAL" -e ncmpcpp
+ ;;
+2) mpc toggle | filter ;; # middle click, pause/unpause
+3) # right click
+ notify-send "🎵 Music module" "\- Shows mpd song playing and status
+- 🎵 if playing
+- ⏸ if paused
+- 🔂 if single on
+- 🔁 if repeat on
+- 🔀 if random on
+- Left click opens ncmpcpp
+- Middle click pauses/unpause
+- Scroll changes track"
+ notify-send "🎵 $(mpc current)" "⏭️ $(mpc queued)"
+ ;;
+4) mpc prev | filter ;; # scroll up, previous
+5) mpc next | filter ;; # scroll down, next
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+*) mpc status | filter ;; # default, show current status
+esac
diff --git a/ar/.local/bin/statusbar/sb-nettraf b/ar/.local/bin/statusbar/sb-nettraf
new file mode 100755
index 0000000..7bba320
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-nettraf
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# Module showing network traffic. Shows how much data has been received (RX) or
+# transmitted (TX) since the previous time this script ran. So if run every
+# second, gives network traffic per second.
+
+case $BLOCK_BUTTON in
+1) setsid -f "$TERMINAL" -e bmon ;;
+3) notify-send "🌐 Network traffic module" "🔻: Traffic received
+🔺: Traffic transmitted" ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+update() {
+ sum=0
+ for arg; do
+ read -r i <"$arg"
+ sum=$((sum + i))
+ done
+ cache=/tmp/${1##*/}
+ [ -f "$cache" ] && read -r old <"$cache" || old=0
+ printf %d\\n "$sum" >"$cache"
+ printf %d\\n $((sum - old))
+}
+
+rx=$(update /sys/class/net/[ew]*/statistics/rx_bytes)
+tx=$(update /sys/class/net/[ew]*/statistics/tx_bytes)
+
+printf "🔻%4sB 🔺%4sB\\n" $(numfmt --to=iec $rx $tx)
diff --git a/ar/.local/bin/statusbar/sb-news b/ar/.local/bin/statusbar/sb-news
new file mode 100755
index 0000000..8bbbbf1
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-news
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+# Displays number of unread news items and an loading icon if updating.
+# When clicked, brings up `newsboat`.
+
+case $BLOCK_BUTTON in
+1) setsid "$TERMINAL" -e newsboat ;;
+2) setsid -f newsup >/dev/null && exit ;;
+3) notify-send "📰 News module" "\- Shows unread news items
+- Shows 🔃 if updating with \`newsup\`
+- Left click opens newsboat
+- Middle click syncs RSS feeds
+<b>Note:</b> Only one instance of newsboat (including updates) may be running at a time" ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+cat /tmp/newsupdate 2>/dev/null || echo "$(newsboat -x print-unread | awk '{ if($1>0) print "📰" $1}')$(cat "${XDG_CONFIG_HOME:-${HOME}/.config}"/newsboat/.update 2>/dev/null)"
diff --git a/ar/.local/bin/statusbar/sb-packages b/ar/.local/bin/statusbar/sb-packages
new file mode 100755
index 0000000..5955c75
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-packages
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+# Displays number of upgradeable packages.
+# For this to work, have a `pacman -Sy` command run in the background as a
+# cronjob every so often as root. This script will then read those packages.
+# When clicked, it will run an upgrade via pacman.
+#
+# Add the following text as a file in /usr/share/libalpm/hooks/statusbar.hook:
+#
+# [Trigger]
+# Operation = Upgrade
+# Type = Package
+# Target = *
+#
+# [Action]
+# Description = Updating statusbar...
+# When = PostTransaction
+# Exec = /usr/bin/pkill -RTMIN+16 dwmblocks # Or i3blocks if using i3.
+
+case $BLOCK_BUTTON in
+1) setsid -f "$TERMINAL" -e sb-popupgrade && remaps ;;
+2) notify-send "$(/usr/bin/pacman -Qu)" "$(/usr/bin/yay -Qu --aur)" ;;
+3) notify-send "🎁 Upgrade module" "📦: number of upgradable 'pacman' packages
+🧰: number of upgradable 'yay' packages
+- Left click to upgrade all packages
+- Middle click to show upgradable packages" ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+pacnum=$(pacman -Qu | grep -Fcv "[ignored]" | sed "s/^/📦/;s/^📦0$//g")
+yaynum=$(yay -Qu --aur | grep -Fcv "[ignored]" | sed "s/^/🧰/;s/^🧰0$//g")
+upgradable=""
+[ -n "$pacnum" ] && upgradable="${upgradable}${pacnum} "
+[ -n "$yaynum" ] && upgradable="${upgradable}${yaynum} "
+upgradable=$(echo "$upgradable" | sed 's/ *$//')
+
+printf "%s\n" "$upgradable"
diff --git a/ar/.local/bin/statusbar/sb-popupgrade b/ar/.local/bin/statusbar/sb-popupgrade
new file mode 100755
index 0000000..14036eb
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-popupgrade
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+printf "Beginning upgrade\\n"
+
+yay -Syu
+pkill -RTMIN+16 "${STATUSBAR:-dwmblocks}"
+
+printf "\\nUpgrade complete.\\nPress <Enter> to exit window.\\n\\n"
+read -r _
diff --git a/ar/.local/bin/statusbar/sb-price b/ar/.local/bin/statusbar/sb-price
new file mode 100755
index 0000000..46731c2
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-price
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+# Usage:
+# price <currency-base currency> <name of currency> <icon> <signal>
+# price bat-btc "Basic Attention Token" 🦁 24
+# This will give the price of BAT denominated in BTC and will update on
+# signal 24.
+# When the name of the currency is multi-word, put it in quotes.
+
+[ -z "$1" ] && exit 1
+
+url="${CRYPTOURL:-rate.sx}"
+target="${1%%-*}"
+denom="${1##*-}"
+name="${2:-$1}"
+icon="${3:-💰}"
+case "$denom" in
+"$target" | usd)
+ denom="usd"
+ symb="$"
+ ;;
+gbp) symb="£" ;;
+eur) symb="€" ;;
+btc) symb="" ;;
+esac
+interval="@1d" # History contained in chart preceded by '@' (7d = 7 days)
+dir="${XDG_CACHE_HOME:-${HOME}/.cache}/crypto-prices"
+pricefile="$dir/$target-$denom"
+chartfile="$dir/$target-$denom-chart"
+filestat="$(stat -c %x "$pricefile" 2>/dev/null)"
+
+[ -d "$dir" ] || mkdir -p "$dir"
+
+updateprice() { curl -sf \
+ --fail-early "${denom}.${url}/1${target}" "${denom}.${url}/${target}${interval}" \
+ --output "$pricefile" --output "$chartfile" ||
+ rm -f "$pricefile" "$chartfile"; }
+
+[ "${filestat%% *}" != "$(date '+%Y-%m-%d')" ] &&
+ updateme="1"
+
+case $BLOCK_BUTTON in
+1) setsid "$TERMINAL" -e less -Srf "$chartfile" ;;
+2)
+ notify-send -u low "$icon Updating..." "Updating $name price..."
+ updateme="1"
+ showupdate="1"
+ ;;
+3)
+ uptime="$(date -d "$filestat" '+%D at %T' | sed "s|$(date '+%D')|Today|")"
+ notify-send "$icon $name module" "\- <b>Exact price: \$$(cat "$pricefile")</b>
+- Left click for chart of changes
+- Middle click to update
+- Shows 🔃 if updating prices
+- <b>Last updated:
+$uptime</b>"
+ ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+[ -n "$updateme" ] &&
+ updateprice "$target" &&
+ [ -n "$showupdate" ] &&
+ notify-send "$icon Update complete" "$name price is now
+\$$(cat "$pricefile")"
+
+[ -f "$pricefile" ] && printf "%s%s%0.2f" "$icon" "$symb" "$(cat "$pricefile")"
diff --git a/ar/.local/bin/statusbar/sb-queues b/ar/.local/bin/statusbar/sb-queues
new file mode 100755
index 0000000..7cd48a7
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-queues
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+notify_filenames() {
+ while IFS= read -r line; do
+ id=$(echo "$line" | awk '{print $1}')
+ url=$(tsp -l | awk -v id="$id" 'flag && /notify-send/ {print $0; flag=0} $1 == id {flag=1}' | grep -o 'https://[^\"]*')
+ if [ -n "$url" ]; then
+ decoded_url=$(echo "$url" | sed 's/&amp;/\&/g')
+ yt-dlp --no-playlist --simulate --get-filename "$decoded_url" 2>/dev/null | while IFS= read -r filename; do
+ notify-send "🔽 Downloading:" "$filename"
+ done
+ else
+ notify-send "🪹 No URL extracted for task $id"
+ fi
+ done <<EOF
+$(tsp -l | awk '/running/ && /yt-dlp/')
+EOF
+ if [ -z "$url" ]; then
+ notify-send "💤 No active yt-dlp downloads"
+ fi
+ pkill -RTMIN+21 "${STATUSBAR:-dwmblocks}"
+}
+
+# This block displays the number of running and queued background tasks. Requires tsp.
+num=$(tsp -l | awk -v numr=0 -v numq=0 '{if (!/notify-send/ && /running/) numr++; if (!/notify-send/ && /queued/) numq++} END{print numr"|"numq}')
+
+# Handle mouse clicks
+case $BLOCK_BUTTON in
+1) setsid -f "$TERMINAL" -e sh -c 'tsp -l; echo "\nPress enter to close..." ; read REPLY' ;;
+2) setsid -f "$TERMINAL" -e sh -c 'tsp -t' ;;
+3)
+ notify_filenames
+ notify-send "📝 Tasks module" "🤖: number of running/queued background tasks
+- Left click to show all tasks
+- Middle click to show the current task progress"
+ ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+cat /tmp/qplaylist 2>/dev/null || ([ "$num" != "0|0" ] && echo "🤖$num")
diff --git a/ar/.local/bin/statusbar/sb-repos b/ar/.local/bin/statusbar/sb-repos
new file mode 100755
index 0000000..1dad9f1
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-repos
@@ -0,0 +1,130 @@
+#!/bin/bash
+
+pidof transmission-daemon >/dev/null && exit 1
+
+# Directories containing Git repositories
+DOTFILES_REPOS="$HOME/.dotfiles"
+SUCKLESS_REPOS="$HOME/.local/src/suckless"
+PRIVATE_REPOS="$HOME/Private/repos"
+PUBLIC_REPOS="$HOME/Public/repos"
+
+# Icon indicators
+DOTFILES_ICON="⚙️"
+SUCKLESS_ICON="🛠"
+PRIVATE_ICON="🏠"
+PUBLIC_ICON="🏢"
+
+# Function to parse Git status and format symbols
+get_git_status_symbols() {
+ git status --porcelain | awk '
+ {
+ if ($1 == "??") {
+ changes["?"]++
+ } else if ($1 ~ /^[MADR]$/) {
+ changes[$1]++
+ }
+ }
+ END {
+ for (change in changes) {
+ printf "%s%s", change, changes[change]
+ }
+ }'
+}
+
+# Function to check for unpushed commits
+get_unpushed_commits() { git cherry -v 2>/dev/null | wc -l; }
+
+# Function to check for unpulled commits
+get_unpulled_commits() { git rev-list --count HEAD..@{upstream} 2>/dev/null; }
+
+# Function to check Git repository status for multiple repos
+check_multi_repo_status() {
+ local dir="$1"
+ local icon="$2"
+ local status=""
+ local changed_repos=""
+
+ while IFS= read -r git_dir; do
+ local repo_dir="${git_dir%/.git}"
+ cd "$repo_dir" || continue
+
+ changes=$(get_git_status_symbols)
+ unpushed=$(get_unpushed_commits)
+ unpulled=$(get_unpulled_commits)
+
+ if [ -n "$changes" ] || [ "$unpushed" -gt 0 ] || [ "$unpulled" -gt 0 ]; then
+ status+="$icon$changes"
+ [ "$unpushed" -gt 0 ] && status+="↑$unpushed"
+ [ "$unpulled" -gt 0 ] && status+="↓$unpulled"
+ status+=" "
+ changed_repos+="$repo_dir"
+ fi
+ done < <(find "$dir" -mindepth 2 -maxdepth 2 -type d -name ".git" 2>/dev/null)
+
+ printf "%s%s" "$status" "$changed_repos"
+}
+
+# Function to check Git repository status for a single repository
+check_single_repo_status() {
+ local dir="$1"
+ local icon="$2"
+ local repo_status=""
+ local changed_repo=""
+
+ if [ -d "$dir/.git" ]; then
+ cd "$dir" || return
+
+ changes=$(get_git_status_symbols)
+ unpushed=$(get_unpushed_commits)
+ unpulled=$(get_unpulled_commits)
+
+ if [ -n "$changes" ] || [ "$unpushed" -gt 0 ] || [ "$unpulled" -gt 0 ]; then
+ repo_status+="$icon$changes"
+ [ "$unpushed" -gt 0 ] && repo_status+="↑$unpushed"
+ [ "$unpulled" -gt 0 ] && repo_status+="↓$unpulled"
+ repo_status+=" "
+ changed_repo="$dir"
+ fi
+ fi
+
+ printf "%s%s" "$repo_status" "$changed_repo"
+}
+
+# Check statuses for repositories
+dotfiles_status=$(check_single_repo_status "$DOTFILES_REPOS" "$DOTFILES_ICON" | awk -F' ' '{print $1}')
+dotfiles_changes=$(check_single_repo_status "$DOTFILES_REPOS" "$DOTFILES_ICON" | awk -F' ' '{print $2}')
+
+suckless_status=$(check_single_repo_status "$SUCKLESS_REPOS" "$SUCKLESS_ICON" | awk -F' ' '{print $1}')
+suckless_changes=$(check_single_repo_status "$SUCKLESS_REPOS" "$SUCKLESS_ICON" | awk -F' ' '{print $2}')
+
+private_status=$(check_multi_repo_status "$PRIVATE_REPOS" "$PRIVATE_ICON" | awk -F' ' '{print $1}')
+private_changes=$(check_multi_repo_status "$PRIVATE_REPOS" "$PRIVATE_ICON" | awk -F' ' '{print $2}')
+
+public_status=$(check_multi_repo_status "$PUBLIC_REPOS" "$PUBLIC_ICON" | awk -F' ' '{print $1}')
+public_changes=$(check_multi_repo_status "$PUBLIC_REPOS" "$PUBLIC_ICON" | awk -F' ' '{print $1}')
+
+[ -f /tmp/gitsync ] && rm -f /tmp/gitsync
+
+# Combine statuses
+output=""
+[ -n "$dotfiles_status" ] && output+="$dotfiles_status "
+[ -n "$suckless_status" ] && output+="$suckless_status "
+[ -n "$private_status" ] && output+="$private_status "
+[ -n "$public_status" ] && output+="$public_status "
+
+# Trim trailing spaces and display output
+output="${output%"${output##*[! ]}"}"
+[ -n "$output" ] && (cat /tmp/gitsync 2>/dev/null || echo "$output")
+
+openrepos() {
+ all_changed_repos="$dotfiles_changes"$'\n'"$suckless_changes"$'\n'"$private_changes"$'\n'"$public_changes"
+ [ -n "$all_changed_repos" ] && exec "$TERMINAL" -e opensessions "$(echo "$all_changed_repos" | grep -v '^$')"
+}
+
+# Handle button actions
+case "$BLOCK_BUTTON" in
+1) openrepos ;;
+3) notify-send " Git module" "\- Shows git repositories changes
+- Left click opens changed repositories" ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;; # Launch editor for the script
+esac
diff --git a/ar/.local/bin/statusbar/sb-tasks b/ar/.local/bin/statusbar/sb-tasks
new file mode 100755
index 0000000..66be81b
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-tasks
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+# Get the current time and the time in one hour
+now=$(date +%s)
+in_one_hour=$(date -d "+1 hour" +%s)
+
+# Check for tasks due in the next hour
+tasks_due_today=$(task due:tomorrow _ids)
+tasks_due_count=0
+tasks_due_list=""
+
+for task_id in $tasks_due_today; do
+ task_due_date=$(task _get "$task_id".due)
+ task_due_epoch=$(date -d "$task_due_date" +%s)
+ task_description=$(task _get "$task_id".description)
+
+ # Check if the task is due within the next hour
+ if [ "$task_due_epoch" -gt "$now" ] && [ "$task_due_epoch" -le "$in_one_hour" ]; then
+ tasks_due_list="$tasks_due_list- $task_description\n"
+ tasks_due_count=$((tasks_due_count + 1))
+ fi
+done
+
+# Check for overdue tasks (tasks with due date in the past)
+overdue_tasks=$(task +OVERDUE _ids)
+overdue_count=0
+overdue_list=""
+
+for task_id in $overdue_tasks; do
+ task_description=$(task _get "$task_id".description)
+ overdue_list="$overdue_list- $task_description\n"
+ overdue_count=$((overdue_count + 1))
+done
+
+# Check for follow-up tasks
+follow_up_tasks=$(task follow.is:Y _ids -PARENT)
+follow_up_count=0
+follow_up_list=""
+
+for task_id in $follow_up_tasks; do
+ task_due_date=$(task _get "$task_id".due)
+ task_due_epoch=$(date -d "$task_due_date" +%s)
+ task_description=$(task _get "$task_id".description)
+
+ # Ensure that follow-up tasks are only shown if they are not overdue
+ if [ "$task_due_epoch" -ge "$now" ]; then
+ follow_up_list="$follow_up_list- $task_description\n"
+ follow_up_count=$((follow_up_count + 1))
+ fi
+done
+
+check_task_sync() {
+ if [ "$(task _get tw.syncneeded)" -eq 1 ]; then
+ tasks-sync
+ notify-send "📚 Tasks synced"
+ fi
+}
+
+# Handle mouse clicks
+case $BLOCK_BUTTON in
+1) # Combine actions for button 1 and button 2
+ notify-send "📚 Follow-up task(s) to complete:" "$(printf "%b" "$follow_up_list")
+📕 Tasks due in the next hour:
+$(printf "%b" "$tasks_due_list")
+⏰ Overdue Tasks:
+$(printf "%b" "$overdue_list")"
+ ;;
+2) check_task_sync ;;
+3)
+ notify-send "🗂️ Task Module" "Shows task counts.
+- Left click: Show tasks due soon.
+- Middle click: Show follow-up tasks."
+ ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+# Output task information to dwmblocks
+output=""
+
+[ "$overdue_count" -gt 0 ] && output="${output}⏰$overdue_count "
+[ "$follow_up_count" -gt 0 ] && output="${output}📚$follow_up_count "
+[ "$tasks_due_count" -gt 0 ] && output="${output}📕$tasks_due_count " && notify-send -u critical "🚑 Tasks Remained!" "$tasks_due_list"
+[ "$(task _get tw.syncneeded)" -eq 1 ] && output="${output}📑 "
+echo "${output%* }"
diff --git a/ar/.local/bin/statusbar/sb-torrent b/ar/.local/bin/statusbar/sb-torrent
new file mode 100755
index 0000000..79da88d
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-torrent
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+status=$(transmission-remote -l | grep % |
+ sed " # The letters are for sorting and will not appear
+ s/.*Stopped.*/A 🛑/;
+ s/.*Seeding.*/Z 🌱/;
+ s/.*100%.*/N ✅/;
+ s/.*Idle.*/B 🕰️/;
+ s/.*Uploading.*/L 🔼/;
+ s/.*%.*/M 🔽/" |
+ sort -h | uniq -c | awk '{print $3 $1}' | paste -sd ' ' -)
+
+if [ -z "$status" ]; then
+ echo "$status"
+else
+ pidof transmission-daemon >/dev/null && echo "🌲"
+fi
+
+case $BLOCK_BUTTON in
+1) setsid -f "$TERMINAL" -e stig ;;
+2) td-toggle ;;
+3) notify-send "🌱 Torrent module" "\- Left click to open stig
+- Middle click to toggle transmission
+- Shift click to edit script
+Module shows number of torrents:
+🛑: paused
+🕰: idle (seeds needed)
+🔼: uploading (unfinished)
+🔽: downloading
+✅: done
+🌱: done and seeding" ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
diff --git a/ar/.local/bin/statusbar/sb-volume b/ar/.local/bin/statusbar/sb-volume
new file mode 100755
index 0000000..800f19e
--- /dev/null
+++ b/ar/.local/bin/statusbar/sb-volume
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+# Prints the current volume or 🔇 if muted.
+
+case $BLOCK_BUTTON in
+1)
+ setsid -w -f "$TERMINAL" -e pulsemixer
+ pkill -RTMIN+5 "${STATUSBAR:-dwmblocks}"
+ ;;
+2) wpctl set-mute @DEFAULT_SINK@ toggle ;;
+4) wpctl set-volume @DEFAULT_SINK@ 1%+ ;;
+5) wpctl set-volume @DEFAULT_SINK@ 1%- ;;
+3) notify-send "📢 Volume module" "\- Shows volume 🔊, 🔇 if muted
+- Middle click to mute
+- Scroll to change" ;;
+6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+vol="$(wpctl get-volume @DEFAULT_AUDIO_SINK@)"
+
+# If muted, print 🔇 and exit.
+[ "$vol" != "${vol%\[MUTED\]}" ] && echo 🔇 && exit
+
+vol="${vol#Volume: }"
+
+split() {
+ # For ommiting the . without calling and external program.
+ IFS=$2
+ set -- $1
+ printf '%s' "$@"
+}
+
+vol="$(printf "%.0f" "$(split "$vol" ".")")"
+
+case 1 in
+$((vol > 100))) icon="📢" ;;
+$((vol >= 70))) icon="🔊" ;;
+$((vol >= 30))) icon="🔉" ;;
+$((vol >= 1))) icon="🔈" ;;
+*) echo 🔇 && exit ;;
+esac
+
+echo "$icon$vol%"
diff --git a/ar/.local/bin/stw b/ar/.local/bin/stw
new file mode 100755
index 0000000..2e221d1
--- /dev/null
+++ b/ar/.local/bin/stw
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+# Directory where your stow packages are located, adjust as necessary
+stowdir="${XDG_DOTFILES_DIR:-${HOME}/.dotfiles}"
+
+# Function to list and select stow packages using dmenu
+select_stow_package() {
+ find "$stowdir" -mindepth 1 -maxdepth 1 -type d -not -name ".*" -not -name "default" | while read -r dir; do
+ if [ -n "$(find "$dir" -mindepth 1 -maxdepth 1)" ]; then
+ basename "$dir"
+ fi
+ done | dmenu -i -p "Select package to stow: "
+}
+
+# Function to ask user for resolution strategy using dmenu
+ask_resolution_strategy() {
+ printf "delete\nmove" | dmenu -i -p "Choose resolution strategy: "
+}
+
+# Function to stow a package and resolve conflicts
+stow_package() {
+ target="$1"
+ resolve_strategy="$2"
+ # Attempt to stow the package
+ output=$(stow --no-folding -S "$target" 2>&1)
+ status=$?
+
+ # Handle conflicts based on resolution strategy
+ if [ $status -ne 0 ]; then
+ echo "$output" | grep "over existing target is stowed to a different package" | while IFS= read -r line; do
+ conflict_path=$(echo "$line" | sed -E 's/.*\: (.*) =>.*/\1/')
+ full_path="$HOME/$conflict_path"
+ if [ "$resolve_strategy" = "delete" ]; then
+ rm -rf "$full_path"
+ elif [ "$resolve_strategy" = "move" ]; then
+ mv "$full_path" "${full_path}.dotbak"
+ fi
+ done
+ echo "$output" | grep "over existing target" | while IFS= read -r line; do
+ conflict_path=$(echo "$line" | sed -E 's/.*over existing target\s(.*)\ssince.*/\1/')
+ full_path="$HOME/$conflict_path"
+ if [ "$resolve_strategy" = "delete" ]; then
+ rm -rf "$full_path"
+ elif [ "$resolve_strategy" = "move" ]; then
+ mv "$full_path" "${full_path}.dotbak"
+ fi
+ done
+
+ # Retry stowing after conflict resolution
+ output=$(stow --no-folding -S "$target" 2>&1)
+ status=$?
+ fi
+}
+
+mail_sync() {
+ [ -f "${XDG_CONFIG_HOME:-${HOME}/.config}/mutt/muttrc" ] && [ -f "${XDG_CONFIG_HOME:-${HOME}/.config}/mutt/thesiah.muttrc" ] &&
+ ! grep -qF "source /home/$USER/.config/mutt/thesiah.muttrc" "${XDG_CONFIG_HOME:-${HOME}/.config}/mutt/muttrc" &&
+ sed -i "/source \/usr\/share\/mutt-wizard\/mutt-wizard\.muttrc/a source \/home\/$USER\/.config\/mutt\/thesiah\.muttrc" /home/"$USER"/.config/mutt/muttrc
+}
+
+# Ensure running from the correct directory
+cd "$stowdir" || exit 1
+
+# Select a stow package
+targetdir=$(select_stow_package) || exit 1
+
+# Ask the user for the resolution strategy
+resolve=$(ask_resolution_strategy) || exit 1
+
+stow_package "$targetdir" "$resolve" && stow_package "default" "$resolve" || exit
+ln -sf "$stowdir/$targetdir/.config/shell/profile" "$HOME/.zprofile"
+
+# Link for bash
+ln -sf "$stowdir/$targetdir/.config/bash/bash_profile" "$HOME/.bash_profile"
+ln -sf "$stowdir/$targetdir/.config/bash/bashrc" "$HOME/.bashrc"
+
+# Link for cro# Link for gtk 2.0
+ln -sf "$stowdir/$targetdir/.config/gtk-2.0/gtkrc-2.0" "$HOME/.gtkrc-2.0"
+
+# Sync mail
+mail_sync
+
+# Reload shortcuts (assumes this functionality is defined elsewhere and works as expected)
+shortcuts >/dev/null 2>&1 || exit 1
+zsh -c "source '${XDG_CONFIG_HOME:-${HOME}/.config}/shell/shortcutrc'" 2>/dev/null || exit 1
+zsh -c "source '${XDG_CONFIG_HOME:-${HOME}/.config}/shell/zshnameddirrc'" 2>/dev/null || exit 1
+notify-send "✅ Updated shortcuts"
diff --git a/ar/.local/bin/syncdot b/ar/.local/bin/syncdot
new file mode 100755
index 0000000..5e560e9
--- /dev/null
+++ b/ar/.local/bin/syncdot
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+# Define ANSI color codes for better readability in script output
+GREEN='\033[1;32m'
+BLUE='\033[1;34m'
+RED='\033[1;31m'
+NC='\033[0m' # No Color
+
+# Ensure the script exits on any error
+set -e
+
+# Navigate to the dotfiles directory
+cd "${XDG_DOTFILES_DIR:-${HOME}/.dotfiles}" || exit
+
+echo "${BLUE}Stashing existing changes...${NC}"
+stash_result=$(git stash push -m "sync-dotfiles: Before syncing dotfiles" 2>&1)
+needs_pop=0
+if ! echo "$stash_result" | grep -q "No local changes to save"; then
+ needs_pop=1
+fi
+
+echo "${BLUE}Pulling updates from dotfiles repo...${NC}"
+git pull origin master
+
+if [ "$needs_pop" -eq 1 ]; then
+ echo "${BLUE}Popping stashed changes...${NC}"
+ git stash pop
+fi
+
+# Check for merge conflicts after attempting to pop the stash
+unmerged_files=$(git diff --name-only --diff-filter=U)
+if [ -n "$unmerged_files" ]; then
+ echo "${RED}The following files have merge conflicts after popping the stash:${NC}"
+ printf "%s\n" "$unmerged_files"
+ exit 1
+else
+ echo "${GREEN}No merge conflicts detected. Proceeding with dotfiles linkage...${NC}"
+ # Ensure GNU Stow is installed
+ if ! command -v stow >/dev/null 2>&1; then
+ echo "${RED}GNU Stow not found. Please install GNU Stow to continue.${NC}"
+ exit 1
+ fi
+ # Run stow to ensure all new dotfiles are linked, targeting directories named after the OS
+ stow -v --no-folding -S "$(whereami)" # Verbose output
+ echo "${GREEN}Dotfiles successfully linked.${NC}"
+fi
diff --git a/ar/.local/bin/synctime b/ar/.local/bin/synctime
new file mode 100755
index 0000000..47f4310
--- /dev/null
+++ b/ar/.local/bin/synctime
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+select_timezone() {
+ initial_path="/usr/share/zoneinfo"
+ tz_selection=$initial_path
+
+ while [ -d "$tz_selection" ]; do
+ selection=$(find "$tz_selection" -mindepth 1 -maxdepth 1 | sed "s|^$initial_path/||" | dmenu -i -l 20 -p "Select Time Zone:") || exit 1
+ tz_selection="$initial_path/$selection"
+ done
+
+ echo "${tz_selection#$initial_path/}"
+}
+
+current_zone=$(select_timezone) || exit 1
+sudo ln -sf /usr/share/zoneinfo/$current_zone /etc/localtime || exit 1
+sudo hwclock --systohc
+
+echo "Current timezone: $current_zone"
+date
+
+pkill -RTMIN+3 ${STATUSBAR:-dwmblocks}
diff --git a/ar/.local/bin/sysact b/ar/.local/bin/sysact
new file mode 100755
index 0000000..3706d2b
--- /dev/null
+++ b/ar/.local/bin/sysact
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+# A dmenu wrapper script for system functions.
+export WM="dwm"
+case "$(readlink -f /sbin/init)" in
+*systemd*) ctl='systemctl' ;;
+*) ctl='loginctl' ;;
+esac
+
+wmpid() { # This function is needed if there are multiple instances of the window manager.
+ tree="$(pstree -ps $$)"
+ tree="${tree#*$WM(}"
+ echo "${tree%%)*}"
+}
+
+case "$(printf "🔒 lock\n🚪 leave $WM\n♻️ renew $WM\n🐻 hibernate\n🔃 reboot\n🖥️shutdown\n💤 sleep\n📺 display off" | dmenu -i -p 'Action: ')" in
+'🔒 lock') slock ;;
+"🚪 leave $WM") kill -TERM "$(wmpid)" ;;
+"♻️ renew $WM") kill -HUP "$(wmpid)" ;;
+'🐻 hibernate') slock $ctl hibernate -i ;;
+'💤 sleep') slock $ctl suspend -i ;;
+'🔃 reboot') $ctl reboot -i ;;
+'🖥️shutdown') $ctl poweroff -i ;;
+'📺 display off') xset dpms force off ;;
+*) exit 1 ;;
+esac
diff --git a/ar/.local/bin/tablet b/ar/.local/bin/tablet
new file mode 100755
index 0000000..1b4e556
--- /dev/null
+++ b/ar/.local/bin/tablet
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+# Find the line in "xrandr -q --verbose" output that contains current screen orientation and "strip" out current orientation.
+rotation="$(xrandr -q --verbose | grep 'connected' | grep -Eo '\) (normal|left|inverted|right) \(' | grep -Eo '(normal|left|inverted|right)')"
+penstylus="$(xsetwacom list devices | grep 'Pen' | grep 'stylus' | sed 's/\s*id.*//g')"
+penerase="$(xsetwacom list devices | grep 'Pen' | grep 'erase' | sed 's/\s*id.*//g')"
+fingertouch="$(xsetwacom list devices | grep 'Finger' | grep 'touch' | sed 's/\s*id.*//g')"
+
+# Using current screen orientation proceed to rotate screen and input tools.
+case "$rotation" in
+normal)
+ # rotate to the left
+ xrandr -o left
+ xsetwacom set "$penstylus" rotate ccw
+ xsetwacom set "$penerase" rotate ccw
+ xsetwacom set "$fingertouch" rotate ccw
+ ;;
+left)
+ # rotate to normal
+ xrandr -o inverted
+ xsetwacom set "$penstylus" rotate half
+ xsetwacom set "$penerase" rotate half
+ xsetwacom set "$fingertouch" rotate half
+ ;;
+inverted)
+ # rotate to normal
+ xrandr -o right
+ xsetwacom set "$penstylus" rotate cw
+ xsetwacom set "$penerase" rotate cw
+ xsetwacom set "$fingertouch" rotate cw
+ ;;
+right)
+ # rotate to normal
+ xrandr -o normal
+ xsetwacom set "$penstylus" rotate none
+ xsetwacom set "$penerase" rotate none
+ xsetwacom set "$fingertouch" rotate none
+ ;;
+esac
diff --git a/ar/.local/bin/tag b/ar/.local/bin/tag
new file mode 100755
index 0000000..8abce0f
--- /dev/null
+++ b/ar/.local/bin/tag
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+err() { echo "Usage:
+ tag [OPTIONS] file
+Options:
+ -a: artist/author
+ -t: song/chapter title
+ -A: album/book title
+ -n: track/chapter number
+ -N: total number of tracks/chapters
+ -d: year of publication
+ -g: genre
+ -c: comment
+You will be prompted for title, artist, album and track if not given." && exit 1; }
+
+while getopts "a:t:A:n:N:d:g:c:" o; do case "${o}" in
+ a) artist="${OPTARG}" ;;
+ t) title="${OPTARG}" ;;
+ A) album="${OPTARG}" ;;
+ n) track="${OPTARG}" ;;
+ N) total="${OPTARG}" ;;
+ d) date="${OPTARG}" ;;
+ g) genre="${OPTARG}" ;;
+ c) comment="${OPTARG}" ;;
+ *) printf "Invalid option: -%s\\n" "$OPTARG" && err ;;
+ esac done
+
+shift $((OPTIND - 1))
+
+file="$1"
+
+temp="$(mktemp -p "$(dirname "$file")")"
+trap 'rm -f $temp' HUP INT QUIT TERM PWR EXIT
+
+[ ! -f "$file" ] && echo 'Provide file to tag.' && err
+
+[ -z "$title" ] && echo 'Enter a title.' && read -r title
+[ -z "$artist" ] && echo 'Enter an artist.' && read -r artist
+[ -z "$album" ] && echo 'Enter an album.' && read -r album
+[ -z "$track" ] && echo 'Enter a track number.' && read -r track
+
+cp -f "$file" "$temp" && ffmpeg -i "$temp" -map 0 -y -codec copy \
+ -metadata title="$title" \
+ -metadata album="$album" \
+ -metadata artist="$artist" \
+ -metadata track="${track}${total:+/"$total"}" \
+ ${date:+-metadata date="$date"} \
+ ${genre:+-metadata genre="$genre"} \
+ ${comment:+-metadata comment="$comment"} "$file"
diff --git a/ar/.local/bin/task/taskwarrior-tui/annotate-with-new-note b/ar/.local/bin/task/taskwarrior-tui/annotate-with-new-note
new file mode 100755
index 0000000..3c67d24
--- /dev/null
+++ b/ar/.local/bin/task/taskwarrior-tui/annotate-with-new-note
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+# UUID of the task to annotate
+uuid="$*"
+
+# Directory where notes are stored
+notes_dir="$HOME/Private/repos/Obsidian/SI/Notes"
+templates_dir="$HOME/Private/repos/Obsidian/SI/Resources/Templates"
+
+# Prompt for the new note name
+printf "Enter the name for the new note: "
+read -r new_note_name
+
+copy_note="$templates_dir/projects.md"
+filepath="$notes_dir/$new_note_name.md"
+
+# Check if a file with this name already exists
+if [ -f "$filepath" ]; then
+ echo "File with this name already exists. Annotating the task with the existing note."
+else
+ nvim -n -c "ObsidianNew $new_note_name" --headless >/dev/null 2>&1 &
+ if [ -f "$copy_note" ]; then
+ cp "$copy_note" "$filepath"
+ echo "New note created and opened in Neovim."
+ fi
+fi
+
+# Annotate the task with the filepath
+task_output=$(task rc.bulk=0 rc.confirmation=off "$uuid" annotate "$filepath")
+
+# Check if annotation was successful
+case "$task_output" in
+*"Annotated"*)
+ echo "Successfully annotated the task with the note."
+ ;;
+*)
+ echo "Failed to annotate the task."
+ ;;
+esac
+
+${EDITOR:-nvim} "$filepath"
diff --git a/ar/.local/bin/task/taskwarrior-tui/annotate-with-note b/ar/.local/bin/task/taskwarrior-tui/annotate-with-note
new file mode 100755
index 0000000..c744314
--- /dev/null
+++ b/ar/.local/bin/task/taskwarrior-tui/annotate-with-note
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+# UUID of the task to annotate
+uuid="$*"
+
+# Base directory where notes are stored
+notes_dir="$HOME/Private/repos/Obsidian/SI/Notes"
+
+# List of subdirectories to search in
+subdirs="areas meetings projects resources reviews"
+
+# Construct the find command with the subdirectories
+search_paths=""
+for subdir in $subdirs; do
+ search_paths="$search_paths $notes_dir/$subdir"
+done
+
+# Find files in the specified subdirectories and show fzf dialog to select an existing note
+filepath=$(find "$search_paths" -type f -name '*.md' | fzf --preview "bat --color=always {}")
+
+# If fzf was cancelled, exit the script
+if [ -z "$filepath" ]; then
+ echo "No file selected. Exiting."
+ exit 1
+fi
+
+# Annotate the task with the selected filepath
+task_output=$(task rc.bulk=0 rc.confirmation=off "$uuid" annotate "$filepath")
+
+# Check if annotation was successful
+case "$task_output" in
+*"Annotated"*)
+ echo "Successfully annotated the task with the note."
+ ;;
+*)
+ echo "Failed to annotate the task."
+ ;;
+esac
+
+# Open the selected note in nvim
+${EDITOR:-nvim} "$filepath"
diff --git a/ar/.local/bin/task/taskwarrior-tui/cycle-priority b/ar/.local/bin/task/taskwarrior-tui/cycle-priority
new file mode 100755
index 0000000..43bc91e
--- /dev/null
+++ b/ar/.local/bin/task/taskwarrior-tui/cycle-priority
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+uuid="$1"
+
+# Get the current priority of the task
+current_priority=$(task _get $uuid.priority)
+
+# Cycle through the priorities: H -> M -> L -> (unset) -> H
+case "$current_priority" in
+H)
+ new_priority="M"
+ echo "Switching priority from 'H' to 'M'"
+ ;;
+M)
+ new_priority="L"
+ echo "Switching priority from 'M' to 'L'"
+ ;;
+L)
+ new_priority=""
+ echo "Switching priority from 'L' to (no priority)"
+ ;;
+"")
+ new_priority="H"
+ echo "Switching priority from (no priority) to 'H'"
+ ;;
+esac
+
+# Update the task with the new priority value, or remove priority if it's an empty string
+if [ -n "$new_priority" ]; then
+ echo "Updating task $uuid with new priority: $new_priority"
+ task rc.bulk=0 rc.confirmation=off "$uuid" modify priority="$new_priority"
+else
+ echo "Removing priority from task $uuid"
+ task rc.bulk=0 rc.confirmation=off "$uuid" modify priority:
+fi
diff --git a/ar/.local/bin/task/taskwarrior-tui/cycle-tmux-projects b/ar/.local/bin/task/taskwarrior-tui/cycle-tmux-projects
new file mode 100755
index 0000000..360dc20
--- /dev/null
+++ b/ar/.local/bin/task/taskwarrior-tui/cycle-tmux-projects
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# Get the UUID of the selected task from the arguments
+uuid="$*"
+
+# Fetch the project of the task using Taskwarrior and jq
+project=$(task "$uuid" export | jq -r '.[0].project')
+
+# Check if the project is empty
+if [ -z "$project" ] || [ "$project" = "null" ]; then
+ echo "No project found for the selected task."
+ # Optionally, reset the filter in taskwarrior-tui
+ tmux send-keys "/" Escape
+ tmux send-keys Escape
+ tmux send-keys "/"
+ tmux send-keys Enter
+ exit 0
+fi
+
+# Escape any special characters in the project name for use in tmux command
+escaped_project=$(printf '%s' "$project" | sed 's/[][\.*^$(){}+?|]/\\&/g')
+
+# Send keys via tmux to apply the filter in taskwarrior-tui
+tmux send-keys "/"
+tmux send-keys Escape
+tmux send-keys Escape
+tmux send-keys "/"
+
+# Use exact match in the filter to avoid partial matches
+tmux send-keys "project.is:$escaped_project"
+tmux send-keys Enter
diff --git a/ar/.local/bin/task/taskwarrior-tui/decrease-priority b/ar/.local/bin/task/taskwarrior-tui/decrease-priority
new file mode 100755
index 0000000..b2b0508
--- /dev/null
+++ b/ar/.local/bin/task/taskwarrior-tui/decrease-priority
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+uuid="$1"
+current_priority=$(task _get "$uuid".manual_priority)
+
+# Check if manual_priority is set; if not, initialize to 0
+if [ -z "$current_priority" ]; then
+ current_priority=0
+fi
+
+# Decrement the priority using expr
+new_priority=$((current_priority - 1))
+
+# Update the task with the new manual_priority value
+task rc.bulk=0 rc.confirmation=off "$uuid" modify manual_priority="$new_priority"
diff --git a/ar/.local/bin/task/taskwarrior-tui/git-issue-sync b/ar/.local/bin/task/taskwarrior-tui/git-issue-sync
new file mode 100755
index 0000000..da01bc4
--- /dev/null
+++ b/ar/.local/bin/task/taskwarrior-tui/git-issue-sync
@@ -0,0 +1,185 @@
+#!/bin/sh
+
+# github_issue_sync.sh
+# Synchronizes GitHub and Linear issues with Taskwarrior
+
+# Set new line and tab for word splitting
+IFS='
+ '
+
+# Logger with timestamp
+log() {
+ echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*"
+}
+
+# Function to trim leading and trailing whitespace using sed
+trim_whitespace() {
+ input="$1"
+ echo "$input" | sed 's/^[ \t]*//;s/[ \t]*$//'
+}
+
+# Validate necessary environment variables
+validate_env_vars() {
+ oldIFS="$IFS"
+ IFS=' '
+ required_vars="api userid"
+ for var in $required_vars; do
+ val=$(pass show "api/linear/$var")
+ if [ -z "$val" ]; then
+ echo "Error: Environment variable $var is not set." >&2
+ exit 1
+ fi
+ done
+ IFS="$oldIFS"
+}
+
+# Retrieve and format GitHub issues
+get_github_issues() {
+ issues=$(gh api -X GET /search/issues \
+ -f q='is:issue is:open assignee:TheSiahxyz' \
+ --jq '.items[] | {id: .number, description: .title, repository: .repository_url, html_url: .html_url}') || {
+ echo "Error: Unable to fetch GitHub issues" >&2
+ return 1
+ }
+ echo "$issues"
+}
+
+# Retrieve and format Linear issues
+get_linear_issues() {
+ issues=$(curl -s -X POST \
+ -H "Content-Type: application/json" \
+ -H "Authorization: $LINEAR_API_KEY" \
+ --data '{"query": "query { user(id: \"'"$LINEAR_USER_ID"'\") { id name assignedIssues(filter: { state: { name: { nin: [\"Released\", \"Canceled\"] } } }) { nodes { id title url } } } }"}' \
+ https://api.linear.app/graphql | jq -c '.data.user.assignedIssues.nodes[] | {id: .id, description: .title, repository: "linear", html_url: .url}') || {
+ echo "Error: Unable to fetch Linear issues" >&2
+ return 1
+ }
+ echo "$issues"
+}
+
+# Synchronize a single issue with Taskwarrior
+sync_to_taskwarrior() {
+ issue_line="$1"
+ issue_id=$(echo "$issue_line" | jq -r '.id')
+ issue_description=$(echo "$issue_line" | jq -r '.description')
+ issue_repo_name=$(echo "$issue_line" | jq -r '.repository' | awk -F/ '{print $NF}')
+ issue_url=$(echo "$issue_line" | jq -r '.html_url')
+
+ log "Processing Issue ID: $issue_id, Description: $issue_description"
+
+ task_uuid=$(get_task_id_by_description "$issue_description")
+
+ if [ -z "$task_uuid" ]; then
+ log "Creating new task for issue: $issue_description"
+ task_uuid=$(create_task "$issue_description" "+$issue_repo_name" "project:$issue_repo_name")
+
+ if [ -n "$task_uuid" ]; then
+ annotate_task "$task_uuid" "$issue_url"
+ log "Task created and annotated for: $issue_description"
+ else
+ log "Error: Failed to create task for: $issue_description" >&2
+ fi
+ else
+ log "Task already exists for: $issue_description (UUID: $task_uuid)"
+ fi
+}
+
+# Mark a GitHub issue as completed in Taskwarrior
+sync_github_issue() {
+ task_description="$1"
+ task_uuid=$(get_task_id_by_description "$task_description")
+
+ if [ -n "$task_uuid" ]; then
+ mark_task_completed "$task_uuid"
+ log "Task marked as completed: $task_description (UUID: $task_uuid)"
+ else
+ log "Task UUID not found for: $task_description" >&2
+ fi
+}
+
+# Compare existing Taskwarrior tasks with current issues and mark as completed if not present
+compare_and_display_tasks_not_in_issues() {
+ existing_task_descriptions="$1"
+ issues_descriptions="$2"
+
+ log "Starting comparison of Taskwarrior tasks and current issues."
+
+ existing_task_descriptions_array=$(echo "$existing_task_descriptions" | tr '\n' ' ')
+ issues_descriptions_array=$(echo "$issues_descriptions" | tr '\n' ' ')
+
+ for task_description in $existing_task_descriptions_array; do
+ trimmed_task_description=$(trim_whitespace "$task_description")
+ issue_exists=false
+
+ for issue_description in $issues_descriptions_array; do
+ trimmed_issue_description=$(trim_whitespace "$issue_description")
+ if [ "$(echo "$trimmed_task_description" | tr '[:upper:]' '[:lower:]')" = "$(echo "$trimmed_issue_description" | tr '[:upper:]' '[:lower:]')" ]; then
+ issue_exists=true
+ break
+ fi
+ done
+
+ if [ "$issue_exists" = false ]; then
+ log "No matching issue found for task: $trimmed_task_description. Marking as completed."
+ sync_github_issue "$trimmed_task_description"
+ fi
+ done
+
+ log "Comparison of Taskwarrior tasks and issues completed."
+}
+
+# Retrieve existing Taskwarrior task descriptions with +github or +linear tags and pending status
+get_existing_task_descriptions() {
+ task +github or +linear status:pending export | jq -r '.[] | .description'
+}
+
+# Log retrieved issues
+log_issues() {
+ issue_type="$1"
+ issues="$2"
+ log "Retrieved $issue_type issues: $(echo "$issues" | jq '.')"
+}
+
+# Synchronize all issues to Taskwarrior
+sync_issues_to_taskwarrior() {
+ issues="$1"
+ echo "$issues" | jq -c '.' | while IFS= read -r line; do
+ sync_to_taskwarrior "$line"
+ done
+}
+
+# Main function to orchestrate the synchronization
+main() {
+ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
+ echo "Not a git repository."
+ exit 1
+ fi
+ validate_env_vars
+
+ github_issues=$(get_github_issues)
+ linear_issues=$(get_linear_issues)
+
+ if [ -z "$github_issues" ] && [ -z "$linear_issues" ]; then
+ log "No issues retrieved from GitHub or Linear. Exiting."
+ exit 0
+ fi
+
+ if [ -n "$github_issues" ]; then
+ log_issues "GitHub" "$github_issues"
+ sync_issues_to_taskwarrior "$github_issues"
+ fi
+
+ if [ -n "$linear_issues" ]; then
+ log_issues "Linear" "$linear_issues"
+ sync_issues_to_taskwarrior "$linear_issues"
+ fi
+
+ existing_task_descriptions=$(get_existing_task_descriptions)
+
+ compare_and_display_tasks_not_in_issues "$existing_task_descriptions" "$(
+ echo "$github_issues" | jq -r '.description'
+ echo "$linear_issues" | jq -r '.description'
+ )"
+}
+
+main
diff --git a/ar/.local/bin/task/taskwarrior-tui/increase-priority b/ar/.local/bin/task/taskwarrior-tui/increase-priority
new file mode 100755
index 0000000..26a3d53
--- /dev/null
+++ b/ar/.local/bin/task/taskwarrior-tui/increase-priority
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+# The set -e option instructs the shell to immediately exit if any command has a non-zero exit status
+# The set -u option treats unset variables (except for $* and $@) as an error
+set -eu
+
+echo "Increase duration of task $*"
+
+# Fetch the duration value using taskwarrior and jq
+dur=$(task "$@" export | jq -r '.[0].duration')
+
+# Check if the duration has hours
+case "$dur" in
+*H*)
+ # Extract both hour and minute parts
+ hour=$(echo "$dur" | awk -F 'T' '{split($2, a, "H"); print a[1]}')
+ minute=$(echo "$dur" | awk -F 'H' '{split($2, a, "M"); print a[1]}')
+
+ # Add 30 to the minutes
+ new_minute=$((minute + 30))
+
+ # Check if minutes are 60 or more
+ if [ "$new_minute" -ge 60 ]; then
+ # Add 1 to the hour
+ new_hour=$((hour + 1))
+
+ # Calculate new minutes
+ new_minute=$((new_minute - 60))
+ else
+ # Keep the original hour
+ new_hour=$hour
+ fi
+
+ # Create the new duration string
+ new_dur="PT${new_hour}H${new_minute}M"
+ ;;
+*)
+ # If duration is already 30M
+ if [ "$dur" = "PT30M" ]; then
+ # Make it 1H
+ new_dur="PT1H"
+ else
+ # Extract the minute part from the duration string
+ minute=$(echo "$dur" | awk -F 'T' '{split($2, a, "M"); print a[1]}')
+
+ # Add 30 to the minutes
+ new_minute=$((minute + 30))
+
+ # Create the new duration string
+ new_dur="PT${new_minute}M"
+ fi
+ ;;
+esac
+
+# Print the new duration
+echo "Original duration: $dur"
+echo "New duration: $new_dur"
+
+# Modify the task duration using taskwarrior
+task rc.bulk=0 rc.confirmation=off rc.dependency.confirmation=off rc.recurrence.confirmation=off "$@" modify duration="$new_dur"
diff --git a/ar/.local/bin/task/taskwarrior-tui/lib-task-interop b/ar/.local/bin/task/taskwarrior-tui/lib-task-interop
new file mode 100755
index 0000000..04e60ac
--- /dev/null
+++ b/ar/.local/bin/task/taskwarrior-tui/lib-task-interop
@@ -0,0 +1,82 @@
+#!/bin/sh
+
+# Interop functions for Taskwarrior
+
+# Create a new task in Taskwarrior with a given description and optional additional attributes.
+# Properly handle special characters in the description and other arguments.
+create_task() {
+ description="$1"
+ shift # Now $@ contains the rest of the arguments
+
+ # Use a variable to hold arguments to prevent word splitting
+ task_args=""
+ for arg in "$@"; do
+ task_args="$task_args $arg"
+ done
+
+ # Use -- to indicate the end of options, and pass the description safely
+ output=$(task add $task_args -- "$description")
+
+ # Extract the UUID from the output using a reliable method
+ task_uuid=$(echo "$output" | grep -o 'Created task [a-z0-9\-]*' | awk '{print $3}')
+
+ echo "$task_uuid"
+}
+
+# Get task UUID from description with specific tags (+github or +linear)
+get_task_id_by_description() {
+ description="$1"
+ # Use task export with tags +github or +linear and status:pending to find the task by description
+ task +github or +linear status:pending export | jq -r --arg desc "$description" '.[] | select(.description == $desc) | .uuid'
+}
+
+# Annotate an existing task
+annotate_task() {
+ task_uuid="$1"
+ annotation="$2"
+ task "$task_uuid" annotate -- "$annotation"
+}
+
+# Mark a task as completed
+mark_task_completed() {
+ task_uuid="$1"
+ echo "Attempting to mark task $task_uuid as completed..."
+ task "$task_uuid" done || {
+ echo "Failed to mark task $task_uuid as completed" >&2
+ exit 1
+ }
+}
+
+# Mark a task as pending
+mark_task_pending() {
+ task_uuid="$1"
+ task "$task_uuid" modify status:pending
+}
+
+# Get task labels (tags)
+get_task_labels() {
+ task_uuid="$1"
+ echo "Getting labels for task $task_uuid"
+ task _get "$task_uuid".tags
+}
+
+# Add a label (tag) to a task
+add_task_label() {
+ task_uuid="$1"
+ label="$2"
+ task "$task_uuid" modify +"$label"
+}
+
+# Remove a label (tag) from a task
+remove_task_label() {
+ task_uuid="$1"
+ label="$2"
+ task "$task_uuid" modify -"$label"
+}
+
+# Set or change a task's project
+change_task_project() {
+ task_uuid="$1"
+ project_name="$2"
+ task "$task_uuid" modify project:"$project_name"
+}
diff --git a/ar/.local/bin/task/taskwarrior-tui/task-switch-context b/ar/.local/bin/task/taskwarrior-tui/task-switch-context
new file mode 100755
index 0000000..0b40141
--- /dev/null
+++ b/ar/.local/bin/task/taskwarrior-tui/task-switch-context
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+switch_context() {
+ task context "$1"
+ echo "Switched to $1 context."
+}
+
+# Get the current task context
+current_context=$(task _get rc.context)
+
+# Check if the current context is "work"
+if [ "$current_context" = "work" ]; then
+ switch_context home
+ # Set the marker file to indicate we should switch back to work next time
+elif [ "$current_context" = "home" ]; then
+ # If we're in home and the marker file exists, it means we want to switch back to work
+ switch_context work
+ # Remove the marker file after switching back to avoid repeated switches
+elif [ -z "$current_context" ]; then
+ switch_context home
+else
+ echo "Current context is: $current_context. No action taken."
+fi
diff --git a/ar/.local/bin/task/taskwarrior-tui/taskopen-annotation b/ar/.local/bin/task/taskwarrior-tui/taskopen-annotation
new file mode 100755
index 0000000..3b292e8
--- /dev/null
+++ b/ar/.local/bin/task/taskwarrior-tui/taskopen-annotation
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+set -eu
+
+if [ -z "$2" ]; then
+ echo "Second argument is unset" >>/tmp/taskopen_debug.log
+else
+ echo "Second argument is set to '$2'" >>/tmp/taskopen_debug.log
+fi
+
+taskopen "$1"
diff --git a/ar/.local/bin/task/taskwarrior-tui/taskopen-line b/ar/.local/bin/task/taskwarrior-tui/taskopen-line
new file mode 100755
index 0000000..832b30e
--- /dev/null
+++ b/ar/.local/bin/task/taskwarrior-tui/taskopen-line
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+# Capture the current session name
+current_session=$(tmux display-message -p '#S')
+
+# Sleep for a bit to allow tui to load
+sleep 0.1
+
+# Extract the line number and file path from the input string
+LINE_NUMBER=$(echo "$1" | awk -F ':' '{print $2}')
+FILE_PATH=$(echo "$1" | awk -F ':' '{print $3}')
+
+# Resolve the file path if it's a symlink
+if [ -L "$FILE_PATH" ]; then
+ FILE_PATH=$(readlink -f "$FILE_PATH")
+fi
+
+# Use all arguments beyond the first one as the TASK_DESCRIPTION
+shift
+TASK_DESCRIPTION="$*"
+
+# If a task description is provided, search for the line number containing that description
+if [ -n "$TASK_DESCRIPTION" ]; then
+ NEW_LINE_NUMBER=$(grep -n -F "$TASK_DESCRIPTION" "$FILE_PATH" | awk -F ':' '{print $1}' | head -n 1)
+ if [ -n "$NEW_LINE_NUMBER" ]; then
+ LINE_NUMBER=$NEW_LINE_NUMBER
+ fi
+fi
+
+# Capture the file name from the file path without the extension
+FILE_NAME=$(basename "$FILE_PATH" | awk -F '.' '{print $1}')
+DIR_NAME=$(dirname "$FILE_PATH")
+
+# Check if directory exists
+if [ ! -d "$DIR_NAME" ]; then
+ exit 1
+fi
+
+# Create a new tmux session which opens the file with neovim at the specific line number
+cd "$DIR_NAME" && tmux new-session -d -s "$FILE_NAME" "nvim +$LINE_NUMBER $FILE_PATH"
+
+# Attach to the new session
+tmux switch-client -t "$FILE_NAME"
+
+# Wait for the session to be closed, either by the user or some other way
+while tmux has-session -t "$FILE_NAME" 2>/dev/null; do
+ sleep 1
+done
+
+# Switch back to the original session
+tmux switch-client -t "$current_session"
diff --git a/ar/.local/bin/task/taskwarrior-tui/tasks-sync b/ar/.local/bin/task/taskwarrior-tui/tasks-sync
new file mode 100755
index 0000000..06334c4
--- /dev/null
+++ b/ar/.local/bin/task/taskwarrior-tui/tasks-sync
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+if [ "$(task _get tw.syncneeded)" -eq 1 ]; then
+ if ! task sync 2>/dev/null; then
+ task config sync.encryption_secret "$(pass show server/task_secret)"
+ task config sync.gcp.bucket "task.thesiah.xyz"
+ task config sync.gcp.credential_path "${XDG_DATA_HOME:-${HOME}/.local/share}/task/task-sync-server.json"
+
+ task sync
+ fi
+fi
diff --git a/ar/.local/bin/task/taskwarrior-tui/toggle-review-label b/ar/.local/bin/task/taskwarrior-tui/toggle-review-label
new file mode 100755
index 0000000..9133c30
--- /dev/null
+++ b/ar/.local/bin/task/taskwarrior-tui/toggle-review-label
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+uuid="$1"
+# Use task _tags command to get current tags
+current_tags=$(task _tags "$uuid")
+
+if echo "$current_tags" | grep -q "review"; then
+ # Remove review tag if present
+ task rc.bulk=0 rc.confirmation=off "$uuid" modify -review manual_priority: -next
+else
+ # Add review tag if not present
+ task rc.bulk=0 rc.confirmation=off "$uuid" modify +review
+fi
diff --git a/ar/.local/bin/td-toggle b/ar/.local/bin/td-toggle
new file mode 100755
index 0000000..5dc4d49
--- /dev/null
+++ b/ar/.local/bin/td-toggle
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# If transmission-daemon is running, will ask to kill, else will ask to start.
+
+if pidof transmission-daemon >/dev/null; then
+ [ "$(printf "Yes\\nNo" | dmenu -i -p "Turn off transmission-daemon?")" = "Yes" ] && killall transmission-daemon && notify-send "❌ transmission-daemon disabled."
+else
+ ifinstalled transmission-cli || exit
+ [ "$(printf "Yes\\nNo" | dmenu -i -p "Turn on transmission daemon?")" = "Yes" ] && transmission-daemon && notify-send "🚄 transmission-daemon enabled."
+fi
+sleep 3 && pkill -RTMIN+22 "${STATUSBAR:-dwmblocks}"
diff --git a/ar/.local/bin/texclear b/ar/.local/bin/texclear
new file mode 100755
index 0000000..58f4439
--- /dev/null
+++ b/ar/.local/bin/texclear
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+# Clears the build files of a LaTeX/XeLaTeX build.
+# I have vim run this file whenever I exit a .tex file.
+
+[ "${1##*.}" = "tex" ] && {
+ find "$(dirname "${1}")" -regex '.*\(_minted.*\|.*\.\(4tc\|xref\|tmp\|pyc\|pyg\|pyo\|fls\|vrb\|fdb_latexmk\|bak\|swp\|aux\|log\|synctex\(busy\)\|lof\|lot\|maf\|idx\|mtc\|mtc0\|nav\|out\|snm\|toc\|bcf\|run\.xml\|synctex\.gz\|blg\|bbl\)\)' -delete
+} || printf "Provide a .tex file.\n"
diff --git a/ar/.local/bin/timer b/ar/.local/bin/timer
new file mode 100755
index 0000000..fd4bf5e
--- /dev/null
+++ b/ar/.local/bin/timer
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+if [ -z "$1" ]; then
+ echo "Usage: ${0##*/} <mins> [message]"
+ exit 1
+fi
+
+mins=$1
+message=$2
+
+nohup timer_ "$mins" "$message" >/dev/null 2>&1 &
+
+echo "timer started for $mins min"
+logger -t timer "timer started for $mins min, message: '$message'"
diff --git a/ar/.local/bin/timer_ b/ar/.local/bin/timer_
new file mode 100755
index 0000000..665e61f
--- /dev/null
+++ b/ar/.local/bin/timer_
@@ -0,0 +1,14 @@
+#!/bin/sh
+set -eu
+
+mins=$1
+message=${2:-Time out!}
+
+# Calculate the sleep time in seconds
+sleep_time=$(expr "$mins" \* 60)
+sleep "$sleep_time"
+
+# Send a notification using a POSIX-compliant command
+# 'notify-send' is not POSIX, but we'll keep it since it's a common tool
+# For complete POSIX compliance, replace this with another method if needed
+notify-send -t 0 "${message}" "Your timer of $mins min is over" -u normal
diff --git a/ar/.local/bin/timezones b/ar/.local/bin/timezones
new file mode 100755
index 0000000..206f8da
--- /dev/null
+++ b/ar/.local/bin/timezones
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+set -e
+
+[ "$1" != "" ] && time=$1 || time=''
+
+printDate() {
+ [ "$time" != "" ] && message="$(TZ=$2 date +'%a, %d %b %H:%M' -d "$time $(date +%Z)")" || message="$(TZ=$2 date +'%a, %d %b %H:%M')"
+ notify-send "$1" "$message"
+}
+
+printDate "Los Angeles" America/Los_Angeles
+printDate "New York" America/New_York
+printDate "London" Europe/London
+printDate "Berlin" Europe/Berlin
+printDate "Moscow" Europe/Moscow
+printDate "Bangkok" Asia/Bangkok
+printDate "Hong Kong" Asia/Hong_Kong
+printDate "Macau" Asia/Macau
+printDate "Seoul" Asia/Seoul
+printDate "Tokyo" Asia/Tokyo
diff --git a/ar/.local/bin/tmuxcreate b/ar/.local/bin/tmuxcreate
new file mode 100755
index 0000000..5fb5ef3
--- /dev/null
+++ b/ar/.local/bin/tmuxcreate
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+create_new_session() {
+ session_name=$1
+ session_path=${2:-"$PWD"} # Default to current directory if no path is provided
+ [ -z "$session_name" ] && { printf "New session name: " && read -r session_name; }
+ if tmux has-session -t "$session_name" 2>/dev/null; then
+ tmux switch-client -t "$session_name"
+ else
+ if [ -n "$TMUX" ]; then
+ tmux new-session -d -s "$session_name" -c "$session_path"
+ tmux switch-client -t "$session_name"
+ else
+ tmux new -s "$session_name" -c "$session_path"
+ fi
+ fi
+}
+
+if [ $# -gt 0 ]; then
+ if [ -d "$1" ]; then
+ create_new_session "$(basename "$1")" "$1"
+ else
+ create_new_session "$1"
+ fi
+else
+ # Capture the output of tmux ls
+ sessions=$(tmux ls 2>/dev/null)
+ if [ -z "$sessions" ]; then
+ create_new_session
+ else
+ session=$( (
+ echo "$sessions"
+ echo "[new session]"
+ ) | fzf-tmux --reverse | cut -d: -f1)
+ [ -z "$session" ] && exit
+ if [ "$session" = "[new session]" ]; then
+ create_new_session
+ else
+ tmux attach -t "$session"
+ fi
+ fi
+fi
diff --git a/ar/.local/bin/tmuxopen b/ar/.local/bin/tmuxopen
new file mode 100755
index 0000000..ff1c275
--- /dev/null
+++ b/ar/.local/bin/tmuxopen
@@ -0,0 +1,193 @@
+#!/bin/sh
+
+usage() {
+ echo "Search for files and open them in Neovim within tmux panes."
+ echo ""
+ echo "Usage: tmuxopen [OPTIONS]"
+ echo ""
+ echo "Options:"
+ echo " -h, --help : Show this help message"
+ echo " -d, --debug : Enable debug output"
+ echo ""
+ echo "Controls:"
+ echo " Tab Select files"
+ echo " Ctrl+f Search filenames"
+ echo " Ctrl+g Search file contents"
+ echo " Ctrl+d Search directories"
+ echo ""
+ echo "Environment Variables:"
+ echo " NVIM_SEARCH_REGISTRY Set to the search query, allowing Neovim to highlight matches"
+ echo ""
+ echo "Example:"
+ echo " tmuxopen # Run the normal search and open"
+ echo " tmuxopen --debug # Run with debug output"
+}
+
+debug() {
+ [ "$DEBUG" -eq 1 ] && echo "DEBUG: $*" >&2
+}
+
+get_fzf_output() {
+ RG_BIND="ctrl-g:reload:rg --line-number --no-heading --color=always --smart-case --glob '!**/.git/**' --glob '!node_modules/**' '' 2>/dev/null || true"
+ FILE_BIND="ctrl-f:reload:rg --files --glob '!**/.git/**' --glob '!node_modules/**' 2>/dev/null || true"
+ if command -v fd >/dev/null 2>&1; then
+ DIR_BIND="ctrl-d:change-prompt(directory> )+reload(cd \"$HOME\" && echo \"$HOME\"; fd --type d --hidden --absolute-path --color never --exclude .git --exclude node_modules)"
+ else
+ DIR_BIND="ctrl-d:change-prompt(directory> )+reload(cd \"$HOME\" && find \"$HOME\" -type d -name node_modules -prune -o -name .git -prune -o -type d -print)"
+ fi
+
+ rg --line-number --no-heading --color=always --smart-case --glob '!**/.git/**' --glob '!LICENSE' '' 2>/dev/null |
+ fzf-tmux \
+ --ansi --multi --delimiter : \
+ --reverse \
+ --print-query \
+ --preview 'bat --style=numbers --color=always --highlight-line {2} {1} 2>/dev/null || echo "Preview not available"' \
+ --preview-window 'up,60%,border-bottom,+{2}+3/3,~3' \
+ --bind "$FILE_BIND" \
+ --bind "$RG_BIND" \
+ --bind "$DIR_BIND" \
+ --bind 'ctrl-c:abort' \
+ --header "^f filenames, ^g contents, ^d directories"
+}
+
+set_nvim_search_variable() {
+ raw_output="$1"
+ query=$(echo "$raw_output" | head -n1)
+ export NVIM_SEARCH_REGISTRY="$query"
+}
+
+open_files_in_nvim() {
+ pane="$1"
+ shift
+ file_indices="$*"
+ nvim_cmd="nvim"
+ for index in $file_indices; do
+ file=$(echo "$files" | awk -v idx="$index" '{print $idx}')
+ line=$(echo "$lines" | awk -v idx="$index" '{print $idx}')
+ nvim_cmd="$nvim_cmd +$line $file"
+ done
+ nvim_cmd="$nvim_cmd -c 'let @/=\"$NVIM_SEARCH_REGISTRY\"'"
+ debug "Running command in pane $pane: $nvim_cmd"
+ tmux send-keys -t "$pane" "$nvim_cmd" C-m
+}
+
+# Main logic
+DEBUG=0
+
+# Parse command line arguments
+while [ "$#" -gt 0 ]; do
+ case "$1" in
+ -d | --debug)
+ DEBUG=1
+ shift
+ ;;
+ -h | --help)
+ usage
+ exit 0
+ ;;
+ *)
+ echo "Unknown option: $1" >&2
+ usage
+ exit 1
+ ;;
+ esac
+done
+
+for cmd in rg fzf bat tmux nvim; do
+ if ! command -v "$cmd" >/dev/null 2>&1; then
+ echo "Error: $cmd not found" >&2
+ exit 1
+ fi
+done
+
+if [ -z "$TMUX" ]; then
+ echo "Error: Not in a tmux session" >&2
+ exit 1
+fi
+
+raw_output=$(get_fzf_output)
+debug "Raw fzf output:"
+debug "$raw_output"
+set_nvim_search_variable "$raw_output"
+
+# Split the newline-delimited output into an array, skipping the first line (query)
+selections=$(echo "$raw_output" | sed 1d)
+
+debug "Number of selections: $(echo "$selections" | wc -l)"
+debug "Selections:"
+debug "$selections"
+if [ -z "$selections" ]; then
+ debug "No selections made"
+ exit 0
+fi
+
+files=""
+lines=""
+count=0
+
+# Use a here document to avoid subshell issues
+while IFS= read -r selection; do
+ file=$(echo "$selection" | awk -F: '{print $1}')
+ line=$(echo "$selection" | awk -F: '{print $2}')
+ debug "Processing selection: $selection"
+ debug "File: $file, Line: $line"
+ if [ -f "$file" ]; then
+ files="$files $file"
+ lines="$lines $line"
+ count=$((count + 1))
+ else
+ debug "File not found: $file"
+ fi
+done <<EOF
+$selections
+EOF
+
+debug "Number of valid files: $count"
+debug "Valid files:"
+debug "$files"
+if [ "$count" -eq 0 ]; then
+ debug "No valid files selected"
+ exit 0
+fi
+
+if [ "$count" -eq 1 ]; then
+ debug "Opening single file"
+ open_files_in_nvim "$(tmux display-message -p '#P')" 1
+else
+ debug "Opening multiple files"
+ window_name="TheSiahxyz-$(date +%s)"
+ tmux new-window -n "$window_name"
+ case "$count" in
+ 2)
+ debug "Opening 2 files side-by-side"
+ tmux split-window -t "$window_name" -h -p 50
+ open_files_in_nvim "$window_name.1" 1
+ open_files_in_nvim "$window_name.2" 2
+ tmux select-pane -t "$window_name.1"
+ ;;
+ 3)
+ debug "Opening 3 files"
+ tmux split-window -t "$window_name" -h -p 50
+ tmux split-window -t "$window_name.2" -v -p 50
+ open_files_in_nvim "$window_name.1" 1
+ open_files_in_nvim "$window_name.2" 2
+ open_files_in_nvim "$window_name.3" 3
+ ;;
+ *)
+ debug "Opening 4 or more files"
+ tmux split-window -t "$window_name" -h -p 50
+ tmux split-window -t "$window_name.1" -v -p 50
+ tmux split-window -t "$window_name.3" -v -p 50
+ open_files_in_nvim "$window_name.1" 1
+ open_files_in_nvim "$window_name.2" 2
+ open_files_in_nvim "$window_name.3" 3
+ remaining_indices=""
+ for i in $(seq 4 "$count"); do
+ remaining_indices="$remaining_indices $i"
+ done
+ open_files_in_nvim "$window_name.4" "$remaining_indices"
+ ;;
+ esac
+fi
+
+debug "Script completed"
diff --git a/ar/.local/bin/tmuxtogglebar b/ar/.local/bin/tmuxtogglebar
new file mode 100755
index 0000000..2ae045e
--- /dev/null
+++ b/ar/.local/bin/tmuxtogglebar
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+case "$(tmux show-option -gqv status)" in
+on) tmux set-option -g status off ;;
+off) tmux set-option -g status on ;;
+esac
diff --git a/ar/.local/bin/tmuxtoggleterm b/ar/.local/bin/tmuxtoggleterm
new file mode 100755
index 0000000..f21f833
--- /dev/null
+++ b/ar/.local/bin/tmuxtoggleterm
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+export TMUX_PANE_DIRECTION="bottom"
+
+if [ "$TMUX_PANE_DIRECTION" = "bottom" ]; then
+ tmux select-pane -U
+elif [ "$TMUX_PANE_DIRECTION" = "right" ]; then
+ tmux select-pane -L
+fi
+
+tmux resize-pane -Z
diff --git a/ar/.local/bin/tordone b/ar/.local/bin/tordone
new file mode 100755
index 0000000..4e097a0
--- /dev/null
+++ b/ar/.local/bin/tordone
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+completed_torrents=$(transmission-remote -n "$USER" -l | grep 100% | awk '{print $1}')
+if [ -n "$completed_torrents" ]; then
+ for torrent_id in $completed_torrents; do
+ transmission-remote -n "$USER" -t "$torrent_id" -r
+ done
+ pkill -RTMIN+22 "${STATUSBAR:-dwmblocks}" && notify-send "✅ Transmission-daemon" "Torrent(s) $TR_TORRENT_NAME has completed downloading. Deleting torrent files."
+ [ -z "$(transmission-remote -n "$USER" -l | grep -v "Sum:")" ] && killall transmission-daemon && notify-send "❌ Transmission-daemon disabled."
+fi
diff --git a/ar/.local/bin/torwrap b/ar/.local/bin/torwrap
new file mode 100755
index 0000000..3d32cc5
--- /dev/null
+++ b/ar/.local/bin/torwrap
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+ifinstalled stig transmission-cli || exit 1
+
+! pidof transmission-daemon >/dev/null && transmission-daemon && notify-send "Starting torrent daemon..."
+
+$TERMINAL -e stig
+pkill -RTMIN+22 "${STATUSBAR:-dwmblocks}"
diff --git a/ar/.local/bin/transadd b/ar/.local/bin/transadd
new file mode 100755
index 0000000..ffd8ded
--- /dev/null
+++ b/ar/.local/bin/transadd
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# Mimeapp script for adding torrent to transmission-daemon, but will also start the daemon first if not running.
+
+# transmission-daemon sometimes fails to take remote requests in its first moments, hence the sleep.
+
+pidof transmission-daemon >/dev/null || (transmission-daemon && notify-send "💡 Starting transmission daemon..." && sleep 3 && pkill -RTMIN+22 "${STATUSBAR:-dwmblocks}")
+
+directory="$HOME/Torrents"
+
+[ "$1" = "-l" ] && {
+ URL=$(xclip -selection clipboard -o)
+ case "$URL" in
+ http://* | https://* | magnet:?*)
+ transmission-remote -a "$URL" && notify-send "🔽 Torrent added."
+ exit 0
+ ;;
+ *)
+ added_torrents=$(transmission-remote -l | grep -vE '^ID|Sum' | awk '{print $NF}' | sed 's/\.torrent$//')
+ filtered_files=$(ls "$directory"/*.torrent 2>/dev/null | sed "s|^$directory/||" | sed 's/\.torrent$//' | grep -vF "$added_torrents")
+ [ -n "$filtered_files" ] && {
+ choice=$(echo "$filtered_files" | dmenu -i -l 20 -p "Select Torrent:")
+ [ -n "$choice" ] && transmission-remote -a "$directory/$choice.torrent" && notify-send "🔽 Torrent added."
+ } || notify-send "🤷 No new torrent found."
+ ;;
+ esac
+} || {
+ transmission-remote -a "$@" && notify-send "🔽 Torrent added." "$TR_TORRENT_NAME"
+}
diff --git a/ar/.local/bin/tutorialvids b/ar/.local/bin/tutorialvids
new file mode 100755
index 0000000..1bac3df
--- /dev/null
+++ b/ar/.local/bin/tutorialvids
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+# This gives the user a list of videos they can select and watch without a
+# browser. If you want to check a tutorial video, it makes it easy. I'll
+# add/remove videos from this list as I go on.
+
+vidlist="
+dwm (window manager) https://videos.lukesmith.xyz/videos/watch/f6b78db7-b368-4647-bc64-28c08fff1988
+dwmblocks (status bar) https://videos.lukesmith.xyz/w/mmxHMbqZZEr5FManB57Yy1
+pacman (installing/managing programs) https://videos.lukesmith.xyz/videos/watch/8e7cadb9-0fed-47ce-a2a8-6635fa48614b
+sxiv/nsxiv (image viewer) https://videos.lukesmith.xyz/videos/watch/ad4c8d85-90c3-4f3d-a1f3-89129e64a3c2
+st (terminal) https://videos.lukesmith.xyz/videos/watch/efddd39d-bac5-4599-b572-177beb4ce6e8
+i3 (old window manager) https://videos.lukesmith.xyz/videos/watch/b861525c-7ada-40ee-a2bb-b5e1ffe0f48b
+neomutt (email) https://videos.lukesmith.xyz/videos/watch/83122e83-52d9-4278-ae1a-7d1beeb50c8e
+ncmpcpp (music player) https://videos.lukesmith.xyz/videos/watch/b5ac6f0d-a220-4433-88e3-e98fc791dc0a
+newsboat (RSS reader) https://videos.lukesmith.xyz/videos/watch/bd2c3fff-40fa-47ea-aa98-5b1ec0c903b6
+lf (file manager) https://videos.lukesmith.xyz/w/rKeHsF5ZHDNDbR1buUKB1c
+zathura (pdf viewer) https://videos.lukesmith.xyz/videos/watch/c780f75a-11f6-48a9-a191-d079ebc36ea4
+gpg keys https://videos.lukesmith.xyz/videos/watch/040f5530-4830-4583-9ddc-2080b421531b
+calcurse (calendar) https://videos.lukesmith.xyz/videos/watch/4b937e8b-7654-46e3-8d01-79392ec5b3d1
+urlview https://videos.lukesmith.xyz/videos/watch/31a4918f-633b-4bd6-b08e-956ac75d0324
+colorschemes with pywal https://videos.lukesmith.xyz/videos/watch/1b476003-61b2-4609-ac4b-820c3d128643
+vi mode in shell https://videos.lukesmith.xyz/videos/watch/228aa50c-836f-456f-9f0d-a45157fe4313
+pass (password manager) https://videos.lukesmith.xyz/videos/watch/432fc942-5e28-4682-9beb-f5cb237a1dd6
+"
+echo "$vidlist" | grep -P "^$(echo "$vidlist" | grep "https:" | sed 's/\t.*//g' | dmenu -i -p "Learn about what? (ESC to cancel)" -l 20 | awk '{print $1}')\s" | sed 's/.*\t//' | xargs -r mpv
diff --git a/ar/.local/bin/unewsboat b/ar/.local/bin/unewsboat
new file mode 100755
index 0000000..547fae2
--- /dev/null
+++ b/ar/.local/bin/unewsboat
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+export FIFO_UEBERZUG="/tmp/vifm-ueberzug-${PPID}"
+
+function cleanup {
+ rm "$FIFO_UEBERZUG" 2>/dev/null
+ pkill -P $$ 2>/dev/null
+}
+
+rm "$FIFO_UEBERZUG" 2>/dev/null
+mkfifo "$FIFO_UEBERZUG"
+trap cleanup EXIT
+tail --follow "$FIFO_UEBERZUG" | ueberzug layer --silent --parser bash &
+
+newsboat "$@"
+cleanup
diff --git a/ar/.local/bin/unix b/ar/.local/bin/unix
new file mode 100755
index 0000000..a9fb96e
--- /dev/null
+++ b/ar/.local/bin/unix
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+#original artwork by http://www.sanderfocus.nl/#/portfolio/tech-heroes
+#converted to shell by #nixers @ irc.unix.chat
+
+cat << 'eof'
+ ,_ ,_==▄▂
+ , ▂▃▄▄▅▅▅▂▅¾. / /
+ ▄▆<´ "»▓▓▓%\ / / / /
+ ,▅7" ´>▓▓▓% / / > / >/%
+ ▐¶▓ ,»▓▓¾´ /> %/%// / /
+ ▓▃▅▅▅▃,,▄▅▅▅Æ\// ///>// />/ /
+ V║«¼.;→ ║<«.,`=// />//%/% / /
+ //╠<´ -²,)(▓~"-╝/¾/ %/>/ />
+ / / / ▐% -./▄▃▄▅▐, /7//;//% / /
+ / ////`▌▐ %zWv xX▓▇▌//&;% / /
+ / / / %//%/¾½´▌▃▄▄▄▄▃▃▐¶\/& /
+ </ /</%//`▓!%▓%╣[38;5;255;╣WY<Y)y&/`\
+ / / %/%//</%//\i7; ╠N>)VY>7; \_ UNIX IS VERY SIMPLE IT JUST NEEDS A
+ / /</ //<///<_/%\▓ V%W%£)XY _/%‾\_, GENIUS TO UNDERSTAND ITS SIMPLICITY
+ / / //%/_,=--^/%/%%\¾%¶%%} /%%%%%%;\,
+ %/< /_/ %%%%%;X%%\%%;, _/%%%;, \
+ / / %%%%%%;, \%%l%%;// _/%;, dmr
+ / %%%;, <;\-=-/ /
+ ;, l
+eof
diff --git a/ar/.local/bin/unmounter b/ar/.local/bin/unmounter
new file mode 100755
index 0000000..1e413bf
--- /dev/null
+++ b/ar/.local/bin/unmounter
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+# Unmount USB drives or Android phones. Replaces the older `dmenuumount`. Fewer
+# prompt and also de-decrypts LUKS drives that are unmounted.
+
+set -e
+
+mounteddroids="$(grep simple-mtpfs /etc/mtab | awk '{print "📱" $2}')"
+lsblkoutput="$(lsblk -nrpo "name,type,size,mountpoint")"
+mounteddrives="$(echo "$lsblkoutput" | awk '($2=="part"||$2="crypt")&&$4!~/\/boot|\/home$|SWAP/&&length($4)>1{printf "💾%s (%s)\n",$4,$3}')"
+mountedcifs="$(grep cifs /etc/mtab | awk '{print "🪟" $2}')"
+
+allunmountable="$(echo "$mounteddroids
+$mounteddrives
+$mountedcifs" | sed "/^$/d;s/ *$//")"
+test -n "$allunmountable"
+
+chosen="$(echo "$allunmountable" | dmenu -i -p "Unmount which drive?")"
+chosen="${chosen%% *}"
+test -n "$chosen"
+
+if grep -q "/${chosen#*/}" /etc/mtab | grep -q "cifs" /etc/mtab; then
+ sudo -A umount "/${chosen#*/}" && sudo -A rm -r "/${chosen#*/}"
+ notify-send "⏏️ SMB Drive unmounted." "/${chosen#*/} has been unmounted."
+ exit 0
+fi
+
+[ "${chosen#*/}" = "${chosen}" ] && [ "${chosen##*/}" = "$(sudo lsblk -no "label" "$(df "/${chosen#*/}" | tail -n 1 | awk '{print $1}')")" ] && rmcheck=true || rmcheck=false
+mountpath="$(sudo lsblk -no "mountpoints" "$(df "/${chosen#*/}" | tail -n 1 | awk '{print $1}')")"
+sudo -A umount -l "/${chosen#*/}"
+[ "/media/$USER/${chosen##*/}" = "$mountpath" ] && [ "$rmcheck" ] && {
+ [ -e "/${chosen#*/}" ] && [ -z "$(ls -A "/${chosen#*/}")" ] && (rm -rf "/${chosen#*/}" >/dev/null 2>&1 || sudo rm -rf "/${chosen#*/}") || {
+ rmdiryn=$(printf "No\\nYes" | dmenu -i -p "Do you want to delete /${chosen#*/}?")
+ [ "$rmdiryn" = "Yes" ] && (rm -rf "/${chosen#*/}" >/dev/null 2>&1 || sudo -A rm -r "/${chosen#*/}")
+ }
+}
+notify-send "⏏️ Device unmounted." "$chosen has been unmounted."
+
+# Close the chosen drive if decrypted.
+cryptid="$(echo "$lsblkoutput" | grep "/${chosen#*/}$")"
+cryptid="${cryptid%% *}"
+test -b /dev/mapper/"${cryptid##*/}"
+sudo -A cryptsetup close "$cryptid"
+notify-send "🔒 Device dencryption closed." "Drive is now securely locked again."
diff --git a/ar/.local/bin/vipy b/ar/.local/bin/vipy
new file mode 100755
index 0000000..ac30f30
--- /dev/null
+++ b/ar/.local/bin/vipy
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+# Create a new notebook JSON structure
+NOTEBOOK='{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython"
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}'
+
+[ -z "$1" ] && echo "Enter a file name!" && exit
+
+# Write the JSON to a new .ipynb file
+echo "$NOTEBOOK" >"$1.ipynb"
+
+# Check if the file was created successfully
+[ -f "$1.ipynb" ] && echo "$1.ipynb created successfully." || echo "Failed to create a Jupyter Notebook."
diff --git a/ar/.local/bin/wallset b/ar/.local/bin/wallset
new file mode 100755
index 0000000..e2414cb
--- /dev/null
+++ b/ar/.local/bin/wallset
@@ -0,0 +1,295 @@
+#!/bin/bash
+# vim: noai:ts=4:sw=4:expandtab
+#
+# wallset: This program customizes images and videos for wallpaper
+# website: http://terminalroot.com.br/
+# author: Marcos Oliveira
+# dependencies: feh, ffmpeg, imagemagick(convert), xrandr, sed, bash, xdg-utils
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+shopt -s extglob
+
+if [[ $(echo $LANG | cut -c 1-2) == 'pt' ]]; then
+ _lang=("uso" "opções" "Altera o Wallpaper para o número informado. Use imagens com 3 dígitos, exemplo: 014,003,099" "Adiciona imagens" "Use antes do parâmetro -a,--add quando quiser adicionar já alterar" "Finaliza o loop" "Cria um loop de imagem com o tempo informado em segundos" "Abre a última imagem adicionada" "Informa quantas imagens há" "Mostra a atual imagem" "Adiciona um vídeo como Wallpaper" "Lista os vídeos que já foram utilizados" "Utiliza o vídeo pelo número listado em --list-videos" "Remove a última imagem adicionada" "Exibe essa ajuda" "Exibe a versão desse programa" "Exemplos" "Adiciona uma imagem" "Adiciona e já define como Wallpaper" "Altera para imagem com esse número" "Adiciona o vídeo" "Usa o vídeo já utilizado e listado" "Significa que precisa usar um número" "Somente vídeos nos formato .mp4" "Somente será exibido 10 segundos iniciais do vídeo" "Use imagens com 3 dígitos, exemplo: 011" "Não há nenhuma imagem." "Preparando o vídeo" "Use números indicados por" "Use números de 1 até" "Tempo inválido" "Tipo inválido" "Use números de 001 até" "Formato inválido." "Somente formato MP4" "Número inválido. Use" "Informe o vídeo" "Não há imagens." "Não há vídeos.")
+else
+ _lang=("usage" "options" "Change the Wallpaper to the number entered. Use images with 3 digits, example: 014,003,099" "Add images" "Use before the -a, --add parameter when you want to add already change" "Ends the loop" "Creates an image loop with the time reported in seconds" "Opens the last image added" "Informs how many images there are" "Shows the current image" "Add a video as Wallpaper" "Lists the videos that have already been used" "Use the video by the number listed in --list-videos" "Remove the last image added" "Display this help" "Display the version of this program" "Examples" "Add an image" "Add and set it as Wallpaper" "Change to image with this number" "Add the video" "Use the video already used and listed" "Means that you need to use a number" "Only videos in .mp4 format" "Only the first 10 seconds of the video will be displayed" "Use 3-digit images, example: 011" "There is no image." "Preparing the video..." "Use numbers indicated by" "Use numbers numbers from 1 to" "Invalid time" "Invalid type" "Use numbers from 001 to" "Invalid format." "MP4 format only" "Invalid number. Use" "Report Video" "No images." "No videos.")
+fi
+
+_usage() {
+ cat <<EOF
+${_lang[0]}: ${0##*/} [${_lang[1]}]
+
+ Options:
+ -u,--use [N] ${_lang[2]}
+ -a,--add img.jpg ... ${_lang[3]}
+ -S,--set ${_lang[4]}
+ -q,--quit ${_lang[5]}
+ -t,--time [N] ${_lang[6]}
+ -d,--display ${_lang[7]}
+ -c,--count ${_lang[8]}
+ -s,--show ${_lang[9]}
+ -V,--video [video] ${_lang[10]}
+ -L,--list-videos ${_lang[11]}
+ -I,--set-video [N] ${_lang[12]}
+ -r,--remove ${_lang[13]}
+ -h,--help ${_lang[14]}
+ -v,--version ${_lang[15]}
+
+ ${_lang[16]}:
+ ${_lang[17]} → '${0##*/} -a img.jpg'
+ ${_lang[18]} → '${0##*/} --set --add img.jpg'
+ ${_lang[19]} → '${0##*/} -u 001'
+ ${_lang[20]} → '${0##*/} -V video.mp4'
+ ${_lang[21]} → '${0##*/} -I 3'
+
+* [N] ${_lang[22]}
+** ${_lang[23]}
+*** ${_lang[24]}
+**** ${_lang[25]}
+EOF
+ exit
+}
+
+_dir_img="${HOME}/.local/share/wallpapers"
+_dir_vid="${_dir_img}/video"
+pid="$$"
+
+[[ ! -d "${_dir_img}" ]] && mkdir -p "${_dir_img}"
+[[ ! -d "${_dir_vid}" ]] && mkdir -p "${_dir_vid}"
+ctrl_c() {
+ exit 127
+}
+
+_get_resolution_screen() {
+ xrandr | grep '*' | awk '{print $1}' | cut -dx -f1
+}
+
+_last_wall() {
+ ls ${_dir_img}/*.jpg 2>/dev/null >/dev/null
+ if [[ "$?" != "0" ]]; then
+ printf "%s" "0"
+ else
+ _n=$(ls -1 ${_dir_img}/*.jpg | tail -n 1 | tr -d 'a-z\.\-\/')
+ printf "%s" "${_n}"
+ fi
+}
+
+_new_number() {
+ _n=$(echo $(_last_wall) + 1 | bc)
+ [[ $(expr length ${_n}) == 1 ]] && _n="00${_n}" || _n="0${_n}"
+ printf "%s" "${_n}"
+
+}
+
+_resize_img() {
+ _n=$(_new_number)
+ _w=$(_get_resolution_screen)
+ convert -resize "${_w}" "${1}" "${_dir_img}/wallpaper-${_n}.jpg"
+ if [[ "${add}" == 1 ]]; then
+ feh --no-fehbg --bg-scale "${_dir_img}/wallpaper-${_n}.jpg"
+ [[ $(which gsettings >&- 2>&-) ]] &&
+ gsettings set org.gnome.desktop.background picture-uri file:///"${_dir_img}/wallpaper-${_n}.jpg"
+ fi
+}
+
+_count() {
+ ls ${_dir_img}/*.jpg 2>/dev/null | wc -l
+}
+
+_current() {
+ xdg-open "${_dir_img}/wallpaper-$(_last_wall).jpg"
+}
+
+_loop() {
+ if [[ $(_count) < 1 ]]; then
+ printf "%s\n" "${_lang[26]}"
+ exit 1
+ fi
+ i=1
+ while :; do
+ [[ "${i}" -ge "$(_count)" ]] && i=1
+ if [[ $(expr length $i) == 1 ]]; then
+ feh --no-fehbg --bg-scale "${_dir_img}/wallpaper-00${i}.jpg"
+ else
+ feh --no-fehbg --bg-scale "${_dir_img}/wallpaper-0${i}.jpg"
+ fi
+ sleep $1
+ let i=i+1
+
+ done &
+
+}
+
+_quit_loop() {
+ #kill -9 $$
+ killall -q bash 2>/dev/null >/dev/null
+}
+
+_set_img() {
+ ls "${_dir_img}/wallpaper-${1}.jpg" >/dev/null 2>/dev/null
+ if [[ "$?" == 0 ]]; then
+ feh --no-fehbg --bg-scale "${_dir_img}/wallpaper-$1.jpg"
+ if [[ $(which gsettings >&- 2>&-) && "$XDG_CURRENT_DESKTOP" == "GNOME" ]]; then
+ gsettings set org.gnome.desktop.background picture-uri file:///"${_dir_img}/wallpaper-$1.jpg"
+ fi
+ else
+ _t=$(_count)
+ [[ "${_t}" -lt 100 ]] && _t="0${_t}"
+ echo "$1 - ${_lang[35]}: "$(ls ${_dir_img} | sed 's/wallpaper-//;s/\.jpg/ /g')
+ fi
+
+}
+
+_show() {
+ mpv --really-quiet "${XDG_DATA_HOME:-${HOME}/.local/share}/wallpapers/video/bg.mp4"
+}
+
+_video() {
+ a=1
+ for i in $(ls -d $_dir_vid/*/ 2>/dev/null); do
+ if [[ -d "$_dir_vid/$a" ]]; then
+ let a=a+1
+ fi
+ done
+
+ mkdir -p "$_dir_vid/$a"
+
+ echo -e "${_lang[27]}...\r"
+ ffmpeg -y -ss 00:00 -i ${1} -q:v 1 -t 10 "${_dir_vid}/${a}/filename%05d.jpg" >${_dir_vid}/${a}/video.info 2>&1
+ t=$(ls ${_dir_vid}/${a}/* | wc -l)
+ let t=t-1
+ echo ${1} >${_dir_vid}/${a}/video.info
+
+ i=1
+ # ctrl_c
+ while :; do
+ feh --no-fehbg --bg-scale $(ls -1 ${_dir_vid}/${a}/* | sed -n "${i}p")
+ sleep 0.01
+ let i=i+1
+ [[ "${i}" -gt "${t}" ]] && i=1
+ done &
+}
+
+_list_vid() {
+ ls -d ${_dir_vid}/* 2>/dev/null >/dev/null
+ if [[ "$?" != "0" ]]; then
+ echo ${_lang[38]}
+ exit 1
+ fi
+ for i in $(ls -1 ${_dir_vid}/*/video.info); do
+ echo $i | awk 'BEGIN { FS = "/" } ; { print $6 }'
+ cat $i
+ done | paste - - | sed 's/\t/ → /g'
+}
+
+_set_video() {
+ ls -d ${_dir_vid}/* 2>/dev/null >/dev/null
+ if [[ "$?" != "0" ]]; then
+ echo ${_lang[38]}
+ exit 1
+ fi
+ if [[ $(echo "$1" | sed -n -r '/^[0-9]+$/p') == "" ]]; then
+ echo "${_lang[28]} '${0##*/} --list-videos'."
+ else
+ if [[ "$1" -gt "$(ls -d1 ${_dir_vid}/* | wc -l)" ]]; then
+ echo "${_lang[29]} $(ls -d1 ${_dir_vid}/* | wc -l)"
+ exit 1
+ else
+ t=$(ls ${_dir_vid}/${1}/* | wc -l)
+ let t=t-1
+ i=1
+ # ctrl_c
+ while :; do
+ feh --no-fehbg --bg-scale $(ls -1 ${_dir_vid}/${1}/* | sed -n "${i}p")
+ sleep 0.01
+ let i=i+1
+ [[ "${i}" -gt "${t}" ]] && i=1
+ done &
+ fi
+ fi
+}
+
+while [[ "$1" ]]; do
+ case "${1}" in
+ -h | --help) _usage ;;
+ -v | --version) echo "${0##*/} 0.0.1-beta" ;;
+ -s | --show) _show && exit ;;
+ -c | --count) _count ;;
+ -d | --display) _current ;;
+ -t | --time)
+ shift
+ if [[ $(echo "$1" | sed -n -r '/^[0-9]+$/p') == "" ]]; then
+ echo "${_lang[30]}"
+ else
+ _loop "$1"
+ fi
+ ;;
+ -q | --quit) _quit_loop ;;
+ -S | --set) export add=1 ;;
+ -a | --add)
+ shift
+ while [[ -f "${1}" ]]; do
+ if [[ "$(file ${1} | awk '{print $2}')" == @(PNG|JPEG) ]]; then
+ _resize_img $1
+ else
+ echo "${_lang[31]}: $1"
+ fi
+ shift
+ done
+ ;;
+ -u | --use)
+ shift
+
+ ls ${_dir_img}/*.jpg 2>/dev/null >/dev/null
+ if [[ "$?" != "0" ]]; then
+ echo "${_lang[37]}"
+ exit 1
+ fi
+
+ if [[ $(echo "$1" | sed -n -r '/^[0-9]+$/p') == "" ]]; then
+ echo "${_lang[32]} $(_last_wall)"
+ else
+ if [[ "$1" > "$(_last_wall)" ]]; then
+ echo "${_lang[32]} $(_last_wall)"
+ else
+ _set_img "$1"
+ fi
+ fi
+ ;;
+ -r | --remove-last) rm "${_dir_img}/wallpaper-$(_last_wall).jpg" ;;
+ --video | -V)
+ shift
+ [[ -z "$1" ]] && {
+ echo "${_lang[36]}"
+ exit 1
+ }
+ [[ $(file -b --mime-type ${1} | cut -d"/" -f1) != 'video' ]] && {
+ echo "${_lang[33]}"
+ exit 1
+ }
+ [[ $(file -b --mime-type ${1} | cut -d"/" -f2) != @(mp4|x-m4v) ]] && {
+ echo "${_lang[34]}"
+ exit 1
+ }
+ _video "${1}"
+ ;;
+ -L | --list-videos) _list_vid ;;
+ -I | --set-video)
+ shift
+ _set_video "${1}"
+ ;;
+ esac
+ shift
+done
diff --git a/ar/.local/bin/weath b/ar/.local/bin/weath
new file mode 100755
index 0000000..d013f6f
--- /dev/null
+++ b/ar/.local/bin/weath
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# Get the weather on the terminal. You can pass an alternative location as a parameter,
+# and/or use the 'cp' option to copy the forecast as plaintext to the clipboard.
+
+weatherreport="${XDG_CACHE_HOME:-${HOME}/.cache}/weatherreport"
+
+formats() {
+ [ "$MANPAGER" = "less -s" ] && pager=true || pager=false
+ [ "$pager" = "false" ] && {
+ export MANPAGER='less -s'
+ export LESS="R"
+ export LESS_TERMCAP_mb="$(printf '%b' '')"
+ export LESS_TERMCAP_md="$(printf '%b' '')"
+ export LESS_TERMCAP_me="$(printf '%b' '')"
+ export LESS_TERMCAP_so="$(printf '%b' '')"
+ export LESS_TERMCAP_se="$(printf '%b' '')"
+ export LESS_TERMCAP_us="$(printf '%b' '')"
+ export LESS_TERMCAP_ue="$(printf '%b' '')"
+ export LESSOPEN="| /usr/bin/highlight -O ansi %s 2>/dev/null"
+ }
+ setsid -f "$TERMINAL" less -S "$1" >/dev/null 2>&1
+ [ "$pager" = "false" ] && {
+ export MANPAGER="sh -c 'col -bx | bat -l man -p'"
+ export MANROFFOPT="-c"
+ }
+}
+
+if [ "$1" = 'cp' ]; then
+ # shellcheck disable=SC2015
+ [ -z "$2" ] && sed 's/\x1b\[[^m]*m//g' "$weatherreport" | xclip -selection clipboard &&
+ notify-send "Weather forecast for '${LOCATION:-$(head -n 1 "$weatherreport" | cut -d' ' -f3-)}' copied to clipboard." ||
+ { data="$(curl -sfm 5 "${WTTRURL:-wttr.in}/$2?T")" &&
+ notify-send "🌞Weather forecast for '$2' copied to clipboard." &&
+ echo "$data" | xclip -selection clipboard ||
+ notify-send '🥶Failed to get weather forecast!' 'Check your internet connection and the supplied location.'; }
+else
+ [ -n "$2" ] &&
+ notify-send "⛔Invalid option '$1'! The only valid option is 'cp'." &&
+ exit 1
+
+ # shellcheck disable=SC2015
+ [ -z "$1" ] && formats "$weatherreport" ||
+ data="$(curl -sfm 5 "${WTTRURL:-wttr.in}/$1")" && echo "$data" | formats - ||
+ notify-send '❗Failed to get weather forecast!' 'Check your internet connection and the supplied location.'
+fi
diff --git a/ar/.local/bin/whereami b/ar/.local/bin/whereami
new file mode 100755
index 0000000..0531376
--- /dev/null
+++ b/ar/.local/bin/whereami
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+arch=$(uname -m)
+os_type=$(uname | tr '[:upper:]' '[:lower:]')
+
+case "${os_type},${arch}" in
+"linux,arm" | "linux,arm64" | "linux,x86_64")
+ [ -f /etc/os-release ] &&
+ grep PRETTY_NAME /etc/os-release | cut -d'=' -f2- | sed 's/"//g' | awk -F' ' '{print $1}' | tr '[:upper:]' '[:lower:]' | sed 's/\(arch\|artix\)/ar/g'
+ ;;
+"darwin,arm64")
+ echo "m1"
+ ;;
+"darwin,x86_64")
+ echo "mac"
+ ;;
+"msys,"* | "cygwin,"* | "windows,"*)
+ echo "windows"
+ ;;
+*)
+ echo "Unsupported OS"
+ exit
+ ;;
+esac
diff --git a/ar/.local/bin/xdg-terminal-exec b/ar/.local/bin/xdg-terminal-exec
new file mode 100755
index 0000000..12b18ff
--- /dev/null
+++ b/ar/.local/bin/xdg-terminal-exec
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+"$TERMINAL" -e "$@"
diff --git a/ar/.local/bin/xinputconf b/ar/.local/bin/xinputconf
new file mode 100755
index 0000000..f469666
--- /dev/null
+++ b/ar/.local/bin/xinputconf
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+device_name="$(xinput list | grep '↳' | sed 's/.*↳ //g' | fzf)"
+[ -n "$device_name" ] && device_id="$(xinput list | grep -F "↳ $device_name" | sed -n 's/.*id=\([0-9]\+\).*/\1/p')" || exit
+
+show_matrix() {
+ printf "%s\n" "$(xinput list-props "$device_id" | awk '/Coordinate Transformation Matrix/{print $0}' | sed 's/^[[:space:]]*//g')"
+}
+
+# Function to set new speed and scrolling speed values
+set_speeds() {
+ printf "Set trackpoint speed: "
+ read -r speed
+
+ printf "Set trackpoint scrolling speed: "
+ read -r scroll_speed
+
+ prop_id=$(xinput list-props "$device_name" | awk '/Coordinate Transformation Matrix/ {match($0, /\(([0-9]+)\)/, a); print a[1]}')
+
+ if [ -n "$prop_id" ]; then
+ xinput set-prop "$device_name" "$prop_id" "$speed, 0, 0, 0, $scroll_speed, 0, 0, 0, 1"
+ show_matrix
+ else
+ printf "Property ID for Coordinate Transformation Matrix not found.\n" >&2
+ return 1
+ fi
+}
+
+case "$1" in
+-s)
+ set_speeds
+ ;;
+-l | "")
+ show_matrix
+ ;;
+*)
+ echo "Invalid option. Use -s to set speeds or -l to list the matrix."
+ ;;
+esac