Here is a variant which builds and executes the Manipulate Window action from JS:
Cycle centered front window size.kmmacros (39.8 KB)

JS Source:
(() => {
'use strict';
//Rob Trew 2017-12-23
ObjC.import('AppKit');
const stages = [
[1 / 10, 8 / 10],
[1 / 3, 1 / 3],
[1 / 5, 3 / 5]
];
// GENERIC ---------------------------------------------------------------
// findIndex :: (a -> Bool) -> [a] -> Maybe Int
const findIndex = (p, xs) =>
xs.reduce((a, x, i) =>
a.nothing ? (
p(x) ? just(i) : a
) : a, nothing('No match found for: ' + p.toString()));
// just :: a -> Just a
const just = x => ({
nothing: false,
just: x
});
// nothing :: () -> Nothing
const nothing = (optionalMsg) => ({
nothing: true,
msg: optionalMsg
});
// readFile :: FilePath -> IO String
const readFile = strPath => {
var error = $(),
str = ObjC.unwrap(
$.NSString.stringWithContentsOfFileEncodingError(
$(strPath)
.stringByStandardizingPath,
$.NSUTF8StringEncoding,
error
)
);
return typeof error.code !== 'string' ? (
str
) : 'Could not read ' + strPath;
};
// show :: Int -> a -> Indented String
// show :: a -> String
const show = (...x) =>
JSON.stringify.apply(
null, x.length > 1 ? [x[1], null, x[0]] : x
);
// takeBaseName :: FilePath -> String
const takeBaseName = strPath =>
strPath !== '' ? (
strPath[strPath.length - 1] !== '/' ? (
strPath.split('/')
.slice(-1)[0].split('.')[0]
) : ''
) : '';
// takeExtension :: FilePath -> String
const takeExtension = strPath => {
const
xs = strPath.split('.'),
lng = xs.length;
return lng > 1 ? (
'.' + xs[lng - 1]
) : '';
};
// File name template -> temporary path
// (Random digit sequence inserted between template base and extension)
// tempFilePath :: String -> IO FilePath
const tempFilePath = template =>
ObjC.unwrap($.NSTemporaryDirectory()) +
takeBaseName(template) + Math.random()
.toString()
.substring(3) + takeExtension(template);
// JXA --------------------------------------------------------------------
// mainScreenXYWH :: () -> {X::Int, Y::Int, W::Int, H::Int}
const mainScreenXYWH = () => {
const
dct = $.NSScreen.mainScreen.visibleFrame,
xy = dct.origin,
wh = dct.size;
return {
X: xy.x,
Y: xy.y + 22,
W: wh.width,
H: wh.height - 22
};
};
// KEYBOARD MAESTRO -----------------------------------------------------
// jsoDoScript :: Object (Dict | Array) -> IO ()
const jsoDoScript = jso => {
const strPath = tempFilePath('tmp.plist');
return (
Application('Keyboard Maestro Engine')
.doScript((
$(Array.isArray(jso) ? jso : [jso])
.writeToFileAtomically(
$(strPath)
.stringByStandardizingPath,
true
),
readFile(strPath)
)),
true
);
};
// MAIN ------------------------------------------------------------------
const
canvas = mainScreenXYWH(),
pixelStages = stages.map(
([x, w]) => [
Math.floor(x * canvas.W),
Math.floor(w * canvas.W)
]
),
w = parseInt(
Application('Keyboard Maestro Engine')
.calculate('WINDOW(0,Width)'), 10
),
mbIndex = findIndex(x => w === x[1], pixelStages),
[xNext, wNext] = pixelStages[
mbIndex.nothing ? (
0
) : ((mbIndex.just + 1) % stages.length)
];
// KEYBOARD MAESTRO 'MANIPULATE WINDOW' ACTION BUILT AND RUN
return (
jsoDoScript([{
"TargetApplication": {},
"WindowName": "",
"MacroActionType": "ManipulateWindow",
"TargetingType": "Front",
"Action": "MoveAndResize",
"HorizontalExpression": xNext.toString(),
"VerticalExpression": "22",
"WidthExpression": wNext.toString(),
"HeightExpression": canvas.H.toString(),
"Targeting": "FrontWindow",
"ActionName": "Move and Resize Front Window",
"WindowIndexExpression": 2,
}]), [xNext, wNext]
)
})();