# include this from .bashrc, .zshrc or # another shell startup file with: # source $HOME/.shellfishrc # # Running from Secure ShellFish # this gives access to: # openUrl to open pages or deeps links # pbcopy to copy text to iOS clipboard # pbpaste to paste from iOS clipboard # quicklook to preview files # runShortcut to run Shortcuts # setbarcolor to change toolbar color # sharesheet to invoke iOS share sheet # snip to add new snippets # textastic to edit files with Textastic # # In any terminal use: # thumbnail to write exif thumbnails # notify to post notifications # widget to change lock/home screen # widgets & apple watch complications # # # this part does nothing outside ShellFish if [[ "$LC_TERMINAL" = "ShellFish" ]]; then ios_printURIComponent() { awk 'BEGIN {while (y++ < 125) z[sprintf("%c", y)] = y while (y = substr(ARGV[1], ++j, 1)) q = y ~ /[a-zA-Z0-9]/ ? q y : q sprintf("%%%02X", z[y]) printf("%s", q)}' "$1" } ios_printBase64Component() { echo -n "$1" | base64 } which printf > /dev/null ios_hasPrintf=$? ios_printf() { if [ $ios_hasPrintf ]; then printf "$1" else awk "BEGIN {printf \"$1\"}" fi } ios_sequence() { if [[ -n "$TMUX" ]]; then OUTPUT=$( ios_printf '\033Ptmux;\033\033]' echo -n "$1" | tr -d '[:space:]' ios_printf '\a\033\\' ) else OUTPUT=$( ios_printf '\033]' echo -n "$1" | tr -d '[:space:]' ios_printf '\a' ) fi if [ -t 1 ] ; then echo -n $OUTPUT elif [[ -n "$SSH_TTY" ]]; then echo -n $OUTPUT > $SSH_TTY else echo >&2 'Standard output is not tty and there is no $SSH_TTY' fi } ios_sequence_spaced() { if [[ -n "$TMUX" ]]; then OUTPUT=$( ios_printf '\033Ptmux;\033\033]' echo -n "$1" ios_printf '\a\033\\' ) else OUTPUT=$( ios_printf '\033]' echo -n "$1" ios_printf '\a' ) fi if [ -t 1 ] ; then echo -n $OUTPUT elif [[ -n "$SSH_TTY" ]]; then echo -n $OUTPUT > $SSH_TTY else echo >&2 'Standard output is not tty and there is no $SSH_TTY' fi } # prepare fifo for communicating result back to shell ios_prepareResult() { FIFO=$(mktemp) rm -f $FIFO mkfifo $FIFO echo $FIFO } # wait for terminal to complete action ios_handleResult() { FIFO=$1 if [ -n "$FIFO" ]; then read <$FIFO -s rm -f $FIFO if [[ $REPLY = error* ]]; then echo "${REPLY#error=}" | base64 >&2 -d return 1 fi if [[ $REPLY = result* ]]; then echo "${REPLY#result=}" | base64 -d fi fi } sharesheet() { if [[ $# -eq 0 ]]; then if tty -s; then cat < Open in Textastic 9.5 or later. File must be in directory represented in the Files app to allow writing back edits. EOF else if [ ! -e "$1" ]; then touch "$1" fi OUTPUT=$( awk 'BEGIN {printf "6;textastic://?ver=2&pwd="}' ios_printBase64Component "$PWD" awk 'BEGIN {printf "&home="}' ios_printBase64Component "$HOME" awk 'BEGIN {printf "&path="}' ios_printBase64Component "$1" ) ios_sequence "$OUTPUT" fi } setbarcolor() { if [[ $# -eq 0 ]]; then cat < Set color of terminal toolbar color with values such as red, #f00, #ff0000, rgb(255,0,0), color(p3 1.0 0.0 0.0) EOF else OUTPUT=$( awk 'BEGIN {printf "6;settoolbar://?ver=2&color="}' ios_printBase64Component "$1" ) ios_sequence "$OUTPUT" fi } openUrl() { if [[ $# -eq 0 ]]; then cat < Open URL on iOS. EOF else FIFO=$(ios_prepareResult) OUTPUT=$( awk 'BEGIN {printf "6;open://?ver=2&respond="}' ios_printBase64Component "$FIFO" awk 'BEGIN {printf "&url="}' ios_printBase64Component "$1" ) ios_sequence "$OUTPUT" ios_handleResult "$FIFO" fi } runShortcut() { local baseUrl="shortcuts://run-shortcut" if [[ $1 == "--x-callback" ]]; then local baseUrl="shortcuts://x-callback-url/run-shortcut" shift fi if [[ $# -eq 0 ]]; then cat < [input-for-shortcut] Run in Shortcuts app bringing back results if --x-callback is included. EOF else local name=$(ios_printURIComponent "$1") shift if [[ $* == "-" ]]; then local text=$(cat -) local input=$(ios_printURIComponent "$text") else local input=$(ios_printURIComponent "$*") fi openUrl "$baseUrl?name=$name&input=$input" fi } # copy standard input or arguments to iOS clipboard pbcopy() { OUTPUT=$( awk 'BEGIN {printf "52;c;"} ' if [ $# -eq 0 ]; then base64 | tr -d '\n' else echo -n "$@" | base64 | tr -d '\n' fi ) ios_sequence "$OUTPUT" } # paste from iOS device clipboard to standard output pbpaste() { FIFO=$(ios_prepareResult) OUTPUT=$( awk 'BEGIN {printf "6;pbpaste://?ver=2&respond="}' ios_printBase64Component "$FIFO" ) ios_sequence "$OUTPUT" ios_handleResult "$FIFO" } # create new snippets snip() { if [[ $# -eq 0 ]]; then cat < EOF else OUTPUT=$( awk 'BEGIN {printf "6;addsnippet://?ver=2&text="}' ios_printBase64Component "$*" ) ios_sequence "$OUTPUT" fi } # Secure ShellFish supports 24-bit colors export COLORTERM=truecolor # We need to pass through escape sequences through tmux if [[ -n "$TMUX" ]]; then # ignore error from old versions of tmux without this command tmux 2> /dev/null set -g allow-passthrough on || true fi if [[ -z "$INSIDE_EMACS" && $- = *i* ]]; then # tmux mouse mode enables scrolling with # swipe and mouse wheel if [[ -n "$TMUX" ]]; then tmux set -g mouse on fi # send the current directory using OSC 7 when showing prompt to # make filename detection work better for interactive shell update_terminal_cwd() { ios_sequence $( awk "BEGIN {printf \"7;%s\", \"file://$HOSTNAME\"}" ios_printURIComponent "$PWD" ) } if [ -n "$ZSH_VERSION" ]; then precmd() { update_terminal_cwd; } elif [[ $PROMPT_COMMAND != *"update_terminal_cwd"* ]]; then PROMPT_COMMAND="update_terminal_cwd${PROMPT_COMMAND:+; $PROMPT_COMMAND}" fi fi fi # this part works in any context thumbnail() { if [[ $# -eq 0 ]]; then cat < [image-file-2] ... Add Exif thumnails to image files using ImageMagick convert and exiftool. EOF else # make sure ImageMagick and exiftool are available convert -version 1>/dev/null 2>/dev/null || { echo "ImageMagick convert needs to be installed"; return 1; } exiftool -v 1>/dev/null 2>/dev/null || { echo "exiftool needs to be installed"; return 1; } THUMBNAIL=/tmp/thumbnail.jpg for arg in "$@" do echo "$arg" convert "$arg" -thumbnail 160x120^ "$THUMBNAIL" exiftool -q -overwrite_original "-thumbnailimage<=$THUMBNAIL" "$arg" rm -f "$THUMBNAIL" done fi } # Updates Terminal Data widget in Secure ShellFish # # This command sends encrypted data through push notifications such # that it doesn't need to run from a Secure ShellFish terminal. widget() { if [[ $# -eq 0 ]]; then cat < ... Update widget on device from which this function was installed with a number of content parameters that can be string, progress, icon, target, color, url or shortcut. Each argument type is derived from input, where the first argument is assumed to be a target if it matches a target configured on the widget. Progress has the form: 50% or 110/220 Icon must match valid SF Symbol name such as globe or terminal.fill Colors must be hex colours such as #000 #ff00ff where the color is used for later content and 'foreground' switches back to default colour Target is used to send different content to different widgets after configuring the widgets with different target identifiers which requires the pro unlock. The target parameter is never assumed unless --target is used and is effective until next --target parameter allowing updates of several widgets with a single command URL is used when tapping the widget and is assumed for arguments starting with https:// and other schemes are supported by using --url Shortcut works like URL running the Shortcut with the given name and is never assumed without --shortcut String is the fallback type if nothing else matches, but content type can be forced for next parameter with --progress, --icon, --color, --text or --target with something like: widget --text "50/100" You can update several widgets at once by using --target to send all parameters until the next --target to a particular widget. Updating several widgets at once allows more total updates per day. EOF return 0 fi local key=ab5d8c58fd13a6a2536504580a527d682fe9f5560aa57298b4a4135ef5ed6987 local user=jBkLbEYRBy33rdItwbMJq0WHYwYoMy2Zf1gBCfVj local iv=ab5bbeb426015da7eedcee8bee3dffb7 local plain=$( echo Secure ShellFish Widget 2.0 for var in "$@" do echo -ne "$var" | base64 done) local base64=$(echo "$plain" | openssl enc -aes-256-cbc -base64 -K $key -iv $iv) curl -sS -X POST -H "Content-Type: text/plain" --data "$base64" "https://secureshellfish.app/push/?user=$user" } # Shows notification on your device with Secure ShellFish installed # optionally opening URL or running Shortcut when notification is # opened. # # This command sends encrypted data through push notifications such # that it doesn't need to run from a Secure ShellFish terminal. notify() { if [[ $# -eq 0 ]]; then cat < ... EOF return 0 fi local key=ab5d8c58fd13a6a2536504580a527d682fe9f5560aa57298b4a4135ef5ed6987 local user=jBkLbEYRBy33rdItwbMJq0WHYwYoMy2Zf1gBCfVj local iv=ab5bbeb426015da7eedcee8bee3dffb7 local plain=$( echo Secure ShellFish Notify 2.0 for var in "$@" do echo -ne "$var" | base64 done) local base64=$(echo "$plain" | openssl enc -aes-256-cbc -base64 -K $key -iv $iv) curl -sS -X POST -H "Content-Type: text/plain" --data "$base64" "https://secureshellfish.app/push/?user=$user&mutable" }