Single-File Plug-In (also called “Simple Plug-In”)

The single-file plug-in employs an economical design that delivers great flexibility and functionality in a solitary file. Containing both the plug-in metadata and function code, these plug-ins are perfect for quick light-weight development using any text editing application.

TIP: Visit the PLUG-IN TEMPLATE GENERATORS for quick generation of simple plug-ins.

PlugIn.Action Class

A plug-in’s “action” is the function that does the work. The parent plug-in is the wrapper for the action.


This constructor is used to create a new “action” or instance of the PlugIn.Action class:

Validation Property

An instance of the PlugIn.Action has an optional validate property whose value is a function that checks to see if the current application environment has the correct conditions for the action to execute. For example, checking to see that something is selected in the application interface.

IMPORTANT: With single-file plug-ins, the parent plug-in and it's single action are contained within the same file. With bundle plug-ins, the actions of the plug-in are contained in single files stored within the plug-in bundle.

Plug-In Design

The structure of a single-file plug-in is expressed in two components:

As listed below, the Plug-In Code section of an Omni Automation single-file plug-in is comprised of specilized JavaScript functions placed within a self-invoking anonymous (unnamed) JavaScript arrow function. This anonymous (unnamed) function wrapper is used to minimize the chance of conflicts with other actions whose code statements contain variables using the same titles as those used in other actions.

Referencing the shown lined-numbered code example:

Basic Plug-In

Basic Single-File Plug-In

/*{ "type": "action", "targets": ["omnigraffle", "omnifocus", "omniplan", "omnioutliner"], "author": "", "identifier": "", "version": "1.0", "description": "A single-file plug-in.", "label": "Plug-In Menu Item Title", "shortLabel": "Item Title", "paletteLabel": "Toolbar Item", "image": "gearshape.fill" }*/ (() => { const action = new PlugIn.Action(function(selection, sender){ // action code try { // PROCESSING STATEMENTS } catch(err){ new Alert(error.name, err.message).show() console.error(err) } }); action.validate = function(selection, sender){ // validation code return true }; return action; })();

Asynchronous Plug-In

For those with more advanced JavaScript knowledge, here’s an asynchronous version of the single-file plug-in that incorporates await statements within an asynchronous wrapper rather than relying on explicit Promise handlers:

Asynchronous Single-File Plug-In

/*{ "type": "action", "targets": ["omnigraffle", "omnifocus", "omniplan", "omnioutliner"], "author": "", "identifier": "", "version": "1.0", "description": "A plug-in that performs asynchronous operations.", "label": "Plug-In Menu Item Title", "shortLabel": "Item Title", "paletteLabel": "Toolbar Item", "image": "gearshape.fill" }*/ (() => { const action = new PlugIn.Action(async function(selection, sender){ try { // ASYNCHRONOUS STATEMENTS (await) } catch(err){ if(!causedByUserCancelling){ new Alert(err.name, err.message).show() } } }); action.validate = function(selection, sender){ // validation code return true }; return action; })();

See the Sequential Forms documentation for an example of the use of the asynchronous plug-in template.

1st-Run Interaction Plug-In

A plug-in that uses the Preferences class to create a 1st-run interaction. To reset the plug-in, hold down Control modifier key when selecting plug-in from the Automation menu.

1st-Run Sensor

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.all.first-run-sensor", "version": "1.0", "description": "This plug-in will present an alert the first time it is executed. To reset the plug-in, hold down Control key when selecting from Automation menu.", "label": "First Run Example", "shortLabel": "1st Run", "paletteLabel": "1st Run", "image": "gearshape.fill" }*/ (() => { const preferences = new Preferences() const action = new PlugIn.Action(async function(selection, sender){ try { incrementedCount = preferences.readNumber("incrementedCount") if(!incrementedCount){ preferences.write("incrementedCount", 0) incrementedCount = 0 } if (app.controlKeyDown){ alertMessage = "Reset the stored count?" alert = new Alert("Confirmation Required", alertMessage) alert.addOption("Reset") alert.addOption("Cancel") buttonIndex = await alert.show() if (buttonIndex === 0){ preferences.write("incrementedCount", 0) console.log("incrementedCount", "reset") return "user reset" } } console.log("incrementedCount", incrementedCount) if (incrementedCount === 0){ alertTitle = "Plug-In 1st Run" alertMessage = "Welcome to the 1st-Run Plug-In!" await new Alert(alertTitle, alertMessage).show() } preferences.write("incrementedCount", incrementedCount + 1) // PROCESSING CODE GOES HERE } catch(err){ if(!causedByUserCancelling){ new Alert(err.name, err.message).show() } } }); action.validate = function(selection, sender){ // validation code return true }; return action; })();

Plug-In Metadata Keys/Values

The plug-in metadata is comprised of key/value pairs detailing the manner in which the plug-in should interact with the host application. Here is a synopsis of the plug-in metadata:

Plug-In Metadata

/*{ "type": "action", "targets": ["omnigraffle", "omnifocus", "omniplan", "omnioutliner"], "author": "You or Your Company", "identifier": "com.youOrCompany.all.plug-in-name", "version": "1.0", "description": "Description of what the plug-in does.", "label": "Plug-In Menu Item Title", "shortLabel": "Item Title", "mediumLable": "Menu Item Title", "paletteLabel": "Toolbar Picker Title", "image": "newspaper.fill" }*/

Plug-In Metadata Interface

(⬇ see below ) The metadata displayed for a plug-in in the Automation Configuration dialog.


Selection Input

In the plug-in code, both the action declaration and validation handlers include two input parameters in their functions:

The sender parameter is documented in detail in the Sender Parameter section of this website.

Validation Function

The value returned by the Validation Function of the PlugIn.Action instance determines whether or not the plug-in is enabled or disabled in the application interface. A returned value of true means that the Automation Menu, Share Menu, and any corresponding Toolbar Item will be enabled for use. A returned value of false will make those disabled.

NOTE: Use of the Validation Function is optional. If omitted from the plug-in code, a result value of true will be assumed by the plug-in.

The Validation Function is often used to ensure that the items necessary to be processed by the plug-in are indeed currently selected in the application interface. Here are examples of the Validation Function using the passed-in selection parameter to identify selected items:

Validation Function

action.validate = function(selection, sender){ // validation code return a value that is either true or false };
OmniFocus: Single Task whose Name begins with “Call”

action.validate = function(selection, sender){ return ( selection.tasks.length === 1 && selection.tasks[0].name.startsWith("Call") ) };
OmniPlan: One or More Resources Selected

action.validate = function(selection, sender){ return (selection.resources.length > 0) };
OmniGraffle: Single Selected Shape with Text

action.validate = function(selection, sender){ return ( selection.solids.length === 1 && selection.solids[0].text != "" ) };
OmniOutliner: One or More Columns Selected

action.validate = function(selection, sender){ return (selection.columns.length > 0) };

IMPORTANT: The validation function is meant to provide confirmation of application state. Avoid using complex or process-intensive statements within the function as they may cause the application to become unresponsive.

For example, calling the flattenedTasks property of Database class in OmniFocus may cause delays for instances where the application database contains a large number of items.

Avoid Intensive Validation

action.validate = function(selection, sender){ return (flattenedTasks.length > 0) };

Validating Plug-In Forms

See the section on Form Validation for a detailed explanation of how to validate plug-in forms.

File Extensions

The file extensions used for Omni Automation plug-ins include four app-specific extensions for single-file plug-ins, matching those currently used for plug-in bundles. In addition, for plug-ins that have two or more Omni applications as targets, the generic “omnijs” file extension is used.

The five supported file extensions are:

NOTE: It is recommended to not use spaces in plug-in or action file names. Hyphens ( - ) and underscore ( _ ) characters work well as replacements for spaces: og-my-graphics-plug-in.omnigrafflejs


For single-file plug-in fies using an app-specific file extension, the installation process is simple:

  1. Download the plug-in
  2. Open|Tap the plug-in file
  3. Complete the forthcoming installation security dialogs to indicate your approval to install the plug-in, and select the location in which the file is to be stored.

(⬇ see below ) Security installation dialogs on iPadOS/iOS. CLICK|TAP Location button  01  to select the folder  02  in which to store the plug-in file:

plug-in-install-ios-security plug-in-install-ios-location

(⬇ see below ) Security installation dialogs on macOS. Click “Location” menu  01  to select the folder in which to store the plug-in file:


IMPORTANT: To install single-file plug-ins whose file extension is the generic “omnijs” on iPadOS or iOS, copy their files into a folder identified as a linked folder in “Configure Plug-Ins” panel in the application’s “Settings” window.

For more information about “linked” plug-in folders, see the section on Plug-In Installation and Manegement.