- Notifications
You must be signed in to change notification settings - Fork404
Creating a Plugin
This is a basic walkthrough for creating a plugin. We'll create a simplesay plugin, which wraps the OS X command-linesay
command to speak whatever text the user gives us.
If you're writing a plugin that's functionally similar to an existing plugin (like searching a website), you may instead want to try copying and modifying an existing plugin (likewebsearch). Find these inPluginDirectories/1
.
Download the tool
Flashlight Tool is a tool for testing and debugging Flashlight plugins. It's not perfect, but it's the best way to debug your Python or Javascript.
What makes a plugin
A plugin needs a couple things, which we'll explain below:
- a
.bundle
directory, containing: - an
info.json
- an
examples.txt
- a
plugin.py
- an
Icon.png
(optional)
All plugins live in~/Library/FlashlightPlugins
. To begin, create a folder and name itsay.bundle
. OS X will immediately give it a fancy lego-brick icon. Right-click andShow Package Contents.
The first thing we need is aninfo.json
file. Here's what to put in it:
{"name":"say",// this must be the same as your folder name"displayName":"Say Something","description":"Speaks your input text","examples":["say hello world","say good morning"],// these appear by your plugin's description"categories":["Utilities"],"creator_name":"Your name","creator_url":"A link of your choosing"}
(Remember to remove the inline comments, or else the JSON won't validate and the plugin will show up blank.)
Once that's there, you should be able to open up the FlashlightInstalled list and see your plugin. It doesn't do anything yet.
We need to give Flashlight some examples of commands that should invoke your plugin. Create anexamples.txt
file.
If this were a simple plugin likeshutdown
, which shuts down your computer, the examples would the things likeshutdown,shut down, andturn off my computer. Our plugin, though, takes text input (the text to speak), so we need to add afield — a slot that Flashlight will fill with text, and pass on to us. That looks like this:
say ~message(Good Morning)speak ~message(Hello, world)please speak ~message(what's up) out loud
As you can imagine, Flashlight will recognize phrases likespeak good morning out loud and passgood morning on to our plugin.
If you have more complex queries you want to process, you shouldlearn more about the parser and examples.txt.
When someone types a command likespeak good morning (but before they press enter), ourplugin.py
file will be loaded, and theresults
function is going to be invoked. It'll pass in a dictionary containing all ourfields — in this case, just~message, which will be set togood morning. It'll also pass the entire original query if we need it.
All we need to do is return a dictionary containing information about what should appear in Spotlight.
defresults(fields,original_query):message=fields['~message']return {"title":"Say '{0}'".format(message),"run_args": [message]# ignore for now }
Now, if we type "speak hello spotlight" into Spotlight, we'll see the title our plugin returned.
Sometimes, your Python script might crash. That's okay. If you're usingFlashlightTool
, you'll see error messages in the "errors" panel. You can use thelogging module to output trace messages. If you want to debug your Javascript or HTML, right-click your web content andInspect Element.
Of course, the plugin doesn't actuallydo anything yet — ideally, we want it to speak something out loud when we hit Enter. That's easy. Just add a functionrun
toplugin.py
.
Now, we need some way of passing the message that we're supposed to speak torun
. That's why we returned an array containing the message in therun_args
field of ourresults
dictionary.run
is invoked with the arguments from therun_args
list (in fact, youneed a run_args list forrun
to even be called, although it can be empty.)
defrun(message):importosos.system('say "{0}"'.format(message))# TODO: proper escaping via pipes.quote
There.Now our plugin should work. Type "say hello" into Spotlight, hit enter, watch it go.
Note: FlashlightTool automatically reloads plugin code pretty often, but Flashlight itself is very conservative about reloading. If things aren't working, try restarting Flashlight by togglingEnable Spotlight Plugins in the Flashlight window.
Many plugins, like Weather and Google, return HTML and JavaScript to show content inline in the Spotlight window. You can do this by returning an HTML string from yourresults
function:
defresults(fields,original_query):message=fields['~message']html="<h1>{0}</h1>".format(message)return {"title":"Say '{0}'".format(message),"run_args": [message],# ignore for now,"html":html }
If you'd like to load a web URL, you should return adelayed Javascript redirect
, which looks like this:
<script> setTimeout(function(){window.location='http://google.com'}, 500); // delay so we don't get rate-limited by doing a request after every keystroke</script>
There are two more fields you can return in yourresults
json that may be relevant if you're using webviews:
webview_links_open_in_browser
:optional when the user clicks links in the webview, they'll close Spotlight and open in a browserwebview_user_agent
:optional override the user agent in the webview. Useful if you want to load a mobile-optimized site that fits the size of the Spotlight window.
Your results function should return fast. There's no time to perform HTTP requests. If you need to fetch data from the web (like the Weather plugin), you shouldreturn HTML and Javascipt that make the request. This way, users can see your result while it's loading. The JS you return fromresults
is not subject to the same-origin policy.
Your run function should also return fast. Results functions get killed after a couple seconds. If you're doing something long-running, spin up another process andfire a notification when you're done.
Add a 512x512 (or smaller) icon to your bundle, and call itIcon.png
. Circular icons are preferred.
You can provide a separate icon for users in Yosemite's Dark Mode — call itIcon-dark.png
.
Online plugin pages likethis one have screenshots of plugins. Take a screenshot of Spotlight showing your plugin by pressingCommand + Shift + 4
, pressing space and clicking the Spotlight window, then saving that screenshot inside your plugin asScreenshot.png
.
If you're doing things like searching contacts or running Applescript, you should check out theuseful modules for plugins.
Once you've written a plugin, we'd love for you to submit it so others can download it from within Flashlight. Clone the Flashlight repository, stick your.bundle
inPluginDirectories/1
, andsubmit a pull request.