Text clipboard tool for floating menus / BTT Mobile

I've created (with the help of Claude) a web component which provides a text clipboard tool which can be used in floating menus / BTT Mobile. It's designed for those cases where there is some text which you are going to reuse repeatedly over the next few hours, but when it's not worth spending the time to create a macro. Here's a screenshot of what it looks like in BTT Mobile running on an iPad Mini:

Each text snippet has a label containing a short description of what the snippet is, followed by an abbreviated display of its content. On the left of each row is a button intended to paste the snippet into the app which current has focus. On the right is a button which, when tapped, deletes the row from the table. You can try out a version of the component online here and you can grab a copy of the javascript defining the component, as well.

Exactly how one configures this will depend on how you want to use it. I'll explain my setup below, but note that this uses Keyboard Maestro in addition to BetterTouchTool. I bet you can do everything needed in just BetterTouchTool, though.

In the floating menu, the clipboard element is created as follows (note that the relevant javascript has to be loaded first):

<saved-clipboard height="400px"></saved-clipboard>

Some additional JavaScript is needed to make it work — I'll explain that below.

How do you insert saved content into the clipboard? The way I do this is as follows: I first use Keyboard Maestro to grab the current selection and save it to a variable. Then I use Keyboard Maestro's "Custom HTML Prompt" to display a dialog box allowing me to enter a label describing the string. That dialog box looks like the following:

When the label is given and OK clicked, JavaScript code is constructed and saved to a BTT string variable named js_code. The code has the form save_string(label, content) where label and content are strings. However, I first encode the strings to avoid issues in case they contain quote marks, etc. In the Keyboard Maestro custom HTML prompt, when the OK button is clicked, both the label and content are encoded as follows:

const encoded_text = btoa(new TextEncoder().encode(content).reduce((s, b) => s + String.fromCharCode(b), ''));

const encoded_label = btoa(new TextEncoder().encode(label).reduce((s, b) => s + String.fromCharCode(b), ''));

That basically turns the label and content strings into something like a base64 encoded string, so that when I set the BTT variable js_code to save_string('${encoded_label}', '${encoded_content}') I know that it is syntactically valid.

The last step is that Keyboard Maestro uses AppleScript to tell BTT to execute js_code in the relevant floating webview. Given the definition of save_string() in that webview, the label and content are inserted into the web component!

How was save_string() defined? Here's the code from the floating webview:

function save_string(encoded_label, encoded_content) {
	const label = new TextDecoder().decode(Uint8Array.from(atob(encoded_label), c => c.charCodeAt(0)));
	const content = new TextDecoder().decode(Uint8Array.from(atob(encoded_content), c => c.charCodeAt(0)));

	const clipboard = document.querySelector('saved-clipboard');
	clipboard.saveContent(label, content);
}

The first two lines undo the encoding, then I get the clipboard object itself, and then I save the content using the relevant function from the web component.

Pasting the saved content into the current window which has focus is easy. That is configured in the webview as follows:

customElements.whenDefined('saved-clipboard').then(() => {
	const clipboard = document.querySelector('saved-clipboard');

	// Handle insert action
	clipboard.onInsert = async (entry) => {
		await paste_text({ text: `${entry.content}`, format: 'NSPasteboardTypeHTML', insert_by_pasting: true });
	};
});

Over the weekend, I'll try to write these notes up into a more detailed guide. But this should be enough to help someone get started!