Experiment: Create Floating Menus With ChatGPT

So this is very experimental. I just gave ChatGPT some basic definitions on how to create floating menus and it seems to work pretty well:

https://chatgpt.com/g/g-682f2d1fe228819189a378ff41eba002-bettertouchtool-floating-menus

Example query:

Can you create a floating menu with 4 buttons in some pleasant looking blue, red, purple and green? The menu should be placed at the bottom right of the screen. It should be 200x200px in size

You can then just copy the JSON it outputs and paste it into BTT. (After pasting you might need to perform one app switch)

This probably needs more tweaking and I need to give it more example code, but even with this super basic approach it already works astonishingly well. I hope I can soon provide definitions for everything in BTT so the AI's can generate full action sequences etc. as well.

Maybe the link expired.

Ah I think it was accidentally still set to private, should work now

1 Like

This should really promising and powerful:

I hope I can soon provide definitions for everything in BTT so the AI's can generate full action sequences etc. as well.

It is really good idea!

yep, and then have some button in BTT "Configure Using AI" or something like that.

I'm currently finalizing some things from the current alpha/beta phase and release the next stable early next week. After that I have quite a few plans for AI related things for the upcoming weeks (mostly integrated local AI MCP server, AI definitions and integrated AI prompt window). I think this will be really powerful and make lots of things easier.

1 Like

May I ask, as an ignorant person, what should prevent an AI from being something like BTT itself in the near future. Instead of setting up a floating menu, a shortcut, a key sequence in BTT, I set it up directly in the AI. I don't want that. Does anyone want this?

nothing will prevent that, everything is heading into that direction and it's going pretty fast. Pretending it's not happening won't help, so I'll at least try to work with it as long as possible :slight_smile:

That's brave of you :smiley: But why do we still need you and BTT? Are you digging your own grave right now? I'm sure nobody here wants that :wink:

Yep, I think society will change drastically. Hundreds of thousands of jobs will be created by AI , but many millions of jobs will be lost to AI

I don’t think we are ready for that, but we also can’t stop it anymore

Will your job be recreated or destroyed? :man_shrugging:

Humanity has just created its Syknet. For those who haven't seen the movie: This is the AI that almost wiped out humanity in Terminator. Perhaps we will then want to return from the future to destroy the machine. But time travel is one of those things ...

maybe both? :wink:

1 Like

You could be one of those who program the AI to be better than you are. Then the AI has to help you be better than it, so that you can make it better than you again ... You could go crazy. :joy:

Or maybe I‘ll just get into wood working

Wood working??? And what should we do who are dependent on BTT? Ah, sure, we use Skynet.

Andreas, now that we're so close and no one is listening, I've rarely granted success to anyone the way I grant it to you. Long may BTT continue to exist. And may you continue to put up with my stupid questions and the even stupider ones from other users for a very long time. Oops, no offense to other users, of course. Except for those who deserve it. :smiley:

Thank you & good night :folded_hands:

this is fun... :upside_down_face:

a small clock display...with Java script generated for the button display
all from GPT...

only a few small tweaks in the script setting and we are done...

Very cool project @Andreas_Hegenberg ! I've been quite busy so I haven't had the time to fully explore your custom GPT.

As you know from our conversations, I've been exploring the possibility of creating BTT configurations using LLMs for some time. Here's a script I wrote in October 2024, I think you'll appreciate it :wink:

// src/index.ts

import dotenv from "dotenv";
dotenv.config();

import OpenAI from "openai";
import { zodResponseFormat } from "openai/helpers/zod";
import { SimpleFormatSchema } from "./schemas";

const openai = new OpenAI({
	apiKey: process.env.OPENAI_API_KEY,
});

async function generateSimpleFormatItem(prompt: string) {
	const responseFormat = zodResponseFormat(
		SimpleFormatSchema,
		"simple_format"
	);

	try {
		const completion = await openai.beta.chat.completions.parse({
			model: "gpt-4o-2024-08-06",
			messages: [
				{
					role: "system",
					content: `You are Andreas Hegenberg, the creator of BetterTouchTool.
Your objective is to help users use the new "Simple JSON Format" feature.
BetterTouchTool introduces a new "Simple JSON Format", which can currently be used
with the predefined action "Show Custom Context Menu (New)".
It allows to retrieve the content for those menus dynamically using JavaScript & JSON.
Your objective is to fully understand the user's request for a new Custom Context Menu
and then produce the JSON to implement their request.`,
				},
				{ role: "user", content: prompt },
			],
			response_format: responseFormat,
		});
		console.log("Completion:", completion);

		const message = completion.choices[0].message;

		if (message.parsed) {
			const parsedData = message.parsed;
			console.log("Parsed Data:", parsedData);
			return parsedData;
		} else if (message.refusal) {
			console.log("Model refused to comply:", message.refusal);
		}
	} catch (error) {
		if (
			error instanceof Error &&
			error.constructor.name === "LengthFinishReasonError"
		) {
			console.log(
				"Response was cut off due to length. Consider increasing max tokens."
			);
		} else {
			console.error("An error occurred:", error);
		}
	}
}

generateSimpleFormatItem("Create a test Custom Context Menu.");

