Creating Floating Menus With JSON and AI
Floating Menus are normal BetterTouchTool triggers with the trigger class BTTTriggerTypeFloatingMenu.
That means they can be created in the UI, exported as JSON, imported again, edited by scripts, and generated by AI.
This page explains the creation formats that are useful when you want to build menus programmatically or ask the BetterTouchTool AI assistant to build them for you.
Which Format Should I Use?
Use the normal BTT UI when
You want to design one menu manually, tune the visual details by eye, or create a reusable template item that scripts or AI can reference later.
Use Simple JSON when
You want a script to dynamically produce menu items. This is the easiest format for AI and for JavaScript content scripts because it focuses on titles, icons, subitems and actions instead of every internal BTT property.
See Simple JSON Format for the full syntax.
Use a bundled template when
You want one of BetterTouchTool's shipped examples as a starting point. These are stored in the app bundle under DefaultTemplates and include the same defaults shown in the template picker.
The AI assistant can import them by template id:
| Template id | Use when |
|---|---|
default-menu-integrated | Basic three-button menu. |
context-menu-like | Context-menu style menu with submenus. |
hot_corner_example | Menu that expands from the top-left hot corner. |
now-playing | Now Playing widget menu. |
launcher_webview | Launcher-integrated webview menu. |
drawer | Drawer widget menu. |
appswitcher | Circular app switcher example. |
notch | Menu positioned relative to the MacBook notch. |
Use full Floating Menu JSON when
You want to import a complete menu, including its size, positioning, layout, item styles, web views, widgets, submenus, and action JSON.
This is the most powerful format, and it is the format used by BetterTouchTool's floating menu AI skill when it imports a new menu.
The AI assistant validates generated floating-menu JSON before importing it. The validator expands the short keys, checks the menu tree, catches misspelled or misplaced BTTMenuConfig properties, verifies important enum values, and reports errors with JSON paths and suggested fixes.
Use a Swift plugin widget when
The menu item should be a native custom view, for example a live timer, a CPU monitor, a mini dashboard, or a fully interactive SwiftUI component.
See Floating Menu Widget Plugins.
Runtime Model
A Floating Menu is a tree of menu objects:
- The top-level object has
BTTTriggerType: 767. - Every item in the menu is another object inside
BTTMenuItems. - Menus and menu items both store most of their layout and appearance in
BTTMenuConfig. - Submenus are items with
BTTTriggerType: 774and their own nestedBTTMenuItems. - Menu item actions are stored in
BTTMenuItemActions.
At runtime, BetterTouchTool loads enabled floating-menu triggers from Core Data, merges global and app-specific menus with the same menu identifier/name, decodes the JSON into menu models, and displays them in floating menu windows. Positioning, hover behavior, item scripts, dynamic content scripts, and runtime property updates are then handled by the Floating Menu runtime.
For users, the important part is this: generated JSON should describe the desired menu tree cleanly. BetterTouchTool handles UUID creation, window creation, positioning updates, script execution, and UI rendering after import.
Full JSON Structure
The most important fields for a full import are:
[
{
"BTTTriggerType": 767,
"BTTTriggerClass": "BTTTriggerTypeFloatingMenu",
"BTTEnabled": 1,
"BTTTriggerName": "Floating Menu: My Menu",
"BTTMenuName": "My Menu",
"BTTMenuAvailability": 0,
"BTTMenuConfig": {},
"BTTMenuItems": []
}
]
UUIDs are optional. If you omit BTTUUID and BTTTriggerParentUUID, BetterTouchTool generates and links them during import.
Short Keys
AI-generated menu JSON often uses short keys to reduce token usage. BetterTouchTool expands these during import:
| Short prefix | Expands to | Example |
|---|---|---|
© | BTT | ©TriggerType -> BTTTriggerType |
$ | BTTMenu | $Config -> BTTMenuConfig |
§ | BTTMenuItem | §s -> BTTMenuItems |
For example, this is equivalent to the full structure above:
[
{
"©TriggerType": 767,
"©TriggerClass": "©TriggerTypeFloatingMenu",
"©Enabled": 1,
"©TriggerName": "Floating Menu: My Menu",
"$Name": "My Menu",
"$Availability": 0,
"$Config": {},
"§s": []
}
]
Both formats are valid. Humans often find the full BTT... keys easier to read. AI imports often use the short keys.
Minimal Working Example
This creates a centered horizontal menu with three buttons. It uses full key names for readability.
[
{
"BTTTriggerType": 767,
"BTTTriggerClass": "BTTTriggerTypeFloatingMenu",
"BTTEnabled": 1,
"BTTTriggerName": "Floating Menu: Quick Work",
"BTTMenuName": "Quick Work",
"BTTMenuAvailability": 0,
"BTTMenuConfig": {
"BTTMenuPositioningType": 1,
"BTTMenuPositionRelativeTo": 3,
"BTTMenuAnchorMenu": 4,
"BTTMenuAnchorRelation": 4,
"BTTMenuSizingBehavior": 1,
"BTTMenuFrameWidth": 360,
"BTTMenuFrameHeight": 96,
"BTTMenuLayoutDirection": 7,
"BTTMenuHorizontalSpacing": 8,
"BTTMenuVerticalSpacing": 8,
"BTTMenuWindowLevel": 3,
"BTTMenuCloseAfterAction": 1,
"BTTMenuItemBackgroundType": 4,
"BTTMenuItemBackgroundColor": "30, 32, 38, 230",
"BTTMenuItemBlurredBackground": 1,
"BTTMenuItemCornerRadius": 12,
"BTTMenuItemPaddingLeft": 8,
"BTTMenuItemPaddingRight": 8,
"BTTMenuItemPaddingTop": 8,
"BTTMenuItemPaddingBottom": 8
},
"BTTMenuItems": [
{
"BTTTriggerType": 773,
"BTTTriggerClass": "BTTTriggerTypeFloatingMenu",
"BTTEnabled": 1,
"BTTOrder": 0,
"BTTTriggerName": "Menu Item: Mission Control",
"BTTMenuName": "Mission Control",
"BTTMenuAvailability": 0,
"BTTMenuItemActions": [
{
"BTTPredefinedActionType": 7,
"BTTPredefinedActionName": "Mission Control",
"BTTEnabled": 1,
"BTTOrder": 0
}
],
"BTTMenuConfig": {
"BTTMenuItemVisibleWhileActive": 1,
"BTTMenuItemVisibleWhileInactive": 1,
"BTTMenuItemMinWidth": 112,
"BTTMenuItemMaxWidth": 112,
"BTTMenuItemMinHeight": 72,
"BTTMenuItemMaxHeight": 72,
"BTTMenuItemBackgroundType": 4,
"BTTMenuItemBackgroundColor": "0, 122, 255, 255",
"BTTMenuItemBackgroundColorHover": "40, 152, 255, 255",
"BTTMenuItemCornerRadius": 10,
"BTTMenuItemIconType": 2,
"BTTMenuItemSFSymbolName": "rectangle.3.group",
"BTTMenuItemIconPosition": 1,
"BTTMenuItemIconColor1": "255, 255, 255, 255",
"BTTMenuItemImageHeight": 24,
"BTTMenuAttributedText": "<html><body style=\"text-align:center;\"><span style=\"font-size:13px; color:white; font-family:-apple-system;\">Mission</span></body></html>"
}
},
{
"BTTTriggerType": 773,
"BTTTriggerClass": "BTTTriggerTypeFloatingMenu",
"BTTEnabled": 1,
"BTTOrder": 1,
"BTTTriggerName": "Menu Item: Paste Date",
"BTTMenuName": "Paste Date",
"BTTMenuAvailability": 0,
"BTTMenuConfig": {
"BTTMenuItemVisibleWhileActive": 1,
"BTTMenuItemVisibleWhileInactive": 1,
"BTTMenuItemMinWidth": 112,
"BTTMenuItemMaxWidth": 112,
"BTTMenuItemMinHeight": 72,
"BTTMenuItemMaxHeight": 72,
"BTTMenuItemBackgroundType": 4,
"BTTMenuItemBackgroundColor": "52, 199, 89, 255",
"BTTMenuItemBackgroundColorHover": "76, 217, 100, 255",
"BTTMenuItemCornerRadius": 10,
"BTTMenuItemIconType": 2,
"BTTMenuItemSFSymbolName": "calendar",
"BTTMenuItemIconPosition": 1,
"BTTMenuItemIconColor1": "255, 255, 255, 255",
"BTTMenuItemImageHeight": 24,
"BTTMenuAttributedText": "<html><body style=\"text-align:center;\"><span style=\"font-size:13px; color:white; font-family:-apple-system;\">Date</span></body></html>"
}
},
{
"BTTTriggerType": 773,
"BTTTriggerClass": "BTTTriggerTypeFloatingMenu",
"BTTEnabled": 1,
"BTTOrder": 2,
"BTTTriggerName": "Menu Item: App Expose",
"BTTMenuName": "App Expose",
"BTTMenuAvailability": 0,
"BTTMenuItemActions": [
{
"BTTPredefinedActionType": 6,
"BTTPredefinedActionName": "Application Expose",
"BTTEnabled": 1,
"BTTOrder": 0
}
],
"BTTMenuConfig": {
"BTTMenuItemVisibleWhileActive": 1,
"BTTMenuItemVisibleWhileInactive": 1,
"BTTMenuItemMinWidth": 112,
"BTTMenuItemMaxWidth": 112,
"BTTMenuItemMinHeight": 72,
"BTTMenuItemMaxHeight": 72,
"BTTMenuItemBackgroundType": 4,
"BTTMenuItemBackgroundColor": "255, 149, 0, 255",
"BTTMenuItemBackgroundColorHover": "255, 179, 64, 255",
"BTTMenuItemCornerRadius": 10,
"BTTMenuItemIconType": 2,
"BTTMenuItemSFSymbolName": "macwindow",
"BTTMenuItemIconPosition": 1,
"BTTMenuItemIconColor1": "255, 255, 255, 255",
"BTTMenuItemImageHeight": 24,
"BTTMenuAttributedText": "<html><body style=\"text-align:center;\"><span style=\"font-size:13px; color:white; font-family:-apple-system;\">Expose</span></body></html>"
}
}
]
}
]
When you create real action JSON, copy it from BetterTouchTool or look it up in the action definitions. Do not rely on guessed action IDs.
Important Item Types
BTTTriggerType | Item type | Notes |
|---|---|---|
767 | Floating Menu | Top-level container. |
773 | Standard item | Button-like item with text, icon, scripts and actions. |
774 | Submenu | Contains nested BTTMenuItems. |
775 | Slider | Has a value from 0 to 1; value change scripts can update labels or trigger actions. |
776 | Text field | User-entered value is available through scripting. |
777 | Back button | Useful inside submenus. |
778 | Web view | Displays HTML or URL from BTTMenuItemText. |
801 | Row breaker | Forces following items onto the next row. |
802 | Column breaker | Forces following items into the next column. |
810 | Text area | Multiline text input. |
811 | Floating menu reference | Embeds/references another floating menu. |
813 | Folder widget | Displays a folder. |
821 | App widget | App-related built-in widget. |
829 | Now Playing widget | Built-in media widget. |
830 | Weather widget | Built-in weather widget. |
835 | Custom Swift widget | Native plugin widget; set BTTMenuWidgetCustomPluginID. |
836 | Drawer widget | File drawer/drop-zone style widget. |
Positioning
Use BTTMenuPositioningType to choose how the menu finds its position.
| Value | Meaning | Best for |
|---|---|---|
0 | Free move | User manually drags the menu somewhere. |
1 | Fixed position | Predictable generated menus. Uses reference, anchors and offsets. |
2 | Menubar status item | Status-item style menus. |
3 | Center on focused screen | Simple centered menus. |
4 | Fixed initial position | Place once, then behave like a free-moving menu. |
For AI-created menus, 1 is usually best because it produces deterministic results.
BTTMenuPositionRelativeTo selects the reference frame:
| Value | Reference |
|---|---|
0 | Focused window |
1 | Screen with mouse |
2 | Specific screen |
3 | Focused screen |
7 | Mouse position |
8 | Built-in display |
9 | Dynamic variable in active-window coordinates |
10 | Dynamic variable in screen coordinates |
11 | MacBook notch |
12 | Focused screen menubar |
16 | Mouse screen menubar |
19 | Screen with Dock |
20 | Green window button |
21 | Hovered item of another floating menu |
22 | Specific floating menu |
23 | Dock |
24 | All windows |
25 | Specific window |
Anchors decide which point of the menu is placed at which point of the reference frame:
| Value | Anchor |
|---|---|
0 | Top left |
1 | Top right |
2 | Bottom left |
3 | Bottom right |
4 | Center |
5 | Top edge center |
6 | Right edge center |
7 | Bottom edge center |
8 | Left edge center |
Example: center a menu on the focused screen:
{
"BTTMenuPositioningType": 1,
"BTTMenuPositionRelativeTo": 3,
"BTTMenuAnchorMenu": 4,
"BTTMenuAnchorRelation": 4,
"BTTMenuOffsetX": 0,
"BTTMenuOffsetY": 0
}
Example: show a menu above the current mouse location:
{
"BTTMenuPositioningType": 1,
"BTTMenuPositionRelativeTo": 7,
"BTTMenuAnchorMenu": 7,
"BTTMenuAnchorRelation": 4,
"BTTMenuOffsetY": 16
}
Sizing
BTTMenuSizingBehavior controls how the menu gets its size:
| Value | Behavior |
|---|---|
1 | Fixed. Use BTTMenuFrameWidth and BTTMenuFrameHeight. |
2 | Variable. Width/height come from BTT variables. |
3 | Content. Menu resizes to fit its content. |
4 | Min/max. Uses frame min/max constraints. |
For generated menus, fixed sizing is the most predictable. Content sizing is convenient when item count changes dynamically.
{
"BTTMenuSizingBehavior": 1,
"BTTMenuFrameWidth": 320,
"BTTMenuFrameHeight": 220
}
For variable sizing:
{
"BTTMenuSizingBehavior": 2,
"BTTMenuFrameWidthVariable": "my_menu_width",
"BTTMenuFrameHeightVariable": "my_menu_height"
}
Layout
BTTMenuLayoutDirection controls item placement:
| Value | Layout |
|---|---|
0 | Fill row, then continue with next row |
1 | Fill column, then continue with next column |
2 | Fill row with fixed wrapping |
3 | Fill column with fixed wrapping |
4 | Absolute, scrollable |
5 | Absolute, fixed |
6 | Vertical one column |
7 | Horizontal one row |
8 | Circular |
For most generated menus:
- Use
7for a toolbar. - Use
6for a vertical command list. - Use
0for a grid. - Use
8for radial menus shown near the mouse. - Use
5only when you explicitly set each item'sBTTMenuItemXandBTTMenuItemY.
Visibility and Window Behavior
Useful menu-level properties:
| Property | Meaning |
|---|---|
BTTMenuVisibility | 0 show on launch, 1 show via action, 2 restore via app activation. |
BTTMenuWindowLevel | Window level. 3 floats above normal windows, 0 behaves like a normal window. |
BTTMenuWindowResizable | 1 lets the user resize the menu. |
BTTMenuCloseOnOutsideClick | 1 closes when clicking elsewhere. |
BTTMenuCloseAfterAction | 1 closes after an item action. |
BTTMenuCloseOnKeyboardInput | 1 closes after keyboard input. |
BTTMenuCloseOnMoveMouseAway | 1 closes when the mouse leaves. |
BTTMenuDisableDrag | 1 prevents dragging. |
BTTMenuStealKeyboardFocusOnShow | 1 asks the menu to take keyboard focus when shown. |
BTTMenuClickThroughEmptyParts | 1 sends clicks on empty menu areas to the app behind it. |
BTTMenuClickThroughEverywhere | 1 makes all clicks pass through. |
For keyboard-driven menus shown by a shortcut, set BTTMenuVisibility to 1, then use the Show/Hide/Toggle Floating Menu actions.
Item Visibility
Most normal items should include:
{
"BTTMenuItemVisibleWhileActive": 1,
"BTTMenuItemVisibleWhileInactive": 1
}
If you omit these on hand-written JSON, items may not appear in the states you expect.
You can also conditionally show items based on variables:
{
"BTTMenuItemVisibleIfVariableIsTrue": "show_work_tools",
"BTTMenuItemVisibleIfVariableIsFalse": "hide_work_tools"
}
Styling
Colors use R, G, B, A strings with 0-255 values:
{
"BTTMenuItemBackgroundType": 4,
"BTTMenuItemBackgroundColor": "40, 44, 52, 230",
"BTTMenuItemBackgroundColorHover": "70, 76, 90, 255"
}
Common item style properties:
| Property | Meaning |
|---|---|
BTTMenuItemBackgroundType | 4 for color, 5 linear gradient, 6 radial gradient, 1 image data, 7 preset file. |
BTTMenuItemBackgroundColor | Main background color. |
BTTMenuItemBackgroundColor2 to 4 | Additional gradient colors. |
BTTMenuItemBackgroundColorHover | Background while hovered. |
BTTMenuItemBlurredBackground | 1 enables blur. |
BTTMenuItemGlassEffect | Enables the newer glass style when supported. |
BTTMenuItemBorderWidth | Border width. |
BTTMenuItemBorderColor | Border color. |
BTTMenuItemCornerRadius | Corner radius. |
BTTMenuItemShadowEnabled | 1 enables shadow. |
BTTMenuItemShadowRadius | Shadow radius. |
Dark mode variants usually use the Dark suffix, for example BTTMenuItemBackgroundColorDark.
Text
For standard items, use BTTMenuAttributedText. It can contain RTF or HTML.
HTML is often easiest for generated menus:
{
"BTTMenuAttributedText": "<html><body style=\"text-align:center;\"><span style=\"font-size:16px; color:white; font-family:-apple-system;\">Open Calendar</span></body></html>"
}
For web view items, use BTTMenuItemText for the HTML or URL:
{
"BTTMenuItemText": "https://folivora.ai"
}
For scripts that update existing item text, BTTMenuItemText is useful because BetterTouchTool applies the existing configured text style to the new string.
Icons and SF Symbols
For SF Symbol icons:
{
"BTTMenuItemIconType": 2,
"BTTMenuItemSFSymbolName": "star.fill",
"BTTMenuItemSFSymbolStyle": 1,
"BTTMenuItemIconColor1": "255, 255, 255, 255",
"BTTMenuItemIconPosition": 4,
"BTTMenuItemImageHeight": 32
}
Useful icon values:
| Property | Values |
|---|---|
BTTMenuItemIconType | 0 none, 1 image data, 2 SF Symbol, 3 file, 7 preset file, 8 internal image. |
BTTMenuItemIconPosition | 0 left, 1 top, 2 right, 3 bottom, 4 center, 5 none. |
BTTMenuItemSFSymbolStyle | 0 monochrome, 1 hierarchical, 2 palette, 3 multicolor. |
For image files, use BTTMenuItemIconType: 3 and BTTMenuItemIconPresetPath or the relevant file/preset path property.
Submenus
A submenu is an item with BTTTriggerType: 774.
{
"BTTTriggerType": 774,
"BTTTriggerClass": "BTTTriggerTypeFloatingMenu",
"BTTEnabled": 1,
"BTTMenuName": "More",
"BTTMenuConfig": {
"BTTMenuItemVisibleWhileActive": 1,
"BTTMenuItemVisibleWhileInactive": 1,
"BTTMenuUseStyleForSubmenu": 0,
"BTTMenuAttributedText": "<html><body><span style=\"font-size:14px; color:white;\">More</span></body></html>"
},
"BTTMenuItems": [
{
"BTTTriggerType": 777,
"BTTTriggerClass": "BTTTriggerTypeFloatingMenu",
"BTTEnabled": 1,
"BTTMenuName": "Back",
"BTTMenuConfig": {
"BTTMenuItemVisibleWhileActive": 1,
"BTTMenuItemVisibleWhileInactive": 1,
"BTTMenuAttributedText": "<html><body><span style=\"font-size:14px; color:white;\">Back</span></body></html>"
}
}
]
}
Use a back button (777) when the submenu replaces the current menu content. If the submenu appears beside or over the menu, a back button may not be needed.
Web View Items
Use BTTTriggerType: 778.
{
"BTTTriggerType": 778,
"BTTTriggerClass": "BTTTriggerTypeFloatingMenu",
"BTTEnabled": 1,
"BTTMenuName": "Mini Web",
"BTTMenuConfig": {
"BTTMenuItemVisibleWhileActive": 1,
"BTTMenuItemVisibleWhileInactive": 1,
"BTTMenuItemMinWidth": 420,
"BTTMenuItemMinHeight": 280,
"BTTMenuItemText": "<html><body style=\"font-family:-apple-system;\">Hello</body></html>",
"BTTMenuItemWebViewPlain": 0,
"BTTMenuItemKeepActiveInBackground": 1
}
}
Useful web view properties:
| Property | Meaning |
|---|---|
BTTMenuItemText | HTML string or URL. |
BTTMenuItemWebViewPlain | 1 disables BTT scripting support inside the web view. |
BTTMenuItemUserAgent | Custom user agent. |
BTTMenuItemUserScript | Script run on load. |
BTTMenuItemUserScriptOnvisible | Script run when visible. |
BTTMenuItemWebViewFocusTextField | 1 auto-focuses an input. |
BTTMenuItemKeepActiveInBackground | 1 keeps the web view active while hidden/backgrounded. |
BTTMenuItemSystemBrowserPrefix | URL prefixes that should open in the system browser. |
You can later update the web view with the Floating Menu Load HTML action or with webview_menu_item_load_html_url_js.
Built-In Widgets
Some item types expose widget-specific BTTMenuConfig properties.
Custom Swift Widget
Use BTTTriggerType: 835 and set BTTMenuWidgetCustomPluginID:
{
"BTTTriggerType": 835,
"BTTTriggerClass": "BTTTriggerTypeFloatingMenu",
"BTTEnabled": 1,
"BTTMenuName": "CPU Widget",
"BTTMenuConfig": {
"BTTMenuItemMinWidth": 220,
"BTTMenuItemMinHeight": 120,
"BTTMenuWidgetCustomPluginID": "com.bttuserplugin.swift.cpu-widget"
}
}
Compile/install the widget plugin first, then import the menu that references it.
Now Playing Widget
Use BTTTriggerType: 829. Useful properties include:
| Property | Meaning |
|---|---|
BTTMenuWidgetNowPlayingHideCover | 0 album cover, 1 app icon, 2 hidden. |
BTTMenuWidgetNowPlayingShowAlbum | 1 shows album text. |
BTTMenuWidgetNowPlayingLayout | 0 horizontal, 1 vertical. |
BTTMenuWidgetNowPlayingShowProgress | 1 shows progress. |
BTTMenuWidgetNowPlayingAllowedApps | Comma-separated bundle IDs, empty for all. |
Weather Widget
Use BTTTriggerType: 830. Useful properties include:
| Property | Meaning |
|---|---|
BTTMenuWidgetWeatherUnit | 0 Celsius, 1 Fahrenheit, 2 system. |
BTTMenuWidgetWeatherAutoLocation | 1 auto, 0 manual. |
BTTMenuWidgetWeatherLatLon | Manual "lat,lon". |
BTTMenuWidgetWeatherForecastDay | -1 current, 0 to 7 forecast. |
Actions
Floating menu item actions are stored in BTTMenuItemActions.
{
"BTTMenuItemActions": [
{
"BTTPredefinedActionType": 7,
"BTTPredefinedActionName": "Mission Control",
"BTTEnabled": 1,
"BTTOrder": 0
}
]
}
If you use the short-key format, this becomes §Actions.
Action JSON can be nested and action-specific. For reliable imports, copy action JSON from BetterTouchTool or use the action definition lookup in the AI assistant before generating the final menu.
Dynamic Menus With Simple JSON
For dynamically generated contents, configure a floating menu content script and return Simple JSON.
async function retrieveJSON() {
return JSON.stringify([
{
title: "Open Safari",
icon: "sfsymbol::safari::color@@#0A84FF",
width: 120,
height: 64,
action: {
js: "runShellScript({script: 'open -a Safari'})"
}
},
{
title: "More",
icon: "sfsymbol::ellipsis.circle",
subitems: [
{
title: "Say Hello",
icon: "sfsymbol::speaker.wave.2",
action: "js::runShellScript({script: `say hello`})"
}
]
}
]);
}
Useful Simple JSON item properties:
| Property | Meaning |
|---|---|
title | Text, optionally with ::size@@18::color@@#ffffff. |
icon | sfsymbol::name, path::~/file.png, or base64::.... |
background | Hex or rgba color. |
width / height | Floating menu item size. |
templateItemUUID | Use an existing item as style template. |
subitems | Static submenu items. |
action | One action string/object or an array of actions. |
actions | Action categories such as standard, rightclick, hover, hoverend, appear, disappear, ondrop. |
For floating menus, a Simple JSON container can also include any full floating-menu config properties to override the template:
async function retrieveJSON() {
return JSON.stringify({
type: "floatingmenu",
BTTMenuSizingBehavior: 3,
BTTMenuLayoutDirection: 7,
BTTMenuItemBackgroundColor: "20, 22, 28, 230",
items: [
{
title: "Template Based",
templateItemUUID: "PASTE-A-MENU-ITEM-UUID-HERE",
icon: "sfsymbol::sparkles"
}
]
});
}
Templates are very useful for AI-generated dynamic menus: design one good-looking item in the BTT UI, copy its UUID, and let the script or AI reuse it for many generated items.
Updating Menus at Runtime
You can update item properties without re-importing the whole menu.
Item script return value
An item script can return JSON5:
async function itemScript(itemUUID) {
return "{BTTMenuItemBackgroundColor: '20, 200, 20, 255', BTTMenuItemText: 'Ready'}";
}
JavaScript function
async function updateItem() {
await update_menu_item({
menu_name: "Quick Work",
item_name: "Status",
json: JSON.stringify({
BTTMenuItemText: "Done",
BTTMenuItemBackgroundColor: "52, 199, 89, 255"
}),
persist: false
});
returnToBTT("updated");
}
AppleScript
tell application "BetterTouchTool"
update_menu_item menu_name "Quick Work" item_name "Status" json "{BTTMenuItemText: 'Done'}" persist false
end tell
Use persist: true only when the runtime update should become the saved default.
Showing and Hiding Generated Menus
Generated menus can be shown with the normal predefined actions:
| Action | Type |
|---|---|
| Show Floating Menu | 386 |
| Hide Floating Menu | 387 |
| Toggle Floating Menu | 388 |
| Open Floating Menu Submenu | 472 |
| Close Floating Menu Submenu | 445 |
| Floating Menu Load HTML | 397 |
| Floating Menu Execute JavaScript | 398 |
| Re-evaluate Position | 390 |
| Run Floating Menu Content Scripts | 469 |
When the menu should be shown only by a shortcut, gesture, Stream Deck button, or other trigger, set:
{
"BTTMenuVisibility": 1
}
Then configure the trigger to run Show, Hide, or Toggle Floating Menu and target the menu by name or UUID.
AI Creation Checklist
When asking AI to create a floating menu, include:
- The purpose of the menu.
- Whether it should be always visible or shown by an action.
- Where it should appear: focused screen, mouse position, focused window, desktop, notch, menubar, etc.
- Expected size or layout: toolbar, vertical list, grid, circular menu, dashboard.
- Item list with labels, icons and actions.
- Whether the menu should close after clicking an item.
- Whether keyboard focus is needed.
- Whether any item should be dynamic, scripted, a web view, or a Swift widget.
Before importing generated JSON, check:
- The top-level value is a JSON array, unless importing a preset wrapper.
- The menu has
BTTTriggerType: 767andBTTTriggerClass: "BTTTriggerTypeFloatingMenu". - Every item has
BTTTriggerClass: "BTTTriggerTypeFloatingMenu". - Items that should appear have
BTTMenuItemVisibleWhileActive: 1andBTTMenuItemVisibleWhileInactive: 1. - Fixed-position menus have positioning type, reference, menu anchor and relation anchor.
- Fixed-size menus have frame width and height.
- Standard item text uses
BTTMenuAttributedText; web views useBTTMenuItemText. - Action JSON was copied or looked up, not guessed.
- UUIDs are either omitted or unique.
Common Problems
The menu imports but no items are visible
Add BTTMenuItemVisibleWhileActive: 1 and BTTMenuItemVisibleWhileInactive: 1 to the item configs. Also confirm the items are inside BTTMenuItems.
The menu is off-screen
Use fixed positioning relative to the focused screen while developing:
{
"BTTMenuPositioningType": 1,
"BTTMenuPositionRelativeTo": 3,
"BTTMenuAnchorMenu": 4,
"BTTMenuAnchorRelation": 4
}
If it is a free-moving menu, use the action that brings free-moving floating menus to the current screen.
The text does not render as expected
For standard items, prefer BTTMenuAttributedText. Use BTTMenuItemText mainly for web view content and runtime text updates.
The menu opens but steals focus unexpectedly
Set BTTMenuStealKeyboardFocusOnShow to 0, or use the Show/Toggle Floating Menu action's keyboard focus option.
A generated action does the wrong thing
Regenerate only the action JSON after looking up the exact action definition or copying the action from BetterTouchTool. Floating menu item layout JSON and action JSON are separate concerns.
A dynamic script is slow
Use content scripts for dynamic items, but cache expensive results where possible. For visual style, use templateItemUUID so the script only needs to return content and actions.