where * refers to applications(or programs), executing commands/scripts, clipboard entries, copying and typing out long/specific/repetitive things, opening files, playing media, bookmarks, searching online, with default and specified search engine with “bangs”, calculations and conversions, and much more
23 June, 2024
Disclaimer :- This is my first article, and I don’t really know how to write, please forgive me for poor structuring or style or content, please help yourself with Table of contents
It is essentially a “dmenu” like approach to launch/perform various things. My idea is to “package” it neatly such that it is easy to extend, and easy to use, be flexible in what programs you choose to perform various things, and not require much knowledge of programming to extend
Yes it has been done many times, even my aim was to mimic
krunner
, basically all “twm users” do it, because these
menus are easy and fast, and keyboard driven, my idea is not to replace
anything, heck, I would not even call this a program, it is a “script”,
it is just easy to use, and just a template where others can mold it
there way.
I have written the launcher script in Posix Shellscript
.
Honestly speaking I have not checked the Posix spec, I have made it such
that it works with dash
(my /bin/sh
). I only
know Python, shellscripting, C, and Java, and between python and
shellscripts, I chose shellscript because it was faster (I have not
included benchmarks, but for certain sections, the difference was nearly
20-50 times)
You can find the scripts here. The idea is setup/operating system agnostic, but my scripts are not, so you may have to make changes.
I am including the launcher script here because i am going to reference it a lot
#!/usr/bin/env sh
llamadir="$HOME/data/local-installations/llama.cpp"
modelsdir="$HOME/data/local-installations/models"
savedir="$HOME/data/save"
tmpoutput=$(mktemp --dry-run)
payload="$(cliphist list | head -n15 | sed 's/^/clipboard\t/' ; \
cat -n "$savedir"/notes | sed 's/^/note\t/' ; \
printf "note\tadd as a note\nunnote\tremove nth note\nexecb\texecute in background\nsave web\tnormal bookmark\nsave webp\tpublic bookmark\nsave contact\tsome number or email with a description\nsave exec\tutable command\nsave command\tsome shell command\nsave random\tstuff to be typed but general\nimport\tbookmarks from html file\nrssadd\nytsub\tsubscribe youtube channel\ncompress\ta file\nyts\tsearch youtube and play video\nplay\tsearch youtube and play audio\nmpv\tplay video\nmpva\tplay audio only\nytdl\tdownload video from youtube\nytdla\tdownload audio from youtube\nexpand\ta shortened url\nupload\ta file and get a linkqr\tgenerate qr with input text\ndownload\tsome link with aria2c\nmdhtml\tconvert md file to html with pandoc\ncalc\tdo calculations with fend and convert units ctof ftoc oC oF\naddtime\tadd time in human formats expects [h:]m:]s, example: tsum 04:20 39:42 1:23:45\nsxpkg\tsearch file in arch packages\nddg\tusing duck duck go to search packages\nwiki\tsearch wik(ipedia or tionary)\n" ; \
sed '/^$/d' "$savedir"/web "$savedir"/web-public | sed 's/^/web\t/' ; \
sed '/^$/d' "$savedir"/contact-details | sed 's/^/type\t/' | sed "s|$|\tcontact-details|" ; \
for i in "$savedir"/snippets-autocomplete/* ; do sed '/^$/d' "$i" | sed 's/^/type\t/' | sed "s|$|\t$(basename ${i})|" ; done ; \
sed '/^$/d' "$savedir"/exec | sed 's/^/exec\t/' ; \
find "$HOME"/games/linux/ -maxdepth 2 -type f -executable | sed 's/^/exec\t/' ; \
sed '/^$/d' "$savedir"/execcopy | sed 's/^/execcopy\t/' ; \
sed '/^$/d' "$savedir"/execsu | sed 's/^/execsu\t/' ; \
sed '/^$/d' "$savedir"/execterm | sed 's/^/execterm\t/' ; \
sed '/^$/d' "$savedir"/exectype | sed 's/^/exectype\t/' ; \
find "$HOME"/.password-store/ -name "*.gpg" -type f -printf "%P\n" | sed 's/^/pass\t/' ; \
find "$HOME"/gallery/memes/reactions/ -type f | sed 's/^/drag-file\t/' ; \
find "$modelsdir" -maxdepth 1 -name "*.gguf" -type f -printf "%f\n" | sed 's/^/gguf\t/' ; \
find "$HOME"/games/windows/ -maxdepth 3 -type f -name '*.exe' | grep -v 'unins\|UnityCrashHandler\|dxwebsetup\|vcredist' | sed 's/^/wine\t/' ; \
find "$HOME"/games/gameboys/ -type f -name '*.gba' | sed 's/^/gba\t/' ; \
(find "$savedir" "$HOME"/music "$HOME"/.local/bin/scripts "$HOME"/study/books "$HOME"/study/admin -type f -name "*" && find "$HOME"/.config/ -maxdepth 2 -type f -name "*" && sed '/^$/d' "$savedir"/files) | sed 's/^/file\t/' ; \
ls /usr/share/applications | sed 's/^/application\t/')"
# in most of the script, i am using printf "%s" instead of echo or echo -E, because in some of my bookmarks, i have escape codes, or such thing, which break and printf "%s" seems to preserve it the best
selection=$(printf "%s" "$payload" | term-dmenu)
# selection=$(printf "%s" "$payload" | tofi)
# selection=$(printf "%s" "$payload" | fuzzel --dmenu --no-icons --background-color=000000ff --text-color=ddddddff --width 60 --match-color=1690dfff --selection-color=111111ff --selection-text-color=ffffffff --selection-match-color=1690dfff --match-mode=exact --font="DejaVu Sans Mono 10")
if [ -z "${selection}" ]; then exit ; fi
tsum() { # https://ocv.me/doc/unix/oneliners/
local p='s/0?([0-9]+):0?([0-9]+)/\1*60+\2/'
local ts=0 h=0 m=0 s=0
for orig in "$@"; do
s=$(echo "$orig" | sed -r "$p" | bc)
ts=$((ts + s))
h=$((ts / (60 * 60)))
s=$((ts - h * 60 * 60))
m=$((s / 60))
s=$((s - m * 60))
printf '%7s %d:%02d:%02d %d\n' "$orig" $h $m $s $ts
done
}
# uncomment the printf statements for debugging
identifier=$(printf "%s" "$selection" | cut -f 1) # && printf "%s" "$identifier" && printf "\n"
selected_option=$(printf "%s" "$selection" | cut -f 2) # && printf "%s" "$selected_option" && printf "\n"
# Perform actions based on the identifier
case "$identifier" in
"application") gtk-launch "$(echo "$selection" | awk 'NF>1{print $NF}')" ;; # instead of using gtk-launch, you can also use exec, and include the exec line in application cache
"type") wtype "$selected_option" ;; # stuff to be auto-typed anywhere you want (currently using to write long commands)
"wine") cd "$(dirname "$selected_option")" && SDL_VIDEODRIVER=windows wine "$selected_option" ;;
"drag-file") dragon-drop "$selected_option" ;;
"exec") output="$(eval "$selected_option" 2>&1)"
notify-send "Command executed and its output" "$selected_option\n------\nOutput\n------\n$output" ;;
"execsu") output="$(pass local/falls | head -n 1 | sudo -SE bash -c "$selected_option" 2>&1)"
notify-send "Command executed and its output" "$selected_option\n------\nOutput\n------\n$output" ;;
"execcopy") eval "$selected_option" | wl-copy --trim-newline ;;
"exectype") eval "$selected_option" | wtype - ;;
"execterm") footclient -e bash -c "$selected_option" ;;
"clipboard") printf "%s" "$selection" | cut -f 2- | cliphist decode | wl-copy --trim-newline ;;
"web") qutebrowser "$selected_option" ;; # replace qutebrowser, with default browser, or your preferred browser
"edit") editor-terminal "$selected_option" ;;
"file") xdg-open "$selected_option" ;;
"pass") action=$(printf "view\nsave\ncopy otp\ncopy login id\ncopy password\nedit" | term-dmenu)
file="${selected_option%.gpg}"
case "$action" in
"view") tmpoutput=$(mktemp --dry-run) && pass show "$file" >| "$tmpoutput" && xdg-open "$tmpoutput" && \rm -rf "$tmpoutput";;
# "save") pass show "$file" >| "$HOME/downloads/$(basename $file)" ;;
"copy otp") pass otp "$file" | tr -d '\n' | wl-copy ;;
"copy login id") pass "$file" | head -2 | tail -1 | tr -d '\n' | cut -d' ' -f 2- | wl-copy ;;
"copy password") pass "$file" | head -1 | tr -d '\n' | wl-copy ;;
"edit") footclient -e pass edit "$file" ;;
esac ;;
"gguf") # prefix="Your are a smart and efficient assistant, give answers in a structured format, and try to keep your replies as short as possible"
# prompt="remember to be precise and correct always, and no emojis"
footclient -e bash -c "cd /tmp && $llamadir/build/bin/llama-cli -m $modelsdir/$selected_option --multiline-input --temp 0.6 --ctx-size 4096 --conversation" ;;
*) # these are used when no matches happen (if anything matches, then match is returned, if not what was typed), we input the identifier ourselves, and the remaining becomes the selected_option_1, for example search the selected_option_1
# can be used as a way to easily use ddg "bangs", but more flexible, and browser/search engine agnostic
identifier_1="$(printf "%s" "$selection" | cut -d' ' -f 1)" # && printf "%s" "$identifier_1" && printf "\n"
selected_option_1="$(printf "%s" "$selection" | cut -d' ' -f 2-)" # && printf "%s" "$selected_option_1" && printf "\n"
wl-copy "$selected_option_1" # copying the selection, helps in rerunning the command (helpful in debugging, or for example with calculator or music, where you slightly messed it up)
case "$identifier_1" in
"unnote") del=$(($selected_option_1)) && sed -i "${del}d" "$savedir"/notes ;;
"note") (printf '%s' "- $selected_option_1" && printf "\n") >> "$savedir"/notes ;;
"execb") eval "$selected_option_1" ;;
"save") category="$(printf "%s" "$selected_option_1" | cut -d' ' -f 1)"
tobesaved="$(printf "%s" "$selected_option_1" | cut -d' ' -f 2)"
description=""
if [ "$(printf "%s" "$selected_option_1" | wc -w)" -gt 2 ]; then description="$(printf "%s" "$selected_option_1" | cut -d' ' -f 3-)" ; fi
sniptobesaved="$(printf "%s" "$selected_option_1" | cut -d' ' -f 2-)"
case "$category" in
"web") title="$(curl -s "$url" | grep -oP '(?<=<title>).*?(?=</title>)')"
printf "$tobesaved\t$title $description\n" >> "$savedir"/web
notify-send "Bookmark added $tobesaved" "$title $description" ;;
"webp") title="$(curl -s "$tobesaved" | grep -oP '(?<=<title>).*?(?=</title>)')"
printf "$tobesaved\t$title $description\n" >> "$savedir"/web-public
notify-send "Bookmark added $tobesaved" "$title $description" ;;
"contact") printf "$tobesaved\t$description\n" >> "$savedir"/contact-details
notify-send "Bookmark added $tobesaved" "$description" ;;
"file") printf "$tobesaved\t$description\n" >> "$savedir"/files
notify-send "Bookmark added $tobesaved" "$description" ;;
"exec") printf "$sniptobesaved\n" >> "$savedir"/exec
notify-send "Bookmark added $savedir/exec" "$sniptobesaved" ;;
"command") printf "$sniptobesaved\n" >> "$savedir"/snippets-autocomplete/commands-and-snippets
notify-send "Bookmark added to $savedir/snippets-autocomplete/commands-and-snippets" "$sniptobesaved" ;;
"random") printf "$sniptobesaved\n" >> "$savedir"/snippets-autocomplete/random-stuff
notify-send "Bookmark added to $savedir/snippets-autocomplete/random-stuff" "$sniptobesaved" ;;
*) notify-send "Usage of save is changed" "do save [web/webp/contact/exec/command/random]"
esac ;;
"import") grep -o '<DT><A HREF="[^"]\+"[^>]\+>[^<]\+</A>' "$selected_option_1" |
sed 's/<DT><A HREF="\([^"]\+\)"[^>]*>\([^<]\+\)<\/A>/web\t\1\t\2/' >> "$savedir"/web.txt ;;
"rssadd") name="$( curl -s $selected_option_1 | grep -oP '(?<=<title>).*?(?=</title>)' | head -n1 )"
printf "$selected_option_1 \"$name\"\n" >> "$HOME"/.config/newsraft/feeds
notify-send "Subscribed to rss feed $name" "$selected_option_1";;
"ytsub") channel_id="$(printf "%s" "$selected_option_1" | grep -oP '(?<=channel/)\K.*')"
name="$( curl -s https://www.youtube.com/channel/$channel_id | grep -oP '(?<=<title>).*?(?=</title>)' | sed -z 's/\ \-\ YouTube//g')"
printf "https://www.youtube.com/feeds/videos.xml?channel_id=$channel_id \"$name\"\n" >> "$HOME"/.config/newsraft/feeds
notify-send "Subscribed to $name" "$channel_id";;
"compress") "$HOME"/.local/bin/compress "$selected_option_1" ;;
"yts") footclient --app-id floating-terminal -e ytfzf --detach --notify-playing --force-youtube "$selected_option_1" ;;
"play") footclient --app-id floating-terminal -e ytfzf --detach --notify-playing --force-youtube -m "$selected_option_1" ;;
"mpv") mpv "$selected_option_1" ;;
"mpva") mpv --no-video --player-operation-mode=pseudo-gui --speed=1 "$selected_option_1" ;;
"ytdl") yt-dlp "$selected_option_1" && notify-send "Download Completed" "$selected_option_1" ;;
"ytdla") yt-dlp -x "$selected_option_1" && notify-send "Download Completed" "$selected_option_1" ;;
"expand") url="$(curl -vL "$selected_option_1" 2>&1 | grep "location: " | grep -v "onion" | cut -d':' -f2-)"
wl-copy "$url" && notify-send "Url expands to $url" "Url copied to clipboard" ;;
"upload") url=$(curl -F'file=@'"$selected_option_1" -Fsecret= "https://envs.sh/")
wl-copy "$url" && notify-send "File uploaded to $url" "Url copied to clipboard" ;;
"qrencode") printf '%s' "$selected_option_1" | qrencode -o "$tmpoutput"
xdg-open "$tmpoutput" ;;
"download") aria2c "$selected_option_1" ;;
"rename") rename-bib.sh "$selected_option_1" ;;
"mdhtml") input_file_name="${selected_option_1%.*}" && pandoc -s --css="style.css" --highlight-style=haddock -f markdown -t html5 -o "$input_file_name".html "$selected_option_1" ;;
"teams") teams-for-linux --url "$selected_option_1" ;;
"calc") result=$(fend "$selected_option_1")
wl-copy "$result" && notify-send "$selected_option_1 = $result" "Result copied to clipboard" ;;
"addtime") result="$(tsum $selected_option_1)" # expects [h:]m:]s, example: tsum 04:20 39:42 1:23:45
wl-copy "$result" && notify-send "$selected_option_1" "$result \n\n Result copied to clipboard" ;;
"ddg") qutebrowser https://html.duckduckgo.com/html?q="$selected_option_1" ;;
"sxpkg") qutebrowser http://127.0.0.1:8888/search?q="site:archlinux.org/packages/ bin/$selected_option_1" ;; # searching a file in arch packages
"wiki") qutebrowser https://wikipedia.org/wiki/Special:Search?search="$selected_option_1" ;;
*) if [ -n "$selection" ]; then qutebrowser http://127.0.0.1:8888/search?q="$selection" ; fi ;; # for some weird reason, i can not use the qutebrowser --search flag, it works, but it will always open a new window, something i do not like, so have to hard code the search engine (--new-tab does not work)
esac ;;
esac
cliphist
to get my clipboard entries, quick
access to clipboard is great for stupidly copying to things, I am even
using it save some results (search queries, links to “media” being
played,calculation results, etc)update-application-database
which finds all
.desktop
files in the usual locations and prepares a cache,
many DEs even do so, since finding desktop files each time is a bit more
expensive (script takes twice as long if I search it each time). We
launch it with gtk-launch
, you can change script, and
include exec
line, and use that to launch stuff. If you can
somehow set it, set it to execute it each time there is a
package-manager
event (called something like post install
scripts). Here is a sample of how it looksApplication About Xfce Information about the Xfce Desktop Environment xfce4-about.desktop
bookmarks
this is the everything file, if you want, you
can make one for each category, or just separate the web bookmarks, here
is the structureIdentifier \t Entry \t (Comments or Tags(optional))
Identifier
and
Entry
. All examples for each kind are in this format. If
you want to use something else, then change it, just update the
cut -f
lines in script with some other delimiter
-d' '
Files
to be opened with xdg-open
, I have
put some of the common config files, or my notes file, the following
line is very similar, where I include all the files in certain dirs,
like music (so the launcher is interface to my local media)
find $HOME/Music $HOME/Videos/Movies $HOME/.local/bin -name "*" | while read -r line ; do printf "File\t$line\n" ; done
Copy
to copy some long stuff, or stuff you copy often,
like mail id, or lorem-ipsum
Copy Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Dummy text
Type
is to type out long stuff, I am now using it for
long commands, that I earlier was using
aliases, cheatsheets, navi
for, and there is no major
enhancement, other than the fact that it is almost as fast, but within
one same menu. I am using wtype to type out stuff, ydotool had more
options, but breaks in printing some stuff. Here is a command to print a
fancy gradient rainbow with awk
Type awk 'BEGIN{s="________________________________";s=s s s s s s s s;for(colnum=0;colnum<77;colnum++){r=255-(colnum*255/76);g=(colnum*510/76);b=(colnum*255/76);if(g>255)g=510-g;printf "\033[48;2;%d;%d;%dm",r,g,b;printf "\033[38;2;%d;%d;%dm",255-r,255-g,255-b;printf "%s\033[0m",substr(s,colnum+1,1);}printf "\n";}' rainbow text with awk terminal, check color capacity of terminal
Exec
to execute any command, this is the singe best
thing, and the reason why I have 20-30 less keyboard shortcuts in my
sway config, I use it toggle my bluetooth connection, change vpns, clean
system, because I have made scripts for them or included relevant
commands, or launching windows games with long wine params. Here is
example command to clear clipboard (useful if the command you want to
execute is also in your clipboard). This has subset
ExecCopy
(an example to copy the colour code) and
ExecType
(I use this and kpxcpc
to somewhat
safely (without copying to/from clipboard) type long passwords from
KeepassXC
outside browser)
Exec cliphist wipe Clear Clipboard
ExecCopy hyprpicker Color Picker
Web
for your browser bookmarks. Now you need not give
up having bookmarks in your browser, you can keep your bookmarks, just
export to html, and convert to required format with
parse-bookmarks-file
, but I moved all bookmarks, and i do
not want to move back 600+
lines back, the benefit of using
it is much better search algorithm, and much easier to share some
sections of bookmarks with others (if you have categorized then
correctly, with some sed
magic) (but you dont have to move
your bookmarks for this). A great video if you would like an intro to
using sed
Web https://www.youtube.com/watch?v=dQw4w9WgXcQ
term-dmenu
(adapted from Seirdy/term-dmenu,
changes include hard coding the use of footclient as terminal, and
ensuring input stays intact), which is basically fzf wrapped in a
floating terminal window
tofi
, it is great and fast, but less
customisability (specifically no way to ellipsise the middle, since I
want to see the comments)rofi
is great with that, but not great (compared to
fzf
) searching algorithmfzf
is great, but way too flexible, but you can use
' or ! or * or .
to get great refinement, and especially
useful for later section of script where we have essentially no matches
and perform actions based on inputcases
to perform actionsytfzf, yt-dlp, and mpv
(I earlier used only yt-dlp to
perform search, but ytfzf is plain better and easier) orqalc
(I earlier used
bc
and python
, bc is worse, and python is much
more flexible, but harder to use in this setup) orposix
cliphist
and wlcip
gtk-launch
browser
libnotify
wtype
(or xdotool or ydotool (breaks in some cases with
certain commands with escape codes))ytfzf, yt-dlp, mpv
qalc
(or dc/dc or python)