Bridge provides a JavaScript api for use in plugins and their widgets.
- Getting started
- Commands
- Events
- State
- Server
- Widgets
- Settings
- Types
- Items
- Client
- Variables
- Keyboard shortcuts
The api is available for plugins and widgets running in either the main process or browser processes of Bridge and can be included as follows. The module will be provided by Bridge at runtime.
const bridge = require('bridge')
Note: ES6 modules must be transpiled to CommonJS
import bridge from 'bridge'
Commands are a way of interacting with Bridge and other plugins. They are a form of RPC. Any data returned from a command handler will be returned as a Promise to executeCommand
.
Register a new command
This is a global operation as commands can only be registered once per workspace. Therefore, prefer registering commands in your plugin's main process.
Execute a command
Unregister a command
Events are similar to commands however multiple handlers can listen for one event and they cannot return any data to the caller.
Events are useful when listening for global operations such as state updates or play states. Such as:
import bridge from 'bridge'
bridge.events.on('item.play', item => {
console.log('Item', item, 'is now playing')
// React to the event
})
bridge.events.on('item.stop', item => {
console.log('Item', item, 'is now stopped')
// React to the event
})
Event | Description |
---|---|
state.change |
Emitted every time the remote state changes |
item.play |
Emitted when an item is played, after an optional delay |
item.stop |
Emitted when an item is stopped |
item.end |
Emitted when an item ends, this will not trigger when an item is stopped |
shortcut |
Emitted when a shortcut is triggered |
selection |
Emitted when the selection of the current client changes |
Emit an event with or without any data
Emit an event with or without any data but only to handlers registered in the local context
Events emitted this way will not reach other processes
Listen for an event
Listen for an event but only fire the handler once
Remove a listener for an event
The state is a shared object representing the workspace, this is used to render the UI as well as keeping track of settings
Apply an object to the state using a deep apply algorithm. Listeners for the event state.change
will be immediately called.
Note
Plugins have full access to the state and it's your responsibility as a developer to not overwrite any data needed for Bridge or other plugins. Each plugin have a reserved path (plugins.plugin-name
) where data can be stored without impacting other plugins.
It's encouraged to abstract state operations behind custom commands in order to minimize the risk of doing unwanted operations and for better readability.
Operations
Some special operations are available apart from adding data. Most of these can be replicated with logic in your plugin, however, utilizing them usually results in less data being sent between processes and will contribute to a faster end product.
Operation | Example | Description |
---|---|---|
- |
{key: 'new value'} |
Set a key to a new value |
$delete |
{key: {$delete: true}} |
Delete a key completely, works for both objects and arrays |
$replace |
{myArr: {$replace: ['new', 'values']}} |
Replace a value without trying to merge this key any deeper, this is useful for replacing complete arrays |
$insert |
{myArr: {$insert: 'my new value', $index: 2}} |
Insert a value at an index in an array - items will be pushed back and not deleted, this is using a splice operation under the hood |
$push |
{myArr: {$push: ['foo', 'bar']}} |
Merge an array with an already existing array by pushing all new values to the current array's tail |
$invert |
{myBoolean: {$invert: true}} |
Inverts the current value at the key as if it was a boolean such that the new value is !oldValue |
Example usage
import bridge from 'bridge'
/*
Apply an object { "foo": "bar" } to
the path of my-plugin
*/
bridge.state.apply({
plugins: {
'my-plugin': {
foo: 'bar'
}
}
})
/*
Apply multiple objects in the same call.
Operations are guaranteed to be executed in order.
The following call will add { "baz": "qux" } to the plugin object and immediately remove it before notifying listeners that the state was changed.
*/
bridge.state.apply([
{
plugins: {
'my-plugin': {
baz: 'qux'
}
}
}, {
plugins: {
'my-plugin': {
baz: { $delete: true }
}
}
}
])
Get the current state directly from the main process. The only argument is an optional dot-notation path specifying the part of the state to be returned. If omitted, the whole state will be returned. Calling this function may also update the local representation of the state.
Example usage
import bridge from 'bridge'
/*
Getting the full state will also update the local state representation
*/
const fullState = await bridge.state.get()
/*
Partial calls does not update the local state representation
*/
const myPlugin = await bridge.state.get('plugins.my-plugin')
Get the full local state without going to the main process. This is useful for rendering the UI and for places where async operations aren't possible.
Example usage
import bridge from 'bridge'
/*
emptyState will be undefined as the state has
not yet been fetched from the main process
*/
const emptyState = bridge.state.getLocalState()
/*
fullState will contain the full and
updated state as it was just fetched
*/
await bridge.state.get()
const fullState = bridge.state.getLocalState()
The server api provides methods for serving files through Bridge's internal web server. This allows the frontend to load static resources even when running as a server deployment. It's often used together with widgets.
Serve a file through the web server. Returns a Promise resolving to the path of the file as it should be requested from the server.
Serve a string as a file throught the web server. Returns a Promise resolving to the path of the file as it should be requested from the server.
Stop serving a file by its id
Widgets are web views controlled by plugins. They can provide additional functionality to the UI.
Register a new widget, it will immediately be made available in the UI
import bridge from 'bridge'
bridge.widgets.registerWidget({
id: 'myplugin.widget',
name: 'My widget',
uri: '/server/id-of-widget-entry',
description: 'A widget meant for demo purposes'
})
Settings are controls bound to a specific property in the shared state that appear under the plugins section in the settings menu.
There are several types of inputs available for settings. A setting and its type is declared through the setting definition.
Register a new setting definition to a group name, that is the name that will appear in the settings panel's navigation.
Types are blueprints for items, they can be created and extended using the contributes
property of your plugin manifest.
...in package.json
"contributes": {
"types": [
{
/*
The type id must be globally unique
*/
"id": "my-plugin.types.my-type",
"name": "My type",
"category": "My plugin",
/*
Inherit properties from
another type
*/
"inherits": "bridge.types.media",
"properties": {
/*
The property key must be globally unique and will
be used as the binding point for the value
It can, but is not required to be,
a dot-notation path
The key my-plugin.my-property will result
in the value being bound to item.data.my-plugin.my-property
*/
"my-plugin.my-property": {
"name": "My property",
"type": "string",
"default": "A default value",
/*
Whether or not to allow variables,
setting this to true will replace
any variable string in the property's
value with the variable value on play
Defaults to false
*/
"allowsVariables": true,
/*
All properties with the same group
will be shown together in the
inspector
*/
"ui.group": "My plugin",
/*
Set to true to signal that widgets should show this
information when presenting the item, such as the rundown
Defaults to false
*/
"ui.readable": true
},
/*
Plugins can can provide their own
inputs in the form of an inspector
widget using 'ui.uri'.
Note that using a custom inspector widget makes you
responsible for rendering both the UI as well as
binding the input to the state.
It's most often preferred to use
the functional api when registering
a type with 'ui.uri' as you can then
calculate the path.
*/
"my-plugin.my-second-property": {
"name": "My second property",
"type": "string",
"ui.uri": "/path/to/custom/input"
}
}
}
]
}
Render a full type specification from its id
Register a type
Items are playable objects containing the metadata of a certain type.
Create a new item of a specific type, its id will be returned as a promise.
Perform an apply operation to an item by its id - this can be seen as a save, or update, method for an item.
Get an item by its id.
Get an item from the local state representation by its id. This is useful when rendering the UI although it is not guaranteed to be up-to-date with the main process.
Delete an item by its id.
Play an item and set its state to 'playing'.
Stop an item and set its state to 'stopped'.
Add an issue to an item.
Example
bridge.items.applyIssue('6jI2', 'myIssue', {
description: 'Item is incorrectly configured'
})
Remove an issue from an item
Example
bridge.items.removeIssue('6jI2', 'myIssue')
Render a specific value for an item.
The client api between the renerer and main processes
Control aspects of clients
Only available within the render process
Await the current identity to be set, will return as soon as the identity is set or immediately if it's already set
Only available within the render process
Get the client's identity as set by the host app. This may be undefined if it has not yet been set. It's useful for manually getting client parameters if optimizing queries to the state.
Only available within the render process
Send a heartbeat
Only available within the render process
Select one or multiple items, will clear the current selection.
A state object can be included which will be forwarded to event handlers of the selection
event, currently only in the browser process. The state object looks like the following, where caller is the identity of the component that set the selection, which is useful for determining behaviour for listeners.
{
caller: String
}
Only available within the render process
Add one or more items to the selecton by their ids.
Only available within the render process
Subtract one or more items to the selecton by their ids.
Only available within the render process
Check whether or not an item is selected by the current client.
Only available within the render process
Clear the current selection
Only available within the render process
Get the current selection
Only available within main processes
Get the current selection of a connection by its id
Set the role of a specified connection, if assigning the main role to a connection, any other main connection will be denoted to a satellite
Get an array of all current connections
Get an array of all current connections with a specific role
Check whether or not a string contains at least one variable
Set a variable to a value
Get a variable's value
Get the values for all variables
Keyboard shortcuts SHOULD be registered with the API to give the user an index of which commands are available. Shortcut triggers can be overridden by the user in the settings panel.
Listen to a registered shortcut by subscribing to the shortcut
event.
When triggered the action will be provided as the listeners first argument as such:
Note: The shortcut event will ONLY be fired inside widgets, not in the plugin's main script.
import bridge from 'bridge'
bridge.events.on('shortcut', action => {
/*
It's your responibility to abort actions if they're not
expected to be triggered if the widget isn't in focus
Check the `bridgeFrameHasFocus` property on the window
object before reacting to the action
*/
if (!window.bridgeFrameHasFocus) {
return
}
console.log('Shortcut was triggered for action:', action)
// React to action
})
A shortcut can be registered using contributes.shortcuts
in a plugin's package.json
.
The field should be set to an array of shortcut-specification objects. Such as:
{
"name": "my-plugin",
"version": "1.0.0",
"description": "",
"engines": {
"bridge": "^0.0.1"
},
"contributes": {
"shortcuts": [
{
"id": "bridge.shortcuts.play",
"action": "play",
"description": "My custom shortcut",
"trigger": ["Shift", "A"]
},
{
"id": "bridge.shortcuts.stop",
"action": "stop",
"description": "My second custom shortcut",
"trigger": ["Shift", "B"]
}
]
}
}
Register a new keyboard shortcut using a shortcut specification.
See the shortcut schema for available keys to use as triggers.
import bridge from 'bridge'
bridge.shortcuts.registerShortcut({
id: 'myPlugin.shortcuts.myShortcut',
action: 'myPlugin.myAction',
description: 'Trigger my command',
trigger: ['Shift', 'CommandOrControl', 'A']
})