Feature request for a keyboard shortcut to fully expand the sidebar

If I had a native keyboard shortcut that executed that, I’d be happy!

In the meanwhile, this JXA script assumes installation of a current version of Keyboard Maestro - you can use it on its own (as long as KM and TaskPaper 3 are both installed), but you can also, of course, paste it into a KM Execute a JavaScript for Automation action, and use that to assign a keystroke to it.

(() => {

    // Click-Toggle Show Project in Sidebar of TaskPaper 3
    // DEPENDENCIES:
    //  Assumes installation of Keyboard Maestro 8, and TaskPaper 3
    // USE: This is a JavaScript for Automation script, which can be
    // tested in Script Editor (with the language tab set to JavaScript)
    // From Script Menu, or as a Keyboard Maestro
    // 'Execute a JavaScript for Automation' action

    // Rob Trew (c) 2018
    // MIT License
    // Ver 0.5 2018-01-11

    // 0.5 In case of wider sidebars set clicked horizontal to X + (W-10)
    // 0.4 (Finally posted the correct version :-)
    // 0.3 (Simplified (>>=) bindMay chain)
    // 0.2 Added the Opt to Opt-Click

    // main :: JS Function -> FilePath -> a

    'use strict';

    // GENERIC FUNCTIONS --------------------------------------

    // Just :: a -> Just a
    const Just = x => ({
        type: 'Maybe',
        Nothing: false,
        Just: x
    });

    // Nothing :: () -> Nothing
    const Nothing = (optionalMsg) => ({
        type: 'Maybe',
        Nothing: true,
        msg: optionalMsg
    });

    // append (++) :: [a] -> [a] -> [a]
    const append = (xs, ys) => xs.concat(ys);

    // bindMay (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
    const bindMay = (mb, mf) =>
        mb.Nothing ? mb : mf(mb.Just);

    // isMaybe :: a -> Bool
    const isMaybe = x =>
        x.type === 'Maybe';

    // map :: (a -> b) -> [a] -> [b]
    const map = (f, xs) => xs.map(f);

    // readFile :: FilePath -> IO String
    const readFile = strPath => {
        var error = $(),
            str = ObjC.unwrap(
                $.NSString.stringWithContentsOfFileEncodingError(
                    $(strPath)
                    .stringByStandardizingPath,
                    $.NSUTF8StringEncoding,
                    error
                )
            );
        return Boolean(error.code) ? (
            ObjC.unwrap(error.localizedDescription)
        ) : str;
    };

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

    // MAIN ---------------------------------------------------

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

    const
        se = Application('System Events'),
        procs = se.applicationProcesses.where({
            name: 'TaskPaper'
        }),
        mbResult =
        bindMay(
            bindMay(
                bindMay(
                    procs.length > 0 ? (
                        Just(procs.at(0))
                    ) : Nothing('TaskPaper not running.'),
                    proc => {
                        const ws = proc.windows;
                        return ws.length > 0 ? (
                            Just(ws.at(0)
                                .splitterGroups.at(0)
                                .scrollAreas)
                        ) : Nothing('No TaskPaper windows open.')
                    }
                ),
                areas => areas.length > 1 ? (
                    Just(areas.at(0)
                        .outlines.at(0)
                        .rows.at(1)
                        .uiElements.at(0))
                ) : Nothing('Sidebar not open.')
            ),
            cell => {
                const [
                    x, y, w, h
                ] = append(cell.position(), cell.size());
                const [
                    sx, sy
                ] = map(
                    n => n.toString(), [x + (w - 10), y + (h / 2)]
                );
                return (
                    // Effect
                    jsoDoScript([{
                            "ReopenWindows": false,
                            "AllWindows": true,
                            "MacroActionType": "ActivateApplication",
                            "AlreadyActivatedActionType": "Normal",
                            "TimeOutAbortsMacro": true,
                            "Application": {
                                "NewFile": "/Applications/TaskPaper.app",
                                "BundleIdentifier": "com.hogbaysoftware.TaskPaper3.direct",
                                "Name": "TaskPaper"
                            }
                        }, {
                            "MacroActionType": "PauseUntil",
                            "TimeOutAbortsMacro": true,
                            "Conditions": {
                                "ConditionList": [{
                                    "ApplicationConditionType": "Active",
                                    "ConditionType": "Application",
                                    "Application": {
                                        "NewFile": "/Applications/TaskPaper.app",
                                        "BundleIdentifier": "com.hogbaysoftware.TaskPaper3.direct",
                                        "Name": "TaskPaper"
                                    }
                                }],
                                "ConditionListMatch": "Any"
                            }
                        },
                        {
                            "UseFormat": false,
                            "MacroActionType": "SetVariableToCalculation",
                            "Variable": "instanceMouseX",
                            "Text": "MOUSEX()"
                        },
                        {
                            "UseFormat": false,
                            "MacroActionType": "SetVariableToCalculation",
                            "Variable": "instanceMouseY",
                            "Text": "MOUSEY()"
                        },
                        {
                            "ClickCount": 0,
                            "DragVerticalPosition": "0",
                            "MacroActionType": "MouseMoveAndClick",
                            "Modifiers": 0,
                            "Button": 0,
                            "Fuzz": 15,
                            "Action": "Move",
                            "DisplayMatches": false,
                            "DragHorizontalPosition": "0",
                            "RestoreMouseLocation": false,
                            "VerticalPositionExpression": sy,
                            "RelativeCorner": "TopLeft",
                            "HorizontalPositionExpression": sx,
                            "Relative": "Absolute",
                            "MouseDrag": "None"
                        },
                        {
                            "Time": ".1",
                            "MacroActionType": "Pause",
                            "TimeOutAbortsMacro": true
                        },
                        {
                            "ClickCount": 1,
                            "DragVerticalPosition": "0",
                            "MacroActionType": "MouseMoveAndClick",
                            "Modifiers": 2048,
                            "Button": 0,
                            "Fuzz": 15,
                            "Action": "MoveAndClick",
                            "DisplayMatches": false,
                            "DragHorizontalPosition": "0",
                            "RestoreMouseLocation": false,
                            "VerticalPositionExpression": sy,
                            "RelativeCorner": "TopLeft",
                            "HorizontalPositionExpression": sx,
                            "Relative": "Absolute",
                            "MouseDrag": "None"
                        },
                        {
                            "ClickCount": 0,
                            "DragVerticalPosition": "0",
                            "MacroActionType": "MouseMoveAndClick",
                            "Modifiers": 0,
                            "Button": 0,
                            "Fuzz": 15,
                            "Action": "Move",
                            "DisplayMatches": false,
                            "DragHorizontalPosition": "0",
                            "RestoreMouseLocation": false,
                            "VerticalPositionExpression": "instanceMouseY",
                            "RelativeCorner": "TopLeft",
                            "HorizontalPositionExpression": "instanceMouseX",
                            "Relative": "Absolute",
                            "MouseDrag": "None"
                        }
                    ]),
                    Just([sx, sy])
                )
            }
        );
    return mbResult.Nothing ? (
        mbResult.msg
    ) : ('Clicked TaskPaper sidebar at: ' + mbResult.Just);
})();

