Requesting some help finding an old TP2.0 applescript replacement

Hi Everyone.

I’m coming back to macOS after ~8 years of Windows use, and was trying to set up Taskpaper the way I had it back in OSX days.

I guess the old extensions using AppleScript won’t work anymore, but it seems like there are plenty of new extensions to replace. I can’t seem to find a replacement for this one however - it was a script for creating a folder in Finder with the project name, and subfolders for sub-projects contained within the project in TP. If it was already remade for TP3, could someone point me to a link?

Sorry if it was in an obvious place and I just couldn’t find it. Any pointers about how to better use TP, or what apps to use it with (I used quicksilver back then, but now it seems like people use Alfred more) would also be appreciated.

1 Like

Perhaps something like this JavaScript for Automation script ?

(which could be launched, for example, from Keyboard Maestro in an Execute a JavaScript for Automation action. See also Taskpaper - Using Scripts)

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

    // Rob Trew @2021
    // Ver 0.01

    // main :: IO ()
    const main = () => {
        // Edit to match the root path for your projects:
        const projectsRootPath = "~/projects";

        const
            taskpaper = Application("TaskPaper"),
            windows = taskpaper.windows;

        return either(
            alert("Folder for selected task")
        )(
            fp => Object.assign(
                Application.currentApplication(), {
                    includeStandardAdditions: true
                }
            )
            .displayNotification(fp, {
                withTitle: "Project folder:",
                soundName: "glass"
            })
        )(
            bindLR(
                0 < windows.length ? (
                    Right(windows.at(0).document)
                ) : Left("No documents open in TaskPaper")
            )(
                document => bindLR(
                    document.evaluate({
                        script: `${TP3Context}`
                    })
                )(
                    folderPathFoundOrCreatedLR(
                        projectsRootPath
                    )
                )
            )
        );
    };


    // folderPathFoundOrCreatedLR :: FilePath ->
    // [String] -> IO FilePath
    const folderPathFoundOrCreatedLR = rootPath =>
        projects => 0 < projects.length ? (
            bindLR(
                createDirectoryIfMissingLR(true)(
                    combine(rootPath)(
                        projects.join("/")
                    )
                )
            )(
                openedInFinderLR
            )
        ) : Left(
            "TaskPaper selection not in a project."
        );


    // openedInFinderLR :: FilePath -> IO FilePath
    const openedInFinderLR = fpFolder => {
        const finder = Application("Finder");

        return (
            finder.open(Path(fpFolder)),
            finder.activate(),
            Right(fpFolder)
        );
    };

    // ---------------- TASKPAPER CONTEXT ----------------

    // TP3Context :: Editor -> IO Either String [String]
    const TP3Context = editor => {
        const item = editor.selection.startItem;

        return Boolean(item) ? {
            Right: item.ancestors.concat(item).flatMap(
                x => "project" === x.getAttribute(
                    "data-type"
                ) ? [
                    x.bodyContentString
                ] : []
            )
        } : {
            Left: "Nothing selected in TaskPaper"
        };
    };

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

    // alert :: String => String -> IO String
    const alert = title =>
        s => {
            const sa = Object.assign(
                Application("System Events"), {
                    includeStandardAdditions: true
                });

            return (
                sa.activate(),
                sa.displayDialog(s, {
                    withTitle: title,
                    buttons: ["OK"],
                    defaultButton: "OK"
                }),
                s
            );
        };

    // createDirectoryIfMissingLR :: Bool ->
    // FilePath -> Either String FilePath
    const createDirectoryIfMissingLR = blnParents =>
        dirPath => {
            const fp = filePath(dirPath);

            return doesPathExist(fp) ? (
                Right(fp)
            ) : (() => {
                const
                    e = $(),
                    blnOK = $.NSFileManager
                    .defaultManager[
                        "createDirectoryAtPath" + (
                            "WithIntermediateDirectories"
                        ) + "AttributesError"
                    ](fp, blnParents, void 0, e);

                return blnOK ? (
                    Right(fp)
                ) : Left(e.localizedDescription);
            })();
        };


    // doesPathExist :: FilePath -> IO Bool
    const doesPathExist = fp =>
        $.NSFileManager.defaultManager
        .fileExistsAtPath(
            $(fp).stringByStandardizingPath
        );


    // filePath :: String -> FilePath
    const filePath = s =>
        // The given file path with any tilde expanded
        // to the full user directory path.
        ObjC.unwrap(ObjC.wrap(s)
            .stringByStandardizingPath);


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

    // Left :: a -> Either a b
    const Left = x => ({
        type: "Either",
        Left: x
    });


    // Right :: b -> Either a b
    const Right = x => ({
        type: "Either",
        Right: x
    });


    // bindLR (>>=) :: Either a ->
    // (a -> Either b) -> Either b
    const bindLR = m =>
        mf => m.Left ? (
            m
        ) : mf(m.Right);


    // combine (</>) :: FilePath -> FilePath -> FilePath
    const combine = fp =>
        // Two paths combined with a path separator.
        // Just the second path if that starts with
        // a path separator.
        fp1 => Boolean(fp) && Boolean(fp1) ? (
            "/" === fp1.slice(0, 1) ? (
                fp1
            ) : "/" === fp.slice(-1) ? (
                fp + fp1
            ) : `${fp}/${fp1}`
        ) : fp + fp1;


    // either :: (a -> c) -> (b -> c) -> Either a b -> c
    const either = fl =>
        // Application of the function fl to the
        // contents of any Left value in e, or
        // the application of fr to its Right value.
        fr => e => e.Left ? (
            fl(e.Left)
        ) : fr(e.Right);

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

Zipped sample macro for Keyboard Maestro 10:

Project-path folder opened.kmmacros.zip (3.1 KB)

1 Like

Thanks! This works great!

1 Like