summaryrefslogtreecommitdiff
path: root/debian/.local/bin
diff options
context:
space:
mode:
authorTheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com>2025-12-24 13:54:03 +0900
committerTheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com>2025-12-24 13:54:03 +0900
commit28e8bdf7f8286bd431b7f3b709e79f3827b31469 (patch)
tree85b44eff6da4d8443198fb6e04dfb6ee55244588 /debian/.local/bin
parent8470ff001befcfd0f626dea69a9e76d43aee0511 (diff)
updates
Diffstat (limited to 'debian/.local/bin')
-rwxr-xr-xdebian/.local/bin/albumsplit45
-rwxr-xr-xdebian/.local/bin/bash-preexec380
-rwxr-xr-xdebian/.local/bin/bookmarks211
-rwxr-xr-xdebian/.local/bin/browse88
-rwxr-xr-xdebian/.local/bin/browserprofile108
-rwxr-xr-xdebian/.local/bin/clonerepo61
-rwxr-xr-xdebian/.local/bin/compiler84
-rwxr-xr-xdebian/.local/bin/concatvideo56
-rwxr-xr-xdebian/.local/bin/createsh104
-rw-r--r--debian/.local/bin/cron/README.md11
-rwxr-xr-xdebian/.local/bin/cron/crontog26
-rwxr-xr-xdebian/.local/bin/cron/mediaup34
-rwxr-xr-xdebian/.local/bin/cron/newsup15
-rwxr-xr-xdebian/.local/bin/cutvideo41
-rwxr-xr-xdebian/.local/bin/cwmx3
-rwxr-xr-xdebian/.local/bin/decodetitle17
-rwxr-xr-xdebian/.local/bin/displayselect95
-rwxr-xr-xdebian/.local/bin/dmenubrowse44
-rwxr-xr-xdebian/.local/bin/dmenudelmusic44
-rwxr-xr-xdebian/.local/bin/dmenugithub25
-rwxr-xr-xdebian/.local/bin/dmenuhandler24
-rwxr-xr-xdebian/.local/bin/dmenuman4
-rwxr-xr-xdebian/.local/bin/dmenumountcifs19
-rwxr-xr-xdebian/.local/bin/dmenupass6
-rwxr-xr-xdebian/.local/bin/dmenurecord220
-rwxr-xr-xdebian/.local/bin/dmenusmbadd58
-rwxr-xr-xdebian/.local/bin/dmenusmbdel55
-rwxr-xr-xdebian/.local/bin/dmenuunicode18
-rwxr-xr-xdebian/.local/bin/dmenuupgrade115
-rwxr-xr-xdebian/.local/bin/dmenuvirt28
-rwxr-xr-xdebian/.local/bin/dvdburn46
-rwxr-xr-xdebian/.local/bin/ecrypt39
-rwxr-xr-xdebian/.local/bin/emojiupdate65
-rwxr-xr-xdebian/.local/bin/ethwifi31
-rwxr-xr-xdebian/.local/bin/extract41
-rwxr-xr-xdebian/.local/bin/extractkeys164
-rwxr-xr-xdebian/.local/bin/fzffiles99
-rwxr-xr-xdebian/.local/bin/fzffns74
-rwxr-xr-xdebian/.local/bin/fzfpass88
-rwxr-xr-xdebian/.local/bin/getbib71
-rwxr-xr-xdebian/.local/bin/getcomproot9
-rwxr-xr-xdebian/.local/bin/getkeys7
-rwxr-xr-xdebian/.local/bin/gitfiles72
-rwxr-xr-xdebian/.local/bin/gitopenbranch24
-rwxr-xr-xdebian/.local/bin/gitstagedfiles11
-rwxr-xr-xdebian/.local/bin/gitupdate85
-rwxr-xr-xdebian/.local/bin/gpt26
-rwxr-xr-xdebian/.local/bin/gracefulkill25
-rwxr-xr-xdebian/.local/bin/hugow70
-rwxr-xr-xdebian/.local/bin/iconupdate19
-rwxr-xr-xdebian/.local/bin/ifinstalled13
-rwxr-xr-xdebian/.local/bin/lastfiles77
-rwxr-xr-xdebian/.local/bin/lfub24
-rwxr-xr-xdebian/.local/bin/linkhandler31
-rwxr-xr-xdebian/.local/bin/maimpick23
-rwxr-xr-xdebian/.local/bin/mbackup14
-rwxr-xr-xdebian/.local/bin/monitorbright24
-rwxr-xr-xdebian/.local/bin/mounter212
-rwxr-xr-xdebian/.local/bin/mpdmenu113
-rwxr-xr-xdebian/.local/bin/mpvplay210
-rwxr-xr-xdebian/.local/bin/noisereduce81
-rwxr-xr-xdebian/.local/bin/openfiles34
-rwxr-xr-xdebian/.local/bin/opensessions37
-rwxr-xr-xdebian/.local/bin/opentasktui27
-rwxr-xr-xdebian/.local/bin/openurl16
-rwxr-xr-xdebian/.local/bin/opout16
-rwxr-xr-xdebian/.local/bin/otp54
-rwxr-xr-xdebian/.local/bin/ovpn22
-rwxr-xr-xdebian/.local/bin/partlabel90
-rwxr-xr-xdebian/.local/bin/passmenu238
-rwxr-xr-xdebian/.local/bin/pauseallmpv10
-rwxr-xr-xdebian/.local/bin/peertubetorrent9
-rwxr-xr-xdebian/.local/bin/podentr7
-rwxr-xr-xdebian/.local/bin/ppts123
-rwxr-xr-xdebian/.local/bin/qndl130
-rwxr-xr-xdebian/.local/bin/queueandnotify14
-rwxr-xr-xdebian/.local/bin/rbackup187
-rwxr-xr-xdebian/.local/bin/refreshbrowser42
-rwxr-xr-xdebian/.local/bin/remapd8
-rwxr-xr-xdebian/.local/bin/remaps69
-rwxr-xr-xdebian/.local/bin/restartnvim25
-rwxr-xr-xdebian/.local/bin/rgafiles120
-rwxr-xr-xdebian/.local/bin/rotdir12
-rwxr-xr-xdebian/.local/bin/rssadd18
-rwxr-xr-xdebian/.local/bin/rssget126
-rwxr-xr-xdebian/.local/bin/schedule22
-rwxr-xr-xdebian/.local/bin/screenshotactivewindow37
-rwxr-xr-xdebian/.local/bin/sd22
-rwxr-xr-xdebian/.local/bin/sessionizer36
-rwxr-xr-xdebian/.local/bin/setbg71
-rwxr-xr-xdebian/.local/bin/setfirmware22
-rwxr-xr-xdebian/.local/bin/setlock23
-rwxr-xr-xdebian/.local/bin/setmonitor35
-rwxr-xr-xdebian/.local/bin/shortcuts93
-rwxr-xr-xdebian/.local/bin/slider132
-rwxr-xr-xdebian/.local/bin/sshadd31
-rwxr-xr-xdebian/.local/bin/stw78
-rwxr-xr-xdebian/.local/bin/syncdic33
-rwxr-xr-xdebian/.local/bin/syncdot46
-rwxr-xr-xdebian/.local/bin/synctime22
-rwxr-xr-xdebian/.local/bin/sysact36
-rwxr-xr-xdebian/.local/bin/tablet39
-rwxr-xr-xdebian/.local/bin/tag49
-rwxr-xr-xdebian/.local/bin/task/taskwarrior-tui/annotate-with-new-note41
-rwxr-xr-xdebian/.local/bin/task/taskwarrior-tui/annotate-with-note41
-rwxr-xr-xdebian/.local/bin/task/taskwarrior-tui/cycle-priority35
-rwxr-xr-xdebian/.local/bin/task/taskwarrior-tui/cycle-tmux-projects31
-rwxr-xr-xdebian/.local/bin/task/taskwarrior-tui/decrease-priority15
-rwxr-xr-xdebian/.local/bin/task/taskwarrior-tui/git-issue-sync185
-rwxr-xr-xdebian/.local/bin/task/taskwarrior-tui/increase-priority60
-rwxr-xr-xdebian/.local/bin/task/taskwarrior-tui/lib-task-interop82
-rwxr-xr-xdebian/.local/bin/task/taskwarrior-tui/task-switch-context23
-rwxr-xr-xdebian/.local/bin/task/taskwarrior-tui/taskopen-annotation11
-rwxr-xr-xdebian/.local/bin/task/taskwarrior-tui/taskopen-line51
-rwxr-xr-xdebian/.local/bin/task/taskwarrior-tui/tasks-sync11
-rwxr-xr-xdebian/.local/bin/task/taskwarrior-tui/toggle-review-label13
-rwxr-xr-xdebian/.local/bin/td-toggle11
-rwxr-xr-xdebian/.local/bin/timer14
-rwxr-xr-xdebian/.local/bin/timer_14
-rwxr-xr-xdebian/.local/bin/timezones21
-rwxr-xr-xdebian/.local/bin/tmuxcreate42
-rwxr-xr-xdebian/.local/bin/tmuxcycleborder7
-rwxr-xr-xdebian/.local/bin/tmuxdbussync15
-rwxr-xr-xdebian/.local/bin/tmuxopen208
-rwxr-xr-xdebian/.local/bin/tmuxtogglebar6
-rwxr-xr-xdebian/.local/bin/tmuxtoggleterm11
-rwxr-xr-xdebian/.local/bin/toggleoutput47
-rwxr-xr-xdebian/.local/bin/tordone10
-rwxr-xr-xdebian/.local/bin/torwrap8
-rwxr-xr-xdebian/.local/bin/transadd29
-rwxr-xr-xdebian/.local/bin/unix26
-rwxr-xr-xdebian/.local/bin/unmounter57
-rwxr-xr-xdebian/.local/bin/vimwikitodo8
-rwxr-xr-xdebian/.local/bin/vipy42
-rwxr-xr-xdebian/.local/bin/wallset295
-rwxr-xr-xdebian/.local/bin/weath46
-rwxr-xr-xdebian/.local/bin/webcam48
-rwxr-xr-xdebian/.local/bin/whereami24
-rwxr-xr-xdebian/.local/bin/xdg-terminal-exec3
-rwxr-xr-xdebian/.local/bin/xdotmouse13
-rwxr-xr-xdebian/.local/bin/xinputconf39
-rwxr-xr-xdebian/.local/bin/xkeysbin0 -> 15816 bytes
-rwxr-xr-xdebian/.local/bin/ylog359
143 files changed, 7990 insertions, 0 deletions
diff --git a/debian/.local/bin/albumsplit b/debian/.local/bin/albumsplit
new file mode 100755
index 0000000..684bf93
--- /dev/null
+++ b/debian/.local/bin/albumsplit
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+# Requires ffmpeg
+
+[ ! -f "$2" ] && printf "The first file should be the audio, the second should be the timecodes.\\n" && exit
+
+printf "Enter the album/book title:"
+read -r booktitle
+printf "Enter the artist/author:"
+read -r author
+printf "Enter the publication year:"
+read -r year
+
+inputaudio="$1"
+ext="${1##*.}"
+
+# Get a safe file name from the book.
+escbook="$(echo "$booktitle" | iconv -c -f UTF-8 -t ASCII//TRANSLIT | tr -d '[:punct:]' | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | sed "s/-\+/-/g;s/\(^-\|-\$\)//g")"
+
+! mkdir -p "$escbook" &&
+ echo "Do you have write access in this directory?" &&
+ exit 1
+
+# Get the total number of tracks from the number of lines.
+total="$(wc -l <"$2")"
+
+cmd="ffmpeg -i \"$inputaudio\" -nostdin -y"
+
+while read -r x; do
+ end="$(echo "$x" | cut -d' ' -f1)"
+ file="$escbook/$(printf "%.2d" "$track")-$esctitle.$ext"
+ if [ -n "$start" ]; then
+ cmd="$cmd -metadata artist=\"$author\" -metadata title=\"$title\" -metadata album=\"$booktitle\" -metadata year=\"$year\" -metadata track=\"$track\" -metadata total=\"$total\" -ss \"$start\" -to \"$end\" -vn -c:a copy \"$file\" "
+ fi
+ title="$(echo "$x" | cut -d' ' -f2-)"
+ esctitle="$(echo "$title" | iconv -c -f UTF-8 -t ASCII//TRANSLIT | tr -d '[:punct:]' | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | sed "s/-\+/-/g;s/\(^-\|-\$\)//g")"
+ track="$((track + 1))"
+ start="$end"
+done <"$2"
+
+# Last track must be added out of the loop.
+file="$escbook/$(printf "%.2d" "$track")-$esctitle.$ext"
+cmd="$cmd -metadata artist=\"$author\" -metadata title=\"$title\" -metadata album=\"$booktitle\" -metadata year=\"$year\" -metadata track=\"$track\" -ss \"$start\" -vn -c copy \"$file\""
+
+eval "$cmd"
diff --git a/debian/.local/bin/bash-preexec b/debian/.local/bin/bash-preexec
new file mode 100755
index 0000000..7222568
--- /dev/null
+++ b/debian/.local/bin/bash-preexec
@@ -0,0 +1,380 @@
+#!/bin/bash
+
+# bash-preexec.sh -- Bash support for ZSH-like 'preexec' and 'precmd' functions.
+# https://github.com/rcaloras/bash-preexec
+#
+#
+# 'preexec' functions are executed before each interactive command is
+# executed, with the interactive command as its argument. The 'precmd'
+# function is executed before each prompt is displayed.
+#
+# Author: Ryan Caloras (ryan@bashhub.com)
+# Forked from Original Author: Glyph Lefkowitz
+#
+# V0.5.0
+#
+
+# General Usage:
+#
+# 1. Source this file at the end of your bash profile so as not to interfere
+# with anything else that's using PROMPT_COMMAND.
+#
+# 2. Add any precmd or preexec functions by appending them to their arrays:
+# e.g.
+# precmd_functions+=(my_precmd_function)
+# precmd_functions+=(some_other_precmd_function)
+#
+# preexec_functions+=(my_preexec_function)
+#
+# 3. Consider changing anything using the DEBUG trap or PROMPT_COMMAND
+# to use preexec and precmd instead. Preexisting usages will be
+# preserved, but doing so manually may be less surprising.
+#
+# Note: This module requires two Bash features which you must not otherwise be
+# using: the "DEBUG" trap, and the "PROMPT_COMMAND" variable. If you override
+# either of these after bash-preexec has been installed it will most likely break.
+
+# Tell shellcheck what kind of file this is.
+# shellcheck shell=bash
+
+# Make sure this is bash that's running and return otherwise.
+# Use POSIX syntax for this line:
+if [ -z "${BASH_VERSION-}" ]; then
+ return 1
+fi
+
+# We only support Bash 3.1+.
+# Note: BASH_VERSINFO is first available in Bash-2.0.
+if [[ -z "${BASH_VERSINFO-}" ]] || ((BASH_VERSINFO[0] < 3 || (BASH_VERSINFO[0] == 3 && BASH_VERSINFO[1] < 1))); then
+ return 1
+fi
+
+# Avoid duplicate inclusion
+if [[ -n "${bash_preexec_imported:-}" || -n "${__bp_imported:-}" ]]; then
+ return 0
+fi
+bash_preexec_imported="defined"
+
+# WARNING: This variable is no longer used and should not be relied upon.
+# Use ${bash_preexec_imported} instead.
+# shellcheck disable=SC2034
+__bp_imported="${bash_preexec_imported}"
+
+# Should be available to each precmd and preexec
+# functions, should they want it. $? and $_ are available as $? and $_, but
+# $PIPESTATUS is available only in a copy, $BP_PIPESTATUS.
+# TODO: Figure out how to restore PIPESTATUS before each precmd or preexec
+# function.
+__bp_last_ret_value="$?"
+BP_PIPESTATUS=("${PIPESTATUS[@]}")
+__bp_last_argument_prev_command="$_"
+
+__bp_inside_precmd=0
+__bp_inside_preexec=0
+
+# Initial PROMPT_COMMAND string that is removed from PROMPT_COMMAND post __bp_install
+__bp_install_string=$'__bp_trap_string="$(trap -p DEBUG)"\ntrap - DEBUG\n__bp_install'
+
+# Fails if any of the given variables are readonly
+# Reference https://stackoverflow.com/a/4441178
+__bp_require_not_readonly() {
+ local var
+ for var; do
+ if ! (unset "$var" 2>/dev/null); then
+ echo "bash-preexec requires write access to ${var}" >&2
+ return 1
+ fi
+ done
+}
+
+# Remove ignorespace and or replace ignoreboth from HISTCONTROL
+# so we can accurately invoke preexec with a command from our
+# history even if it starts with a space.
+__bp_adjust_histcontrol() {
+ local histcontrol
+ histcontrol="${HISTCONTROL:-}"
+ histcontrol="${histcontrol//ignorespace/}"
+ # Replace ignoreboth with ignoredups
+ if [[ "$histcontrol" == *"ignoreboth"* ]]; then
+ histcontrol="ignoredups:${histcontrol//ignoreboth/}"
+ fi
+ export HISTCONTROL="$histcontrol"
+}
+
+# This variable describes whether we are currently in "interactive mode";
+# i.e. whether this shell has just executed a prompt and is waiting for user
+# input. It documents whether the current command invoked by the trace hook is
+# run interactively by the user; it's set immediately after the prompt hook,
+# and unset as soon as the trace hook is run.
+__bp_preexec_interactive_mode=""
+
+# These arrays are used to add functions to be run before, or after, prompts.
+declare -a precmd_functions
+declare -a preexec_functions
+
+# Trims leading and trailing whitespace from $2 and writes it to the variable
+# name passed as $1
+__bp_trim_whitespace() {
+ local var=${1:?} text=${2:-}
+ text="${text#"${text%%[![:space:]]*}"}" # remove leading whitespace characters
+ text="${text%"${text##*[![:space:]]}"}" # remove trailing whitespace characters
+ printf -v "$var" '%s' "$text"
+}
+
+# Trims whitespace and removes any leading or trailing semicolons from $2 and
+# writes the resulting string to the variable name passed as $1. Used for
+# manipulating substrings in PROMPT_COMMAND
+__bp_sanitize_string() {
+ local var=${1:?} text=${2:-} sanitized
+ __bp_trim_whitespace sanitized "$text"
+ sanitized=${sanitized%;}
+ sanitized=${sanitized#;}
+ __bp_trim_whitespace sanitized "$sanitized"
+ printf -v "$var" '%s' "$sanitized"
+}
+
+# This function is installed as part of the PROMPT_COMMAND;
+# It sets a variable to indicate that the prompt was just displayed,
+# to allow the DEBUG trap to know that the next command is likely interactive.
+__bp_interactive_mode() {
+ __bp_preexec_interactive_mode="on"
+}
+
+# This function is installed as part of the PROMPT_COMMAND.
+# It will invoke any functions defined in the precmd_functions array.
+__bp_precmd_invoke_cmd() {
+ # Save the returned value from our last command, and from each process in
+ # its pipeline. Note: this MUST be the first thing done in this function.
+ # BP_PIPESTATUS may be unused, ignore
+ # shellcheck disable=SC2034
+
+ __bp_last_ret_value="$?" BP_PIPESTATUS=("${PIPESTATUS[@]}")
+
+ # Don't invoke precmds if we are inside an execution of an "original
+ # prompt command" by another precmd execution loop. This avoids infinite
+ # recursion.
+ if ((__bp_inside_precmd > 0)); then
+ return
+ fi
+ local __bp_inside_precmd=1
+
+ # Invoke every function defined in our function array.
+ local precmd_function
+ for precmd_function in "${precmd_functions[@]}"; do
+
+ # Only execute this function if it actually exists.
+ # Test existence of functions with: declare -[Ff]
+ if type -t "$precmd_function" 1>/dev/null; then
+ __bp_set_ret_value "$__bp_last_ret_value" "$__bp_last_argument_prev_command"
+ # Quote our function invocation to prevent issues with IFS
+ "$precmd_function"
+ fi
+ done
+
+ __bp_set_ret_value "$__bp_last_ret_value"
+}
+
+# Sets a return value in $?. We may want to get access to the $? variable in our
+# precmd functions. This is available for instance in zsh. We can simulate it in bash
+# by setting the value here.
+__bp_set_ret_value() {
+ return ${1:+"$1"}
+}
+
+__bp_in_prompt_command() {
+
+ local prompt_command_array IFS=$'\n;'
+ read -rd '' -a prompt_command_array <<<"${PROMPT_COMMAND[*]:-}"
+
+ local trimmed_arg
+ __bp_trim_whitespace trimmed_arg "${1:-}"
+
+ local command trimmed_command
+ for command in "${prompt_command_array[@]:-}"; do
+ __bp_trim_whitespace trimmed_command "$command"
+ if [[ "$trimmed_command" == "$trimmed_arg" ]]; then
+ return 0
+ fi
+ done
+
+ return 1
+}
+
+# This function is installed as the DEBUG trap. It is invoked before each
+# interactive prompt display. Its purpose is to inspect the current
+# environment to attempt to detect if the current command is being invoked
+# interactively, and invoke 'preexec' if so.
+__bp_preexec_invoke_exec() {
+
+ # Save the contents of $_ so that it can be restored later on.
+ # https://stackoverflow.com/questions/40944532/bash-preserve-in-a-debug-trap#40944702
+ __bp_last_argument_prev_command="${1:-}"
+ # Don't invoke preexecs if we are inside of another preexec.
+ if ((__bp_inside_preexec > 0)); then
+ return
+ fi
+ local __bp_inside_preexec=1
+
+ # Checks if the file descriptor is not standard out (i.e. '1')
+ # __bp_delay_install checks if we're in test. Needed for bats to run.
+ # Prevents preexec from being invoked for functions in PS1
+ if [[ ! -t 1 && -z "${__bp_delay_install:-}" ]]; then
+ return
+ fi
+
+ if [[ -n "${COMP_POINT:-}" || -n "${READLINE_POINT:-}" ]]; then
+ # We're in the middle of a completer or a keybinding set up by "bind
+ # -x". This obviously can't be an interactively issued command.
+ return
+ fi
+ if [[ -z "${__bp_preexec_interactive_mode:-}" ]]; then
+ # We're doing something related to displaying the prompt. Let the
+ # prompt set the title instead of me.
+ return
+ else
+ # If we're in a subshell, then the prompt won't be re-displayed to put
+ # us back into interactive mode, so let's not set the variable back.
+ # In other words, if you have a subshell like
+ # (sleep 1; sleep 2)
+ # You want to see the 'sleep 2' as a set_command_title as well.
+ if [[ 0 -eq "${BASH_SUBSHELL:-}" ]]; then
+ __bp_preexec_interactive_mode=""
+ fi
+ fi
+
+ if __bp_in_prompt_command "${BASH_COMMAND:-}"; then
+ # If we're executing something inside our prompt_command then we don't
+ # want to call preexec. Bash prior to 3.1 can't detect this at all :/
+ __bp_preexec_interactive_mode=""
+ return
+ fi
+
+ local this_command
+ this_command=$(
+ export LC_ALL=C
+ HISTTIMEFORMAT='' builtin history 1 | sed '1 s/^ *[0-9][0-9]*[* ] //'
+ )
+
+ # Sanity check to make sure we have something to invoke our function with.
+ if [[ -z "$this_command" ]]; then
+ return
+ fi
+
+ # Invoke every function defined in our function array.
+ local preexec_function
+ local preexec_function_ret_value
+ local preexec_ret_value=0
+ for preexec_function in "${preexec_functions[@]:-}"; do
+
+ # Only execute each function if it actually exists.
+ # Test existence of function with: declare -[fF]
+ if type -t "$preexec_function" 1>/dev/null; then
+ __bp_set_ret_value "${__bp_last_ret_value:-}"
+ # Quote our function invocation to prevent issues with IFS
+ "$preexec_function" "$this_command"
+ preexec_function_ret_value="$?"
+ if [[ "$preexec_function_ret_value" != 0 ]]; then
+ preexec_ret_value="$preexec_function_ret_value"
+ fi
+ fi
+ done
+
+ # Restore the last argument of the last executed command, and set the return
+ # value of the DEBUG trap to be the return code of the last preexec function
+ # to return an error.
+ # If `extdebug` is enabled a non-zero return value from any preexec function
+ # will cause the user's command not to execute.
+ # Run `shopt -s extdebug` to enable
+ __bp_set_ret_value "$preexec_ret_value" "$__bp_last_argument_prev_command"
+}
+
+__bp_install() {
+ # Exit if we already have this installed.
+ if [[ "${PROMPT_COMMAND[*]:-}" == *"__bp_precmd_invoke_cmd"* ]]; then
+ return 1
+ fi
+
+ trap '__bp_preexec_invoke_exec "$_"' DEBUG
+
+ # Preserve any prior DEBUG trap as a preexec function
+ local prior_trap
+ # we can't easily do this with variable expansion. Leaving as sed command.
+ # shellcheck disable=SC2001
+ prior_trap=$(sed "s/[^']*'\(.*\)'[^']*/\1/" <<<"${__bp_trap_string:-}")
+ unset __bp_trap_string
+ if [[ -n "$prior_trap" ]]; then
+ eval '__bp_original_debug_trap() {
+ '"$prior_trap"'
+ }'
+ preexec_functions+=(__bp_original_debug_trap)
+ fi
+
+ # Adjust our HISTCONTROL Variable if needed.
+ __bp_adjust_histcontrol
+
+ # Issue #25. Setting debug trap for subshells causes sessions to exit for
+ # backgrounded subshell commands (e.g. (pwd)& ). Believe this is a bug in Bash.
+ #
+ # Disabling this by default. It can be enabled by setting this variable.
+ if [[ -n "${__bp_enable_subshells:-}" ]]; then
+
+ # Set so debug trap will work be invoked in subshells.
+ set -o functrace >/dev/null 2>&1
+ shopt -s extdebug >/dev/null 2>&1
+ fi
+
+ local existing_prompt_command
+ # Remove setting our trap install string and sanitize the existing prompt command string
+ existing_prompt_command="${PROMPT_COMMAND:-}"
+ # Edge case of appending to PROMPT_COMMAND
+ existing_prompt_command="${existing_prompt_command//$__bp_install_string/:}" # no-op
+ existing_prompt_command="${existing_prompt_command//$'\n':$'\n'/$'\n'}" # remove known-token only
+ existing_prompt_command="${existing_prompt_command//$'\n':;/$'\n'}" # remove known-token only
+ __bp_sanitize_string existing_prompt_command "$existing_prompt_command"
+ if [[ "${existing_prompt_command:-:}" == ":" ]]; then
+ existing_prompt_command=
+ fi
+
+ # Install our hooks in PROMPT_COMMAND to allow our trap to know when we've
+ # actually entered something.
+ PROMPT_COMMAND='__bp_precmd_invoke_cmd'
+ PROMPT_COMMAND+=${existing_prompt_command:+$'\n'$existing_prompt_command}
+ if ((BASH_VERSINFO[0] > 5 || (BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 1))); then
+ PROMPT_COMMAND+=('__bp_interactive_mode')
+ else
+ # shellcheck disable=SC2179 # PROMPT_COMMAND is not an array in bash <= 5.0
+ PROMPT_COMMAND+=$'\n__bp_interactive_mode'
+ fi
+
+ # Add two functions to our arrays for convenience
+ # of definition.
+ precmd_functions+=(precmd)
+ preexec_functions+=(preexec)
+
+ # Invoke our two functions manually that were added to $PROMPT_COMMAND
+ __bp_precmd_invoke_cmd
+ __bp_interactive_mode
+}
+
+# Sets an installation string as part of our PROMPT_COMMAND to install
+# after our session has started. This allows bash-preexec to be included
+# at any point in our bash profile.
+__bp_install_after_session_init() {
+ # bash-preexec needs to modify these variables in order to work correctly
+ # if it can't, just stop the installation
+ __bp_require_not_readonly PROMPT_COMMAND HISTCONTROL HISTTIMEFORMAT || return
+
+ local sanitized_prompt_command
+ __bp_sanitize_string sanitized_prompt_command "${PROMPT_COMMAND:-}"
+ if [[ -n "$sanitized_prompt_command" ]]; then
+ # shellcheck disable=SC2178 # PROMPT_COMMAND is not an array in bash <= 5.0
+ PROMPT_COMMAND=${sanitized_prompt_command}$'\n'
+ fi
+ # shellcheck disable=SC2179 # PROMPT_COMMAND is not an array in bash <= 5.0
+ PROMPT_COMMAND+=${__bp_install_string}
+}
+
+# Run our install so long as we're not delaying it.
+if [[ -z "${__bp_delay_install:-}" ]]; then
+ __bp_install_after_session_init
+fi
diff --git a/debian/.local/bin/bookmarks b/debian/.local/bin/bookmarks
new file mode 100755
index 0000000..a892a33
--- /dev/null
+++ b/debian/.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/debian/.local/bin/browse b/debian/.local/bin/browse
new file mode 100755
index 0000000..976e2a4
--- /dev/null
+++ b/debian/.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/debian/.local/bin/browserprofile b/debian/.local/bin/browserprofile
new file mode 100755
index 0000000..c66f183
--- /dev/null
+++ b/debian/.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/debian/.local/bin/clonerepo b/debian/.local/bin/clonerepo
new file mode 100755
index 0000000..cc1e23f
--- /dev/null
+++ b/debian/.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/debian/.local/bin/compiler b/debian/.local/bin/compiler
new file mode 100755
index 0000000..bf8b443
--- /dev/null
+++ b/debian/.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/debian/.local/bin/concatvideo b/debian/.local/bin/concatvideo
new file mode 100755
index 0000000..93a2060
--- /dev/null
+++ b/debian/.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/debian/.local/bin/createsh b/debian/.local/bin/createsh
new file mode 100755
index 0000000..53c2a32
--- /dev/null
+++ b/debian/.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/debian/.local/bin/cron/README.md b/debian/.local/bin/cron/README.md
new file mode 100644
index 0000000..fa0c354
--- /dev/null
+++ b/debian/.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/debian/.local/bin/cron/crontog b/debian/.local/bin/cron/crontog
new file mode 100755
index 0000000..70e7cf1
--- /dev/null
+++ b/debian/.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/debian/.local/bin/cron/mediaup b/debian/.local/bin/cron/mediaup
new file mode 100755
index 0000000..85935a2
--- /dev/null
+++ b/debian/.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/debian/.local/bin/cron/newsup b/debian/.local/bin/cron/newsup
new file mode 100755
index 0000000..346ec75
--- /dev/null
+++ b/debian/.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/debian/.local/bin/cutvideo b/debian/.local/bin/cutvideo
new file mode 100755
index 0000000..3220008
--- /dev/null
+++ b/debian/.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/debian/.local/bin/cwmx b/debian/.local/bin/cwmx
new file mode 100755
index 0000000..5ab496e
--- /dev/null
+++ b/debian/.local/bin/cwmx
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+bluetoothctl devices | grep 'WH-1000XM3' | awk '{print $2}' | xargs -I {} bluetoothctl connect "{}"
diff --git a/debian/.local/bin/decodetitle b/debian/.local/bin/decodetitle
new file mode 100755
index 0000000..e7a553c
--- /dev/null
+++ b/debian/.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/debian/.local/bin/displayselect b/debian/.local/bin/displayselect
new file mode 100755
index 0000000..87c8bd6
--- /dev/null
+++ b/debian/.local/bin/displayselect
@@ -0,0 +1,95 @@
+#!/bin/sh
+
+# A UI for detecting and selecting all displays. Probes xrandr for connected
+# displays and lets user select one to use. User may also select "manual
+# selection" which opens arandr.
+
+twoscreen() { # If multi-monitor is selected and there are two screens.
+ mirror=$(printf "no\\nyes" | dmenu -i -p "Mirror displays?")
+ [ -z "$mirror" ] && exit
+ # Mirror displays using native resolution of external display and a scaled
+ # version for the internal display
+ if [ "$mirror" = "yes" ]; then
+ external=$(echo "$screens" | dmenu -i -p "Optimize resolution for:")
+ internal=$(echo "$screens" | grep -v "$external")
+
+ res_external=$(xrandr --query | sed -n "/^$external/,/\+/p" |
+ tail -n 1 | awk '{print $1}')
+ res_internal=$(xrandr --query | sed -n "/^$internal/,/\+/p" |
+ tail -n 1 | awk '{print $1}')
+
+ res_ext_x=$(echo "$res_external" | sed 's/x.*//')
+ res_ext_y=$(echo "$res_external" | sed 's/.*x//')
+ res_int_x=$(echo "$res_internal" | sed 's/x.*//')
+ res_int_y=$(echo "$res_internal" | sed 's/.*x//')
+
+ scale_x=$(echo "$res_ext_x / $res_int_x" | bc -l)
+ scale_y=$(echo "$res_ext_y / $res_int_y" | bc -l)
+
+ xrandr --output "$external" --auto --scale 1.0x1.0 \
+ --output "$internal" --auto --same-as "$external" \
+ --scale "$scale_x"x"$scale_y"
+ else
+ primary=$(echo "$screens" | dmenu -i -p "Select primary display:")
+ [ -z "$primary" ] && exit
+ secondary=$(echo "$screens" | grep -v ^"$primary"$)
+ direction=$(printf "left\\nright" | dmenu -i -p "What side of $primary should $secondary be on?")
+ xrandr --output "$primary" --primary --auto --scale 1.0x1.0 --output "$secondary" --"$direction"-of "$primary" --auto --scale 1.0x1.0
+ fi
+}
+
+morescreen() { # If multi-monitor is selected and there are more than two screens.
+ primary=$(echo "$screens" | dmenu -i -p "Select primary display:")
+ [ -z "$primary" ] && exit
+ secondary=$(echo "$screens" | grep -v ^"$primary"$ | dmenu -i -p "Select secondary display:")
+ direction=$(printf "left\\nright" | dmenu -i -p "What side of $primary should $secondary be on?")
+ tertiary=$(echo "$screens" | grep -v ^"$primary"$ | grep -v ^"$secondary"$ | dmenu -i -p "Select third display:")
+ xrandr --output "$primary" --primary --auto --output "$secondary" --"$direction"-of "$primary" --auto --output "$tertiary" --"$(printf "left\\nright" | grep -v "$direction")"-of "$primary" --auto
+}
+
+multimon() { # Multi-monitor handler.
+ case "$(echo "$screens" | wc -l)" in
+ 2) twoscreen ;;
+ *) morescreen ;;
+ esac
+}
+
+onescreen() { # If only one output available or chosen.
+ xrandr --output "$1" --primary --auto --scale 1.0x1.0 $(echo "$allposs" | grep -v "\b$1" | awk '{print "--output", $1, "--off"}' | paste -sd ' ' -)
+}
+
+postrun() { # Stuff to run to clean up.
+ setbg # Fix background if screen size/arangement has changed.
+ {
+ killall dunst
+ setsid -f dunst
+ } >/dev/null 2>&1 # Restart dunst to ensure proper location on screen
+}
+
+# Get all possible displays
+allposs=$(xrandr -q | grep "connected")
+
+# Get all connected screens.
+screens=$(echo "$allposs" | awk '/ connected/ {print $1}')
+
+# If there's only one screen
+[ "$(echo "$screens" | wc -l)" -lt 2 ] &&
+ {
+ onescreen "$screens"
+ postrun
+ notify-send "đŸ’» Only one screen detected." "Using it in its optimal settings..."
+ exit
+ }
+
+# Get user choice including multi-monitor and manual selection:
+chosen=$(printf "%s\\nmulti-monitor\\nmanual selection" "$screens" | dmenu -i -p "Select display arangement:") &&
+ case "$chosen" in
+ "manual selection")
+ arandr
+ exit
+ ;;
+ "multi-monitor") multimon ;;
+ *) onescreen "$chosen" ;;
+ esac
+
+postrun
diff --git a/debian/.local/bin/dmenubrowse b/debian/.local/bin/dmenubrowse
new file mode 100755
index 0000000..f894491
--- /dev/null
+++ b/debian/.local/bin/dmenubrowse
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+# Use dmenu to choose a search option
+search_tool=$(printf "Searx\nDuckDuckGo\nWebsite\nYouTube" | dmenu -i -p "Which option?")
+
+# Exit if no option is selected
+[ -z "$search_tool" ] && exit 1
+
+# Determine the command to execute based on the search tool
+case "$search_tool" in
+"Searx")
+ # Searx can be run directly in the browser
+ tool="browse"
+ ;;
+"DuckDuckGo")
+ # For DuckDuckGo, run ddgr in the terminal
+ tool="$TERMINAL -e browse -d"
+ ;;
+"Website")
+ # Ask the user for the website
+ site=$(printf "bing\ngoogle\nnaver\nyahoo\nyoutube" | dmenu -i -p "Which site?")
+
+ # Exit if no site is provided
+ [ -z "$site" ] && exit 1
+
+ # For website searches, run ddgr in the terminal with the website option
+ tool="$TERMINAL -e browse $site"
+ ;;
+"YouTube")
+ tool="browse -y"
+ ;;
+*)
+ tool="browse"
+ ;;
+esac
+
+# Get the search query from the user
+search_query=$(echo | dmenu -i -p "Search: ")
+
+# Exit if no search query is provided
+[ -z "$search_query" ] && exit 1
+
+# Execute the command
+$tool "$search_query"
diff --git a/debian/.local/bin/dmenudelmusic b/debian/.local/bin/dmenudelmusic
new file mode 100755
index 0000000..2efbafa
--- /dev/null
+++ b/debian/.local/bin/dmenudelmusic
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+music_dir="${XDG_MUSIC_DIR:-${HOME}/Music}"
+music_txt="${music_dir}/.music.txt"
+playlist_dir="${XDG_CONFIG_HOME:-${HOME}/.config}/mpd/playlists"
+
+# Using POSIX-compliant methods for file selection
+selected_filename=$(find "$music_dir" -type f | awk -F/ '{print $NF}' | dmenu -i -l 20 -p "Select a file to delete:") || exit 1
+
+selected_file="$music_dir/$selected_filename"
+
+# Extracting YouTube video ID without using -P in grep
+video_id=$(strings "$selected_file" | grep 'watch?v=' | sed 's/.*watch?v=\([a-zA-Z0-9_-]*\).*/\1/' | head -1) || {
+ notify-send "❌ No YouTube video ID found in file: $selected_filename"
+ exit 1
+}
+
+# Confirmation dialog without using echo -e
+confirm=$(printf "Yes\nNo" | dmenu -i -p "Delete $selected_filename and update mpc?")
+
+[ "$confirm" = "Yes" ] || {
+ notify-send "❌ Operation cancelled."
+ exit 0
+}
+
+# More portable sed command without -i and updating mpc
+if grep -v "$video_id" "$music_txt" >"${music_txt}.tmp" && mv "${music_txt}.tmp" "$music_txt"; then
+ # Search and remove the filename from playlists
+ for playlist in "$playlist_dir"/*.m3u; do
+ [ -e "$playlist" ] || continue
+ if grep -q "$selected_filename" "$playlist"; then
+ grep -v "$selected_filename" "$playlist" > "${playlist}.tmp" && mv "${playlist}.tmp" "$playlist"
+ # Remove empty lines
+ sed -i '/^$/d' "$playlist"
+ fi
+ done
+
+ # Delete the music file
+ rm "$selected_file"
+ mpc update >/dev/null
+ notify-send " Success to delete:" "$selected_filename"
+else
+ notify-send "❌ An error occurred."
+fi
diff --git a/debian/.local/bin/dmenugithub b/debian/.local/bin/dmenugithub
new file mode 100755
index 0000000..db9d5d8
--- /dev/null
+++ b/debian/.local/bin/dmenugithub
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# Clone a repository into the current directory
+clone_repo() {
+ repository=$1
+ if [ -z "${repository}" ]; then
+ echo "ERROR: You need to enter the name of the repository you wish to clone."
+ else
+ git clone "${url}${repository}"
+ fi
+}
+
+# Get all the repositories for the user with curl and GitHub API and filter only
+# the repository name from the output with sed substitution
+all_repos() {
+ curl -s "https://api.github.com/users/${user}/repos?per_page=1000" | grep -o 'git@[^"]*' |
+ sed "s/git@github.com:${user}\///g"
+}
+
+select_repo() { dmenu -p "Select a repository >" -l 10; }
+
+user="${TheSiahxyz:-$1}"
+url="https://github.com/${user}/"
+
+clone_repo "$(all_repos | select_repo)"
diff --git a/debian/.local/bin/dmenuhandler b/debian/.local/bin/dmenuhandler
new file mode 100755
index 0000000..02e015e
--- /dev/null
+++ b/debian/.local/bin/dmenuhandler
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+# Feed this script a link and it will give dmenu
+# some choice programs to use to open it.
+feed="${1:-$(true | dmenu -p 'Paste URL or file path')}"
+
+case "$(printf "copy url\\nnsxiv\\nsetbg\\nPDF\\nbrowser\\nlynx\\nvim\\nmpv\\nmpv loop\\nmpv float\\nqueue download\\nqueue yt-dlp\\nqueue yt-dlp audio" | dmenu -i -p "Open it with?")" in
+"copy url") echo "$feed" | xclip -selection clipboard ;;
+mpv) setsid -f mpv -quiet "$feed" >/dev/null 2>&1 ;;
+"mpv loop") setsid -f mpv -quiet --loop "$feed" >/dev/null 2>&1 ;;
+"mpv float") setsid -f "$TERMINAL" -e mpv --geometry=+0-0 --autofit=30% --title="mpvfloat" "$feed" >/dev/null 2>&1 ;;
+"queue yt-dlp") qndl "$feed" >/dev/null 2>&1 ;;
+"queue yt-dlp audio") qndl "$feed" 'yt-dlp -o "%(title)s.%(ext)s" -f bestaudio --embed-metadata --restrict-filenames' ;;
+"queue download") qndl "$feed" 'curl -LO' >/dev/null 2>&1 ;;
+PDF) curl -sL "$feed" >"/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" && zathura "/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" >/dev/null 2>&1 ;;
+nsxiv) curl -sL "$feed" >"/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" && nsxiv -a "/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" >/dev/null 2>&1 ;;
+vim) curl -sL "$feed" >"/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" && setsid -f "$TERMINAL" -e "$EDITOR" "/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" >/dev/null 2>&1 ;;
+setbg)
+ curl -L "$feed" >"$XDG_CACHE_HOME/pic"
+ xwallpaper --zoom "$XDG_CACHE_HOME/pic" >/dev/null 2>&1
+ ;;
+browser) setsid -f "$BROWSER" "$feed" >/dev/null 2>&1 ;;
+lynx) lynx "$feed" >/dev/null 2>&1 ;;
+esac
diff --git a/debian/.local/bin/dmenuman b/debian/.local/bin/dmenuman
new file mode 100755
index 0000000..6da7501
--- /dev/null
+++ b/debian/.local/bin/dmenuman
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cmd="$(man -k . | dmenu -i -l 20 -p "Select Program " | cut -d" " -f1)"
+[ -n "$cmd" ] && man_path=$(man -w "$cmd" 2>/dev/null) && [ -n "$man_path" ] && setsid -f "${TERMINAL:-st}" -e man "$cmd"
diff --git a/debian/.local/bin/dmenumountcifs b/debian/.local/bin/dmenumountcifs
new file mode 100755
index 0000000..46c2b57
--- /dev/null
+++ b/debian/.local/bin/dmenumountcifs
@@ -0,0 +1,19 @@
+#!/bin/sh
+# Gives a dmenu prompt to mount unmounted local NAS shares for read/write.
+# Requirements - "%wheel ALL=(ALL) NOPASSWD: ALL"
+#
+# Browse for mDNS/DNS-SD services using the Avahi daemon...
+srvname=$(avahi-browse _smb._tcp -t | awk '{print $4}' | dmenu -i -p "Which NAS?") || exit 1
+notify-send "Searching for network shares..." "Please wait..."
+# Choose share disk...
+share=$(smbclient -L "$srvname" -N | grep Disk | awk '{print $1}' | dmenu -i -p "Mount which share?") || exit 1
+# Format URL...
+share2mnt=//"$srvname".local/"$share"
+
+sharemount() {
+ mounted=$(mount -v | grep "$share2mnt") || ([ ! -d /mnt/"$share" ] && sudo mkdir /mnt/"$share")
+ [ -z "$mounted" ] && sudo mount -t cifs "$share2mnt" -o user=nobody,password="",noperm /mnt/"$share" && notify-send "Netshare $share mounted" && exit 0
+ notify-send "Netshare $share already mounted"; exit 1
+}
+
+sharemount
diff --git a/debian/.local/bin/dmenupass b/debian/.local/bin/dmenupass
new file mode 100755
index 0000000..2c14e6f
--- /dev/null
+++ b/debian/.local/bin/dmenupass
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+# This script is the SUDO_ASKPASS variable, meaning that it will be used as a
+# password prompt if needed.
+
+dmenu -fn Monospace-18 -P -p "$1" <&- && echo
diff --git a/debian/.local/bin/dmenurecord b/debian/.local/bin/dmenurecord
new file mode 100755
index 0000000..621133d
--- /dev/null
+++ b/debian/.local/bin/dmenurecord
@@ -0,0 +1,220 @@
+#!/bin/sh
+
+usage() {
+ echo "Asks for recording type via dmenu."
+ echo "If there is already a running instance, user will be prompted to end it. "
+ echo ""
+ echo "Usage: ${0##*/} [-h] [-a|--audio|audio] [-k|--kill|kill] [-v|--video|video] [-s|--screencast|screencast]"
+ echo ""
+ echo "Options:"
+ echo " - audio : Records only audio"
+ echo " - kill : Kills existing recording"
+ echo " - video : Records only screen"
+ echo " - screencast : Records both audio and screen"
+}
+
+getdim() { xrandr | grep -oP '(?<=current ).*(?=,)' | tr -d ' '; }
+
+updateicon() {
+ echo "$1" >/tmp/recordingicon
+ pkill -RTMIN+24 "${STATUSBAR:-dwmblocks}"
+}
+
+killrecording() {
+ recpid="$(cat /tmp/recordingpid)"
+ kill -15 "$recpid"
+ rm -f /tmp/recordingpid
+ updateicon ""
+ pkill -RTMIN+24 "${STATUSBAR:-dwmblocks}"
+}
+
+getmonitor() {
+ xrandr | awk '
+ /^[^ ]+ connected/ {
+ name=$1
+ if (match($0, /([0-9]+)x([0-9]+)\+([0-9]+)\+([0-9]+)/, a)) {
+ printf "%s %s %s+%s\n", name, a[1] "x" a[2], a[3], a[4]
+ }
+ }
+ '
+}
+
+selectmonitor() {
+ map=$(getmonitor)
+ [ -n "$map" ] || exit 1
+
+ names=$(printf "%s\n" "$map" | awk '{print $1}')
+ monitor_count=$(printf "%s\n" "$names" | wc -l)
+
+ if [ "$monitor_count" -ge 2 ]; then
+ options=$(printf "%s\nall" "$names")
+ choice=$(printf "%s\n" "$options" | dmenu -p "Select monitor to record:")
+ [ -n "$choice" ] || exit 1
+
+ if [ "$choice" = "all" ]; then
+ echo "all"
+ else
+ line=$(printf "%s\n" "$map" | awk -v mon="$choice" '$1 == mon')
+ [ -n "$line" ] || exit 1
+ res=$(echo "$line" | awk '{print $2}')
+ pos=$(echo "$line" | awk '{print $3}')
+ echo "$res+$pos"
+ fi
+ else
+ echo "all"
+ fi
+}
+
+screencast() {
+ ffmpeg -y \
+ -f x11grab \
+ -framerate 30 \
+ -s "$(getdim)" \
+ -i "$DISPLAY" \
+ -r 24 \
+ -use_wallclock_as_timestamps 1 \
+ -f alsa -thread_queue_size 1024 -i default \
+ -c:v h264 \
+ -crf 0 -preset ultrafast -c:a aac \
+ "$recordings/screencast-$(date '+%y%m%d-%H%M-%S').mp4" &
+ echo $! >/tmp/recordingpid
+ updateicon "âșïžđŸŽ™ïž"
+}
+
+screencastselected() {
+ geometry=$(selectmonitor) || exit 1
+ if [ "$geometry" = "all" ]; then
+ screencast
+ else
+ ffmpeg -y \
+ -f x11grab \
+ -framerate 30 \
+ -video_size "${geometry%%+*}" \
+ -i "$DISPLAY+${geometry#*+}" \
+ -r 24 \
+ -use_wallclock_as_timestamps 1 \
+ -f alsa -thread_queue_size 1024 -i default \
+ -c:v h264 \
+ -crf 0 -preset ultrafast -c:a aac \
+ "$recordings/screencast-$(date '+%y%m%d-%H%M-%S').mp4" &
+ echo $! >/tmp/recordingpid
+ updateicon "âșïžđŸŽ™ïž"
+ fi
+}
+
+video() {
+ ffmpeg \
+ -f x11grab \
+ -framerate 30 \
+ -s "$(getdim)" \
+ -i "$DISPLAY" \
+ -c:v libx264 -qp 0 -r 30 \
+ "$recordings/video-$(date '+%y%m%d-%H%M-%S').mkv" &
+ echo $! >/tmp/recordingpid
+ updateicon "âș"
+}
+
+videoselected() {
+ slop -f "%x %y %w %h" >/tmp/slop
+ read -r X Y W H </tmp/slop
+ rm /tmp/slop
+
+ ffmpeg \
+ -f x11grab \
+ -framerate 30 \
+ -video_size "$W"x"$H" \
+ -i :0.0+"$X,$Y" \
+ -c:v libx264 -qp 0 -r 30 \
+ "$recordings/box-$(date '+%y%m%d-%H%M-%S').mkv" &
+ echo $! >/tmp/recordingpid
+ updateicon "âș"
+}
+
+webcamselect() {
+ cameras=$(
+ v4l2-ctl --list-devices | awk '
+ BEGIN { RS=""; FS="\n" }
+ {
+ name = $1;
+ sub(/ \(.*$/, "", name);
+ gsub(/^[ \t]+|[ \t]+$/, "", name);
+ for (i=2; i<=NF; i++) {
+ if ($i ~ /\/dev\/video/) {
+ gsub(/^[ \t]+/, "", $i);
+ print name "|" $i;
+ break;
+ }
+ }
+ }'
+ )
+ names=$(echo "$cameras" | cut -d '|' -f1)
+ choice=$(echo "$names" | dmenu -i -p 'Choose a camera:')
+ camera=$(echo "$cameras" | awk -F '|' -v sel="$choice" '$1 == sel {print $2}')
+}
+
+webcamhidef() {
+ [ -z "$camera" ] && exit
+ ffmpeg \
+ -display_hflip \
+ -f v4l2 \
+ -i "$camera" \
+ -video_size 1920x1080 \
+ "$recordings/webcam-$(date '+%y%m%d-%H%M-%S').mkv" &
+ echo $! >/tmp/recordingpid
+ updateicon "đŸŽ„"
+}
+
+webcam() {
+ [ -z "$camera" ] && exit
+ ffmpeg \
+ -display_hflip \
+ -f v4l2 \
+ -i "$camera" \
+ -video_size 640x480 \
+ "$recordings/webcam-$(date '+%y%m%d-%H%M-%S').mkv" &
+ echo $! >/tmp/recordingpid
+ updateicon "đŸŽ„"
+}
+
+audio() {
+ ffmpeg \
+ -f alsa -i default \
+ -c:a flac \
+ "$recordings/audio-$(date '+%y%m%d-%H%M-%S').flac" &
+ echo $! >/tmp/recordingpid
+ updateicon "đŸŽ™ïž"
+}
+
+askrecording() {
+ choice=$(printf "screencast\\nscreencast selected\\nvideo\\nvideo selected\\naudio\\nwebcam\\nwebcam (hi-def)" | dmenu -i -p "Select recording style:")
+ case "$choice" in
+ screencast) screencast ;;
+ "screencast selected") screencastselected ;;
+ audio) audio ;;
+ video) video ;;
+ "video selected") videoselected ;;
+ webcam) webcamselect && webcam ;;
+ "webcam (hi-def)") webcamselect && webcamhidef ;;
+ *) exit ;;
+ esac
+}
+
+asktoend() {
+ response=$(printf "No\\nYes" | dmenu -i -p "Recording still active. End recording?")
+ [ -z "$response" ] && exit
+ [ "$response" = "Yes" ] && killrecording
+}
+
+recordings="${XDG_VIDEOS_DIR:-$HOME/Videos}/recordings"
+[ -d "$recordings" ] || mkdir -p "$recordings"
+
+case "$1" in
+-h | --help | help) usage && exit 0 ;;
+-a | --audio | audio) audio ;;
+-k | --kill | kill) killrecording ;;
+-s | --screencast | screencast) screencast ;;
+-ss | --screencast-selected | "screencast selected") screencastselected ;;
+-v | --video | video) video ;;
+-vs | --video-selected | "video selected") videoselected ;;
+*) ([ -f /tmp/recordingpid ] && asktoend && exit) || askrecording ;;
+esac
diff --git a/debian/.local/bin/dmenusmbadd b/debian/.local/bin/dmenusmbadd
new file mode 100755
index 0000000..d1b76cd
--- /dev/null
+++ b/debian/.local/bin/dmenusmbadd
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+samba_conf="/etc/samba/smb.conf"
+base_path="/media /mnt /home/$USER"
+os="$(grep '^ID=' /etc/os-release | cut -d= -f2)"
+
+restartinit() {
+ case "$(basename "$(readlink -f /sbin/init)" | sed 's/-init//g')" in
+ *systemd*)
+ sudo systemctl restart smb >/dev/null/ 2>&1 && sudo systemctl restart nmb >/dev/null/ 2>&1
+ ;;
+ *runit*)
+ sudo sv restart smbd >/dev/null 2>&1 && sudo sv restart nmbd >/dev/null 2>&1
+ ;;
+ esac
+}
+
+[ -f "$samba_conf" ] || { sudo touch "$samba_conf" && smbpasswd -a "$USER"; }
+if [ "$#" -eq 1 ]; then
+ folder_path="$1"
+ folder_name=$(basename "$folder_path" | tr '[:upper:]' '[:lower:]')
+ if [ "$folder_path" = "/media/$USER" ]; then
+ folder_name="media"
+ fi
+else
+ folder_name=$(echo | dmenu -p "Enter the name of the folder to share:" | tr '[:upper:]' '[:lower:]') || exit 1
+ [ -z "$folder_name" ] && notify-send "📁No folder name provided." && exit 1
+
+ if [ "$folder_name" = "media" ]; then
+ target_name="$USER"
+ else
+ target_name="$folder_name"
+ fi
+
+ folder_path=$(for path in $base_path; do
+ find "$path" -type d -iname "$target_name" -print 2>/dev/null
+ done | sort -r | dmenu -l 10 -p "Select the folder to share:")
+ [ -z "$folder_path" ] && notify-send "📁Folder not found." && exit 1
+fi
+
+[ -d "$folder_path" ] || exit 1
+
+if grep -qF "[$USER-$os-$folder_name]" "$samba_conf"; then
+ notify-send "📁The folder '$target_name' is already shared."
+ exit 0
+fi
+
+echo | sudo tee -a "$samba_conf" >/dev/null
+sudo tee -a "$samba_conf" >/dev/null <<EOF && restartinit || exit 1
+[$USER-$os-$folder_name]
+ path = $folder_path
+ writable = yes
+ browsable = yes
+ guest ok = yes
+ create mask = 0755
+EOF
+
+notify-send "📁'$USER-$os-$folder_name' starts sharing." "path: '$folder_path'"
diff --git a/debian/.local/bin/dmenusmbdel b/debian/.local/bin/dmenusmbdel
new file mode 100755
index 0000000..5769695
--- /dev/null
+++ b/debian/.local/bin/dmenusmbdel
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+samba_conf="/etc/samba/smb.conf"
+dmenu_command=$(command -v dmenu)
+
+# Check if Samba configuration file exists
+[ -f "$samba_conf" ] || {
+ echo "Error: Samba configuration file not found at $samba_conf"
+ exit 1
+}
+
+# Extract share names and their paths for selection, with alignment
+shares=$(awk '
+/^\[.*\]/ {
+ share_name = $0
+ gsub(/[\[\]]/, "", share_name)
+}
+/^ *path *=/ {
+ sub(/^ *path *= */, "", $0)
+ printf "%-40s %s\n", share_name, $0
+}
+' "$samba_conf")
+
+# Check if dmenu is installed and available
+if [ -n "$dmenu_command" ]; then
+ selected=$(printf "%s\n" "$shares" | "$dmenu_command" -l 10 -p "Select a shared folder to disable:")
+
+ # Exit if no selection was made
+ [ -z "$selected" ] && exit 0
+
+ # Extract share name from the selected entry (before the spaces)
+ selected_share=$(echo "$selected" | awk '{print $1}')
+
+ # Confirm with the user
+ confirm=$(printf "Yes\nNo\n" | "$dmenu_command" -p "Disable sharing for $selected_share?")
+ if [ "$confirm" = "Yes" ]; then
+ # Remove only the selected share block and its preceding empty line
+ sed -n -e "/^$/N;/^\n\[${selected_share}\]/,/^ *create mask = 0755$/!p" "$samba_conf" | sudo tee "$samba_conf" >/dev/null
+
+ # Restart Samba services
+ case "$(basename "$(readlink -f /sbin/init)" | sed 's/-init//g')" in
+ *systemd*)
+ sudo systemctl restart smb >/dev/null 2>&1 && sudo systemctl restart nmb >/dev/null 2>&1
+ ;;
+ *runit*)
+ sudo sv restart smbd >/dev/null 2>&1 && sudo sv restart nmbd >/dev/null 2>&1
+ ;;
+ esac
+
+ notify-send "✂ Disabled sharing for '$selected_share'"
+ fi
+else
+ # Print share names if dmenu is not installed
+ printf "%s\n" "$shares"
+fi
diff --git a/debian/.local/bin/dmenuunicode b/debian/.local/bin/dmenuunicode
new file mode 100755
index 0000000..eb5ff4c
--- /dev/null
+++ b/debian/.local/bin/dmenuunicode
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# The famous "get a menu of emojis to copy" script.
+
+# Get user selection via dmenu from emoji file.
+chosen=$(cut -d ';' -f1 ~/.local/share/thesiah/chars/* | dmenu -i -l 30 | sed "s/ .*//")
+
+# Exit if none chosen.
+[ -z "$chosen" ] && exit
+
+# If you run this command with an argument, it will automatically insert the
+# character. Otherwise, show a message that the emoji has been copied.
+if [ -n "$1" ]; then
+ xdotool type "$chosen"
+else
+ printf "%s" "$chosen" | xclip -selection clipboard
+ notify-send "'$chosen' copied to clipboard." &
+fi
diff --git a/debian/.local/bin/dmenuupgrade b/debian/.local/bin/dmenuupgrade
new file mode 100755
index 0000000..b43ff4c
--- /dev/null
+++ b/debian/.local/bin/dmenuupgrade
@@ -0,0 +1,115 @@
+#!/bin/sh
+
+# Get list of pacman and yay upgradable packages
+pacman_updates=$(pacman -Qu)
+yay_updates=$(yay -Qu)
+
+# Count the number of upgradable packages, filtering yay's output to count only AUR packages
+pacman_count=$(printf "%s" "$pacman_updates" | grep -c '^\S')
+aur_count=$(printf "%s" "$yay_updates" | grep -c '^\[AUR\]')
+
+# Display the upgrade options with package counts
+selection=$(printf "All\nPacman(%d)\nAUR(%d)" "$pacman_count" "$aur_count" | dmenu -i -p "Upgrade Option:")
+
+case "$selection" in
+"All")
+ if [ "$(printf "No\nYes" | dmenu -i -p 'Proceed with upgrade for all packages?')" = "Yes" ]; then
+ notify-send "📩 Upgrading all packages..."
+ yay -Syu --noconfirm
+ pkill -RTMIN+16 "${STATUSBAR:-dwmblocks}"
+ notify-send "✅ Upgrade of all packages completed."
+ else
+ notify-send "❌ Upgrade cancelled."
+ exit 0
+ fi
+ ;;
+"Pacman($pacman_count)")
+ # Show all upgradable pacman packages with "Upgrade All" option
+ selection=$(printf "Upgrade All\n%s" "$pacman_updates" | dmenu -i -l 10 -p "Pacman: Upgrade or Open URL:")
+ case "$selection" in
+ "Upgrade All")
+ if [ "$(printf "No\nYes" | dmenu -i -p 'Proceed with pacman upgrade?')" = "Yes" ]; then
+ notify-send "📩 Upgrading pacman packages..."
+ printf "%s" "$pacman_updates" | awk '{print $1}' | xargs sudo pacman -S --noconfirm
+ pkill -RTMIN+16 "${STATUSBAR:-dwmblocks}"
+ notify-send "✅ Pacman packages upgrade completed."
+ else
+ notify-send "❌ Upgrade cancelled."
+ exit 0
+ fi
+ ;;
+ *)
+ # Individual package selected: Prompt to upgrade or open URL
+ action=$(printf "Upgrade\nOpen URL" | dmenu -i -p "Package: $selection")
+ case "$action" in
+ "Upgrade")
+ if [ "$(printf "No\nYes" | dmenu -i -p 'Proceed with upgrade for this package?')" = "Yes" ]; then
+ notify-send "📩 Upgrading package: $selection..."
+ sudo pacman -S --noconfirm "$(printf "%s" "$selection" | awk '{print $1}')"
+ pkill -RTMIN+16 "${STATUSBAR:-dwmblocks}"
+ notify-send "✅ Upgrade completed for package: $selection."
+ else
+ notify-send "❌ Upgrade cancelled."
+ exit 0
+ fi
+ ;;
+ "Open URL")
+ if [ "$(printf "No\nYes" | dmenu -i -p 'Open URL?')" = "Yes" ]; then
+ xdg-open "https://archlinux.org/packages/?q=$(printf "%s" "$selection" | awk '{print $1}')" >/dev/null 2>&1 &
+ else
+ exit
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+"AUR($aur_count)")
+ # Show all upgradable AUR packages with "Upgrade All" option
+ aur_updates=$(printf "%s" "$yay_updates" | grep '^\[AUR\]')
+ selection=$(printf "Upgrade All\n%s" "$aur_updates" | dmenu -i -l 10 -p "AUR: Upgrade or Open URL:")
+ case "$selection" in
+ "Upgrade All")
+ if [ "$(printf "No\nYes" | dmenu -i -p 'Proceed with AUR upgrade?')" = "Yes" ]; then
+ notify-send "📩 Upgrading AUR packages..."
+ yay -Syu --aur --noconfirm
+ pkill -RTMIN+16 "${STATUSBAR:-dwmblocks}"
+ notify-send "✅ AUR packages upgrade completed."
+ else
+ notify-send "❌ Upgrade cancelled."
+ exit 0
+ fi
+ ;;
+ *)
+ # Individual AUR package selected: Prompt to upgrade or open URL
+ action=$(printf "Upgrade\nOpen URL" | dmenu -i -p "Package: $selection")
+ case "$action" in
+ "Upgrade")
+ if [ "$(printf "No\nYes" | dmenu -i -p 'Proceed with upgrade for this package?')" = "Yes" ]; then
+ notify-send "📩 Upgrading AUR package: $selection..."
+ yay -S --noconfirm "$(printf "%s" "$selection" | awk '{print $2}')"
+ pkill -RTMIN+16 "${STATUSBAR:-dwmblocks}"
+ notify-send "✅ Upgrade completed for AUR package: $selection."
+ else
+ notify-send "❌ Upgrade cancelled."
+ exit 0
+ fi
+ ;;
+ "Open URL")
+ if [ "$(printf "No\nYes" | dmenu -i -p 'Open URL?')" = "Yes" ]; then
+ xdg-open "https://aur.archlinux.org/packages/?O=0&K=$(printf "%s" "$selection" | awk '{print $2}')" >/dev/null 2>&1 &
+ else
+ exit
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+*)
+ notify-send "❌ Invalid selection."
+ exit 0
+ ;;
+esac
+
+remaps
diff --git a/debian/.local/bin/dmenuvirt b/debian/.local/bin/dmenuvirt
new file mode 100755
index 0000000..bc90680
--- /dev/null
+++ b/debian/.local/bin/dmenuvirt
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# Select action
+choice=$(printf "virt-manager\nstart\nshutdown" | dmenu -i -p "Choose an action:")
+[ -z "$choice" ] && exit 1
+
+# Get list of VMs based on state
+case "$choice" in
+virt-manager) setsid -f virt-manager && exit ;;
+start) vmlist=$(virsh --connect qemu:///system list --all | awk '/shut off/ {print $2}') ;;
+shutdown) vmlist=$(virsh --connect qemu:///system list --all | awk '/running/ {print $2}') ;;
+*) exit 1 ;;
+esac
+
+# Select a VM from the list
+vm=$(printf "%s\n" "$vmlist" | dmenu -i -p "$choice which VM?")
+[ -z "$vm" ] && exit 1
+
+# Perform the action
+case "$choice" in
+start)
+ virsh --connect qemu:///system start "$vm" &&
+ setsid -f virt-viewer --connect qemu:///system "$vm" >/dev/null 2>&1
+ ;;
+shutdown)
+ virsh --connect qemu:///system shutdown "$vm"
+ ;;
+esac
diff --git a/debian/.local/bin/dvdburn b/debian/.local/bin/dvdburn
new file mode 100755
index 0000000..30993e1
--- /dev/null
+++ b/debian/.local/bin/dvdburn
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+# Check if input file is provided
+if [ $# -eq 0 ]; then
+ echo "Usage: $0 input_video.mp4"
+ exit 1
+fi
+
+# Check if input file exists
+input="$1"
+fn="${input%.*}"
+if [ ! -f "$input" ]; then
+ echo "Error: Input file '$input' not found."
+ exit 1
+fi
+
+# Set default VIDEO_FORMAT if not provided
+export VIDEO_FORMAT="${VIDEO_FORMAT:-NTSC}"
+
+# Create temporary directory for DVD structure
+tmp_dir=$(mktemp -d)
+echo "Temporary directory created: $tmp_dir"
+
+# Convert MP4 to DVD-compatible MPEG-2 format
+echo "Converting $input to DVD-Video format ($VIDEO_FORMAT)..."
+format=$(printf '%s' "$VIDEO_FORMAT" | tr '[:upper:]' '[:lower:]')
+ffmpeg -i "$input" -target "$format-dvd" -aspect 16:9 "$tmp_dir/$fn.mpg"
+
+# Create DVD file structure
+echo "Creating DVD file structure..."
+dvdauthor -o "$tmp_dir/dvd" -t "$tmp_dir/$fn.mpg"
+dvdauthor -o "$tmp_dir/dvd" -T
+
+# Create ISO image from DVD structure
+echo "Creating ISO image..."
+mkisofs -dvd-video -o "$tmp_dir/$fn.iso" "$tmp_dir/dvd/"
+
+# Burn ISO image to DVD
+echo "Burning DVD..."
+wodim -v dev=/dev/sr0 speed=8 -eject "$tmp_dir/$fn.iso"
+
+# Cleanup
+echo "Cleaning up..."
+rm -rf "$tmp_dir"
+
+echo "Done!"
diff --git a/debian/.local/bin/ecrypt b/debian/.local/bin/ecrypt
new file mode 100755
index 0000000..e13c033
--- /dev/null
+++ b/debian/.local/bin/ecrypt
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+mount_encrypted() {
+ ! mount | grep -q " $1 " && echo "$passphrase" | sudo mount -t ecryptfs "$1" "$2" \
+ -o ecryptfs_cipher=aes,ecryptfs_key_bytes=32,ecryptfs_passthrough=no,ecryptfs_enable_filename_crypto=yes,ecryptfs_sig=$ecryptfs_sig,ecryptfs_fnek_sig=$fnek_sig,key=passphrase:passphrase_passwd=$(printf '%s' "$passphrase")
+}
+
+attempt_mount() {
+ if mount | grep -q " $2 "; then
+ if sudo umount "$2"; then
+ notify-send "🔒 Locked: $3"
+ else
+ notify-send "❗ Unable to lock" "Mounted: $3"
+ fi
+ else
+ ecryptfs_sig=$(pass show encryption/ecryptfs-sig-"$4")
+ fnek_sig=$ecryptfs_sig
+ passphrase=$(pass show encryption/ecryptfs)
+ [ -z "$passphrase" ] && {
+ notify-send "❌ Failed to retrieve passphrase."
+ exit 1
+ }
+ mount_encrypted "$1" "$2" && notify-send "🔑 Unlocked: $3"
+ fi
+}
+
+targets="$HOME/.secret"
+mounts="$HOME/Private"
+pw="default"
+set -- $mounts # Set positional parameters to mounts
+i=1
+for target in $targets; do
+ mp=$(eval echo "\$$i") # Get the mount point using indirect expansion
+ path=$(basename "$mp") # Extract last directory component
+ pw=$(echo "$pw" | cut -d' ' -f$i) # Get the corresponding passthrough option
+
+ attempt_mount "$target" "$mp" "$path" "$pw"
+ i=$((i + 1))
+done
diff --git a/debian/.local/bin/emojiupdate b/debian/.local/bin/emojiupdate
new file mode 100755
index 0000000..7185a4d
--- /dev/null
+++ b/debian/.local/bin/emojiupdate
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+# Define input and output files
+url="https://unicode.org/Public/emoji/latest/emoji-test.txt"
+input_file="${XDG_DOTFILES_DIR:-${HOME}/.dotfiles}/global/.local/share/thesiah/chars/emoji_raw"
+temp_file="${XDG_DOTFILES_DIR:-${HOME}/.dotfiles}/global/.local/share/thesiah/chars/emoji_temp"
+output_file="${XDG_DOTFILES_DIR:-${HOME}/.dotfiles}/global/.local/share/thesiah/chars/emoji"
+
+# Create the directory for output files if it doesn't exist
+mkdir -p "$(dirname "$input_file")"
+
+# Download the emoji file
+echo "Downloading emoji-test.txt from Unicode..."
+if curl -o "$input_file" -L "$url"; then
+ echo "Download complete! File saved to: $input_file"
+else
+ echo "Failed to download emoji"
+ exit 1
+fi
+
+awk '
+ # Skip empty lines and comments
+ /^[[:space:]]*$/ || /^#/ { next }
+
+ # Keep only fully-qualified lines
+ !/(fully-qualified|component)/ { next }
+
+ # Skip lines containing 200D (zero-width joiner)
+ /200D/ { next }
+
+ # Skip lines containing components
+ $2 ~ /1F3F[BCDEF]/ { next }
+
+ # Print valid lines
+ { print }
+' "$input_file" >"$temp_file"
+
+# Second stage: Extract emoji and description
+awk -F'#' '
+ {
+ if (NF >= 2) {
+ full_data = $2 # Extract the emoji and description (after #)
+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", full_data) # Trim spaces around the entire field
+
+ split(full_data, parts, " ") # Split into parts by spaces
+ emoji = parts[1] # First part is the emoji
+
+ # Reconstruct description from parts[3] onward
+ description = ""
+ for (i = 3; i <= length(parts); i++) {
+ description = description parts[i] " "
+ }
+
+ # Remove excessive internal spaces and trim description
+ gsub(/[[:space:]]+/, " ", description)
+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", description)
+
+ # Print emoji and description
+ print emoji, description
+ }
+ }
+' "$temp_file" >"$output_file"
+
+rm -rf "$input_file" "$temp_file"
+echo "Processing complete! File saved to: $output_file"
diff --git a/debian/.local/bin/ethwifi b/debian/.local/bin/ethwifi
new file mode 100755
index 0000000..26366e9
--- /dev/null
+++ b/debian/.local/bin/ethwifi
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+check_ethernet() {
+ interfaces=$(ip link | awk '/^[0-9]+: e/ {print substr($2, 1, length($2)-1)}')
+ for iface in $interfaces; do
+ ip link show "$iface" | grep -q 'state UP' && return 0
+ done
+ return 1
+}
+
+toggle_wifi() {
+ wifi_status=$(nmcli radio wifi) # Get current Wi-Fi status
+ if [ "$wifi_status" = "enabled" ]; then
+ nmcli radio wifi off
+ notify-send "📡 wifi: OFF"
+ else
+ nmcli radio wifi on
+ notify-send "📡 wifi: ON"
+ fi
+}
+
+# Check Ethernet and toggle Wi-Fi based on its status
+if check_ethernet; then
+ nmcli radio wifi off
+ notify-send "📡 wifi: OFF (Ethernet connected)"
+else
+ toggle_wifi
+fi
+
+# Refresh status bar
+pkill -RTMIN+7 "${STATUSBAR:-dwmblocks}"
diff --git a/debian/.local/bin/extract b/debian/.local/bin/extract
new file mode 100755
index 0000000..b352a70
--- /dev/null
+++ b/debian/.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/debian/.local/bin/extractkeys b/debian/.local/bin/extractkeys
new file mode 100755
index 0000000..b45358d
--- /dev/null
+++ b/debian/.local/bin/extractkeys
@@ -0,0 +1,164 @@
+#!/bin/bash
+
+# Define paths and phrases
+path="${XDG_SOURCES_HOME:-${HOME}/.local/src}/suckless/dwm/thesiah"
+readme_file="${XDG_DATA_HOME:-${HOME}/.local/share}/thesiah/thesiah.mom"
+output_file="$path.mom"
+temp_file_before="$path.tmp.before"
+temp_file_after="$path.tmp.after"
+start_phrase="When config.def.h in DWM and ST is saved, key bindings will be extracted and updated below."
+end_phrase=".HEADING 2 \"Other buttons\""
+
+[ -f "$path.mom" ] && source_file="$path.mom" || source_file="$path-default.mom"
+
+# Extract the section before the end marker
+sed "/$end_phrase/,\$d" "$source_file" >"$temp_file_before"
+
+# Extract the section from the end marker to the end of the file
+sed -n "/$end_phrase/,\$p" "$source_file" >"$temp_file_after"
+
+# Remove the contents between the start and end phrases, including the markers themselves
+sed -i "/$start_phrase/,/$end_phrase/d" "$temp_file_before"
+
+# Verify that the section has been removed
+echo "Section between markers removed. Check $temp_file_before for correctness."
+
+# Re-append the start marker since it was deleted
+echo "$start_phrase" >>"$temp_file_before"
+
+# Define your configuration files
+mapfile -t config_files <<EOF
+${XDG_SOURCES_HOME:-${HOME}/.local/src}/suckless/dwm/config.def.h
+${XDG_SOURCES_HOME:-${HOME}/.local/src}/suckless/st/config.def.h
+EOF
+
+# Process each configuration file and append content
+for file_path in "${config_files[@]}"; do
+ project_name=$(basename "$(dirname "$file_path")")
+ project_name=${project_name^^}
+
+ echo ".HEADING 3 \"$project_name\"" >>"$temp_file_before"
+ if [ "$project_name" == "DWM" ]; then
+ printf ".PP\nTHESIAH's window manager. I do not use a desktop environment.\n" >>"$temp_file_before"
+ elif [ "$project_name" == "ST" ]; then
+ printf ".PP\nTHESIAH's default terminal in C. It is light, configurable, and fast.\n" >>"$temp_file_before"
+ fi
+ echo ".LI" >>"$temp_file_before"
+
+ awk 'BEGIN {flag=0} /static[[:space:]]+(const[[:space:]]+)?(Keychord|Shortcut)[[:space:]]+(*keychords|shortcuts)[[:space:]]*\[\][[:space:]]*=[[:space:]]*{/ {flag=1} /\};/ {flag=0} flag' "$file_path" | while read -r line; do
+ if [[ "$line" =~ \/\*.*\*\/ || "$line" =~ .*\"\\.* || "$line" =~ ^$ || "$line" =~ STACKKEYS || "$line" =~ TAGKEYS || "$line" =~ static\ Keychord\ \*keychords || "$line" =~ static\ Shortcut\ \shortcuts || "$line" =~ ^\#.* ]]; then
+ continue
+ fi
+
+ if [[ "$line" =~ ^\/\/.* ]]; then
+ echo ".LIST OFF" >>"$temp_file_before"
+ line=$(echo "$line" | sed -e 's/\/\/\s*//g' | awk '{for (i=1; i<=NF; i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2)); print}')
+ output_line=".HEADING 5 \"$line\""
+ echo "$output_line" >>"$temp_file_before"
+ echo ".LI" >>"$temp_file_before"
+ continue
+ fi
+
+ if echo "$line" | grep -q "Keychord"; then
+ line=$(echo "$line" | sed -e "s/&((Keychord){[0-9], {{\(.*\)}),/\1/g;s/^0\(.*XF86XK_.*\)/MEDIA\1/g;/XF86XK_/!s/^0/Null/;s/\([MEDIA\|WIN\|ALT\|ULTRA\|EXTRA\|Shift\|Control]\w\+\),\s\(.*\)}},\s\+\(\w\+\),\s\([^,]*\)/\1|\2|\3|\4/g;s/},{0,//g;s/},{\(Shift\|Ctrl\),/\1/g;s/},{//g;s/Null//g")
+ else
+ line=$(echo "$line" | sed -e "s/^{ \(.*\) },/\1/g;s/{ \([^, ]*\), \([^, ]*\), \([^, ]*\), \([^}]*\) }/\1 \2 \3 \4/g;s/,/|/g")
+ fi
+
+ modkey=$(echo "$line" | awk -F'|' '{print $1}' | sed "s/\b0//g;
+ s/WINKEY/WIN/g;s/WINMODALL/WIN+Ctrl+Shift/g;s/WINMOD2/WIN+Ctrl/g;s/WINMOD/WIN+Shift/g;
+ s/ALTKEY/ALT/g;s/ALTMODALL/ALT+Ctrl+Shift/g;s/ALTMOD2/ALT+Ctrl/g;s/ALTMOD/ALT+Shift/g;
+ s/ULTRAKEY/WIN+ALT/g;s/ULTRAMODALL/WIN+ALT+Ctrl+Shift/g;s/ULTRAMOD2/WIN+ALT+Ctrl/g;s/ULTRAMOD/WIN+ALT+Shift/g;
+ s/EXTRAMOD/Ctrl+Shift/g;
+ s/\(Shift\|Control\)Mask/\1/g;
+ s/Control/Ctrl/g;
+ s/ //g;
+ s/XK_NO_MOD/NO_MOD/g;
+ s/XK_ANY_MOD/ANY_MOD/g")
+
+ key=$(echo "$line" | awk -F'|' '{print $2}' | sed "s/\b0//g;
+ s/XF86XK_//g;
+ s/XK_//g;
+ s/\s*\(.*\)_R$/Right_\1/g;
+ s/\s*\(.*\)_L$/Left_\1/g;
+ s/MODKEY\(\d*\)/MOD\1/g;
+ s/\(Shift\|Control\)Mask[?,]/\<\1\>/g;
+ s/Control/Ctrl/g;
+ s/space/<space>/g;
+ s/BackSpace/<backspace>/g;
+ s/apostrophe/\'/g;
+ s/comma/,/g;
+ s/bracketleft/[/g;
+ s/bracketright/]/g;
+ s/equal/=/g;
+ s/grave/\`/g;
+ s/minus/-/g;
+ s/period/./g;
+ s/slash/\//g;
+ s/semicolon/;/g;
+ s/|/+/g;
+ s/ //g")
+
+ func=$(echo "$line" | awk -F'|' '{print $3}' | grep -v "spawn" | sed "s/ //g")
+
+ args=$(echo "$line" | cut -d'|' -f4- | sed -E 's/.*\.v\s*=\s*\(const\s*char\s*\*\[\]\)\s*\{\s*([^}]*)\s*\}.*/\1/g;
+ s/.*\.v\s*=\s*\(int\s*\[\]\)\s*\{\s*([^}]*)\s*\}.*/\1/g;
+ s/.*SHCMD\((.*)\).*/\1/g;
+ s/.*\{\s*\.\w*\s*=\s*(.*)\s*\}.*/\1/g;
+ s/\{0\}//g;
+ s/\"//g;
+ s/\,//g;
+ s/NULL//g;
+ s/TERMINAL\s*-e\s*//g;
+ s/&layouts\[0\]/"[]="/g;
+ s/&layouts\[1\]/"[M]"/g;
+ s/&layouts\[2\]/"|||"/g;
+ s/&layouts\[3\]/"[@]"/g;
+ s/&layouts\[4\]/"[\\\\]"/g;
+ s/&layouts\[5\]/"H[]"/g;
+ s/&layouts\[6\]/"TTT"/g;
+ s/&layouts\[7\]/"==="/g;
+ s/&layouts\[8\]/"HHH"/g;
+ s/&layouts\[9\]/"###"/g;
+ s/&layouts\[10\]/"---"/g;
+ s/&layouts\[11\]/":::"/g;
+ s/&layouts\[12\]/"|M|"/g;
+ s/&layouts\[13\]/">M>"/g;
+ s/^\s*//g;
+ /^\s*0\s*$/d
+ ')
+
+ # args=$(echo "$line" | cut -d',' -f4- | sed "s/\.v = (const char \*\[\])//g;s/{\s*\.v = (int \[\])//g;s/SHCMD(\(.*\))/\1/g;s/\.. = //g")
+
+ if [[ -z "$modkey" ]]; then
+ if [[ -z $func ]]; then
+ output_line="\\f(CW${key}\\fP \\(en ${args}"
+ else
+ output_line="\\f(CW${key}\\fP \\(en ${func} ${args}"
+ fi
+ else
+ if [[ -z $func ]]; then
+ output_line="\\f(CW${modkey}+${key}\\fP \\(en ${args}"
+ else
+ output_line="\\f(CW${modkey}+${key}\\fP \\(en ${func} ${args}"
+ fi
+ fi
+
+ echo ".ITEM" >>"$temp_file_before"
+ echo "$output_line" >>"$temp_file_before"
+ done
+
+ echo ".LIST OFF" >>"$temp_file_before"
+done
+
+# Append the latter part of the document that follows the end marker
+cat "$temp_file_after" >>"$temp_file_before"
+
+# Replace the original file with the updated content
+mv -f "$temp_file_before" "$output_file"
+cp -f "$output_file" "$readme_file"
+
+# Clean up the temporary after-section file
+rm -f "$temp_file_after"
+
+notify-send "👍 Operation completed successfully."
diff --git a/debian/.local/bin/fzffiles b/debian/.local/bin/fzffiles
new file mode 100755
index 0000000..97047e9
--- /dev/null
+++ b/debian/.local/bin/fzffiles
@@ -0,0 +1,99 @@
+#!/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 "selection={};
+ clean=\$(printf '%s' \"\$selection\" | sed -e 's/^📄 //' -e 's/^✏ //' -e 's/^✅ //' -e 's/^❌ //' -e 's/^🔀 //' -e 's/^❓ //');
+ [ -z \"\$clean\" ] && { echo 'No selection'; exit 0; }
+ target=\$(readlink -f \"\$clean\" 2>/dev/null || printf '%s' \"\$clean\");
+ if [ -z \"\$target\" ]; then
+ echo 'Could not resolve path';
+ exit 0;
+ fi
+ if [ -f \"\$target\" ]; then
+ dir=\$(dirname \"\$target\");
+ if git_root=\$(git -C \"\$dir\" rev-parse --show-toplevel 2>/dev/null); then
+ rel=\${target#\"\$git_root\"/};
+ diff_output=\$(git -C \"\$git_root\" diff --color -- \"\$rel\");
+ if [ -n \"\$diff_output\" ]; then
+ printf '%s\n' \"\$diff_output\"
+ exit 0
+ fi
+ diff_output=\$(git -C \"\$git_root\" diff --color --cached -- \"\$rel\");
+ if [ -n \"\$diff_output\" ]; then
+ printf '%s\n' \"\$diff_output\"
+ exit 0
+ fi
+ fi
+ fi
+ 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 \"\$target\"
+ 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_SCRIPTS_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 top=\$(git rev-parse --show-toplevel 2>/dev/null); git -C \"\$top\" status --porcelain | awk -v root=\"\$top\" '{
+ staged=substr(\$0,1,1);
+ unstaged=substr(\$0,2,1);
+ file=substr(\$0,4);
+ gsub(/^ +/, \"\", file);
+ if (file == \"\") next;
+ if (staged == \"?\" && unstaged == \"?\") icon=\"📄\";
+ else if (staged == \"!\" && unstaged == \"!\") icon=\"❌\";
+ else if (staged != \" \" && staged != \"?\" && unstaged != \" \" && unstaged != \"?\") icon=\"🔀\";
+ else if (staged != \" \" && staged != \"?\") icon=\"✅\";
+ else if (unstaged != \" \") icon=\"✏\";
+ else icon=\"❓\";
+ print icon \" \" root \"/\" file
+ }'; 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
+
+files=$(printf '%s\n' "$files" | sed -e 's/^📄 //' -e 's/^✏ //' -e 's/^✅ //' -e 's/^❌ //' -e 's/^🔀 //' -e 's/^❓ //')
+
+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/debian/.local/bin/fzffns b/debian/.local/bin/fzffns
new file mode 100755
index 0000000..c919723
--- /dev/null
+++ b/debian/.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/debian/.local/bin/fzfpass b/debian/.local/bin/fzfpass
new file mode 100755
index 0000000..5190f8e
--- /dev/null
+++ b/debian/.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/debian/.local/bin/getbib b/debian/.local/bin/getbib
new file mode 100755
index 0000000..1eb0731
--- /dev/null
+++ b/debian/.local/bin/getbib
@@ -0,0 +1,71 @@
+#!/bin/sh
+
+bib_file="${HOME}/latex/uni.bib"
+[ -f "${bib_file}" ] || bib_file="${2:-$(find "${HOME}" -path "${HOME}/.*" \
+ -prune -o -type "f" -name "*.bib" -print -quit)}"
+
+{ [ -f "${bib_file}" ] || [ "${2}" ]; } || {
+ printf "%s\n" "Create a .bib file or provide as \$2." && exit "1"
+}
+
+filter() {
+ sed -n -E 's/.*((DOI|doi)((\.(org))?\/?|:? *))([^: ]+[^ .]).*/\6/p; T; q'
+}
+
+fpdf() {
+ pdf="${1}"
+ doi="$(pdfinfo "${pdf}" 2>"/dev/null" | filter)"
+
+ [ "${doi}" ] || doi="$(pdftotext -q -l "2" "${pdf}" - 2>"/dev/null" | filter)"
+
+ [ "${doi}" ] || printf "%s\n" "No DOI found for PDF: ${pdf}" >&2
+
+ printf "%s\n" "${doi}"
+}
+
+arrange() {
+ sed 's/\}, /\},\n /g
+ s/, /,\n /
+ s/ }/\n}/
+ s/,\s*pages=/,\n\tpages=/' |
+ sed '1s/^ *//
+ 1s/[0-9]*\([0-9]\{2\}\)/\1/
+ 1s/_//
+ 1s/.*/\L&/
+ s/.*=/\L&/
+ s/=/ = /'
+}
+
+doi2bib() {
+ doi="${1#doi:}"
+ url="https://api.crossref.org/works/${doi}/transform/application/x-bibtex"
+ entry="$(curl -kLsS --no-fail "${url}" | arrange)"
+ red='\033[0;31m'
+ reset='\033[0m'
+
+ printf "${red}%s${reset}\n" "${entry}"
+
+ [ "${entry%"${entry#?}"}" != "@" ] && {
+ printf "%s\n" "Failed to fetch bibtex entry for DOI: ${doi}"
+ return "1"
+ }
+
+ grep -iFq "doi = {${doi}}" "${bib_file}" 2>"/dev/null" && {
+ printf "%s\n" "Bibtex entry for DOI: ${doi} already exists in the file."
+ } || {
+ [ -s "${bib_file}" ] && printf "\n" >>"${bib_file}"
+ printf "%s\n" "${entry}" >>"${bib_file}"
+ printf "%s\n" "Added bibtex entry for DOI: ${doi}"
+ }
+}
+
+[ "${1}" ] || {
+ printf "%s\n" "Give either a pdf file or a DOI or a directory path that has PDFs as an argument."
+ exit "1"
+}
+
+[ -f "${1}" ] && doi="$(fpdf "${1}")" && doi2bib "${doi}" && exit "0"
+
+[ -d "${1}" ] && for i in "${1}"/*.pdf; do doi="$(fpdf "${i}")" && doi2bib "${doi}"; done && exit "0"
+
+doi="$(printf "%s\n" "${1}" | filter)" && doi2bib "${doi}"
diff --git a/debian/.local/bin/getcomproot b/debian/.local/bin/getcomproot
new file mode 100755
index 0000000..dbee348
--- /dev/null
+++ b/debian/.local/bin/getcomproot
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+# A helper script for LaTeX/groff files used by `compiler` and `opout`.
+# The user can add the root file of a larger project as a comment as below:
+# % root = mainfile.tex
+# And the compiler script will run on that instead of the opened file.
+
+texroot="$(sed -n 's/^\s*%.*root\s*=\s*\(\S\+\).*/\1/p' "${1}")"
+[ -f "${texroot}" ] && readlink -f "${texroot}" || exit "1"
diff --git a/debian/.local/bin/getkeys b/debian/.local/bin/getkeys
new file mode 100755
index 0000000..492b056
--- /dev/null
+++ b/debian/.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/debian/.local/bin/gitfiles b/debian/.local/bin/gitfiles
new file mode 100755
index 0000000..510b032
--- /dev/null
+++ b/debian/.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/debian/.local/bin/gitopenbranch b/debian/.local/bin/gitopenbranch
new file mode 100755
index 0000000..56c8d12
--- /dev/null
+++ b/debian/.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/debian/.local/bin/gitstagedfiles b/debian/.local/bin/gitstagedfiles
new file mode 100755
index 0000000..1cdd902
--- /dev/null
+++ b/debian/.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/debian/.local/bin/gitupdate b/debian/.local/bin/gitupdate
new file mode 100755
index 0000000..a2c65ba
--- /dev/null
+++ b/debian/.local/bin/gitupdate
@@ -0,0 +1,85 @@
+#!/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}"
+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
+ # Add and commit changes
+ git add --all .
+ git commit -m "$message"
+ 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/debian/.local/bin/gpt b/debian/.local/bin/gpt
new file mode 100755
index 0000000..fb12148
--- /dev/null
+++ b/debian/.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/debian/.local/bin/gracefulkill b/debian/.local/bin/gracefulkill
new file mode 100755
index 0000000..918ab79
--- /dev/null
+++ b/debian/.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/debian/.local/bin/hugow b/debian/.local/bin/hugow
new file mode 100755
index 0000000..0460174
--- /dev/null
+++ b/debian/.local/bin/hugow
@@ -0,0 +1,70 @@
+#!/bin/sh
+set -eu
+
+repodir="$HOME/Private/repos/THESIAH"
+out="$repodir/public/recordings/index.html"
+server="${THESIAH_SERVER:-root@thesiah.xyz}"
+dest="/var/www/thesiah/recordings/"
+defaults="sy after foramonth"
+src="$repodir/public/recordings/"
+
+cd "$repodir"
+hugo --cleanDestinationDir
+
+if [ ! -f "$out" ]; then
+ echo "error: not found: $out" >&2
+ exit 1
+fi
+
+tmp="$(mktemp "$out.XXXXXXXX.tmp")"
+
+awk -v defaults="$defaults" '
+BEGIN {
+ n = split(defaults, a, /[[:space:]]+/)
+ insert = ""
+ for (i = 1; i <= n; i++) {
+ if (a[i] == "") continue
+ name = a[i] ".mp4"
+ insert = insert \
+" <li>\n" \
+" <a href=\"/recordings/" name "\" data-name=\"" name "\" class=\"vid\">" name "</a>\n" \
+" </li>\n"
+ }
+ injected = 0
+}
+{
+ print
+ if (!injected && $0 ~ /<ul[^>]*id=["'\''"]list["'\''"][^>]*>/) {
+ printf("%s", insert)
+ injected = 1
+ }
+}
+END { if (!injected) exit 2 }
+' "$out" >"$tmp"
+
+mv "$tmp" "$out"
+echo "Injected defaults into: $out"
+
+ssh "$server" "mkdir -p '$dest'"
+
+if [ -n "${THESIAH_SSH_OPTS:-}" ]; then
+ rsync -azv --update --itemize-changes --stats \
+ -e "ssh $THESIAH_SSH_OPTS" \
+ "$src" "$server:$dest"
+else
+ rsync -azv --update --itemize-changes --stats \
+ "$src" "$server:$dest"
+fi
+
+if [ -n "${1-}" ]; then
+ new="$repodir/content/recordings/$1"
+ if [ -f "$new" ]; then
+ rsync -az --update "$new" "$server:$dest"
+ elif [ -d "$new" ]; then
+ rsync -az --update "$new/" "$server:$dest$(basename "$new")/"
+ fi
+fi
+
+ssh "$THESIAH_SERVER" "chmod 644 $dest/index.html"
+
+cd - >/dev/null
diff --git a/debian/.local/bin/iconupdate b/debian/.local/bin/iconupdate
new file mode 100755
index 0000000..0a1aebb
--- /dev/null
+++ b/debian/.local/bin/iconupdate
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+icons="${XDG_DOTFILES_DIR:-$HOME/.dotfiles}/global/.local/share/thesiah/chars/icons"
+tmpfile="$(mktemp)"
+
+curl -sSL -o "$tmpfile" "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/metadata/icons.json" || exit 1
+
+while read -r hex name; do
+ hex_uc=$(printf '%s' "$hex" | tr '[:lower:]' '[:upper:]')
+ if [[ ${#hex_uc} -le 4 ]]; then
+ unicode="\\u$hex_uc"
+ else
+ unicode="\\U$(printf '%08X' "0x$hex_uc")"
+ fi
+ printf "%b %s; %s\n" "$unicode" "$name" "$hex"
+done < <(jq -r 'to_entries[] | select(.value.unicode) | .value.unicode + " " + .key' "$tmpfile") >"$icons"
+
+rm -f "$tmpfile"
+echo "Processing complete! icons saved to: $icons"
diff --git a/debian/.local/bin/ifinstalled b/debian/.local/bin/ifinstalled
new file mode 100755
index 0000000..2f9653f
--- /dev/null
+++ b/debian/.local/bin/ifinstalled
@@ -0,0 +1,13 @@
+#!/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 ! command -v "$x" >/dev/null 2>&1; then
+ echo "📩 $x must be installed and in PATH." >&2
+ exit 1
+ fi
+done
diff --git a/debian/.local/bin/lastfiles b/debian/.local/bin/lastfiles
new file mode 100755
index 0000000..b1ab6c9
--- /dev/null
+++ b/debian/.local/bin/lastfiles
@@ -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/debian/.local/bin/lfub b/debian/.local/bin/lfub
new file mode 100755
index 0000000..c999412
--- /dev/null
+++ b/debian/.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/debian/.local/bin/linkhandler b/debian/.local/bin/linkhandler
new file mode 100755
index 0000000..6e007bc
--- /dev/null
+++ b/debian/.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/debian/.local/bin/maimpick b/debian/.local/bin/maimpick
new file mode 100755
index 0000000..06d9092
--- /dev/null
+++ b/debian/.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/debian/.local/bin/mbackup b/debian/.local/bin/mbackup
new file mode 100755
index 0000000..71856b1
--- /dev/null
+++ b/debian/.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/debian/.local/bin/monitorbright b/debian/.local/bin/monitorbright
new file mode 100755
index 0000000..2de2e5d
--- /dev/null
+++ b/debian/.local/bin/monitorbright
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+monitor=$(xrandr --query | grep -i '\sconnected' | grep '[0-9]x[0-9]' | grep -i 'primary' | cut -d ' ' -f1)
+[ -z "$monitor" ] && monitor=$(xrandr --query | grep -i '\sconnected' | grep '[0-9]x[0-9]' | cut -d ' ' -f1)
+case "$monitor" in
+*DP* | *HDMI*)
+ current_brightness=$(xrandr --verbose | grep -i "^$monitor connected" -A5 | grep -i "Brightness:" | cut -d ' ' -f2)
+ [ -z "$current_brightness" ] && exit 1
+ if [ "$#" -eq 2 ]; then
+ scale_change=$(echo "$2 / 100" | bc -l)
+ case "$1" in
+ "-inc") new_brightness=$(echo "$current_brightness + $scale_change" | bc -l) ;;
+ "-dec") new_brightness=$(echo "$current_brightness - $scale_change" | bc -l) ;;
+ *) echo "Invalid argument $1. Use -inc or -dec." && exit 1 ;;
+ esac
+ new_brightness=$(echo "if ($new_brightness > 1) 1 else if ($new_brightness < 0) 0 else $new_brightness" | bc -l)
+ xrandr --output "$monitor" --brightness "$new_brightness"
+ current_brightness=$(echo "$new_brightness * 100" | bc -l)
+ else
+ current_brightness=$(echo "$current_brightness * 100" | bc -l)
+ fi
+ printf "đŸȘŸ%.0f%%\n" "$current_brightness"
+ ;;
+esac
diff --git a/debian/.local/bin/mounter b/debian/.local/bin/mounter
new file mode 100755
index 0000000..a9d7a61
--- /dev/null
+++ b/debian/.local/bin/mounter
@@ -0,0 +1,212 @@
+#!/bin/bash
+
+# Mounts Android Phones and USB drives (encrypted or not). This script will
+# replace the older `dmenumount` which had extra steps and couldn't handle
+# encrypted drives.
+# TODO: Try decrypt for drives in crtypttab
+# TODO: Add some support for connecting iPhones (although they are annoying).
+
+IFS='
+'
+# Function for escaping cell-phone names.
+escape() { echo "$@" | iconv -cf UTF-8 -t ASCII//TRANSLIT | tr -d '[:punct:]' | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | sed "s/-\+/-/g;s/\(^-\|-\$\)//g"; }
+
+# Function for prompting user for a mountpoint.
+getmount() {
+ [ -z "$1" ] && mp="$(find "/media/$USER" /mnt -mindepth 1 -maxdepth 1 -type d 2>/dev/null | dmenu -i -p "Mount this drive where?")" || mp="$1"
+ test -n "$mp"
+ if [ ! -d "$mp" ]; then
+ mkdiryn=$(printf "Yes\nNo" | dmenu -i -p "$mp does not exist. Create it?")
+ [ "$mkdiryn" = "Yes" ] && (mkdir -p "$mp" 2>/dev/null || sudo -A mkdir -p "$mp")
+ fi
+}
+
+attemptmount() {
+ # Attempt to mount without a mountpoint, to see if drive is in fstab.
+ mplabel=$(sudo lsblk -no "label" "$chosen")
+ mp="/media/$USER/$mplabel"
+ [ -n "$mplabel" ] || return
+ if [ ! -d "$mp" ] && [ ! -d "/mnt/$mplabel" ]; then
+ getmount "$mp" && sudo -A mount "$chosen" "$mp" >/dev/null 2>&1 || return 1
+ elif [ -d "$mp" ] && [ ! -d "/mnt/$mplabel" ]; then
+ sudo -A mount "$chosen" "$mp" >/dev/null 2>&1 || return 1
+ elif [ -d "/mnt/$mplabel" ]; then
+ getmount "/mnt/$mplabel" && sudo -A mount "$chosen" "/mnt/$mplabel" >/dev/null 2>&1 || return 1
+ else
+ sudo -A mount "$chosen" >/dev/null 2>&1 || return 1
+ fi
+}
+
+notify-send -t 10000 "🔎 Searching drives to mount..."
+
+# Check for phones.
+phones="$(simple-mtpfs -l 2>/dev/null | sed "s/^/đŸ“±/")"
+mountedphones="$(grep "simple-mtpfs" /etc/mtab)"
+# If there are already mounted phones, remove them from the list of mountables.
+[ -n "$mountedphones" ] && phones="$(for phone in $phones; do
+ for mounted in $mountedphones; do
+ escphone="$(escape "$phone")"
+ [[ "$mounted" =~ $escphone ]] && break 1
+ done && continue 1
+ echo "$phone"
+done)"
+
+iphones="$(ideviceinfo | grep -E "^DeviceName" | sed "s/DeviceName: //;s/^/🍎/")"
+mountediphones="$(grep "ifuse" /etc/mtab)"
+[ -n "$mountediphones" ] && iphones="$(for iphone in $iphones; do
+ for mounted in $mountediphones; do
+ esciphone="$(escape "$iphone")"
+ [[ "$mounted" =~ $esciphone ]] && break 1
+ done && continue 1
+ echo "$iphone"
+done)"
+
+# Check for drives.
+lsblkoutput="$(sudo lsblk -rpo "uuid,name,type,size,label,mountpoint,fstype")"
+# Get all LUKS drives
+allluks="$(echo "$lsblkoutput" | grep crypto_LUKS)"
+# Get a list of the LUKS drive UUIDs already decrypted.
+decrypted="$(find /dev/disk/by-id/dm-uuid-CRYPT-LUKS2-* | sed "s|.*LUKS2-||;s|-.*||")"
+# Functioning for formatting drives correctly for dmenu:
+filter() { sed "s/ /:/g" | awk -F':' '$7==""{printf "%s%s (%s) %s\n",$1,$3,$5,$6}'; }
+
+# Get only LUKS drives that are not decrypted.
+unopenedluks="$(for drive in $allluks; do
+ uuid="${drive%% *}"
+ uuid="${uuid//-/}" # This is a bashism.
+ [ -n "$decrypted" ] && for open in $decrypted; do
+ [ "$uuid" = "$open" ] && break 1
+ done && continue 1
+ echo "🔒 $drive"
+done | filter)"
+
+# Get all normal, non-encrypted or decrypted partitions that are not mounted.
+normalparts="$(echo "$lsblkoutput" | grep -v crypto_LUKS | grep -v 'part 1M' | grep 'part\|rom\|crypt' | sed "s/^/đŸ’Ÿ /" | filter)"
+
+# Get all available IP addresses with open Samba shares in the wlan0 subnet, excluding eth0 IP
+smbips="$(sudo arp-scan --interface=eth0 --interface=wlan0 --localnet | grep -vEi '(EFM Networks|DUP:)' | awk '/^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/{print $1}')"
+
+# Get currently mounted CIFS shares
+mountedsmbs="$(grep 'cifs' /etc/mtab)"
+
+for smbip in $smbips; do
+ win=$(
+ smbclient -L "$smbip" -U% -A /dev/stdin <<EOF 2>/dev/null | awk '/Disk/ {print $1}' | grep -vEi '(ADMIN|\w)\$'
+username=$(whoami)
+password=$(pass show default/windows)
+EOF
+ )
+ mac=$(
+ smbclient -L "$smbip" -U% -A /dev/stdin <<EOF 2>/dev/null | awk '/Disk/ {print $1}' | grep -vEi '(ADMIN|\w)\$|Macintosh'
+username=$(whoami)
+password=$(pass show default/mac)
+EOF
+ )
+ smb=$(
+ smbclient -L "$smbip" -U% -A /dev/stdin <<EOF 2>/dev/null | awk '/Disk/ {print $1}' | grep -vEi '(ADMIN|\w)\$|Macintosh'
+username=$(whoami)
+password=$(pass show default/default)
+EOF
+ )
+ while IFS= read -r share; do
+ if ! echo "$smbshares" | grep -q "$share"; then
+ smbshares+="//$smbip/$share"$'\n'
+ fi
+ done <<<"$win"
+ while IFS= read -r share; do
+ if ! echo "$smbshares" | grep -q "$share"; then
+ smbshares+="//$smbip/$share"$'\n'
+ fi
+ done <<<"$mac"
+ while IFS= read -r share; do
+ if ! echo "$smbshares" | grep -q "$share"; then
+ smbshares+="//$smbip/$share"$'\n'
+ fi
+ done <<<"$smb"
+done
+
+smbshares="$(echo "$smbshares" | sed '/^$/d')"
+
+[ -n "$smbshares" ] && smbs="$(echo "$smbshares" | while IFS= read -r smb; do
+ [ -n "$mountedsmbs" ] && for mountedsmb in $mountedsmbs; do
+ mountedsmb="${mountedsmb%% *}"
+ [[ "$mountedsmb" =~ $smb ]] && break 1
+ done && continue 1
+ echo "📡 ${smb#//*}"
+done)"
+
+# Add all to one variable. If no mountable drives found, exit.
+alldrives="$(echo "$phones
+$iphones
+$unopenedluks
+$normalparts
+$smbs" | sed "/^$/d;s/ *$//")"
+
+# Quit the script if a sequential command fails.
+set -e
+test -n "$alldrives" || { echo "No mountable drives" && exit; }
+
+# Feed all found drives to dmenu and get user choice.
+chosen="$(echo "$alldrives" | dmenu -p "Mount which drive?" -i)"
+case "$chosen" in
+đŸ’Ÿ*)
+ chosen="${chosen%% *}"
+ chosen="${chosen:1}" # This is a bashism.
+ parttype="$(echo "$lsblkoutput" | grep "$chosen")"
+ attemptmount || {
+ getmount
+ case "${parttype##* }" in
+ vfat) sudo -A mount -t vfat "$chosen" "$mp" -o rw,umask=0000 ;;
+ btrfs) sudo -A mount "$chosen" "$mp" ;;
+ *) sudo -A mount "$chosen" "$mp" -o uid="$(id -u)",gid="$(id -g)" ;;
+ esac
+ }
+ notify-send "đŸ’Ÿ Drive Mounted." "$chosen mounted to $mp."
+ ;;
+🔒*)
+ chosen="${chosen%% *}"
+ chosen="${chosen:1}" # This is a bashism.
+ # Number the drive.
+ while [ -e "/dev/mapper/usb$num" ]; do
+ num=$((num + 1))
+ done
+ pass show default/default | sudo cryptsetup open "$chosen" "usb$num" || ${TERMINAL:-st} -n floatterm -g 60x1 -e sudo cryptsetup open "$chosen" "usb$num"
+ # Check if now decrypted.
+ test -b "/dev/mapper/usb$num"
+ chosen="/dev/mapper/usb$num"
+ attemptmount || {
+ getmount
+ sudo -A mount "/dev/mapper/usb$num" "$mp" -o uid="$(id -u)",gid="$(id -g)" || sudo -A mount "/dev/mapper/usb$num" "$mp"
+ }
+ notify-send "🔓 Decrypted drive Mounted." "$chosen decrypted and mounted to $mp."
+ ;;
+đŸ“±*)
+ notify-send "❗ Note" "Remember to allow file access on your phone now."
+ getmount
+ number="${chosen%%:*}"
+ number="${chosen:1}" # This is a bashism.
+ sudo -A simple-mtpfs -o allow_other -o fsname="simple-mtpfs-$(escape "$chosen")" --device "$number" "$mp" 2>dev/null
+ notify-send "đŸ€– Android Mounted." "Android device mounted to $mp."
+ ;;
+🍎*)
+ getmount
+ number="${chosen%%:*}"
+ number="${chosen:1}" # This is a bashism.
+ sudo -A ifuse "$mp" 2>dev/null
+ notify-send "🍎 iPhone Mounted." "$chosen mounted to $mp."
+ ;;
+📡*)
+ chosen="//${chosen##* }"
+ path="${chosen##*/}"
+ if getmount "/media/$USER/$path"; then
+ sudo -A mount -t cifs -o username="$(whoami)",password="$(pass show default/default)" "$chosen" "$mp" 2>/dev/null ||
+ sudo -A mount -t cifs -o username="$(whoami)",password="$(pass show default/windows)" "$chosen" "$mp" 2>/dev/null
+ else
+ notify-send "❌ Failed to mount samba" "$chosen
+ Check if $(whoami) is added to samba user list."
+ sudo rm -rf "$mp"
+ exit
+ fi
+ notify-send "📡 Samba successfully mounted to" "$mp"
+ ;;
+esac
diff --git a/debian/.local/bin/mpdmenu b/debian/.local/bin/mpdmenu
new file mode 100755
index 0000000..126a876
--- /dev/null
+++ b/debian/.local/bin/mpdmenu
@@ -0,0 +1,113 @@
+#!/bin/bash
+
+all_name='[ALL]'
+playlist_dir="${XDG_CONFIG_HOME:-${HOME}/.config}/mpd/playlists"
+
+# Functions
+d_artist() {
+ mpc list artist | sort -f | dmenu -i -p artist "${dmenu_args[@]}" || exit
+}
+
+d_album() {
+ local artist="$1"
+ local albums
+
+ mapfile -t albums < <(mpc list album artist "$artist")
+ if ((${#albums[@]} > 1)); then
+ {
+ printf '%s\n' "$all_name"
+ printf '%s\n' "${albums[@]}" | sort -f
+ } | dmenu -i -p album "${dmenu_args[@]}" || exit
+ else
+ # We only have one album, so just use that.
+ printf '%s\n' "${albums[0]}"
+ fi
+}
+
+d_title() {
+ titles=$(mpc list title | sort -f)
+ [[ -z "$titles" ]] && exit
+ echo "$titles" | dmenu -i -p "Select title:" "${dmenu_args[@]}"
+}
+
+d_playlist() {
+ playlists=$(find "$playlist_dir" \( -type f -o -type l \) -name "*.m3u" -exec basename {} .m3u \;)
+
+ selected_playlist=$(echo "$playlists" | sort | dmenu -i -p "Select Playlist:" "${dmenu_args[@]}")
+
+ printf '%s' "$selected_playlist"
+}
+
+d_queue() {
+ format="%position% %title%"
+ extra_format="(%artist% - %album%)"
+
+ # If all tracks are from the same artist and album, no need to display that
+ num_extras=$(mpc playlist -f "$extra_format" | sort | uniq | wc -l)
+ ((num_extras == 1)) || format+=" $extra_format"
+
+ track=$(mpc playlist -f "$format" | dmenu -i -p track "${dmenu_args[@]}")
+ printf '%s' "${track%% *}"
+}
+
+# Parse arguments (if any)
+i=2
+for arg in "$@"; do
+ if [[ $arg == :: ]]; then
+ dmenu_args=("${@:$i}")
+ break
+ fi
+
+ case "$arg" in
+ -l) mode="library" ;;
+ -p) mode="playlist" ;;
+ -q) mode="queue" ;;
+ esac
+
+ ((i + 1))
+done
+
+# Main Menu
+[[ -z "$mode" ]] && mode=$(echo -e "library\nplaylist\nqueue" | dmenu -i -p "Choose mode:" "${dmenu_args[@]}")
+
+# Mode Handling
+case "$mode" in
+"library")
+ search_type=$(echo -e "artist\ntitle" | dmenu -i -p "Search by:" "${dmenu_args[@]}")
+ case "$search_type" in
+ "artist")
+ artist=$(d_artist)
+ album=$(d_album "$artist")
+ mpc clear
+ if [[ $album == "$all_name" ]]; then
+ mpc find artist "$artist" | sort | mpc add
+ else
+ mpc find artist "$artist" album "$album" | sort | mpc add
+ fi
+ ;;
+ "title")
+ title=$(d_title)
+ mpc clear
+ mpc find title "$title" | sort | mpc add
+ ;;
+ *) exit ;;
+ esac
+ mpc random on
+ mpc repeat on
+ mpc play 2>/dev/null
+ ;;
+"playlist")
+ mpc clear
+ mpc load "$(d_playlist)" 2>/dev/null || exit
+ mpc random on
+ mpc repeat on
+ mpc play 2>/dev/null
+ ;;
+"queue")
+ ! mpc playlist | grep -q '.' && exit
+ mpc play "$(d_queue)" 2>/dev/null || exit
+ ;;
+*) exit ;;
+esac
+
+sleep 0.5 && mpc volume 50
diff --git a/debian/.local/bin/mpvplay b/debian/.local/bin/mpvplay
new file mode 100755
index 0000000..f5a899e
--- /dev/null
+++ b/debian/.local/bin/mpvplay
@@ -0,0 +1,210 @@
+#!/bin/sh
+
+mount_script="${XDG_SCRIPTS_HOME:-${HOME}/.local/bin}/ecrypt"
+db_path="$HOME/.local/share/history/mpv.sqlite"
+
+check_mount() { findmnt "$HOME/Private" >/dev/null || $mount_script; }
+
+check_unmount() { findmnt "$HOME/Private" >/dev/null && $mount_script; }
+
+loginurl() {
+ notify-send "🔑 Authentication required"
+ username="$(echo | dmenu -i -p "Enter a username:")"
+ [ -n "$username" ] && password="$(echo | dmenu -i -P -p "Enter a password:")" || exit
+ if [ -n "$username" ] && [ -n "$password" ]; then
+ if ! mpv --x11-name=video --ytdl-format='bestvideo[height<=1080]+bestaudio/best[height<=1080]' --ytdl-raw-options=username="$username",password="$password" "$url"; then
+ notify-send "❌ Failed to play $url" "❗ Check your username or password"
+ exit 1
+ fi
+ fi
+}
+
+play_url() {
+ url=$(xclip -selection clipboard -o)
+ [ -n "$url" ] && echo "$url" | grep -E '^https?://' >/dev/null || return 1
+ if yt-dlp --simulate --dump-json "$url" >/dev/null 2>&1; then
+ notify-send "đŸ“œïž Playing video from URL:" "$url"
+ mpv --x11-name=video --ytdl-format='bestvideo[height<=1080]+bestaudio/best[height<=1080]' "$url"
+ else
+ loginurl
+ fi
+}
+
+play_media() {
+ if echo "$1" | grep -q ".*\.m3u$"; then
+ playlist_file="${1#--playlist=}"
+ if grep -q "/home/$USER/Private" "$playlist_file"; then
+ mpv --x11-name=video "$@" && check_unmount || exit
+ else
+ $mount_script && mpv --x11-name=video "$@" || exit
+ fi
+ elif echo "$1" | grep -q "/home/$USER/Private"; then
+ mpv --x11-name=video "$@" && check_unmount || exit
+ else
+ $mount_script && mpv --x11-name=video "$@" || exit
+ fi
+}
+
+play_playlist() { play_media "--playlist=$1"; }
+
+tmp_playlist() {
+ playlistdir="$HOME/.config/mpv/playlists"
+ [ -d "$playlistdir" ] || mkdir -p "$playlistdir"
+ tmplist="$playlistdir/tmplist.m3u"
+ [ -f "$tmplist" ] && rm -rf "$tmplist"
+ find "$1" -maxdepth 1 \
+ -type f \( -iname "*.mp4" -o -iname "*.mkv" -o -iname "*.mov" -o -iname "*.flv" -o -iname "*.wmv" -o -iname "*.webm" -o -iname "*.mpeg" -o -iname "*.mpg" -o -iname "*.avi" -o -iname "*.ts" -o -iname "*.3gp" -o -iname "*.rmvb" \) |
+ sort >>"$tmplist"
+ play_playlist "$tmplist"
+ rm -rf "$tmplist"
+}
+
+list_and_play() {
+ dir=$1
+ choice=$(printf "List files\nEnter filenames" | dmenu -i -p "Choose an option:")
+ case "$choice" in
+ "Enter filenames")
+ search_term=$(echo | dmenu -i -p "File names:")
+ [ -z "$search_term" ] && echo "Invalid search term \"$search_term\"" && exit
+ notify-send "🔎 Finding videos named with '$search_term'.."
+ files=$(find "$dir" \
+ -type f \( -iname "*.mp4" -o -iname "*.mkv" -o -iname "*.mov" -o -iname "*.flv" -o -iname "*.wmv" -o -iname "*.webm" -o -iname "*.mpeg" -o -iname "*.mpg" -o -iname "*.avi" -o -iname "*.ts" -o -iname "*.3gp" -o -iname "*.rmvb" \) \
+ -iname "*$search_term*" | sort)
+ [ -z "$files" ] && echo "No files named with \"$search_term\"." && exit
+ tmpplaylist=$(mktemp /tmp/mpv_playlist_XXXXXX.m3u)
+ echo "$files" | while read -r file; do
+ echo "$file"
+ done >"$tmpplaylist"
+ play_playlist "$tmpplaylist"
+ rm -rf "$tmpplaylist"
+ ;;
+ "List files")
+ files_with_paths=$(find "$dir" -mindepth 1 -maxdepth 1 \
+ -type f \( -iname "*.mp4" -o -iname "*.mkv" -o -iname "*.mov" -o -iname "*.flv" -o -iname "*.wmv" -o -iname "*.webm" -o -iname "*.mpeg" -o -iname "*.mpg" -o -iname "*.avi" -o -iname "*.ts" -o -iname "*.3gp" -o -iname "*.rmvb" \) |
+ sort)
+ selected_file=$(printf "All files\n%s" "$files_with_paths" | sed 's|.*/||' | dmenu -i -l 21 -p "Select a file:")
+ [ -z "$selected_file" ] && echo "No file selected." && exit
+ [ "$selected_file" = "All files" ] && tmp_playlist "$dir" && return
+ full_path="$(echo "$files_with_paths" | grep -F "$selected_file")"
+ [ -f "$full_path" ] && play_media "$full_path" && return
+ ;;
+ *) return ;;
+ esac
+}
+
+history_play() {
+ # Check if the database exists
+ if [ ! -f "$db_path" ]; then
+ echo "Error: SQLite database not found at $db_path" >&2
+ exit 1
+ fi
+
+ # Query the database for the latest distinct files by path, formatting time_pos as HH:MM:SS
+ history=$(
+ sqlite3 "$db_path" <<EOF
+WITH LatestFiles AS (
+ SELECT path, title, time_pos, MAX(date) AS max_date
+ FROM loaded_items
+ GROUP BY path
+),
+FormattedHistory AS (
+ SELECT
+ path,
+ title,
+ CASE
+ WHEN time_pos IS NOT NULL THEN
+ printf('%02d:%02d:%02d',
+ time_pos / 3600,
+ (time_pos % 3600) / 60,
+ time_pos % 60
+ )
+ ELSE '00:00:00'
+ END AS formatted_time,
+ max_date
+ FROM LatestFiles
+)
+SELECT path || ' | ' || title || ' | ' || formatted_time
+FROM FormattedHistory
+ORDER BY max_date DESC;
+EOF
+ )
+
+ # Check if there are any results
+ if [ -z "$history" ]; then
+ echo "No history items found in the database." >&2
+ exit 1
+ fi
+
+ # Create a temporary file for filtered results
+ temp_file=$(mktemp)
+
+ # Filter out entries with non-existing files
+ echo "$history" | while IFS= read -r line; do
+ file_path=$(printf '%s\n' "$line" | awk -F ' \\| ' '{print $1}')
+ if [ -f "$file_path" ]; then
+ printf '%s\n' "$line" >>"$temp_file"
+ fi
+ done
+
+ # Check if there are valid entries after filtering
+ if [ ! -s "$temp_file" ]; then
+ echo "No valid history items found (all files missing)." >&2
+ rm -f "$temp_file"
+ exit 1
+ fi
+
+ # Display results in dmenu and get the user's choice
+ chosen=$(dmenu -i -l 20 -p "Choose a file to play:" <"$temp_file")
+ rm -f "$temp_file"
+
+ # Check if the user made a selection
+ if [ -z "$chosen" ]; then
+ echo "No file selected." >&2
+ exit 1
+ fi
+
+ # Extract the file path and formatted time position from the selected item
+ file_path=$(printf '%s\n' "$chosen" | awk -F ' \\| ' '{print $1}')
+ formatted_time=$(printf '%s\n' "$chosen" | awk -F ' \\| ' '{print $3}')
+
+ # Convert the formatted time back to seconds for mpv
+ time_pos=$(printf '%s\n' "$formatted_time" | awk -F: '{print ($1 * 3600) + ($2 * 60) + $3}')
+
+ # Play the file with mpv, resuming from the saved time position
+ if [ "$time_pos" -gt 0 ]; then
+ mpv --x11-name=video --start="$time_pos" "$file_path"
+ else
+ mpv --x11-name=video "$file_path"
+ fi
+}
+
+content_choice=$(printf "URL\nLocal Files\nPlaylist\nHistory" | dmenu -i -p "Choose media source:")
+case "$content_choice" in
+"URL") play_url ;;
+"Playlist")
+ playlist=$(find "$HOME/.config/mpv/playlists" -maxdepth 1 -type f -name "*.m3u" -exec basename {} .m3u \; | dmenu -i -p "Select a playlist:")
+ [ -z "$playlist" ] && exit
+ play_playlist "$HOME/.config/mpv/playlists/$playlist.m3u"
+ ;;
+"Local Files")
+ check_mount
+ printf "%s\n%s\n%s\n%s\n%s\n%s\n" "$HOME/Downloads" "$HOME/Private" "$HOME/Torrents/complete" "$HOME/Videos" "/media/$USER" "/mnt/second" | dmenu -i -p "Choose your initial directory:" | {
+ read -r init_dir
+ [ -z "$init_dir" ] && $mount_script && exit
+ selected_dir="$init_dir"
+ while true; do
+ subdir_options="$(find "$selected_dir" -mindepth 1 -maxdepth 1 -type d ! -name ".*" -printf "%P\n" | sort)"
+ [ -z "$subdir_options" ] && list_and_play "$selected_dir" && break
+ options="All files\n$subdir_options"
+ selected_relative_dir="$(printf "%b" "$options" | dmenu -i -p "Select a directory or 'All files':")"
+ [ -z "$selected_relative_dir" ] && echo "No relative directory." && exit
+ [ "$selected_relative_dir" = "All files" ] && list_and_play "$selected_dir" && break
+ selected_dir="$selected_dir/$selected_relative_dir"
+ done
+ }
+ ;;
+"history") history_play ;;
+*) exit ;;
+esac
+
+trap 'check_unmount; exit' EXIT INT
diff --git a/debian/.local/bin/noisereduce b/debian/.local/bin/noisereduce
new file mode 100755
index 0000000..709b257
--- /dev/null
+++ b/debian/.local/bin/noisereduce
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+usage() {
+ printf "Usage : noisereduce <input video file> <output video file>\n"
+ exit 1
+}
+
+# Tests for requirements
+ifinstalled ffmpeg || {
+ echo >&2 "We require 'ffmpeg' but it's not installed."
+ exit 1
+}
+ifinstalled sox || {
+ echo >&2 "We require 'ffmpeg' but it's not installed."
+ exit 1
+}
+
+if [ "$#" -ne 2 ]; then
+ usage
+fi
+
+if [ ! -e "$1" ]; then
+ printf "File not found: %s\n" "$1"
+ exit
+fi
+
+if [ -e "$2" ]; then
+ printf "File %s already exists, overwrite? [y/N]\n: " "$2"
+ read -r yn
+ case $yn in
+ [Yy]*) ;;
+ *) exit ;;
+ esac
+fi
+
+inBasename=$(basename "$1")
+inExt="${inBasename##*.}"
+
+isVideoStr=$(ffprobe -v warning -show_streams "$1" | grep codec_type=video)
+if [ -n "$isVideoStr" ]; then
+ isVideo=1
+ printf "Detected %s as a video file\n" "$inBasename"
+else
+ isVideo=0
+ printf "Detected %s as an audio file\n" "$inBasename"
+fi
+
+printf "Sample noise start time [00:00:00]: "
+read -r sampleStart
+if [ -z "$sampleStart" ]; then sampleStart="00:00:00"; fi
+printf "Sample noise end time [00:00:00.900]: "
+read -r sampleEnd
+if [ -z "$sampleEnd" ]; then sampleEnd="00:00:00.900"; fi
+printf "Noise reduction amount [0.21]: "
+read -r sensitivity
+if [ -z "$sensitivity" ]; then sensitivity="0.21"; fi
+
+tmpVidFile="/tmp/noiseclean_tmpvid.$inExt"
+tmpAudFile="/tmp/noiseclean_tmpaud.wav"
+noiseAudFile="/tmp/noiseclean_noiseaud.wav"
+noiseProfFile="/tmp/noiseclean_noise.prof"
+tmpAudCleanFile="/tmp/noiseclean_tmpaud-clean.wav"
+
+printf "Cleaning noise on %s...\n" "$1"
+
+if [ $isVideo -eq "1" ]; then
+ ffmpeg -v warning -y -i "$1" -qscale:v 0 -vcodec copy -an "$tmpVidFile"
+ ffmpeg -v warning -y -i "$1" -qscale:a 0 "$tmpAudFile"
+else
+ cp "$1" "$tmpAudFile"
+fi
+ffmpeg -v warning -y -i "$1" -vn -ss "$sampleStart" -t "$sampleEnd" "$noiseAudFile"
+sox "$noiseAudFile" -n noiseprof "$noiseProfFile"
+sox "$tmpAudFile" "$tmpAudCleanFile" noisered "$noiseProfFile" "$sensitivity"
+if [ $isVideo -eq "1" ]; then
+ ffmpeg -v warning -y -i "$tmpAudCleanFile" -i "$tmpVidFile" -vcodec copy -qscale:v 0 -qscale:a 0 "$2"
+else
+ cp "$tmpAudCleanFile" "$2"
+fi
+
+printf "Done"
diff --git a/debian/.local/bin/openfiles b/debian/.local/bin/openfiles
new file mode 100755
index 0000000..5b4f7e2
--- /dev/null
+++ b/debian/.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/debian/.local/bin/opensessions b/debian/.local/bin/opensessions
new file mode 100755
index 0000000..6f9f236
--- /dev/null
+++ b/debian/.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/debian/.local/bin/opentasktui b/debian/.local/bin/opentasktui
new file mode 100755
index 0000000..1db6bef
--- /dev/null
+++ b/debian/.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/debian/.local/bin/openurl b/debian/.local/bin/openurl
new file mode 100755
index 0000000..cd096e1
--- /dev/null
+++ b/debian/.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/debian/.local/bin/opout b/debian/.local/bin/opout
new file mode 100755
index 0000000..70bc2cb
--- /dev/null
+++ b/debian/.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/debian/.local/bin/otp b/debian/.local/bin/otp
new file mode 100755
index 0000000..be80265
--- /dev/null
+++ b/debian/.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/debian/.local/bin/ovpn b/debian/.local/bin/ovpn
new file mode 100755
index 0000000..a89c357
--- /dev/null
+++ b/debian/.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/debian/.local/bin/partlabel b/debian/.local/bin/partlabel
new file mode 100755
index 0000000..3f28210
--- /dev/null
+++ b/debian/.local/bin/partlabel
@@ -0,0 +1,90 @@
+#!/bin/sh
+
+command_exists() { command -v "$1" >/dev/null 2>&1; }
+get_fs_type() { sudo blkid -o value -s TYPE "$1" 2>/dev/null; }
+label_ext() { sudo e2label "$1" "$2"; }
+label_fat() { sudo fatlabel "$1" "$2"; }
+label_ntfs() { sudo ntfslabel --force --quiet "$1" "$2"; }
+label_btrfs() { sudo btrfs filesystem label "$1" "$2"; }
+label_luks() { sudo cryptsetup config "$1" --label "$2"; }
+label_parted() { sudo parted "$1" name "$2" "$3"; }
+print_label() { sudo lsblk -o NAME,LABEL; }
+
+getfs() {
+ print_label
+ echo -n "\nEnter the partition (e.g., /dev/sda1): "
+ read partition
+
+ fs_type=$(get_fs_type "$partition")
+
+ if [ -z "$fs_type" ]; then
+ echo "Unable to determine file system type for $partition. Please ensure the partition exists and has a valid file system."
+ echo "Debugging information:"
+ echo "blkid output for $partition:"
+ sudo blkid "$partition"
+ echo "Raw blkid output:"
+ sudo blkid
+ exit 1
+ fi
+
+ echo "Detected file system type: $fs_type"
+ echo -n "Enter the label: "
+ read label
+}
+
+echo "Partition Labeling Script"
+for cmd in blkid e2label fatlabel ntfslabel btrfs parted lsblk cryptsetup; do
+ if ! command_exists "$cmd"; then
+ echo "Error: $cmd is not installed or not in PATH."
+ echo "Installing necessary package for $cmd..."
+ case $cmd in
+ blkid | lsblk)
+ sudo apt install -y util-linux
+ ;;
+ e2label)
+ sudo apt install -y e2fsprogs
+ ;;
+ fatlabel)
+ sudo apt install -y dosfstools
+ ;;
+ ntfslabel)
+ sudo apt install -y ntfs-3g
+ ;;
+ btrfs)
+ sudo apt install -y btrfs-progs
+ ;;
+ parted)
+ sudo apt install -y parted
+ ;;
+ cryptsetup)
+ sudo apt install -y cryptsetup
+ ;;
+ *)
+ echo "Unknown command: $cmd"
+ exit 1
+ ;;
+ esac
+ fi
+done
+
+getfs
+
+case "$fs_type" in
+ext2 | ext3 | ext4) label_ext "$partition" "$label" ;;
+vfat) label_fat "$partition" "$label" ;;
+ntfs) label_ntfs "$partition" "$label" ;;
+btrfs) label_btrfs "$partition" "$label" ;;
+crypto_LUKS)
+ echo "Labeling LUKS partition using cryptsetup."
+ label_luks "$partition" "$label"
+ getfs
+ ;;
+*)
+ echo "File system type $fs_type is not directly supported by this script. Attempting to label with parted."
+ label_parted "$partition" "$label"
+ ;;
+esac
+
+echo "Partition labeled successfully. Verifying..."
+print_label
+echo "Done."
diff --git a/debian/.local/bin/passmenu2 b/debian/.local/bin/passmenu2
new file mode 100755
index 0000000..00a6b35
--- /dev/null
+++ b/debian/.local/bin/passmenu2
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+shopt -s nullglob globstar
+
+typeit=0
+if [[ $1 == "--type" ]]; then
+ typeit=1
+ shift
+fi
+
+if [[ -n $WAYLAND_DISPLAY ]]; then
+ dmenu_cmd="dmenu-wl -l 20"
+ xdotool="ydotool type --file -"
+elif [[ -n $DISPLAY ]]; then
+ dmenu_cmd="dmenu -l 20"
+ xdotool="xdotool type --clearmodifiers --file -"
+else
+ echo "Error: No Wayland or X11 display detected" >&2
+ exit 1
+fi
+
+prefix=${PASSWORD_STORE_DIR-~/.password-store}
+password_files=("$prefix"/**/*.gpg)
+password_files=("${password_files[@]#"$prefix"/}")
+password_files=("${password_files[@]%.gpg}")
+
+password=$(printf '%s\n' "${password_files[@]}" | $dmenu_cmd "$@")
+
+[[ -n $password ]] || exit
+
+if [[ $typeit -eq 0 ]]; then
+ pass show -c "$password" 2>/dev/null
+else
+ pass show "$password" | {
+ IFS= read -r pass
+ printf %s "$pass"
+ } | $xdotool
+fi
diff --git a/debian/.local/bin/pauseallmpv b/debian/.local/bin/pauseallmpv
new file mode 100755
index 0000000..9b14148
--- /dev/null
+++ b/debian/.local/bin/pauseallmpv
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+# You might notice all mpv commands are aliased to have this input-ipc-server
+# thing. That's just for this particular command, which allows us to pause
+# every single one of them with one command! This is bound to super + shift + p
+# (with other things) by default and is used in some other places.
+
+for i in $(ls /tmp/mpvSockets/*); do
+ echo '{ "command": ["set_property", "pause", true] }' | socat - "$i"
+done
diff --git a/debian/.local/bin/peertubetorrent b/debian/.local/bin/peertubetorrent
new file mode 100755
index 0000000..4d8f630
--- /dev/null
+++ b/debian/.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/debian/.local/bin/podentr b/debian/.local/bin/podentr
new file mode 100755
index 0000000..ae72d41
--- /dev/null
+++ b/debian/.local/bin/podentr
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# entr command to run `queueandnotify` when newsboat queue is changed
+
+[ "$(pgrep -x "$(basename "$0")" | wc -l)" -gt 2 ] && exit
+
+echo "${XDG_DATA_HOME:-${HOME}/.local/share}"/newsboat/queue | entr -p queueandnotify 2>/dev/null
diff --git a/debian/.local/bin/ppts b/debian/.local/bin/ppts
new file mode 100755
index 0000000..e315367
--- /dev/null
+++ b/debian/.local/bin/ppts
@@ -0,0 +1,123 @@
+#!/bin/sh
+
+# Auto workflow script for git push with content/ directory handling
+# Usage: ppts
+# Automatically: git add ., commit without content/, push to origin, then add content/ and push to home
+
+# Always cd to THESIAH repository
+THESIAH_REPO="${THESIAH_WWW:-${HOME}/Private/repos/THESIAH}"
+
+if [ ! -d "$THESIAH_REPO" ]; then
+ echo "Error: THESIAH repository not found at $THESIAH_REPO"
+ exit 1
+fi
+cd "$THESIAH_REPO" || exit 1
+
+# Verify this is the correct git repository
+if ! git rev-parse --git-dir >/dev/null 2>&1; then
+ echo "Error: Not a git repository at $THESIAH_REPO"
+ exit 1
+fi
+
+# If no arguments, run auto workflow
+if [ -z "$1" ]; then
+ echo "Running auto workflow..."
+
+ # Step 0: Stage all changes first
+ echo "Step 0: Staging all changes..."
+ git add .
+
+ # Check if there are any changes to commit
+ if git diff --quiet && git diff --cached --quiet; then
+ echo "No changes to commit"
+ exit 0
+ fi
+
+ # Step 1: Unstage content/ from staging
+ echo "Step 1: Excluding content/ from staging..."
+ git reset content/ 2>/dev/null || true
+
+ # Check if there are changes to commit (excluding content/)
+ has_changes_without_content=false
+ if ! git diff --cached --quiet; then
+ has_changes_without_content=true
+ fi
+
+ # Check if content/ has changes
+ has_content_changes=false
+ if [ -d "content/" ]; then
+ # Check for untracked files in content/
+ if [ -n "$(git ls-files --others --exclude-standard content/ 2>/dev/null)" ]; then
+ has_content_changes=true
+ fi
+ # Check for modified tracked files in content/
+ if ! git diff --quiet content/ 2>/dev/null; then
+ has_content_changes=true
+ fi
+ # Check for staged changes in content/
+ if ! git diff --cached --quiet content/ 2>/dev/null; then
+ has_content_changes=true
+ fi
+ fi
+
+ # Step 2: Commit without content/ if there are changes
+ if [ "$has_changes_without_content" = true ]; then
+ echo "Step 2: Committing changes (without content/)..."
+ git commit -m "Update (without content/)" || exit 1
+
+ # Step 3: Push to origin (without content/)
+ echo "Step 3: Pushing to origin (without content/)..."
+ # Temporarily disable pre-push hook since we already excluded content/
+ hook_disabled=false
+ if [ -f .git/hooks/pre-push ]; then
+ mv .git/hooks/pre-push .git/hooks/pre-push.disabled
+ hook_disabled=true
+ fi
+ # Try normal push first
+ if git push origin master 2>/dev/null; then
+ # Push succeeded
+ :
+ else
+ # Push failed, try force push (origin may have diverged)
+ echo "Warning: Origin has diverged, using force push..."
+ git push origin master --force || {
+ # Restore hook if push failed
+ if [ "$hook_disabled" = true ] && [ -f .git/hooks/pre-push.disabled ]; then
+ mv .git/hooks/pre-push.disabled .git/hooks/pre-push
+ fi
+ exit 1
+ }
+ fi
+ # Restore hook after successful push
+ if [ "$hook_disabled" = true ] && [ -f .git/hooks/pre-push.disabled ]; then
+ mv .git/hooks/pre-push.disabled .git/hooks/pre-push
+ fi
+ echo "✓ Pushed to origin (without content/)"
+ else
+ echo "No changes to commit (excluding content/)"
+ fi
+
+ # Step 4: Add and commit content/ if there are changes
+ if [ "$has_content_changes" = true ]; then
+ echo "Step 4: Adding content/..."
+ git add content/
+
+ echo "Step 5: Committing content/..."
+ git commit -m "Update content/" || exit 1
+
+ # Step 6: Push to home (with content/)"
+ echo "Step 6: Pushing to home (with content/)..."
+ git push home master || exit 1
+ echo "✓ Pushed to home (with content/)"
+ else
+ echo "No content/ changes to commit"
+ fi
+
+ echo "Auto workflow completed!"
+ exit 0
+fi
+
+# If arguments provided, show usage
+echo "Usage: ppts"
+echo " Run without arguments to execute auto workflow"
+exit 1
diff --git a/debian/.local/bin/qndl b/debian/.local/bin/qndl
new file mode 100755
index 0000000..46b4aa8
--- /dev/null
+++ b/debian/.local/bin/qndl
@@ -0,0 +1,130 @@
+#!/bin/sh
+
+ytdl_cmd_base="yt-dlp --continue --embed-metadata --ignore-errors --no-force-overwrites --no-playlist --verbose"
+simulation_cmd="yt-dlp --simulate --print %(filename)s"
+
+case "$BROWSER" in
+*firefox*) cookies="firefox" ;;
+# *librewolf*) cookies="firefox:~/.librewolf/$USER.default" ;;
+# *qutebrowser*) cookies="chromium:~/.local/share/qutebrowser" ;;
+esac
+
+[ -n "$cookies" ] && ytdl_cmd_base="$ytdl_cmd_base --cookies-from-browser \"$cookies\""
+
+shift $((OPTIND - 1))
+
+# Use the first non-option argument as the URL if provided, else from clipboard
+# [url] [type] [cmd]
+if [ $# -eq 1 ]; then
+ type="$1"
+ url="$(xclip -selection clipboard -o)"
+elif [ $# -eq 2 ]; then
+ if echo "$1" | grep -qE "https?://"; then
+ url="$1"
+ elif echo "$2" | grep -qE "https?://"; then
+ type="$1"
+ url="$2"
+ fi
+fi
+
+# Process command-line options for download type
+case $type in
+-m | --music | m | music)
+ download_type="music"
+ output_dir="${XDG_MUSIC_DIR:-${HOME}/Music}"
+ archive_file="${XDG_DOTFILES_DIR:-${HOME}/.dotfiles}/global/Music/.music.txt"
+ ytdl_output_format="${output_dir}/%(artist|)s%(artist& - |)s%(title)s.%(ext)s"
+ ytdl_cmd_base="$ytdl_cmd_base --audio-format best --audio-quality 0 --download-archive \"$archive_file\" --extract-audio --recode-video mp3"
+ ;;
+-r | --restore | r | restore)
+ output_dir="${XDG_MUSIC_DIR:-${HOME}/Music}"
+ archive_file="${XDG_DOTFILES_DIR:-${HOME}/.dotfiles}/global/Music/.music.txt"
+ ytdl_output_format="${output_dir}/%(artist|)s%(artist& - |)s%(title)s.%(ext)s"
+ ytdl_cmd_base="$ytdl_cmd_base --audio-format best --audio-quality 0 --extract-audio --recode-video mp3"
+ ytdl_cmd="$ytdl_cmd_base --output \"$ytdl_output_format\""
+ [ ! -f "$archive_file" ] && exit 1
+ while read -r line; do
+ video_id=$(echo "$line" | awk '{print $2}')
+ ytdl_cmd="$ytdl_cmd_base --output \"$ytdl_output_format\" \"https://www.youtube.com/watch?v=$video_id\""
+ idnum=$(tsp bash -c "$ytdl_cmd")
+ pkill -RTMIN+21 "${STATUSBAR:-dwmblocks}"
+ done <"$archive_file"
+ exit 0
+ ;;
+-v | --video | v | video)
+ download_type="video"
+ output_dir="${XDG_VIDEOS_DIR:-${HOME}/Videos}"
+ ytdl_output_format="${output_dir}/%(title)s [%(id)s].%(ext)s"
+ video_ext=$(printf "best\n60fps\n30fps\nmp4\nmkv" | dmenu -i -p "Choose an encoding (default: 1080p)") || exit
+ case $video_ext in
+ best)
+ video_formats="--format bestvideo+bestaudio/best"
+ ;;
+ 60fps)
+ video_formats='--format "((bv*[fps=60]/bv*)[height<=1080]/(wv*[fps=60]/wv*)) + ba / (b[fps=60]/b)[height<=1080]/(w[fps=60]/w)"'
+ ;;
+ 30fps)
+ video_formats='--format "((bv*[fps=30]/bv*)[height<=1080]/(wv*[fps=30]/wv*)) + ba / (b[fps=30]/b)[height<=1080]/(w[fps=30]/w)"'
+ ;;
+ *)
+ video_formats="--format bestvideo+bestaudio/best"
+ video_options="--recode-video $video_ext"
+ ;;
+ esac
+ ytdl_cmd_base="$ytdl_cmd_base --buffer-size 1M --embed-thumbnail $video_formats --no-sponsorblock $video_options"
+ ytdl_cmd_base="${ytdl_cmd_base%* }"
+ ;;
+*)
+ notify-send "⛔ Invalid option: -$OPTARG"
+ exit 1
+ ;;
+esac
+
+[ -z "$url" ] && notify-send "⛔ No URL provided and clipboard is empty or does not contain a valid URL." && exit 1
+
+# Validate the URL format
+! echo "$url" | grep -qE '^https?://[a-zA-Z0-9.-]+(/[a-zA-Z0-9./?&%=_-]*)?$' && notify-send "⛔ Invalid URL format: $url" && exit 1
+
+# Validate URL accessibility
+! curl --head --silent --fail "$url" >/dev/null && notify-send "⛔ URL is not accessible: $url" && exit 1
+
+case $url in
+*playlist* | *list=*)
+ pl_download_choice=$(printf "playlist\na content" | dmenu -i -p "Download entire playlist or just this content?")
+ [ "$pl_download_choice" = "playlist" ] &&
+ ytdl_cmd_base=$(echo "$ytdl_cmd_base" | sed 's/ --no-playlist//') &&
+ ytdl_cmd_base="$ytdl_cmd_base --yes-playlist" &&
+ echo đŸȘ >/tmp/qplaylist
+ [ "$download_type" = "video" ] &&
+ channel=$(yt-dlp --print "%(channel)s" "$url" | head -n 1 | sed 's/, /,/g;s/[\/:*?"<>| ]/-/g' | tr '[:upper:]' '[:lower:]') &&
+ playlist=$(yt-dlp --print "%(playlist_title)s" "$url" | head -n 1 | sed 's/, /,/g;s/[\/:*?"<>| ]/-/g' | tr '[:upper:]' '[:lower:]') &&
+ subdir="${channel}/${playlist}" &&
+ mkdir -p "${output_dir}/${subdir}" &&
+ ytdl_output_format="${output_dir}/${subdir}/%(playlist_index)02d_%(title)s [%(id)s].%(ext)s"
+ ;;
+esac
+
+[ -n "$cookies" ] && simulation_cmd="$simulation_cmd --cookies-from-browser $cookies $url" ||
+ simulation_cmd="$simulation_cmd $url"
+
+ytdl_cmd="$ytdl_cmd_base --output \"$ytdl_output_format\" \"$url\""
+
+# Notify and perform simulation to get filename (feedback to user)
+echo "$simulation_cmd" | while IFS= read -r line; do
+ filename=$(basename "$line")
+ notify-send "đŸ“„ Queuing $download_type to download:" "$filename"
+done
+
+# Enqueue the download task with tsp
+filename=$($simulation_cmd 2>/dev/null)
+[ -f /tmp/qplaylist ] && rm -rf /tmp/qplaylist
+notify-send "⏳ Downloading $download_type:" "$filename"
+idnum=$(tsp bash -c "$ytdl_cmd")
+pkill -RTMIN+21 "${STATUSBAR:-dwmblocks}"
+
+# Notify upon completion
+tsp -D "$idnum" notify-send "✅ $download_type download complete:" "$url" ||
+ notify-send "❌ Faild to download:" "$url"
+
+# Conditionally update the music database if the download type is music
+[ "$download_type" = "music" ] && tsp -D "$idnum" bash -c "mpc update"
diff --git a/debian/.local/bin/queueandnotify b/debian/.local/bin/queueandnotify
new file mode 100755
index 0000000..0059e93
--- /dev/null
+++ b/debian/.local/bin/queueandnotify
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# Podboat sucks. This script replaces it.
+# It reads the newsboat queue, queuing downloads with taskspooler.
+# It also removes the junk from extensions.
+queuefile="${XDG_DATA_HOME:-${HOME}/.local/share}/newsboat/queue"
+
+while read -r line; do
+ [ -z "$line" ] && continue
+ url="${line%%[ ]*}"
+ qndl "$url" "curl -LO"
+done <"$queuefile"
+
+echo >"$queuefile"
diff --git a/debian/.local/bin/rbackup b/debian/.local/bin/rbackup
new file mode 100755
index 0000000..d2dc610
--- /dev/null
+++ b/debian/.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/debian/.local/bin/refreshbrowser b/debian/.local/bin/refreshbrowser
new file mode 100755
index 0000000..4b1812d
--- /dev/null
+++ b/debian/.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/debian/.local/bin/remapd b/debian/.local/bin/remapd
new file mode 100755
index 0000000..f669b0c
--- /dev/null
+++ b/debian/.local/bin/remapd
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+# Rerun the remaps script whenever a new input device is added.
+
+while :; do
+ remaps
+ grep -qP -m1 '[^un]bind.+\/[^:]+\(usb\)' <(udevadm monitor -u -t seat -s input -s usb)
+done
diff --git a/debian/.local/bin/remaps b/debian/.local/bin/remaps
new file mode 100755
index 0000000..93832ea
--- /dev/null
+++ b/debian/.local/bin/remaps
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+# This script is called on startup to remap keys.
+xset s off -dpms
+# Decrease key repeat delay to 200ms and increase key repeat rate to 50 per second.
+xset r rate 200 50
+# Map the caps lock key to control, and map the menu key to right super.
+xinput list | grep 'id=' | while read -r line; do
+ keyboard=$(echo "$line" | grep -i 'keyboard.*id.*keyboard' | sed 's/.*id=\([0-9]\+\).*/\1/')
+ mouse=$(echo "$line" | grep -i '.*id.*pointer' | sed 's/.*id=\([0-9]\+\).*/\1/')
+ [ -z "$keyboard" ] || {
+ case "$(echo "$line" | grep -oE '.*id=' | sed 's/ id=.*//')" in
+ *"Lite-On Tech Lenovo USB Travel Keyboard with Ultra Nav"*)
+ setxkbmap -device "$keyboard" -option
+ setxkbmap -device "$keyboard" -option caps:ctrl_modifier,ctrl:swap_lwin_lctl
+ ;;
+ *"Lite-On Technology Corp. ThinkPad USB Keyboard with TrackPoint"* | *"AT Translated Set 2 keyboard"*)
+ setxkbmap -device "$keyboard" -option
+ setxkbmap -device "$keyboard" -option caps:ctrl_modifier,altwin:menu_win,altwin:swap_lalt_lwin
+ ;;
+ *"Magic keyboard"*)
+ setxkbmap -device "$keyboard" -option
+ setxkbmap -device "$keyboard" -option caps:ctrl_modifier
+ ;;
+ *"HHKB"*)
+ setxkbmap -device "$keyboard" -option
+ setxkbmap -device "$keyboard" -option altwin:menu_win
+ ;;
+ *"Glove80"*)
+ setxkbmap -device "$keyboard" -option
+ ;;
+ *)
+ setxkbmap -device "$keyboard" -option
+ setxkbmap -device "$keyboard" -option caps:ctrl_modifier,altwin:menu_win
+ ;;
+ esac
+ }
+ [ -z "$mouse" ] || {
+ case "$(echo "$line" | grep -oE '.*id=' | sed 's/ id=.*//')" in
+ *"Apple Inc. Magic Trackpad"*)
+ xinput set-prop "$mouse" "libinput Tapping Enabled" 0
+ ;;
+ *"SynPS/2 Synaptics TouchPad"*)
+ xinput set-prop "$mouse" "libinput Tapping Enabled" 0
+ ;;
+ *"Lite-On Tech Lenovo USB Travel Keyboard with Ultra Nav Mouse"*)
+ [ -z "$1" ] && xinput set-prop "$mouse" "Coordinate Transformation Matrix" 5, 0, 0, 0, 5, 0, 0, 0, 1 || xinput set-prop "$mouse" "Coordinate Transformation Matrix" $1, 0, 0, 0, $1, 0, 0, 0, 1
+ xinput set-prop "$mouse" "libinput Scroll Method Enabled" 0, 0, 1
+ ;;
+ *"Logitech USB Receiver"*)
+ [ -z "$1" ] && xinput set-prop "$mouse" "Coordinate Transformation Matrix" 3, 0, 0, 0, 3, 0, 0, 0, 1 || xinput set-prop "$mouse" "Coordinate Transformation Matrix" $1, 0, 0, 0, $1, 0, 0, 0, 1
+ ;;
+ *"TPPS/2 IBM TrackPoint"*)
+ [ -z "$1" ] && xinput set-prop "$mouse" "Coordinate Transformation Matrix" 1, 0, 0, 0, 1, 0, 0, 0, 1 || xinput set-prop "$mouse" "Coordinate Transformation Matrix" $1, 0, 0, 0, $1, 0, 0, 0, 1
+ ;;
+ *"Lite-On Technology Corp. ThinkPad USB Keyboard with TrackPoint"*)
+ [ -z "$1" ] && xinput set-prop "$mouse" "Coordinate Transformation Matrix" 3, 0, 0, 0, 3, 0, 0, 0, 1 || xinput set-prop "$mouse" "Coordinate Transformation Matrix" $1, 0, 0, 0, $1, 0, 0, 0, 1
+ ;;
+ *"Glove80 Mouse"*)
+ [ -z "$1" ] && xinput set-prop "$mouse" "Coordinate Transformation Matrix" 2, 0, 0, 0, 2, 0, 0, 0, 1 || xinput set-prop "$mouse" "Coordinate Transformation Matrix" $1, 0, 0, 0, $1, 0, 0, 0, 1
+ ;;
+ esac
+ }
+done
+# When left control, caps lock, or Super_L is pressed only once, treat it as escape.
+killall xcape 2>/dev/null
+xcape -e 'Caps_Lock=Escape;Control_L=Escape' #;Super_L=Escape'
+# Turn off caps lock if on since there is no longer a key for it.
+xset -q | grep -q "Caps Lock:\s*on" && xdotool key Caps_Lock
diff --git a/debian/.local/bin/restartnvim b/debian/.local/bin/restartnvim
new file mode 100755
index 0000000..ab040ab
--- /dev/null
+++ b/debian/.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/debian/.local/bin/rgafiles b/debian/.local/bin/rgafiles
new file mode 100755
index 0000000..e8b5e72
--- /dev/null
+++ b/debian/.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/debian/.local/bin/rotdir b/debian/.local/bin/rotdir
new file mode 100755
index 0000000..d171f29
--- /dev/null
+++ b/debian/.local/bin/rotdir
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# When I open an image from the file manager in nsxiv (the image viewer), I want
+# to be able to press the next/previous keys to key through the rest of the
+# images in the same directory. This script "rotates" the content of a
+# directory based on the first chosen file, so that if I open the 15th image,
+# if I press next, it will go to the 16th etc. Autistic, I know, but this is
+# one of the reasons that nsxiv is great for being able to read standard input.
+
+[ -z "$1" ] && echo "usage: rotdir regex 2>&1" && exit 1
+base="$(basename "$1")"
+ls "$PWD" | awk -v BASE="$base" 'BEGIN { lines = ""; m = 0; } { if ($0 == BASE) { m = 1; } } { if (!m) { if (lines) { lines = lines"\n"; } lines = lines""$0; } else { print $0; } } END { print lines; }'
diff --git a/debian/.local/bin/rssadd b/debian/.local/bin/rssadd
new file mode 100755
index 0000000..f78a538
--- /dev/null
+++ b/debian/.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/debian/.local/bin/rssget b/debian/.local/bin/rssget
new file mode 100755
index 0000000..f51ecb3
--- /dev/null
+++ b/debian/.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/debian/.local/bin/schedule b/debian/.local/bin/schedule
new file mode 100755
index 0000000..c339e2b
--- /dev/null
+++ b/debian/.local/bin/schedule
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+if [ -z "$1" ]; then
+ echo "Usage: ${0##*/} <date-string>"
+ echo " Example: ${0##*/} 'tomorrow 06:00'"
+ exit 2
+fi
+
+set -e
+
+# Calculate the seconds until the given date
+secsUntil=$(expr "$(date +%s -d "$*")" - "$(date +%s)")
+
+# Calculate minutes and hours
+minutesUntil=$(echo "scale=1; $secsUntil/60" | bc)
+hoursUntil=$(echo "scale=2; $secsUntil/3600" | bc)
+
+# Get the formatted date
+date=$(date -d "$*")
+
+# Display the result
+echo "$hoursUntil hours (or $minutesUntil mins) until $date"
diff --git a/debian/.local/bin/screenshotactivewindow b/debian/.local/bin/screenshotactivewindow
new file mode 100755
index 0000000..79365e0
--- /dev/null
+++ b/debian/.local/bin/screenshotactivewindow
@@ -0,0 +1,37 @@
+#!/bin/bash
+set -eo pipefail
+
+pidfile=/tmp/screencast.lock
+
+if [ -e $pidfile ]; then
+ pid=$(cat $pidfile)
+ echo "Already running as ID $pid, now quitting"
+ rm $pidfile
+ kill -15 $pid
+ exit 0
+fi
+
+if [[ "$1" == "" ]]; then
+ echo "Usage: ${0##*/} <window-id>"
+ exit 2
+fi
+
+windowId=$1
+tmpFile=/tmp/screencast-$(date +%s).mkv
+paletteFile=/tmp/palette-$(date +%s).png
+gifFile=/tmp/screencast-$(date +%s).gif
+
+size=$(xdotool getwindowgeometry $windowId | grep Geometry | awk '{print $2}')
+posX=$(xwininfo -id $windowId | grep 'Absolute upper-left X' | awk '{print $4}')
+posY=$(xwininfo -id $windowId | grep 'Absolute upper-left Y' | awk '{print $4}')
+pos="$posX,$posY"
+
+ffmpeg -hide_banner -loglevel info -f x11grab -show_region 1 -video_size $size -i :0+$pos $tmpFile &
+ffPID=$!
+echo $ffPID >$pidfile
+wait $ffPID && echo
+
+ffmpeg -y -i $tmpFile -vf fps=10,palettegen $paletteFile
+ffmpeg -i $tmpFile -i $paletteFile -filter_complex "paletteuse" $gifFile
+
+rm $paletteFile $tmpFile
diff --git a/debian/.local/bin/sd b/debian/.local/bin/sd
new file mode 100755
index 0000000..8c6f6a4
--- /dev/null
+++ b/debian/.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/debian/.local/bin/sessionizer b/debian/.local/bin/sessionizer
new file mode 100755
index 0000000..93f32b1
--- /dev/null
+++ b/debian/.local/bin/sessionizer
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+sessionizer() {
+ path="$(
+ sesh list -d --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 -d -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 -d -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 -d -t --icons)" \
+ --bind "ctrl-v:execute($EDITOR ${0})+abort" \
+ --bind 'ctrl-x:execute(tmux kill-session -t "$(echo {} | cut -d" " -f2-)")+reload(sesh list -d --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/debian/.local/bin/setbg b/debian/.local/bin/setbg
new file mode 100755
index 0000000..66f6819
--- /dev/null
+++ b/debian/.local/bin/setbg
@@ -0,0 +1,71 @@
+#!/bin/sh
+
+# This script does the following:
+# Run by itself, set the wallpaper (at X start).
+# If given a file, set that as the new wallpaper.
+# If given a directory, choose random file in it.
+# If wal is installed, also generates a colorscheme.
+
+# Location of link to wallpaper link.
+wallloc="${XDG_DATA_HOME:-${HOME}/.local/share}/wallpapers"
+bgloc="$wallloc/bg"
+vidloc="$wallloc/video/1/filename00001.jpg"
+
+# Configuration files of applications that have their themes changed by pywal.
+dunstconf="${XDG_CONFIG_HOME:-${HOME}/.config}/dunst/dunstrc"
+zathuraconf="${XDG_CONFIG_HOME:-${HOME}/.config}/zathura/zathurarc"
+
+# Give -s as parameter to make notifications silent.
+while getopts "s" o; do
+ case "${o}" in
+ s) silent='1' ;;
+ *) ;;
+ esac
+done
+
+shift $((OPTIND - 1))
+
+[ -d "$wallloc" ] || mkdir -p "$wallloc"
+
+[ "$(ls "$wallloc/video" 2>/dev/null)" ] && {
+ [ -n "$(pgrep -f "wallset")" ] && pgrep -f "wallset" | xargs kill
+ wallset -r 2>/dev/null
+ wallset -q 2>/dev/null
+ rm -rf "$wallloc/video"
+}
+
+[ -f ~/.fehbg ] && rm -f ~/.fehbg
+
+trueloc="$(readlink -f "$1")" &&
+ case "$(file --mime-type -b "$trueloc")" in
+ image/*) ln -sf "$trueloc" "$bgloc" && [ -z "$silent" ] && notify-send -i "$bgloc" "Changing wallpaper..." ;;
+ inode/directory) ln -sf "$(find -L "$trueloc" -iregex '.*.\(jpg\|jpeg\|png\|gif\)' -type f | shuf -n 1)" "$bgloc" && [ -z "$silent" ] && notify-send -i "$bgloc" "Random Wallpaper chosen." ;;
+ video/*)
+ mkdir -p "$wallloc/video" 2>/dev/null
+ if [ ! "${trueloc##*.}" = "mp4" ] || [ "$(($(mediainfo --Output="General;%Duration%" "$trueloc")))" -gt 10000 ]; then
+ notify-send "⌛ Converting video for wallpaper..." && ffmpeg -i "$trueloc" -t 3 "$wallloc/video/bg.mp4" 2>/dev/null
+ else
+ cp "$trueloc" "$wallloc/video/bg.mp4" 2>/dev/null
+ fi
+ trueloc="$wallloc/video/bg.mp4"
+ wallset --video "$trueloc" >/dev/null 2>&1 && [ -z "$silent" ] && notify-send -i "$vidloc" "Changing wallpaper..."
+ exit
+ ;;
+ *)
+ [ -z "$silent" ] && notify-send "đŸ–Œïž Error" "Not a valid image or directory."
+ exit 1
+ ;;
+ esac
+
+# If pywal is installed, use it.
+if command -v wal >/dev/null 2>&1; then
+ wal -n -i "$(readlink -f "$bgloc")" -o "${XDG_CONFIG_HOME:-${HOME}/.config}/wal/postrun" >/dev/null 2>&1
+# If pywal is removed, return config files to normal.
+else
+ [ -f "$dunstconf.bak" ] && unlink "$dunstconf" && mv "$dunstconf.bak" "$dunstconf"
+ [ -f "$zathuraconf.bak" ] && unlink "$zathuraconf" && mv "$zathuraconf.bak" "$zathuraconf"
+fi
+
+xwallpaper --zoom "$bgloc"
+# If running, dwm hit the key to refresh the color scheme.
+pidof dwm >/dev/null && xdotool key super+F5
diff --git a/debian/.local/bin/setfirmware b/debian/.local/bin/setfirmware
new file mode 100755
index 0000000..326d14b
--- /dev/null
+++ b/debian/.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/debian/.local/bin/setlock b/debian/.local/bin/setlock
new file mode 100755
index 0000000..611ae5e
--- /dev/null
+++ b/debian/.local/bin/setlock
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+lockloc="${XDG_DATA_HOME:-${HOME}/.local/share}/wallpapers/lock"
+
+# Give -s as parameter to make notifications silent.
+while getopts "s" o; do
+ case "${o}" in
+ s) silent='1' ;;
+ *) ;;
+ esac
+done
+
+shift $((OPTIND - 1))
+
+trueloc="$(readlink -f "$1")" &&
+ case "$(file --mime-type -b "$trueloc")" in
+ image/*) ln -sf "$trueloc" "$lockloc" && [ -z "$silent" ] && notify-send -i "$lockloc" "Changing lock screen..." ;;
+ inode/directory) ln -sf "$(find -L "$trueloc" -iregex '.*.\(jpg\|jpeg\|png\|gif\)' -type f | shuf -n 1)" "$lockloc" && [ -z "$silent" ] && notify-send -i "$lockloc" "Random Lock Screen chosen." ;;
+ *)
+ [ -z "$silent" ] && notify-send "đŸ–Œïž Error" "Not a valid image or directory."
+ exit 1
+ ;;
+ esac
diff --git a/debian/.local/bin/setmonitor b/debian/.local/bin/setmonitor
new file mode 100755
index 0000000..8ed4589
--- /dev/null
+++ b/debian/.local/bin/setmonitor
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+# Parse connected displays
+default="--mode 1920x1080 --rotate normal --scale 1.0x1.0 --dpi 96"
+
+for connected in $(xrandr -q | grep -w "connected" | cut -d ' ' -f 1); do
+ case $connected in
+ eDP*) edp="$connected" ;;
+ HDMI*) hdmi="$connected" ;;
+ DP*) dp="$connected" ;;
+ *) display="$connected" ;;
+ esac
+done
+
+# If the lid is closed, turn off the laptop's screen
+if grep -q "closed" /proc/acpi/button/lid/LID/state; then
+ if [ -n "$hdmi" ] && [ -z "$dp" ] && [ -n "$edp" ]; then
+ xrandr --output "$edp" --off --output "$hdmi" --primary $default
+ elif [ -z "$hdmi" ] && [ -n "$dp" ] && [ -n "$edp" ]; then
+ xrandr --output "$edp" --off --output "$dp" --primary $default
+ else
+ xrandr --output "$edp" --off --output "$display" --auto --primary $default
+ fi
+else
+ # Apply display settings when lid is open
+ if [ -n "$hdmi" ] && [ -z "$dp" ] && [ -n "$edp" ]; then
+ xrandr --output "$edp" --pos 1920x0 $default --output "$hdmi" --primary --pos 0x0 $default
+ elif [ -z "$hdmi" ] && [ -n "$dp" ] && [ -n "$edp" ]; then
+ xrandr --output "$edp" --pos 1920x0 $default --output "$dp" --primary --pos 0x0 $default
+ elif [ -z "$hdmi" ] && [ -z "$dp" ] && [ -n "$edp" ]; then
+ xrandr --output "$edp" --primary $default
+ else
+ xrandr --output "$display" --primary --auto
+ fi
+fi
diff --git a/debian/.local/bin/shortcuts b/debian/.local/bin/shortcuts
new file mode 100755
index 0000000..e131ce4
--- /dev/null
+++ b/debian/.local/bin/shortcuts
@@ -0,0 +1,93 @@
+#!/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"
+command -v lf && lf_shortcuts="${XDG_CONFIG_HOME:-${HOME}/.config}/lf/shortcutrc" || lf_shortcuts="/dev/null"
+command -v vim && vim_shortcuts="${XDG_CONFIG_HOME:-${HOME}/.config}/vim/shortcuts.vim" || vim_shortcuts="/dev/null"
+command -v nvim && nvim_shortcuts="${XDG_CONFIG_HOME:-${HOME}/.config}/nvim/shortcuts.lua" || nvim_shortcuts="/dev/null"
+if command -v yazi; then
+ yazi_shortcuts="${XDG_CONFIG_HOME:-${HOME}/.config}/yazi/keymap.toml"
+ yazi_shortcuts_tmp="$yazi_shortcuts.tmp"
+else
+ yazi_shortcuts="/dev/null"
+ yazi_shortcuts_tmp="/dev/null"
+fi
+command -v ranger && ranger_shortcuts="${XDG_CONFIG_HOME:-${HOME}/.config}/ranger/shortcuts.conf" || ranger_shortcuts="/dev/null"
+command -v qutebrowser && qute_shortcuts="$HOME/.config/qutebrowser/shortcuts.py" || qute_shortcuts="/dev/null"
+command -v fish && fish_shortcuts="${XDG_CONFIG_HOME:-${HOME}/.config}/fish/shortcuts.fish" || fish_shortcuts="/dev/null"
+command -v vifm && vifm_shortcuts="${XDG_CONFIG_HOME:-${HOME}/.config}/vifm/shortcuts.rc" || vifm_shortcuts="/dev/null"
+
+# Remove, prepare files
+rm -f "$lf_shortcuts" "$ranger_shortcuts" "$qute_shortcuts" "$zsh_named_dirs" "$vim_shortcuts" "$nvim_shortcuts" "$yazi_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"
+[ -f "$HOME/.config/yazi/keymap-default.toml" ] && cp "$HOME/.config/yazi/keymap-default.toml" "$yazi_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\" ;
+ desc_path = \$2; gsub(\"^/home/$USER/\",\"~/\",desc_path);
+ printf(\"vim.keymap.set('c', ';%s', '%s<cr>', { noremap = true, silent = true, desc = '%s' })\n\", \$1, \$2, desc_path) >> \"$nvim_shortcuts\" ;
+ printf(\"vim.keymap.set('n', '<localleader><leader>%s', '<cmd>Explore %s<cr>', { noremap = true, silent = true, desc = '%s' })\n\", \$1, \$2, desc_path) >> \"$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, desc_path) >> \"$nvim_shortcuts\" ;
+ desc_path = \$2; gsub(\"^/home/$USER/\",\"~/\",desc_path);
+ key_array = \"\"; for(i=1; i<=length(\$1); i++) { if(i==1) key_array = \"\\\"\" substr(\$1,i,1) \"\\\"\"; else key_array = key_array \", \\\"\" substr(\$1,i,1) \"\\\"\" }
+ printf(\"\\t{ on = [ %s ], run = \\\"cd %s\\\", desc = \\\"Go to %s\\\" },\\n\",key_array,\$2,desc_path) >> \"$yazi_shortcuts_tmp\"}"
+
+# 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\" ;
+ desc_path = \$2; gsub(\"^/home/$USER/\",\"~/\",desc_path);
+ printf(\"vim.keymap.set('c', ';%s', '%s<cr>', { noremap = true, silent = true, desc = '%s' })\n\", \$1, \$2, desc_path) >> \"$nvim_shortcuts\" ;
+ printf(\"vim.keymap.set('n', '<localleader><localleader>%s', '<cmd>e %s<cr>', { noremap = true, silent = true, desc = '%s' })\n\", \$1, \$2, desc_path) >> \"$nvim_shortcuts\" ;
+ desc_path = \$2; gsub(\"^/home/$USER/\",\"~/\",desc_path);
+ key_array = \"\"; for(i=1; i<=length(\$1); i++) { if(i==1) key_array = \"\\\"\" substr(\$1,i,1) \"\\\"\"; else key_array = key_array \", \\\"\" substr(\$1,i,1) \"\\\"\" }
+ printf(\"\\t{ on = [ %s ], run = [ 'reveal %s', 'open' ], desc = \\\"Open %s\\\" },\\n\", key_array, \$2, desc_path) >> \"$yazi_shortcuts_tmp\" }"
+
+# Merge bookmarks into keymap-default.toml
+if [ -f "$yazi_shortcuts_tmp" ]; then
+ # Find the line with the closing bracket in the [mgr] section
+ line_num=$(grep -n "^]" "$yazi_shortcuts" | head -1 | cut -d: -f1)
+
+ # Create the merged file
+ head -n $((line_num - 1)) "$yazi_shortcuts" >"$yazi_shortcuts.new"
+ echo "" >>"$yazi_shortcuts.new"
+ echo " # Custom bookmarks" >>"$yazi_shortcuts.new"
+ cat "$yazi_shortcuts_tmp" >>"$yazi_shortcuts.new"
+ echo "]" >>"$yazi_shortcuts.new"
+ tail -n +$((line_num + 1)) "$yazi_shortcuts" >>"$yazi_shortcuts.new"
+
+ # Replace the original file
+ mv "$yazi_shortcuts.new" "$yazi_shortcuts"
+ rm -f "$yazi_shortcuts_tmp"
+fi
diff --git a/debian/.local/bin/slider b/debian/.local/bin/slider
new file mode 100755
index 0000000..35bb6a7
--- /dev/null
+++ b/debian/.local/bin/slider
@@ -0,0 +1,132 @@
+#!/bin/sh
+
+# Give a file with images and timecodes and creates a video slideshow of them.
+#
+# Timecodes must be in format 00:00:00.
+#
+# Imagemagick and ffmpeg required.
+
+# Application cache if not stated elsewhere.
+cache="${XDG_CACHE_HOME:-${HOME}/.cache}/slider"
+
+while getopts "hvrpi:c:a:o:d:f:t:e:x:s:" o; do case "${o}" in
+ c) bgc="$OPTARG" ;;
+ t) fgc="$OPTARG" ;;
+ f) font="$OPTARG" ;;
+ i) file="$OPTARG" ;;
+ a) audio="$OPTARG" ;;
+ o) outfile="$OPTARG" ;;
+ d) prepdir="$OPTARG" ;;
+ r) redo="$OPTARG" ;;
+ s) ppt="$OPTARG" ;;
+ e) endtime="$OPTARG" ;;
+ x)
+ res="$OPTARG"
+ echo "$res" | grep -qv "^[0-9]\+x[0-9]\+$" &&
+ echo "Resolution must be dimensions separated by a 'x': 1280x720, etc." &&
+ exit 1
+ ;;
+ p)
+ echo "Purge old build files in $cache? [y/N]"
+ read -r confirm
+ echo "$confirm" | grep -iq "^y$" && rm -rf "$cache" && echo "Done."
+ exit
+ ;;
+ v) verbose=True ;;
+ *) echo "$(basename "$0") usage:
+ -i input timecode list (required)
+ -a audio file
+ -c color of background (use html names, black is default)
+ -t text color for text slides (white is default)
+ -s text font size for text slides (150 is default)
+ -f text font for text slides (sans serif is default)
+ -o output video file
+ -e if no audio given, the time in seconds that the last slide will be shown (5 is default)
+ -x resolution (1920x1080 is default)
+ -d tmp directory
+ -r rerun imagemagick commands even if done previously (in case files or background has changed)
+ -p purge old build files instead of running
+ -v be verbose" && exit 1 ;;
+
+ esac done
+
+# Check that the input file looks like it should.
+{ head -n 1 "$file" 2>/dev/null | grep -q "^00:00:00 "; } || {
+ echo "Give an input file with -i." &&
+ echo "The file should look as this example:
+
+00:00:00 first_image.jpg
+00:00:03 otherdirectory/next_image.jpg
+00:00:09 this_image_starts_at_9_seconds.jpg
+etc...
+
+Timecodes and filenames must be separated by Tabs." &&
+ exit 1
+}
+
+if [ -n "${audio+x}" ]; then
+ # Check that the audio file looks like an actual audio file.
+ case "$(file --dereference --brief --mime-type -- "$audio")" in
+ audio/*) ;;
+ *)
+ echo "That doesn't look like an audio file."
+ exit 1
+ ;;
+ esac
+ totseconds="$(date '+%s' -d $(ffmpeg -i "$audio" 2>&1 | awk '/Duration/ {print $2}' | sed s/,//))"
+fi
+
+prepdir="${prepdir:-$cache/$file}"
+outfile="${outfile:-$file.mp4}"
+prepfile="$prepdir/$file.prep"
+
+[ -n "${verbose+x}" ] && echo "Preparing images... May take a while depending on the number of files."
+mkdir -p "$prepdir"
+
+{
+ while read -r x; do
+ # Get the time from the first column.
+ time="${x%% *}"
+ seconds="$(date '+%s' -d "$time")"
+ # Duration is not used on the first looped item.
+ duration="$((seconds - prevseconds))"
+
+ # Get the filename/text content from the rest.
+ content="${x#* }"
+ base="$(basename "$content")"
+ base="${base%.*}.jpg"
+
+ if [ -f "$content" ]; then
+ # If images have already been made in a previous run, do not recreate
+ # them unless -r was given.
+ { [ ! -f "$prepdir/$base" ] || [ -n "${redo+x}" ]; } &&
+ magick -size "${res:-1920x1080}" canvas:"${bgc:-black}" -gravity center "$content" -resize 1920x1080 -composite "$prepdir/$base"
+ else
+ { [ ! -f "$prepdir/$base" ] || [ -n "${redo+x}" ]; } &&
+ magick -size "${res:-1920x1080}" -background "${bgc:-black}" -fill "${fgc:-white}" -font "${font:-Sans}" -pointsize "${ppt:-150}" -gravity center label:"$content" "$prepdir/$base"
+ fi
+
+ # If the first line, do not write yet.
+ [ "$time" = "00:00:00" ] || echo "file '$prevbase'
+duration $duration"
+
+ # Keep the information required for the next file.
+ prevbase="$base"
+ prevtime="$time"
+ prevseconds="$(date '+%s' -d "$prevtime")"
+ done <"$file"
+ # Do last file which must be given twice as follows
+ endtime="$((totseconds - seconds))"
+ echo "file '$base'
+duration ${endtime:-5}
+file '$base'"
+} >"$prepfile"
+if [ -n "${audio+x}" ]; then
+ ffmpeg -hide_banner -y -f concat -safe 0 -i "$prepfile" -i "$audio" -c:a aac -vsync vfr -c:v libx264 -pix_fmt yuv420p "$outfile"
+else
+ ffmpeg -hide_banner -y -f concat -safe 0 -i "$prepfile" -vsync vfr -c:v libx264 -pix_fmt yuv420p "$outfile"
+fi
+
+# Might also try:
+# -vf "fps=${fps:-24},format=yuv420p" "$outfile"
+# but has given some problems.
diff --git a/debian/.local/bin/sshadd b/debian/.local/bin/sshadd
new file mode 100755
index 0000000..ea389b9
--- /dev/null
+++ b/debian/.local/bin/sshadd
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+ssh_key_dir="${PASSWORD_STORE_DIR:-${HOME}/.local/share/.password-store}"
+
+temp_private_keys_list=$(mktemp)
+
+# Ensure that filenames with spaces or other special characters are handled correctly.
+find "$ssh_key_dir" -name "*.pub" | while IFS= read -r pub_file_path; do
+ private_key_path="${pub_file_path%.pub}"
+ if [ -f "$private_key_path" ]; then
+ echo "$(basename "$private_key_path")" >>"$temp_private_keys_list"
+ echo "$private_key_path" >>"$temp_private_keys_list"
+ fi
+done
+
+# Use of dmenu is system-specific and not covered by POSIX standards
+selected_key_name=$(awk 'NR % 2 == 1' "$temp_private_keys_list" | dmenu -i -p "Select SSH key:")
+
+if [ -n "$selected_key_name" ]; then
+ selected_key_path=$(awk -v name="$selected_key_name" '$0 == name { getline; print }' "$temp_private_keys_list")
+
+ if [ -n "$selected_key_path" ]; then
+ export SSH_ASKPASS="$HOME/.local/bin/demupass"
+ setsid ssh-add "$selected_key_path" </dev/null
+ ln -sf "$selected_key_path" "$HOME/.ssh/$(basename "$selected_key_path")"
+ ln -sf "${selected_key_path}.pub" "$HOME/.ssh/$(basename "$selected_key_path").pub"
+ notify-send "🔑 SSH key added:" "$selected_key_name"
+ fi
+fi
+
+rm "$temp_private_keys_list"
diff --git a/debian/.local/bin/stw b/debian/.local/bin/stw
new file mode 100755
index 0000000..f6a5c85
--- /dev/null
+++ b/debian/.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/debian/.local/bin/syncdic b/debian/.local/bin/syncdic
new file mode 100755
index 0000000..c125f42
--- /dev/null
+++ b/debian/.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/debian/.local/bin/syncdot b/debian/.local/bin/syncdot
new file mode 100755
index 0000000..5e560e9
--- /dev/null
+++ b/debian/.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/debian/.local/bin/synctime b/debian/.local/bin/synctime
new file mode 100755
index 0000000..47f4310
--- /dev/null
+++ b/debian/.local/bin/synctime
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+select_timezone() {
+ initial_path="/usr/share/zoneinfo"
+ tz_selection=$initial_path
+
+ while [ -d "$tz_selection" ]; do
+ selection=$(find "$tz_selection" -mindepth 1 -maxdepth 1 | sed "s|^$initial_path/||" | dmenu -i -l 20 -p "Select Time Zone:") || exit 1
+ tz_selection="$initial_path/$selection"
+ done
+
+ echo "${tz_selection#$initial_path/}"
+}
+
+current_zone=$(select_timezone) || exit 1
+sudo ln -sf /usr/share/zoneinfo/$current_zone /etc/localtime || exit 1
+sudo hwclock --systohc
+
+echo "Current timezone: $current_zone"
+date
+
+pkill -RTMIN+3 ${STATUSBAR:-dwmblocks}
diff --git a/debian/.local/bin/sysact b/debian/.local/bin/sysact
new file mode 100755
index 0000000..d4f3653
--- /dev/null
+++ b/debian/.local/bin/sysact
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+# A dmenu wrapper script for system functions.
+export WM="dwm"
+case "$(readlink -f /sbin/init)" in
+*systemd*) ctl='systemctl' ;;
+*) ctl='loginctl' ;;
+esac
+
+lock() {
+ mpc pause
+ pauseallmpv
+ wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
+ kill -39 "$(pidof dwmblocks)"
+ slock
+ wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
+ kill -39 "$(pidof dwmblocks)"
+}
+
+wmpid() { # This function is needed if there are multiple instances of the window manager.
+ tree="$(pstree -ps $$)"
+ tree="${tree#*$WM(}"
+ echo "${tree%%)*}"
+}
+
+case "$(printf "🔒 lock\nđŸšȘ leave $WM\n♻ renew $WM\nđŸ» hibernate\n🔃 reboot\nđŸ–„ïžshutdown\nđŸ’€ sleep\nđŸ“ș display off" | dmenu -i -p 'Action: ')" in
+'🔒 lock') lock ;;
+"đŸšȘ leave $WM") kill -TERM "$(wmpid)" ;;
+"♻ renew $WM") kill -HUP "$(wmpid)" ;;
+'đŸ» hibernate') slock $ctl hibernate -i ;;
+'đŸ’€ sleep') slock $ctl suspend -i ;;
+'🔃 reboot') $ctl reboot -i ;;
+'đŸ–„ïžshutdown') $ctl poweroff -i ;;
+'đŸ“ș display off') xset dpms force off ;;
+*) exit 1 ;;
+esac
diff --git a/debian/.local/bin/tablet b/debian/.local/bin/tablet
new file mode 100755
index 0000000..1b4e556
--- /dev/null
+++ b/debian/.local/bin/tablet
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+# Find the line in "xrandr -q --verbose" output that contains current screen orientation and "strip" out current orientation.
+rotation="$(xrandr -q --verbose | grep 'connected' | grep -Eo '\) (normal|left|inverted|right) \(' | grep -Eo '(normal|left|inverted|right)')"
+penstylus="$(xsetwacom list devices | grep 'Pen' | grep 'stylus' | sed 's/\s*id.*//g')"
+penerase="$(xsetwacom list devices | grep 'Pen' | grep 'erase' | sed 's/\s*id.*//g')"
+fingertouch="$(xsetwacom list devices | grep 'Finger' | grep 'touch' | sed 's/\s*id.*//g')"
+
+# Using current screen orientation proceed to rotate screen and input tools.
+case "$rotation" in
+normal)
+ # rotate to the left
+ xrandr -o left
+ xsetwacom set "$penstylus" rotate ccw
+ xsetwacom set "$penerase" rotate ccw
+ xsetwacom set "$fingertouch" rotate ccw
+ ;;
+left)
+ # rotate to normal
+ xrandr -o inverted
+ xsetwacom set "$penstylus" rotate half
+ xsetwacom set "$penerase" rotate half
+ xsetwacom set "$fingertouch" rotate half
+ ;;
+inverted)
+ # rotate to normal
+ xrandr -o right
+ xsetwacom set "$penstylus" rotate cw
+ xsetwacom set "$penerase" rotate cw
+ xsetwacom set "$fingertouch" rotate cw
+ ;;
+right)
+ # rotate to normal
+ xrandr -o normal
+ xsetwacom set "$penstylus" rotate none
+ xsetwacom set "$penerase" rotate none
+ xsetwacom set "$fingertouch" rotate none
+ ;;
+esac
diff --git a/debian/.local/bin/tag b/debian/.local/bin/tag
new file mode 100755
index 0000000..8abce0f
--- /dev/null
+++ b/debian/.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/debian/.local/bin/task/taskwarrior-tui/annotate-with-new-note b/debian/.local/bin/task/taskwarrior-tui/annotate-with-new-note
new file mode 100755
index 0000000..3c67d24
--- /dev/null
+++ b/debian/.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/debian/.local/bin/task/taskwarrior-tui/annotate-with-note b/debian/.local/bin/task/taskwarrior-tui/annotate-with-note
new file mode 100755
index 0000000..c744314
--- /dev/null
+++ b/debian/.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/debian/.local/bin/task/taskwarrior-tui/cycle-priority b/debian/.local/bin/task/taskwarrior-tui/cycle-priority
new file mode 100755
index 0000000..43bc91e
--- /dev/null
+++ b/debian/.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/debian/.local/bin/task/taskwarrior-tui/cycle-tmux-projects b/debian/.local/bin/task/taskwarrior-tui/cycle-tmux-projects
new file mode 100755
index 0000000..360dc20
--- /dev/null
+++ b/debian/.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/debian/.local/bin/task/taskwarrior-tui/decrease-priority b/debian/.local/bin/task/taskwarrior-tui/decrease-priority
new file mode 100755
index 0000000..b2b0508
--- /dev/null
+++ b/debian/.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/debian/.local/bin/task/taskwarrior-tui/git-issue-sync b/debian/.local/bin/task/taskwarrior-tui/git-issue-sync
new file mode 100755
index 0000000..da01bc4
--- /dev/null
+++ b/debian/.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/debian/.local/bin/task/taskwarrior-tui/increase-priority b/debian/.local/bin/task/taskwarrior-tui/increase-priority
new file mode 100755
index 0000000..26a3d53
--- /dev/null
+++ b/debian/.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/debian/.local/bin/task/taskwarrior-tui/lib-task-interop b/debian/.local/bin/task/taskwarrior-tui/lib-task-interop
new file mode 100755
index 0000000..04e60ac
--- /dev/null
+++ b/debian/.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/debian/.local/bin/task/taskwarrior-tui/task-switch-context b/debian/.local/bin/task/taskwarrior-tui/task-switch-context
new file mode 100755
index 0000000..0b40141
--- /dev/null
+++ b/debian/.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/debian/.local/bin/task/taskwarrior-tui/taskopen-annotation b/debian/.local/bin/task/taskwarrior-tui/taskopen-annotation
new file mode 100755
index 0000000..3b292e8
--- /dev/null
+++ b/debian/.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/debian/.local/bin/task/taskwarrior-tui/taskopen-line b/debian/.local/bin/task/taskwarrior-tui/taskopen-line
new file mode 100755
index 0000000..379a2af
--- /dev/null
+++ b/debian/.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/debian/.local/bin/task/taskwarrior-tui/tasks-sync b/debian/.local/bin/task/taskwarrior-tui/tasks-sync
new file mode 100755
index 0000000..06334c4
--- /dev/null
+++ b/debian/.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/debian/.local/bin/task/taskwarrior-tui/toggle-review-label b/debian/.local/bin/task/taskwarrior-tui/toggle-review-label
new file mode 100755
index 0000000..9133c30
--- /dev/null
+++ b/debian/.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/debian/.local/bin/td-toggle b/debian/.local/bin/td-toggle
new file mode 100755
index 0000000..4bbe75c
--- /dev/null
+++ b/debian/.local/bin/td-toggle
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# If transmission-daemon is running, will ask to kill, else will ask to start.
+
+if pidof transmission-daemon >/dev/null; then
+ [ "$(printf "Yes\\nNo" | dmenu -i -p "Turn off transmission-daemon?")" = "Yes" ] && killall -q -9 transmission-daemon && notify-send "❌ transmission-daemon disabled."
+else
+ ifinstalled transmission-cli || exit
+ [ "$(printf "No\\nYes" | dmenu -i -p "Turn on transmission daemon?")" = "Yes" ] && transmission-daemon && notify-send "🚄 transmission-daemon enabled."
+fi
+sleep 3 && pkill -RTMIN+22 "${STATUSBAR:-dwmblocks}"
diff --git a/debian/.local/bin/timer b/debian/.local/bin/timer
new file mode 100755
index 0000000..fd4bf5e
--- /dev/null
+++ b/debian/.local/bin/timer
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+if [ -z "$1" ]; then
+ echo "Usage: ${0##*/} <mins> [message]"
+ exit 1
+fi
+
+mins=$1
+message=$2
+
+nohup timer_ "$mins" "$message" >/dev/null 2>&1 &
+
+echo "timer started for $mins min"
+logger -t timer "timer started for $mins min, message: '$message'"
diff --git a/debian/.local/bin/timer_ b/debian/.local/bin/timer_
new file mode 100755
index 0000000..665e61f
--- /dev/null
+++ b/debian/.local/bin/timer_
@@ -0,0 +1,14 @@
+#!/bin/sh
+set -eu
+
+mins=$1
+message=${2:-Time out!}
+
+# Calculate the sleep time in seconds
+sleep_time=$(expr "$mins" \* 60)
+sleep "$sleep_time"
+
+# Send a notification using a POSIX-compliant command
+# 'notify-send' is not POSIX, but we'll keep it since it's a common tool
+# For complete POSIX compliance, replace this with another method if needed
+notify-send -t 0 "${message}" "Your timer of $mins min is over" -u normal
diff --git a/debian/.local/bin/timezones b/debian/.local/bin/timezones
new file mode 100755
index 0000000..206f8da
--- /dev/null
+++ b/debian/.local/bin/timezones
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+set -e
+
+[ "$1" != "" ] && time=$1 || time=''
+
+printDate() {
+ [ "$time" != "" ] && message="$(TZ=$2 date +'%a, %d %b %H:%M' -d "$time $(date +%Z)")" || message="$(TZ=$2 date +'%a, %d %b %H:%M')"
+ notify-send "$1" "$message"
+}
+
+printDate "Los Angeles" America/Los_Angeles
+printDate "New York" America/New_York
+printDate "London" Europe/London
+printDate "Berlin" Europe/Berlin
+printDate "Moscow" Europe/Moscow
+printDate "Bangkok" Asia/Bangkok
+printDate "Hong Kong" Asia/Hong_Kong
+printDate "Macau" Asia/Macau
+printDate "Seoul" Asia/Seoul
+printDate "Tokyo" Asia/Tokyo
diff --git a/debian/.local/bin/tmuxcreate b/debian/.local/bin/tmuxcreate
new file mode 100755
index 0000000..5fb5ef3
--- /dev/null
+++ b/debian/.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/debian/.local/bin/tmuxcycleborder b/debian/.local/bin/tmuxcycleborder
new file mode 100755
index 0000000..ad2a430
--- /dev/null
+++ b/debian/.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/debian/.local/bin/tmuxdbussync b/debian/.local/bin/tmuxdbussync
new file mode 100755
index 0000000..d6da872
--- /dev/null
+++ b/debian/.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/debian/.local/bin/tmuxopen b/debian/.local/bin/tmuxopen
new file mode 100755
index 0000000..5362215
--- /dev/null
+++ b/debian/.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/debian/.local/bin/tmuxtogglebar b/debian/.local/bin/tmuxtogglebar
new file mode 100755
index 0000000..2ae045e
--- /dev/null
+++ b/debian/.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/debian/.local/bin/tmuxtoggleterm b/debian/.local/bin/tmuxtoggleterm
new file mode 100755
index 0000000..f21f833
--- /dev/null
+++ b/debian/.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/debian/.local/bin/toggleoutput b/debian/.local/bin/toggleoutput
new file mode 100755
index 0000000..357c16d
--- /dev/null
+++ b/debian/.local/bin/toggleoutput
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+echo "Usage: ${0##*/} toggles between"
+echo " - usb: plays sound via main usb speaker or"
+echo " - head: plays sound via main headset"
+echo
+
+current_output=$(wpctl status | grep -A 4 'Sinks:' | grep '\*' | grep -oP '\d+(?=\.)' | head -n 1)
+usb_output=$(wpctl status | grep 'USB Audio Analog Stereo' | grep 'vol:' | grep -oP '\d+(?=\.)' | head -n 1)
+headset_output=$(wpctl status | grep 'WH-1000XM3' | grep 'vol:' | grep -oP '\d+(?=\.)' | head -n 1)
+
+echo "Debug: current_output=$current_output"
+echo "Debug: usb_output=$usb_output"
+echo "Debug: headset_output=$headset_output"
+
+if [ -z "$current_output" ] || [ -z "$usb_output" ] || [ -z "$headset_output" ]; then
+ echo "Error: Unable to determine audio outputs"
+ return 1
+fi
+
+if [ "$current_output" = "$usb_output" ]; then
+ new_output=$headset_output
+ echo "Debug: Switching to headset"
+else
+ new_output=$usb_output
+ echo "Debug: Switching to speaker"
+fi
+
+echo "Debug: new_output=$new_output"
+
+wpctl set-default "$new_output"
+
+if [ "$new_output" = "$usb_output" ]; then
+ mic_id=$(wpctl status | grep "Microphone Mono" | grep -oP '\d+(?=\.)' | head -n 1)
+ if [ -n "$mic_id" ]; then
+ wpctl set-mute "$mic_id" 1
+ else
+ echo "Warning: Unable to find microphone ID"
+ fi
+ wpctl set-volume @DEFAULT_AUDIO_SINK@ 50%
+ echo "Sound output set to usb=speaker, mic muted (if found)"
+ echo "Volume 50%"
+else
+ wpctl set-volume @DEFAULT_AUDIO_SINK@ 25%
+ echo "Sound output set to head=headset"
+ echo "Volume 25%"
+fi
diff --git a/debian/.local/bin/tordone b/debian/.local/bin/tordone
new file mode 100755
index 0000000..4e097a0
--- /dev/null
+++ b/debian/.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/debian/.local/bin/torwrap b/debian/.local/bin/torwrap
new file mode 100755
index 0000000..54f5c0e
--- /dev/null
+++ b/debian/.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/debian/.local/bin/transadd b/debian/.local/bin/transadd
new file mode 100755
index 0000000..ffd8ded
--- /dev/null
+++ b/debian/.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/debian/.local/bin/unix b/debian/.local/bin/unix
new file mode 100755
index 0000000..a9fb96e
--- /dev/null
+++ b/debian/.local/bin/unix
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+#original artwork by http://www.sanderfocus.nl/#/portfolio/tech-heroes
+#converted to shell by #nixers @ irc.unix.chat
+
+cat << 'eof'
+ ,_ ,_==▄▂
+ , ▂▃▄▄▅▅▅▂▅Ÿ. / /
+ ▄▆<ÂŽ "»▓▓▓%\ / / / /
+ ,▅7" ®>▓▓▓% / / > / >/%
+ ▐¶▓ ,»▓▓ŸŽ /> %/%// / /
+ ▓▃▅▅▅▃,,▄▅▅▅Æ\// ///>// />/ /
+ V║«ÂŒ.;→ ║<«.,`=// />//%/% / /
+ //╠<ÂŽ -ÂČ,)(▓~"-╝/Ÿ/ %/>/ />
+ / / / ▐% -./▄▃▄▅▐, /7//;//% / /
+ / ////`▌▐ %zWv xX▓▇▌//&;% / /
+ / / / %//%/ŸœŽ▌▃▄▄▄▄▃▃▐¶\/& /
+ </ /</%//`▓!%▓%╣[38;5;255;╣WY<Y)y&/`\
+ / / %/%//</%//\i7; ╠N>)VY>7; \_ UNIX IS VERY SIMPLE IT JUST NEEDS A
+ / /</ //<///<_/%\▓ V%W%ÂŁ)XY _/%‟\_, GENIUS TO UNDERSTAND ITS SIMPLICITY
+ / / //%/_,=--^/%/%%\Ÿ%¶%%} /%%%%%%;\,
+ %/< /_/ %%%%%;X%%\%%;, _/%%%;, \
+ / / %%%%%%;, \%%l%%;// _/%;, dmr
+ / %%%;, <;\-=-/ /
+ ;, l
+eof
diff --git a/debian/.local/bin/unmounter b/debian/.local/bin/unmounter
new file mode 100755
index 0000000..537fd25
--- /dev/null
+++ b/debian/.local/bin/unmounter
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+# Unmount USB drives or Android phones. Replaces the older `dmenuumount`. Fewer
+# prompt and also de-decrypts LUKS drives that are unmounted.
+
+set -e
+
+mounteddroids="$(grep simple-mtpfs /etc/mtab | awk '{print "đŸ“±" $2}')"
+mountedios="$(grep ifuse /etc/mtab | awk '{print "🍎" $2}')"
+lsblkoutput="$(lsblk -nrpo "name,type,size,mountpoint")"
+mounteddrives="$(echo "$lsblkoutput" | awk '($2=="part"||$2="crypt")&&$4!~/\/boot|\/home$|SWAP/&&length($4)>1{printf "đŸ’Ÿ%s (%s)\n",$4,$3}')"
+mountedcifs="$(grep cifs /etc/mtab | awk '{print "đŸȘŸ" $2}')"
+
+allunmountable="$(echo "$mounteddroids
+$mountedios
+$mounteddrives
+$mountedcifs" | sed "/^$/d;s/ *$//")"
+test -n "$allunmountable"
+
+chosen="$(echo "$allunmountable" | dmenu -i -p "Unmount which drive?")"
+chosen="${chosen%% *}"
+test -n "$chosen"
+
+label=$(df "/${chosen#*/}" | tail -n 1 | awk '{print $1}' | xargs -I {} sudo blkid {} | awk -F '\"' '{print $2}')
+if [ -n "$label" ]; then
+ mountpath="$(sudo lsblk -no "mountpoints" "$(df "/${chosen#*/}" | tail -n 1 | awk '{print $1}')")"
+ sudo -A umount -l "/${chosen#*/}"
+ notify-send "⏏ Device unmounted." "$chosen has been unmounted."
+ if [ "/media/$USER/${chosen##*/}" = "/${chosen#*/}" ] &&
+ [ "${chosen##*/}" = "$label" ] &&
+ [ "/media/$USER/${chosen##*/}" = "$mountpath" ] &&
+ [ -e "/${chosen#*/}" ] &&
+ [ ! -s "/${chosen#*/}" ]; then
+ chosen="/${chosen#*/}"
+ rm -r "${chosen:?}" >/dev/null 2>&1 || sudo -A rm -r "${chosen:?}"
+ notify-send "🚼 Mounted path removed." "$chosen has been removed."
+ fi
+else
+ if grep -q "/${chosen#*/}" /etc/mtab | grep -qE "cifs" /etc/mtab; then
+ sudo -A umount "/${chosen#*/}"
+ notify-send "⏏ SMB Drive unmounted." "/${chosen#*/} has been unmounted."
+ elif grep -q "/${chosen#*/}" /etc/mtab | grep -q "ifus" /etc/mtab; then
+ sudo -A umount "/${chosen#*/}"
+ notify-send "⏏ IOS Drive unmounted." "/${chosen#*/} has been unmounted."
+ fi
+ chosen="/${chosen#*/}"
+ [ -e "$chosen" ] && [ ! -s "$chosen" ] &&
+ sudo -A rm -r "${chosen:?}" &&
+ notify-send "🚼 Mounted path removed." "$chosen has been removed."
+fi
+
+# Close the chosen drive if decrypted.
+cryptid="$(echo "$lsblkoutput" | grep "/${chosen#*/}$")"
+cryptid="${cryptid%% *}"
+test -b /dev/mapper/"${cryptid##*/}"
+sudo -A cryptsetup close "$cryptid"
+notify-send "🔒 Device dencryption closed." "Drive is now securely locked again."
diff --git a/debian/.local/bin/vimwikitodo b/debian/.local/bin/vimwikitodo
new file mode 100755
index 0000000..99758f4
--- /dev/null
+++ b/debian/.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/debian/.local/bin/vipy b/debian/.local/bin/vipy
new file mode 100755
index 0000000..8e0e3cb
--- /dev/null
+++ b/debian/.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/debian/.local/bin/wallset b/debian/.local/bin/wallset
new file mode 100755
index 0000000..e2414cb
--- /dev/null
+++ b/debian/.local/bin/wallset
@@ -0,0 +1,295 @@
+#!/bin/bash
+# vim: noai:ts=4:sw=4:expandtab
+#
+# wallset: This program customizes images and videos for wallpaper
+# website: http://terminalroot.com.br/
+# author: Marcos Oliveira
+# dependencies: feh, ffmpeg, imagemagick(convert), xrandr, sed, bash, xdg-utils
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+shopt -s extglob
+
+if [[ $(echo $LANG | cut -c 1-2) == 'pt' ]]; then
+ _lang=("uso" "opçÔes" "Altera o Wallpaper para o nĂșmero informado. Use imagens com 3 dĂ­gitos, exemplo: 014,003,099" "Adiciona imagens" "Use antes do parĂąmetro -a,--add quando quiser adicionar jĂĄ alterar" "Finaliza o loop" "Cria um loop de imagem com o tempo informado em segundos" "Abre a Ășltima imagem adicionada" "Informa quantas imagens hĂĄ" "Mostra a atual imagem" "Adiciona um vĂ­deo como Wallpaper" "Lista os vĂ­deos que jĂĄ foram utilizados" "Utiliza o vĂ­deo pelo nĂșmero listado em --list-videos" "Remove a Ășltima imagem adicionada" "Exibe essa ajuda" "Exibe a versĂŁo desse programa" "Exemplos" "Adiciona uma imagem" "Adiciona e jĂĄ define como Wallpaper" "Altera para imagem com esse nĂșmero" "Adiciona o vĂ­deo" "Usa o vĂ­deo jĂĄ utilizado e listado" "Significa que precisa usar um nĂșmero" "Somente vĂ­deos nos formato .mp4" "Somente serĂĄ exibido 10 segundos iniciais do vĂ­deo" "Use imagens com 3 dĂ­gitos, exemplo: 011" "NĂŁo hĂĄ nenhuma imagem." "Preparando o vĂ­deo" "Use nĂșmeros indicados por" "Use nĂșmeros de 1 atĂ©" "Tempo invĂĄlido" "Tipo invĂĄlido" "Use nĂșmeros de 001 atĂ©" "Formato invĂĄlido." "Somente formato MP4" "NĂșmero invĂĄlido. Use" "Informe o vĂ­deo" "NĂŁo hĂĄ imagens." "NĂŁo hĂĄ vĂ­deos.")
+else
+ _lang=("usage" "options" "Change the Wallpaper to the number entered. Use images with 3 digits, example: 014,003,099" "Add images" "Use before the -a, --add parameter when you want to add already change" "Ends the loop" "Creates an image loop with the time reported in seconds" "Opens the last image added" "Informs how many images there are" "Shows the current image" "Add a video as Wallpaper" "Lists the videos that have already been used" "Use the video by the number listed in --list-videos" "Remove the last image added" "Display this help" "Display the version of this program" "Examples" "Add an image" "Add and set it as Wallpaper" "Change to image with this number" "Add the video" "Use the video already used and listed" "Means that you need to use a number" "Only videos in .mp4 format" "Only the first 10 seconds of the video will be displayed" "Use 3-digit images, example: 011" "There is no image." "Preparing the video..." "Use numbers indicated by" "Use numbers numbers from 1 to" "Invalid time" "Invalid type" "Use numbers from 001 to" "Invalid format." "MP4 format only" "Invalid number. Use" "Report Video" "No images." "No videos.")
+fi
+
+_usage() {
+ cat <<EOF
+${_lang[0]}: ${0##*/} [${_lang[1]}]
+
+ Options:
+ -u,--use [N] ${_lang[2]}
+ -a,--add img.jpg ... ${_lang[3]}
+ -S,--set ${_lang[4]}
+ -q,--quit ${_lang[5]}
+ -t,--time [N] ${_lang[6]}
+ -d,--display ${_lang[7]}
+ -c,--count ${_lang[8]}
+ -s,--show ${_lang[9]}
+ -V,--video [video] ${_lang[10]}
+ -L,--list-videos ${_lang[11]}
+ -I,--set-video [N] ${_lang[12]}
+ -r,--remove ${_lang[13]}
+ -h,--help ${_lang[14]}
+ -v,--version ${_lang[15]}
+
+ ${_lang[16]}:
+ ${_lang[17]} → '${0##*/} -a img.jpg'
+ ${_lang[18]} → '${0##*/} --set --add img.jpg'
+ ${_lang[19]} → '${0##*/} -u 001'
+ ${_lang[20]} → '${0##*/} -V video.mp4'
+ ${_lang[21]} → '${0##*/} -I 3'
+
+* [N] ${_lang[22]}
+** ${_lang[23]}
+*** ${_lang[24]}
+**** ${_lang[25]}
+EOF
+ exit
+}
+
+_dir_img="${HOME}/.local/share/wallpapers"
+_dir_vid="${_dir_img}/video"
+pid="$$"
+
+[[ ! -d "${_dir_img}" ]] && mkdir -p "${_dir_img}"
+[[ ! -d "${_dir_vid}" ]] && mkdir -p "${_dir_vid}"
+ctrl_c() {
+ exit 127
+}
+
+_get_resolution_screen() {
+ xrandr | grep '*' | awk '{print $1}' | cut -dx -f1
+}
+
+_last_wall() {
+ ls ${_dir_img}/*.jpg 2>/dev/null >/dev/null
+ if [[ "$?" != "0" ]]; then
+ printf "%s" "0"
+ else
+ _n=$(ls -1 ${_dir_img}/*.jpg | tail -n 1 | tr -d 'a-z\.\-\/')
+ printf "%s" "${_n}"
+ fi
+}
+
+_new_number() {
+ _n=$(echo $(_last_wall) + 1 | bc)
+ [[ $(expr length ${_n}) == 1 ]] && _n="00${_n}" || _n="0${_n}"
+ printf "%s" "${_n}"
+
+}
+
+_resize_img() {
+ _n=$(_new_number)
+ _w=$(_get_resolution_screen)
+ convert -resize "${_w}" "${1}" "${_dir_img}/wallpaper-${_n}.jpg"
+ if [[ "${add}" == 1 ]]; then
+ feh --no-fehbg --bg-scale "${_dir_img}/wallpaper-${_n}.jpg"
+ [[ $(which gsettings >&- 2>&-) ]] &&
+ gsettings set org.gnome.desktop.background picture-uri file:///"${_dir_img}/wallpaper-${_n}.jpg"
+ fi
+}
+
+_count() {
+ ls ${_dir_img}/*.jpg 2>/dev/null | wc -l
+}
+
+_current() {
+ xdg-open "${_dir_img}/wallpaper-$(_last_wall).jpg"
+}
+
+_loop() {
+ if [[ $(_count) < 1 ]]; then
+ printf "%s\n" "${_lang[26]}"
+ exit 1
+ fi
+ i=1
+ while :; do
+ [[ "${i}" -ge "$(_count)" ]] && i=1
+ if [[ $(expr length $i) == 1 ]]; then
+ feh --no-fehbg --bg-scale "${_dir_img}/wallpaper-00${i}.jpg"
+ else
+ feh --no-fehbg --bg-scale "${_dir_img}/wallpaper-0${i}.jpg"
+ fi
+ sleep $1
+ let i=i+1
+
+ done &
+
+}
+
+_quit_loop() {
+ #kill -9 $$
+ killall -q bash 2>/dev/null >/dev/null
+}
+
+_set_img() {
+ ls "${_dir_img}/wallpaper-${1}.jpg" >/dev/null 2>/dev/null
+ if [[ "$?" == 0 ]]; then
+ feh --no-fehbg --bg-scale "${_dir_img}/wallpaper-$1.jpg"
+ if [[ $(which gsettings >&- 2>&-) && "$XDG_CURRENT_DESKTOP" == "GNOME" ]]; then
+ gsettings set org.gnome.desktop.background picture-uri file:///"${_dir_img}/wallpaper-$1.jpg"
+ fi
+ else
+ _t=$(_count)
+ [[ "${_t}" -lt 100 ]] && _t="0${_t}"
+ echo "$1 - ${_lang[35]}: "$(ls ${_dir_img} | sed 's/wallpaper-//;s/\.jpg/ /g')
+ fi
+
+}
+
+_show() {
+ mpv --really-quiet "${XDG_DATA_HOME:-${HOME}/.local/share}/wallpapers/video/bg.mp4"
+}
+
+_video() {
+ a=1
+ for i in $(ls -d $_dir_vid/*/ 2>/dev/null); do
+ if [[ -d "$_dir_vid/$a" ]]; then
+ let a=a+1
+ fi
+ done
+
+ mkdir -p "$_dir_vid/$a"
+
+ echo -e "${_lang[27]}...\r"
+ ffmpeg -y -ss 00:00 -i ${1} -q:v 1 -t 10 "${_dir_vid}/${a}/filename%05d.jpg" >${_dir_vid}/${a}/video.info 2>&1
+ t=$(ls ${_dir_vid}/${a}/* | wc -l)
+ let t=t-1
+ echo ${1} >${_dir_vid}/${a}/video.info
+
+ i=1
+ # ctrl_c
+ while :; do
+ feh --no-fehbg --bg-scale $(ls -1 ${_dir_vid}/${a}/* | sed -n "${i}p")
+ sleep 0.01
+ let i=i+1
+ [[ "${i}" -gt "${t}" ]] && i=1
+ done &
+}
+
+_list_vid() {
+ ls -d ${_dir_vid}/* 2>/dev/null >/dev/null
+ if [[ "$?" != "0" ]]; then
+ echo ${_lang[38]}
+ exit 1
+ fi
+ for i in $(ls -1 ${_dir_vid}/*/video.info); do
+ echo $i | awk 'BEGIN { FS = "/" } ; { print $6 }'
+ cat $i
+ done | paste - - | sed 's/\t/ → /g'
+}
+
+_set_video() {
+ ls -d ${_dir_vid}/* 2>/dev/null >/dev/null
+ if [[ "$?" != "0" ]]; then
+ echo ${_lang[38]}
+ exit 1
+ fi
+ if [[ $(echo "$1" | sed -n -r '/^[0-9]+$/p') == "" ]]; then
+ echo "${_lang[28]} '${0##*/} --list-videos'."
+ else
+ if [[ "$1" -gt "$(ls -d1 ${_dir_vid}/* | wc -l)" ]]; then
+ echo "${_lang[29]} $(ls -d1 ${_dir_vid}/* | wc -l)"
+ exit 1
+ else
+ t=$(ls ${_dir_vid}/${1}/* | wc -l)
+ let t=t-1
+ i=1
+ # ctrl_c
+ while :; do
+ feh --no-fehbg --bg-scale $(ls -1 ${_dir_vid}/${1}/* | sed -n "${i}p")
+ sleep 0.01
+ let i=i+1
+ [[ "${i}" -gt "${t}" ]] && i=1
+ done &
+ fi
+ fi
+}
+
+while [[ "$1" ]]; do
+ case "${1}" in
+ -h | --help) _usage ;;
+ -v | --version) echo "${0##*/} 0.0.1-beta" ;;
+ -s | --show) _show && exit ;;
+ -c | --count) _count ;;
+ -d | --display) _current ;;
+ -t | --time)
+ shift
+ if [[ $(echo "$1" | sed -n -r '/^[0-9]+$/p') == "" ]]; then
+ echo "${_lang[30]}"
+ else
+ _loop "$1"
+ fi
+ ;;
+ -q | --quit) _quit_loop ;;
+ -S | --set) export add=1 ;;
+ -a | --add)
+ shift
+ while [[ -f "${1}" ]]; do
+ if [[ "$(file ${1} | awk '{print $2}')" == @(PNG|JPEG) ]]; then
+ _resize_img $1
+ else
+ echo "${_lang[31]}: $1"
+ fi
+ shift
+ done
+ ;;
+ -u | --use)
+ shift
+
+ ls ${_dir_img}/*.jpg 2>/dev/null >/dev/null
+ if [[ "$?" != "0" ]]; then
+ echo "${_lang[37]}"
+ exit 1
+ fi
+
+ if [[ $(echo "$1" | sed -n -r '/^[0-9]+$/p') == "" ]]; then
+ echo "${_lang[32]} $(_last_wall)"
+ else
+ if [[ "$1" > "$(_last_wall)" ]]; then
+ echo "${_lang[32]} $(_last_wall)"
+ else
+ _set_img "$1"
+ fi
+ fi
+ ;;
+ -r | --remove-last) rm "${_dir_img}/wallpaper-$(_last_wall).jpg" ;;
+ --video | -V)
+ shift
+ [[ -z "$1" ]] && {
+ echo "${_lang[36]}"
+ exit 1
+ }
+ [[ $(file -b --mime-type ${1} | cut -d"/" -f1) != 'video' ]] && {
+ echo "${_lang[33]}"
+ exit 1
+ }
+ [[ $(file -b --mime-type ${1} | cut -d"/" -f2) != @(mp4|x-m4v) ]] && {
+ echo "${_lang[34]}"
+ exit 1
+ }
+ _video "${1}"
+ ;;
+ -L | --list-videos) _list_vid ;;
+ -I | --set-video)
+ shift
+ _set_video "${1}"
+ ;;
+ esac
+ shift
+done
diff --git a/debian/.local/bin/weath b/debian/.local/bin/weath
new file mode 100755
index 0000000..d013f6f
--- /dev/null
+++ b/debian/.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/debian/.local/bin/webcam b/debian/.local/bin/webcam
new file mode 100755
index 0000000..0a4a297
--- /dev/null
+++ b/debian/.local/bin/webcam
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+case "$1" in
+-h | h | hflip) flip="--vf=lavfi=hflip" ;;
+-v | v | vflip) flip="--vf=lavfi=vflip" ;;
+-hv | hv | hvflip) flip="--vf=lavfi=hflip,vflip" ;;
+*) flip="" ;;
+esac
+
+# Extract full camera names and their first /dev/video* device
+cameras=$(
+ v4l2-ctl --list-devices | awk '
+ BEGIN { RS=""; FS="\n" }
+ {
+ name = $1;
+ sub(/ \(.*$/, "", name);
+ gsub(/^[ \t]+|[ \t]+$/, "", name);
+ for (i=2; i<=NF; i++) {
+ if ($i ~ /\/dev\/video/) {
+ gsub(/^[ \t]+/, "", $i);
+ print name "|" $i;
+ break;
+ }
+ }
+ }'
+)
+
+# Extract only camera names for dmenu
+names=$(echo "$cameras" | cut -d '|' -f1)
+
+# Select camera with dmenu
+choice=$(echo "$names" | dmenu -i -p 'Choose a camera:')
+
+# Find corresponding /dev/video* device
+camera=$(echo "$cameras" | awk -F '|' -v sel="$choice" '$1 == sel {print $2}')
+
+# If no selection, exit
+[ -z "$camera" ] && exit 1
+
+mpv --untimed \
+ --no-cache \
+ --no-osc \
+ --no-input-default-bindings \
+ --profile=low-latency \
+ --input-conf=/dev/null \
+ --title=webcam \
+ --x11-name=webcam \
+ $flip "$camera"
diff --git a/debian/.local/bin/whereami b/debian/.local/bin/whereami
new file mode 100755
index 0000000..68c9ffe
--- /dev/null
+++ b/debian/.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 "mac"
+ ;;
+"darwin,x86_64")
+ echo "intel mac"
+ ;;
+"msys,"* | "cygwin,"* | "windows,"*)
+ echo "windows"
+ ;;
+*)
+ echo "Unsupported OS"
+ exit
+ ;;
+esac
diff --git a/debian/.local/bin/xdg-terminal-exec b/debian/.local/bin/xdg-terminal-exec
new file mode 100755
index 0000000..12b18ff
--- /dev/null
+++ b/debian/.local/bin/xdg-terminal-exec
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+"$TERMINAL" -e "$@"
diff --git a/debian/.local/bin/xdotmouse b/debian/.local/bin/xdotmouse
new file mode 100755
index 0000000..43e1a0d
--- /dev/null
+++ b/debian/.local/bin/xdotmouse
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+speed="10"
+
+case "$1" in
+h) xdotool mousemove_relative -- -$speed 0 ;;
+j) xdotool mousemove_relative 0 $speed ;;
+k) xdotool mousemove_relative -- 0 -$speed ;;
+l) xdotool mousemove_relative $speed 0 ;;
+c) xdotool click --clearmodifiers 1 ;;
+C) xdotool click --clearmodifiers --repeat 2 1 ;;
+m) xdotool click --clearmodifiers 2 ;;
+esac
diff --git a/debian/.local/bin/xinputconf b/debian/.local/bin/xinputconf
new file mode 100755
index 0000000..f469666
--- /dev/null
+++ b/debian/.local/bin/xinputconf
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+device_name="$(xinput list | grep '↳' | sed 's/.*↳ //g' | fzf)"
+[ -n "$device_name" ] && device_id="$(xinput list | grep -F "↳ $device_name" | sed -n 's/.*id=\([0-9]\+\).*/\1/p')" || exit
+
+show_matrix() {
+ printf "%s\n" "$(xinput list-props "$device_id" | awk '/Coordinate Transformation Matrix/{print $0}' | sed 's/^[[:space:]]*//g')"
+}
+
+# Function to set new speed and scrolling speed values
+set_speeds() {
+ printf "Set trackpoint speed: "
+ read -r speed
+
+ printf "Set trackpoint scrolling speed: "
+ read -r scroll_speed
+
+ prop_id=$(xinput list-props "$device_name" | awk '/Coordinate Transformation Matrix/ {match($0, /\(([0-9]+)\)/, a); print a[1]}')
+
+ if [ -n "$prop_id" ]; then
+ xinput set-prop "$device_name" "$prop_id" "$speed, 0, 0, 0, $scroll_speed, 0, 0, 0, 1"
+ show_matrix
+ else
+ printf "Property ID for Coordinate Transformation Matrix not found.\n" >&2
+ return 1
+ fi
+}
+
+case "$1" in
+-s)
+ set_speeds
+ ;;
+-l | "")
+ show_matrix
+ ;;
+*)
+ echo "Invalid option. Use -s to set speeds or -l to list the matrix."
+ ;;
+esac
diff --git a/debian/.local/bin/xkeys b/debian/.local/bin/xkeys
new file mode 100755
index 0000000..e26a94a
--- /dev/null
+++ b/debian/.local/bin/xkeys
Binary files differ
diff --git a/debian/.local/bin/ylog b/debian/.local/bin/ylog
new file mode 100755
index 0000000..254803b
--- /dev/null
+++ b/debian/.local/bin/ylog
@@ -0,0 +1,359 @@
+#!/bin/sh
+
+HOST="root@thesiah.xyz"
+LOG_DIR="/var/log/nginx"
+
+TARGET="all" # "all" means no target filter (show all lines)
+COUNTRY="all" # all|kr|us
+SCOPE="all" # all|access|recordings
+EXCL_FIREFOX=0 # 1 = exclude Firefox lines by default
+EXCLUDES="59.19.56.8" # default exclude pattern
+ADD_EXCLUDES=""
+LINE_LIMIT=500 # default number of lines when TARGET=all
+DATE_FILTER="" # date filter: "2" = 2 days ago, "~2" = last 2 days to today
+
+usage() {
+ cat <<'EOF'
+Usage: ylog [options]
+
+Options:
+ -t TARGET Search IP or string (default: all → no filter, show all lines)
+ e.g. -t 207.96.105.230
+ e.g. -t si
+ e.g. -t all
+
+ -c COUNTRY Select country logs (default: all)
+ all : all logs
+ kr : recordings.kr.log + recordings.access.log
+ us : recordings.us.log + recordings.access.log
+
+ -s SCOPE Select log scope (default: all)
+ all : recordings + access
+ recordings : recordings.* logs only
+ access : access.* logs only
+
+ -n Disable Firefox exclusion (by default, Firefox lines are excluded)
+
+ -x PATTERN Add extra exclude pattern (can be repeated)
+ e.g. -x bot -x '192\.0\.2\.1'
+
+ -l N Limit number of lines (default: 10)
+ Only applies when TARGET=all
+ e.g. -l 50 → show last 50 lines per file
+
+ -d DATE Filter by date
+ e.g. -d 2 → logs from 2 days ago only
+ e.g. -d 1 → logs from 1 day ago only
+ e.g. -d ~2 → logs from 2 days ago to today
+
+ -h Show this help
+
+Examples:
+ ylog # All logs, last 10 lines each
+ ylog -s recordings # Recordings logs only, last 10 lines each
+ ylog -c kr -t 1.2.3.4 # Search specific IP in Korean logs
+ ylog -t all -l 50 # All logs, last 50 lines each
+ ylog -d 1 # Logs from 1 day ago only
+ ylog -d ~2 # Logs from 2 days ago to today
+EOF
+ exit 0
+}
+
+while getopts "t:c:s:nx:l:d:h" opt; do
+ case "$opt" in
+ t) TARGET="$OPTARG" ;;
+ c) COUNTRY="$OPTARG" ;;
+ s) SCOPE="$OPTARG" ;;
+ n) EXCL_FIREFOX=1 ;;
+ x) ADD_EXCLUDES="${ADD_EXCLUDES}
+$OPTARG" ;;
+ l) LINE_LIMIT="$OPTARG" ;;
+ d) DATE_FILTER="$OPTARG" ;;
+ h) usage ;;
+ *) usage ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+# escape for grep -E
+esc_target=$(printf '%s' "$TARGET" | sed -E 's/[][^$.*/+?(){}|\\]/\\&/g')
+
+remote_sh='
+set -eu
+LOG_DIR="'"$LOG_DIR"'"
+COUNTRY="'"$COUNTRY"'"
+SCOPE="'"$SCOPE"'"
+TARGET="'"$TARGET"'"
+ESC_TARGET="'"$esc_target"'"
+EXCL_FIREFOX='"$EXCL_FIREFOX"'
+LINE_LIMIT='"$LINE_LIMIT"'
+DATE_FILTER="'"$DATE_FILTER"'"
+
+# collect files
+pick_files() {
+ # recordings: include recordings.access.log only if COUNTRY=all
+ if [ "$SCOPE" = "recordings" ] || [ "$SCOPE" = "all" ]; then
+ if [ "$COUNTRY" = "all" ]; then
+ for q in "$LOG_DIR/recordings.access.log" "$LOG_DIR/recordings.access.log".*; do
+ [ -e "$q" ] && printf "%s\n" "$q"
+ done
+ fi
+ case "$COUNTRY" in
+ kr) for q in "$LOG_DIR/recordings.kr.log" "$LOG_DIR/recordings.kr.log".*; do [ -e "$q" ] && printf "%s\n" "$q"; done ;;
+ us) for q in "$LOG_DIR/recordings.us.log" "$LOG_DIR/recordings.us.log".*; do [ -e "$q" ] && printf "%s\n" "$q"; done ;;
+ all)
+ for p in recordings.kr.log recordings.us.log; do
+ for q in "$LOG_DIR/$p" "$LOG_DIR/$p".*; do [ -e "$q" ] && printf "%s\n" "$q"; done
+ done
+ ;;
+ esac
+ fi
+ # access logs
+ if [ "$SCOPE" = "access" ] || [ "$SCOPE" = "all" ]; then
+ for q in "$LOG_DIR/access.log" "$LOG_DIR/access.log".*; do
+ [ -e "$q" ] && printf "%s\n" "$q"
+ done
+ fi
+ if [ "$SCOPE" = "hidden" ] || [ "$SCOPE" = "all" ]; then
+ for q in "$LOG_DIR/hidden.access.log" "$LOG_DIR/hidden.access.log".*; do
+ [ -e "$q" ] && printf "%s\n" "$q"
+ done
+ fi
+}
+
+# build exclude regex
+build_exre() {
+ EXRE=""
+ TEMP_FILE="/tmp/.ylog_exre_$$"
+ rm -f "$TEMP_FILE"
+
+ { printf "%s\n" "${EXCLUDES:-}"; printf "%s\n" "${ADD_EXCLUDES:-}"; } | sed "/^$/d" | while IFS= read -r pat
+ do
+ esc=$(printf "%s" "$pat" | sed -E "s/[][^$.*/+?(){}|\\]/\\\\&/g")
+ if [ -s "$TEMP_FILE" ]; then
+ EXRE="$(cat "$TEMP_FILE")|$esc"
+ else
+ EXRE="$esc"
+ fi
+ printf "%s" "$EXRE" > "$TEMP_FILE"
+ done
+
+ if [ -f "$TEMP_FILE" ]; then
+ cat "$TEMP_FILE"
+ rm -f "$TEMP_FILE"
+ fi
+}
+
+FILES_TMP="/tmp/.ylog_files_$$"
+pick_files | sed "/^$/d" | sort -u > "$FILES_TMP"
+
+if [ ! -s "$FILES_TMP" ]; then
+ echo "[WARN] No log files found for COUNTRY=$COUNTRY SCOPE=$SCOPE." >&2
+ exit 0
+fi
+
+if [ -n "$DATE_FILTER" ]; then
+ echo "[SCAN] Target: \"$TARGET\" Country: $COUNTRY Scope: $SCOPE Date: $DATE_FILTER"
+else
+ echo "[SCAN] Target: \"$TARGET\" Country: $COUNTRY Scope: $SCOPE"
+fi
+echo "[FILES]"
+cat "$FILES_TMP"
+
+EXRE="$(build_exre || true)"
+
+RESULTS_TMP="/tmp/.ylog_results_$$"
+rm -f "$RESULTS_TMP"
+
+found=0
+for f in $(cat "$FILES_TMP"); do
+ [ -e "$f" ] || continue
+ case "$f" in *.gz) reader="zcat -f -- \"$f\"" ;; *) reader="cat -- \"$f\"" ;; esac
+
+ if [ "$TARGET" = "all" ]; then
+ cmd="$reader"
+ else
+ cmd="$reader | grep -E -- \"${ESC_TARGET}\""
+ fi
+
+ if [ -n "${EXRE:-}" ]; then
+ cmd="$cmd | grep -v -E -- \"$EXRE\""
+ fi
+ [ "$EXCL_FIREFOX" -eq 1 ] && cmd="$cmd | grep -vi firefox"
+
+ if [ "$TARGET" = "all" ]; then
+ sh -c "$cmd | tail -n $LINE_LIMIT" >> "$RESULTS_TMP" 2>/dev/null && found=1
+ else
+ sh -c "$cmd" >> "$RESULTS_TMP" 2>/dev/null && found=1
+ fi
+done
+
+# Deduplicate: keep only the latest log for same IP+URI within same minute
+# Format: IP - user [datetime] "METHOD URI PROTOCOL" status bytes ...
+if [ -f "$RESULTS_TMP" ] && [ -s "$RESULTS_TMP" ]; then
+ # Date filtering: calculate target dates
+ TARGET_DATE=""
+ START_DATE=""
+ if [ -n "$DATE_FILTER" ]; then
+ if [ "$(printf '%s' "$DATE_FILTER" | cut -c1)" = "~" ]; then
+ # Range mode: ~N means last N days to today
+ DAYS=$(printf '%s' "$DATE_FILTER" | sed 's/^~//')
+ if [ -n "$DAYS" ] && [ "$DAYS" -gt 0 ] 2>/dev/null; then
+ START_DATE=$(date -d "$DAYS days ago" +%d/%b/%Y 2>/dev/null || date -v-${DAYS}d +%d/%b/%Y 2>/dev/null || echo "")
+ [ -z "$START_DATE" ] && START_DATE=""
+ fi
+ else
+ # Single date mode: N means N days ago only
+ DAYS="$DATE_FILTER"
+ if [ -n "$DAYS" ] && [ "$DAYS" -ge 0 ] 2>/dev/null; then
+ TARGET_DATE=$(date -d "$DAYS days ago" +%d/%b/%Y 2>/dev/null || date -v-${DAYS}d +%d/%b/%Y 2>/dev/null || echo "")
+ [ -z "$TARGET_DATE" ] && TARGET_DATE=""
+ fi
+ fi
+ fi
+
+ awk -v date_filter="${DATE_FILTER:-}" -v target_date="${TARGET_DATE:-}" -v start_date="${START_DATE:-}" "
+ function parse_date(date_str, parts, month_num) {
+ # date_str format: \"DD/MMM/YYYY\"
+ split(date_str, parts, \"/\")
+ month_names = \"JanFebMarAprMayJunJulAugSepOctNovDec\"
+ month_num = index(month_names, parts[2])
+ month_num = (month_num + 2) / 3 # Convert to 1-12
+ # Return YYYYMMDD for easy comparison
+ return sprintf(\"%04d%02d%02d\", parts[3], month_num, parts[1])
+ }
+
+ BEGIN {
+ today = strftime(\"%Y%m%d\")
+ filter_target = \"\"
+ date_range_start = \"\"
+ date_range_end = \"\"
+
+ if (date_filter != \"\" && date_filter ~ /^~/) {
+ # Range mode: extract days
+ if (start_date != \"\" && split(start_date, sd_parts, \"/\") == 3) {
+ month_names = \"JanFebMarAprMayJunJulAugSepOctNovDec\"
+ month_num = index(month_names, sd_parts[2])
+ if (month_num > 0) {
+ month_num = (month_num + 2) / 3
+ start_date_num = sprintf(\"%04d%02d%02d\", sd_parts[3], month_num, sd_parts[1])
+ date_range_start = start_date_num
+ date_range_end = today
+ }
+ }
+ } else if (date_filter != \"\" && target_date != \"\") {
+ # Single date mode
+ if (split(target_date, td_parts, \"/\") == 3) {
+ month_names = \"JanFebMarAprMayJunJulAugSepOctNovDec\"
+ month_num = index(month_names, td_parts[2])
+ if (month_num > 0) {
+ month_num = (month_num + 2) / 3
+ target_date_num = sprintf(\"%04d%02d%02d\", td_parts[3], month_num, td_parts[1])
+ filter_target = target_date_num
+ }
+ }
+ }
+ }
+ {
+ line = \$0
+
+ # Extract datetime [DD/MMM/YYYY:HH:MM:SS
+ datetime = \"\"
+ minute_part = \"\"
+ pos = match(line, /\\[[0-9]{2}\\/[A-Z][a-z]{2}\\/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2}/)
+ if (pos > 0) {
+ datetime = substr(line, pos + 1, 20)
+ date_part = substr(datetime, 1, 11) # DD/MMM/YYYY
+ minute_part = substr(datetime, 1, 17) # DD/MMM/YYYY:HH:MM
+
+ # Date filtering
+ if (filter_target != \"\" || date_range_start != \"\") {
+ line_date_num = parse_date(date_part)
+ if (filter_target != \"\" && line_date_num != filter_target) {
+ next # Skip if not matching target date
+ }
+ if (date_range_start != \"\" && (line_date_num < date_range_start || line_date_num > date_range_end)) {
+ next # Skip if outside date range
+ }
+ }
+ }
+
+ # Extract IP (first field before \" - \")
+ ip = \"\"
+ pos = match(line, /^[0-9a-fA-F:.]+/)
+ if (pos > 0) {
+ ip = substr(line, pos, RLENGTH)
+ }
+
+ # Extract URI (between quotes after method)
+ uri = \"\"
+ # Find the quoted request part: \"METHOD URI PROTOCOL\"
+ # Use split on quotes to find the request section
+ split(line, parts, \"\\\"\")
+ if (length(parts) >= 2) {
+ # parts[2] should be \"METHOD URI PROTOCOL\"
+ n = split(parts[2], req_parts, \" \")
+ if (n >= 2) {
+ uri = req_parts[2]
+ }
+ }
+
+ # Create key: IP + minute + URI
+ key = ip \"|\" minute_part \"|\" uri
+
+ # Store latest line for each key (compare by full line for latest)
+ if (!(key in latest) || line > latest[key]) {
+ latest[key] = line
+ timestamp[key] = datetime
+ }
+ }
+ END {
+ empty_str = \"\"
+ idx = 0
+ for (key in latest) {
+ line = latest[key]
+ pos = match(line, /\\[[0-9]{2}\\/[A-Z][a-z]{2}\\/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2}/)
+ if (pos > 0) {
+ datetime = substr(line, pos + 1, 20)
+ date_part = substr(datetime, 1, 11)
+ time_part = substr(datetime, 13, 8)
+ gsub(/:/, empty_str, time_part)
+ time_num = time_part + 0
+ split(date_part, d_parts, \"/\")
+ month_names = \"JanFebMarAprMayJunJulAugSepOctNovDec\"
+ month_num = index(month_names, d_parts[2])
+ if (month_num > 0) {
+ month_num = (month_num + 2) / 3
+ sort_key = sprintf(\"%04d%02d%02d%06d%08d\", d_parts[3], month_num, d_parts[1], time_num, idx)
+ sorted_lines[sort_key] = line
+ } else {
+ sorted_lines[sprintf(\"99999999999999999999%08d\", idx)] = line
+ }
+ } else {
+ sorted_lines[sprintf(\"99999999999999999999%08d\", idx)] = line
+ }
+ idx++
+ }
+ n = asorti(sorted_lines, sorted_keys)
+ for (i = 1; i <= n; i++) {
+ print sorted_lines[sorted_keys[i]]
+ }
+ }
+ " "$RESULTS_TMP"
+ found=1
+fi
+
+rm -f "$FILES_TMP" "$RESULTS_TMP"
+
+if [ "$TARGET" != "all" ] && [ "$found" -eq 0 ]; then
+ echo "[INFO] No matches found (or filtered out)." >&2
+fi
+'
+
+# remote execution
+ssh "$HOST" \
+ EXCLUDES="$(printf '%s' "$EXCLUDES")" \
+ ADD_EXCLUDES="$(printf '%s' "$ADD_EXCLUDES")" \
+ /bin/sh <<REMOTE_EOF
+$remote_sh
+REMOTE_EOF