BetterTouchTool Simple JSON Format
BetterTouchTool 4.690 introduced a new "Simple JSON Format", which can currently be used with the predefined actions "Show Custom Context Menu (New)" and "Choose From List". It allows to create those menus dynamically using Java Script & JSON.
Soon this will also be supported for Floating Menus.
4.690 now contains a first version of this that works with the predefined actions "Custom Context Menu New" and "Choose From List". Unfortunately I will need a few more hours to document this properly as it became quite powerful.
Here is what I started typing - it's probably not very useful yet - but I'm now on a day trip with the kids. Will finish the docs this evening.
You need to click the "Retrieve Content Dynamically Via Java Script" checkbox.
Then you can have a function like this that returns a JSON:
Simple example
async function retrieveJSON() {
let items = [
{
"title": "trigger some shortcut",
"icon": "sfsymbol::globe",
"action": "shortcut::TheShortcutFromTheShortcutsApp"
},
{
"title": "trigger some named trigger",
"icon": "sfsymbol::house",
"action": "named::TheNameOfTheBTTNamedTrigger"
},
{
"title": "trigger some BTT trigger via UUID",
"icon": "sfsymbol::star",
"action": "uuid::4ff033d2-8f67-490d-b281-3124d452ef07"
}
];
return JSON.stringify(items);
}
Complex example
async function retrieveJSON() {
let items = [
{
"title": "hello world",
"icon": "sfsymbol::globe"
},
{
title: "some title",
subtitle: "some subtitle",
subitems: [
{
title: "some sub-item",
action: "named::NameOfNamedTriggerInBTT",
icon: "sfsymbol::house::weight@@light::size@@30"
},
{
title: "some sub-item2",
icon: "base64::iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mNkYPhfz0AEYBxVSF+FAP5FDvcfRYWgAAAAAElFTkSuQmCC",
action: "shortcut::NameOfShortcutInShortcutsApp@@OptionalInput"
},
],
icon: "sfsymbol::star::color@@B53E93",
// this shows various forms of actions. In case you are just triggering
// a single action, it does not need to be an array.
action: [
`btt::paste_text@@{"text": "hello world"}`,
"keyboard::0", //type a
"keyboard::11", //type b
"keyboard::8", //type c
"js::runShellScript({script: `say hello`})",
{
btt: "paste_text",
args: {
text: "hello world 2",
insert_by_pasting: true,
},
},
{
shortcut: "theNameOfTheShortcut",
input: "some optional input",
},
],
},
];
return JSON.stringify(items);
}
You'll find more examples on https://community.folivora.ai for example here is an example that will list all apps / files in a specified folder: Example to list all files in a folder
Allowed keys:
title
supported properties color & size
Examples:
{
"title": "hello::size@@30::color@@#000000"
}
Alternative syntax:
{
"title": {
"text": "some title",
"size": 30,
"color": "#000000"
}
}
subtitle
supported properties color & size. Only works in the "choose from list action".
Examples:
{
"subtitle": "some subtitle::size@@30::color@@#000000"
}
Alternative syntax:
{
"subtitle": {
"text": "some subtitle",
"size": 30,
"color": "#000000"
}
}
subitems
Examples:
{
"title": "submenu",
"subitems": [
{
"title": "hello"
},
{
"title": "world"
}
]
}
setvariable
this allows to set a variable value before the action is executed
Examples:
{
"setvariable": "variablename@@variablevalue"
}
Alternative syntax:
{
"setvariable": {
"name": "variablename",
"variablevalue": "test"
}
}
icon
this allows to define an icon
Supported properties: type (sfsymbol, size, path, base64, background, width, height)
Examples:
{
"icon": "sfsymbol::star"
}
Or with more options:
{
"icon": "sfsymbol::star::weight@@light::color@@#fefefe::color@@#000000::background@@#B53E93"
}
From file:
{
"icon": "path::~/Downloads/test.png::width@@40::height@@40"
}
From base64:
{
"icon": "base64::base64code"
}
Alternative syntax:
{
"icon": {
"type": "sfymbol",
"sfymbol": "star",
"weight": "light",
"colors" : ["#000000", "#fefefe"],
"background": "#B53E93",
"size" : 30
}
}
action
supported properties named, shortcut, js, keyboard, uuid, btt. Can also be an array of actions.
named
This will trigger a named trigger configured in BTT (see http://docs.folivora.ai/docs/1002_named_triggers.html )
{"title": "trigger named trigger", "action": "named::theNameOfTheNamedTrigger"},
Alternative syntax:
{
"title": "trigger named trigger alternative",
"action": {
"named": "helloworld"
},
"icon": "sfsymbol::star.leadinghalf.filled"
}
uuid
This will trigger any trigger configured in BTT using its UUID
{"title": "trigger via uuid", "action": "uuid::4ff033d2-8f67-490d-b281-3124d452ef07"},
Alternative syntax:
{
"title": "trigger via UUID alternative",
"action": {
"uuid": "4ff033d2-8f67-490d-b281-3124d452ef07"
}
}
shortcut
This will trigger a shortcut configured in Apple's shortcuts app
{"title": "trigger shortcut form shortcuts app", "action": "shortcut::theNameOfTheShortcut@@someOptionalInput"},
Alternative syntax:
{
"title": "trigger shortcut form shortcuts app alternative",
"action": {
"shortcut": "theNameOfTheShortcut",
"input": "some optional input"
}
}
js
This will run BTT Java Script which also allows to run shell scripts or apple scripts and trigger ny of BTT's scripting functions (http://docs.folivora.ai/docs/1106_java_script.html)
E.g. run some shell script:
{"title": "say hello", "action": "js::runShellScript({script: `say hello`})"},
Or run some arbitrary apple script:
{
title: "show some apple script dialog",
action: {
js: `(async () => {
// put the Apple Script into a string (back ticks are great for multi-line strings)
let appleScript = \`
set theDialogText to "The curent date and time is " & (current date) & "."
set result to display dialog theDialogText
return result
\`;
// this will execute the Apple Script and store the result in the result variable.
let result = await runAppleScript(appleScript);
// do whatever you want with the result
// at the end you always need to call returnToBTT to exit the script / return the value to BTT.
returnToBTT(result);
// it is important that this function self-executes ()
})()
`,
},
},
btt
This can run any of BTT's scripting functions (http://docs.folivora.ai/docs/1102_apple_script.html ). To see how to configure & trigger a specific action you can right-click the action in BTT and choose "copy java script to trigger action".
"action": `btt::paste_text@@{"text": "hello world"}`
Alternative syntax
{
"title": "paste item 1",
"action": {
"btt":"paste_text",
"args": {
"text": "ddasdas",
"insert_by_pasting": true
}
}
},
{
"title": "application expose",
"action": {
"btt": "trigger_action",
"args": {
"json": {
"BTTActionCategory": 0,
"BTTPredefinedActionType": 6,
"BTTPredefinedActionName": "Application Expose"
}
}
}
}
keyboard
This can trigger a keyboard shortcut.
It needs to constist of comma separated modifier keys (cmd, shift, opt, fn, ctrl) and one key code at the end. A list of key codes can e.g. be found here: https://eastmanreference.com/complete-list-of-applescript-key-codes
"action": "keyboard::shift,opt,0"
Examples
Get result from Apple Shortcut, e.g. some weather conditions:
async function retrieveJSON() {
let weather = await runAppleShortcut({name: "weather", "input": ""});
let items = [
{"title": weather},
{"title": "test item 2"},
{"title": "test item 3", "icon": "sfsymbol::star"}
];
return JSON.stringify(items);
}
Execute multiple actions from one item (example types abc)
async function retrieveJSON() {
let items = [
{
"title": "multiple actions (type abc)",
"action": [
"keyboard::0", //type a
"keyboard::11", //type b
"keyboard::8", //type c
],
"icon": "sfsymbol::keyboard"
},
];
return JSON.stringify(items);
}
Fetch menu from some server:
async function retrieveJSON() {
const response = await fetch('https://folivora.ai/various/test-menu.json');
const data = await response.json();
const jsonString = JSON.stringify(data);
return jsonString;
}
Works with custom menubar items as well when assigning the "Show Context Menu New" action: