A stop-gap script: toggling a @done tag in selected rows

This is something for which, in due course, Bike.app may no longer need tags (stylesheets revealing toggled attributes might intrude less on the line, and reward the eye less noisily), but for the moment, I find myself marking things off, as I think them through, with either:

  • plain @done, or a timestamped:
  • @done(2022-07-08 08:13) etc.

Here is an interim script for toggling a @done tag (with or without timestamp) in one or more selected (and non-empty) Bike rows.


See: Using Scripts - Bike

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

    // `@done` tag toggled in selected and non-empty
    // lines of a Bike.app (1.3.1 Preview) outline.
    // Optionally with date-time stamp
    // (for simple `@done`, edit tagValue to "" below)

    // Rob Trew @2022
    // Ver 0.03

    // main :: IO ()
    // eslint-disable-next-line max-lines-per-function
    const main = () => {
        // ------------------- OPTIONS -------------------
        const tagName = "done";

        const tagValue = taskPaperDateString(new Date());
        // OR tagValue = "";

        // ------------------- TOGGLE --------------------
        const
            bike = Application("Bike"),
            doc = bike.documents.at(0);

        return doc.exists() ? (() => {
            const
                selectedRows = doc.rows.where({
                    selected: true,
                    _not: [{
                        name: ""
                    }]
                });

            return Boolean(selectedRows.length) ? (() => {
                const
                    tagRegex = new RegExp(
                        Boolean(tagValue.length) ? (
                            `@${tagName}\\(.*\\)`
                        ) : `@${tagName}`, "u"
                    ),
                    isTagged = Boolean(
                        tagRegex.exec(
                            selectedRows.at(0).name()
                        )
                    ),
                    updated = isTagged ? (
                        clearTag(tagRegex)
                    ) : addTag(tagRegex)(tagName)(tagValue);

                return (
                    selectedRows().forEach(row => (
                        row.name = updated(row.name()),
                        row.textContent.strikethrough = !(
                            isTagged
                        )
                    )),
                    isTagged ? (
                        `@${tagName} cleared`
                    ) : `Tagged @${tagName}`
                );
            })() : "Nothing selected in Bike";
        })() : "No documents open in Bike";
    };

    // ---------------------- TAGS -----------------------

    // clearTag :: Regex -> String -> String
    const clearTag = tagRegex =>
        rowText => {
            const match = tagRegex.exec(rowText);

            return Boolean(match) ? (() => {
                const
                    matchStart = match.index,
                    preTag = rowText.slice(
                        0, matchStart
                    ).trim(),
                    postTag = rowText.slice(
                        matchStart + match[0].length
                    ).trim();

                return `${preTag} ${postTag}`.trim();
            })() : rowText.trim();
        };


    // addTag :: String -> String -> String -> String
    const addTag = tagRegex =>
        tagName => tagValue => txt => {
            const
                affix = Boolean(tagValue.length) ? (
                    `@${tagName}(${tagValue})`
                ) : `@${tagName}`;

            return `${clearTag(tagRegex)(txt)} ${affix}`;
        };


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

    // iso8601Local :: Date -> String
    const iso8601Local = dte =>
        new Date(dte - (6E4 * dte.getTimezoneOffset()))
        .toISOString();


    // taskPaperDateString :: Date -> String
    const taskPaperDateString = dte => {
        const [d, t] = iso8601Local(dte).split("T");

        return [d, t.slice(0, 5)].join(" ");
    };

    // MAIN ---
    return main();
})();



Keyboard Maestro macro version:

BIKE Outliner – Toggle @done tag in selected rows - Macro Library - Keyboard Maestro

3 Likes

Script and Keyboard Maestro macro above now updated (here and on the the Keyboard Maestro Forum posting), for Bike Preview 76.

Preview 76 introduces a more easily used .textContent interface to format scripting, and drops the earlier .htmlContent interface.

(The original draft of this interim @done and strikethrough toggling script used the .htmlContent property)


If you have been using the original version this script, you will find that you need the new version, above, for Bike versions 76+

1 Like

It does not strike through, right?

Should do – the version I have here toggles on ⌘D between:

  • striking out the text while adding the dated @done tag
  • clearing both strike and @done tag

BIKE – Toggle @done tag in selected rows.kmmacros.zip (15.0 KB)

you’re right. I had to restart…

1 Like

I am wondering if there are other TaskPaper tags that works already with Bike…
@due, @tomorrow, etc…
thanks

No, so far Bike doesn’t have any specific tag support, though it does have general tag support through the API. Tags will become more useful once I have defined style sheets so that you can customize row styling based on tags. Still a ways to go on that.

thanks, I know that…
Actually i was referring to something similar to Rob solution (above) for @done

Said in other words: does it exist a way to filter Bike outlines?

Ah, no … again will happen someday, but not possible now.