diff options
| author | Robert Günzler <r@gnzler.io> | 2026-09-27 17:38:39 +0200 |
|---|---|---|
| committer | Robert Günzler <r@gnzler.io> | 2026-09-27 17:38:39 +0200 |
| commit | 0860db73ae6fd9a6f6347219d820bf026a6b8b91 (patch) | |
| tree | 11318c3670d1c32c5aa0bf404c22fba3bf63fc53 /bin | |
| parent | 4e34cbf9673aeaba252e01df554a0d204e83c5e2 (diff) | |
| download | dotfiles-master.tar.gz | |
Automated-commit-by: dots Signed-off-by: Robert Günzler <r@gnzler.io>
Diffstat (limited to '')
| -rwxr-xr-x | bin/2av1 | 13 | ||||
| -rwxr-xr-x | bin/audm | 31 | ||||
| -rwxr-xr-x | bin/blum | 40 | ||||
| -rwxr-xr-x | bin/dots | 10 | ||||
| -rwxr-xr-x | bin/genup | 8 | ||||
| -rwxr-xr-x | bin/git-sync | 58 | ||||
| -rw-r--r-- | bin/libfuzl.sh | 39 | ||||
| -rwxr-xr-x | bin/netm | 37 | ||||
| -rwxr-xr-x | bin/pasm | 48 | ||||
| -rwxr-xr-x | bin/play-mpv | 26 | ||||
| -rwxr-xr-x | bin/play-music | 9 | ||||
| -rwxr-xr-x | bin/screenrec | 1 | ||||
| -rwxr-xr-x | bin/svcm | 1 | ||||
| -rwxr-xr-x | bin/todo | 6 | ||||
| -rwxr-xr-x | bin/urlopen | 31 | ||||
| -rwxr-xr-x | bin/vpnm | 74 |
16 files changed, 287 insertions, 145 deletions
diff --git a/bin/2av1 b/bin/2av1 index 19ace0b..287323a 100755 --- a/bin/2av1 +++ b/bin/2av1 @@ -3,8 +3,13 @@ set -eu [ -n "${DEBUG:-}" ] && set -x [ -z "${DEBUG:-}" ] && export SVT_LOG=1 -BITRATE=${BITRATE:-128000} -EXT=${EXT:-webm} +: ${KEEP:=} +: ${OUTDIR:=} +: ${BITRATE:=128k} +: ${EXT:=webm} + +# expand +BITRATE=${BITRATE%k*}000 case "${VCODEC:-av1}" in vp9) vcodec_args=(-c:v libvpx-vp9 -crf "${CRF:-31}" -b:v 0) ;; @@ -76,6 +81,10 @@ for f in "$@"; do awk -v fs="$in_size" -v os="$out_size" \ 'BEGIN{p=((100/fs) * (fs-os)); printf("♻️ %.2f%% reduction\n", p);}' + if [ -n "$KEEP" ]; then + return + fi + if [ $in_size -gt $out_size ]; then trash-put -v "${f}" else diff --git a/bin/audm b/bin/audm index 58a5a91..33abb34 100755 --- a/bin/audm +++ b/bin/audm @@ -17,7 +17,9 @@ main() { source=$(inspect_node '@DEFAULT_SOURCE@' | awk -F\\t '{print $2}') fuzl_action_add 'change_source' "" "$source" - fuzl_action_add 'change_profile' "" "Change profile" + fuzl_action_add 'change_profile' "" "Change profile" + + fuzl_action_add 'mixsource' "" "Mix source into sink" fuzl_action_menu || exit 1 } @@ -67,11 +69,11 @@ audm_change_profile() { device=$(pw-dump | jq -rc --arg class "Audio/Device" \ '.[]|select((.info.props["media.class"]//"")|test($class))|.info.props|[.["object.id"],.["device.description"],.["device.nick"]]|join("\t")' | - FUZL_PROMPT='device' fuzl_menu $FUZL_MENU_FILTER --with-nth='{2} {3}' --accept-nth='{1}\t{2}') + FUZL_PROMPT='device' fuzl_menu $FUZL_MENU_FILTER --with-nth='{2} {3}') test -n "$device" || main - did=$(printf %s "$device" | awk -F\\t '{print $1}') - dname=$(printf %s "$device" | awk -F\\t '{print $2}') + did=$(awk -F\\t '{print $1}' <<<"$device") + dname=$(awk -F\\t '{print $2}' <<<"$device") profile=$(pw-dump | jq -r --argjson did "$did" \ '.[]|select(.id==$did)|.info.params.EnumProfile[]|[.index,.description]|join("\t")' | @@ -81,4 +83,25 @@ audm_change_profile() { fuzl_try -- wpctl set-profile "$did" "$profile" } +audm_mixsource() { + sink=$(inspect_node '@DEFAULT_SINK@' | awk -F\\t '{print $1}') + sink_json=$(pw-dump -R "$sink" | jq -rc --argjson id "$sink" '.[]|select(.id==$id)') + sink_node=$(jq -rc '.info.props["node.name"]' <<<$sink_json) + sink_ports=$(jq -r '.info.params.PortConfig[]|select(.direction=="Input")|.format.position[]' <<<$sink_json) + + source=$(inspect_node '@DEFAULT_SOURCE@' | awk -F\\t '{print $1}') + source_json=$(pw-dump -R "$source" | jq -rc --argjson id "$source" '.[]|select(.id==$id)') + source_node=$(jq -rc '.info.props["node.name"]' <<<$source_json) + source_ports=$(jq -rc '.info.params.PortConfig[]|select(.direction=="Output")|.format.position[]' <<<$source_json) + + for source_port in $source_ports; do + for sink_port in $sink_ports; do + ( + pw-link --passive "$source_node":capture_"$source_port" "$sink_node":playback_"$sink_port" || + pw-link --disconnect "$source_node":capture_"$source_port" "$sink_node":playback_"$sink_port" + ) & + done + done +} + main diff --git a/bin/blum b/bin/blum index 19c2aef..6f65f55 100755 --- a/bin/blum +++ b/bin/blum @@ -13,7 +13,8 @@ main() { if ! bt_enabled; then fuzl_action_add "enable" "" "Enable Bluetooth" else - connected=$(bt_devices Connected | bt_devices_getname) + # XXX: handle multiple + connected=$(bt_devices Connected | tail -1 | bt_devices_getname) if [ -n "$connected" ]; then fuzl_action_add "disconnect" "" "Connected to $connected" fi @@ -35,7 +36,6 @@ bt_devices() { echo "devices" "${1:-}" | bluetoothctl | awk '/^Device /{match($0, /[0-9A-F:]{17}/); mac=substr($0,RSTART,RLENGTH); name=substr($0,RSTART+RLENGTH+1); print mac "\t" name}' } - bt_devices_getmac() { awk -F '\t' '{print $1}' } @@ -44,19 +44,40 @@ bt_devices_getname() { awk -F '\t' '{print $2}' } +amagiri_mac='3C:38:F4:5D:3D:49' +bt_tether_amagiri_connected() { + nmcli device show "$amagiri_mac" | + grep -i '^general.state' | + grep -q 'connected' +} +bt_tether_amagiri_up() { + echo "connect" "$amagiri_mac" | + fuzl_try -- bluetoothctl + fuzl_try -- nmcli device connect "$amagiri_mac" + fuzl_try -- nmcli radio wifi off + fuzl_notification "tethered" "amagiri" +} +bt_tether_amagiri_down() { + fuzl_try -- nmcli device disconnect "$amagiri_mac" + echo "disconnect" "$amagiri_mac" | + fuzl_try -- bluetoothctl + fuzl_try -- nmcli radio wifi on + fuzl_notification "untethered" "amagiri" +} + # # actions # blum_enable() { fuzl_try -- doas -n rc-service bluetooth start - fuzl_notification "enabled" "-" + fuzl_notification "enabled" "service started" main } blum_disable() { fuzl_try -- doas -n rc-service bluetooth stop - fuzl_notification "disabled" "-" + fuzl_notification "disabled" "service stopped" } blum_disconnect() { @@ -89,12 +110,11 @@ blum_repl() { } blum_tether_amagiri() { - local amagiri_mac='3C:38:F4:5D:3D:49' - local amagiri_net='e4e75ff7-b907-43b7-98b7-e0bf94a56d08' - echo "connect" "$amagiri_mac" | fuzl_try -- bluetoothctl - fuzl_notification "connected" "amagiri" - fuzl_try -- nmcli connection up "$amagiri_net" - fuzl_notification "network up" "amagiri" + if ! bt_tether_amagiri_connected; then + bt_tether_amagiri_up + else + bt_tether_amagiri_down + fi } main diff --git a/bin/dots b/bin/dots index 7076ece..c455f58 100755 --- a/bin/dots +++ b/bin/dots @@ -7,7 +7,7 @@ git) exec /usr/bin/git --git-dir=$HOME/.dotfiles --work-tree=$HOME $* ;; st | status) - exec $0 git status -sb --untracked-files=no + exec $0 git status -sb --untracked-files=yes ;; add) shift @@ -29,4 +29,12 @@ untracked) shift exec $0 git ls-files -o $* ;; +edit) + $0 git ls-files --full-name $HOME | + fzf --layout=reverse --height='~60%' \ + --bind=enter:replace-query+become:"nvim $HOME/{q}" + ;; +*) + echo "dots has its own subcommands, if you want git, run dots git [git command]" + ;; esac diff --git a/bin/genup b/bin/genup index c759a10..ad8d621 100755 --- a/bin/genup +++ b/bin/genup @@ -68,7 +68,7 @@ ebegin updating @world ebegin updating unstable [ -n "${dolive:-}" ] && - _ emerge -a1 --keep-going @live-rebuild && + _ emerge -a1 --keep-going "$@" $eargs @live-rebuild && eend ebegin depcleaning @@ -85,7 +85,7 @@ ebegin updating @preserved-rebuild ebegin updating flatpaks [ -x "$(command -v flatpak)" ] && [ -n "${doflatpak:-}" ] && - _ flatpak upgrade && - _ flatpak uninstall --unused && - _ flatpak repair && + _ doas -u robert flatpak upgrade && + _ doas -u robert flatpak uninstall --unused && + _ doas -u robert flatpak repair && eend diff --git a/bin/git-sync b/bin/git-sync index b18c94d..9a41c5c 100755 --- a/bin/git-sync +++ b/bin/git-sync @@ -2,37 +2,45 @@ set -eu [ -n "${DEBUG:-}" ] && set -x -# use cli arg -base_branch=${1:-} +sync() { + local this_branch="${1:?}" + local base_branch="" + local upstream="" -# detect base branch (when git-new was used) -[ -n "$base_branch" ] || base_branch=$(git symbolic-ref --short refs/bases/"$(git symbolic-ref --short HEAD)" 2>/dev/null || :) + # detect base branch (when git-new was used) + [ -n "$base_branch" ] || base_branch=$(git symbolic-ref --short refs/baseof/"$this_branch" 2>/dev/null || :) -# detect trunk via origin/HEAD -[ -n "$base_branch" ] || base_branch=$(git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null || :) + # recursive + [ -n "$base_branch" ] && sync "$base_branch" && git switch "$this_branch" -# fallback to base branch => current branch -[ -n "$base_branch" ] || base_branch=$(git symbolic-ref --short HEAD 2>/dev/null || :) + # detect trunk via origin/HEAD + [ -n "$base_branch" ] || base_branch=$(git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null || :) -# resolve upstream -if [[ "$base_branch" = "origin/"* ]]; then - upstream=$base_branch -else - upstream=$(git rev-parse --abbrev-ref --symbolic-full-name "$base_branch"'@{u}') -fi + # fallback to base branch == current branch + [ -n "$base_branch" ] || base_branch=$this_branch -[ -n "${base_branch:?}" ] || exit 1 -[ -n "${upstream:?}" ] || exit 1 + # resolve upstream + if [[ "$base_branch" != "origin/"* ]]; then + upstream=$(git rev-parse --abbrev-ref --symbolic-full-name "$base_branch"'@{u}') + else + upstream=origin + fi -echo "Syncing with $upstream (base: $base_branch)" + echo "Syncing $this_branch with $base_branch from $upstream" -# fetch upstream -git fetch ${upstream/\// } + [ -n "${base_branch:?}" ] || exit 1 + [ -n "${upstream:?}" ] || exit 1 -# check if base and upstream have diverged -if ! git diff --quiet "$upstream" "$base_branch"; then - git diff --stat "$upstream" "$base_branch" -fi + # fetch upstream + git fetch ${upstream/\// } -# rebase -git rebase --interactive "$upstream" + # check if base and upstream have diverged + if ! git diff --quiet "$upstream" "$base_branch"; then + git diff --stat "$upstream" "$base_branch" + fi + + # rebase + git rebase --interactive "$upstream" "$this_branch" +} + +sync "$(git symbolic-ref --short HEAD)" diff --git a/bin/libfuzl.sh b/bin/libfuzl.sh index a28c861..c53a71f 100644 --- a/bin/libfuzl.sh +++ b/bin/libfuzl.sh @@ -4,9 +4,12 @@ set -eu +# TODO: use custom-1 somehow + # pick up configuration FUZL_PROMPT=${FUZL_PROMPT:-fuzl} FUZL_ICON=${FUZL_ICON:-} +FUZL_CLI_ARGS=${FUZL_CLI_ARGS:-$@} fuzl_try() { [ "$1" == "--" ] && shift 1 @@ -89,6 +92,11 @@ fuzl_loading_cancel() { FUZL_ACTIONS=() +fuzl_action_reset() { + # reset actions + FUZL_ACTIONS=() +} + fuzl_action_add() { local action="${1:?}" icon="${2:?}" shift 2 @@ -97,17 +105,36 @@ fuzl_action_add() { FUZL_ACTIONS+=("${action}\t${icon}\t${*}") } +fuzl_action_help() { + echo "usage: $FUZL_PROMPT [action]" + echo + for action in "${FUZL_ACTIONS[@]}"; do + echo -e " $action" + done + echo + exit 0 +} + fuzl_action_menu() { local action - action=$(printf '%s\n' "${FUZL_ACTIONS[@]}" | - FUZL_PROMPT="$FUZL_PROMPT: $*" fuzl_menu $FUZL_MENU_FILTER --with-nth='{2} {3..}' --accept-nth=1) + # only actually ask if action wasn't provided on cli + action=${FUZL_CLI_ARGS} + if [[ "$action" == "--help" ]]; then + fuzl_action_help + fi + if test -z "$action"; then + action=$(printf '%s\n' "${FUZL_ACTIONS[@]}" | + FUZL_PROMPT="$FUZL_PROMPT: $*" fuzl_menu $FUZL_MENU_FILTER --with-nth='{2} {3..}' --accept-nth=1) + fi - # reset actions - FUZL_ACTIONS=() + fuzl_action_reset - test -n "$action" || return - eval "${FUZL_PROMPT}_${action}" "'$*'" + if declare -f "${FUZL_PROMPT}_${action}" >/dev/null; then + eval "${FUZL_PROMPT}_${action}" "'$*'" + else + return 1 + fi } # setup common debug helper diff --git a/bin/netm b/bin/netm index fab7e95..0ea9d2e 100755 --- a/bin/netm +++ b/bin/netm @@ -1327,39 +1327,12 @@ show_qrcode() { fi local qr_string="WIFI:T:${security};S:${ssid};P:${password};;" - local qr_file="/tmp/hyprltm-net-qr-${ssid}.png" - show_loading_notification "$tr_qrcode_generating" - qrencode -o "$qr_file" -s 10 -m 2 "$qr_string" - kill_loading_notification - - # Display QR code inside Rofi - # -i: Enable icons - # -theme-str: Override theme to show large icon and no text input - # Display QR code inside Rofi - # -i: Enable icons - # -show-icons: Required to show icons in dmenu mode - # -theme-str: Override theme to show large icon and no text input - - # We use the successful configuration from the test script - local rofi_override=" - window { width: 500px; } - listview { lines: 1; scrollbar: false; } - element { orientation: vertical; padding: 20px; children: [ element-icon, element-text ]; } - element-icon { enabled: true; size: 300px; horizontal-align: 0.5; } - element-text { horizontal-align: 0.5; } - entry { enabled: false; } - inputbar { enabled: false; } - " - - # We pass an entry with the icon set to the QR file path - # Format: Valid Text \0icon\x1f/path/to/image.png - # We use "Scan to Connect" as the text under the QR code - echo -e "Scan to Connect\0icon\x1f${qr_file}" | - fuzzel --dmenu -i -show-icons -p "$tr_qrcode_message" - - # Cleanup after Rofi closes - rm -f "$qr_file" 2>/dev/null + { + echo "" # spacer + qrencode -t UTF8i -s 10 -m 2 "$qr_string" + } | + display_menu 4 "Scan to connect..." "" '-l 18' } # Get password for a known Wi-Fi connection diff --git a/bin/pasm b/bin/pasm index 7555167..60c0394 100755 --- a/bin/pasm +++ b/bin/pasm @@ -16,12 +16,33 @@ main() { local eid eid=$(rbw_select_entry) + case "$eid" in + cards) + eid=$(rbw list --raw | + jq -r '.[]|select(.type=="Card")|"\(.id)\t\(.name)"' | + fuzl_menu $FUZL_MENU_FILTER --with-nth=2 --accept-nth=1) - fuzl_action_add "copy" "" "copy username/password" - rbw_has_totp "$eid" && fuzl_action_add "totp" "" "copy totp code" - fuzl_action_add "url" "" "copy url" + fuzl_action_add "raw" "" "view json" + fuzl_action_menu "$eid" || exit 1 + ;; + identities) + eid=$(rbw list --raw | + jq -r '.[]|select(.type=="Identity")|"\(.id)\t\(.name)"' | + fuzl_menu $FUZL_MENU_FILTER --with-nth=2 --accept-nth=1) + + fuzl_action_add "raw" "" "view json" + fuzl_action_menu "$eid" || exit 1 + ;; + *) + fuzl_action_add "copy" "" "copy username/password" + rbw_has_totp "$eid" && fuzl_action_add "totp" "" "copy totp code" + fuzl_action_add "url" "" "copy url" + fuzl_action_add "raw" "" "view json" + + fuzl_action_menu "$eid" || exit 1 + ;; + esac - fuzl_action_menu "$eid" || exit 1 } # @@ -29,9 +50,15 @@ main() { # rbw_select_entry() { - rbw list --raw | - jq -r '.[] | select(.type=="Login") | [.id,.name,.user,(.uris[0]//"-")]|join("%")' | - awk -F% '{print $1 "\t" ($2 " →") "\t" (" " $3) "\t" (" " $4)}' | + ( + rbw list --raw | + jq -r '.[] | select(.type=="Login") | [.id,.name,.user,(.uris[0]//"-")]|join("%")' | + awk -F% '{print $1 "\t" ($2 " →") "\t" (" " $3) "\t" (" " $4)}' + printf 'cards\t~> C\tcards\n' + printf 'identities\t~> I\tidentities\n' + printf 'keys\t~> K\tkeys\n' + printf 'notes\t~> N\tnotes\n' + ) | fuzl_menu $FUZL_MENU_FILTER --with-nth='{2} {3..}' --accept-nth=1 -w 80 } @@ -68,4 +95,11 @@ pasm_url() { fuzl_notification "$(rbw_entry "$eid")" "copied url to clipboard" } +pasm_raw() { + local eid=${1:?missing eid} + local tmpfile="/tmp/$(printf '%s' "$eid" | sha1sum | cut -d\ -f1).json" + fuzl_try -- rbw get --raw "$eid" >"$tmpfile" + fuzl_try -- urlopen open "$tmpfile" +} + main diff --git a/bin/play-mpv b/bin/play-mpv index 21f20c6..bb85c02 100755 --- a/bin/play-mpv +++ b/bin/play-mpv @@ -35,15 +35,9 @@ import sys from collections.abc import Iterable from typing import BinaryIO -from itertools import tee def notify(files: Iterable[str]) -> None: - for f in files: - subprocess.Popen([ - 'fyi', - 'play-mpv', - f, - ], start_new_session=True) + for f in files: subprocess.Popen(['fyi', 'play-mpv', f], start_new_session=True) def is_url(filename: str) -> bool: parts = filename.split("://", 1) @@ -67,25 +61,22 @@ def get_socket_path() -> str: "Ensure that one of the following environment variables is set: " "UMPV_SOCKET_DIR, XDG_RUNTIME_DIR, HOME or TMPDIR.") - return os.path.join(base_dir, ".umpv") + sockid = "0" + return os.path.join(base_dir, "umpv-" + os.getenv("SOCKID", sockid)) def send_files_to_mpv(conn: socket.socket | BinaryIO, files: Iterable[str]) -> None: - notify(tee(files)[0]) - try: send = conn.send if isinstance(conn, socket.socket) else conn.write for f in files: f = f.replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\n") - send(f'raw loadfile "{f}" append-play\n'.encode()) + opt = os.getenv("LOADFILE", "append-play") + send(f'raw loadfile "{f}" {opt}\n'.encode()) except Exception: print("mpv is terminating or the connection was lost.", file=sys.stderr) sys.exit(1) def start_mpv(files: Iterable[str], socket_path: str) -> None: - [files, files_copy] = tee(files, 2) - notify(files_copy) - mpv = "mpv" mpv_command = shlex.split(os.getenv("MPV", mpv)) mpv_command.extend([ @@ -94,13 +85,14 @@ def start_mpv(files: Iterable[str], socket_path: str) -> None: "--", ]) mpv_command.extend(files) - print(mpv_command) - # subprocess.Popen(mpv_command, start_new_session=True) + subprocess.Popen(mpv_command, start_new_session=True) def main() -> None: - files = (os.path.abspath(f) if not is_url(f) else f for f in sys.argv[1:]) + files = list(os.path.abspath(f) if not is_url(f) else f for f in sys.argv[1:]) socket_path = get_socket_path() + os.getenv("NONOTIFY") or notify(files) + try: with socket.socket(socket.AF_UNIX) as sock: sock.connect(socket_path) diff --git a/bin/play-music b/bin/play-music index 14e91f1..554d93f 100755 --- a/bin/play-music +++ b/bin/play-music @@ -20,4 +20,13 @@ select) subdir=$(find . -type d -printf "%P\n" | shuf | fuzzel -d -w 80) mpa "$subdir" ;; +playlists) + cd $music_dir + playlist=$(find . -maxdepth 1 -name '*.m3u' -printf "%P\n" | shuf | fuzzel -d -w 80) + mpa "$playlist" + ;; +*) + shift + mpa "$@" + ;; esac diff --git a/bin/screenrec b/bin/screenrec index 5324ead..3b221c5 100755 --- a/bin/screenrec +++ b/bin/screenrec @@ -1,5 +1,6 @@ #!/bin/sh set -eu +[ -n "${DEBUG:-}" ] && set -x update_waybar() { sleep .5 diff --git a/bin/svcm b/bin/svcm index b7daf39..1c818fe 100755 --- a/bin/svcm +++ b/bin/svcm @@ -37,6 +37,7 @@ select_service() { return fi + fuzl_action_reset fuzl_action_add "restart" "" "Restart" fuzl_action_add "start" "" "Start" fuzl_action_add "stop" "" "Stop" diff --git a/bin/todo b/bin/todo index 7436779..51f60c0 100755 --- a/bin/todo +++ b/bin/todo @@ -52,9 +52,9 @@ function link(uri) { // support todotxt files if (filename() == "todo.txt") { status = "TODO" - if ($1 ~ /^x/) { + if ($1 == "x") { status = "DONE" - $0 = gensub(/^[x] /, "", "1") + $0 = gensub(/^x /, "", "1") $0 = strike($0) } @@ -66,7 +66,7 @@ function link(uri) { } // support TODO files - if ($1 ~ /^(-|*)/) { + if ($1 == "*" || $1 == "-") { // detect status strings match($0, /\s(TODO|WIP|WAITING|DONE)\s/, arr) diff --git a/bin/urlopen b/bin/urlopen index 2b0479c..0f2e6dd 100755 --- a/bin/urlopen +++ b/bin/urlopen @@ -14,7 +14,7 @@ main() { fuzl_action_add "ask" "" "ask" fuzl_action_add "clipboard" "" "clipboard" fuzl_action_add "primary" "" "primary" - fuzl_action_menu "$(get_text "$*")" + fuzl_action_menu "$(get_text "$*")" || open_default "$(get_text "$*")" ;; 2) local action @@ -30,6 +30,8 @@ main() { # helpers # +DEFAULT_BROWSER=librewolf.desktop + get_text() { local text="$*" [ -z "$text" ] && read -r text @@ -40,25 +42,30 @@ get_mimetype() { local text=${1:?missing url} case "$text" in http*) printf %s 'x-scheme-handler/http' ;; - *) file -bdE --mime-type "$text" ;; + *) xdg-mime query filetype "$text" 2>/dev/null || file -bE --mime-type "$text" ;; + esac +} +get_x-scheme-handler_override() { + case "$1" in + slack.com) printf %s 'workfox.desktop' ;; + *) return 1 ;; esac } open_default() { local text=${1:?missing url} local application case "$(get_mimetype "$text")" in - x-scheme-handler/http*) application=librewolf ;; + x-scheme-handler/http*) application=$DEFAULT_BROWSER ;; esac - case "$text" in - *linear.app*) application=linear ;; - *) application=librewolf ;; - esac - if [ -z "$application" ]; then - gio open "$text" - else - desktop_file=$(rg -Lil -e "$application" -- /usr/share/applications/ /var/lib/flatpak/exports/share/applications/ ~/.local/share/applications/ | tail -1) - gio launch "$desktop_file" "$text" + application_override=$(get_x-scheme-handler_override "$text" || :) + if [ -n "${application_override}" ]; then + application=$application_override + fi + if [ -n "${application:-}" ]; then + desktop_file=$(rg -Lil -g "$application" . -- /usr/share/applications/ /var/lib/flatpak/exports/share/applications/ ~/.local/share/applications/ | head -1) + exec gio launch "$desktop_file" "$text" fi + exec gio open "$text" } # # actions diff --git a/bin/vpnm b/bin/vpnm index f09b41a..0fe1224 100755 --- a/bin/vpnm +++ b/bin/vpnm @@ -12,9 +12,17 @@ main() { local conn conn=$(get_current_connection) - [ -n "$conn" ] && fuzl_action_add "down" "" "Connected to $conn" + [ -n "$conn" ] && fuzl_action_add "exitnode_down" "" "Connected to $conn" fuzl_action_add "networks" "" "Available networks" - fuzl_action_add "profile" "" "Switch profile" + + profile=$(ts_profile_current) + fuzl_action_add "profile" "" "Profile: $profile" + + if ts_state; then + fuzl_action_add "down" "" "Disable tailscale" + else + fuzl_action_add "up" "" "Enable tailscale" + fi fuzl_action_menu || return } @@ -23,6 +31,23 @@ main() { # helpers # +ts_state() { + tailscale wait --timeout=1s +} +ts_profile_list() { + fuzl_try -- tailscale switch --list --json +} +ts_profile_current() { + ts_profile_list | jq -rc '.[]|select(.selected)|.nickname' +} +ts_profile_switch() { + local profile=${1?} + fuzl_try -- tailscale switch "$profile" + sleep 2 + fuzl_try -- pkill -SIGRTMIN+2 waybar + +} + get_emoji_flag() { code= # TODO: calc flag @@ -68,18 +93,17 @@ get_vpn_connections() { get_current_connection() { tailscale exit-node list | grep -e selected | - grep -v -e Any | ( - awk '/.tail2d6ce.ts.net/{print $2, $1}' - awk -F '[ ]{2,}' '/.mullvad.ts.net/{print $2, $1, ($3 "/" $4)}' - ) | while read -r conn ip location; do - printf '%s' "$(get_emoji_flag "$conn")" - printf ' ' - if grep -q 'mullvad.ts.net' <<<"$conn"; then - printf '%s' "$location" - else - printf '%s' "$conn" - fi - done + grep -v -e Any | + awk -F '[ ]{2,}' '/.mullvad.ts.net/{print $2, $1, ($3 "/" $4)} /.tail2d6ce.ts.net/{print $2, $1}' | + while read -r conn ip location; do + printf '%s' "$(get_emoji_flag "$conn")" + printf ' ' + if grep -q 'mullvad.ts.net' <<<"$conn"; then + printf '%s' "$location" + else + printf '%s' "$conn" + fi + done } vpnup() { @@ -96,7 +120,7 @@ vpndown() { # actions # -vpnm_down() { +vpnm_exitnode_down() { fuzl_try -- vpndown } @@ -110,15 +134,21 @@ vpnm_networks() { } vpnm_profile() { - profile=$(tailscale switch --list 2>/dev/null | - tail -n+2 | - awk '{printf "%s\t%s\n", $3, $2}' | - fuzl_menu $FUZL_MENU_FILTER --accept-nth=1) + profile=$( + ts_profile_list | + jq -rc '.[]|"\(.nickname)\t\(.tailnet)\t\(if .selected then "*" else "" end)"' | + fuzl_menu $FUZL_MENU_FILTER --accept-nth=1 + ) [ -n "$profile" ] || main - fuzl_try -- tailscale switch "$profile" - sleep 2 - fuzl_try -- pkill -SIGRTMIN+2 waybar + ts_profile_switch "$profile" +} + +vpnm_up() { + fuzl_try -- tailscale up +} +vpnm_down() { + fuzl_try -- tailscale down } main |