Debugging the Copy as Markdown Link Macro?

I appreciate that everyone has a job and helping is fun. I tried appending a problem to the end of the Copy As Markdown Link Topic

I think my question got lost. My question:
Two apps that the Macro isn't working for right now, I'm curious about tweaking it for:

  • OmniFocus4 - The link generate: [Inbox](file:///Users/marklevison/Library/Containers/com.omnigroup.OmniFocus4/Data/Library/Application%20Support/OmniFocus/OmniFocus.ofocus/) -- looks wrong
  • Obsidian - The Current implementation doesn't appear to be working, it is getting called - an alert proves that, it's just doing anything.

Happy to help, debug code. My JXA skills are less than 0.

I've added coverage of OF4 today (Keyboard Maestro's new subroutines turn out to help with cases where the bundle id differs, but the other code required is the same)

Going to take a closer look at Obsidian tomorrow – I think that the issue may be variation in the path on which people have their vault.

After that I'll tidy up support for a few other applications, and push an update to the Github repository – Friday if I get enough time. Otherwise Sunday.

2 Likes

OmniFocus 4, Obsidian

Just pushed a draft update aiming to add OmniFocus 4 and improve Obsidian support.

Please test and report.

(Download with green Code button from GitHub - RobTrew/copy-as-md-link: macOS Keyboard Maestro macro group – single keystroke to copy MD links from different applications. )


If those seem to be working (as it happens, I'm not a user of either :slight_smile: ) I'll move on to :

  1. look at some some other suggestions that have been made
  2. tidy up a bit to make more use of subroutines, especially where different version of the same app essentially need the same code

FWIW the Obsidian code has been simplified, and, in this draft, is essentially:

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

    // MD link to file selected in Obsidian v. 0.10.1

    // Rob Trew @2020
    // Ver 0.2

    // main :: IO ()
    const main = () =>
        either(
            alert("Copy as MD Link")
        )(
            mdLink => mdLink
        )(
            bindLR(
                openObsidianVaultIdPathsLR()
            )(
                mdLinkFromIdPathsLR
            )
        );


    // openObsidianVaultIdPathsLR :: () -> IO Either String FilePath
    const openObsidianVaultIdPathsLR = () =>
        bindLR(
            bindLR(
                readFileLR(
                    [
                        "~/Library/Application Support",
                        "/Obsidian/obsidian.json"
                    ]
                    .join("")
                )
            )(
                jsonParseLR
            )
        )(
            dict => "vaults" in dict
                ? Right(
                    Object.entries(dict.vaults)
                    .flatMap(
                        ([k, v]) => v.open
                            ? [Tuple(k)(v.path)]
                            : []
                    )
                )
                : Left("No 'vaults' key found in Obsidian.json")

        );

    // mdLinkFromIdPathsLR ::
    // [(id, FilePath)] -> Either String String
    const mdLinkFromIdPathsLR = idPaths => {
        const
            parts = kmvar.local_Window_Title
            .split(" - "),
            noteName = parts[0],
            vaultName = parts[1],
            iVault = idPaths.findIndex(
                idFp => idFp[1].endsWith(vaultName)
            );

        return bindLR(
            -1 !== iVault
                ? Right(idPaths[iVault])
                : Left(
                    "Window title doesn't match open vault name."
                )
        )(
            idPath => Right(
                `[${noteName}](` + (
                    `obsidian://open?vault=${idPath[0]}&` + (
                        `file=${encodeURIComponent(noteName)})`
                    )
                )
            )
        );
    };


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

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

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


    // --------------------- GENERIC ---------------------
    // https: //github.com/RobTrew/prelude-jxa

    // 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 = lr =>
    // Bind operator for the Either option type.
    // If lr has a Left value then lr unchanged,
    // otherwise the function mf applied to the
    // Right value in lr.
        mf => "Left" in lr
            ? lr
            : mf(lr.Right);


    // 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 => "Left" in e
            ? fl(e.Left)
            : fr(e.Right);


    // jsonParseLR :: String -> Either String a
    const jsonParseLR = s => {
        // Either a message, or a JS value obtained
        // from a successful parse of s.
        try {
            return Right(JSON.parse(s));
        } catch (e) {
            return Left(
                `${e.message} (line:${e.line} col:${e.column})`
            );
        }
    };


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

PPS

( Remember that before using a new or updated install, you need to rebuild the map of app bundle IDs to Keyboard Maestro macro ids by running the included macro with the name:

Update map from bundleIDs to KM UUIDs 

)

OmniFocus 4 addition seems to work as expected. THANKS!

1 Like