Change the welcome text

Hi @complexpoint
Thanks a lot but get this

in my ~/Documents/Template/Taskpaper/" folder I have 2 files with extension .taskpaper
Something goes wrong
Both running from Keyboard maestro or Script editor (using Javascript) same issue

Thanks
Patrick

Forgat to add in the Script editors got this
“No ‘com.hogbaysoftware.taskpaper’ templates\n\nfound in:\n\t/Users/XXX/Documents/Template/Taskpaper”

The first thing to try might be to edit the line (near the top of the script)

const templateUTI = "com.hogbaysoftware.taskpaper";

changing it to something like:

const templateUTI = "public.text";

and then tell us what happens.

Exactly the same as the message change a bit

does the folder must contain more than .taskpaper file into right?
thanks

with:

const fpTemplateFolder = "~/Documents/Template/Taskpaper/";

const templateUTI = "public.text";

const title = "TaskPaper Templates";

Thanks.

Next test (seems to work here):

const templateUTI = "com.hogbaysoftware.taskpaper";

same issue as before
Should I grant Script editor to access the file system?

OK, give me 20 mins. I’ll look closer.

Something seems to be behaving differently between Mojave and Big Sur, and I have obviously not yet thought deeply enough about it :slight_smile:

Merci
Take your time I’m back in 3 h…

1 Like

I’ve updated to 2.4 here and above:

