Anyone know if there's a way to check the KM version in a script or Custom HTML Prompt?

Anyone know if there's a way to check the KM version in a script or Custom HTML Prompt?

Application("Keyboard Maestro Engine").version()

?

Or, of course, more comparably:

Application("Keyboard Maestro Engine")
    .version()
    .split(".")
    .map(x => parseInt(x, 10));
2 Likes

A footnote on comparing / sorting versions – I'm not sure how you prefer to go about that, but here's one approach to defining comparison over integer lists like the ones above:

Given the JS convention of representing LT EQ and GT as -1, 0, 1

(as in the functions we pass to Array.sort)

The basic JS comparison of two values (of the same type) for their ordering might be written in an 'uncurried' form as:

// compare :: (a, a) -> Ordering
const compare = (x, y) =>
    x < y ? -1 : (x > y ? 1 : 0);

and when we want to sort by a property of slightly more complex values (ordering strings by their length, for example, rather than by an alphabetic scheme), we can then step back one level and write:

// comparing :: (a -> b) -> (a -> a -> Ordering)
const comparing = f =>
    (x, y) => {
        const
            a = f(x),
            b = f(y);

        return compare(a, b);
    };

so that an ascending sort of strings by their length might look like:

Expand disclosure triangle for ASC by length
(() => {
    "use strict";

    const main = () => {
        // Ascending sort by length of string.
        console.log(
            "Ascending length ::",
            [
                "alpha",
                "beta",
                "gamma",
                "delta",
                "epsilon",
                "zeta",
                "eta",
                "theta",
                "iota",
                "kappa",
                "lambda",
                "mu",
                "nu",
                "omicron"
            ].sort(comparing(x => x.length))
        );
    };


    // --------------------- GENERIC ---------------------

    // compare :: (a, a) -> Ordering
    const compare = (x, y) =>
        x < y ? -1 : (x > y ? 1 : 0);


    // comparing :: (a -> b) -> (a -> a -> Ordering)
    const comparing = f =>
        (x, y) => {
            const
                a = f(x),
                b = f(y);

            return compare(a, b);
        };

    return JSON.stringify(main());
})();

and a descending sort by length:

Expand disclosure triangle to view DESC by length
(() => {
    "use strict";

    const main = () => {
        // Descending sort by length of string.
        console.log(
            "Descending length ::",
            [
                "alpha",
                "beta",
                "gamma",
                "delta",
                "epsilon",
                "zeta",
                "eta",
                "theta",
                "iota",
                "kappa",
                "lambda",
                "mu",
                "nu",
                "omicron"
            ].sort(
                flip(comparing(x => x.length))
            )
        );
    };


    // --------------------- GENERIC ---------------------

    // compare :: (a, a) -> Ordering
    const compare = (x, y) =>
        x < y ? -1 : (x > y ? 1 : 0);


    // comparing :: (a -> b) -> (a -> a -> Ordering)
    const comparing = f =>
        (x, y) => {
            const
                a = f(x),
                b = f(y);

            return compare(a, b);
        };

    // flip :: (a -> b -> c) -> b -> a -> c
    const flip = op =>
        // The binary function op with
        // its arguments reversed.
        1 < op.length ? (
            (a, b) => op(b, a)
        ) : (x => y => op(y)(x));

    return JSON.stringify(main());
})();

But compare, and the JS comparison operators, aren't defined for working directly over lists of simple values, so what might a definition of compareList (that we can use with versions) look like ?

Here's a possible sketch (assuming that we've split the version string on dots, and mapped its integer strings to integer values):