{
	"$schema": "http://json-schema.org/draft-07/schema#",
	"type": "object",
	"title": "SimpleFormatRoot",
	"properties": {
		"items": {
			"type": "array",
			"items": {
				"$ref": "#/$defs/SimpleFormatItemBase"
			}
		}
	},
	"additionalProperties": false,
	"required": ["items"],
	"$defs": {
		"Color": {
			"type": "object",
			"properties": {
				"red": {
					"type": "integer",
					"minimum": 0,
					"maximum": 255
				},
				"green": {
					"type": "integer",
					"minimum": 0,
					"maximum": 255
				},
				"blue": {
					"type": "integer",
					"minimum": 0,
					"maximum": 255
				},
				"alpha": {
					"type": "number",
					"minimum": 0,
					"maximum": 1
				}
			},
			"additionalProperties": false,
			"required": ["red", "green", "blue", "alpha"]
		},
		"SimpleFormatItemBase": {
			"type": "object",
			"properties": {
				"title": {
					"$ref": "#/$defs/AttributedText"
				},
				"type": {
					"$ref": "#/$defs/SimpleFormatItemType"
				},
				"submenu": {
					"type": ["array", "null"],
					"items": {
						"$ref": "#/$defs/SimpleFormatItemBase"
					}
				},
				"background": {
					"$ref": "#/$defs/Color"
				},
				"icon": {
					"$ref": "#/$defs/IconDefinition"
				},
				"action": {
					"$ref": "#/$defs/Action"
				},
				"setvariable": {
					"$ref": "#/$defs/SetVariableDefinition"
				}
			},
			"additionalProperties": false,
			"required": ["title", "type"]
		},
		"SimpleFormatItemChooseFromList": {
			"allOf": [
				{
					"$ref": "#/$defs/SimpleFormatItemBase"
				},
				{
					"properties": {
						"subtitle": {
							"$ref": "#/$defs/AttributedText"
						}
					}
				}
			]
		},
		"SimpleFormatItemFloatingMenu": {
			"allOf": [
				{
					"$ref": "#/$defs/SimpleFormatItemBase"
				},
				{
					"properties": {
						"width": {
							"type": "number"
						},
						"height": {
							"type": "number"
						},
						"templateitemuuid": {
							"type": "string"
						}
					}
				}
			]
		},
		"AttributedText": {
			"type": "object",
			"properties": {
				"text": {
					"type": "string"
				},
				"color": {
					"$ref": "#/$defs/Color"
				},
				"size": {
					"type": "number"
				}
			},
			"additionalProperties": false,
			"required": ["text"]
		},
		"SimpleFormatItemType": {
			"type": "string",
			"enum": ["standard", "separator"]
		},
		"IconDefinition": {
			"type": "object",
			"properties": {
				"type": {
					"type": "string",
					"enum": ["sfsymbol", "path", "base64"]
				},
				"value": {
					"type": "string"
				},
				"path": {
					"type": "string"
				},
				"base64": {
					"type": "string"
				},
				"weight": {
					"type": "string"
				},
				"size": {
					"type": "number"
				},
				"colors": {
					"type": "array",
					"items": {
						"$ref": "#/$defs/Color"
					}
				},
				"background": {
					"$ref": "#/$defs/Color"
				},
				"width": {
					"type": "number"
				},
				"height": {
					"type": "number"
				}
			},
			"required": ["type"]
		},
		"SetVariableDefinition": {
			"type": "object",
			"properties": {
				"name": {
					"type": "string"
				},
				"value": {
					"type": "string"
				}
			},
			"required": ["name", "value"]
		},
		"BaseAction": {
			"type": "object",
			"properties": {
				"setvariable": {
					"$ref": "#/$defs/SetVariableDefinition"
				}
			}
		},
		"NamedAction": {
			"allOf": [
				{
					"$ref": "#/$defs/BaseAction"
				},
				{
					"properties": {
						"named": {
							"type": "string"
						}
					},
					"required": ["named"]
				}
			]
		},
		"UUIDAction": {
			"allOf": [
				{
					"$ref": "#/$defs/BaseAction"
				},
				{
					"properties": {
						"uuid": {
							"type": "string"
						}
					},
					"required": ["uuid"]
				}
			]
		},
		"JSAction": {
			"allOf": [
				{
					"$ref": "#/$defs/BaseAction"
				},
				{
					"properties": {
						"js": {
							"type": "string"
						}
					},
					"required": ["js"]
				}
			]
		},
		"KeyboardAction": {
			"allOf": [
				{
					"$ref": "#/$defs/BaseAction"
				},
				{
					"properties": {
						"keyboard": {
							"type": "string"
						}
					},
					"required": ["keyboard"]
				}
			]
		},
		"ShortcutAction": {
			"allOf": [
				{
					"$ref": "#/$defs/BaseAction"
				},
				{
					"properties": {
						"shortcut": {
							"type": "string"
						},
						"input": {
							"type": "string"
						}
					},
					"required": ["shortcut"]
				}
			]
		},
		"BTTAction": {
			"allOf": [
				{
					"$ref": "#/$defs/BaseAction"
				},
				{
					"properties": {
						"btt": {
							"type": "string"
						},
						"input": {},
						"args": {},
						"json": {}
					},
					"required": ["btt"]
				}
			]
		},
		"Action": {
			"oneOf": [
				{
					"type": "string"
				},
				{
					"$ref": "#/$defs/NamedAction"
				},
				{
					"$ref": "#/$defs/UUIDAction"
				},
				{
					"$ref": "#/$defs/JSAction"
				},
				{
					"$ref": "#/$defs/KeyboardAction"
				},
				{
					"$ref": "#/$defs/ShortcutAction"
				},
				{
					"$ref": "#/$defs/BTTAction"
				}
			]
		},
		"ActionCategory": {
			"type": "string",
			"enum": [
				"standard",
				"hover",
				"longpress",
				"touchrelease",
				"hoverend",
				"hyperkeyrelease",
				"rightclick",
				"changetofalse",
				"appear",
				"disappear",
				"ondrop"
			]
		}
	}
}