summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mac/.config/lf/lfrc14
-rwxr-xr-xmac/.local/bin/bookmarks211
-rwxr-xr-xmac/.local/bin/browse88
-rwxr-xr-xmac/.local/bin/browserprofile108
-rwxr-xr-xmac/.local/bin/clonerepo61
-rwxr-xr-xmac/.local/bin/compiler84
-rwxr-xr-xmac/.local/bin/concatvideo56
-rwxr-xr-xmac/.local/bin/createsh104
-rw-r--r--mac/.local/bin/cron/README.md11
-rwxr-xr-xmac/.local/bin/cron/checkup16
-rwxr-xr-xmac/.local/bin/cron/crontog26
-rwxr-xr-xmac/.local/bin/cron/mediaup34
-rwxr-xr-xmac/.local/bin/cron/newsup15
-rwxr-xr-xmac/.local/bin/cutvideo41
-rwxr-xr-xmac/.local/bin/decodetitle17
-rwxr-xr-xmac/.local/bin/extract41
-rwxr-xr-xmac/.local/bin/fzffiles61
-rwxr-xr-xmac/.local/bin/fzffns74
-rwxr-xr-xmac/.local/bin/fzfpass88
-rwxr-xr-xmac/.local/bin/getkeys7
-rwxr-xr-xmac/.local/bin/gitfiles72
-rwxr-xr-xmac/.local/bin/gitopenbranch24
-rwxr-xr-xmac/.local/bin/gitstagedfiles11
-rwxr-xr-xmac/.local/bin/gitupdate86
-rwxr-xr-xmac/.local/bin/gpt26
-rwxr-xr-xmac/.local/bin/gracefulkill25
-rwxr-xr-xmac/.local/bin/ifinstalled12
-rwxr-xr-xmac/.local/bin/lastnvim77
-rwxr-xr-xmac/.local/bin/lfub24
-rwxr-xr-xmac/.local/bin/linkhandler31
-rwxr-xr-xmac/.local/bin/maimpick23
-rwxr-xr-xmac/.local/bin/mbackup14
-rwxr-xr-xmac/.local/bin/openfiles34
-rwxr-xr-xmac/.local/bin/opensessions37
-rwxr-xr-xmac/.local/bin/opentasktui27
-rwxr-xr-xmac/.local/bin/openurl16
-rwxr-xr-xmac/.local/bin/opout16
-rwxr-xr-xmac/.local/bin/otp54
-rwxr-xr-xmac/.local/bin/ovpn22
-rwxr-xr-xmac/.local/bin/peertubetorrent9
-rwxr-xr-xmac/.local/bin/rbackup187
-rwxr-xr-xmac/.local/bin/refreshbrowser42
-rwxr-xr-xmac/.local/bin/restartnvim25
-rwxr-xr-xmac/.local/bin/rgafiles120
-rwxr-xr-xmac/.local/bin/rssadd18
-rwxr-xr-xmac/.local/bin/rssget126
-rwxr-xr-xmac/.local/bin/sd22
-rwxr-xr-xmac/.local/bin/sessionizer36
-rwxr-xr-xmac/.local/bin/setfirmware22
-rwxr-xr-xmac/.local/bin/shortcuts86
-rwxr-xr-xmac/.local/bin/stw78
-rwxr-xr-xmac/.local/bin/syncdic33
-rwxr-xr-xmac/.local/bin/syncdot46
-rwxr-xr-xmac/.local/bin/tag49
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/annotate-with-new-note41
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/annotate-with-note41
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/cycle-priority35
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/cycle-tmux-projects31
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/decrease-priority15
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/git-issue-sync185
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/increase-priority60
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/lib-task-interop82
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/task-switch-context23
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/taskopen-annotation11
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/taskopen-line51
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/tasks-sync11
-rwxr-xr-xmac/.local/bin/task/taskwarrior-tui/toggle-review-label13
-rwxr-xr-xmac/.local/bin/tmuxcreate42
-rwxr-xr-xmac/.local/bin/tmuxcycleborder7
-rwxr-xr-xmac/.local/bin/tmuxdbussync15
-rwxr-xr-xmac/.local/bin/tmuxopen208
-rwxr-xr-xmac/.local/bin/tmuxtogglebar6
-rwxr-xr-xmac/.local/bin/tmuxtoggleterm11
-rwxr-xr-xmac/.local/bin/tordone10
-rwxr-xr-xmac/.local/bin/torwrap8
-rwxr-xr-xmac/.local/bin/transadd29
-rwxr-xr-xmac/.local/bin/vimwikitodo8
-rwxr-xr-xmac/.local/bin/vipy42
-rwxr-xr-xmac/.local/bin/weath46
-rwxr-xr-xmac/.local/bin/whereami24
80 files changed, 3735 insertions, 7 deletions
diff --git a/mac/.config/lf/lfrc b/mac/.config/lf/lfrc
index 06a13b0..661ccf3 100644
--- a/mac/.config/lf/lfrc
+++ b/mac/.config/lf/lfrc
@@ -95,10 +95,10 @@ cmd copyto ${{
done &&
notify-send "📋 File(s) copied." "File(s) copied to $destpath."
}}
-cmd yank-dirname $dirname -- "$f" | head -c-1 | xclip -i -selection clipboard
-cmd yank-path $printf '%s' "$fx" | xclip -i -selection clipboard
-cmd yank-basename $basename -a -- $fx | head -c-1 | xclip -i -selection clipboard
-cmd yank-basename-without-extension &basename -a -- $fx | cut -d. -f1 | head -c-1 | xclip -i -selection clipboard
+cmd yank-dirname $dirname -- "$f" | head -c-1 | pbcopy clipboard
+cmd yank-path $printf '%s' "$fx" | pbcopy clipboard
+cmd yank-basename $basename -a -- $fx | head -c-1 | pbcopy clipboard
+cmd yank-basename-without-extension &basename -a -- $fx | cut -d. -f1 | head -c-1 | pbcopy clipboard
# Create
cmd mkdir ${{ clear; tput cup $(($(tput lines)/3)); tput bold
@@ -489,14 +489,14 @@ map C compress
# Copy
# map yy %cp -ri -- $fs .; clear
-map Y $printf "%s" "$fx" | xclip -selection clipboard; clear; save-select
+map Y $printf "%s" "$fx" | pbcopy clipboard; clear; save-select
map yb yank-basename
map yd yank-dirname
map ye copyto; clear; save-select
-map yl $printf "%s" "$fx" | sed -E 's/^.+\[/https:\/\/piped.video\/watch?v=/' | sed -E 's/\]\..+//' | xclip -selection clipboard
+map yl $printf "%s" "$fx" | sed -E 's/^.+\[/https:\/\/piped.video\/watch?v=/' | sed -E 's/\]\..+//' | pbcopy clipboard
map yn yank-basename-without-extension
map yp yank-path
-map yt $printf "%s" "$fx" | sed -E 's/^.+\[/https:\/\/www.youtube.com\/watch?v=/' | sed -E 's/\]\..+//' | xclip -selection clipboard
+map yt $printf "%s" "$fx" | sed -E 's/^.+\[/https:\/\/www.youtube.com\/watch?v=/' | sed -E 's/\]\..+//' | pbcopy clipboard
map yy copy
# Create
diff --git a/mac/.local/bin/bookmarks b/mac/.local/bin/bookmarks
new file mode 100755
index 0000000..a892a33
--- /dev/null
+++ b/mac/.local/bin/bookmarks
@@ -0,0 +1,211 @@
+#!/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 " lynx : A text browser for World Wide Web"
+ echo " w3m : A text WWW browser, similar to lynx"
+ 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"
+}
+
+addurls() {
+ url=$(echo | dmenu -i -p "Enter a url: ")
+ [ -z "$url" ] && printf "Error: url must be provided\n\n" && exit 0
+
+ description=$(echo | dmenu -i -p "Enter a description of the url: ")
+ [ -z "$description" ] && echo "https://$url" >>~/.local/share/thesiah/snippets
+ [ -n "$description" ] && echo "$description https://$url" >>~/.local/share/thesiah/snippets
+}
+
+opentool() {
+ 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 lynx 2>/dev/null | grep -v "alias" -q && available_tools="$available_tools lynx"
+ command -v w3m 2>/dev/null | grep -v "alias" -q && available_tools="$available_tools w3m"
+ 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
+ ;;
+ lynx) setsid -f "$TERMINAL" -e lynx "$1" ;;
+ w3m) setsid -f "$TERMINAL" -e w3m "$1" ;;
+ *) echo "Invalid selection" >&2 && exit 1 ;;
+ esac
+}
+
+openwindow() {
+ if [ "$1" = "private" ]; then
+ case "$BROWSER" in
+ *qutebrowser*) "$BROWSER" --target private-window "$url" ;;
+ *firefox* | *librewolf*) "$BROWSER" --private-window "$url" ;;
+ esac
+ else
+ case "$BROWSER" in
+ *qutebrowser*) "$BROWSER" --target window "$url" ;;
+ *firefox* | *librewolf*) "$BROWSER" --new-window "$url" ;;
+ esac
+ 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)
+ db_path="$profile_dir/places.sqlite"
+ ;;
+ *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)
+ db_path="$profile_dir/places.sqlite"
+ ;;
+ *qutebrowser*)
+ profile_dir="${XDG_DATA_HOME:-${HOME}/.local/share}/qutebrowser"
+ db_path="$profile_dir/history.sqlite"
+ ;;
+ *) echo "Default browser path is needed." && exit ;;
+ esac
+
+ tmp_file="$(mktemp)"
+ 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"
+
+ cols=$((${COLUMNS:-90} / 3))
+ case "$1" in
+ *bookmark*)
+ case "$BROWSER" in
+ qutebrowser) bookmarks -o ;;
+ *firefox* | *librewolf*)
+ sqlite_query="
+ SELECT substr(b.title, 1, $cols) || ' | ' || 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.title;
+ "
+ ;;
+ *qutebrowser*) geturls && openwindow && exit ;;
+ esac
+ ;;
+ *history*)
+ case "$BROWSER" in
+ *qutebrowser*)
+ sqlite_query="
+ SELECT substr(h.title, 1, $cols) || ' | ' || h.url AS bookmark
+ FROM CompletionHistory h
+ ORDER BY h.last_atime DESC
+ LIMIT 100;
+ "
+ ;;
+ *firefox* | *librewolf*)
+ 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
+ ;;
+ 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
+ opentool "$choice"
+ fi
+ else
+ exit
+ fi
+ rm "$tmp_file"
+}
+
+geturls() {
+ urls=$(cat ~/.config/qutebrowser/quickmarks ~/.config/qutebrowser/bookmarks/urls ~/.local/share/thesiah/snippets ~/.local/share/thesiah/urls 2>/dev/null)
+ choice=$(echo "$urls" | grep -v -e '^#' -e '^$' | awk '
+ {
+ if ($1 ~ /^https?:\/\//) { alias = substr($0, index($0, $2)) } # Case 2: URL first
+ else { alias = substr($0, 1, length($0) - length($NF) - 1) } # Case 1: URL last
+ print alias
+ }' | dmenu -i -l 50 -p "Choose an alias $1:")
+
+ [ -z "$choice" ] && exit
+ url=$(echo "$urls" | grep -v -e '^#' -e '^$' | awk -v choice="$choice" '
+ {
+ if ($1 ~ /^https?:\/\//) { url = $1; alias = substr($0, index($0, $2)) } # Case 2
+ else { alias = substr($0, 1, length($0) - length($NF) - 1); url = $NF } # Case 1
+ if (alias == choice) print url
+ }')
+}
+
+copytoclipboard() {
+ if command -v xclip >/dev/null 2>&1; then
+ printf "%s" "$url" | xclip -selection clipboard
+ elif command -v clipcopy >/dev/null 2>&1; then
+ printf "%s" "$url" | clipcopy
+ elif command -v xsel >/dev/null 2>&1; then
+ printf "%s" "$url" | xsel --clipboard --input
+ else
+ echo "Clipboard utility not found. Install xclip, clipcopy, or xsel." >&2
+ exit 1
+ fi
+ notify-send "'$choice' copied in clipbaord" "$url"
+}
+
+[ $# -eq 0 ] && usage && exit 1
+
+while getopts "abchopstv" opt; do
+ case $opt in
+ a) addurls ;;
+ b) openinbrowser "bookmark" ;;
+ c) geturls "to copy" && copytoclipboard ;;
+ o) geturls "to open in $BROWSER" && openwindow ;;
+ p) geturls "to open in private $BROWSER" && openwindow private ;;
+ s) openinbrowser "history" ;;
+ t) geturls "to type under cursor" && xdotool type "$url" ;;
+ v) openinbrowser "private bookmark" ;;
+ h | *) usage && exit 0 ;;
+ esac
+done
diff --git a/mac/.local/bin/browse b/mac/.local/bin/browse
new file mode 100755
index 0000000..976e2a4
--- /dev/null
+++ b/mac/.local/bin/browse
@@ -0,0 +1,88 @@
+#!/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 (bing, duckduckgo, google, naver, yahoo, 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 | naver | 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="
+ ;;
+ naver)
+ base_url="https://search.naver.com/search.naver?query="
+ ;;
+ 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/mac/.local/bin/browserprofile b/mac/.local/bin/browserprofile
new file mode 100755
index 0000000..c66f183
--- /dev/null
+++ b/mac/.local/bin/browserprofile
@@ -0,0 +1,108 @@
+#!/bin/sh
+
+# Define the profile paths
+work_profile="$USER.work"
+home_profile="$USER.default"
+tmux_profile="$USER.tmux"
+
+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 " tmux: Sets the home profile ($tmux_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"
+ echo " ${0##*/} -f -t # Set the tmux 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"
+ ;;
+ -t | --tmux | tmux)
+ update_profiles_ini "$browser" "$tmux_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/mac/.local/bin/clonerepo b/mac/.local/bin/clonerepo
new file mode 100755
index 0000000..cc1e23f
--- /dev/null
+++ b/mac/.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/mac/.local/bin/compiler b/mac/.local/bin/compiler
new file mode 100755
index 0000000..bf8b443
--- /dev/null
+++ b/mac/.local/bin/compiler
@@ -0,0 +1,84 @@
+#!/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.def.h files: (For suckless utils) recompiles and installs program.
+# all others: run `sent` to show a presentation
+
+file=$(readlink -f "$1")
+ext="${file##*.}"
+dir=${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; }
+}
+
+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() { [ -f "${dir}/Makefile" ] && make || cc "$file" -o "$base" && "$base"; }
+
+case "${ext}" in
+[0-9]) preconv "$file" | refer -PS -e | groff -mandoc -T pdf >"$base".pdf ;;
+apl) apl -f "$file" ;;
+c) compilec ;;
+cob) cobc -x -o "$base" "$file" && "$base" ;;
+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) fennel --compile "$file" >"$base".lua ;;
+go) go run "$file" ;;
+java) javac "$file" && echo "${base##*/}" | xargs java ;;
+js) node "$file" ;;
+m) octave "$file" ;;
+md)
+ pandoc "$file" -s --pdf-engine=xelatex -V geometry:margin=2cm -o "${base}.pdf" || {
+ [ -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" ;;
+rink) rink -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) latexmk ;;
+tcl) tclsh "$file" ;;
+vim*) vint "$file" ;;
+*) chmod +x "$file" && sed 1q "$file" | grep "^#!/" | sed "s/^#!//" | xargs -r -I % "$file" ;;
+esac
diff --git a/mac/.local/bin/concatvideo b/mac/.local/bin/concatvideo
new file mode 100755
index 0000000..93a2060
--- /dev/null
+++ b/mac/.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/mac/.local/bin/createsh b/mac/.local/bin/createsh
new file mode 100755
index 0000000..53c2a32
--- /dev/null
+++ b/mac/.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/mac/.local/bin/cron/README.md b/mac/.local/bin/cron/README.md
new file mode 100644
index 0000000..fa0c354
--- /dev/null
+++ b/mac/.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/mac/.local/bin/cron/checkup b/mac/.local/bin/cron/checkup
new file mode 100755
index 0000000..801d406
--- /dev/null
+++ b/mac/.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/mac/.local/bin/cron/crontog b/mac/.local/bin/cron/crontog
new file mode 100755
index 0000000..70e7cf1
--- /dev/null
+++ b/mac/.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/mac/.local/bin/cron/mediaup b/mac/.local/bin/cron/mediaup
new file mode 100755
index 0000000..85935a2
--- /dev/null
+++ b/mac/.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/mac/.local/bin/cron/newsup b/mac/.local/bin/cron/newsup
new file mode 100755
index 0000000..346ec75
--- /dev/null
+++ b/mac/.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/mac/.local/bin/cutvideo b/mac/.local/bin/cutvideo
new file mode 100755
index 0000000..3220008
--- /dev/null
+++ b/mac/.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/mac/.local/bin/decodetitle b/mac/.local/bin/decodetitle
new file mode 100755
index 0000000..e7a553c
--- /dev/null
+++ b/mac/.local/bin/decodetitle
@@ -0,0 +1,17 @@
+#!/usr/bin/env python3
+
+import html
+import sys
+import xml.etree.ElementTree as ET
+
+text_input = sys.stdin.read()
+root = ET.fromstring(text_input)
+
+# RSS 2.0: <channel><item><title>
+for item in root.findall(".//item"):
+ title = item.find("title")
+ if title is not None and title.text:
+ title.text = html.unescape(title.text)
+
+# re-serialize
+sys.stdout.buffer.write(ET.tostring(root, encoding="utf-8", xml_declaration=True))
diff --git a/mac/.local/bin/extract b/mac/.local/bin/extract
new file mode 100755
index 0000000..b352a70
--- /dev/null
+++ b/mac/.local/bin/extract
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+# Default behavior: Extract archive into new directory
+# Behavior with `-c` option: Extract contents into current directory
+
+while getopts "hc" o; do case "${o}" in
+ c) extracthere="True" ;;
+ *) printf 'Options:\n -c: Extract archive into current directory rather than a new one.\n' && exit ;;
+ esac done
+
+if [ -z "$extracthere" ]; then
+ archive="$(readlink -f "$*")" &&
+ directory=${archive%.*} &&
+ mkdir -p "$directory" &&
+ cd "$directory" || exit
+else
+ archive="$(readlink -f "$(echo "$*" | cut -d' ' -f2)")"
+fi
+
+[ "$archive" = "" ] && printf 'Give archive to extract as argument.\n' && exit
+
+if [ -f "$archive" ]; then
+ case "$archive" in
+ *.tar.bz2 | *.tar.xz | *.tbz2) tar xvjf "$archive" ;;
+ *.tar.gz | *.tgz) tar xvzf "$archive" ;;
+ *.lzma) unlzma "$archive" ;;
+ *.bz2) bunzip2 "$archive" ;;
+ *.rar) unrar x -ad "$archive" ;;
+ *.gz) gunzip "$archive" ;;
+ *.tar) tar xvf "$archive" ;;
+ *.zip | *.jar | *.war) unzip "$archive" ;;
+ *.Z) uncompress "$archive" ;;
+ *.7z) 7z x "$archive" ;;
+ *.xz) unxz "$archive" ;;
+ *.exe) cabextract "$archive" ;;
+ *.ace) unace x "$archive" ;;
+ *) printf "extract: '%s' - unknown archive method\\n" "$archive" ;;
+ esac
+else
+ printf 'File "%s" not found.\n' "$archive"
+fi
diff --git a/mac/.local/bin/fzffiles b/mac/.local/bin/fzffiles
new file mode 100755
index 0000000..d092596
--- /dev/null
+++ b/mac/.local/bin/fzffiles
@@ -0,0 +1,61 @@
+#!/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 "target=\$(readlink -f {}) &&
+ if [ -d \"\$target\" ]; then
+ exa --color=always --long --all --header --icons --git \"\$target\"
+ elif [ -f \"\$target\" ]; then
+ bat --color=always --style=header,grid --line-range=:500 \"\$target\"
+ else
+ file -h {}
+ 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
+
+if [ -d "$files" ]; then
+ absolute_files=$(realpath $files)
+ if echo "$absolute_files" | while read -r file; do file --mime-type "$file" | grep -q 'video/'; done; then
+ mpv --quiet --loop $absolute_files
+ else
+ openfiles "$absolute_files"
+ fi
+else
+ openfiles "$files"
+fi
diff --git a/mac/.local/bin/fzffns b/mac/.local/bin/fzffns
new file mode 100755
index 0000000..c919723
--- /dev/null
+++ b/mac/.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/mac/.local/bin/fzfpass b/mac/.local/bin/fzfpass
new file mode 100755
index 0000000..5190f8e
--- /dev/null
+++ b/mac/.local/bin/fzfpass
@@ -0,0 +1,88 @@
+#!/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 (no symbol) | ^r: Rename | ^s: Generate (symbol) | ^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-s:execute(if [ -z {} ]; then read -p \"Can't generate empty password. Press enter to continue...\"; else pass generate -i {} < /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"
+)
+
+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*)
+ printf "%s" "$password" | pbcopy # Use pbcopy on macOS
+ ;;
+ Linux*)
+ printf "%s" "$password" | xclip -selection clipboard # Use xclip on Linux
+ ;;
+ *)
+ echo "Unsupported operating system"
+ ;;
+ esac
+ sleep 0.1
+ fi
+fi
diff --git a/mac/.local/bin/getkeys b/mac/.local/bin/getkeys
new file mode 100755
index 0000000..492b056
--- /dev/null
+++ b/mac/.local/bin/getkeys
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# Print available keys from thesiah
+[ -n "$1" ] && cat "${XDG_DATA_HOME:-${HOME}/.local/share}"/thesiah/keys/"$1" 2>/dev/null && exit
+keys_dir="${XDG_DATA_HOME:-${HOME}/.local/share}/thesiah/keys"
+selected_file=$(du -a "$keys_dir" | cut -f2- | sed "s|$keys_dir/||" | fzf)
+[ -n "$selected_file" ] && cat "$keys_dir/$selected_file"
diff --git a/mac/.local/bin/gitfiles b/mac/.local/bin/gitfiles
new file mode 100755
index 0000000..510b032
--- /dev/null
+++ b/mac/.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/mac/.local/bin/gitopenbranch b/mac/.local/bin/gitopenbranch
new file mode 100755
index 0000000..56c8d12
--- /dev/null
+++ b/mac/.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/mac/.local/bin/gitstagedfiles b/mac/.local/bin/gitstagedfiles
new file mode 100755
index 0000000..1cdd902
--- /dev/null
+++ b/mac/.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/mac/.local/bin/gitupdate b/mac/.local/bin/gitupdate
new file mode 100755
index 0000000..4fed27e
--- /dev/null
+++ b/mac/.local/bin/gitupdate
@@ -0,0 +1,86 @@
+#!/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
+
+# Save current directory and change to the Git repo root
+repo_root=$(git rev-parse --show-toplevel || echo ".")
+cd "$repo_root" || exit
+
+# Get the current Git branch name
+branch=$(git symbolic-ref -q HEAD | sed -e 's|^refs/heads/||')
+
+# 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
+
+if [ "$repo_root" = "${PASSWORD_STORE_DIR:-${HOME}/.password-store}" ]; then
+ pass git remote | xargs -L1 pass git push --all
+else
+ git pull --rebase origin "$branch"
+ git remote | xargs -L1 git push --all
+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/mac/.local/bin/gpt b/mac/.local/bin/gpt
new file mode 100755
index 0000000..fb12148
--- /dev/null
+++ b/mac/.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('$'), readfile('$tmpfile'))" \
+ -c "normal iKeep your responses short and simple, when asked to provide command, provide only one. Do not provide explanations unless explicitly asked for. When you need to find out something about my system or the environment, rather than asking, provide a one-line command that I can execute and which output would give you the desired information, make sure to provide only one command per answer and wait for me to execute it. When providing commands that save files, make sure to use the /home/decoder/dev path. When providing commands or code always enclose them in tripple backticks with appropriate scope, bash, python etc." \
+ -c "normal 2o" \
+ -c "call timer_start(100, {-> feedkeys('A', 'n')})"
+
+# Remove the temporary file after usage
+rm "$tmpfile"
diff --git a/mac/.local/bin/gracefulkill b/mac/.local/bin/gracefulkill
new file mode 100755
index 0000000..918ab79
--- /dev/null
+++ b/mac/.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/mac/.local/bin/ifinstalled b/mac/.local/bin/ifinstalled
new file mode 100755
index 0000000..d0bd26b
--- /dev/null
+++ b/mac/.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/mac/.local/bin/lastnvim b/mac/.local/bin/lastnvim
new file mode 100755
index 0000000..b1ab6c9
--- /dev/null
+++ b/mac/.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/mac/.local/bin/lfub b/mac/.local/bin/lfub
new file mode 100755
index 0000000..c999412
--- /dev/null
+++ b/mac/.local/bin/lfub
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+# This is a wrapper script for lf that allows it to create image previews with
+# ueberzugpp. This works in concert with the lf configuration file and the
+# lf-cleaner script.
+
+set -e
+
+cleanup() {
+ exec 3>&-
+ rm "$FIFO_UEBERZUGPP"
+}
+
+if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ]; then
+ lf "$@"
+else
+ [ ! -d "$HOME/.cache/lf" ] && mkdir -p "$HOME/.cache/lf"
+ export FIFO_UEBERZUGPP="$HOME/.cache/lf/ueberzugpp-$$"
+ mkfifo "$FIFO_UEBERZUGPP"
+ ueberzugpp layer -s -p json <"$FIFO_UEBERZUGPP" &
+ exec 3>"$FIFO_UEBERZUGPP"
+ trap cleanup HUP INT QUIT TERM PWR EXIT
+ lf "$@" 3>&-
+fi
diff --git a/mac/.local/bin/linkhandler b/mac/.local/bin/linkhandler
new file mode 100755
index 0000000..6e007bc
--- /dev/null
+++ b/mac/.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.thesiah.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:-st}" -e "$EDITOR" "$url" >/dev/null 2>&1 || setsid -f "${BROWSER}" "$url" >/dev/null 2>&1
+ ;;
+esac
diff --git a/mac/.local/bin/maimpick b/mac/.local/bin/maimpick
new file mode 100755
index 0000000..06d9092
--- /dev/null
+++ b/mac/.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/mac/.local/bin/mbackup b/mac/.local/bin/mbackup
new file mode 100755
index 0000000..71856b1
--- /dev/null
+++ b/mac/.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/mac/.local/bin/openfiles b/mac/.local/bin/openfiles
new file mode 100755
index 0000000..5b4f7e2
--- /dev/null
+++ b/mac/.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;' "$@" -c 'wincmd t'
+ ;;
+3)
+ ${EDITOR:-nvim} -O "$1" -c 'wincmd j' -c "silent! vsplit $2" -c "silent! split $3" -c 'wincmd t'
+ ;;
+4)
+ ${EDITOR:-nvim} -O "$1" -c "silent! vsplit $2" -c "silent! split $3" -c 'wincmd h' -c "silent! split $4" -c 'wincmd t'
+ ;;
+*)
+ ${EDITOR:-nvim} "$@"
+ ;;
+esac
diff --git a/mac/.local/bin/opensessions b/mac/.local/bin/opensessions
new file mode 100755
index 0000000..6f9f236
--- /dev/null
+++ b/mac/.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/mac/.local/bin/opentasktui b/mac/.local/bin/opentasktui
new file mode 100755
index 0000000..1db6bef
--- /dev/null
+++ b/mac/.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/mac/.local/bin/openurl b/mac/.local/bin/openurl
new file mode 100755
index 0000000..cd096e1
--- /dev/null
+++ b/mac/.local/bin/openurl
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+ext="${1##*.}"
+mpvfiles="avi flv mkv mov mpeg mpg mp4 rmvb webm wmv w3m ogv ts 3gp"
+nsxivfiles="png jpg jpeg jpe gif bmp tiff tif"
+wgetfiles="mp3 flac opus wav aac wma m4a mp3?source=feed"
+
+if echo $mpvfiles | grep -w $ext >/dev/null; then
+ nohup mpv --loop --quiet "$1" >/dev/null 2>&1 &
+elif echo $nsxivfiles | grep -w $ext >/dev/null; then
+ nohup nsxiv "$1" >/dev/null 2>&1 &
+elif echo $wgetfiles | grep -w $ext >/dev/null; then
+ nohup wget "$1" >/dev/null 2>&1 &
+else
+ nohup "$BROWSER" "$1" >/dev/null 2>&1 &
+fi
diff --git a/mac/.local/bin/opout b/mac/.local/bin/opout
new file mode 100755
index 0000000..70bc2cb
--- /dev/null
+++ b/mac/.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/mac/.local/bin/otp b/mac/.local/bin/otp
new file mode 100755
index 0000000..be80265
--- /dev/null
+++ b/mac/.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/mac/.local/bin/ovpn b/mac/.local/bin/ovpn
new file mode 100755
index 0000000..a89c357
--- /dev/null
+++ b/mac/.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/mac/.local/bin/peertubetorrent b/mac/.local/bin/peertubetorrent
new file mode 100755
index 0000000..4d8f630
--- /dev/null
+++ b/mac/.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/mac/.local/bin/rbackup b/mac/.local/bin/rbackup
new file mode 100755
index 0000000..d2dc610
--- /dev/null
+++ b/mac/.local/bin/rbackup
@@ -0,0 +1,187 @@
+#!/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
+ cd "$THESIAH_WWW" || exit 1
+ [ -d "$thesiah_path" ] || hugo --cleanDestinationDir
+ rsync -vrazPlu --delete "$thesiah_path/" "$THESIAH_SERVER:/var/www/thesiah/" >/dev/null 2>&1 && rm -rf "$thesiah_path"
+ 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/mac/.local/bin/refreshbrowser b/mac/.local/bin/refreshbrowser
new file mode 100755
index 0000000..4b1812d
--- /dev/null
+++ b/mac/.local/bin/refreshbrowser
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+######################################################################
+# @author : Gavin Jaeger-Freeborn (gavinfreeborn@gmail.com)
+# @file : test.sh
+# @created : Wed 25 Mar 2020 05:49:29 PM
+#
+# @description : simple xdotool script used to reload browsers
+######################################################################
+
+browserclass="${BROWSER:-firefox}"
+
+#=== FUNCTION ======================================================
+# NAME: moveto
+# DESCRIPTION: move to the center of the specified window id
+#=====================================================================
+moveto() {
+ geom=$(xdotool getwindowgeometry "${1}")
+ local=$(echo "${geom}" | awk NR==2 | cut -d: -f 2 | cut -d\( -f 1)
+ dimentions=$(echo "${geom}" | awk NR==3 | cut -d: -f 2 | cut -d\( -f 1 | cut -d, -f1)
+ x=$(echo "${local}" | cut -d, -f1)
+ y=$(echo "${local}" | cut -d, -f2)
+ w=$(echo "${dimentions}" | cut -dx -f1)
+ h=$(echo "${dimentions}" | cut -dx -f2)
+
+ xdotool mousemove $((x + w / 2)) $((y + h / 2))
+}
+
+# Save the current window
+cwid=$(xdotool getwindowfocus)
+# Find the browser window
+twid=$(xdotool search --onlyvisible --class "${browserclass}")
+[ -z "${twid}" ] && notify-send 'failed to determine browser window' && exit
+[ -z "${cwid}" ] && notify-send 'failed to determine current window' && exit
+
+moveto "${twid}"
+
+xdotool key F5
+
+moveto "${cwid}"
+
+# vim: set tw=78 ts=2 et sw=2 sr:
diff --git a/mac/.local/bin/restartnvim b/mac/.local/bin/restartnvim
new file mode 100755
index 0000000..ab040ab
--- /dev/null
+++ b/mac/.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/mac/.local/bin/rgafiles b/mac/.local/bin/rgafiles
new file mode 100755
index 0000000..e8b5e72
--- /dev/null
+++ b/mac/.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() {
+ 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' {}" "$case_flag" "$*")
+ rga_output=$(rga --follow --no-ignore --hidden --text --max-count=1 ${case_flag:+$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
+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) case_flag="--case-sensitive" ;; # Case-sensitive
+ i) 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 "$case_flag" "$@"
+fi
diff --git a/mac/.local/bin/rssadd b/mac/.local/bin/rssadd
new file mode 100755
index 0000000..f78a538
--- /dev/null
+++ b/mac/.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 $2" >>"$rssfile" && notify-send "RSS feed added."
+fi
diff --git a/mac/.local/bin/rssget b/mac/.local/bin/rssget
new file mode 100755
index 0000000..f51ecb3
--- /dev/null
+++ b/mac/.local/bin/rssget
@@ -0,0 +1,126 @@
+#!/bin/bash
+
+# Searches the website for RSS feeds and adds them to newsboat url list. Can
+# also find hidden RSS feeds on various websites, namely Youtube, Reddit,
+# Vimeo, Github, Gitlab and Medium. Gets site url as $1 or (if not present)
+# from X clipboard. Gets tags as $2. If it finds more than one feed, calls
+# dmenu for the user to choose which one to add. I have bound it to a keyboard
+# shortcut so i copy a site link and easily add its feed to the reader.
+
+# Inspired by and based on the logic of this extension:
+# https://github.com/shevabam/get-rss-feed-url-extension
+
+# This script requires rssadd to add feeds to the list.
+
+getlink() {
+ local url="$1"
+ feeds="$(curl -s "$url" | grep -Ex '.*type=.*(rss|rdf|atom).*' | sed 's/ //g')"
+ url="$(echo $url | sed 's|^\(https://[^/]*/\).*|\1|')"
+
+ for rsspath in $feeds; do
+ rsspath="$(echo $rsspath | sed -n "s|.*href=['\"]\([^'\"]*\)['\"].*|\1|p")"
+ if echo "$rsspath" | grep "http" >/dev/null; then
+ link="$rsspath"
+ elif echo "$rsspath" | grep -E "^/" >/dev/null; then
+ link="$url$(echo $rsspath | sed 's|^/||')"
+ else
+ link="$url$rsspath"
+ fi
+ echo $link
+ done
+}
+
+getRedditRss() {
+ echo "${1%/}.rss"
+}
+
+getYoutubeRss() {
+ local url="$1"
+ path=$(echo "$url" | sed -e 's|^http[s]*://||')
+ case "$path" in
+ *"/channel/"*) channel_id="$(echo $path | sed -r 's|.*channel/([^/]*).*|\1|')" && feed="https://www.youtube.com/feeds/videos.xml?channel_id=${channel_id}" ;;
+ *"/c/"* | *"/user/"*)
+ feed=$(wget -q "$url" -O tmp_rssget_yt &&
+ sed -n 's|.*\("rssUrl":"[^"]*\).*|\1|; p' tmp_rssget_yt |
+ grep rssUrl |
+ sed 's|"rssUrl":"||')
+ ;;
+ *)
+ channel_id="$(curl -sA "Mozilla/5.0" "$url" | grep -Po '"rssUrl":"https://www.youtube.com/feeds/videos.xml\?channel_id=\K(UC[0-9A-Za-z_-]+)')"
+ feed="https://www.youtube.com/feeds/videos.xml?channel_id=${channel_id}"
+ ;;
+ esac
+ echo "$feed"
+}
+
+getVimeoRss() {
+ local url="$1"
+ if echo "$url" | grep -q "/videos$"; then
+ feed_url=$(echo "$url" | sed 's/\/videos$//' | sed 's/\/$/\/rss/')
+ else
+ feed_url="${url}/videos/rss"
+ fi
+ echo "$feed_url"
+}
+
+getGithubRss() {
+ local url="${1%/}"
+ if echo $url | grep -E "github.com/[^/]*/[a-zA-Z0-9].*" >/dev/null; then
+ echo "${url}/commits.atom"
+ echo "${url}/releases.atom"
+ echo "${url}/tags.atom"
+ elif echo $url | grep -E "github.com/[^/]*(/)" >/dev/null; then
+ echo "${url}.atom"
+ fi
+}
+
+getGitlabRss() {
+ local url="${1%/}"
+ echo "${url}.atom"
+}
+
+getMediumRss() {
+ echo $1 | sed 's|/tag/|/feed/|'
+}
+
+if [ -n "$1" ]; then
+ url="$1"
+else
+ url="$(xclip -selection clipboard -o)"
+ [ -z "$url" ] && echo "usage: $0 url 'tag1 tag2 tag3'" && exit 1
+fi
+
+tags="$2"
+
+declare -a list=()
+
+yt_regex="^(http(s)?://)?((w){3}\.)?(youtube\.com|invidio\.us|invidious\.flokinet\.to|invidious\.materialio\.us|iv\.datura\.network|invidious\.perennialte\.ch|invidious\.fdn\.fr|invidious\.private\.coffee|invidious\.protokolla\.fi|invidious\.privacyredirect\.com|yt\.artemislena\.eu|yt\.drgnz\.club|invidious\.incogniweb\.net|yewtu\.be|inv\.tux\.pizza|invidious\.reallyaweso\.me|iv\.melmac\.space|inv\.us\.projectsegfau\.lt|inv\.nadeko\.net|invidious\.darkness\.services|invidious\.jing\.rocks|invidious\.privacydev\.net|inv\.in\.projectsegfau\.lt|invidious\.drgns\.space)/(@|(channel|user|c)).+"
+reddit_regex="^(http(s)?://)?((w){3}\.)?reddit\.com.*"
+vimeo_regex="^(http(s)?://)?((w){3}.)?vimeo\.com.*"
+if echo $url | grep -Ex "$yt_regex" >/dev/null; then
+ list="$(getYoutubeRss "$url")"
+ channel_name="${url##*@}"
+ [ -z "$tags" ] && tags="\"~$channel_name\" Youtube"
+elif echo $url | grep -Ex "$reddit_regex" >/dev/null; then
+ list="$(getRedditRss "$url")"
+# vimeo actually works with getlink
+elif echo $url | grep -E "$vimeo_regex" >/dev/null; then
+ list="$(getVimeoRss "$url")"
+elif echo $url | grep -E "github.com" >/dev/null; then
+ list="$(getGithubRss "$url")"
+ repo="${url##*/}"
+ author="${url%/*}"
+ author="${author##*/}"
+ [ -z "$tags" ] && tags="\"~$author's $repo\" Git"
+# gitlab also works with getlink
+elif echo $url | grep -E "gitlab.com/[a-zA-Z0-9].*" >/dev/null; then
+ list="$(getGitlabRss "$url")"
+elif echo $url | grep -E "medium.com/tag" >/dev/null; then
+ list="$(getMediumRss "$url")"
+else
+ list="$(getlink "$url")"
+fi
+
+[ "$(echo "$list" | wc -l)" -eq 1 ] && chosen_link="$list" || chosen_link=$(printf '%s\n' "${list[@]}" | dmenu -p "Choose a feed:")
+ifinstalled rssadd && rssadd "$chosen_link" "$tags"
+echo "$chosen_link" "$tags"
diff --git a/mac/.local/bin/sd b/mac/.local/bin/sd
new file mode 100755
index 0000000..8c6f6a4
--- /dev/null
+++ b/mac/.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 ' ')" 2>/dev/null)
+ 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/mac/.local/bin/sessionizer b/mac/.local/bin/sessionizer
new file mode 100755
index 0000000..48f3bbf
--- /dev/null
+++ b/mac/.local/bin/sessionizer
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+sessionizer() {
+ path="$(
+ sesh list --icons | fzf-tmux \
+ -p 80%,70% --no-sort --cycle --ignore-case --ansi --border=sharp --multi --reverse \
+ --border-label "╢ TheSiahxyz ╟" \
+ --header "^a all ^e sesh ^f zoxide ^g git ^t tmux ^u staged files ^x tmux kill M-cr open in editor ^/ help" \
+ --prompt "💡 " \
+ --bind "ctrl-a:change-prompt(💡 )+reload(sesh list -d -H --icons)" \
+ --bind "ctrl-e:change-prompt(📑 )+reload(sesh list -c -H --icons)" \
+ --bind "ctrl-f:change-prompt(🔎 )+reload(sesh list -d -z -H --icons)" \
+ --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 --icons)" \
+ --bind "ctrl-v:execute($EDITOR ${0})+abort" \
+ --bind 'ctrl-x:execute(tmux kill-session -t "$(echo {} | cut -d" " -f2-)")+reload(sesh list --icons)' \
+ --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:45%' \
+ --preview 'sesh preview {}'
+ )" 2>/dev/null
+
+ case "$path" in
+ ^*) sessionizer ;;
+ *) sesh connect "$path" >/dev/null 2>&1 && exit ;;
+ esac
+}
+
+sessionizer
diff --git a/mac/.local/bin/setfirmware b/mac/.local/bin/setfirmware
new file mode 100755
index 0000000..326d14b
--- /dev/null
+++ b/mac/.local/bin/setfirmware
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+drives=$(lsblk -n -r -o NAME,SIZE,MOUNTPOINT | grep -E --color=always '3[0-9].*M')
+drive=$(echo "$drives" | awk '{print $1}' | head -n1)
+[ -z "$drive" ] && exit
+echo "$drives"
+drive="/dev/$drive"
+printf "\n%s\n" "Target drive: $drive"
+firmwarepath="${XDG_DOTFILES_DIR:-${HOME}/.dotfiles}/$(whereami)/.config/glove80"
+firmware="$(find "$firmwarepath" "${XDG_DOWNLOAD_DIR:-${HOME}/Downloads}" -type f -iname '*.uf2' -printf '%T@ %p\n' | sort -n | cut -d' ' -f2- | fzf --prompt "Choose a firmware: ")"
+[ -f "$firmware" ] || exit
+[ -f "$firmwarepath/$(basename "$firmware")" ] || mv "$firmware" "$firmwarepath/"
+mp="/media/$USER/firmware"
+[ -d "$mp" ] || mkdir -p "$mp"
+sudo -A mount "$drive" "$mp"
+sudo -A cp "$firmwarepath/$(basename "$firmware")" "$mp/"
+[ -f "$firmwarepath/$(basename "$firmware")" ] || exit 1
+ls -la "$mp"
+[ -f "$mp/$(basename "$firmware")" ] || exit 1
+sudo -A umount "$mp"
+[ -e "$mp" ] && [ ! -s "$mp" ] && sudo -A rm -rf "$mp" || exit 1
+echo "Done!"
diff --git a/mac/.local/bin/shortcuts b/mac/.local/bin/shortcuts
new file mode 100755
index 0000000..118becb
--- /dev/null
+++ b/mac/.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/$USER/\",\"~/\",\"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/$USER/\",\"~/\",\"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/$USER/\",\"~/\",\"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/$USER/\",\"~/\",\"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/$USER/\",\"~/\",\"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/$USER\", \"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/$USER\", \"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/mac/.local/bin/stw b/mac/.local/bin/stw
new file mode 100755
index 0000000..f6a5c85
--- /dev/null
+++ b/mac/.local/bin/stw
@@ -0,0 +1,78 @@
+#!/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 "global" | 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
+}
+
+# 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
+stow_package "$targetdir" "$resolve" && stow_package "global" "$resolve" || exit
+
+# Link for profile
+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"
+
+# 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/mac/.local/bin/syncdic b/mac/.local/bin/syncdic
new file mode 100755
index 0000000..c125f42
--- /dev/null
+++ b/mac/.local/bin/syncdic
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+dicfile="${XDG_DATA_HOME:-${HOME}/.local/share}/thesiah/dic"
+dest="${XDG_DATA_HOME:-${HOME}/.local/share}/dic"
+
+[ -d "$dest" ] || mkdir -p "$dest"
+
+while read -r url; do
+ [ -z "$url" ] && continue
+ filename=$(basename "$url")
+ filepath="${dest}/${filename}"
+ tarpath=$(echo "$filepath" | sed 's/.zip//;s/.tar.gz//;s/.tgz//;s/.tar.bz2//;s/.tbz2//;s/.tar.xz//;s/.txz//')
+
+ # Download only if the file does not exist
+ if [ ! -d "$tarpath" ]; then
+ curl -L "$url" -o "$filepath"
+ case "$filename" in
+ *.zip)
+ unzip -o "$filepath" -d "$dest"
+ ;;
+ *.tar.gz | *.tgz)
+ tar -xzf "$filepath" -C "$dest"
+ ;;
+ *.tar.bz2 | *.tbz2)
+ tar -xjf "$filepath" -C "$dest"
+ ;;
+ *.tar.xz | *.txz)
+ tar -xJf "$filepath" -C "$dest"
+ ;;
+ esac
+ rm -f "$filepath"
+ fi
+done <"$dicfile"
diff --git a/mac/.local/bin/syncdot b/mac/.local/bin/syncdot
new file mode 100755
index 0000000..5e560e9
--- /dev/null
+++ b/mac/.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/mac/.local/bin/tag b/mac/.local/bin/tag
new file mode 100755
index 0000000..8abce0f
--- /dev/null
+++ b/mac/.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/mac/.local/bin/task/taskwarrior-tui/annotate-with-new-note b/mac/.local/bin/task/taskwarrior-tui/annotate-with-new-note
new file mode 100755
index 0000000..3c67d24
--- /dev/null
+++ b/mac/.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/mac/.local/bin/task/taskwarrior-tui/annotate-with-note b/mac/.local/bin/task/taskwarrior-tui/annotate-with-note
new file mode 100755
index 0000000..c744314
--- /dev/null
+++ b/mac/.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/mac/.local/bin/task/taskwarrior-tui/cycle-priority b/mac/.local/bin/task/taskwarrior-tui/cycle-priority
new file mode 100755
index 0000000..43bc91e
--- /dev/null
+++ b/mac/.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/mac/.local/bin/task/taskwarrior-tui/cycle-tmux-projects b/mac/.local/bin/task/taskwarrior-tui/cycle-tmux-projects
new file mode 100755
index 0000000..360dc20
--- /dev/null
+++ b/mac/.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/mac/.local/bin/task/taskwarrior-tui/decrease-priority b/mac/.local/bin/task/taskwarrior-tui/decrease-priority
new file mode 100755
index 0000000..b2b0508
--- /dev/null
+++ b/mac/.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/mac/.local/bin/task/taskwarrior-tui/git-issue-sync b/mac/.local/bin/task/taskwarrior-tui/git-issue-sync
new file mode 100755
index 0000000..da01bc4
--- /dev/null
+++ b/mac/.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/mac/.local/bin/task/taskwarrior-tui/increase-priority b/mac/.local/bin/task/taskwarrior-tui/increase-priority
new file mode 100755
index 0000000..26a3d53
--- /dev/null
+++ b/mac/.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/mac/.local/bin/task/taskwarrior-tui/lib-task-interop b/mac/.local/bin/task/taskwarrior-tui/lib-task-interop
new file mode 100755
index 0000000..04e60ac
--- /dev/null
+++ b/mac/.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/mac/.local/bin/task/taskwarrior-tui/task-switch-context b/mac/.local/bin/task/taskwarrior-tui/task-switch-context
new file mode 100755
index 0000000..0b40141
--- /dev/null
+++ b/mac/.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/mac/.local/bin/task/taskwarrior-tui/taskopen-annotation b/mac/.local/bin/task/taskwarrior-tui/taskopen-annotation
new file mode 100755
index 0000000..3b292e8
--- /dev/null
+++ b/mac/.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/mac/.local/bin/task/taskwarrior-tui/taskopen-line b/mac/.local/bin/task/taskwarrior-tui/taskopen-line
new file mode 100755
index 0000000..379a2af
--- /dev/null
+++ b/mac/.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/mac/.local/bin/task/taskwarrior-tui/tasks-sync b/mac/.local/bin/task/taskwarrior-tui/tasks-sync
new file mode 100755
index 0000000..06334c4
--- /dev/null
+++ b/mac/.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/mac/.local/bin/task/taskwarrior-tui/toggle-review-label b/mac/.local/bin/task/taskwarrior-tui/toggle-review-label
new file mode 100755
index 0000000..9133c30
--- /dev/null
+++ b/mac/.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/mac/.local/bin/tmuxcreate b/mac/.local/bin/tmuxcreate
new file mode 100755
index 0000000..5fb5ef3
--- /dev/null
+++ b/mac/.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/mac/.local/bin/tmuxcycleborder b/mac/.local/bin/tmuxcycleborder
new file mode 100755
index 0000000..ad2a430
--- /dev/null
+++ b/mac/.local/bin/tmuxcycleborder
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+case "$(tmux show-option -gqv pane-border-status)" in
+"off") tmux set-option -g pane-border-status top ;;
+"top") tmux set-option -g pane-border-status bottom ;;
+"bottom") tmux set-option -g pane-border-status off ;;
+esac
diff --git a/mac/.local/bin/tmuxdbussync b/mac/.local/bin/tmuxdbussync
new file mode 100755
index 0000000..d6da872
--- /dev/null
+++ b/mac/.local/bin/tmuxdbussync
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+fcitx5_pid=$(pgrep -f fcitx5 | head -n1)
+
+if [ -z "$fcitx5_pid" ]; then
+ echo "echo 'No terminal process found matching: fcitx5' >&2"
+ exit 1
+fi
+
+fcitx5_dbus=$(tr '\0' '\n' </proc/"$fcitx5_pid"/environ | grep DBUS_SESSION_BUS_ADDRESS)
+fcitx5_dbus="${fcitx5_dbus#DBUS_SESSION_BUS_ADDRESS=}"
+
+if [ "$DBUS_SESSION_BUS_ADDRESS" != "$fcitx5_dbus" ]; then
+ echo "export DBUS_SESSION_BUS_ADDRESS=$fcitx5_dbus"
+fi
diff --git a/mac/.local/bin/tmuxopen b/mac/.local/bin/tmuxopen
new file mode 100755
index 0000000..5362215
--- /dev/null
+++ b/mac/.local/bin/tmuxopen
@@ -0,0 +1,208 @@
+#!/bin/sh
+
+wrapper() {
+ 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 ""
+ 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"
+ }
+
+ get_fzf_output() {
+ # Create temporary files to store search state
+ tmp_files="/tmp/search-files-$$"
+ tmp_content_query="/tmp/search-content-query-$$"
+
+ # Cleanup on exit
+ trap "rm -f $tmp_files $tmp_content_query" EXIT
+
+ rg_fixed_bind="ctrl-g:transform-query(
+ echo {q} > $tmp_content_query;
+ echo {q}
+ )+reload(
+ rm -f $tmp_files;
+ rg --line-number --follow --fixed-strings --hidden --no-heading --color=always --smart-case --glob '!**/.git/**' --glob '!node_modules/**' {q} 2>/dev/null || true
+ )"
+ file_bind="ctrl-f:transform-query(
+ current_query={q};
+ if [ ! -s $tmp_content_query ]; then
+ echo \$current_query > $tmp_content_query;
+ fi;
+ rg --hidden --follow --files-with-matches --no-messages --glob '!**/.git/**' --glob '!node_modules/**' -- \$current_query > $tmp_files;
+ )+reload(
+ if [ -s $tmp_files ]; then
+ if [ -n {q} ]; then
+ grep -i -- {q} $tmp_files || true;
+ else
+ cat $tmp_files;
+ fi | while IFS= read -r file; do
+ if [ -f \"\$file\" ]; then
+ echo \"\$file:1\";
+ fi;
+ done;
+ else
+ echo 'No matching files found';
+ fi
+ )"
+ if command -v fd >/dev/null 2>&1; then
+ dir_bind="ctrl-d:change-prompt(📁 )+reload(fd --follow --type d --hidden --absolute-path --color never --exclude .git --exclude node_modules --search-path \"\$PWD\")"
+ else
+ dir_bind="ctrl-d:change-prompt(📁 )+reload(find \"\$PWD\" -L -type d -name node_modules -prune -o -name .git -prune -o -type d -print)"
+ fi
+
+ rg --line-number --follow --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 || bat --color=always {} 2>/dev/null || ls -la {} 2>/dev/null || echo "Preview is not available."' \
+ --preview-window 'right,55%,border-bottom,+{2}+3/3,~3' \
+ --bind "$file_bind" \
+ --bind "$rg_fixed_bind" \
+ --bind "$dir_bind" \
+ --bind 'ctrl-c:abort' \
+ --header "^f filenames | ^g contents | ^d directories" \
+ --prompt "🔎 "
+ }
+
+ set_nvim_search_variable() {
+ raw_output="$1"
+ tmp_content_query="/tmp/search-content-query-$$"
+ if [ -f "$tmp_content_query" ]; then
+ saved_query=$(cat "$tmp_content_query" 2>/dev/null)
+ if [ -n "$saved_query" ]; then
+ export NVIM_SEARCH_REGISTRY="$saved_query"
+ return
+ fi
+ fi
+ 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\"'"
+ tmux send-keys -t "$pane" "$nvim_cmd" C-m
+ }
+
+ # Parse command line arguments
+ while [ "$#" -gt 0 ]; do
+ case "$1" in
+ -h | --help)
+ usage
+ exit 0
+ ;;
+ *)
+ echo "Unknown option: $1" >&2
+ usage
+ exit 1
+ ;;
+ esac
+ done
+
+ raw_output=$(get_fzf_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)
+
+ if [ -z "$selections" ]; then
+ echo "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}')
+ if [ -f "$file" ]; then
+ files="$files $file"
+ lines="$lines $line"
+ count=$((count + 1))
+ else
+ echo "File not found: $file"
+ fi
+ done <<EOF
+$selections
+EOF
+
+ if [ "$count" -eq 0 ]; then
+ echo "No valid files selected"
+ exit 0
+ fi
+
+ if [ "$count" -eq 1 ]; then
+ open_files_in_nvim "$(tmux display-message -p '#P')" 1
+ else
+ window_name="$(date +%s)"
+ tmux new-window -n "$window_name"
+ case "$count" in
+ 2)
+ 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)
+ 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
+ ;;
+ *)
+ 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
+}
+
+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
+
+wrapper "$@"
diff --git a/mac/.local/bin/tmuxtogglebar b/mac/.local/bin/tmuxtogglebar
new file mode 100755
index 0000000..2ae045e
--- /dev/null
+++ b/mac/.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/mac/.local/bin/tmuxtoggleterm b/mac/.local/bin/tmuxtoggleterm
new file mode 100755
index 0000000..f21f833
--- /dev/null
+++ b/mac/.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/mac/.local/bin/tordone b/mac/.local/bin/tordone
new file mode 100755
index 0000000..4e097a0
--- /dev/null
+++ b/mac/.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/mac/.local/bin/torwrap b/mac/.local/bin/torwrap
new file mode 100755
index 0000000..54f5c0e
--- /dev/null
+++ b/mac/.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:-st}" -n stig -e stig
+pkill -RTMIN+22 "${STATUSBAR:-dwmblocks}"
diff --git a/mac/.local/bin/transadd b/mac/.local/bin/transadd
new file mode 100755
index 0000000..ffd8ded
--- /dev/null
+++ b/mac/.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/mac/.local/bin/vimwikitodo b/mac/.local/bin/vimwikitodo
new file mode 100755
index 0000000..99758f4
--- /dev/null
+++ b/mac/.local/bin/vimwikitodo
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+nvim +'setlocal nonumber norelativenumber noruler signcolumn=no' \
+ +'set laststatus=0 showtabline=0' \
+ +'silent lua vim.diagnostic.disable()' \
+ +'silent lua vim.api.nvim_clear_autocmds({ event = { "CursorHold", "CursorHoldI" }, buffer = 0 })' \
+ +'lua vim.defer_fn(function() local ok, lualine = pcall(require, "lualine"); if ok then lualine.hide() end end, 100)' \
+ ~/.local/share/vimwiki/todo.md
diff --git a/mac/.local/bin/vipy b/mac/.local/bin/vipy
new file mode 100755
index 0000000..8e0e3cb
--- /dev/null
+++ b/mac/.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/mac/.local/bin/weath b/mac/.local/bin/weath
new file mode 100755
index 0000000..d013f6f
--- /dev/null
+++ b/mac/.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/mac/.local/bin/whereami b/mac/.local/bin/whereami
new file mode 100755
index 0000000..0531376
--- /dev/null
+++ b/mac/.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