API overview - lmparppei/BeatPlugins GitHub Wiki
Debugging console
The easiest way to debug your plugin code is to do it using the plugin console.
Beat.openConsole()
— open console windowBeat.log(string)
— log something (note: single argument only)
Access parsed screenplay content
See Document model
Beat.lines()
– allLine
objects in the scriptBeat.outline()
– all outline objects (OutlineScene
), including synopsis lines and sectionsBeat.scenes()
– only scene objects (OutlineScene
withheading
type)Beat.linesForScene(scene)
– lines for a specified sceneBeat.getText()
— whole document as stringBeat.currentLine
— line which has the caretBeat.setColorForScene(scene, color)
— set color for a scene object (use"none"
to remove any existing color)
Line objects
line.string
— string contentline.position
— starting index of lineline.textRange
— range of the line ({ location: ..., range: ... }
)line.range
— full location and length INCLUDING line breakline.type
— returns an integer value, matchesBeat.type
values (see Document model for valid types)line.typeAsString()
— returns type as string (see Document model for valid type strings)line.isTitlePage()
— true/falseline.isOutlineElement()
— true/falseline.isInvisible()
— true/falseline.cleanedString()
— non-printing stuff removedline.stripFormatting()
– non-printing suff and any Fountain formatting removedline.omitted
— true/false, check if the line is inside an omitted block (/* */
)line.note
— true/false, check if the line is completely a note (wrapped in[](/lmparppei/BeatPlugins/wiki/)
)line.clone()
— make a copy of the line, detached from the parserline.forSerialization()
- JSON data for the line
JSON serialization
Beat.outlineAsJSON()
— complete outline as JSONBeat.scenesAsJSON()
— scenes as JSONline.forSerialization()
— serialize a single line object
Retrieve document data
See Document data
Beat.createDocumentFile()
— returns the plain-text Fountain file for current document, along with document settings JSON blockBeat.createDocumentFileWithAdditionalSettings(settings)
— returns the plain-text Fountain file for current document, and appends custom JSON settings to the block
Access the editor
Adding and replacing text
Beat.addString(String, index)
– add string at some indexBeat.replaceRange(index, length, string)
– replace a range with a string (which can be empty to remove text)
Navigating Through The Document
Beat.selectedRange()
– returns a range object with.location
and.length
propertiesBeat.setSelectedRange(location, length)
– set user selection (make sure you don't go out of range)Beat.scrollTo(index)
– scroll to character indexBeat.scrollToScene(scene)
– scroll to a scene objectBeat.scrollToLine(line)
– scroll to a line objectBeat.focusEditor()
— focus the editor window (from an HTML, for example)
Formatting
Beat.reformat(line)
— reformat a single line in the screenplayBeat.reformatRange(location, length)
— reformat a range
Highlighting Text
Beat.textBackgroundHighlight("#000000", location, length)
– set text background colorBeat.textHighlight("#000000", location, length)
– set foreground color
Highlights can be cleared by reformatting the range (or the whole line).
Modal windows and prompts
-
Beat.alert("Alert title", "Informative Text")
– simple alert box -
Beat.confirm("Title", "Informative text")
— ask for confirmation, returnstrue
orfalse
-
Beat.prompt("Title", "Informative Text", "Placeholder string")
– get text input from the user, returns a string -
Beat.dropdownPrompt("Title", "Informative Text", [value, value, value])
– allow the user to select a value from an array, returns a string
Advanced Modal Windows
You can create more advanced modal windows with multiple types of inputs using Beat.modal()
. It takes in an object with the following properties: title
(title of the modal), info
(informative text) and items: []
(an array of inputs).
See the example below to get an idea on how advanced modals work.
Beat.modal({
title: "This is a test modal",
info: "You can input stuff into multiple types of fields",
items: [
{
type: "text",
name: "characterName",
label: "Character Name",
placeholder: "First Name"
},
{
type: "dropdown",
name: "characterRole",
label: "Role",
items: ["Protagonist", "Supporting Character", "Other"]
},
{
type: "space"
},
{
type: "checkbox",
name: "important",
label: "This is an important character"
},
{
type: "checkbox",
name: "recurring",
label: "Recurring character"
}
]
}, function(response) {
if (response) {
// The user clicked OK
Beat.log(JSON.stringify(response))
} else {
// The user clicked CANCEL
}
})
Saving Plugin Data
See Saving User and Document Settings
App-wide settings
Beat.getUserDefault("setting name")
– get a user settingBeat.setUserDefault("setting name", value)
– save a user setting
Document-specific settings
Beat.getDocumentSetting("setting name")
– get a document-specific setting for current pluginBeat.setDocumentSetting("setting name", value)
– set a document-specific setting for current plugin
You are only able to set plugin-specific document settings with these methods. If you really know what you are doing, you can access the actual document settings, such as character genders. Be sure not to overwrite any required values. To get a clue about the values, open a Fountain file created by Beat in a plain-text editor and see the JSON block at the end of the file.
Beat.setRawDocumentSetting("setting name", value)
— set a document-specific settingBeat.getRawDocumentSetting()
— get a document-specific setting
Listen to changes in document
See Resident plugins and listeners
Listen to text change
Beat.onTextChange(
function (location, length) {
Beat.log("Edited at " + location + " (length: " + length +")")
}
)
Listen to changes in outline
Beat.onOutlineChange(
function (...outline) {
for (let i=0; i < outline.length; i++) {
// Do something with the new outline
}
}
)
Listen to current scene
const scenes = Beat.outline()
Beat.onSceneIndexUpdate(
function (sceneIndex) {
let currentScene = scenes[sceneIndex]
}
)
Listen to Document Saving
Beat.onDocumentSave(function () {
// Do something when the document is saved
})
Disabling Listeners
Beat.onTextChangeDisabled = true/false
Beat.onOutlineChangeDisabled = true/false
Beat.onSelectionChangeDisabled = true/false
Beat.onSceneIndexChangeDisabled = true/false
Screen and window controls
Beat.nextTab()
— move to next document tabBeat.previousTab()
— move to previous document tabBeat.screen()
— retrieve current screen size ([width, height]
)Beat.windowFrame()
— retrieve frame for current document window ([x, y, width, height]
)
Displaying HTML content
Beat.htmlPanel(htmlContent, width, height, callback, okButton)
— displays a modal HTML windowlet htmlWindow = Beat.htmlWindow(htmlContent, width, height, callback)
— displays a standalone HTML window
Interacting With Windows
htmlWindow.title
— window titlehtmlWindow.setTitle(string)
— set window titlehtmlWindow.setHTML(htmlString)
— set window contenthtmlWindow.close()
— close the window and run callbackhtmlWindow.setFrame(x, y, width, height)
— set window position and sizehtmlWindow.getFrame()
— returns position and size for the windowhtmlWindow.screenSize()
— returns size for the screen window has appeared onhtmlWindow.runJS(javascriptString)
— sends JavaScript to be evaluated in the window
File access
See File access
Import data
Beat.openFile([extensions], function (filePath) { })
– displays an open dialog for an array of extensions and returns a pathBeat.openFiles([extensions], function ([filePaths]) { })
— as above, but allows selecting multiple files and returns an array of pathsBeat.fileToString(path)
– file contents as stringBeat.pdfToString(path)
– converts PDF file contents into a string
Export data
Beat.saveFile(extension, function (filePath) { })
– displays a save dialog and returns the path to callbackBeat.writeToFile(path, content)
– write a string to path
Printing
See Printing
Beat.printHTML(htmlString, settings)
— prints out a pure HTML document
Pagination
- See Pagination
Beat.currentPagination()
– pagination used by the preview controllerBeat.pagination()
— creates a new pagination
Timer
See Timer
let timer = Beat.timer(seconds, callback, repeat)
timer.invalidate()
— stop and remove the timer from memorytimer.stop()
— stop and reset the timertimer.start()
— start the timer againtimer.running()
– check if the timer is currently running
When repeat
is set to true
the timer will stay in memory. It defaults to false
, so you can create one-off timeouts, too:
Beat.timer(1.0, function () {
Beat.log("One second elapsed.")
})
Revisions
See Revisions
Beat.revisedRanges()
— returns an object with revision generation as key and ranges as arrays, ie.{ "revisionColor": [[loc, len], ...], ... }
Beat.bakeRevisions()
— stores all the current revised ranges into lines (line.ranges.revisions
)Beat.bakeRevisionsInRange(loc, len)
— stores revisions in a given range into corresponding lines
Background threading
Beat.async(function)
— run asynchronous code in a background thread (note: never access anything related to UI or screenplay content)Beat.sync(function)
— run synchronous code in the main thread (to return results to the UI)
Accessing the parser
-
Beat.currentParser
— current parser -
Beat.parser(stringToParse)
— create a new parser -
parser.lines
— line objects (note: property, not a method) -
parser.outline
— all scene objects, including synopsis lines and sections (note: property, not a method) -
parser.scenes
— scene objects only (note: property, not a method) -
parser.titlePage
— title page elements -
parser.linesInRange({ location: x, length: y })
— get all lines in the selected range (note: parameter has to be a range object) -
parser.lineAtIndex(index)
— get line item at given character index
parser.sceneAtIndex(index)
— get outline item at given character index
Accessing other documents
Beat.document()
- returns the document object for current documentBeat.documents()
– returns an array of every open documentBeat.interface(document)
— returns a plugin API interface for another document
The Beat
object always refers to the current document. If you need to access the other documents, you'll have to first retrieve a Document
object and then create an so-called interface for it, which is identical to the Beat
object, but refers to another document.
const documents = Beat.documents()
for (const doc of documents) {
if (doc == Beat.document()) {
// This is our open document. Skip.
continue
}
const interface = Beat.interface(doc)
// You can now use any plugin API methods on the other document using this interface.
interface.addString("Hello world", 0)
}
Menus (macOS only)
See Menus
-
Beat.menuItem(title, [shortcuts], callback)
— creates and returns a menu item -
menuItem.on
— whentrue
, menu item will display a checkmark next to it -
Beat.separatorMenuItem()
— creates and returns a separator menu items -
Beat.menu(title, [children])
— creates (and adds) a menu with given menu items -
menu.addItem(menuItem)
– adds a menu item -
menu.removeItem(menuItem)
– removes a menu item
Whenever a menu is created, it's automatically added to the top menu bar.
let item = Beat.menuItem("Hello World", ["alt", "cmd", "x"], () => {
Beat.log("Hello world")
})
let menu = Beat.menu("My menu", [item])
Checking compatibility
Beat.compatibleWith(versionString)
— see if the user is running a compatible version of Beat
Accessing document variables
If you REALLY, REALLY know what you are doing, you can access document class property values. These calls should be used only for development purposes and preferrably not in a distributed plugin. Contact the developer for exposing the required property for actual plugin API, if you happen to find something that you need.
Beat.setPropertyValue(key, value)
— set a property value by keyBeat.getPropertyValue(key)
— get property value by key
Accessing Objective C methods directly
If you REALLY, REALLY, REALLY, REALLY know what you are doing, you can call Objective C methods in the document directly. This allows access to methods that are not exposed to the API, and might come in handy when debugging your plugin, or just generally poking around and experimenting.
Beat.objc_call(selector, [arg1, arg2, ...])
selector
is an Objective C selector, such as getText
or replaceCharactersInRange:withString:
. You need to have an understanding of how Objective C selectors and calls work, and bear in mind that it is extremely easy to crash the app with malformed selectors and arguments.