The Engine.log is populated with entries that include ActionUID's. Is there any way to directly determine the associated macro UUID from one of these ActionUID's?
I do know that I can use the following AppleScript code to select the action (with the associated ActionUID).
### Requires Keyboard Maestro 8.0.3+ ###
set kmInst to system attribute "KMINSTANCE"
tell application "Keyboard Maestro Engine"
set kmAction to getvariable "local_Action" instance kmInst
end tell
tell application "Keyboard Maestro"
selectAction kmAction
activate
end tell
After the action is selected, I also know that I could determine the macro by referencing the selected macro.
But my question is: if I have an ActionUID, can I directly determine the macro name or UUID?
return (() => {
"use strict";
const main = () =>
macroNameFromActionUidLR(kmvar.local_ActionUID);
const macroNameFromActionUidLR = actionUID => {
const
ms = Application("Keyboard Maestro").macros,
actionIDLists = ms.actions.id(),
iMacro = actionIDLists.findIndex(
ids => ids.includes(actionUID)
);
return either(
alert("Macro containing given ActionUID")
)(
x => x
)(
-1 === iMacro
? Left(
[
"No macro found containing ActionUID:",
` "${kmvar.local_ActionUID}"`
]
.join("")
)
: Right(
(() => {
const macro = ms.at(iMacro);
return [
macro.name(),
macro.id()
]
.join("\n");
})()
)
);
};
// ----------------------- 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
);
};
// --------------------- 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
});
// 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);
return main();
})();
It seems to work (and also @ComplexPoint's JXA), if the action is at the top level. But if an action is within a Repeat action (or I suspect any other group), neither seem to work.
In the script:
### Requires Keyboard Maestro 8.0.3+ ###
set kmInst to system attribute "KMINSTANCE"
tell application "Keyboard Maestro Engine"
set kmAction to getvariable "local_Action" instance kmInst
end tell
tell application "Keyboard Maestro"
selectAction kmAction
activate
end tell
selectAction does not seem to have that limitation.
XPATH over the plist XML (perhaps within XQuery or XSLT) would be my next thought, though it might take a little experimentation to get the details clean and efficient.
I leave the details as an exercise for the reader, but here's a first rough sketch (still overproducing and yielding the name of the enclosing Macro Group, as well as that of the Macro, I think.
PS I think perhaps looking for a Triggers key may sort the Macros from the Macro Groups.
Perhaps worth trying something like this:
for $x in //key["ActionUID"=text()]/following-sibling::real["21421"=text()]
return $x/ancestor-or-self::dict/key["Triggers"=text()]/
following-sibling::key["UID"=text()]/following-sibling::string
and in the JS return value clause, using the .stringValue property, rather than the .XMLString property, which is useful during experimentation, but retains the surrounding tags;
To return the macro name, rather than the UUID (Yes, I know, I changed my original question. ), I changed the query to:
for $x in //key["ActionUID"=text()]/following-sibling::real["%Variable%local_ActionUID%"=text()]
return ($x/ancestor-or-self::dict/key["Name"=text()]/following-sibling::string)[1]
Yes, I used to drop them, when transferring code to a KM JS action, but it's since turned out to be easier leave them in place.
If you need to maintain, extend or test anything in Visual Studio Code, for example, you may find yourself having to restore the IIFE, and then decide whether or not to remove it again for pasting back into your action.
Not sure that the extra maintenance churn is really rewarded by much difference in performance, and leaving the IIFE in place has the additional advantage (beyond lowered maintenance and testing friction) of keeping the return in a single consistent place, where you can check its presence (for the KM 'Modern' syntax), at a glance.
+/- a leading return may turn out to be a simpler (testing and adjusting ⇄ use) switch than +/- (() => { ... })(),
but I would just try both approaches, and see which works, over time, for you.