# include this from .bashrc, .zshrc or # another shell startup file with: # source $HOME/.shellfishrc # this script 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 } # 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 <<EOF Usage: sharesheet [FILE]... Present share sheet for files and directories. Alternatively you can pipe in text and call it without arguments. If arguments exist inside the Files app changes made are written back to the server. EOF return 0 fi fi FIFO=$(ios_prepareResult) OUTPUT=$( awk 'BEGIN {printf "6;sharesheet://?ver=2&respond="}' ios_printBase64Component "$FIFO" awk 'BEGIN {printf "&pwd="}' ios_printBase64Component "$PWD" awk 'BEGIN {printf "&home="}' ios_printBase64Component "$HOME" for var in "$@" do awk 'BEGIN {printf "&path="}' ios_printBase64Component "$var" done if [[ $# -eq 0 ]]; then text=$(cat -) awk 'BEGIN {printf "&text="}' ios_printBase64Component "$text" fi ) ios_sequence "$OUTPUT" ios_handleResult "$FIFO" } quicklook() { if [[ $# -eq 0 ]]; then if tty -s; then cat <<EOF Usage: quicklook [FILE]... Show QuickLook preview for files and directories. Alternatively you can pipe in text and call it without arguments. EOF return 0 fi fi FIFO=$(ios_prepareResult) OUTPUT=$( awk 'BEGIN {printf "6;quicklook://?ver=2&respond="}' ios_printBase64Component "$FIFO" awk 'BEGIN {printf "&pwd="}' ios_printBase64Component "$PWD" awk 'BEGIN {printf "&home="}' ios_printBase64Component "$HOME" for var in "$@" do awk 'BEGIN {printf "&path="}' ios_printBase64Component "$var" done if [[ $# -eq 0 ]]; then text=$(cat -) awk 'BEGIN {printf "&text="}' ios_printBase64Component "$text" fi ) ios_sequence "$OUTPUT" ios_handleResult "$FIFO" } textastic() { if [[ $# -eq 0 ]]; then cat <<EOF Usage: textastic <text-file> 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 } openUrl() { if [[ $# -eq 0 ]]; then cat <<EOF Usage: openUrl <url> 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 <<EOF Usage: runShortcut [--x-callback] <shortcut-name> [input-for-shortcut] Run in Shortcuts app bringing back results if --x-callback is included. EOF else local name=$(ios_printURIComponent "$1") shift local input=$(ios_printURIComponent "$*") openUrl "$baseUrl?name=$name&input=$input" fi } notify() { if [[ $# -eq 0 ]]; then cat <<EOF Usage: notify <title> [body] Show notification on iOS device. Title cannot contain semicolon. EOF else local title="${1-}" body="${2-}" OUTPUT=$( echo $title | awk -F";" 'BEGIN {printf "777;notify;"} {printf "%s;", $1}' echo $body ) ios_sequence "$OUTPUT" 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" } # Secure ShellFish supports 24-bit colors export COLORTERM=truecolor 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