Run Real JavaScript not running on a Menubar Item click

Describe the bug
Running "Real Javascript from the Menubar doesn't seem to work. If I compile / test the javascript it works fine. See attached video.

Affected input device (e.g. MacBook Trackpad, Magic Mouse/Trackpad, Touch Bar, etc.):
Run Real JavaScript
Menubar Item


Device information:

  • Type of Mac: 13" M1 MBP
  • macOS version: 13.4
  • BetterTouchTool version: 4.155 (2369)

Any help would be much appreciated :blush:

Most likely it's due to context reuse. When running in the editor, it will always start a fresh context, but outside of the editor it will reuse a global context for performance reasons. This can lead to errors if variables are already defined.

Try putting your script in a self executing function:

(async () => {
   // your script here

I'll make this more clear soon.

Thanks for the quick response. I believe I am actually doing that. I based the original script off another forum post I saw here. And it was working until recently (forgot to mention that in the OP)

I don't know much about javascript (I code mostly in Swift), so forgive me for the quality of this code. Here is my script:


// - Toggles the "Hide Menu Bar Icons Left of the BTT Icon" and if it is not hidden for 30 seconds, hides it automatically


// NOTE: UUID - is of the Menubar Item Trigger
// Variable Name - can be anything 

// ref:
const delay = ms => new Promise(res => setTimeout(res, ms));

(async ()=> {

let isOn = await callBTT('get_number_variable', {variable_name:'isButtonOn'})

var offJSON = {uuid: '4BCBF29A-1574-4BCD-8847-D765C263E3EA',
    "text" : "Off",
	"sf_symbol_name": "",
	//"background_color": "200,50,100,255"

var onJSON = {uuid: '4BCBF29A-1574-4BCD-8847-D765C263E3EA',
    "text" : "ON",
	"sf_symbol_name": "chevron.backward",
	//"background_color": "50,200,100,255"

let actionDefinition = {
    "BTTPredefinedActionType" : 265,
    "BTTPredefinedActionName" : "Show \/ Hide Menu Bar Icons left of BTT Icon"

await callBTT('trigger_action', {json: JSON.stringify(actionDefinition)});

if(isOn === 1) {
 await callBTT('update_menubar_item', offJSON);
 await callBTT('set_number_variable', {variable_name:'isButtonOn', to: 0});

} else {
 await callBTT('update_menubar_item', onJSON);
 await callBTT('set_number_variable', {variable_name:'isButtonOn', to: 1});
//  (async ()=> {
// 	await delay(30_000); // 30 seconds
// 	let isOn = await callBTT('get_number_variable', {variable_name:'isButtonOn'})

// 	if(isOn === 1) {
// 		await callBTT('trigger_action', {json: JSON.stringify(actionDefinition)});
// 	 	await callBTT('update_menubar_item', offJSON);
// 	 	await callBTT('set_number_variable', {variable_name:'isButtonOn', to: 0});
// 	} 

// })()




Right at the beginning you have

const delay = ms => new Promise(res => setTimeout(res, ms));

This will immediately throw an error if it has been defined before. That means the rest of the script won't run at all.

If you move that inside of your async function it sould work

1 Like

By the way, the new BTT and latest macOS version make it possible to debug the java script stuff using Safari's develop menu. This makes it way easier to find what's going on:

1 Like

Oh wow, that was it! Thank you for an amazingly fast reply and solution. I have no idea how that wasn't an issue before.

And debugging in Safari's develop menu is a sweet tip. I will definitely use that. Thanks a ton! :raised_hands: