Ability to mark items as @done

Loving the UX of Bike so far, and would like to use it as a todo list as well. Will there be the ability to mark items as @done?

Thanks!

Yes I think so, but I’m still not entirely sure how it will work and I need to get more infrastructure in place before I think about adding.

Generally I want to support tags/value in Bike, but I expect I’ll take a different approach then the syntax highlighting approach that TaskPaper uses. Instead I think I will take a more rich text approach that uses commands instead of syntax highlighting to add the tags. Maybe a popup UI somewhat similar to how links are added now.

Also tags might need to wait until I get some sort of theme and query system going in Bike. So they are on my list for sure, but could be a while.

1 Like

In the meanwhile, I’m personally using a keystroke bound to a scripted strikethrough and @done(timestamp):

Screenshot 2022-11-07 at 18.27.35

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


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();
})();

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

See: Using Scripts - Bike

2 Likes

Thank you @jessegrosjean for the quick reply, and @complexpoint for the script.

I never used Taskpaper so I’m not familiar with the @done syntax. What is the idea behind using it instead of using a checkmark like :white_check_mark: or just the strikethrough? Is it a tag that offers some search benefits? Thanks!

In TaskPaper tags/value are parsed out of your text. General format is @tag(value) though the value part is optional. More detail in TaskPaper’s user guide.

Generally a tag system like TaskPaper’s that can also be styled and searched is more open ended and flexible than using special checkmark value. I’ll likely take the same overall approach at the model level with Bike. I just don’t want tags to be defined via syntax likely, instead there will be special commands/UI to add/remove them.

2 Likes

Stored as attributes, and made visible by styles ?

I think so. Again haven’t really decided, but that would be most strait forward. Probably namespaces somehow so that attributes can be used for other things to.

2 Likes