PopClip shows a row of action buttons when you select text with your pointer in any app. This new extension adds an action button that sends the selected text into a nominated Bike document.
Full disclosure: I am the developer of PopClip. I’m also a Bike user!
I use the PopClip extension to collect ideas/links etc. into a Bike “Inbox”. It’s quite similar to the existing extension for TaskPaper.
It works with Bike 1 and is implemented using Bike’s AppleScript interface. (I’m afraid I haven’t tried Bike 2 yet!)
For your Bike 2 pop-clip extension, you may find that the bundled bike CLI works well – bypassing the need for the target .bike file to be opened.
If we are collecting in ~/Capture.bike for example, under a top-level Input heading, and needed a grandparental post office reminder, then perhaps something like:
Thanks for the very useful information. Much as I love the idea of scripting with AppleScript, it is just so cumbersome to use! That CLI call looks like it’s going to be much more robust.
Admittedly, having only a plain text box for specifying the file path is not ideal. A file picker, or simply tilde expansion, would have eliminated the error. And PopClip gives no indication of what the actual problem is, just the generic “X”, leaving you to only guess at the issue. Both are areas I would like to improve.
Interestingly enough, the first time I tested my own extension, I put a tilde in the file path too.
PS @jessegrosjean pointed out something that I had missed in the CLI documentation – that there is a direct match with your position option to add new items at top or bottom of the list.
You can refine the targeting of a --parent switch by following it with a --position {start, end} switch.
So to prepend items at the top of the list (rather than the default append) perhaps something like:
bike create row --outline ~/Capture.bike --parent /Input --position start --text "Something for the top of the list"
using the bike CLI to create the specified .bike file, and/or target row, if either doesn’t yet exist, and
allowing for multiple selected lines, and aiming to preserve any indents.
Expand disclosure triangle to view YAML source
#popclip
name: Bike 2
identifier: com.robtrew.popclip.extension.bike2
description: Capture text to a Bike 2 document using the CLI. Creates the document and target row if they do not exist.
icon: symbol:bicycle
popclip version: 4151
keywords: outliner
app:
name: Bike
link: https://www.hogbaysoftware.com/bike/
bundle identifiers: [com.hogbaysoftware.Bike, com.hogbaysoftware.Bike-setapp]
check installed: true
options:
- identifier: document
label: Document Path
type: string
description: Absolute path to the Bike document, such as "~/Documents/Inbox.bike".
- identifier: row
label: Parent Row Path
type: string
default value: /Inbox
description: Bike outline path for the parent row (e.g., /Inbox). Leave blank to add directly to the root.
- identifier: position
label: Add At
type: multiple
values: [start, end]
value labels: [Top, Bottom]
default value: start
description: Where in the list to add new rows.
actions:
- requirements: [text]
after: show-status
interpreter: zsh
shell script: |
setopt extended_glob
BIKE="/usr/local/bin/bike"
DOC_PATH="${POPCLIP_OPTION_DOCUMENT/#\~/$HOME}"
# Spaces, tabs, newlines trimmed from start and end
TRIMMED_TEXT="${POPCLIP_TEXT##[[:space:]]#}"
TRIMMED_TEXT="${TRIMMED_TEXT%%[[:space:]]#}"
# Any indents preserved, all non-empty lines have markdown bullets
BULLETED_TEXT=$(printf "%s" "$TRIMMED_TEXT" | sed -E 's/^([[:blank:]]*)([^[:blank:]-]+)/\1- \2/')
add_rows() {
printf "%s\n" "$BULLETED_TEXT" | "$BIKE" create rows -f - --outline "$DOC_PATH" "$@"
}
if [[ ! -f "$DOC_PATH" ]]; then
"$BIKE" create outline "$DOC_PATH"
fi
if [[ -n "$POPCLIP_OPTION_ROW" ]]; then
ROW_EXISTS=$("$BIKE" get outline --refs "$POPCLIP_OPTION_ROW" --outline "$DOC_PATH" --shape flat -o txt)
# The parent heading created if not found
if [[ -z "$ROW_EXISTS" ]]; then
ROW_TEXT="${POPCLIP_OPTION_ROW#/}"
"$BIKE" create row --outline "$DOC_PATH" --text "$ROW_TEXT"
fi
add_rows --parent "$POPCLIP_OPTION_ROW" --position "$POPCLIP_OPTION_POSITION"
else
add_rows --position "$POPCLIP_OPTION_POSITION"
fi
A variant which experiments with adding a couple more features to the three already listed above:
Allowing for filepath ~
using the bike CLI to create the specified .bike file, and/or target row, if either doesn’t yet exist, and
allowing for multiple selected lines, and aiming to preserve any indents.
4. Adds a date-stamped added badge to the (first) line added to the inbox, and
5. Copies to clipboard a bike:// url link to that line in the inbox.
Expand disclosure triangle to view Popclip YAML source
#popclip
name: Bike 2
identifier: com.robtrew.popclip.extension.bike2
description: Capture text to a Bike 2 document using the CLI. Creates the document and target row if they do not exist.
icon: symbol:bicycle
popclip version: 4151
keywords: outliner
app:
name: Bike
link: https://www.hogbaysoftware.com/bike/
bundle identifiers: [com.hogbaysoftware.Bike, com.hogbaysoftware.Bike-setapp]
check installed: true
options:
- identifier: document
label: Document Path
type: string
default value: ~/Documents/Inbox.bike
description: Absolute path to the Bike document, such as "~/Documents/Inbox.bike".
- identifier: row
label: Parent Row Path
type: string
default value: /Inbox
description: Bike outline path for the parent row (e.g., /Inbox). Leave blank to add directly to the root.
- identifier: position
label: Add At
type: multiple
values: [start, end]
value labels: [Top, Bottom]
default value: start
description: Where in the list to add new rows.
actions:
- requirements: [text]
after: show-status
interpreter: zsh
shell script: |
setopt extended_glob
DOC_PATH="${POPCLIP_OPTION_DOCUMENT/#\~/$HOME}"
# Spaces, tabs, newlines trimmed from start and end
TRIMMED_TEXT="${POPCLIP_TEXT##[[:space:]]#}"
TRIMMED_TEXT="${TRIMMED_TEXT%%[[:space:]]#}"
# Any indents preserved, all non-empty lines have markdown bullets
BULLETED_TEXT=$(printf "%s" "$TRIMMED_TEXT" | sed -E 's/^([[:blank:]]*)([^[:blank:]-]+)/\1- \2/')
add_rows() {
# Text of first line, and its session ID, as two lines of text
NAME_SESSIONID=$(printf "%s\n" "$BULLETED_TEXT" |
bike create rows -f - --outline "$DOC_PATH" "$@" |
jaq -r '.[0].text[0].string, .[0].id'
)
FIRST_ROW_TEXT="${NAME_SESSIONID%$'\n'*}"
FIRST_CLAUSE="${FIRST_ROW_TEXT%%[.,;:]*}"
SESSION_ID="${NAME_SESSIONID##*$'\n'}"
# Persistent ID of the document
DOC_ID=$(bike get outline /* --outline "$DOC_PATH" | jaq -r ".persistentId")
BIKE_URL=$(bike update rows "$SESSION_ID" --outline "$DOC_PATH" \
--attr added="$(date +'%Y-%m-%d %H:%M')" \
--persistent-id @ensure |
jaq -r --arg doc "$DOC_ID" '"bike://\($doc)/#\(.[0].row.persistentId)"')
MD_LINK="[${FIRST_CLAUSE}](${BIKE_URL})"
# Placed in various pasteboard items
osascript -l JavaScript -e "
ObjC.import('AppKit');
const pb = $.NSPasteboard.generalPasteboard;
pb.clearContents;
pb.setStringForType('${FIRST_CLAUSE}', 'public.url-name');
pb.setStringForType('${BIKE_URL}', 'public.url');
pb.setStringForType('${MD_LINK}', 'public.utf8-plain-text');
" > /dev/null
echo "$MD_LINK"
}
if [[ ! -f "$DOC_PATH" ]]; then
bike create outline "$DOC_PATH"
fi
if [[ -n "$POPCLIP_OPTION_ROW" ]]; then
ROW_EXISTS=$(bike get outline --refs "$POPCLIP_OPTION_ROW" --outline "$DOC_PATH" --shape flat -o txt)
# The parent heading created if not found
if [[ -z "$ROW_EXISTS" ]]; then
ROW_TEXT="${POPCLIP_OPTION_ROW#/}"
bike create row --outline "$DOC_PATH" --text "$ROW_TEXT"
fi
add_rows --parent "$POPCLIP_OPTION_ROW" --position "$POPCLIP_OPTION_POSITION"
else
add_rows --position "$POPCLIP_OPTION_POSITION"
fi