Activate Normal CreationDate 459387347.72605699 IsActive Macros Actions IsActive IsDisclosed MacroActionType Comment Text If you want to change where the temporary file is stored, edit the EDIT_ANYWHERE_FILE variable. Note that the filename (and path, if any) will be relative to your $HOME. So, for example, if you want to use a file named ~/Dropbox/txt/my-very-special-name-here.txt then you should set the variable to "Dropbox/txt/my-very-special-name-here.txt" Do NOT include the /Users/YourNameHere/ part of the path, that will be added automatically later. Do not change the variable name (EDIT_ANYWHERE_FILE) just change the part in the "To:" field. Title Note: You can customize 'EDIT_ANYWHERE_FILE' IsActive IsDisclosed MacroActionType SetVariableToText Text edit_anywhere.txt Variable EDIT_ANYWHERE_FILE ActionName Store to the name of the active application to the variable ‘EditAnywhereApp’ IsActive IsDisclosed MacroActionType SetVariableToText Text %Application%1% Variable EditAnywhereApp ActionName Type the ⌘A Keystroke (This should trigger 'Select All Text') IsActive IsDisclosed KeyCode 0 MacroActionType SimulateKeystroke Modifiers 256 ReleaseAll ActionName Type the ⌘A Keystroke (This should trigger 'Select All Text') IsActive IsDisclosed KeyCode 0 MacroActionType SimulateKeystroke Modifiers 256 ReleaseAll IsActive IsDisclosed MacroActionType Pause Time 1 TimeOutAbortsMacro IsActive IsDisclosed KeyCode 7 MacroActionType SimulateKeystroke Modifiers 256 ReleaseAll Action ByTyping IsActive IsDisclosed MacroActionType InsertText Paste Text (Waiting for EditAnywhere) ActionName Type the ⌘A Keystroke (This should trigger 'Select All Text') IsActive IsDisclosed KeyCode 0 MacroActionType SimulateKeystroke Modifiers 256 ReleaseAll IsActive IsDisclosed MacroActionType Pause Time .5 TimeOutAbortsMacro ActionName This is where the magic happens DisplayKind None IncludeStdErr IsActive IsDisclosed MacroActionType ExecuteShellScript Path /usr/local/scripts/km-edit-anywhere-any-editor.sh Text #!/bin/zsh -f ## This is the app that you want to use as your external editor ## Note that it should not include the '.app' suffix ## ## Examples: ## APP='BBEdit' ## APP='TextWrangler' ## APP='MultiMarkdown Composer' ## APP='BBEdit' ## OS X no longer has a good way to ensure that GUI apps like ## Keyboard Maestro have access to your $PATH so I often just create ## a text file in my $HOME which I can use in all my scripts. This ## checks to see if that file exists, and if it does not exist, it ## will use the PATH as defined in the second half of the if/else if [[ -e "$HOME/.path" ]] then source "$HOME/.path" else PATH=/usr/local/scripts:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin fi ####|####|####|####|####|####|####|####|####|####|####|####|####|####|#### # # You should not ~need~ to edit anything below this line # ###################################################################### ## This is the name of the temporary file which will be used to keep ## your edits before it is sent back to the app where it came from. ## ## This is now based on a variable set in the Keyboard Maestro macro ## ## If, for some reason, the filename is NOT set, use a reasonable default [[ "$KMVAR_EDIT_ANYWHERE_FILE" == "" ]] && KMVAR_EDIT_ANYWHERE_FILE='edit_anywhere.txt' ## Try to protect against someone putting '..' in the file path and ## attempting to get out of the $HOME I don't know if this is ## actually necessary, but once it occurred to me, I figured I might ## as well add it. FILE=`echo "${HOME}/${KMVAR_EDIT_ANYWHERE_FILE}" | tr -s '.' '.'` NAME="[Edit Anywhere via Keyboard Maestro]" ## Change this if necessary ## Used for pbcopy and pbpaste LANG=en_US.UTF-8 zmodload zsh/datetime ## needed for strftime function timestamp { strftime "%Y-%m-%d--%H.%M.%S" "$EPOCHSECONDS" } # This gives us a unique reference point for when the script was launched TIME=`strftime "%Y-%m-%d--%H.%M.%S" "$EPOCHSECONDS"` if (( $+commands[growlnotify] )) then ## We found the growlnotify tool installed GROWLNOTIFY_FOUND='yes' ## We only check to see if Growl.app is running IFF we have ## found growlnotify because otherwise there's no point caring. if [ "`pgrep -x Growl`" = "" ] then ## Growl is NOT running GROWL_RUNNING='no' else ## Growl IS running GROWL_RUNNING='yes' fi else ## We did NOT find the growlnotify tool installed GROWLNOTIFY_FOUND='no' ## We just set this to 'no' because it doesn't matter, if the ## growlnotify tool isn't found too. GROWL_RUNNING='no' fi if [ "$GROWL_RUNNING" = "yes" -a "$GROWLNOTIFY_FOUND" = "yes" ] then ## If growlnotify is installed AND Growl.app is running, use Growl for notifications function msg { growlnotify \ --appIcon "$APP" \ --identifier "$NAME" \ --message "$@" \ --title "$NAME" } ## end of msg/growl elif (( $+commands[terminal-notifier] )) then ## OTHERWISE, use terminal-notifier for notifications, if it is installed. function msg { if [[ -e "/Applications/$APP.app/Contents/Info.plist" ]] then ## If there is an app plist where we'd expect it to be, try to get the Bundle identifier from it TN_BUNDLE_ID=`defaults read /Applications/${APP}.app/Contents/Info CFBundleIdentifier` fi ## If we were not able to get a bundle ID, use Keyboard Maestro's [[ "$TN_BUNDLE_ID" == "" ]] && TN_BUNDLE_ID='com.stairways.keyboardmaestro.editor' terminal-notifier \ -title "$NAME" \ -subtitle "`timestamp`" \ -group "$NAME" \ -sender "$TN_BUNDLE_ID" \ -activate "$TN_BUNDLE_ID" \ -message "$@" } ## end of msg/terminal-notifier else function msg { echo "$NAME: $@" | tee -a "${TMPDIR-/tmp/}${NAME}.$TIME.log" } fi ########################################################################################### ########################################################################################### ## ## Here is where the action begins ## ## What do we do if they asked for a path which does not exist? ## Well, we will try to create the folder and then paste the file in there PARENT_DIR="$FILE:h" [[ ! -d "$PARENT_DIR" ]] && mkdir -p "$PARENT_DIR" && msg "Created $PARENT_DIR for $FILE" if [[ -e "$FILE" ]] then pbpaste >> "$FILE" else pbpaste > "$FILE" fi EXIT="$?" if [ "$EXIT" != "0" ] then msg "FATAL ERROR: Failed to create file at $FILE. Exit = $EXIT. Sorry." ## if we can't create the file, send the contents of the ## pasteboard to a temp file in TextEdit at least so the user ## will not lose whatever they had there at least /usr/bin/pbpaste | /usr/bin/open -e -f exit 1 fi if [[ "$KMVAR_EditAnywhereApp" == "" ]] then ## "$KMVAR_EditAnywhereApp" is not set. ABORT. msg "Variable KMVAR_EditAnywhereApp is empty. Giving up." ## Open the file in the default text editor ## so the user knows their text has not been lost /usr/bin/open -t "$FILE" exit 1 else ## Tell the user what app we took text from ## This is mostly a debugging comment and could be removed if it annoys you msg "Will send text back to: $KMVAR_EditAnywhereApp" fi if (( $+commands[bbedit] )) then ## We found the command line bbedit tool CL_BBEDIT='yes' else ## We did NOT find the command line bbedit tool CL_BBEDIT='no' fi if [[ "$CL_BBEDIT" = "yes" && "$APP:l" == "bbedit" ]] then ## IFF the user wants to use BBEdit and they have the bbedit ## command line tool installed, then we will use the bbedit tool ## if BBEdit is not running, launch it pgrep -xq BBEdit || (open -a BBEdit && sleep 3) ## We will try to open the app with the bbedit command, ## but if it fails we will open TextEdit bbedit \ --language 'Markdown' \ --create-unix \ --new-window \ --resume \ --wait \ -- "$FILE" \ || open -n -e -W "$FILE" else ## if we get here, either the app is NOT BBEdit ## or they do not have the `bbedit` tool installed ## We will try to open the file in the requested app ## but if the open command fails for some reason, ## we will open TextEdit open -a "$APP" -n -W "$FILE" || open -a -n -e -W "$FILE" fi # if app is BBEdit EXIT="$?" if [ "$EXIT" != "0" ] then ## If we get here, something went wrong msg "Unable to open $FILE in $APP." ## Show the file in the Finder, so the user can try to do something with it /usr/bin/open -R "$FILE" exit 1 fi ## If we get here, `open` or `bbedit` exited successfully ## Make sure that the app we want to paste into is actually running ## it *should* be, but it's possible that it either crashed or the ## user quit it in the interim, so let's check pgrep -xq "$KMVAR_EditAnywhereApp" EXIT="$?" if [ "$EXIT" = "1" ] then ## If we get here, the app is NOT running, so let's tell the user ## that we are launching it. msg "Launching $KMVAR_EditAnywhereApp so we can paste to it" ## Here is where we actually launch the app open -a "$KMVAR_EditAnywhereApp" ## now we need to give it a few seconds to get itself running ## This is guesswork, because we don't know how long it will ## take for the app to launch and be ready to accept input sleep 5 fi ## Tell the user we are ready to paste information back to the source app msg "Pasting text to $KMVAR_EditAnywhereApp" ## Activate the app ## NOTE: originally this was not happening fast enough ## for it to be ready to accept a 'paste' ## when this script finished ## so I added some extra 'padding' ## and an extra 'open' call ## This should not harm anything. open -a "$KMVAR_EditAnywhereApp" ## wait a second sleep 1 ## No no, REALLY activate the app open -a "$KMVAR_EditAnywhereApp" ## copy the file back to the pasteboard pbcopy < "$FILE" ## OK, now we are done with the FILE so we have to either leave it there, ## delete it, or move it to the trash. ## I don't want to leave it there when it has already been used, ## and I don't want to delete it in case something went wrong ## So I'm going to move it to the trash instead ## AND I'm going to rename the file with the current time stamp so if I ## happen to have several of these and need to recover one, ## I can do that, as long as I haven't emptied my trash ## This will be 'edit_anywhere' without the path or extension ## unless you changed $FILE SHORT="$FILE:t:r" ## This will be 'txt' unless you changed $FILE EXT="$FILE:e" /bin/mv -n "$FILE" "$HOME/.Trash/$SHORT.`timestamp`.$EXT" exit 0 #EOF TimeOutAbortsMacro TimeOutPeriod 86400 TrimResults TrimResultsNew UseText ActionName Paste contents of file back to "Source" app Conditions ConditionList ConditionType Menu MenuConditionSelectionType Is MenuConditionType IsEnabled MenuModifiers 256 MenuShortcut C MenuTitle Paste ConditionListMatch All ElseActions IsActive IsDisclosed KeyCode 9 MacroActionType SimulateKeystroke Modifiers 256 ReleaseAll IsActive IsDisclosed MacroActionType IfThenElse ThenActions IsActive IsDisclosed MacroActionType SelectMenuItem Menu Edit Paste TargetingType Front TimeOutAbortsMacro CreationDate 459387347.72605699 IsActive ModificationDate 460507471.884552 Name Edit Anywhere Triggers FireType Pressed KeyCode 11 MacroTriggerType HotKey Modifiers 6912 UID 63DA32F6-8BA8-49AD-B4BC-C9AC7761D87E Name Global Macro Group ToggleMacroUID 4948A9C1-5CD3-4AA1-A25D-9D29C5471ED8 UID DA8512F7-F323-46B1-8319-752E0495CFD0