Ascending sort of versions
(() => {
    "use strict";

    const main = () => {
        // Some [Int] versions lists
        const versions = [
            [5, 0, 1],
            [5, 0, 0],
            [7, 2, 7],
            [7, 0, 8],
            [10, 1, 2],
            [7, 2, 6],
            [10, 1, 1],
            [10, 0, 1],
            [7, 2, 8]
        ];

        // Ascending sort of versions.
        console.log(
            "\nAscending version\n",
            JSON.stringify(
                versions.sort((a, b) => compareList(a, b))
            )
        );
    };

    // --------------------- GENERIC ---------------------

    // compareList :: ([a], [a]) -> Ordering
    const compareList = (xs, ys) => {
        const
            nx = xs.length,
            ny = ys.length;

        return 0 === nx ? (
            0 === ny ? 0 : -1
        ) : 0 === ny ? (
            1
        ) : (() => {
            const ord = compare(xs[0], ys[0]);

            return 0 === ord ? (
                compareList(xs.slice(1), ys.slice(1))
            ) : ord;
        })();
    };


    // compare :: (a, a) -> Ordering
    const compare = (x, y) =>
        x < y ? -1 : (x > y ? 1 : 0);


    return JSON.stringify(main());
})();
Descending sort of versions
(() => {
    "use strict";

    const main = () => {
        // Some [Int] versions lists
        const versions = [
            [5, 0, 1],
            [5, 0, 0],
            [7, 2, 7],
            [7, 0, 8],
            [10, 1, 2],
            [7, 2, 6],
            [10, 1, 1],
            [10, 0, 1],
            [7, 2, 8]
        ];

        // Descending sort of version integers.
        console.log(
            "\nDescending version\n",
            JSON.stringify(
                versions.sort(
                    flip(compareList)
                )
            )
        );
    };

    // --------------------- GENERIC ---------------------

    // compareList :: ([a], [a]) -> Ordering
    const compareList = (xs, ys) => {
        const
            nx = xs.length,
            ny = ys.length;

        return 0 === nx ? (
            0 === ny ? 0 : -1
        ) : 0 === ny ? (
            1
        ) : (() => {
            const ord = compare(xs[0], ys[0]);

            return 0 === ord ? (
                compareList(xs.slice(1), ys.slice(1))
            ) : ord;
        })();
    };


    // compare :: (a, a) -> Ordering
    const compare = (x, y) =>
        x < y ? -1 : (x > y ? 1 : 0);


    // flip :: (a -> b -> c) -> b -> a -> c
    const flip = op =>
        // The binary function op with
        // its arguments reversed.
        1 < op.length ? (
            (a, b) => op(b, a)
        ) : (x => y => op(y)(x));


    return JSON.stringify(main());
})();

I've decided I don't need to know the version after all, but thanks anyway.

1 Like

For any future reader needing version comparisons, a TLDR might be:

// compareList :: ([a], [a]) -> Ordering
const compareList = (xs, ys) =>
    // 0 if two lists are identical.
    // -1 if xs is empty, or has a lower leftward value.
    // 1 if ys is empty, or has a lower leftward value.
    compare(
        0 === xs.length,
        0 === ys.length
    ) || compare(xs[0], ys[0]) || (
        compareList(xs.slice(1), ys.slice(1))
    );

// compare :: (a, a) -> Ordering
const compare = (x, y) =>
    x < y ? -1 : (x > y ? 1 : 0);

e.g.

Expand disclosure triangle to view working example
(() => {
    "use strict";

    const main = () => {
        // Some [Int] versions lists
        const versions = [
            [7, 2, 7],
            [5, 0, 1],
            [5, 0, 0],
            [7, 0, 8],
            [10, 1, 2],
            [7, 2, 6],
            [10, 1, 1],
            [10, 0, 1],
            [7, 2, 8]
        ];

        // Ascending sort of versions.
        console.log(
            "\nAscending version\n",
            JSON.stringify(
                versions.sort(compareList)
            )
        );

        // Descending sort of versions.
        console.log(
            "\nDescending version\n",
            JSON.stringify(
                versions.sort(flip(compareList))
            )
        );
    };

    // --------------------- GENERIC ---------------------

    // compareList :: ([a], [a]) -> Ordering
    const compareList = (xs, ys) =>
        // 0 if two lists are identical.
        // -1 if xs is empty, or has a lower leftward value.
        // 1 if ys is empty, or has a lower leftward value.
        compare(
            0 === xs.length,
            0 === ys.length
        ) || compare(xs[0], ys[0]) || (
            compareList(xs.slice(1), ys.slice(1))
        );

    // compare :: (a, a) -> Ordering
    const compare = (x, y) =>
        x < y ? -1 : (x > y ? 1 : 0);


    // flip :: (a -> b -> c) -> b -> a -> c
    const flip = op =>
        // The binary function op with
        // its arguments reversed.
        1 < op.length ? (
            (a, b) => op(b, a)
        ) : (x => y => op(y)(x));

    return JSON.stringify(main());
})();

The point being that (once we have integer list copies of the version strings) a definition of compare for lists gives us all the comparisons we need:

< <= === => >

can all be expressed by the ordering values

  • LT -1
  • EQ 0
  • GT 1

which compareList returns from a comparison of two lists.

1 Like