Matching now on the file extension rather than the UTI:

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

    // 1. Create a folder containing two or more
    //    model TaskPaper documents
    // 2. Edit the value of `fpTemplateFolder` at the top
    //    of this script to match the path the folder.
    // 3. Run this script
    //    (e.g. from a Keyboard Maestro or Fastscripts shortcut)

    ObjC.import("AppKit");

    // TaskPaper 3 – new file from menu of templates.
    // Ver 2.8

    // Rob Trew @2021

    const fpTemplateFolder = "~/TaskPaper Templates";

    const templateExtension = ".taskpaper";

    const title = "TaskPaper Templates";

    // main :: IO ()
    const main = () => {
        const
            fpFolder = filePath(fpTemplateFolder),
            extension = templateExtension
            .startsWith(".") ? (
                templateExtension
            ) : `.${templateExtension}`;

        return either(
            msg => alert(title)(msg)
        )(
            chosenTemplateName => chosenTemplateName
        )(
            bindLR(
                doesDirectoryExist(fpFolder) ? (
                    Right(fpFolder)
                ) : Left(`Folder not found: ${fpFolder}`)
            )(folderPath => {
                const
                    templateNames = folderTemplates(
                        fpFolder
                    )(
                        extension
                    )
                    .sort(),
                    intFiles = templateNames.length;

                return bindLR(
                    0 < intFiles ? (
                        1 < intFiles ? (
                            showMenuLR(false)(title)(
                                "Choose a template:"
                            )(templateNames[0])(
                                templateNames
                            )
                        ) : Right([templateNames[0]])
                    ) : Left(
                        `No '${extension}' templates` + (
                            `\n\nfound in:\n\t${fpFolder}`
                        )
                    )
                )(
                    chosenTemplateOpened(folderPath)(
                        extension
                    )
                );
            })
        );
    };


    // folderTemplates :: FilePath -> String -> [String]
    const folderTemplates = fpFolder =>
        extension => listDirectory(fpFolder)
        .flatMap(fileName => {
            const [base, ext] = Array.from(
                splitExtension(fileName)
            );

            return extension !== ext ? (
                []
            ) : Boolean(base) ? (
                [base]
            ) : [];
        });


    // chosenTemplateOpened :: FilePath -> [FileName]
    // -> IO FilePath
    const chosenTemplateOpened = fpFolder =>
        extension => choices => {
            const
                taskpaper = Application("TaskPaper"),
                fpTemplate = filePath(combine(fpFolder)(
                    `${choices[0]}${extension}`
                ));

            return bindLR(
                readFileLR(fpTemplate)
            )(
                txt => {
                    // showLog("Template path:", fpTemplate);
                    // showLog("Template content:", txt);

                    const newDoc = taskpaper.Document({
                        textContents: txt
                    });

                    // In TaskPaper,
                    return (
                        taskpaper.activate(),
                        taskpaper.documents.push(newDoc),
                        // newDoc.textContents = txt,
                        // newDoc.evaluate({
                        //     script: `${TaskPaperContext}`,
                        //     withOptions: {
                        //         textContents: txt
                        //     }
                        // }),
                        // in the JS interpreter.
                        Right(fpTemplate)
                    );
                }
            );
        };


    // ---------------- TASKPAPER CONTEXT ----------------
    // const TaskPaperContext = (editor, options) =>
    //     editor.outline.reloadSerialization(
    //         options.textContents
    //     );


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

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

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


    // showMenuLR :: Bool -> String -> String ->
    // [String] -> String -> Either String [String]
    const showMenuLR = blnMult =>
        // An optionally multi-choice menu, with
        // a given title and prompt string.
        // Listing the strings in xs, with
        // the string `selected` pre-selected
        // if found in xs.
        menuTitle => prompt => selected => xs =>
        0 < xs.length ? (() => {
            const sa = Object.assign(
                Application("System Events"), {
                    includeStandardAdditions: true
                });

            sa.activate();

            const v = sa.chooseFromList(xs, {
                withTitle: menuTitle,
                withPrompt: prompt,
                defaultItems: xs.includes(selected) ? (
                    [selected]
                ) : [xs[0]],
                okButtonName: "OK",
                cancelButtonName: "Cancel",
                multipleSelectionsAllowed: blnMult,
                emptySelectionAllowed: false
            });

            return Array.isArray(v) ? (
                Right(v)
            ) : Left(`User cancelled ${title} menu.`);
        })() : Left(`${title}: No items to choose from.`);


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


    // Tuple (,) :: a -> b -> (a, b)
    const Tuple = a =>
        b => ({
            type: "Tuple",
            "0": a,
            "1": b,
            length: 2
        });


    // 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);


    // doesDirectoryExist :: FilePath -> IO Bool
    const doesDirectoryExist = fp => {
        const ref = Ref();

        return $.NSFileManager.defaultManager
            .fileExistsAtPathIsDirectory(
                $(fp)
                .stringByStandardizingPath, ref
            ) && ref[0];
    };


    // 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);


    // listDirectory :: FilePath -> [FilePath]
    const listDirectory = fp =>
        ObjC.unwrap(
            $.NSFileManager.defaultManager
            .contentsOfDirectoryAtPathError(
                ObjC.wrap(fp)
                .stringByStandardizingPath,
                null
            ))
        .map(ObjC.unwrap);


    // readFileLR :: FilePath -> Either String IO String
    const readFileLR = fp => {
        // Either a message or the contents of any
        // text file at the given filepath.
        const
            e = $(),
            ns = $.NSString
            .stringWithContentsOfFileEncodingError(
                $(fp).stringByStandardizingPath,
                $.NSUTF8StringEncoding,
                e
            );

        return ns.isNil() ? (
            Left(ObjC.unwrap(e.localizedDescription))
        ) : Right(ObjC.unwrap(ns));
    };


    // splitExtension :: FilePath -> (String, String)
    const splitExtension = fp => {
        // A tuple of the basename and the extension,
        // in which the latter includes the "."
        const
            xs = fp.split("."),
            lng = xs.length;

        return 1 < lng ? (
            Tuple(
                xs.slice(0, -1).join(".")
            )(
                `.${xs[lng - 1]}`
            )
        ) : Tuple(fp)("");
    };

    // // showLog :: a -> IO ()
    // const showLog = (...args) =>
    //     // eslint-disable-next-line no-console
    //     console.log(
    //         args
    //         .map(JSON.stringify)
    //         .join(" -> ")
    //     );

    return main();
})();

HI
Better Thanks a lot
now it propose the choice box with my template name into
when I click(choose) on one it open an empty taskpaper document
in the script editor the result is success with the full path and name of the template I have choose
looks like the template is not used to create the new doc

Thanks

I’ve seen that with one file, but not others.

Are you able to share a zip of one or more templates with which you see that ? It would be helpful to track down the pattern.

UPDATE

Version 2.7 reverts to the original technique for reading template contents into a new document,

but requires an update to TaskPaper 3.8.16 or above.

You are really great!!!
The 2.7 works perfectly
I have already Taskpaper 3.8.16
Super!!!
I will need to pay you a coffee! :slight_smile:

1 Like