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 window
  • Beat.log(string) — log something (note: single argument only)

Access parsed screenplay content

See Document model

  • Beat.lines() – all Line objects in the script
  • Beat.outline() – all outline objects (OutlineScene), including synopsis lines and sections
  • Beat.scenes() – only scene objects (OutlineScene with heading type)
  • Beat.linesForScene(scene) – lines for a specified scene
  • Beat.getText() — whole document as string
  • Beat.currentLine — line which has the caret
  • Beat.setColorForScene(scene, color) — set color for a scene object (use "none" to remove any existing color)

Line objects

  • line.string — string content
  • line.position — starting index of line
  • line.textRange — range of the line ({ location: ..., range: ... })
  • line.range — full location and length INCLUDING line break
  • line.type — returns an integer value, matches Beat.type values (see Document model for valid types)
  • line.typeAsString() — returns type as string (see Document model for valid type strings)
  • line.isTitlePage() — true/false
  • line.isOutlineElement() — true/false
  • line.isInvisible() — true/false
  • line.cleanedString() — non-printing stuff removed
  • line.stripFormatting() – non-printing suff and any Fountain formatting removed
  • line.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 parser
  • line.forSerialization() - JSON data for the line

JSON serialization

  • Beat.outlineAsJSON() — complete outline as JSON
  • Beat.scenesAsJSON() — scenes as JSON
  • line.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 block
  • Beat.createDocumentFileWithAdditionalSettings(settings) — returns the plain-text Fountain file for current document, and appends custom JSON settings to the block

Access the editor

See Accessing the editor

Adding and replacing text

  • Beat.addString(String, index) – add string at some index
  • Beat.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 properties
  • Beat.setSelectedRange(location, length) – set user selection (make sure you don't go out of range)
  • Beat.scrollTo(index) – scroll to character index
  • Beat.scrollToScene(scene) – scroll to a scene object
  • Beat.scrollToLine(line) – scroll to a line object
  • Beat.focusEditor() — focus the editor window (from an HTML, for example)

Formatting

  • Beat.reformat(line) — reformat a single line in the screenplay
  • Beat.reformatRange(location, length) — reformat a range

Highlighting Text

  • Beat.textBackgroundHighlight("#000000", location, length) – set text background color
  • Beat.textHighlight("#000000", location, length) – set foreground color

Highlights can be cleared by reformatting the range (or the whole line).

Modal windows and prompts

See Modal windows and prompts

  • Beat.alert("Alert title", "Informative Text") – simple alert box

  • Beat.confirm("Title", "Informative text") — ask for confirmation, returns true or false

  • 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 setting
  • Beat.setUserDefault("setting name", value) – save a user setting

Document-specific settings

  • Beat.getDocumentSetting("setting name") – get a document-specific setting for current plugin
  • Beat.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 setting
  • Beat.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 tab
  • Beat.previousTab() — move to previous document tab
  • Beat.screen() — retrieve current screen size ([width, height])
  • Beat.windowFrame() — retrieve frame for current document window ([x, y, width, height])

Displaying HTML content

See Displaying HTML content

  • Beat.htmlPanel(htmlContent, width, height, callback, okButton) — displays a modal HTML window
  • let htmlWindow = Beat.htmlWindow(htmlContent, width, height, callback) — displays a standalone HTML window

Interacting With Windows

  • htmlWindow.title — window title
  • htmlWindow.setTitle(string) — set window title
  • htmlWindow.setHTML(htmlString) — set window content
  • htmlWindow.close() — close the window and run callback
  • htmlWindow.setFrame(x, y, width, height) — set window position and size
  • htmlWindow.getFrame() — returns position and size for the window
  • htmlWindow.screenSize() — returns size for the screen window has appeared on
  • htmlWindow.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 path
  • Beat.openFiles([extensions], function ([filePaths]) { }) — as above, but allows selecting multiple files and returns an array of paths
  • Beat.fileToString(path) – file contents as string
  • Beat.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 callback
  • Beat.writeToFile(path, content) – write a string to path

Printing

See Printing

  • Beat.printHTML(htmlString, settings) — prints out a pure HTML document

Pagination

  • Beat.currentPagination()pagination used by the preview controller
  • Beat.pagination() — creates a new pagination

Timer

See Timer

let timer = Beat.timer(seconds, callback, repeat)
  • timer.invalidate() — stop and remove the timer from memory
  • timer.stop() — stop and reset the timer
  • timer.start() — start the timer again
  • timer.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

See 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 document
  • Beat.documents() – returns an array of every open document
  • Beat.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 — when true, 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 key
  • Beat.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.