Faster way to add a list of items

Subject: Easier Way to Populate Lists from External Files (CSV/TXT) in BetterTouchTool?

Sometimes it can be quite tedious to populate lists in BetterTouchTool — for example, when creating a list of folder paths or items to choose from in a floating menu. Right now, it seems I have to add each item manually, either as separate rules or line-by-line in a list widget.

I’m wondering:
Is there a more efficient way to populate lists in BTT — ideally by referencing a CSV or plain text file that can be easily edited externally?


Example 1 — List for Quick Pasting in Safari

I currently use a shortcut that brings up a list of email addresses, physical addresses, etc., in Safari — allowing me to quickly paste them. However, each item in that list requires its own trigger, even though they all use the same gesture. This quickly becomes messy and hard to maintain.


Example 2 — CSV-Based Script (Workaround)

I’ve created a partial workaround using AppleScript in BTT, which loads a list from a CSV file. I can select and copy an item from it — in this case, I used it to paste items from a price list. While this works, it feels like a hack and isn't very scalable.


Ideal Scenario

What I’d love is something like what Keyboard Maestro offers:

  • You can reference a text file for dynamic input
  • Or use a variable that contains multiple values
    This makes it easy to maintain long lists without touching the UI.

Here are two screenshots from Keyboard Maestro for comparison:

Keyboard Maestro – referencing a list from a text file or variable:



Final Question

Is there a smarter or more dynamic way to create lists in BetterTouchTool — especially ones that can be maintained in a single external file — without needing to update every trigger or button individually?

I don’t have programming skills and often rely on ChatGPT to put together AppleScript or workflows. There might be a better feature in BTT that I’ve overlooked — it's such a powerful tool that it’s easy to miss things.

Any tips, ideas, or workarounds would be much appreciated!

(used chatgpt to make the text more clear)

You can use BTT's "Simple JSON Format" for that:

Here is an example that dynamically loads paste items from a json file. In this example the file is located in the downloads folder.

This function can be edited to bring any input file into the simple json format, in this example it expects a json file that is already in the correct format - but it could easily be changed to read csvs etc. as well:

//see https://docs.folivora.ai/docs/1108_simple_format.html
async function retrieveJSON() {

let jsonStringFromFile = readFile("~/Downloads/example.json");

return jsonStringFromFile;
}

Here is an example.json in BTT's Simple JSON Format that loads two paste text items:

[
	{
	"title": "paste item 1",
	 "icon": "sfsymbol::globe",
	 "action": {
	      "btt":"paste_text", 
	      "args": {
	          "text": "lalelu",
	          "insert_by_pasting": true
	       }
	  }
	},
	{
	"title": "paste item 2",
	 "action": {
	      "btt":"paste_text", 
	      "args": {
	          "text": "bibabu",
	          "insert_by_pasting": true
	       }
	  }
	}
]

Of course the retrieveJSON() javascript function could be changed to read any type of input format and transform it to the "simple json format"

Would display like this:

This can also be used with the "Show / Choose From List" action, then this example json would look like this:

It also works for dynamically loading floating menus (if you are interested in these I can give some more instructions on how to fully customize via such a file):

Hi Andreas,

Andreas, do you think you can add "Filter & Save the matched item to BTTPromptInput" as an additional option to "When Entering Text" for "Show / Choose From List (Configurable)"? This of course can be achieved if I add an action to the selected item, but it is not really ideal for long lists.

Thanks! But if I would do "readFile" with a csv or keep it simple just use a txt file where each line is the text to be grabbed. It needs some sort of script that creates a json format of the standard text kept inside the file?

Right, here is an example that would just read a file (example.txt from the download folder) and use every line as "paste item":

async function retrieveJSON() {
    let allItems = [];
    let textFileContent = readFile("~/Downloads/example.txt");
    let splitLines = textFileContent.split(/\r?\n/)
    for (line of splitLines) {
        allItems.push(
            {
                "title": line,
                "action": {
                    "btt": "paste_text",
                    "args": {
                        "text": line,
                        "insert_by_pasting": true
                    }
                }
            }
        )
    }
    return JSON.stringify(allItems);
}

Example file:

Mh what would be your use case for this? Would this only happen when triggering or already when searching?

One of the use cases is: after a selection is made from the list, variable to be set (equal to the selected item without the need to type the whole input) which and then used in the action "Open URL / Open URL With Selection".

Something like:

For example I can just type "cent" in the "Show / Choose From List (Configurable)" action which will filter only "eu-central-1" for example and then the saved value will be "eu-central-1" which will then used in the next action.

If you are using the simple json format you can use the set variable option for this. With the above example:

