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 ) I'll move on to :
- look at some some other suggestions that have been made
- 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
)