(Updated to 0.2 above – added the ⌥ as in Opt-Click )

Hi Rob,

Doesn’t work for me. I am guessing this is because I have made my sidebar wider.

Which number should I change to make the click work on my wider sidebar?

Is there an easy way to measure what the number should be?

Example:

Not your fault – I posted a version with dependencies - correcting to 0.4 now

And in 0.5 I’ve changed the clicked X to (X + (W-10)) which should be insensitive to sidebar width, I think,

Thanks! Version 0.5 works on my wide sidebar!

1 Like

Posted in a Keyboard Maestro macro (which additionally opens the sidebar if it finds it closed) at:

1 Like

(Incidentally, there may be a redundant pause action in there:

 {
    "Time": "0.1",
    "MacroActionType": "Pause",
     "TimeOutAbortsMacro": true
}

I find that in practice I can I either remove it or set the Time string to “0” on my system.

 {
    "Time": "0",
    "MacroActionType": "Pause",
     "TimeOutAbortsMacro": true
}

Removed it and it works fine on my MacBook Pro (15-inch, 2016).

1 Like

@Jim @complexpoint In latest preview release I’ve added “View > Show Sidebar And Expand Completely”:

2 Likes

@jessegrosjean — works well here! The only thing I would add is a native keyboard shortcut.

Interesting discussion! Thanks for all the juicy details.