Bike to and from TP

what are the best practices so far?
I tried script “Import OPML or BML outlines into TaskPaper 3”… it works but no TP syntax…
correct isn’t it?

Could you tell us a bit more about what kind of mappings you are after in each direction ?

(and perhaps expand ‘no TP syntax…’ a little ?)


For example, from Bike to TaskPaper:

  • would an outline with - task prefixes suffice ?
  • or would you want a trailling project colon after each top-level row ?
  • anything else ?

and from TaskPaper to Bike:

  • would you want any preservation of the project ⇄ task ⇄ note distinctions ?
  • in the form of attribute values perhaps ?
  • or by preserving elements like trailing project colons ?
  • anything else ?

thanks for your quick answer…
TP syntax is
project:

  • task
    as usually
    I think that an outline with - task prefixes suffice perhaps with “notes” (old problem…),
    this last not so urgent
    If you were able to realize (in any form) what you prospect I am OK and thanks

Which is your priority ?

  • TaskPaper → Bike ?
  • (or Bike → Taskpaper ? )

(my weekend is a little booked : -)

For this weekend Bike to TP… :innocent:

Here’s a first rough draft for you to test.

A draft of a Copy As > TaskPaper for Bike, which aims to:

  • Copy top level (unindented) rows as TaskPaper projects, with trailling :
  • copy all other rows with a leading - task bullet.

BIKE - Copy as TaskPaper.kmmacros.zip (14.7 KB)

Expand disclosure triangle to view JS source
(() => {
    "use strict";

    ObjC.import("AppKit");

    // Copy Bike document
    // (or Bike selected rows, if selected is extended)

    // as bulleted TaskPaper text
    // with project colons trailling top-level rows.

    // RobTrew @2022
    // Ver 0.01

    // main :: IO ()
    const main = () => {
        const doc = Application("Bike").documents.at(0);

        return doc.exists() ? (
            copyText(
                taskPaperTextFromBikeDoc(doc)
            )
        ) : "No document open in Bike";
    };

    // ---------------------- BIKE -----------------------

    // taskPaperTextFromBikeDoc :: Bike Doc -> IO String
    const taskPaperTextFromBikeDoc = doc => {
        const
            rows = Boolean(doc.selectedText()) ? (
                doc.rows.where({selected: true})
            ) : doc.rows;

        return tp3OutlineFromForest(
            forestFromIndentedLines(
                zip(
                    rows.level()
                )(
                    rows.name()
                )
            )
        );
    };

    // ----------------------- JXA -----------------------

    // copyText :: String -> IO String
    const copyText = s => {
        const pb = $.NSPasteboard.generalPasteboard;

        return (
            pb.clearContents,
            pb.setStringForType(
                $(s),
                $.NSPasteboardTypeString
            ),
            s
        );
    };

    // --------------------- GENERIC ---------------------

    // Node :: a -> [Tree a] -> Tree a
    const Node = v =>
    // Constructor for a Tree node which connects a
    // value of some kind to a list of zero or
    // more child trees.
        xs => ({
            type: "Node",
            root: v,
            nest: xs || []
        });


    // Tuple (,) :: a -> b -> (a, b)
    const Tuple = a =>
    // A pair of values, possibly of
    // different types.
        b => ({
            type: "Tuple",
            "0": a,
            "1": b,
            length: 2,
            *[Symbol.iterator]() {
                for (const k in this) {
                    if (!isNaN(k)) {
                        yield this[k];
                    }
                }
            }
        });


    // tp3OutlineFromForest ::
    // Forest {level :: Int, text :: String} -> String
    const tp3OutlineFromForest = trees => {
        const go = tabs => baseLevel => tree => {
            const
                node = tree.root,
                txt = node.text;

            return [
                Boolean(txt) ? (
                    baseLevel === node.level ? (
                        `${txt}:`
                    ) : `${tabs}- ${txt}`
                ) : `${tabs}`,
                ...tree.nest.flatMap(
                    go(`\t${tabs}`)(baseLevel)
                )
            ];
        };

        return 0 < trees.length ? (
            trees.flatMap(
                go("")(trees[0].root.level)
            )
            .join("\n")
        ) : "";
    };


    // forestFromIndentedLines :: [(Int, String)] ->
    // [Tree {text:String, body:Int}]
    const forestFromIndentedLines = tuples => {
        const go = xs =>
            0 < xs.length ? (() => {
            // First line and its sub-tree,
                const [depth, body] = xs[0],
                    [tree, rest] = span(x => depth < x[0])(
                        xs.slice(1)
                    );

                return [
                    Node({
                        text: body,
                        level: depth
                    })(go(tree))
                ]
                // followed by the rest.
                .concat(go(rest));
            })() : [];

        return go(tuples);
    };


    // span :: (a -> Bool) -> [a] -> ([a], [a])
    const span = p =>
    // Longest prefix of xs consisting of elements which
    // all satisfy p, tupled with the remainder of xs.
        xs => {
            const i = xs.findIndex(x => !p(x));

            return -1 !== i ? (
                Tuple(xs.slice(0, i))(
                    xs.slice(i)
                )
            ) : Tuple(xs)([]);
        };


    // zip :: [a] -> [b] -> [(a, b)]
    const zip = xs =>
    // The paired members of xs and ys, up to
    // the length of the shorter of the two lists.
        ys => Array.from({
            length: Math.min(xs.length, ys.length)
        }, (_, i) => [xs[i], ys[i]]);

    return main();
})();

To test in Script Editor, set the language selector at top left to JavaScript.

See: Using Scripts - Bike

2 Likes

perfect thanks!
of course no note field as it is in Bike :frowning:

1 Like

If manually removing bullets from particular para sequences looks like a source of friction, it might be worth assigning a single keystroke to:

TaskPaper > Item > Format As > Notes

you mean via kbmaestro?

That would certainly be one of several options – and the one that I would personally use.

I think you may be able to do it through System Preferences too.

1 Like

On TaskPaper → Bike

I guess the main choice turns on how you want to handle any TP3 tags in the copied/pasted material.

  • Import them to Bike as literal text ?
  • Import them as Bike attribute key: value pairs (we don’t yet have stylesheets or UI to make them visible)
  • Drop them ?
  • Something else ?

all these opportunities are not clickable yet, isn’t it?

Not sure that I have quite understood that question yet – could you expand ?

(Attributes are only accessible through scripting in Bike at the moment – there isn’t any dedicated UI at this point)

tags in TP are clickable, they act as a filter…

Got it – correct, there is no built in filtering by tags/attributes in Bike at the moment.

( Could be added by script though )

1 Like

if you are able to add in any way a filter I personally would like it as clickable but I suppose the overall design -in the end- is up to Jesse

Direct textual import of TaskPaper tags is probably the sensible default for the moment.

Filtering by attributes is already feasible by script for custom purposes, but for general use, and most users, I don’t think it makes much sense without built-in GUI mechanisms for:

  • viewing attribute names and values
  • flexibly filtering on them
1 Like

I agree

I’ll look at TaskPaper → Bike next week.


On the visibility of attributes – it would, of course, be possible to write a script which toggled the visibility, in Bike, of tags in the TaskPaper format, or some other pattern.

i.e. toggling visible tagshidden attributes

(in the whole document, or in selected lines)

thanks
very kind of you