Variable Name pasted as Display Text in Window action

When I'm testing a Keyboard Maestro macro, I often need a temporary Display Text in Window action to show me what value is now attached to one or more KM Variable names.

If you copy a KM variable name (or two or more variable names, separated by whitespace) this macro will paste the name(s) as a pre-filled Display Text in Window action.

For example, if we copy the lines:

local_First
local_Second

The macro will paste the clipboard contents as the following action:


It needs, of course, to be placed in a Keyboard Maestro group for macros which are available in the Keyboard Maestro editor.


Variable Name pasted as Display Text in Window action.kmmacros (6.3 KB)

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

    ObjC.import("AppKit");

    // Paste row(s) of KM variable names as
    // a Display Text in Window action.
    const main = () =>
        either(
            alert("Paste KM var names as Display action")
        )(
            copyText
        )(
            fmapLR(
                txt => displayXML(
                    lines(txt).map(
                        textLine => words(textLine)
                        .map(k => `${k}: %Variable%${k}%`)
                        .join(" ")
                    )
                    .join("\n")
                )
            )(
                clipTextLR()
            )
        );


    const displayXML = stringPart =>
        `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
	<dict>
		<key>Action</key>
		<string>DisplayWindow</string>
		<key>ActionUID</key>
		<integer>15662137</integer>
		<key>MacroActionType</key>
		<string>InsertText</string>
		<key>Text</key>
		<string>${stringPart}</string>
	</dict>
</array>
</plist>`;

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

    // clipTextLR :: () -> Either String String
    const clipTextLR = () => {
    // Either a message, or the plain text
    // content of the clipboard.
        const
            mb = $.NSPasteboard.generalPasteboard
            .stringForType($.NSPasteboardTypeString);

        return mb.isNil()
            ? (
                Left("No utf8-plain-text found in clipboard.")
            )
            : Right(ObjC.unwrap(mb));
    };

    // copyText :: String -> IO String
    const copyText = s => {
        const pb = $.NSPasteboard.generalPasteboard;

        return (
            pb.clearContents,
            pb.setStringForType(
                $(s),
                $.NSPasteboardTypeString
            ),
            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);


    // fmapLR (<$>) :: (b -> c) -> Either a b -> Either a c
    const fmapLR = f =>
        // Either f mapped into the contents of any Right
        // value in e, or e unchanged if is a Left value.
        e => "Left" in e
            ? e
            : Right(f(e.Right));

    // lines :: String -> [String]
    const lines = s =>
    // A list of strings derived from a single string
    // which is delimited by \n or by \r\n or \r.
        0 < s.length
            ? s.split(/\r\n|\n|\r/u)
            : [];

    // words :: String -> [String]
    const words = s =>
    // List of space-delimited sub-strings.
    // Leading and trailling space ignored.
        s.split(/\s+/u).filter(Boolean);

    return main();
})();
1 Like