aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--[-rwxr-xr-x]archive/bridge-sync.sh.bak0
-rw-r--r--[-rwxr-xr-x]archive/create-lab0
-rw-r--r--archive/dm-recent-edit19
-rw-r--r--[-rwxr-xr-x]archive/kvm-lab-create.bak0
-rw-r--r--[-rwxr-xr-x]archive/kvm-lab-destroy.bak0
-rw-r--r--[-rwxr-xr-x]archive/rofi-notmuch (renamed from rofi-notmuch)0
-rw-r--r--[-rwxr-xr-x]archive/rofi-task-names (renamed from rofi-task-names)0
-rw-r--r--[-rwxr-xr-x]archive/rofi-vim-power (renamed from rofi-vim-power)0
-rw-r--r--[-rwxr-xr-x]archive/timer.bak (renamed from timer)0
-rw-r--r--[-rwxr-xr-x]archive/vm-new0
-rw-r--r--[-rwxr-xr-x]archive/vm-rm0
-rwxr-xr-xbookmark-clip2
-rwxr-xr-xcht.sh796
l---------claude1
-rwxr-xr-xclip-capture16
-rwxr-xr-xclipwipe17
-rwxr-xr-xdm-frecent27
-rwxr-xr-xdm-memo8
-rwxr-xr-xdm-vimwiki-open2
-rwxr-xr-xdwm-status17
-rwxr-xr-xfeh6
-rwxr-xr-xfkill22
-rwxr-xr-xfre-prune.sh8
-rwxr-xr-xhledger-category-select35
-rwxr-xr-xlibreoffice6
-rwxr-xr-xnsxiv6
-rwxr-xr-xocr-scan146
-rwxr-xr-xpassmenu15
-rwxr-xr-xpdf-fzf55
-rwxr-xr-xpdf-txt80
-rwxr-xr-xproton-bridge-cert-refresh39
l---------pudb1
l---------pylsp1
-rwxr-xr-xrofi-cheat28
l---------sfeed.sh1
-rwxr-xr-xshield-backup9
-rwxr-xr-xsimplex4
-rwxr-xr-xupdate-cht.sh12
-rwxr-xr-xwiki341
-rwxr-xr-xwiki-search26
-rwxr-xr-xzathura6
41 files changed, 1677 insertions, 75 deletions
diff --git a/archive/bridge-sync.sh.bak b/archive/bridge-sync.sh.bak
index 7aecb75..7aecb75 100755..100644
--- a/archive/bridge-sync.sh.bak
+++ b/archive/bridge-sync.sh.bak
diff --git a/archive/create-lab b/archive/create-lab
index 8640ed6..8640ed6 100755..100644
--- a/archive/create-lab
+++ b/archive/create-lab
diff --git a/archive/dm-recent-edit b/archive/dm-recent-edit
new file mode 100644
index 0000000..583094f
--- /dev/null
+++ b/archive/dm-recent-edit
@@ -0,0 +1,19 @@
+#!/usr/bin/env sh
+# Pick a recently-edited file from vim's :oldfiles, open in st -e vim.
+set -eu
+
+VIMINFO="${VIMINFO:-$HOME/.vim/.viminfo}"
+
+file=$(
+ vim -e -c "rviminfo! $VIMINFO" \
+ -c 'redir >> /dev/stdout | silent oldfiles | redir END | qa!' \
+ 2>/dev/null \
+ | sed 's/^[[:space:]]*[0-9]*:[[:space:]]*//' \
+ | sed "s|^~|$HOME|" \
+ | awk 'NF && !seen[$0]++' \
+ | while IFS= read -r f; do [ -f "$f" ] && printf '%s\n' "$f"; done \
+ | dmenu -l 20 -i -p 'recent:'
+) || exit 0
+[ -n "$file" ] || exit 0
+
+exec st -e vim "$file"
diff --git a/archive/kvm-lab-create.bak b/archive/kvm-lab-create.bak
index 43d2926..43d2926 100755..100644
--- a/archive/kvm-lab-create.bak
+++ b/archive/kvm-lab-create.bak
diff --git a/archive/kvm-lab-destroy.bak b/archive/kvm-lab-destroy.bak
index 3548ef9..3548ef9 100755..100644
--- a/archive/kvm-lab-destroy.bak
+++ b/archive/kvm-lab-destroy.bak
diff --git a/rofi-notmuch b/archive/rofi-notmuch
index 8fa5097..8fa5097 100755..100644
--- a/rofi-notmuch
+++ b/archive/rofi-notmuch
diff --git a/rofi-task-names b/archive/rofi-task-names
index 965bb93..965bb93 100755..100644
--- a/rofi-task-names
+++ b/archive/rofi-task-names
diff --git a/rofi-vim-power b/archive/rofi-vim-power
index 3673119..3673119 100755..100644
--- a/rofi-vim-power
+++ b/archive/rofi-vim-power
diff --git a/timer b/archive/timer.bak
index fa8883c..fa8883c 100755..100644
--- a/timer
+++ b/archive/timer.bak
diff --git a/archive/vm-new b/archive/vm-new
index 3955c6c..3955c6c 100755..100644
--- a/archive/vm-new
+++ b/archive/vm-new
diff --git a/archive/vm-rm b/archive/vm-rm
index 3cbeb7d..3cbeb7d 100755..100644
--- a/archive/vm-rm
+++ b/archive/vm-rm
diff --git a/bookmark-clip b/bookmark-clip
index 57b14c7..e9e5df4 100755
--- a/bookmark-clip
+++ b/bookmark-clip
@@ -9,8 +9,6 @@ notify() { [ "$has_notify" = 1 ] && notify-send -u "$1" "bookmark" "$2" || true;
# Primary selection first, fall back to clipboard
bookmark="$(xclip -o -selection primary 2>/dev/null || true)"
-[ -z "$bookmark" ] && bookmark="$(xclip -o -selection clipboard 2>/dev/null || true)"
-
bookmark="$(printf '%s' "$bookmark" | sed 's/[[:space:]]\+$//')"
if [ -z "$bookmark" ]; then
diff --git a/cht.sh b/cht.sh
new file mode 100755
index 0000000..2f662ac
--- /dev/null
+++ b/cht.sh
@@ -0,0 +1,796 @@
+#!/bin/bash
+# shellcheck disable=SC1117,SC2001
+#
+# [X] open section
+# [X] one shot mode
+# [X] usage info
+# [X] dependencies check
+# [X] help
+# [X] yank/y/copy/c
+# [X] Y/C
+# [X] eof problem
+# [X] more
+# [X] stealth mode
+#
+# here are several examples for the stealth mode:
+#
+# zip lists
+# list permutation
+# random list element
+# reverse a list
+# read json from file
+# append string to a file
+# run process in background
+# count words in text counter
+# group elements list
+
+__CHTSH_VERSION=0.0.4
+__CHTSH_DATETIME="2021-04-25 12:30:30 +0200"
+
+# cht.sh configuration loading
+#
+# configuration is stored in ~/.cht.sh/ (can be overridden with CHTSH env var.)
+#
+CHTSH_HOME=${CHTSH:-"$HOME"/.cht.sh}
+[ -z "$CHTSH_CONF" ] && CHTSH_CONF=$CHTSH_HOME/cht.sh.conf
+# shellcheck disable=SC1090,SC2002
+[ -e "$CHTSH_CONF" ] && source "$CHTSH_CONF"
+[ -z "$CHTSH_URL" ] && CHTSH_URL=https://cht.sh
+
+# currently we support only two modes:
+# * lite = access the server using curl
+# * auto = try standalone usage first
+CHTSH_MODE="$(cat "$CHTSH_HOME"/mode 2> /dev/null)"
+[ "$CHTSH_MODE" != lite ] && CHTSH_MODE=auto
+CHEATSH_INSTALLATION="$(cat "$CHTSH_HOME/standalone" 2> /dev/null)"
+
+
+export LESSSECURE=1
+STEALTH_MAX_SELECTION_LENGTH=5
+
+case "$(uname -s)" in
+ Darwin) is_macos=yes ;;
+ *) is_macos=no ;;
+esac
+
+# for KSH93
+# shellcheck disable=SC2034,SC2039,SC2168
+if echo "$KSH_VERSION" | grep -q ' 93' && ! local foo 2>/dev/null; then
+ alias local=typeset
+fi
+
+fatal()
+{
+ echo "ERROR: $*" >&2
+ exit 1
+}
+
+_say_what_i_do()
+{
+ [ -n "$LOG" ] && echo "$(date '+[%Y-%m-%d %H:%M%S]') $*" >> "$LOG"
+
+ local this_prompt="\033[0;1;4;32m>>\033[0m"
+ printf "\n${this_prompt}%s\033[0m\n" " $* "
+}
+
+cheatsh_standalone_install()
+{
+ # the function installs cheat.sh with the upstream repositories
+ # in the standalone mode
+ local installdir; installdir="$1"
+ local default_installdir="$HOME/.cheat.sh"
+
+ [ -z "$installdir" ] && installdir=${default_installdir}
+
+ if [ "$installdir" = help ]; then
+ cat <<EOF
+Install cheat.sh in the standalone mode.
+
+After the installation, cheat.sh can be used locally, without accessing
+the public cheat.sh service, or it can be used in the server mode,
+where the newly installed server could be accessed by external clients
+in the same fashion as the public cheat.sh server.
+
+During the installation, cheat.sh code as well as the cheat.sh upstream
+cheat sheets repositories will be fetched.
+
+It takes approximately 1G of the disk space.
+
+Default installation location: ~/.cheat.sh/
+It can be overridden by a command line parameter to this script:
+
+ ${0##*/} --standalone-install DIR
+
+See cheat.sh/:standalone or https://github.com/chubin/cheat.sh/README.md
+for more information:
+
+ cht.sh :standalone
+ curl cheat.sh/:standalone
+
+After the installation is finished, the cht.sh shell client is switched
+to the auto mode, where it uses the local cheat.sh installation if possible.
+You can switch the mode with the --mode switch:
+
+ cht.sh --mode lite # use https://cheat.sh/ only
+ cht.sh --mode auto # use local installation
+
+For intallation and standalone usage, you need \`git\`, \`python\`,
+and \`virtualenv\` to be installed locally.
+EOF
+ return
+ fi
+
+ local _exit_code=0
+
+ local dependencies=(python git virtualenv)
+ for dep in "${dependencies[@]}"; do
+ command -v "$dep" >/dev/null || \
+ { echo "DEPENDENCY: \"$dep\" is needed to install cheat.sh in the standalone mode" >&2; _exit_code=1; }
+ done
+ [ "$_exit_code" -ne 0 ] && return "$_exit_code"
+
+ while true; do
+ local _installdir
+ echo -n "Where should cheat.sh be installed [$installdir]? "; read -r _installdir
+ [ -n "$_installdir" ] && installdir=$_installdir
+
+ if [ "$installdir" = y ] \
+ || [ "$installdir" = Y ] \
+ || [ "$(echo "$installdir" | tr "[:upper:]" "[:lower:]")" = yes ]
+ then
+ echo Please enter the directory name
+ echo If it was the directory name already, please prepend it with \"./\": "./$installdir"
+ else
+ break
+ fi
+ done
+
+ if [ -e "$installdir" ]; then
+ echo "ERROR: Installation directory [$installdir] exists already"
+ echo "Please remove it first before continuing"
+ return 1
+ fi
+
+ if ! mkdir -p "$installdir"; then
+ echo "ERROR: Could not create the installation directory \"$installdir\""
+ echo "ERROR: Please check the permissions and start the script again"
+ return 1
+ fi
+
+ local space_needed=700
+ local space_available; space_available=$(($(df -k "$installdir" | awk '{print $4}' | tail -1)/1024))
+
+ if [ "$space_available" -lt "$space_needed" ]; then
+ echo "ERROR: Installation directory has no enough space (needed: ${space_needed}M, available: ${space_available}M"
+ echo "ERROR: Please clean up and start the script again"
+ rmdir "$installdir"
+ return 1
+ fi
+
+ _say_what_i_do Cloning cheat.sh locally
+ local url=https://github.com/chubin/cheat.sh
+ rmdir "$installdir"
+ git clone "$url" "$installdir" || fatal Could not clone "$url" with git into "$installdir"
+ cd "$installdir" || fatal "Cannot cd into $installdir"
+
+ # after the repository cloned, we may have the log directory
+ # and we can write our installation log into it
+ mkdir -p "log/"
+ LOG="$PWD/log/install.log"
+
+ # we use tee everywhere so we should set -o pipefail
+ set -o pipefail
+
+ # currently the script uses python 2,
+ # but cheat.sh supports python 3 too
+ # if you want to switch it to python 3
+ # set PYTHON2 to NO:
+ # PYTHON2=NO
+ #
+ PYTHON2=NO
+ if [[ $PYTHON2 = YES ]]; then
+ python="python2"
+ pip="pip"
+ virtualenv_python3_option=()
+ else
+ python="python3"
+ pip="pip3"
+ virtualenv_python3_option=(-p python3)
+ fi
+
+ _say_what_i_do Creating virtual environment
+ virtualenv "${virtualenv_python3_option[@]}" ve \
+ || fatal "Could not create virtual environment with 'virtualenv ve'"
+
+ export CHEATSH_PATH_WORKDIR=$PWD
+
+ # rapidfuzz does not support Python 2,
+ # so if we are using Python 2, install fuzzywuzzy instead
+ if [[ $PYTHON2 = YES ]]; then
+ sed -i s/rapidfuzz/fuzzywuzzy/ requirements.txt
+ echo "python-Levenshtein" >> requirements.txt
+ fi
+
+ _say_what_i_do Installing python requirements into the virtual environment
+ ve/bin/"$pip" install -r requirements.txt > "$LOG" \
+ || {
+
+ echo "ERROR:"
+ echo "---"
+ tail -n 10 "$LOG"
+ echo "---"
+ echo "See $LOG for more"
+ fatal Could not install python dependencies into the virtual environment
+ }
+ echo "$(ve/bin/"$pip" freeze | wc -l) dependencies were successfully installed"
+
+ _say_what_i_do Fetching the upstream cheat sheets repositories
+ ve/bin/python lib/fetch.py fetch-all | tee -a "$LOG"
+
+ _say_what_i_do Running self-tests
+ (
+ cd tests || exit
+
+ if CHEATSH_TEST_STANDALONE=YES \
+ CHEATSH_TEST_SKIP_ONLINE=NO \
+ CHEATSH_TEST_SHOW_DETAILS=NO \
+ PYTHON=../ve/bin/python bash run-tests.sh | tee -a "$LOG"
+ then
+ printf "\033[0;32m%s\033[0m\n" "SUCCESS"
+ else
+ printf "\033[0;31m%s\033[0m\n" "FAILED"
+ echo "Some tests were failed. Run the tests manually for further investigation:"
+ echo " cd $PWD; bash run-tests.sh)"
+ fi
+ )
+
+ mkdir -p "$CHTSH_HOME"
+ echo "$installdir" > "$CHTSH_HOME/standalone"
+ echo auto > "$CHTSH_HOME/mode"
+
+ _say_what_i_do Done
+
+ local v1; v1=$(printf "\033[0;1;32m")
+ local v2; v2=$(printf "\033[0m")
+
+ cat <<EOF | sed "s/{/$v1/; s/}/$v2/"
+
+{ _ }
+{ \\ \\ } The installation is successfully finished.
+{ \\ \\ }
+{ / / } Now you can use cheat.sh in the standalone mode,
+{ /_/ } or you can start your own cheat.sh server.
+
+
+Now the cht.sh shell client is switched to the auto mode, where it uses
+the local cheat.sh installation if possible.
+You can switch the mode with the --mode switch:
+
+ cht.sh --mode lite # use https://cheat.sh/ only
+ cht.sh --mode auto # use local installation
+
+You can add your own cheat sheets repository (config is in \`etc/config.yaml\`),
+or create new cheat sheets adapters (in \`lib/adapters\`).
+
+To update local copies of cheat sheets repositores on a regular basis,
+add the following line to your user crontab (crontab -e):
+
+ 10 * * * * $installdir/ve/bin/python $installdir/lib/fetch.py update-all
+
+All cheat sheets will be automatically actualized each hour.
+
+If you are running a server reachable from the Internet, it can be instantly
+notified via a HTTP request about any cheat sheets changes. For that, please
+open an issue on the cheat.sh project repository [github.com/chubin/cheat.sh]
+with the ENTRY-POINT from the URL https://ENTRY-POINT/:actualize specified
+EOF
+}
+
+chtsh_mode()
+{
+ local mode="$1"
+
+ local text; text=$(
+ echo " auto use the standalone installation first"
+ echo " lite use the cheat sheets server directly"
+ )
+
+ if [ -z "$mode" ]; then
+ echo "current mode: $CHTSH_MODE ($(printf "%s" "$text" | grep "$CHTSH_MODE" | sed "s/$CHTSH_MODE//; s/^ *//; s/ \+/ /"))"
+ if [ -d "$CHEATSH_INSTALLATION" ]; then
+ echo "cheat.sh standalone installation: $CHEATSH_INSTALLATION"
+ else
+ echo 'cheat.sh standalone installation not found; falling back to the "lite" mode'
+ fi
+ elif [ "$mode" = auto ] || [ "$mode" = lite ]; then
+ if [ "$mode" = "$CHTSH_MODE" ]; then
+ echo "The configured mode was \"$CHTSH_MODE\"; nothing changed"
+ else
+ mkdir -p "$CHTSH_HOME"
+ echo "$mode" > "$CHTSH_HOME/mode"
+ echo "Configured mode: $mode"
+ fi
+ else
+ echo "Unknown mode: $mode"
+ echo Supported modes:
+ echo " auto use the standalone installation first"
+ echo " lite use the cheat sheets server directly"
+ fi
+}
+
+get_query_options()
+{
+ local query="$*"
+ if [ -n "$CHTSH_QUERY_OPTIONS" ]; then
+ case $query in
+ *\?*) query="$query&${CHTSH_QUERY_OPTIONS}";;
+ *) query="$query?${CHTSH_QUERY_OPTIONS}";;
+ esac
+ fi
+ printf "%s" "$query"
+}
+
+do_query()
+{
+ local query="$*"
+ local b_opts=
+ local uri="${CHTSH_URL}/\"\$(get_query_options $query)\""
+
+ if [ -e "$CHTSH_HOME/id" ]; then
+ b_opts="-b \"\$CHTSH_HOME/id\""
+ fi
+
+ eval curl "$b_opts" -s "$uri" > "$TMP1"
+
+ if [ -z "$lines" ] || [ "$(wc -l "$TMP1" | awk '{print $1}')" -lt "$lines" ]; then
+ cat "$TMP1"
+ else
+ ${PAGER:-$defpager} "$TMP1"
+ fi
+}
+
+prepare_query()
+{
+ local section="$1"; shift
+ local input="$1"; shift
+ local arguments="$1"
+
+ local query
+ if [ -z "$section" ] || [ x"${input}" != x"${input#/}" ]; then
+ query=$(printf %s "$input" | sed 's@ @/@; s@ @+@g')
+ else
+ query=$(printf %s "$section/$input" | sed 's@ @+@g')
+ fi
+
+ [ -n "$arguments" ] && arguments="?$arguments"
+ printf %s "$query$arguments"
+}
+
+get_list_of_sections()
+{
+ curl -s "${CHTSH_URL}"/:list | grep -v '/.*/' | grep '/$' | xargs
+}
+
+gen_random_str()
+(
+ len=$1
+ if command -v openssl >/dev/null; then
+ openssl rand -base64 $((len*3/4)) | awk -v ORS='' //
+ else
+ rdev=/dev/urandom
+ for d in /dev/{srandom,random,arandom}; do
+ test -r "$d" && rdev=$d
+ done
+ if command -v hexdump >/dev/null; then
+ hexdump -vn $((len/2)) -e '1/1 "%02X" 1 ""' "$rdev"
+ elif command -v xxd >/dev/null; then
+ xxd -l $((len/2)) -ps "$rdev" | awk -v ORS='' //
+ else
+ cd /tmp || { echo Cannot cd into /tmp >&2; exit 1; }
+ s=
+ # shellcheck disable=SC2000
+ while [ "$(echo "$s" | wc -c)" -lt "$len" ]; do
+ s="$s$(mktemp -u XXXXXXXXXX)"
+ done
+ printf "%.${len}s" "$s"
+ fi
+ fi
+)
+
+if [ "$CHTSH_MODE" = auto ] && [ -d "$CHEATSH_INSTALLATION" ]; then
+ curl() {
+ # ignoring all options
+ # currently the standalone.py does not support them anyway
+ local opt
+ while getopts "b:s" opt; do
+ :
+ done
+ shift $((OPTIND - 1))
+
+ local url; url="$1"; shift
+ PYTHONIOENCODING=UTF-8 "$CHEATSH_INSTALLATION/ve/bin/python" "$CHEATSH_INSTALLATION/lib/standalone.py" "${url#"$CHTSH_URL"}" "$@"
+ }
+elif [ "$(uname -s)" = OpenBSD ] && [ -x /usr/bin/ftp ]; then
+ # any better test not involving either OS matching or actual query?
+ curl() {
+ local opt args="-o -"
+ while getopts "b:s" opt; do
+ case $opt in
+ b) args="$args -c $OPTARG";;
+ s) args="$args -M -V";;
+ *) echo "internal error: unsupported cURL option '$opt'" >&2; exit 1;;
+ esac
+ done
+ shift $((OPTIND - 1))
+ /usr/bin/ftp "$args" "$@"
+ }
+else
+ command -v curl >/dev/null || { echo 'DEPENDENCY: install "curl" to use cht.sh' >&2; exit 1; }
+ _CURL=$(command -v curl)
+ if [ x"$CHTSH_CURL_OPTIONS" != x ]; then
+ curl() {
+ $_CURL "${CHTSH_CURL_OPTIONS}" "$@"
+ }
+ fi
+fi
+
+if [ "$1" = --read ]; then
+ read -r a || a="exit"
+ printf "%s\n" "$a"
+ exit 0
+elif [ x"$1" = x--help ] || [ -z "$1" ]; then
+
+ n=${0##*/}
+ s=$(echo "$n" | sed "s/./ /"g)
+
+ cat <<EOF
+Usage:
+
+ $n [OPTIONS|QUERY]
+
+Options:
+
+ QUERY process QUERY and exit
+
+ --help show this help
+ --shell [LANG] shell mode (open LANG if specified)
+
+ --standalone-install [DIR|help]
+ install cheat.sh in the standalone mode
+ (by default, into ~/.cheat.sh/)
+
+ --mode [auto|lite] set (or display) mode of operation
+ * auto - prefer the local installation
+ * lite - use the cheat sheet server
+
+EOF
+ exit 0
+elif [ x"$1" = x--shell ]; then
+ shell_mode=yes
+ shift
+elif [ x"$1" = x--standalone-install ]; then
+ shift
+ cheatsh_standalone_install "$@"
+ exit "$?"
+elif [ x"$1" = x--mode ]; then
+ shift
+ chtsh_mode "$@"
+ exit "$?"
+fi
+
+prompt="cht.sh"
+opts=""
+input=""
+for o; do
+ if [ x"$o" != x"${o#-}" ]; then
+ opts="${opts}${o#-}"
+ else
+ input="$input $o"
+ fi
+done
+query=$(echo "$input" | sed 's@ *$@@; s@^ *@@; s@ @/@; s@ @+@g')
+
+if [ "$shell_mode" != yes ]; then
+ curl -s "${CHTSH_URL}"/"$(get_query_options "$query")"
+ exit 0
+else
+ new_section="$1"
+ valid_sections=$(get_list_of_sections)
+ valid=no; for q in $valid_sections; do [ "$q" = "$new_section/" ] && { valid=yes; break; }; done
+
+ if [ "$valid" = yes ]; then
+ section="$new_section"
+ # shellcheck disable=SC2001
+ this_query="$(echo "$input" | sed 's@ *[^ ]* *@@')"
+ this_prompt="\033[0;32mcht.sh/$section>\033[0m "
+ else
+ this_query="$input"
+ this_prompt="\033[0;32mcht.sh>\033[0m "
+ fi
+ if [ -n "$this_query" ] && [ -z "$CHEATSH_RESTART" ]; then
+ printf "$this_prompt$this_query\n"
+ curl -s "${CHTSH_URL}"/"$(get_query_options "$query")"
+ fi
+fi
+
+if [ "$is_macos" != yes ]; then
+ if [ "$XDG_SESSION_TYPE" = wayland ]; then
+ command -v wl-copy >/dev/null || echo 'DEPENDENCY: please install "wl-copy" for "copy"' >&2
+ else
+ command -v xsel >/dev/null || echo 'DEPENDENCY: please install "xsel" for "copy"' >&2
+ fi
+fi
+command -v rlwrap >/dev/null || { echo 'DEPENDENCY: install "rlwrap" to use cht.sh in the shell mode' >&2; exit 1; }
+
+mkdir -p "$CHTSH_HOME/"
+lines=$(tput lines)
+
+if command -v less >/dev/null; then
+ defpager="less -R"
+elif command -v more >/dev/null; then
+ defpager="more"
+else
+ defpager="cat"
+fi
+
+cmd_cd() {
+ if [ $# -eq 0 ]; then
+ section=""
+ else
+ new_section=$(echo "$input" | sed 's/cd *//; s@/*$@@; s@^/*@@')
+ if [ -z "$new_section" ] || [ ".." = "$new_section" ]; then
+ section=""
+ else
+ valid_sections=$(get_list_of_sections)
+ valid=no; for q in $valid_sections; do [ "$q" = "$new_section/" ] && { valid=yes; break; }; done
+ if [ "$valid" = no ]; then
+ echo "Invalid section: $new_section"
+ echo "Valid sections:"
+ echo "$valid_sections" \
+ | xargs printf "%-10s\n" \
+ | tr ' ' . \
+ | xargs -n 10 \
+ | sed 's/\./ /g; s/^/ /'
+ else
+ section="$new_section"
+ fi
+ fi
+ fi
+}
+
+cmd_copy() {
+ if [ -z "$DISPLAY" ]; then
+ echo copy: supported only in the Desktop version
+ elif [ -z "$input" ]; then
+ echo copy: Make at least one query first.
+ else
+ curl -s "${CHTSH_URL}"/"$(get_query_options "$query"?T)" > "$TMP1"
+ if [ "$is_macos" != yes ]; then
+ if [ "$XDG_SESSION_TYPE" = wayland ]; then
+ wl-copy < "$TMP1"
+ else
+ xsel -bi < "$TMP1"
+ fi
+ else
+ pbcopy < "$TMP1"
+ fi
+ echo "copy: $(wc -l "$TMP1" | awk '{print $1}') lines copied to the selection"
+ fi
+}
+
+cmd_ccopy() {
+ if [ -z "$DISPLAY" ]; then
+ echo copy: supported only in the Desktop version
+ elif [ -z "$input" ]; then
+ echo copy: Make at least one query first.
+ else
+ curl -s "${CHTSH_URL}"/"$(get_query_options "$query"?TQ)" > "$TMP1"
+ if [ "$is_macos" != yes ]; then
+ if [ "$XDG_SESSION_TYPE" = wayland ]; then
+ wl-copy < "$TMP1"
+ else
+ xsel -bi < "$TMP1"
+ fi
+ else
+ pbcopy < "$TMP1"
+ fi
+ echo "copy: $(wc -l "$TMP1" | awk '{print $1}') lines copied to the selection"
+ fi
+}
+
+cmd_exit() {
+ exit 0
+}
+
+cmd_help() {
+ cat <<EOF
+help - show this help
+hush - do not show the 'help' string at start anymore
+cd LANG - change the language context
+copy - copy the last answer in the clipboard (aliases: yank, y, c)
+ccopy - copy the last answer w/o comments (cut comments; aliases: cc, Y, C)
+exit - exit the cheat shell (aliases: quit, ^D)
+id [ID] - set/show an unique session id ("reset" to reset, "remove" to remove)
+stealth - stealth mode (automatic queries for selected text)
+update - self update (only if the scriptfile is writeable)
+version - show current cht.sh version
+/:help - service help
+QUERY - space separated query staring (examples are below)
+ cht.sh> python zip list
+ cht.sh/python> zip list
+ cht.sh/go> /python zip list
+EOF
+}
+
+cmd_hush() {
+ mkdir -p "$CHTSH_HOME/" && touch "$CHTSH_HOME/.hushlogin" && echo "Initial 'use help' message was disabled"
+}
+
+cmd_id() {
+ id_file="$CHTSH_HOME/id"
+
+ if [ id = "$input" ]; then
+ new_id=""
+ else
+ new_id=$(echo "$input" | sed 's/id *//; s/ *$//; s/ /+/g')
+ fi
+ if [ "$new_id" = remove ]; then
+ if [ -e "$id_file" ]; then
+ rm -f -- "$id_file" && echo "id is removed"
+ else
+ echo "id was not set, so you can't remove it"
+ fi
+ return
+ fi
+ if [ -n "$new_id" ] && [ reset != "$new_id" ] && [ "$(/bin/echo -n "$new_id" | wc -c)" -lt 16 ]; then
+ echo "ERROR: $new_id: Too short id. Minimal id length is 16. Use 'id reset' for a random id"
+ return
+ fi
+ if [ -z "$new_id" ]; then
+ # if new_id is not specified check if we have some id already
+ # if yes, just show it
+ # if not, generate a new id
+ if [ -e "$id_file" ]; then
+ awk '$6 == "id" {print $NF}' <"$id_file" | tail -n 1
+ return
+ else
+ new_id=reset
+ fi
+ fi
+ if [ "$new_id" = reset ]; then
+ new_id=$(gen_random_str 12)
+ else
+ echo WARNING: if someone gueses your id, he can read your cht.sh search history
+ fi
+ if [ -e "$id_file" ] && grep -q '\tid\t[^\t][^\t]*$' "$id_file" 2> /dev/null; then
+ sed -i 's/\tid\t[^\t][^\t]*$/ id '"$new_id"'/' "$id_file"
+ else
+ if ! [ -e "$id_file" ]; then
+ printf '#\n\n' > "$id_file"
+ fi
+ printf ".cht.sh\tTRUE\t/\tTRUE\t0\tid\t$new_id\n" >> "$id_file"
+ fi
+ echo "$new_id"
+}
+
+cmd_query() {
+ query=$(prepare_query "$section" "$input")
+ do_query "$query"
+}
+
+cmd_stealth() {
+ if [ "$input" != stealth ]; then
+ arguments=$(echo "$input" | sed 's/stealth //; s/ /\&/')
+ fi
+ trap break INT
+ if [ "$is_macos" = yes ]; then
+ past=$(pbpaste)
+ else
+ if [ "$XDG_SESSION_TYPE" = wayland ]; then
+ past=$(wl-paste -p)
+ else
+ past=$(xsel -o)
+ fi
+ fi
+ printf "\033[0;31mstealth:\033[0m you are in the stealth mode; select any text in any window for a query\n"
+ printf "\033[0;31mstealth:\033[0m selections longer than $STEALTH_MAX_SELECTION_LENGTH words are ignored\n"
+ if [ -n "$arguments" ]; then
+ printf "\033[0;31mstealth:\033[0m query arguments: ?$arguments\n"
+ fi
+ printf "\033[0;31mstealth:\033[0m use ^C to leave this mode\n"
+ while true; do
+ if [ "$is_macos" = yes ]; then
+ current=$(pbpaste)
+ else
+ if [ "$XDG_SESSION_TYPE" = wayland ]; then
+ current=$(wl-paste -p)
+ else
+ current=$(xsel -o)
+ fi
+ fi
+ if [ "$past" != "$current" ]; then
+ past=$current
+ current_text="$(echo $current | tr -c '[a-zA-Z0-9]' ' ')"
+ if [ "$(echo "$current_text" | wc -w)" -gt "$STEALTH_MAX_SELECTION_LENGTH" ]; then
+ printf "\033[0;31mstealth:\033[0m selection length is longer than $STEALTH_MAX_SELECTION_LENGTH words; ignoring\n"
+ continue
+ else
+ printf "\n\033[0;31mstealth: \033[7m $current_text\033[0m\n"
+ query=$(prepare_query "$section" "$current_text" "$arguments")
+ do_query "$query"
+ fi
+ fi
+ sleep 1;
+ done
+ trap - INT
+}
+
+cmd_update() {
+ [ -w "$0" ] || { echo "The script is readonly; please update manually: curl -s ${CHTSH_URL}/:cht.sh | sudo tee $0"; return; }
+ TMP2=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX)
+ curl -s "${CHTSH_URL}"/:cht.sh > "$TMP2"
+ if ! cmp "$0" "$TMP2" > /dev/null 2>&1; then
+ if grep -q ^__CHTSH_VERSION= "$TMP2"; then
+ # section was vaildated by us already
+ args=(--shell "$section")
+ cp "$TMP2" "$0" && echo "Updated. Restarting..." && rm "$TMP2" && CHEATSH_RESTART=1 exec "$0" "${args[@]}"
+ else
+ echo "Something went wrong. Please update manually"
+ fi
+ else
+ echo "cht.sh is up to date. No update needed"
+ fi
+ rm -f "$TMP2" > /dev/null 2>&1
+}
+
+cmd_version() {
+ insttime=$(ls -l -- "$0" | sed 's/ */ /g' | cut -d ' ' -f 6-8)
+ echo "cht.sh version $__CHTSH_VERSION of $__CHTSH_DATETIME; installed at: $insttime"
+ TMP2=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX)
+ if curl -s "${CHTSH_URL}"/:cht.sh > "$TMP2"; then
+ if ! cmp "$0" "$TMP2" > /dev/null 2>&1; then
+ echo "Update needed (type 'update' for that)".
+ else
+ echo "Up to date. No update needed"
+ fi
+ fi
+ rm -f "$TMP2" > /dev/null 2>&1
+}
+
+TMP1=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX)
+trap 'rm -f $TMP1 $TMP2' EXIT
+trap 'true' INT
+
+if ! [ -e "$CHTSH_HOME/.hushlogin" ] && [ -z "$this_query" ]; then
+ echo "type 'help' for the cht.sh shell help"
+fi
+
+while true; do
+ if [ "$section" != "" ]; then
+ full_prompt="$prompt/$section> "
+ else
+ full_prompt="$prompt> "
+ fi
+
+ input=$(
+ rlwrap -H "$CHTSH_HOME/history" -pgreen -C cht.sh -S "$full_prompt" bash "$0" --read | sed 's/ *#.*//'
+ )
+
+ cmd_name=${input%% *}
+ cmd_args=${input#* }
+ case $cmd_name in
+ "") continue;; # skip empty input lines
+ '?'|h|help) cmd_name=help;;
+ hush) cmd_name=hush;;
+ cd) cmd_name="cd";;
+ exit|quit) cmd_name="exit";;
+ copy|yank|c|y) cmd_name=copy;;
+ ccopy|cc|C|Y) cmd_name=ccopy;;
+ id) cmd_name=id;;
+ stealth) cmd_name=stealth;;
+ update) cmd_name=update;;
+ version) cmd_name=version;;
+ *) cmd_name="query"; cmd_args="$input";;
+ esac
+ "cmd_$cmd_name" $cmd_args
+done
diff --git a/claude b/claude
new file mode 120000
index 0000000..7571232
--- /dev/null
+++ b/claude
@@ -0,0 +1 @@
+/home/lukasz/.local/share/claude/versions/2.1.138 \ No newline at end of file
diff --git a/clip-capture b/clip-capture
index 8bedcda..5184430 100755
--- a/clip-capture
+++ b/clip-capture
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
-OUTFILE="${OUTFILE:-$HOME/.clips}"
+OUTFILE="${OUTFILE:-$HOME/docs/vimwiki/inbox/clips.md}"
HAS_NOTIFY=$(command -v notify-send >/dev/null 2>&1 && echo 1 || echo 0)
notify() {
@@ -17,13 +17,13 @@ die() {
get_clipboard() {
local out
if command -v xclip >/dev/null 2>&1; then
- out="$(xclip -o -selection clipboard 2>/dev/null)"
- [[ -z "$out" ]] && out="$(xclip -o -selection primary 2>/dev/null)"
+ out="$(xclip -o -selection primary 2>/dev/null)"
+ [[ -z "$out" ]] && out="$(xclip -o -selection clipboard 2>/dev/null)"
printf '%s' "$out"; return
fi
if command -v xsel >/dev/null 2>&1; then
- out="$(xsel --clipboard --output 2>/dev/null)"
- [[ -z "$out" ]] && out="$(xsel --primary --output 2>/dev/null)"
+ out="$(xsel --primary --output 2>/dev/null)"
+ [[ -z "$out" ]] && out="$(xsel --clipboard --output 2>/dev/null)"
printf '%s' "$out"; return
fi
die "need xclip or xsel"
@@ -34,12 +34,12 @@ clip="$(get_clipboard)"
ts="$(date '+%Y-%m-%d %H:%M:%S')"
{
- printf '# captured %s\n' "$ts"
+ printf '## captured %s\n' "$ts"
printf '%s\n' "$clip"
- printf -- '---\n'
+ printf -- '\n'
} >> "$OUTFILE"
lines="$(printf '%s\n' "$clip" | wc -l | awk '{print $1}')"
chars="${#clip}"
-notify normal "capture-clip" "Saved to ~/.clips ($lines lines, $chars chars)"
+notify normal "capture-clip" "Saved to vimwiki/inbox/clips.md ($lines lines, $chars chars)"
diff --git a/clipwipe b/clipwipe
new file mode 100755
index 0000000..88cb88c
--- /dev/null
+++ b/clipwipe
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+# Wipe X selections and clipmenud history in one shot.
+set -eu
+
+xsel -bc 2>/dev/null || true
+xsel -pc 2>/dev/null || true
+
+# Full reset beats clipdel due to upstream tmpdir bug (#133)
+if pgrep -x clipmenud >/dev/null; then
+ pkill clipmenud
+ sleep 0.2
+fi
+rm -rf "/run/user/$(id -u)/clipmenu."*
+setsid clipmenud >/dev/null 2>&1 &
+disown 2>/dev/null || true
+
+command -v notify-send >/dev/null 2>&1 && notify-send -u low "clipwipe" "Clipboard and history cleared"
diff --git a/dm-frecent b/dm-frecent
new file mode 100755
index 0000000..0b12a92
--- /dev/null
+++ b/dm-frecent
@@ -0,0 +1,27 @@
+#!/usr/bin/env sh
+# Pick a frecent file via fre, open with the right app for its type.
+set -eu
+
+file=$(
+ fre --sorted \
+ | while IFS= read -r f; do [ -f "$f" ] && printf '%s\n' "$f"; done \
+ | dmenu -l 20 -i -p 'frecent:'
+) || exit 0
+[ -n "$file" ] || exit 0
+
+fre --add "$file" # bump score on reopen
+
+case "$file" in
+ *.pdf|*.epub)
+ exec zathura "$file" ;;
+ *.odt|*.ods|*.odp|*.doc|*.docx|*.xls|*.xlsx|*.ppt|*.pptx)
+ exec libreoffice "$file" ;;
+ *.png|*.jpg|*.jpeg|*.gif|*.webp|*.bmp|*.tiff)
+ exec nsxiv "$file" ;;
+ *.mp4|*.mkv|*.webm|*.avi|*.mov|*.mp3|*.flac|*.ogg|*.opus|*.wav|*.m4a)
+ exec mpv "$file" ;;
+ *.html|*.htm)
+ exec firefox "$file" ;;
+ *)
+ exec st -e vim "$file" ;;
+esac
diff --git a/dm-memo b/dm-memo
index b8364c5..de2fc67 100755
--- a/dm-memo
+++ b/dm-memo
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
-OUTFILE="${OUTFILE:-$HOME/.memo}"
+OUTFILE="${OUTFILE:-$HOME/docs/vimwiki/inbox/memo.md}"
DMENU="${DMENU:-dmenu}"
PROMPT="${PROMPT:-Memo: }"
@@ -29,9 +29,9 @@ fi
ts="$(date '+%Y-%m-%d %H:%M')"
{
- printf '# %s\n' "$ts"
+ printf '## %s\n' "$ts"
printf '%s\n' "$note"
- printf -- '---\n'
+ printf -- '\n'
} >> "$OUTFILE" || die "Failed to write to $OUTFILE"
-notify normal "Saved to ~/.memo"
+notify normal "Saved to vimwiki/inbox/memo"
diff --git a/dm-vimwiki-open b/dm-vimwiki-open
index e8c4891..a53f4b9 100755
--- a/dm-vimwiki-open
+++ b/dm-vimwiki-open
@@ -11,7 +11,7 @@ EDITOR_CMD="${EDITOR_CMD:-vim}"
TERM_CMD="${TERM_CMD:-st -e}"
# Set this to your VimWiki root directory
-VIMWIKI_ROOT="${VIMWIKI_ROOT:-${HOME}/vimwiki}" # <-- CHANGE if needed
+VIMWIKI_ROOT="${VIMWIKI_ROOT:-${HOME}/docs/vimwiki}" # <-- CHANGE if needed
CACHE_DIR="${XDG_CACHE_HOME:-${HOME}/.cache}/dmenu"
CACHE_FILE="${CACHE_DIR}/vimwiki_files.txt"
diff --git a/dwm-status b/dwm-status
index 7674482..e95dad3 100755
--- a/dwm-status
+++ b/dwm-status
@@ -1,5 +1,4 @@
#!/bin/sh
-
while true; do
datetime="$(date '+%d %a %H:%M')"
@@ -19,13 +18,19 @@ while true; do
net="offline"
fi
- # VPN (tun0 / wg0 / proton0 – adjust to match your system)
- if ip link show tun0 >/dev/null 2>&1 || ip link show wg0 >/dev/null 2>&1 || ip link show proton0 >/dev/null 2>&1; then
- vpn="VPN on"
+ # CPU usage
+ cpu="$(top -bn1 | awk '/^%Cpu/{print int($2+$4) "%"}')"
+
+ # Memory
+ mem="$(free -m | awk '/^Mem/{print $3 "M"}')"
+
+ # VPN status
+ if ip link show wg-vps >/dev/null 2>&1; then
+ vpn="VPS on"
else
- vpn="VPN off"
+ vpn="VPS off"
fi
- xsetroot -name "VOL $vol | NET $net | $vpn | $datetime "
+ xsetroot -name "$datetime | CPU $cpu | MEM $mem | VOL $vol | NET $net | $vpn "
sleep 5
done
diff --git a/feh b/feh
new file mode 100755
index 0000000..3fd43fc
--- /dev/null
+++ b/feh
@@ -0,0 +1,6 @@
+#!/bin/sh
+# Wrapper: log file opens to fre, then exec real feh.
+for arg in "$@"; do
+ [ -f "$arg" ] && fre --add "$(readlink -f "$arg")" 2>/dev/null
+done
+exec /usr/bin/feh "$@"
diff --git a/fkill b/fkill
new file mode 100755
index 0000000..83ee9b5
--- /dev/null
+++ b/fkill
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+# Pick processes via fzf, kill them. Default SIGTERM, override with first arg.
+# Usage: fkill # SIGTERM
+# fkill KILL # SIGKILL
+# fkill 9 # SIGKILL (numeric)
+set -eu
+
+sig="${1:-TERM}"
+
+pids=$(
+ { printf '%-7s %-10s %5s %5s %10s %s\n' PID USER %CPU %MEM ELAPSED COMMAND
+ ps -eo pid,user,pcpu,pmem,etime,args --sort=-pcpu --no-headers \
+ | awk -v me=$$ -v ppid=$PPID '$1 != me && $1 != ppid'
+ } \
+ | fzf --multi --header-lines=1 \
+ --header='TAB multi-select, Enter to kill' \
+ --preview='echo {}' --preview-window=down:3:wrap \
+ | awk '{print $1}'
+)
+
+[[ -z "$pids" ]] && exit 0
+echo "$pids" | xargs -r kill -"$sig"
diff --git a/fre-prune.sh b/fre-prune.sh
new file mode 100755
index 0000000..e1a8c37
--- /dev/null
+++ b/fre-prune.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+# Prune dead paths from fre and zoxide.
+fre --sorted | while IFS= read -r f; do
+ [ -f "$f" ] || fre --delete "$f"
+done
+zoxide query --list | while IFS= read -r d; do
+ [ -d "$d" ] || zoxide remove "$d"
+done
diff --git a/hledger-category-select b/hledger-category-select
new file mode 100755
index 0000000..7b34ff4
--- /dev/null
+++ b/hledger-category-select
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+FILE="${FILE:-$HOME/docs/priv/finance/hledger_main/accounts.journal}"
+DMENU="${DMENU:-dmenu -i -l 20 -p 'Account:'}"
+CLIP="${CLIP:-xclip -selection clipboard}"
+
+if [[ ! -r "$FILE" ]]; then
+ printf 'ERROR: cannot read %s\n' "$FILE" >&2
+ exit 1
+fi
+
+# Extract account names:
+# - only lines starting with: account ...
+# - strip the leading "account " part
+# - sort uniquely
+choices="$(
+ awk '
+ /^[[:space:]]*account[[:space:]]+/ {
+ sub(/^[[:space:]]*account[[:space:]]+/, "", $0)
+ print
+ }
+ ' "$FILE" | sort -u
+)"
+
+[[ -z "$choices" ]] && {
+ printf 'ERROR: no account entries found in %s\n' "$FILE" >&2
+ exit 1
+}
+
+picked="$(printf '%s\n' "$choices" | eval "$DMENU" || true)"
+[[ -z "$picked" ]] && exit 0
+
+printf '%s' "$picked" | eval "$CLIP"
+printf 'Copied: %s\n' "$picked" >&2
diff --git a/libreoffice b/libreoffice
new file mode 100755
index 0000000..054af3f
--- /dev/null
+++ b/libreoffice
@@ -0,0 +1,6 @@
+#!/bin/sh
+# Wrapper: log file opens to fre, then exec real libreoffice.
+for arg in "$@"; do
+ [ -f "$arg" ] && fre --add "$(readlink -f "$arg")" 2>/dev/null
+done
+exec /usr/bin/libreoffice "$@"
diff --git a/nsxiv b/nsxiv
new file mode 100755
index 0000000..f9654ed
--- /dev/null
+++ b/nsxiv
@@ -0,0 +1,6 @@
+#!/bin/sh
+# Wrapper: log file opens to fre, then exec real nsxiv.
+for arg in "$@"; do
+ [ -f "$arg" ] && fre --add "$(readlink -f "$arg")" 2>/dev/null
+done
+exec /usr/bin/nsxiv "$@"
diff --git a/ocr-scan b/ocr-scan
new file mode 100755
index 0000000..36994b5
--- /dev/null
+++ b/ocr-scan
@@ -0,0 +1,146 @@
+#!/usr/bin/env bash
+# ocr-scan.sh — recursively OCR un-OCR'd PDFs
+# Deps: ocrmypdf, poppler-utils (pdftotext), tesseract-ocr
+# Optional: python3-langdetect (for -a auto-detect)
+
+set -euo pipefail
+
+LANG_CODE="eng"
+AUTO_DETECT=false
+MIN_CHARS=50 # chars per page threshold — below = needs OCR
+DIR="."
+
+usage() {
+ cat <<EOF
+Usage: $(basename "$0") [OPTIONS] [DIRECTORY]
+
+Recursively find un-OCR'd PDFs and produce OCR'd copies with _OCR suffix.
+
+OPTIONS:
+ -l LANG Tesseract language code(s) (default: eng)
+ Single: -l pol
+ Multiple: -l pol+eng
+ Examples: eng pol tha deu fra rus chi_sim jpn ara
+ -a Auto-detect language via langdetect (requires: pip install langdetect)
+ Note: falls back to eng for fully un-OCR'd scans (no text layer)
+ -h Show this help and exit
+
+ARGUMENTS:
+ DIRECTORY Root directory to search recursively (default: current directory)
+
+OUTPUT:
+ Each processed file is saved alongside the original:
+ original.pdf → original_OCR.pdf
+ Files already having an _OCR copy or sufficient text are skipped.
+
+DEPENDENCIES:
+ Required: ocrmypdf, poppler-utils (pdftotext, pdfinfo), tesseract-ocr
+ Per-lang: apt install tesseract-ocr-pol tesseract-ocr-tha ...
+ Optional: pip install langdetect (for -a flag)
+
+EXAMPLES:
+ $(basename "$0") # current dir, English
+ $(basename "$0") ~/docs # ~/docs recursively, English
+ $(basename "$0") -l pol ~/docs # Polish
+ $(basename "$0") -l pol+eng ~/docs # Polish + English mixed
+ $(basename "$0") -a ~/docs # auto-detect language
+EOF
+ exit 0
+}
+
+while getopts ":l:ah" opt; do
+ case $opt in
+ l) LANG_CODE="$OPTARG" ;;
+ a) AUTO_DETECT=true ;;
+ h) usage ;;
+ :) echo "Option -$OPTARG requires an argument."; usage ;;
+ \?) echo "Unknown option: -$OPTARG"; usage ;;
+ esac
+done
+shift $((OPTIND - 1))
+[[ $# -gt 0 ]] && DIR="$1"
+
+# Map ISO 639-1 → Tesseract lang codes
+map_lang() {
+ case "$1" in
+ pl|pol) echo "pol" ;;
+ en|eng) echo "eng" ;;
+ th|tha) echo "tha" ;;
+ de|deu) echo "deu" ;;
+ fr|fra) echo "fra" ;;
+ ru|rus) echo "rus" ;;
+ zh|chi) echo "chi_sim" ;;
+ ja|jpn) echo "jpn" ;;
+ ar|ara) echo "ara" ;;
+ *) echo "eng" ;;
+ esac
+}
+
+detect_lang() {
+ local pdf="$1"
+ local sample
+ sample=$(pdftotext "$pdf" - 2>/dev/null | head -c 500)
+ if [[ -z "$sample" ]]; then
+ echo "eng"; return
+ fi
+ python3 - <<EOF 2>/dev/null || echo "eng"
+from langdetect import detect
+try:
+ print("$(echo "$sample" | tr -d "'" | head -c 300)")
+except:
+ print("eng")
+EOF
+ # simpler: call python directly
+ python3 -c "
+from langdetect import detect
+import sys
+try:
+ lang = detect('''$sample''')
+ print(lang)
+except:
+ print('eng')
+" 2>/dev/null || echo "eng"
+}
+
+needs_ocr() {
+ local pdf="$1"
+ local pages
+ pages=$(pdfinfo "$pdf" 2>/dev/null | awk '/^Pages:/{print $2}')
+ [[ -z "$pages" || "$pages" -eq 0 ]] && pages=1
+ local chars
+ chars=$(pdftotext "$pdf" - 2>/dev/null | wc -c)
+ local threshold=$(( pages * MIN_CHARS ))
+ [[ "$chars" -lt "$threshold" ]]
+}
+
+find "$DIR" -type f -iname "*.pdf" ! -iname "*_OCR.pdf" | while read -r pdf; do
+ out="${pdf%.pdf}_OCR.pdf"
+
+ if [[ -f "$out" ]]; then
+ echo "[SKIP] Already has OCR copy: $out"
+ continue
+ fi
+
+ if ! needs_ocr "$pdf"; then
+ echo "[SKIP] Already has text: $pdf"
+ continue
+ fi
+
+ lang="$LANG_CODE"
+ if $AUTO_DETECT; then
+ detected=$(detect_lang "$pdf")
+ lang=$(map_lang "$detected")
+ echo "[DETECT] $pdf → $detected → $lang"
+ fi
+
+ echo "[OCR] $pdf (lang: $lang) → $out"
+ ocrmypdf \
+ --language "$lang" \
+ --output-type pdfa \
+ --skip-text \
+ --rotate-pages \
+ --deskew \
+ "$pdf" "$out" \
+ && echo "[DONE] $out" \
+ || echo "[FAIL] $pdf"
+done
diff --git a/passmenu b/passmenu
index 76d92ab..d96599c 100755
--- a/passmenu
+++ b/passmenu
@@ -1,13 +1,10 @@
#!/usr/bin/env bash
-
shopt -s nullglob globstar
-
typeit=0
if [[ $1 == "--type" ]]; then
typeit=1
shift
fi
-
if [[ -n $WAYLAND_DISPLAY ]]; then
dmenu=dmenu-wl
xdotool="ydotool type --file -"
@@ -18,18 +15,20 @@ 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" "$@")
-
[[ -n $password ]] || exit
-
if [[ $typeit -eq 0 ]]; then
- pass show -c "$password" 2>/dev/null
+ if command -v clipctl >/dev/null 2>&1; then
+ clipctl disable >/dev/null
+ pass show -c "$password" 2>/dev/null
+ ( sleep $((${PASSWORD_STORE_CLIP_TIME:-45} + 1)) && clipctl enable >/dev/null ) &
+ else
+ pass show -c "$password" 2>/dev/null
+ fi
else
pass show "$password" | { IFS= read -r pass; printf %s "$pass"; } | $xdotool
fi
diff --git a/pdf-fzf b/pdf-fzf
new file mode 100755
index 0000000..c2570c7
--- /dev/null
+++ b/pdf-fzf
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+# pdf-fzf — interactive PDF text search via fzf + bat
+# Deps: pdftotext (poppler-utils), fzf, bat
+
+set -euo pipefail
+
+DIR="${1:-.}"
+CACHE=$(mktemp -d /tmp/pdf-fzf.XXXXXX)
+trap 'rm -rf "$CACHE"' EXIT
+
+MAPFILE="$CACHE/map.tsv"
+INDEX="$CACHE/index.tsv"
+
+echo "Indexing PDFs in '$DIR'..." >&2
+
+find "$DIR" -type f -iname "*.pdf" | sort | while read -r pdf; do
+ hash=$(printf '%s' "$pdf" | md5sum | cut -c1-8)
+ txtfile="$CACHE/${hash}.txt"
+ pdftotext "$pdf" - 2>/dev/null > "$txtfile" || continue
+ [[ ! -s "$txtfile" ]] && continue
+ printf '%s\t%s\t%s\n' "$hash" "$pdf" "$txtfile" >> "$MAPFILE"
+ awk -v src="$pdf" -F'\t' \
+ 'NF { printf "%s\t%d\t%s\n", src, NR, $0 }' "$txtfile"
+done > "$INDEX"
+
+PDF_COUNT=$(wc -l < "${MAPFILE:-/dev/null}" 2>/dev/null || echo 0)
+LINE_COUNT=$(wc -l < "$INDEX")
+echo "Indexed $LINE_COUNT lines from $PDF_COUNT PDFs." >&2
+
+# Preview script — receives the raw fzf line, shows bat at matched line
+PREVIEW_SH="$CACHE/preview.sh"
+cat > "$PREVIEW_SH" <<PREVIEW
+#!/usr/bin/env bash
+IFS=\$'\\t' read -r pdf linenum _ <<< "\$1"
+txtfile=\$(awk -F'\\t' -v p="\$pdf" '\$2==p{print \$3; exit}' "$MAPFILE")
+[[ -f "\$txtfile" ]] || { echo "No preview for: \$pdf"; exit 1; }
+echo -e "\\033[1;34m\$pdf\\033[0m"
+batcat --style=numbers,grid \\
+ --highlight-line="\$linenum" \\
+ --language=txt \\
+ --pager=never \\
+ "\$txtfile"
+PREVIEW
+chmod +x "$PREVIEW_SH"
+
+fzf \
+ --delimiter=$'\t' \
+ --with-nth='1,3' \
+ --preview="$PREVIEW_SH {}" \
+ --preview-window='right:65%:wrap' \
+ --prompt='PDF › ' \
+ --header="$LINE_COUNT lines · $PDF_COUNT PDFs · $DIR [Enter: open PDF]" \
+ --color='header:italic,prompt:bold' \
+ --bind "enter:execute(xdg-open {1} 2>/dev/null &)" \
+ < "$INDEX"
diff --git a/pdf-txt b/pdf-txt
new file mode 100755
index 0000000..92da697
--- /dev/null
+++ b/pdf-txt
@@ -0,0 +1,80 @@
+#!/usr/bin/env bash
+# pdf-txt — extract clean text from OCR'd PDFs, recursively
+#
+# Usage: pdf-txt <path> [<path>...]
+#
+# <path> can be a .pdf file or a directory (walked recursively).
+# For each PDF with a text layer, writes a sibling .txt file using
+# pdftotext -layout, with "=== Page N ===" markers inserted.
+# Skips PDFs without a text layer.
+# Skips PDFs whose .txt is newer than the source.
+
+set -eu
+
+die() { printf "ERROR: %s\n" "$*" >&2; exit 1; }
+need() { command -v "$1" >/dev/null 2>&1 || die "Missing: $1 (apt install $2)"; }
+
+need pdftotext poppler-utils
+need find findutils
+need awk gawk
+
+processed=0; skipped_no_ocr=0; skipped_existing=0; failed=0
+
+extract_one() {
+ local pdf="$1"
+ local txt="${pdf%.[Pp][Dd][Ff]}.txt"
+
+ if [ -f "$txt" ] && [ "$txt" -nt "$pdf" ]; then
+ printf " up-to-date %s\n" "$pdf"
+ skipped_existing=$((skipped_existing + 1))
+ return 0
+ fi
+
+ # OCR/text-layer detection: sample first 3 pages
+ local tmp; tmp=$(mktemp)
+ pdftotext -l 3 "$pdf" "$tmp" 2>/dev/null || true
+ local nchars; nchars=$(tr -d '[:space:]' < "$tmp" | wc -c)
+ rm -f "$tmp"
+
+ if [ "$nchars" -lt 50 ]; then
+ printf " no OCR %s\n" "$pdf"
+ skipped_no_ocr=$((skipped_no_ocr + 1))
+ return 0
+ fi
+
+ if pdftotext -layout "$pdf" - 2>/dev/null | awk '
+ BEGIN { page = 1; printf "=== Page %d ===\n\n", page }
+ /\014/ { page++; printf "\n=== Page %d ===\n\n", page; next }
+ { print }
+ ' > "$txt"; then
+ printf " extracted %s\n" "$txt"
+ processed=$((processed + 1))
+ else
+ rm -f "$txt"
+ printf " FAILED %s\n" "$pdf" >&2
+ failed=$((failed + 1))
+ fi
+}
+
+walk() {
+ local target="$1"
+ if [ -f "$target" ]; then
+ case "$target" in
+ *.pdf|*.PDF) extract_one "$target" ;;
+ *) printf " not a PDF %s\n" "$target" >&2 ;;
+ esac
+ elif [ -d "$target" ]; then
+ while IFS= read -r -d '' pdf; do
+ extract_one "$pdf"
+ done < <(find "$target" -type f -iname '*.pdf' -print0 | sort -z)
+ else
+ printf " not found %s\n" "$target" >&2
+ fi
+}
+
+[ $# -ge 1 ] || die "Usage: pdf-txt <path> [<path>...]"
+
+for arg in "$@"; do walk "$arg"; done
+
+printf "\nDone. processed=%d no_ocr=%d up_to_date=%d failed=%d\n" \
+ "$processed" "$skipped_no_ocr" "$skipped_existing" "$failed"
diff --git a/proton-bridge-cert-refresh b/proton-bridge-cert-refresh
deleted file mode 100755
index dc3be73..0000000
--- a/proton-bridge-cert-refresh
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-CERT_DIR="$HOME/.config/isync"
-CERT_FILE="$CERT_DIR/proton-bridge.pem"
-
-mkdir -p "$CERT_DIR"
-chmod 700 "$CERT_DIR"
-
-# Ensure Bridge port is up (wait up to 30s)
-for i in $(seq 1 30); do
- if ss -ltn '( sport = :1143 )' | grep -q 1143; then
- break
- fi
- sleep 1
-done
-
-if ! ss -ltn '( sport = :1143 )' | grep -q 1143; then
- echo "Bridge not listening on 127.0.0.1:1143" >&2
- exit 1
-fi
-
-tmp="$(mktemp)"
-
-openssl s_client -starttls imap -connect 127.0.0.1:1143 -showcerts </dev/null 2>/dev/null \
-| awk '
- /BEGIN CERTIFICATE/ {c++}
- c==1 {print}
- /END CERTIFICATE/ && c==1 {exit}
-' > "$tmp"
-
-# sanity check
-openssl x509 -in "$tmp" -noout >/dev/null 2>&1
-
-mv "$tmp" "$CERT_FILE"
-chmod 600 "$CERT_FILE"
-
-echo "Updated: $CERT_FILE"
-
diff --git a/pudb b/pudb
new file mode 120000
index 0000000..36f4ad9
--- /dev/null
+++ b/pudb
@@ -0,0 +1 @@
+/home/lukasz/.local/share/pipx/venvs/pudb/bin/pudb \ No newline at end of file
diff --git a/pylsp b/pylsp
new file mode 120000
index 0000000..0d3f8bf
--- /dev/null
+++ b/pylsp
@@ -0,0 +1 @@
+/home/lukasz/.local/share/pipx/venvs/python-lsp-server/bin/pylsp \ No newline at end of file
diff --git a/rofi-cheat b/rofi-cheat
new file mode 100755
index 0000000..a0440b6
--- /dev/null
+++ b/rofi-cheat
@@ -0,0 +1,28 @@
+#!/bin/sh
+set -eu
+
+DB_DIR="$HOME/.local/share/cheat-sheets"
+[ -d "$DB_DIR" ] || exit 1
+
+# Stage 1: pick a sheet
+sheet="$(
+ ls "$DB_DIR"/*.tsv 2>/dev/null |
+ xargs -I{} basename {} .tsv |
+ rofi -dmenu -i -p 'Cheat sheet ›'
+)" || exit 0
+[ -n "${sheet:-}" ] || exit 0
+
+db="$DB_DIR/${sheet}.tsv"
+[ -f "$db" ] || exit 1
+
+# Stage 2: search within the sheet
+choice="$(rofi -dmenu -i -p "${sheet} ›" < "$db")" || exit 0
+[ -n "${choice:-}" ] || exit 0
+
+# Copy first column (keystroke/command) to clipboard
+keys="$(printf '%s' "$choice" | cut -f1)"
+if command -v xclip >/dev/null 2>&1; then
+ printf '%s' "$keys" | xclip -selection clipboard
+elif command -v xsel >/dev/null 2>&1; then
+ printf '%s' "$keys" | xsel --clipboard --input
+fi
diff --git a/sfeed.sh b/sfeed.sh
new file mode 120000
index 0000000..9787ca9
--- /dev/null
+++ b/sfeed.sh
@@ -0,0 +1 @@
+/home/lukasz/.sfeed/sfeed.sh \ No newline at end of file
diff --git a/shield-backup b/shield-backup
index 05c8945..ccaff5b 100755
--- a/shield-backup
+++ b/shield-backup
@@ -1,6 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
+export PATH="/usr/sbin:/sbin:/usr/bin:/bin:$PATH"
+RCLONE_CONFIG="$HOME/.config/rclone/rclone.conf"
#################### CONFIG – EDIT THESE ####################
# LUKS2 block device
@@ -26,6 +28,7 @@ MACHINE_DIR="T480"
RSYNC_EXCLUDES_FILE="/tmp/rsync_excludes_$$.txt"
cat > "${RSYNC_EXCLUDES_FILE}" <<'EOF'
+downloads/
# Caches & regenerable state
.cache/
.fasd
@@ -122,9 +125,9 @@ sudo mkdir -p \
"${LUKS_MOUNTPOINT}/SP_Skyscale"
echo ">>> Starting rclone syncs (SharePoint -> ${LUKS_MOUNTPOINT}/SP_*)..."
-rclone sync 'SP_Administration:' "${LUKS_MOUNTPOINT}/SP_Administration" --create-empty-src-dirs --fast-list --progress
-rclone sync 'SP_Operations:' "${LUKS_MOUNTPOINT}/SP_Operations" --create-empty-src-dirs --fast-list --progress
-rclone sync 'SP_SkyscaleCommerce:' "${LUKS_MOUNTPOINT}/SP_Skyscale" --create-empty-src-dirs --fast-list --progress
+rclone --config "$RCLONE_CONFIG" sync 'SP_Administration:' "${LUKS_MOUNTPOINT}/SP_Administration" --create-empty-src-dirs --fast-list --progress
+rclone --config "$RCLONE_CONFIG" sync 'SP_Operations:' "${LUKS_MOUNTPOINT}/SP_Operations" --create-empty-src-dirs --fast-list --progress
+rclone --config "$RCLONE_CONFIG" sync 'SP_SkyscaleCommerce:' "${LUKS_MOUNTPOINT}/SP_Skyscale" --create-empty-src-dirs --fast-list --progress
echo ">>> All rclone syncs finished."
echo ">>> Starting rsync backups of local folders -> ${LUKS_MOUNTPOINT}/${MACHINE_DIR}/..."
diff --git a/simplex b/simplex
deleted file mode 100755
index 9ae7bfc..0000000
--- a/simplex
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-mkdir -p "$HOME/.cache/simplex-tmp"
-export TMPDIR="$HOME/.cache/simplex-tmp"
-exec st -e simplex-chat
diff --git a/update-cht.sh b/update-cht.sh
new file mode 100755
index 0000000..5a1d389
--- /dev/null
+++ b/update-cht.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+set -eu
+
+URL="https://cht.sh/:cht.sh"
+DEST="$HOME/.local/bin/cht.sh"
+TMP="$(mktemp)"
+
+curl -fsSL "$URL" -o "$TMP"
+chmod +x "$TMP"
+
+# replace atomically
+mv "$TMP" "$DEST"
diff --git a/wiki b/wiki
new file mode 100755
index 0000000..b629903
--- /dev/null
+++ b/wiki
@@ -0,0 +1,341 @@
+#!/usr/bin/python3
+# coding=utf-8
+#
+# See LICENSE.
+#
+# Copy me if you can.
+# by 20h
+#
+
+import os
+import sys
+import re
+import getopt
+import click
+import codecs
+from subprocess import Popen, PIPE
+
+DEFAULTBASE = "~/.psw"
+
+class git(object):
+ def __init__(self, base=None):
+ if base == None:
+ base = DEFAULTBASE
+ self.base = os.path.expanduser(base)
+ if not os.path.exists(self.base):
+ os.makedirs(self.base, 0o750)
+
+ if self.base[-1] != os.sep:
+ self.pages = "%s%s" % (self.base, os.sep)
+ else:
+ self.pages = self.base
+
+ self.keycache = None
+
+ """ I/O helper functions. """
+ def getfile(self, fi):
+ fd = codecs.open(fi, mode="r", encoding="utf-8")
+ text = fd.read()
+ fd.close()
+ return text
+
+ def putfile(self, fi, content):
+ os.makedirs(os.path.dirname(fi), exist_ok=True)
+ fd = codecs.open(fi, mode="w+", encoding="utf-8")
+ fd.write(content)
+ fd.close()
+ return None
+
+ def mkpath(self, path, fi=None):
+ if path[-1] != os.sep:
+ path = "%s%s" % (path, os.sep)
+ if fi != None:
+ path = "%s%s" % (path, fi)
+
+ return path
+
+ def recursedir(self, path):
+ subdirs = []
+ files = os.listdir(path)
+ files.sort()
+ for f in files:
+ npath = self.mkpath(path, f)
+ if os.path.isfile(npath) or \
+ os.path.islink(npath):
+ yield npath
+ else:
+ subdirs.append(f)
+
+ for s in subdirs:
+ npath = self.mkpath(path, s)
+ if s == ".git":
+ continue
+ for j in self.recursedir(npath):
+ yield j
+
+ def makesearchlist(self, fun, path):
+ li = []
+ npath = self.mkpath(path)
+ for i in fun(path):
+ i = i.replace(npath, "")
+ i = i.removesuffix(".md")
+ li.append(i)
+ return li
+
+ """ Git command functions. """
+ def git(self, args):
+ gitdir = "%s.git" % (self.pages)
+ workdir = self.pages
+ cmd = ["git", "--git-dir=%s" % gitdir, "--work-tree=%s" % workdir] + args
+
+ p = Popen(cmd, stdout=PIPE, stderr=PIPE)
+ result = p.stdout.read().decode("utf-8", errors="replace")
+
+ return [result, p.wait()]
+
+ def gitpath(self, file):
+ return "%s%s.md" % (self.pages, file)
+
+ def init(self):
+ self.git(["init"])
+
+ def add(self, file):
+ self.git(["add", file])
+
+ def rm(self, file):
+ self.git(["rm", file])
+
+ def mv(self, old, new):
+ self.git(["mv", old, new])
+
+ def commit(self, msg):
+ self.git(["commit", "--allow-empty", "--no-verify",
+ "--message=%s" % msg, "--author=psw <psw@psw>"])
+ self.git(["gc"])
+
+ def log(self, page):
+ changes = []
+ if page == "":
+ extra = []
+ else:
+ extra = ["--", self.gitpath(page)]
+ sep = "\x01"
+ (result, status) = self.git(
+ ["log", "--pretty=format:%%H%s%%T%s%%an%s%%ae%s%%aD%s%%s" % ((sep,) * 5)] + extra
+ )
+ for line in result.splitlines():
+ if not line.strip():
+ continue
+ entries = line.split(sep)
+ if len(entries) < 6:
+ continue
+ change = {}
+ change["commit"] = entries[0]
+ change["author"] = entries[2]
+ change["email"] = entries[3]
+ change["date"] = entries[4]
+ change["message"] = entries[5]
+ try:
+ (task, cpage) = entries[5].split(" ", 1)
+ except ValueError:
+ cpage = page
+ change["page"] = cpage
+ changes.append(change)
+
+ return changes
+
+ def showcommit(self, page, commit):
+ (file, status) = self.git(["cat-file", "-p", "%s:%s.md" % (commit, page)])
+ return file
+
+ """ Dictionary abstraction functions. """
+ def __setitem__(self, page, value):
+ file = self.gitpath(page)
+ needadd = False
+ if os.path.exists(file) == False:
+ needadd = True
+ self.putfile(file, value)
+
+ self.add(file)
+ if needadd:
+ self.commit("Added: %s" % (page))
+ else:
+ self.commit("Changed: %s" % (page))
+
+ def __delitem__(self, page):
+ file = self.gitpath(page)
+ os.remove(file)
+
+ self.rm(file)
+ self.commit("Deleted: %s" % (page))
+
+ def __getitem__(self, page):
+ try:
+ return self.getfile(self.gitpath(page))
+ except IOError as err:
+ return ""
+
+ def getlog(self, page):
+ log = self.log(page)
+ ret = ""
+ for i in log:
+ ret += "%s %s %s %s %s %s\n" % (i["commit"],
+ i["author"],
+ i["email"], i["date"], i["page"],
+ i["message"])
+ return ret
+
+ def mkkeycache(self):
+ if self.keycache == None:
+ self.keycache = self.makesearchlist(self.recursedir,
+ self.pages)
+
+ def keys(self):
+ self.mkkeycache()
+ return self.keycache
+
+ def __contains__(self, item):
+ return item in list(self.keys())
+
+ def search(self, query):
+ try:
+ pattern = re.compile(query)
+ except re.error as e:
+ sys.stderr.write("Invalid regex: %s\n" % e)
+ return []
+
+ name_hits = []
+ content_hits = []
+ seen = set()
+
+ for page in list(self.keys()):
+ if pattern.search(page):
+ name_hits.append(page)
+ seen.add(page)
+
+ for page in list(self.keys()):
+ if page in seen:
+ continue
+ try:
+ content = self.getfile(self.gitpath(page))
+ except OSError:
+ continue
+ for line in content.splitlines():
+ if pattern.search(line):
+ content_hits.append("%s: %s" % (page, line.strip()))
+ seen.add(page)
+ break
+
+ return name_hits + content_hits
+
+ def move(self, old, new):
+ opath = self.gitpath(old)
+ npath = self.gitpath(new)
+ self.mv(opath, npath)
+ self.commit("Moved: %s -> %s" % (old, new))
+
+def editor(content):
+ try:
+ data = click.edit(content, require_save=True, extension='.md')
+ except click.UsageError:
+ return (1, content)
+ if data == None:
+ return (1, content)
+
+ return (0, data)
+
+def usage(app):
+ app = os.path.basename(app)
+ sys.stderr.write("usage: %s [-oh] [-b base] [[-d|-e|-c|-s|-p] item" \
+ "|-l|-r old new]\n" % (app))
+ sys.exit(1)
+
+def main(args):
+ try:
+ opts, largs = getopt.getopt(args[1:], "hosplb:sdecr")
+ except getopt.GetoptError as err:
+ print(str(err))
+ usage(args[0])
+
+ dorm = False
+ doedit = False
+ docommit = False
+ dosearch = False
+ dolist = False
+ dorename = False
+ tostdout = False
+ base = DEFAULTBASE
+ for o, a in opts:
+ if o == "-h":
+ usage(args[0])
+ elif o == "-b":
+ base = a
+ elif o == "-c":
+ docommit = True
+ elif o == "-d":
+ dorm = True
+ elif o == "-e":
+ doedit = True
+ elif o == "-l":
+ dolist = True
+ elif o == "-o":
+ doedit = True
+ tostdout = True
+ elif o == "-p":
+ doedit = True
+ tostdout = True
+ elif o == "-r":
+ dorename = True
+ elif o == "-s":
+ dosearch = True
+ else:
+ assert False, "unhandled option"
+
+ val = ""
+ if doedit == True or dosearch == True or dorm == True or docommit == True:
+ if len(largs) < 1 and not docommit:
+ usage(args[0])
+ val = "_".join(largs)
+ elif dorename == True:
+ if len(largs) < 2:
+ usage(args[0])
+
+ lgit = git(base)
+
+ if doedit == True:
+ content = lgit[val]
+ if tostdout == True:
+ sys.stdout.write(content)
+ else:
+ (sts, data) = editor(content)
+ if data == content:
+ print("No changes made. Quitting.")
+ else:
+ print("Some changes made. Committing.")
+ lgit[val] = data
+ elif dorename == True:
+ lgit.move(largs[0], largs[1])
+ elif dorm == True:
+ del lgit[val]
+ elif docommit == True:
+ commits = lgit.getlog(val)
+ if tostdout == True:
+ sys.stdout.write(commits)
+ else:
+ p = Popen(["less"], stdin=PIPE)
+ p.communicate(input=commits.encode("utf-8"))
+ elif dosearch == True:
+ results = lgit.search(val)
+ for r in results:
+ print(r)
+ elif dolist == True:
+ for k in list(lgit.keys()):
+ print(k)
+ else:
+ usage(args[0])
+
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv))
+
diff --git a/wiki-search b/wiki-search
new file mode 100755
index 0000000..15ee443
--- /dev/null
+++ b/wiki-search
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+# Live ripgrep through vimwiki, live preview, open match in vim at the line.
+set -euo pipefail
+
+wiki="${VIMWIKI_DIR:-$HOME/docs/vimwiki}"
+[[ -d $wiki ]] || { echo "wiki dir not found: $wiki" >&2; exit 1; }
+cd "$wiki"
+
+rg_cmd='rg --color=always --line-number --no-heading --smart-case --glob "*.md"'
+
+selection=$(
+ fzf --ansi --disabled \
+ --delimiter=':' \
+ --bind="start:reload:$rg_cmd -- ''" \
+ --bind="change:reload:$rg_cmd -- {q} || true" \
+ --bind='ctrl-/:change-preview-window(hidden|right,60%,+{2}/2)' \
+ --preview='batcat --color=always --style=numbers --highlight-line={2} -- {1} 2>/dev/null' \
+ --preview-window='right,60%,+{2}/2' \
+ --header='Type to search · Enter opens at line · Ctrl-/ toggles preview'
+) || exit 0
+[[ -z $selection ]] && exit 0
+
+file=$(printf '%s' "$selection" | cut -d: -f1)
+line=$(printf '%s' "$selection" | cut -d: -f2)
+
+exec vim "+$line" "$file"
diff --git a/zathura b/zathura
new file mode 100755
index 0000000..6441190
--- /dev/null
+++ b/zathura
@@ -0,0 +1,6 @@
+#!/bin/sh
+# Wrapper: log file opens to fre, then exec real zathura.
+for arg in "$@"; do
+ [ -f "$arg" ] && fre --add "$(readlink -f "$arg")" 2>/dev/null
+done
+exec /usr/bin/zathura "$@"