Initiator + Multiple Keystroke Trigger

You are welcome! Speaking of "tons of shortcuts." I know the problem :slight_smile:

Apart from Key Sequences, BTT can distinguish between left and right modifiers. So "Commad + a" triggers different macros/actions depending on which command key you use.

This gives me more options. But more important: I can remember the shorcuts better if I only use "right command + x" and "right option + x" in certain apps.

1 Like

BTT might be the right tool for you, but here's a KM solution too:

With the following macros, you set the shortcut for all your merge macros to (for example) ⌘⇧M. The next key you press triggers a specific merge type.

For example, if you want to merge visible, you hit ⌘⇧M and then v.


MacroCat - Merge.kmmacros (51 KB)

Macro screenshot

MacroCat - Watch for Keypress.kmmacros (43 KB)

Macro screenshot

"MacroCat" is short for Macro Categories.


Setup:

Add the actions you want to perform into the Switch/Case group.


In Use:

Trigger MacroCat - Merge with ⌘⇧M and then hit one of the following keys to perform a merge action:

v (merge visible)
a (merge all)
s (merge selected)
d (merge down)

For actions other than Merge, duplicate this macro, rename it, set a suitable hotkey and edit the Switch/Case group. The typed key can be any letter or number. If the keypress is found in the Switch/Case group, it will perform the appropriate actions; if you type a key that is not set to perform an action, nothing will happen.


MacroCat - Watch for Keypress simply captures the keypress to be used by the main macro. If you duplicate the main macro for other uses, each duplicate can reference this macro; there's no need to duplicate it too.

I've been working on this as well because I've wanted to use something like this for a while.

I noticed a few other requirements from the OP that appeal to me:

  1. Don't allow keystrokes to press through to the current application
    • These could interfere with the current app
  2. Allow sequences larger than 2
    • While it's true they only need '⌘⇧M > Key' for the macros mentioned – at some point there will be conflicts and a need to expand
  3. Input timeout after 3 seconds
    • Allows the user to exit the input sequence
  4. Ability to use modifier keys
    • More customization

Here is my solution: Initiator Keystroke Trigger.kmmacros (113.9 KB)

Pros
• You get to call commands via character sequences without disturbing the current app
• Lots of control and granularity

Cons
• Adding a new sequence is not quick
• It's easy to make a mistake when adding

Usage

  1. Trigger with whatever you choose (currently Ctrl-Option-Cmd+I)
  2. After the audible tone, type one of the demo input sequences: "ind", "pdf", "caps", or "⌥a⌥b".
  3. Terminate your sequence with 'Return'.

It's currently configured to launch 'Display Text' actions that say what would have been launched. To customize your sequences, take a look at the walkthrough to see what's going on in the macros.

Basically, you can use what's already there to build your own sequences.

Walkthrough

I gotta say that this one is very bulky and requires some manual labor up front when adding new commands – but it does work almost exactly as desired and is pretty nice once up and running.

The main concept here is to assign a macro for every "level". For example, the "caps" command has 4 levels, "pdf" and "ind" have 3 and "⌥a⌥b" has 2. This walkthrough will show how each is encoded in the macro so you can make your own.

Level 1 Setup

The first major thing the macro does is save the current focused application. This is necessary so we can switch somewhere else while a key sequence is inputted – satisfying requirement #1 above:

Keyboard Maestro Export

Then we make the aforementioned switch to the Keyboard Maestro Engine app, which doesn't seem to care that we are focusing it or pressing buttons while focused:

Keyboard Maestro Export

From there you'll hear a sound signaling that you can enter a sequence now. I added this sound because at first I was entering commands too fast and needed some indicator of when to start pressing keys. It's being played asynchronously so we don't have to wait for the sound to end before we can start typing.

Listening for our first keys

After setup, the macro listens for expected keys pressed within a 3 second timeout – requirement #3:

Keyboard Maestro Export

If you click the gear on the 'Pause Until' action above, you'll notice the timeout is set to 3 seconds and both 'Timeout Aborts Macro' and 'Notify on Timeout' are disabled (since the timeout here is a feature and not a bug):

The next section consists of actions specific to each key at level 1.

Here is the start of the 'caps' chain:

Keyboard Maestro Export

The 'InitiatorString' variable is used to keep track of state. When the user eventually finishes entering a command, 'InitiatorString' will be checked.

Then the macro throws execution to the next level, the 'Initiator Trigger L2' macro.

Before we go there, note that there are other keys at this level to understand.

The one that sticks out is the '⌥a' in the '⌥a⌥b' sequence:

