BetterTouchTool Plugins
This repository is the public home for BetterTouchTool plugin examples, reviewed community plugins, and the older Xcode bundle development project.
For most new plugins, use a single Swift source file. Drop the .swift file onto
the BetterTouchTool preferences window, or copy it into the BetterTouchTool
Plugins folder. BetterTouchTool will ask before compiling and loading it.
Official Documentation
Read the BetterTouchTool plugin documentation for the complete plugin API overview, installation flow, and current development guidance.
Plugin Gallery
The repository includes a static, searchable plugin gallery in site. It reads the official and community plugin manifests and can be published with GitHub Pages.
Update the generated gallery catalog after changing plugin metadata:
node tools/build-site-catalog.mjs
The GitHub Pages workflow in .github/workflows/plugin-gallery.yml runs the
same generator and publishes the site folder.
Repository Structure
.github/workflows/plugin-gallery.yml GitHub Pages publisher for the gallery
site/ Static searchable plugin gallery
tools/build-site-catalog.mjs Gallery catalog generator
plugins/
index.json Reviewed plugin registry
official/ Examples maintained by BetterTouchTool
community/ Reviewed user-submitted plugins
_template/ Starting point for pull requests
xcode-bundle-examples/ Advanced / legacy Xcode bundle project
BetterTouchToolPluginDevelopment.xcodeproj
BTTPluginSupport/
BTTDisplayNotificationActionPlugin/
BTTStreamDeckPluginCPUUsage/
BTTTouchBar...
LICENSE
README.md
Plugin Types
BetterTouchTool supports these plugin types:
| Type | Typical Use |
|---|---|
| Launcher | Add native rows, commands, saved instances, and surfaces to the BTT launcher |
| Action | Add a custom action to the action picker |
| Trigger | Observe external state and fire BTT triggers |
| FloatingMenuWidget | Add native widgets to floating menus |
| StreamDeck | Add Stream Deck widgets |
| TouchBar | Add Touch Bar widgets |
Swift Source Plugins
A Swift source plugin is just a .swift file with metadata comments and a class
that conforms to one of the BTT plugin protocols.
// BTT-Plugin-Name: Hello Launcher
// BTT-Plugin-Identifier: com.example.hello-launcher
// BTT-Plugin-Type: Launcher
// BTT-Plugin-Icon: hand.wave
import Cocoa
final class HelloLauncher: NSObject, BTTLauncherPluginInterface {
weak var delegate: (any BTTLauncherPluginDelegate)?
static func launcherPluginName() -> String { "Hello Launcher" }
static func launcherPluginDescription() -> String { "Shows one launcher result." }
static func launcherPluginIcon() -> String { "hand.wave" }
func launcherResults(for context: BTTLauncherPluginContext) -> [BTTLauncherPluginResult]? {
let result = BTTLauncherPluginResult()
result.itemIdentifier = "hello"
result.title = "Hello from BetterTouchTool"
result.subtitle = "This row came from a Swift source plugin."
result.systemImageName = "hand.wave"
return [result]
}
}
SwiftUI State Compatibility
For SwiftUI source plugins that support standalone Command Line Tools and older
macOS SDKs, use a uniquely named local alias for the State property wrapper:
import SwiftUI
private typealias MyPluginState<Value> = SwiftUI.State<Value>
private struct EditorView: View {
@MyPluginState private var text = ""
var body: some View {
TextField("Text", text: $text)
}
}
SDK 27 also declares an @State macro, but some standalone tools packages lack
its SwiftUIMacros implementation. The distinct alias selects the existing
property wrapper and retains its earlier initialization behavior. This does not
provide other missing macros or the new macro's lazy initialization semantics.
The Quick Links example uses this pattern.
Install
Use one of these:
- drag the
.swiftfile onto the BetterTouchTool preferences window - use File > Open in BetterTouchTool and select the
.swiftfile - copy the file to
~/Library/Application Support/BetterTouchTool/Plugins/
swiftc. Xcode Command Line Tools
must be installed.
Official Examples
| Plugin | Type | What It Shows |
|---|---|---|
| Sample Launcher | Launcher | Rows, children, commands, variables, named triggers, and a native surface |
| Quick Links | Launcher | Saved plugin instances, editor surfaces, URL templates, and commands |
| 1Password Launcher Example | Launcher | Searching 1Password items through the op CLI |
| Google Search Launcher | Launcher | Opening launcher queries as Google searches |
| Caffeinate | Launcher | Toggling a background caffeinate process from the launcher |
| Launcher Falling Blocks | Launcher | A SwiftUI game hosted in a launcher surface |
| Launcher Pong | Launcher | Another interactive SwiftUI launcher-surface game |
| Clipboard Change | Trigger | Firing BTT triggers when the clipboard changes |
| File Watcher | Trigger | Watching a configured file or folder and firing BTT triggers |
| Compress Finder Selection | Action | Creating zip archives from Finder selections |
| Finder Convert Selected Image to JPEG | Action | Converting a selected Finder image to JPEG |
| Analog Clock | FloatingMenuWidget | A native analog clock floating menu widget |
The registry for these plugins lives in plugins/index.json.
Community Plugins
plugins/community contains user-generated plugins submitted through pull
requests. Each accepted plugin has its own folder with source, metadata,
description, screenshots when useful, and safety notes.
The first imported community collection comes from jhasubhash/btt-plugins. Each imported plugin README includes the original source link, import commit, copyright attribution, and upstream license note.
| Plugin | Type | What It Shows |
|---|---|---|
| Copy Path / URL | Action | Copy the front app document path or active browser tab URL |
| Cursor Launcher | Launcher | Open recent Cursor workspaces |
| GitHub PR Monitor | Launcher | Browse open pull requests through the gh CLI |
| Jira Issues | Launcher | Browse Jira issues and custom JQL results |
| Kill Process | Launcher | List and terminate running processes |
| News Search | Launcher | Search Google News with a native preview surface |
| Quick Links | Launcher | Save and open reusable URL or path templates |
| QuickTime Recording | Launcher | Start QuickTime recordings |
| Stock Prices | Launcher | Track stock quotes and watchlists |
| Code Launcher | Launcher | Open recent Visual Studio Code workspaces |
| Xcode Recent Projects | Launcher | Open recent Xcode projects and workspaces |
Required folder shape:
plugins/community/launcher-my-plugin/
README.md
plugin.json
MyPlugin.swift
screenshots/
main.png
Start from plugins/_template when creating a new submission.
Folder Naming
Plugin folders must start with a type prefix:
launcher-for launcher pluginsfloating-for floating menu widget pluginsaction-for action pluginstrigger-for trigger pluginsstreamdeck-for Stream Deck pluginstouchbar-for Touch Bar plugins
Review And Whitelisting
Native Swift plugins run inside BetterTouchTool's process, so community plugins are reviewed before they are shown as trusted or installable.
In this repository, "whitelisted" means:
- the plugin was accepted into
plugins/community - the plugin has a
plugin.jsonmanifest - the plugin is listed in
plugins/index.json reviewStatusiscommunity-reviewedorofficial- the plugin README documents privacy-sensitive behavior
- source code is readable and intentionally scoped
- plugin identifier is unique and stable
- metadata comments match
plugin.json - file, network, shell, AppleScript, clipboard, and accessibility behavior is documented
- plugin does not collect or transmit unnecessary data
- plugin does not auto-run destructive actions
- plugin has a screenshot or short explanation when the UI is not obvious
- plugin compiles in BetterTouchTool from a clean
.swiftfile
Plugin Manifest
Each plugin folder must include a plugin.json file:
{
"schemaVersion": 1,
"name": "Plugin Name",
"identifier": "com.example.btt.plugin-name",
"type": "Launcher",
"entry": "Plugin.swift",
"author": {
"name": "Your Name",
"url": "https://example.com"
},
"description": "Short description of what this plugin does.",
"minimumBetterTouchToolVersion": "TBD",
"permissions": ["clipboard-read"],
"screenshots": ["screenshots/main.png"],
"reviewStatus": "submitted",
"origin": {
"repository": "https://github.com/example/source-repo",
"source": "https://github.com/example/source-repo/blob/main/Plugin.swift",
"importedFromCommit": "commit-sha"
},
"copyright": "Copyright (c) Original Author.",
"license": "MIT"
}
Use origin, copyright, and license when a plugin is imported from another
repository or adapted from existing source.
Allowed reviewStatus values:
submitted: used in pull requests before reviewcommunity-reviewed: accepted community pluginofficial: maintained by the BetterTouchTool projectdeprecated: kept for reference but hidden from normal discovery
clipboard-readclipboard-writefile-readfile-writenetworkopen-urlshellapple-scriptaccessibilitybtt-variablesnamed-triggerslauncher-plugin-instancesuser-defaultsprocess-controlspotlight
Submitting A Plugin
- Copy
plugins/_templateto a type-prefixed folder such asplugins/community/launcher-your-plugin-name. - Rename
Plugin.swiftand update the metadata comments. - Fill out
plugin.json. - Add screenshots if the plugin has visible UI.
- Add safety notes to the plugin README.
- Open a pull request.
Xcode Bundle Plugins
The older multi-target Xcode project now lives in xcode-bundle-examples.
Use the Xcode bundle path when a plugin needs:
- multiple source files
- bundled resources
- third-party dependencies
- custom framework setup
- explicit signing and notarization
xcode-bundle-examples/BetterTouchToolPluginDevelopment.xcodeproj
Bundle plugins are still useful, but the single-file Swift source plugin path is the default starting point for most users.
License
See LICENSE.