A first draft of a basic Copy As Bike Outline
for TaskPaper 3
- Drops task bullets and project colons
- Copies TaskPaper tags as literal text
Other refinements could be added, if they seemed necessary (perhaps tags as Bike attributes rather than literal text, and/or special handling of any links in the TaskPaper.
But for the moment, probably sensible to test a basic version and see if we can get that working OK for you:
TaskPaper 3 – Copy As Bike Outline.kmmacros.zip (2.8 KB)
Expand disclosure triangle to view JS source
(() => {
"use strict";
ObjC.import("AppKit");
// First draft of a basic
// `Copy from TaskPaper 3 as Bike`
// com.hogbaysoftware.bike.xml
// clipboard.
// Copies selected lines of front TaskPaper 3 document
// or all visible rows int the TP3 outline
// if the selection is collapsed.
// Rob Trew @2023
// Ver 0.01
// ---------- TASKPAPER EVALUATION CONTEXT -----------
// eslint-disable-next-line max-lines-per-function
const tp3Context = editor => {
// eslint-disable-next-line max-lines-per-function
const tp3Main = () => {
const selection = editor.selection;
return (
selection.isCollapsed ? (
// eslint-disable-next-line no-undef
Item.getCommonAncestors(
editor.displayedItems
)
) : selection.selectedItemsCommonAncestors
)
.flatMap(foldTreeTP(bikeLine))
.join("\n");
};
// ------------------ BIKE XML -------------------
// bikeLine :: TP Node -> [String] -> String
const bikeLine = tpNode => xs => {
// Nest of <li><p> ... </li>
const
txt = tpNode.bodyContentString,
tags = tagText(tpNode),
txtAndTags = tags ? (
`${txt} ${tags}`
) : `${txt}`;
return [
["<li>"],
[
Boolean(txt) ? (
`<p>${txtAndTags}</p>`
) : "<p/>"
],
0 < xs.length ? [(
`<ul>${xs.flat().join("\n")}</ul>`
)] : [],
["</li>"]
]
.flat();
};
// tagText :: TP Node -> String
const tagText = tpNode => {
// Any trailling tags, or an empty string.
const dict = tpNode.attributes;
return Object.keys(dict).flatMap(
k => k.startsWith("data-") && (
k !== "data-type"
) ? (() => {
const
short = k.slice(5),
v = dict[k];
return v ? (
`@${short}(${v})`
) : `@${short}`;
})() : []
)
.join(" ");
};
// ----------- GENERICS FOR TASKPAPER ------------
// foldTreeTP :: (TPNode -> [a] -> a) -> TPNode -> a
const foldTreeTP = f => {
// The catamorphism on TP3 trees. A summary
// value obtained by a depth-first fold.
const go = node => f(node)(
node.children.map(go)
);
return go;
};
return tp3Main();
};
// ------------- JXA EVALUATION CONTEXT --------------
// main :: IO ()
const main = () => {
const doc = Application("TaskPaper").documents.at(0);
return doc.exists() ? (() => {
const
bikeLines = doc.evaluate({
script: `${tp3Context}`
});
return (
setClipOfTextType(
"com.hogbaysoftware.bike.xml"
)(
`<?xml version="1.0" encoding="UTF-8"?>
<html>
<head><meta charset="utf-8"/></head>
<body><ul>${bikeLines}</ul></body>
</html>`
),
"TaskPaper copied as Bike outline."
);
})() : "No document open in TaskPaper.";
};
// ----------------------- JXA -----------------------
// setClipOfTextType :: String -> String -> IO String
const setClipOfTextType = utiOrBundleID =>
txt => {
const pb = $.NSPasteboard.generalPasteboard;
return (
pb.clearContents,
pb.setStringForType(
$(txt),
utiOrBundleID
),
txt
);
};
// MAIN ---
return main();
})();