Subroutine version of "Reposition a Keyboard Maestro Palette" action

A follow-up to an old custom action: Reposition a Keyboard Maestro Palette.

Here is a Keyboard Maestro subroutine version, with a sample macro to test its use with the Global palette. (Any other palette can be specified by name):

Move a named KM palette (subroutine & example).kmmacros (8,5 Ko)

2 Likes

Updated the subroutine to allow for X position or Y position to be left blank.
(So that the move can be purely horizontal or purely vertical)

Move a Named Keyboard Maestro Palette.kmmacros (7.0 KB)


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

    const main = () => {
        const
            paletteName = kmvar.Palette_name.trim(),
            X = kmvar.New_X_position || "",
            Y = kmvar.New_Y_position || "";

        return either(
            alert(`Move palette named "${paletteName}"`)
        )(
            msg => msg
        )(
            0 < paletteName.length
                ? [X, Y].some(
                    s => 0 < s.trim().length && isNaN(s)
                )
                    ? Left(
                        [
                            "X and Y must both be numeric or blank.",
                            `( Saw X:${X} Y:${Y} )`
                        ].join("\n\n\t")
                    )
                    : namedPaletteToXYLR(paletteName)(
                        [X, Y].map(Number)
                    )
                : Left("Palette name not specified.")
        );
    };

    // namedPaletteToXYLR :: String ->
    // (Int, Int) -> Either String IO String
    const namedPaletteToXYLR = paletteName =>
        (xy) => {
            const
                se = Application("System Events"),
                procs = se.applicationProcesses.where({
                    name: "Keyboard Maestro Engine"
                });

            return bindLR(
                0 < procs.length
                    ? Right(procs[0])
                    : Left("Keyboard Maestro Engine not running")
            )(
                namedWindowMovedLR(se)(paletteName)(xy)
            );
        };

    // namedWindowMovedLR :: System Events -> String ->
    // (Num, Num) -> KM Engine Process -> IO Either String String
    const namedWindowMovedLR = se =>
        paletteName => ([x, y]) =>
            procKME => {
                const
                    windowName = (
                        "global" === paletteName.toLocaleLowerCase()
                            ? "Keyboard Maestro"
                            : paletteName
                    ),
                    paletteWin = procKME.windows
                    .byName(windowName);

                return paletteWin.exists()
                    ? Right(
                        (() => {
                            const
                                xy = paletteWin.position(),
                                newXY = [x || xy[0], y || xy[1]];

                            return (
                                paletteWin.position = newXY,
                                se.perform(paletteWin.actions.AXRaise),
                                `Palette "${paletteName}" moved to ${newXY}.`
                            );
                        })()
                    )
                    : Left(
                        [
                            "Palette not found as spelled:",
                            `\t"${paletteName}"`
                        ].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
    });

    // bindLR (>>=) :: Either a ->
    // (a -> Either b) -> Either b
    const bindLR = m =>
        mf => m.Left ? (
            m
        ) : mf(m.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 => e.Left ? (
            fl(e.Left)
        ) : fr(e.Right);

    return main();
// })();

1 Like