Keyboard Maestro Export

Here we want to be able to know if the key and modifier are pressed (requirement #4). This is why I had to use an 'If' action instead of a Switch. The Switch can't check for modifiers unfortunately.

The last key to look for at this level is the 'Escape' key:

Keyboard Maestro Export

No official actions here, but pressing 'Escape' allows the user to abort input without having to wait for a 3 second timeout.

Finally, at the end of the level 1 macro, and all of them, execution runs into this:

Keyboard Maestro Export

'ReturnToInitialApplication' is set to YES by default at the start of every sequence at level 1 (Initiator Trigger L1). Note that if the macro you're calling opens another app and continues there, then you'll want to be sure to set 'ReturnToInitialApplication' to NO right before executing that macro. More on this in the Level 4 section below.

The Next Level

Level 2 is more of the same – just continue adding the next link in whatever sequence you're building.

One difference worth noting is what must appear at every level starting at level 2: a special 'Pause Until' action.

This had to be added after first testing this macro because I noticed that sometimes keypresses were bleeding into the next level. I'd press a 'D' in level X and it would still be observed in level X+1 somehow. To stop it, this 'Pause Until' action will wait for there to be no keypresses.

Unfortunately, this meant hardcoding every key that'll be supported:

From...

all the way to...

Specifically, supported keys are: a-z, 0-9, F1-F19, 'Return', & 'Escape'.

Of course you can add more. I will add symbols and Numpad support at some point, but I got tired of entering each one manually. Just add them at the end of the list. The good news is that you only have to specify them once and then you can copy/paste this 'Pause Until' action at the start of every macro at Level 2 or above.

Notice that you can't add modifier keys here in the 'Pause Until' action. Fortunately, when ⌥ is pressed with a key, let's say 'X', the 'X' registers as being pressed down, so this 'Pause Until' will still work even if modifiers are also being pressed at the moment.

If you want to check for a modifier key, you have to handle this below the 'Pause Until', just as was done in the above example where I showed the start of the ⌥a⌥b sequence.

Level 2 then continues on in the same way that Level 1 does recording the keys pressed and calling the next level.

The other Level 2 difference is that now we are appending onto the 'InitiatorString' variable instead of setting it:

Keyboard Maestro Export

Level 3

This is the first level that includes a sequence termination and macro call. Ideally, we'd want sequences with 3 characters (like "ind" or "pdf") to terminate on level 3 – but we actually end up needing an extra character to terminate everything. That's why here at level 3 we're terminating a sequence with 2 characters: "⌥a⌥b".

The reason sequences are 1 keypress longer than their character count is because the macro needs to know when we're done sending input. A 'Return' key is then sent to terminate the whole thing.

Otherwise, all sequences would have to be the same length. You couldn't have a sequence of length 2 and a sequence of length 4 without signaling when to stop taking input. They'd all have to stop in the same level, and we want the flexibility of being able to create sequences at variable lengths (requirement #2).

Here's how the "⌥a⌥b" sequence terminates in Level 3:

Keyboard Maestro Export

We respond to the 'Return' keypress and check to see if the 'InitiatorString' matches the known terminating sequence at this level. If it does, we activate the initial application so that the macro will return from the focus on the KM Engine BEFORE executing. This is required if you want the macro to execute in the app you're currently in.

Level 4

This section has 2 sequences that terminate – "ind" and "pdf":

Keyboard Maestro Export

Notice how "ind" uses 'Activate Initial Application' and "pdf" doesn't. Basically I made the assumption that a macro named 'Export to PDF' would open the Preview app and go there. It's an example of how to make sure the macro takes you to another application instead of the default – which is to return to it.

This is done by setting 'ReturnToInitialApplication' to NO right before calling the macro intended to run at the end of a sequence.

On the other hand, "ind" does return to the initial application first before executing the macro. This is so execution can continue in the intended app.

Level 5

Finally, the last sequence is terminated in 'Initiator Trigger L5' ("caps"). Nothing special here other than it being the longest sequence. This macros will handle sequences of any length – but you'll have to add another level in the macro chain every time you add a command with a larger sequence. It currently supports sequences with 1-4 characters, which will get you a long way, but it helps to know you can get more.

..and that's it!

Go ahead and edit the macros to create whatever sequences you want!

New Sequence Quick Guide

  1. Each new key needs to be added 2-3 times per each level:
    a. In the collection of 'Pause Until key is down' specific to that level
    b. In its own 'If key is down'
    c. [L2+ only] In the first action entitled 'Pause Until None of the supported keys are down...'. Note that a-z, 0-9, F1-F19, 'Return', & 'Escape' are already added.

  2. 'If key is down' contents (before terminating)
    a. 'InitiatorString' must be set in L1 with the first letter and then be appended to in L2+
    b. Call the next level macro, Initiator Trigger L(X+1)

  3. 'If key is down' contents (while terminating)
    a. A sequence ends in (character sequence count +1) with the 'Return' key
    b. Call any macro from there based on the contents of 'InitiatorString'
    c. If you want to return to the app you were in before inputting a sequence, then call the 'Activate Initial Application' macro before calling your sequence ending macro
    d. If the macro you call switches applications, then set 'ReturnToInitialApplication' to 'NO' before calling that particular macro

Wish List / Future

• Some sort of visual component. I'd like to at least see what's been typed so far and if possible see what commands are still available in real-time while typing. Some kind of updating HUD.
• An audio beep after entering an unsupported sequence

1 Like

Wow, thanks for your kindness! :man_bowing: I have to try that, you made a good work there :clap::clap:

That's a lot of work right there.

Personally, I think this is overkill and, in removing one problem, presents new ones. My previous suggestion is more than adequate for most needs, I think. However, may I humbly submit two alternative approaches to using strings as a trigger? This is primarily with a view to simplifying the setup.


1

Pros: No lengthy setup procedure or need to wait before entering the trigger string. Any letter or number combination, of any length, can be used.
Cons: No modifiers allowed. Requires an extra piece of software.

I have opted for the actions to run upon hitting Return, but if preferred this can easily be set as a timeout instead.

You will need this small, free app, KeyboardLocker, which temporarily disables keyboard input, preventing the typed string keystrokes from interfering with the app you're working in.

In Use:

As in my previous suggestion, trigger using ⇧⌘M, and then type the desired string followed by the Enter Key. If the typed string is found in the Switch/Case group, the related actions will be performed; if not, nothing will happen.

MacroCat String - Merge.kmmacros (27 KB)

Macro screenshot

MacroCat String - Watch for Keypress.kmmacros (36 KB)

Macro screenshot

NB: to manually exit KeyboardLocker, simply hit ⌘Q.


2

...and here's an even simpler approach using a prompt. This does require the use of the Enter Key, so may or may not be desirable, depending on personal preference.

MacroCat Prompt - Merge.kmmacros (42 KB)

Macro screenshot

2 Likes

Definitely more concise. Most users probably won't need the modifier keys anyways.

The KeyboardLocker is a good find, although I was a tad distracted with the 'Keyboard Locker Activated' message. Still usable enough though.

I also don't understand how KM can still listen for keys if Keyboard Locker is active - but hey, it works :+1:

1 Like

I'm guessing it might be because the keystrokes aren't actually coming from the keyboard...?

kraftyDevil, noisneil your solutions are wonderful! The only idea I had is to set up this:

  • Open a palette for one action with a shortcut
  • Type the trigger

Eg: „⌃a“ (opens palette), then „a“ or „⌘c“

image

Pros

  • Nothing dangerous is transferred to the front app.
  • There is a visual help if you have forgotten the trigger
  • Shortcuts can be used twice. If the palette is visible ⌘c does not trigger copy

Cons

  • Triggers are limited to single letters (a, b, c), numbers (1, 2, 3) or shortcuts.
2 Likes

This is the best and most logical solution and is what I would use if I didn't have a Stream Deck. @AIvaro was quite specific about the way he wanted to trigger the macros, so I focused on trying to solve that particular problem, but this is a perfect scenario for using palettes.

Thanks noisneil :slight_smile: You're right, it probably doesn't work for Alvaro, but it works for me.
By the way, I just noticed that "special" keys also work as triggers. So Spacebar, fn, Enter, Delete, esc, Arrows.

@Frankb Sorry I've just woken up so my brain isn't working yet... Do you mean that "special keys" work for selecting macros in a palette?

The palette and the macros in the palette
so you could type fn (opens palette) then again fn triggers macro

Sorry, not true. fn opens the palette, then another key is needed, but can be a "special keys"

eg. fn + enter, or a letter, number...

1 Like

Those keys are probably best avoided for triggering macros directly, as they are commonly used for in-app functions, but in theory any key can be used as a hotkey, yes. The fn key would be an exception, provided you have access to your function keys without it or if the function keys aren't needed.

@noisneil I tried your new approach, it is nice and simple to configure, great idea! However, probably the problem is that this feature should be included in the KM package itself to handle it right with care. I do really appreciate your effort and kindness, but those are some cons I found while using your approach:

  • Since it is based on an external tool, it is a bit messy. If you mistake the spelling of your shortcut you have to manually close KeyboardLocker by pressing ⌘Q. Probably a timer could be added to abort the current key sequence and quit KeyboardLocker if 3sec has been elapsed or something like this.
  • Pressing enter to validate the input is not very nice according to my taste, (but I understand it has some advantages too). It would be nice if as soon as a string matches the keyboard is unlocked and the macro fired.
  • The KeyboardLocker notification is distracting, but this is a minor problem, Im pretty sure this can be fixed or another similar programs can be used for the same purpose.

In conclusion, I think your approach is great, probably the best so far... but it could be even better if KM add this behavior as a new kind of trigger.

I am trying to understand @kraftyDevil script and adapt it to my needs to see how it performs. I also tried BTT and it is nice, but it is not exactly what I expected either.

I think it is nice to be able to share this kind of knowledge, it can be helpful for many people and luckily help to improve KM itself.

Thank you! I will come back to this post soon to add my own conclusions about all suggested methods

eg, this works. fn opens palette, triggers are Enter, Spacebar, esc

image

I agree that locking keyboard input is a compromise. It's worth noting however, that if you mis-type your string as one that isn't recognised by the Switch/Case group, hitting Enter will quit KeyboardLocker and complete the macro without performing any actions. So you can hit Enter rather than ⌘Q.

I agree and it can easily be done with a timeout pause instead. I would personally find it annoying to have to wait before the macro executes, which is why I opted for hitting Enter instead, but it's still a compromise. The way you asked to trigger the macros isn't something natively supported by KM, so our best options will still involve compromise. We've explored some ways to get your preferred method of triggering to work but, for the moment, perhaps you should consider others.

In terms of practicality, @Frankb's suggestion of using palettes is probably the most flexible and user-friendly option. However, if you prefer not to see a palette, then I think this one might be most suited to a practical workflow.

1 Like

For the sake of completeness, here is another option for using strings as triggers. You will need to be aware of whether or not you want the typed string to be deleted. If the typed string enters unwanted text in your app, you will probably want to turn this feature on; if it doesn't, then you will probably want to leave it off.

MacroCat Typed String - Merge.kmmacros (43 KB)

Macro screenshot

MacroCat Typed String - Activate.kmmacros (37 KB)

Macro screenshot

As you can see, I've set multiple typed string triggers for the Merge macro that are mirrored in the Switch/Case group.

To use this, trigger the Activate macro with ⇧⌘M and type your string trigger.

NB: I just edited the above macro to include an activation timeout.

The limitations of KM Conflict Palettes is probably the entire reason I've been seeking a better input method like the one in this post.

Maybe a more in-depth explanation of these palettes from yourself or @Frankb would help.

I would explain it but I don't understand them beyond the default Conflict Palette – which I think is so-so but not great.

The reason being that the letter-by-letter selection option in the default Conflict Palette breaks down when the names of your macros start getting too similar.

For example, here are the names of some of my macros and what KM expects me to press after the initial 'I':

Expected letters have been bolded and capitalize for detail:

InsErt Row Above
InsErt Row Below
InsTall Beta Build
InsTall UAT build

I > E to Insert Row? I > R would be more intuitive.

I > T to Install Beta? I > B works better.

These resolutions makes no sense so then you have to spend too much time trying to find the next letter in the Conflict Palette – for which the rule by default is – "resolve to the next character that differs".

Honestly, if KM could change this Conflict Palette character resolution method then I wouldn't need any of the custom macros we've made in this thread.

In addition to the default, it would be useful to have these user configurable options when resolving macro names in Conflict Palettes:

  1. Resolve to the next character after a space (if one does not exist, then use the default)
  2. Resolve to the next capital letter (if one does not exist, then use the default)

Using #1, my Conflict Palette would look like this after inputting the first 'I':

Insert Row Above
Insert Row Below
Install Beta Build
Install UAT build

Much better – because now "Insert Row Above" is now I>R>A, which is easier to remember.

This would allow me to think about keypresses as I have named my macros and not by some heuristic that breaks down with macros too similarly named.

Notice this is the same mechanism that @AIvaro wants to use. It blocks input and you can enter a sequence to launch a macro.

I'm not sure where you'd specify this in KM if it were an option. Preferences?

So, while I definitely have issues with the default Conflict Palette – I would be interested to see how a custom one would address this issue