Action Editor

An action's steps can be edited two ways: as raw DSL text in the Code tab of the action's detail view, or in the pop-out Step Editor, a wider window with structured per-step forms, an address picker, and value pickers driven by the canonical mapping.

The Code tab is good for quick edits and reading existing actions. For anything multi-step, the pop-out Step Editor is what you want.

For the underlying file format, the full step grammar, predicate syntax, and validation rules, see the Action File Reference.


The Code tab

The action's detail view has two tabs along the top: Notes (metadata) and Code (steps as DSL text). Switch to Code to see and edit the action's body.

The tab is a single text area:

  • Monospace font, sized for ~16 lines of step text.
  • Saves on blur: click out of the field, or press Tab. Edits in flight don't write to disk until the field loses focus.
  • Edits in this tab are also visible in the pop-out Step Editor, and vice versa.

Parse errors

Below the text area, any DSL parse errors show in red, one line per error, formatted as Line N: <message>. The text area's border also turns red while errors exist. The Code tab won't save while any error is showing, so the file on disk keeps its last good content. Fix the reported line and click out again. The engine validates again at load time and execution time, so a malformed action never reaches the runtime.

Common parse errors are listed in the Action File Reference → Validation errors table.

Opening the Step Editor

The pencil icon at the right end of the tab bar (visible only on the Code tab, tooltip "Open Step Editor") opens the pop-out Step Editor window for the same file.