[
    {
        "title": "paste item 1",
        "icon": "sfsymbol::globe",
        "action": {
              "setvariable": {
                    "name": "pasted_text",
                    "variablevalue": "lalelu"
            },
            "btt": "paste_text",
            "args": {
                "text": "lalelu",
                "insert_by_pasting": true
            }
        }
    },
    {
        "title": "paste item 2",
        "action": {
            "setvariable": {
                 "name": "pasted_text",
                 "variablevalue": "bibabu"
             },
            "btt": "paste_text",
            "args": {
                "text": "bibabu",
                "insert_by_pasting": true
            }
        }
       
    }
]

Or via the compact form:

{
    "title": "some item title",
    "action": "keyboard::0"  
    "setvariable": "variablename@@variablevalue"
}

Ah but the example in your screenshot won't work because these list actions can not "wait" at the moment. So your open url action would be executed before something is selected. Instead you'd need to put that into your json as well, either directly or call a named trigger.

Example: Open URL directly

[
    {
        "title": "Open Google",
        "icon": "sfsymbol::globe",
        "action": {
            "btt": "trigger_action",
            "args": {
                "json": {
                    "BTTPredefinedActionType": 59,
                    "BTTOpenURL": "https://google.com",
                }
            }
        }
    }
]

Example: Call Named Trigger

[
    {
        "title": "Call Named Trigger",
        "icon": "sfsymbol::globe",
    
         "action": {
              "setvariable": {
                   "name": "url_to_open",
                  "value": "https://google.com"
             },
            "named": "helloworld"
          },
    }
]

This will do the job, it isn't as straight forward as simply defining items (either in the "Configure List Items" or in some file), but I see also some pros, so thank you!

you can still configure them in just some file. If you can post an example with the format in which you would like to store them I can post an example script that would use that. The great thing about this that it's really simple to transform basically anything into the correct format

Initially I've tried with "Wait for key press" with both Enter (76) / Return (36), but it didn't work - var was set, but URL was no opened after hitting enter/return:

Afterwards I've tried with the below code (for "Show / Choose From List (Configurable)") and no other actions, with named trigger, but it seems that the variable is not set (checked in "Variables & Scripting" window).

async function retrieveJSON() {
  let items = [
    {
    "title": "Frankfurt",
"setvariable": {
            "name": "custom_aws_region",
            "variablevalue": "eu-central-1"
        },
         "action": {
            "named": "named_trigger_aws_region"
          },
    },
	{
    "title": "Ireland",
"setvariable": {
            "name": "custom_aws_region",
            "variablevalue": "eu-west-2"
        },
         "action": {
            "named": "named_trigger_aws_region"
          },
		  },
  ];

  return JSON.stringify(items);
}

Named trigger config:

Ah damn, the docs are incorrect on this. It should be

async function retrieveJSON() {
    let items = [
        {
            "title": "Frankfurt",
            "action": {
                "named": "named_trigger_aws_region",
                "setvariable": {
                    "name": "custom_aws_region",
                    "value": "eu-west-2"
                }
            },
        },
        {
            "title": "Ireland",
            "action": {
                "named": "named_trigger_aws_region",
                "setvariable": {
                    "name": "custom_aws_region",
                    "value": "eu-west-2"
                }
            }
        }
    ];

    return JSON.stringify(items);
}

I'll fix them now...

1 Like

Ah, nice. This works like a charm. Appreciate that!

Out of curiosity - is it expected the configuration (screenshot from my previous comment) with "Wait for key press" to not work?

latet alpha should make it work even without the wait for keypress action

Maybe I haven't express myself clear. Should the config/set of actions, from the screenshot, work? So idea is:

  1. Show the list
  2. Select/filter item
  3. Wait until Enter is pressed, which should set the variable
  4. Execute last action - open the URL with the variable

with the latest alpha you don’t need the „wait for key“ action because BTT waits automatically until you have selected an item.

It would also work with wait for key (when configured with the correct key code), but then you’d need to hit enter twice

Ah, okay. I've misunderstood you. I've just tested and it works. Thank you.

Ok. So i tried with the help of chatgpt with a csv. But for this example I would like to use a CSV that has a pricelist, where I want to be able to select multiple items at once. Would that be possible?
As I said before, when using it with apple script I had a workaround by just using cmd to select multiple lines. Is there a workaround with "choose from lists" as well?

async function retrieveJSON() {
    let allItems = [];
    let textFileContent = readFile("/Users/admin/Library/Mobile Documents/com~apple~CloudDocs/Databases/csv/arbete.csv");
    let lines = textFileContent.split(/\r?\n/);

    // Skip header row if necessary
    for (let i = 1; i < lines.length; i++) {
        let line = lines[i].trim();
        if (line) {
            allItems.push({
                "title": line,
                "action": {
                    "btt": "paste_text",
                    "args": {
                        "text": line,
                        "insert_by_typing": true
                    }
                }
            });
        }
    }
    return JSON.stringify(allItems);
}

sorry, multi selection is currently not supported