Hot Key Cheat Sheet?

Thanks for the heads-up on that.

Thanks. I assumed this was the case, but I'm not sure I I had tested it yet. Now I don't have to. :grinning:

Just a quick demo of what is already possible with existing functionality (might require current alpha version). Maybe useful as reference.

Putting this into a floating webview action, will list all of the shortcuts and their actions like this:


It could easily be styled with some CSS.

<html>
  <head>
    <script>
     async function BTTWindowWillBecomeVisible() {
        console.log("loading triggers");
        const allKeyboardShortcutsJSONString = await callBTT("get_triggers", {
          trigger_type: "BTTTriggerTypeKeyboardShortcut",
        });
        const allTriggersJSONArray = JSON.parse(allKeyboardShortcutsJSONString);
        let allKeyboardShortcuts = "";
        for (const trigger of allTriggersJSONArray) {
          if (trigger.BTTShortcutModifierKeys) {
            const shortcut = `<div><span><b>${
              trigger?.BTTTriggerConfig?.BTTLeftRightModifierDifferentiation
                ? getModifiersForBitmask(
                    trigger.BTTAdditionalConfiguration,
                    true
                  )
                : getModifiersForBitmask(trigger.BTTShortcutModifierKeys, false)
            }${getKeycodeStringForKeycode(
              trigger.BTTShortcutKeyCode
            )}</b></span> <span>${getActionNameForID(
              trigger.BTTPredefinedActionType,
              trigger
            )}</span></div>`;
            allKeyboardShortcuts = allKeyboardShortcuts + shortcut;
          }
        }
        document.getElementById("triggerList").innerHTML = allKeyboardShortcuts;
      }

  function getKeycodeStringForKeycode(keycode) {
        switch (keycode) {
          case 0x00: {
            return "A";
          }
          case 0x01: {
            return "S";
          }
          case 0x02: {
            return "D";
          }
          case 0x03: {
            return "F";
          }
          case 0x04: {
            return "H";
          }
          case 0x05: {
            return "G";
          }
          case 0x06: {
            return "Z";
          }
          case 0x07: {
            return "X";
          }
          case 0x08: {
            return "C";
          }
          case 0x09: {
            return "V";
          }
          case 0x0b: {
            return "B";
          }
          case 0x0c: {
            return "Q";
          }
          case 0x0d: {
            return "W";
          }
          case 0x0e: {
            return "E";
          }
          case 0x0f: {
            return "R";
          }
          case 0x10: {
            return "Y";
          }
          case 0x11: {
            return "T";
          }
          case 0x12: {
            return "1";
          }
          case 0x13: {
            return "2";
          }
          case 0x14: {
            return "3";
          }
          case 0x15: {
            return "4";
          }
          case 0x16: {
            return "6";
          }
          case 0x17: {
            return "5";
          }
          case 0x18: {
            return "=";
          }
          case 0x19: {
            return "9";
          }
          case 0x1a: {
            return "7";
          }
          case 0x1b: {
            return "-";
          }
          case 0x1c: {
            return "8";
          }
          case 0x1d: {
            return "0";
          }
          case 0x1e: {
            return "]";
          }
          case 0x1f: {
            return "O";
          }
          case 0x20: {
            return "U";
          }
          case 0x21: {
            return "[";
          }
          case 0x22: {
            return "I";
          }
          case 0x23: {
            return "P";
          }
          case 0x25: {
            return "L";
          }
          case 0x26: {
            return "J";
          }
          case 0x27: {
            return '"';
          }
          case 0x28: {
            return "K";
          }
          case 0x29: {
            return ";";
          }
          case 0x2a: {
            return "\\";
          }
          case 0x2b: {
            return ",";
          }
          case 0x2c: {
            return "/";
          }
          case 0x2d: {
            return "N";
          }
          case 0x2e: {
            return "M";
          }
          case 0x2f: {
            return ".";
          }
          case 0x32: {
            return "?";
          }
          case 0x41: {
            return ".";
          }
          case 0x43: {
            return "*";
          }
          case 0x45: {
            return "+";
          }
          case 0x47: {
            return "?";
          }
          case 0x4b: {
            return "%";
          }
          case 0x4c: {
            return "return";
          }
          case 0x4e: {
            return "-";
          }
          case 0x51: {
            return "=";
          }
          case 0x52: {
            return "0";
          }
          case 0x53: {
            return "1";
          }
          case 0x54: {
            return "2";
          }
          case 0x55: {
            return "3";
          }
          case 0x56: {
            return "4";
          }
          case 0x57: {
            return "5";
          }
          case 0x58: {
            return "6";
          }
          case 0x59: {
            return "7";
          }
          case 0x5b: {
            return "8";
          }
          case 0x5c: {
            return "9";
          }
          case 0x24: {
            return "Return";
          }
          case 0x30: {
            return "Tab";
          }
          case 0x31: {
            return "Space";
          }
          case 0x33: {
            return "Delete";
          }
          case 0x35: {
            return "Escape";
          }
          case 0x37: {
            return "Command";
          }
          case 0x38: {
            return "Shift";
          }
          case 0x39: {
            return "CapsLock";
          }
          case 0x3a: {
            return "Option";
          }
          case 0x3b: {
            return "Control";
          }
          case 0x3c: {
            return "RightShift";
          }
          case 0x3d: {
            return "RightOption";
          }
          case 0x3e: {
            return "RightControl";
          }
          case 0x3f: {
            return "Function";
          }
          case 0x40: {
            return "F17";
          }
          case 0x48: {
            return "VolumeUp";
          }
          case 0x49: {
            return "VolumeDown";
          }
          case 0x4a: {
            return "Mute";
          }
          case 0x4f: {
            return "F18";
          }
          case 0x50: {
            return "F19";
          }
          case 0x5a: {
            return "F20";
          }
          case 0x60: {
            return "F5";
          }
          case 0x61: {
            return "F6";
          }
          case 0x62: {
            return "F7";
          }
          case 0x63: {
            return "F3";
          }
          case 0x64: {
            return "F8";
          }
          case 0x65: {
            return "F9";
          }
          case 0x67: {
            return "F11";
          }
          case 0x69: {
            return "F13";
          }
          case 0x6a: {
            return "F16";
          }
          case 0x6b: {
            return "F14";
          }
          case 0x6d: {
            return "F10";
          }
          case 0x6f: {
            return "F12";
          }
          case 0x71: {
            return "F15";
          }
          case 0x72: {
            return "Help";
          }
          case 0x73: {
            return "Home";
          }
          case 0x74: {
            return "PageUp";
          }
          case 0x75: {
            return "ForwardDelete";
          }
          case 0x76: {
            return "F4";
          }
          case 0x77: {
            return "End";
          }
          case 0x78: {
            return "F2";
          }
          case 0x79: {
            return "PageDown";
          }
          case 0x7a: {
            return "F1";
          }
          case 0x7b: {
            return "LeftArrow";
          }
          case 0x7c: {
            return "RightArrow";
          }
          case 0x7d: {
            return "DownArrow";
          }
          case 0x7e: {
            return "UpArrow";
          }
          default: {
            return "??";
          }
        }
      }

      function getModifiersForBitmask(bitMask, leftRight) {
        console.log("modifier", bitMask);
        if (bitMask === 0 || bitMask === -1) {
          return "";
        }
        var modifierString = "";

        if (leftRight && (bitMask & 0x00000001 || bitMask & 0x00002000)) {
          if (bitMask & 0x00000001) {
            modifierString += "⌃(L)";
          }
          if (bitMask & 0x00002000) {
            modifierString += "⌃(R)";
          }
        } else {
          if (bitMask & (1 << 18)) {
            modifierString += "⌃";
          }
        }

        if (leftRight && (bitMask & 0x00000002 || bitMask & 0x00000004)) {
          if (bitMask & 0x00000002) {
            modifierString += "⇧(L)";
          }
          if (bitMask & 0x00000004) {
            modifierString += "⇧(R)";
          }
        } else {
          if (bitMask & (1 << 17)) {
            modifierString += "⇧";
          }
        }

        if (leftRight && (bitMask & 0x00000010 || bitMask & 0x00000008)) {
          if (bitMask & 0x00000010) {
            modifierString += "⌘(R)";
          }
          if (bitMask & 0x00000008) {
            modifierString += "⌘(L)";
          }
        } else {
          if (bitMask & (1 << 20)) {
            modifierString += "⌘";
          }
        }

        if (leftRight && (bitMask & 0x00000020 || bitMask & 0x00000040)) {
          if (bitMask & 0x00000020) {
            modifierString += "⌥(L)";
          }
          if (bitMask & 0x00000040) {
            modifierString += "⌥(R)";
          }
        } else {
          if (bitMask & (1 << 19)) {
            modifierString += "⌥";
          }
        }

        if (bitMask & (1 << 23)) {
          modifierString += "fn";
        }

        console.log("modifierstring", modifierString);
        return modifierString;
      }

      function getShortcutString(value) {
        var kCommand = 0x37;
        var kShift = 0x38;
        var kOption = 0x3a;
        var kControl = 0x3b;
        var kRightCommand = 0x36;
        var kRightShift = 0x3c;
        var kRightOption = 0x3d;
        var kRightControl = 0x3e;
        var kFunction = 0x3f;
        var modsString = "";
        if (
          value.length > 2 &&
          parseInt(value.substring(0, 2), 10) === kFunction
        ) {
          modsString = "fn " + modsString;
          value = value.substring(3);
        }
        if (
          value.length > 2 &&
          parseInt(value.substring(0, 2), 10) === kControl
        ) {
          modsString += "⌃";
          value = value.substring(3);
        }
        if (
          value.length > 2 &&
          parseInt(value.substring(0, 2), 10) === kOption
        ) {
          modsString += "⌥";
          value = value.substring(3);
        }
        if (
          value.length > 2 &&
          parseInt(value.substring(0, 2), 10) === kShift
        ) {
          modsString += "⇧";
          value = value.substring(3);
        }
        if (
          value.length > 2 &&
          parseInt(value.substring(0, 2), 10) === kCommand
        ) {
          modsString += "⌘";
          value = value.substring(3);
        }
        if (value.length > 2 && value.substring(0, 2) === "aL") {
          modsString = "l⌥ " + modsString;
          value = value.substring(3);
        }
        if (
          value.length > 2 &&
          parseInt(value.substring(0, 2), 10) === kRightOption
        ) {
          modsString = "r⌥ " + modsString;
          value = value.substring(3);
        }
        if (value.length > 2 && value.substring(0, 2) === "sL") {
          modsString = "l⇧ " + modsString;
          value = value.substring(3);
        }
        if (
          value.length > 2 &&
          parseInt(value.substring(0, 2), 10) === kRightShift
        ) {
          modsString = "l⇧ " + modsString;
          value = value.substring(3);
        }
        if (value.length > 2 && value.substring(0, 2) === "cL") {
          modsString = "l⌘ " + modsString;
          value = value.substring(3);
        }
        if (
          value.length > 2 &&
          parseInt(value.substring(0, 2), 10) === kRightCommand
        ) {
          modsString = "r⌘ " + modsString;
          value = value.substring(3);
        }
        if (value.length > 2 && value.substring(0, 2) === "rL") {
          modsString = "l⌃ " + modsString;
          value = value.substring(3);
        }
        if (
          value.length > 2 &&
          parseInt(value.substring(0, 2), 10) === kRightControl
        ) {
          modsString = "r⌃ " + modsString;
          value = value.substring(3);
        }
        return (
          modsString + " " + getKeycodeStringForKeycode(parseInt(value, 10))
        );
      }
      function getActionNameForID(aid, trigger) {
        switch (aid) {
          case -1: {
            if (trigger.BTTTriggerType === 630) {
              return "Open Group";
            }
            if (trigger.BTTShortcutToSend) {
              return (
                "Execute Keyboard Shortcut: " +
                getShortcutString(trigger.BTTShortcutToSend)
              );
            }
            return "No Action";
          }

          default:
            return trigger.BTTPredefinedActionName;
        }
      }

      function BTTInitialize() {}

      function BTTWillCloseWindow() {}

      function BTTWillHideWindow() {}
    </script>
  </head>
  <body>
    <div
      id="triggerList"
      style="
        height: 100%;
        display: flex;
        flex-direction: column;
        align-items: left;
        justify-content: space-between;
      "
    ></div>
  </body>
</html>

If you get triggers via the get_triggers call they will also have a property BTTTriggerBelongsToPreset, so you could filter the list by preset. To filter by app you'd add the trigger_app_bundle_identifier filter ( Using Apple Script or JXA · GitBook (folivora.ai) )

It would also be easy to make the items clickable to trigger the action or you could make it always show the shortcuts for the active application.

Hi, @Andreas_Hegenberg. Thanks so much for the reply!

This looks very powerful and promising!

I suspect it does as did a quick test with the code you provided. (I'm using BTT v4.062 [Setapp].)

  1. In Preset: Default, All Apps I created a Keyboard Shortcut trigger with the action: Show Floating WebView: Show KM Hotkeys.

  2. In the HTML/Content>Instead of a URL Path you..., I pasted your code.

When using the Keyboard Shortcut to trigger, Safari moved to the foreground, but nothing appeared.

Maybe I'm missing something, or as you suspected, the latest alpha is required. Since I'm using the Setapp version, I don't think I have access to other versions. If I purchase a standalone license of BTT, is there anything I'd need to do to maintain my current BTT configuration?

Thanks again for the information, @Andreas_Hegenberg. I'm fairly adept with Keyboard Maestro, but very green with BTT. I do look forward to learning more!

Hi, @Frank1. This is what I've worked out so far.

Since this mostly pertains to Keyboard Maestro, when I get some extra time, I'll create a tutorial post at Keyboard Maestro Forum>Tips and Tutorials and then post a link to it in this thread.

Hi @jim-sauer :slightly_smiling_face:

What palette did you choose? These all seem to be macros for the KM editor, so app specific? Does the palette appear when you hold down a modifier on the right side (of the spacebar)? Do you use a key sequence in BTT to trigger the action "Show Macro Group / show palette" in KM?

While testing something else I improved the HTML posted above a little bit.

It's now functional (clicking one of the items triggers the action)

Could be a good starting point for others, but requires btt > 4.063, should be on Setapp soon.

  <head>
    <style>
      #triggerList {
        height: 100%;
        display: grid;
        grid-template-columns: 1fr 1fr 1fr;
        align-items: left;
        justify-content: space-between;
      }

      .trigger {
        border-radius: 4px;
        background: lightblue;
        padding: 4px;
        margin: 4px;
        border: 1 px solid gray;
        display: flex;
        flex-direction: column;
        justify-content: space-around;
        align-items: center;
        align-content: center;
      }

      .trigger:hover {
        cursor: hand;
        background: gray;
      }
    </style>
    <script>
          async function BTTWindowWillBecomeVisible() {
        console.log("loading triggers");
        const allNamedTriggersJSONString = await callBTT("get_triggers", {
          trigger_type: "BTTTriggerTypeKeyboardShortcut",
        });
        let allTriggersJSONArray = JSON.parse(allNamedTriggersJSONString);
        let allKeyboardShortcuts = "";
        for (const trigger of allTriggersJSONArray) {
          if (trigger.BTTShortcutModifierKeys) {
            const shortcut = `<div class="trigger" onclick="callBTT('execute_assigned_actions_for_trigger', {uuid: '${
              trigger.BTTUUID
            }'})"><span><b>${
              trigger?.BTTTriggerConfig?.BTTLeftRightModifierDifferentiation
                ? getModifiersForBitmask(
                    trigger.BTTAdditionalConfiguration,
                    true
                  )
                : getModifiersForBitmask(trigger.BTTShortcutModifierKeys, false)
            }${getKeycodeStringForKeycode(
              trigger.BTTShortcutKeyCode
            )}</b></span> <span>${getActionNameForID(
              trigger.BTTPredefinedActionType,
              trigger
            )}</span></div>`;
            allKeyboardShortcuts = allKeyboardShortcuts + shortcut;
          }
        }
        document.getElementById("triggerList").innerHTML = allKeyboardShortcuts;
      }
      
      function getKeycodeStringForKeycode(keycode) {
        switch (keycode) {
          case 0x00: {
            return "A";
          }
          case 0x01: {
            return "S";
          }
          case 0x02: {
            return "D";
          }
          case 0x03: {
            return "F";
          }
          case 0x04: {
            return "H";
          }
          case 0x05: {
            return "G";
          }
          case 0x06: {
            return "Z";
          }
          case 0x07: {
            return "X";
          }
          case 0x08: {
            return "C";
          }
          case 0x09: {
            return "V";
          }
          case 0x0b: {
            return "B";
          }
          case 0x0c: {
            return "Q";
          }
          case 0x0d: {
            return "W";
          }
          case 0x0e: {
            return "E";
          }
          case 0x0f: {
            return "R";
          }
          case 0x10: {
            return "Y";
          }
          case 0x11: {
            return "T";
          }
          case 0x12: {
            return "1";
          }
          case 0x13: {
            return "2";
          }
          case 0x14: {
            return "3";
          }
          case 0x15: {
            return "4";
          }
          case 0x16: {
            return "6";
          }
          case 0x17: {
            return "5";
          }
          case 0x18: {
            return "=";
          }
          case 0x19: {
            return "9";
          }
          case 0x1a: {
            return "7";
          }
          case 0x1b: {
            return "-";
          }
          case 0x1c: {
            return "8";
          }
          case 0x1d: {
            return "0";
          }
          case 0x1e: {
            return "]";
          }
          case 0x1f: {
            return "O";
          }
          case 0x20: {
            return "U";
          }
          case 0x21: {
            return "[";
          }
          case 0x22: {
            return "I";
          }
          case 0x23: {
            return "P";
          }
          case 0x25: {
            return "L";
          }
          case 0x26: {
            return "J";
          }
          case 0x27: {
            return '"';
          }
          case 0x28: {
            return "K";
          }
          case 0x29: {
            return ";";
          }
          case 0x2a: {
            return "\\";
          }
          case 0x2b: {
            return ",";
          }
          case 0x2c: {
            return "/";
          }
          case 0x2d: {
            return "N";
          }
          case 0x2e: {
            return "M";
          }
          case 0x2f: {
            return ".";
          }
          case 0x32: {
            return "?";
          }
          case 0x41: {
            return ".";
          }
          case 0x43: {
            return "*";
          }
          case 0x45: {
            return "+";
          }
          case 0x47: {
            return "?";
          }
          case 0x4b: {
            return "%";
          }
          case 0x4c: {
            return "return";
          }
          case 0x4e: {
            return "-";
          }
          case 0x51: {
            return "=";
          }
          case 0x52: {
            return "0";
          }
          case 0x53: {
            return "1";
          }
          case 0x54: {
            return "2";
          }
          case 0x55: {
            return "3";
          }
          case 0x56: {
            return "4";
          }
          case 0x57: {
            return "5";
          }
          case 0x58: {
            return "6";
          }
          case 0x59: {
            return "7";
          }
          case 0x5b: {
            return "8";
          }
          case 0x5c: {
            return "9";
          }
          case 0x24: {
            return "Return";
          }
          case 0x30: {
            return "Tab";
          }
          case 0x31: {
            return "Space";
          }
          case 0x33: {
            return "Delete";
          }
          case 0x35: {
            return "Escape";
          }
          case 0x37: {
            return "Command";
          }
          case 0x38: {
            return "Shift";
          }
          case 0x39: {
            return "CapsLock";
          }
          case 0x3a: {
            return "Option";
          }
          case 0x3b: {
            return "Control";
          }
          case 0x3c: {
            return "RightShift";
          }
          case 0x3d: {
            return "RightOption";
          }
          case 0x3e: {
            return "RightControl";
          }
          case 0x3f: {
            return "Function";
          }
          case 0x40: {
            return "F17";
          }
          case 0x48: {
            return "VolumeUp";
          }
          case 0x49: {
            return "VolumeDown";
          }
          case 0x4a: {
            return "Mute";
          }
          case 0x4f: {
            return "F18";
          }
          case 0x50: {
            return "F19";
          }
          case 0x5a: {
            return "F20";
          }
          case 0x60: {
            return "F5";
          }
          case 0x61: {
            return "F6";
          }
          case 0x62: {
            return "F7";
          }
          case 0x63: {
            return "F3";
          }
          case 0x64: {
            return "F8";
          }
          case 0x65: {
            return "F9";
          }
          case 0x67: {
            return "F11";
          }
          case 0x69: {
            return "F13";
          }
          case 0x6a: {
            return "F16";
          }
          case 0x6b: {
            return "F14";
          }
          case 0x6d: {
            return "F10";
          }
          case 0x6f: {
            return "F12";
          }
          case 0x71: {
            return "F15";
          }
          case 0x72: {
            return "Help";
          }
          case 0x73: {
            return "Home";
          }
          case 0x74: {
            return "PageUp";
          }
          case 0x75: {
            return "ForwardDelete";
          }
          case 0x76: {
            return "F4";
          }
          case 0x77: {
            return "End";
          }
          case 0x78: {
            return "F2";
          }
          case 0x79: {
            return "PageDown";
          }
          case 0x7a: {
            return "F1";
          }
          case 0x7b: {
            return "LeftArrow";
          }
          case 0x7c: {
            return "RightArrow";
          }
          case 0x7d: {
            return "DownArrow";
          }
          case 0x7e: {
            return "UpArrow";
          }
          default: {
            return "??";
          }
        }
      }

      function getModifiersForBitmask(bitMask, leftRight) {
        console.log("modifier", bitMask);
        if (bitMask === 0 || bitMask === -1) {
          return "";
        }
        var modifierString = "";

        if (leftRight && (bitMask & 0x00000001 || bitMask & 0x00002000)) {
          if (bitMask & 0x00000001) {
            modifierString += "⌃(L)";
          }
          if (bitMask & 0x00002000) {
            modifierString += "⌃(R)";
          }
        } else {
          if (bitMask & (1 << 18)) {
            modifierString += "⌃";
          }
        }

        if (leftRight && (bitMask & 0x00000002 || bitMask & 0x00000004)) {
          if (bitMask & 0x00000002) {
            modifierString += "⇧(L)";
          }
          if (bitMask & 0x00000004) {
            modifierString += "⇧(R)";
          }
        } else {
          if (bitMask & (1 << 17)) {
            modifierString += "⇧";
          }
        }

        if (leftRight && (bitMask & 0x00000010 || bitMask & 0x00000008)) {
          if (bitMask & 0x00000010) {
            modifierString += "⌘(R)";
          }
          if (bitMask & 0x00000008) {
            modifierString += "⌘(L)";
          }
        } else {
          if (bitMask & (1 << 20)) {
            modifierString += "⌘";
          }
        }

        if (leftRight && (bitMask & 0x00000020 || bitMask & 0x00000040)) {
          if (bitMask & 0x00000020) {
            modifierString += "⌥(L)";
          }
          if (bitMask & 0x00000040) {
            modifierString += "⌥(R)";
          }
        } else {
          if (bitMask & (1 << 19)) {
            modifierString += "⌥";
          }
        }

        if (bitMask & (1 << 23)) {
          modifierString += "fn";
        }

        console.log("modifierstring", modifierString);
        return modifierString;
      }

      function getShortcutString(value) {
        var kCommand = 0x37;
        var kShift = 0x38;
        var kOption = 0x3a;
        var kControl = 0x3b;
        var kRightCommand = 0x36;
        var kRightShift = 0x3c;
        var kRightOption = 0x3d;
        var kRightControl = 0x3e;
        var kFunction = 0x3f;
        var modsString = "";
        if (
          value.length > 2 &&
          parseInt(value.substring(0, 2), 10) === kFunction
        ) {
          modsString = "fn " + modsString;
          value = value.substring(3);
        }
        if (
          value.length > 2 &&
          parseInt(value.substring(0, 2), 10) === kControl
        ) {
          modsString += "⌃";
          value = value.substring(3);
        }
        if (
          value.length > 2 &&
          parseInt(value.substring(0, 2), 10) === kOption
        ) {
          modsString += "⌥";
          value = value.substring(3);
        }
        if (
          value.length > 2 &&
          parseInt(value.substring(0, 2), 10) === kShift
        ) {
          modsString += "⇧";
          value = value.substring(3);
        }
        if (
          value.length > 2 &&
          parseInt(value.substring(0, 2), 10) === kCommand
        ) {
          modsString += "⌘";
          value = value.substring(3);
        }
        if (value.length > 2 && value.substring(0, 2) === "aL") {
          modsString = "l⌥ " + modsString;
          value = value.substring(3);
        }
        if (
          value.length > 2 &&
          parseInt(value.substring(0, 2), 10) === kRightOption
        ) {
          modsString = "r⌥ " + modsString;
          value = value.substring(3);
        }
        if (value.length > 2 && value.substring(0, 2) === "sL") {
          modsString = "l⇧ " + modsString;
          value = value.substring(3);
        }
        if (
          value.length > 2 &&
          parseInt(value.substring(0, 2), 10) === kRightShift
        ) {
          modsString = "l⇧ " + modsString;
          value = value.substring(3);
        }
        if (value.length > 2 && value.substring(0, 2) === "cL") {
          modsString = "l⌘ " + modsString;
          value = value.substring(3);
        }
        if (
          value.length > 2 &&
          parseInt(value.substring(0, 2), 10) === kRightCommand
        ) {
          modsString = "r⌘ " + modsString;
          value = value.substring(3);
        }
        if (value.length > 2 && value.substring(0, 2) === "rL") {
          modsString = "l⌃ " + modsString;
          value = value.substring(3);
        }
        if (
          value.length > 2 &&
          parseInt(value.substring(0, 2), 10) === kRightControl
        ) {
          modsString = "r⌃ " + modsString;
          value = value.substring(3);
        }
        return (
          modsString + " " + getKeycodeStringForKeycode(parseInt(value, 10))
        );
      }
      function getActionNameForID(aid, trigger) {
        switch (aid) {
          case -1: {
            if (trigger.BTTTriggerType === 630) {
              return "Open Group";
            }
            if (trigger.BTTShortcutToSend) {
              return (
                "Execute Keyboard Shortcut: " +
                getShortcutString(trigger.BTTShortcutToSend)
              );
            }
            return "No Action";
          }

          default:
            return trigger.BTTPredefinedActionName;
        }
      }

    </script>
  </head>
  <body>
    <div id="triggerList"></div>
  </body>
</html>

1 Like

Great !

I have modified this one line...
over here I have a lot of disabled triggers and separator lines...otherwise
Thx,
Christian

for (const trigger of allTriggersJSONArray) {
if (trigger.BTTShortcutModifierKeys > 0) {

2 Likes

Yep in my version it just outputs all shortcuts. You can also only include triggers with trigger.BTTEnabled === 1 to exclude disabled ones.

1 Like

I may not understand anything about it, but what's the difference in doing the same thing with the Stream Deck Emulator, only much easier from my point of view.

What I posted will automatically retrieve your currently configured shortcuts. With the stream deck emulator you'd need to set every shortcut up manually.

With some additions you could e.g. make it always show the currently configured shortcuts for the active app.

1 Like

Ah, ok, it will find the BTT shortcuts that trigger the KM macros, speaking from @jim-sauer's point of view.

1 Like

Exactly. (Although to only show the KM shortcuts you'd need to adapt the get_triggers query like this:

   const allKeyboardShortcutsJSONString = await callBTT("get_triggers", {
          trigger_type: "BTTTriggerTypeKeyboardShortcut",
trigger_app_bundle_identifier: "com.stairways.keyboardmaestro.engine"
        });

(not sure about the bundle identifier!). Or if they are not assigned to keyboard maestro in BTT, you'd need to add some other identifier that you could use for filtering e.g. into the notes field.

1 Like

If this works, many KM users will be very happy ... if they also use BTT. But since I am mainly a BTT user, I am much more interested in the new floating menus.

2 Likes

Thx Andreas,
that is what I was looking for !

I have now some issue with long/short keyboard triggers, which are listed somehow multiple times.I want basically select only active keyboard triggers including description/notes like these:

here there are multiple times...

Christian

Do you maybe have the duplicates in some disabled preset? Or in some other app?
By default it will return all triggers from all presets and all apps.

The palette is displayed using the Show Palette of Macros KM action. I created that palette using a handy macro that @noisneil shared: Create and Populate a 'Show Palette of Macros' Macro.

Also, the order of the palette entries was adjusted using @DanThomas's Palette Organizer.

Yes. By triggering all of these with the right-modifiers I now obviously have more left-modifiers available for other purposes.

Right-⌥⌘P triggered from BTT. I also added a KM-native trigger: ⌃F12.

Since the palette entries run the underlying KM macros, this could all be run without BTT; but generally I'll use the BTT right-modifier Keyboard Shortcuts.


To save time, I created a simple KM macro that generates the required AppleScript to launch macros from BTT. Here are two examples, the first without a parameter and the second with one.

-- Show .BTT Palatte

tell application "Keyboard Maestro Engine"
	do script "8F5F1AED-8BD7-4911-BD47-E76D39D502CA"
end tell

-- Set Action Color

property theP : "red"

tell application "Keyboard Maestro Engine"
	do script "7ECFBA5D-3708-403D-9A58-0666C1246AD4" with parameter theP
end tell

BTW, @Frank1 since my earlier post, I've added a few items. The image in the above post has been updated.

Here's what this looks like in the KM editor.


Once the Setapp version of BTT is updated, I'll explore the technique @Andreas_Hegenberg has shared in this thread and/or the forthcoming Floating Palettes you mentioned. In the meantime, this approach works quite nicely.

Apart from the fact that I don’t know what the difference is between "Show Palette of Macros" and "Show Macro Group / show Palette", I more or less understand what you have set up. :slightly_smiling_face:

Probably you want it exactly like this, but just in case, you can also show/hide your palette with eg. right ⌘ (alone) instead of Right-⌥⌘P. BTT offers more than KM when it comes to string triggers. BTT can also do this only with modifiers.

There are two options for a Key Sequence that triggers your KM shortcut for the palette.

(1) Tap right ⌘, shows the palette, tap again hides the palette.

(2) Press and hold right ⌘, shows the palette, releasing the key hides the palette.

(2) seems very convenient to me. But that is of course a matter of opinion. :slightly_smiling_face:

The Show Palette of Macros action, is harder to set up, but more flexible. It allows you to select the specific macros to include. That's where @noisneil's macro that I mentioned above is so helpful: just select the desired macros, run his macro, and a on-action macro is created that includes the Show Palette of Macros action.

Note, there is a typo on the Show Palette of Macros wiki page:

The Show Palette of Macros action (v8+) allows you to display a palette of selected actions macros and select which one to execute.


Thanks, that sounds nice. I'll poke around and see if I can figure that out. I'm a BTT newbie.

Ah, sorry, I could have posted that. It is a key sequence, very simple :slightly_smiling_face:

And 0.2 s is a good value because the normal shortcuts with r⌘ + letter are not disturbed.

1 Like

Thanks for directing me and suggesting the 0.2s value. The Deutsch dialog is a bonus! :grinning:

1 Like