The Step Editor and the Code tab share the underlying .faction, but the Step Editor has its own explicit Save (it doesn't autosave). Edits made in the Step Editor land on disk when you click Save in its footer; they then show up in the Code tab on the next refresh.


The Step Editor window

The Step Editor is a separate window, 680×552 by default. Its header carries the window title Action Editor, the action's name beside it, an italic "unsaved" marker when there are pending edits, and a close button (X) at the far right.

Below the header is the Short ID field, then a scrollable stack of step cards with an Add step affordance at the bottom, then the footer.

The footer is where saving lives, the same idiom as the Domain Editor, since both are pop-out editors:

ButtonWhat it does
CancelAppears only while there are unsaved edits. Closes the window without writing.
SaveWrites the steps back to the .faction and keeps the window open. Disabled when there's nothing to save, and while the Short ID is invalid.
Save & CloseCommits and closes. When there's nothing to save, this button reads Close instead.

Closing via the header's X discards unsaved edits without prompting, the same as Cancel.


Short ID

The Short ID is what names this action on the status line when it runs, fails, or is skipped: ACT2-Prerun aborted: ResetInEQ. The status line has room for about 40 characters in total, so it can't use the action's full name.

  • 1–12 characters: letters, digits, space, dot, underscore, hyphen.
  • Required. While it's empty or invalid the field is outlined red and Save is disabled.
  • Pick something you'd recognise at a glance mid-transition.

Actions authored before Short IDs existed open with a value derived from the name and are marked unsaved straight away. The file on disk has no Short ID and will not run until you save one. The derived value is a truncation (Tempo Ramp Setup (Traktor)TempoRampSet), so treat it as a starting point rather than an answer.


A step card

Every step is rendered as a card with one or more labeled field rows. The card header has:

  • Type selector — a dropdown that flips the step's type. Switching type replaces the step's payload with a fresh default for that type (see Defaults below).
  • Up arrow / Down arrow — move the step up or down in the list. Disabled at the ends.
  • Trash icon (red) — delete the step.

The fields under the header depend on the step type.

SET

FieldNotes
AddressGrouped address picker. Filtered to writable addresses (telemetry-only addresses don't appear). See Address picker below.
ValueContext-aware. See Value input modes below.

WAIT

FieldNotes
DurationNumber input, milliseconds.

WAIT_UNTIL

FieldNotes
AddressGrouped address picker — any address (writable or telemetry).
ConditionOperator dropdown (==, !=, >=, <=, >, <) + value input. The operator + value combine into a predicate.
TimeoutMilliseconds. If the predicate isn't satisfied in time, the action fails.

CAPTURE

FieldNotes
NameSnapshot name. Used by a later RESTORE step, in this action or another one — snapshots are session-scoped. See Action files → CAPTURE.
AddressesMulti-address picker — pick one or more writable addresses to snapshot.

RESTORE

FieldNotes
NameSnapshot name. If the action contains any CAPTURE steps, this is a dropdown of their names; otherwise it's a text input — which is the restore-only half of a pre-run / post-run pair, where you type the name the other action captured. Referencing a name that was never captured (typo, skipped IF branch, or a pair whose capture half didn't run) fails the action at run time.

IF

FieldNotes
AddressGrouped address picker.
ConditionOperator + value, same shape as WAIT_UNTIL.
THENNested step list (indented). Any step type, including a nested IF.
ELSEOptional nested step list. Add it via + Add ELSE; remove it via the trash icon on the ELSE header.

Value input modes

The Value field on SET, WAIT_UNTIL, and IF adapts to the address's control type. The Step Editor reads the canonical mapping at load time to pick the right input.

  • Trigger / Boolean controls (e.g. transport toggles) → a Select dropdown of the address's symbolic names (ON / OFF, etc.).
  • Indexed controls (e.g. tempo range, FX type) → a searchable Select of all symbolic names defined for that address.
  • Continuous controls with named points (e.g. EQ bands with CUT, UNITY, BOOST) → a Select that lists each named point as Name (numeric value), plus a Custom… option. Picking Custom switches to a number input clamped to [0.0, 1.0] with a Named link to return to the symbolic dropdown.
  • Continuous without named points → number input directly.
  • Predicate values (the right-hand side of WAIT_UNTIL / IF conditions) → free-form text input with a small dropdown of any symbolic shortcuts defined for the address.

Authoring symbolically is the recommended idiom: UNITY survives mapping renumbering and self-documents the intent. Numeric literals are always a safe fallback. The full rule set lives in Action File Reference → Symbolic values.

Address picker

The Address field on SET, WAIT_UNTIL, CAPTURE, and IF opens a single popover with every canonical address organized by region:

  • Incoming / Outgoing — role-addressed deck controls (see Role addresses).
  • Deck A / Deck B / Deck C / Deck D — literal-deck controls.
  • FX 1FX 4 — global FX-unit controls. Units beyond what the active DJ app exposes are labeled unsupported.
  • Common — master, crossfader, and other non-deck / non-FX-unit controls.

Each region is collapsible. The region containing the current value is expanded by default; the rest are collapsed with an item-count badge on the right so you can see how much is in each section at a glance. Clicking a region header toggles it.

The search box at the top filters items across every region simultaneously and auto-expands any region with matches. The currently selected item is highlighted blue with a check mark.

Role addresses

The Address picker offers role addresses (deck.incoming.* and deck.outgoing.*) for any deck-scoped control. Authoring against a role address keeps the action portable across decks: the runtime resolves the role to a physical deck via the Control Surface API at execution time, and roles swap on transition completion. Pick a literal deck address (deck.a.eq.high) only when you specifically need that physical deck regardless of role.

CAPTURE's multi-address picker

CAPTURE uses a multi-select variant of the same picker: picked addresses appear as chips above the popover trigger, and the popover hides addresses you've already picked so you can't double-add. Remove a chip via its × button.


Adding, reordering, removing

  • Add a step. The + Add step affordance at the bottom of the step list adds a new SET step with empty address and value.
  • Add inside an IF branch. Each IF.then and IF.else list has its own + Add step at the bottom.
  • Flip a step's type. Use the Type selector on the step header. The step's payload resets to the default for that type, so your previous fields are not preserved across a type change.
  • Reorder. Up / Down arrows on each step card. Disabled when the step is already at the start or end.
  • Remove. Trash icon on the step card. Deletion is immediate within the Step Editor, but the change isn't persisted until you click Save in the editor's footer.

Defaults when you add or flip a step

When you add a new step or change a step's type, the Step Editor inserts the type's default payload:

TypeDefault
SETempty address, empty value
WAIT100 ms
WAIT_UNTILempty address, predicate == ON, timeout 5000 ms
CAPTUREempty name, no addresses
RESTOREempty name
IFempty address, predicate == ON, one empty SET step in then, no else

These are starting points; fill in the real values before saving.


Save flow and round-trip

A round-trip looks like:

  1. Code tab → pencil → Step Editor opens, reads the .faction from disk.
  2. You edit step cards; the "unsaved" marker appears in the header.
  3. Save → the Step Editor serialises steps back to the file, then notifies the main app.
  4. The main app re-reads the file so the Code tab and any open fusor's pre-run row pick up the new content.

The Code tab and the Step Editor are two views of the same file. The Code tab autosaves on blur; the Step Editor saves explicitly. If you're editing both at once, the last save wins. Work in one at a time.


Where to next