DSL Inspector — Developer Reference

Technical reference for the in-browser DSL editor overlay: element hit detection, cue collection, save paths, and the propagate-parent redirect.


What It Is

The DSL inspector is an interactive overlay that activates when the inspector button in the topbar is toggled. It lets you:


File

system/dslInspector.js

Activation

// Toggled by the topbar inspector button or the I shortcut
active = true/false;

When active, pointerover, pointermove, and pointerdown listeners are attached to the document. All three use _cueIdAt() for element resolution.


Element Hit Detection — _cueIdAt(el)

Walks up the DOM from el toward document.body looking for the nearest element with a parseable DSL string. Returns { id, el, fromDataAttr } or null.

Resolution order (per node)

  1. Propagate child redirect — if data-oscilla-propagate-child="true", skip to the parent and return the parent's data-oscilla-propagate-id as the hit id. This means clicking any expanded child opens the parent propagate template, not the child's expanded DSL.

  2. data-oscilla attribute containing ( — preferred source for elements that have been migrated from legacy id-DSL.

  3. id attribute containing ( — legacy source.

If no match is found walking up to <body>, returns null.


Cue Collection — _collectCues(startEl)

Called after _cueIdAt to build the editable cue list. Walks up from startEl collecting DSL from:

Each DSL string is parsed by _parseAllCues(dsl) which calls parseCueToAST for every space-separated cue expression. Results are accumulated into _allCues which the form panel renders.


Save Paths — _save()

Three distinct paths depending on the element type:

1. Propagate parent group

Detected by srcEl.getAttribute('data-oscilla-propagate-id') being set.

_fetchPatchId(project, propagateSourceId, newDsl)
  → PATCH /api/project/:name/patch-svg-id
  → replaces <g id="OLD_PROPAGATE_DSL"> with <g id="NEW_PROPAGATE_DSL">

srcEl.setAttribute('id', newDsl)
srcEl.removeAttribute('data-oscilla-propagate-id')
reexpandPropagateGroup(srcEl)           ← live re-expansion, no reload

reexpandPropagateGroup is imported from parser/preProcessPropagate.js. See dev-propagate.md for its internals.

2. Legacy id-DSL element

Detected by currentId?.includes('(') — the element still has its cue DSL baked into its id attribute (the old format).

_fetchPatchId(project, oldCueId, uid)
  → renames the element id from the DSL string to a plain uid

_fetchSetAttr(project, uid, newDsl)
  → sets data-oscilla="newDsl" on the now-plain-id element

srcEl.setAttribute('id', uid)
srcEl.setAttribute('data-oscilla', newDsl)
handleCueTrigger(newDsl, false, true, srcEl)   ← restart animation live

This is a one-way migration: after saving, the element moves from legacy id-DSL to the data-oscilla format.

3. data-oscilla element (current format)

_fetchSetAttr(project, currentId, newDsl)
  → sets data-oscilla="newDsl" on element with id=currentId

srcEl.setAttribute('data-oscilla', newDsl)
handleCueTrigger(newDsl, false, true, srcEl)   ← restart animation live

Server API Calls

Function Endpoint Effect
_fetchPatchId(project, oldId, newId) POST /api/project/:name/patch-svg-id Replaces id="oldId" with id="newId" in score.svg
_fetchSetAttr(project, elementId, value) POST /api/project/:name/set-attr Sets data-oscilla="value" on element with id=elementId

Both operations work on the source score.svg file on the server. The client applies the same change to the live DOM immediately so the result is visible without reload.

For local file system projects (localProjectFS.isActiveForProject) both functions fall back to _localSetAttr / _clientSvgDataAttr which patch the in-memory SVG text directly.


Propagate-Specific Behaviour Summary

Scenario What happens
Hover over a propagate child Tooltip shows the parent's propagate template (via data-oscilla-propagate-id)
Click a propagate child _cueIdAt redirects to parent; _collectCues reads data-oscilla-propagate-id; form shows propagate template
Save a propagate edit _fetchPatchId patches the <g id="propagate(...)"> in source SVG; reexpandPropagateGroup re-expands in place
Hover over plain cue Tooltip shows data-oscilla or id DSL
Click a plain cue Form shows parsed cue params; save writes back via _fetchSetAttr

_parseAllCues(dsl) and _serializeDSL(cueName, params)

Parsing

_parseAllCues splits compound DSL strings with splitCueId (from parser/cueUtils.js) then calls parseCueToAST on each expression. Returns an array of { cueName, params: Map }.

The propagate cue type is handled specially: the entire inner content is stored as a single template key rather than parsed into individual params.

Serialisation

_serializeDSL rebuilds the DSL string from the edited params map. For propagate, it simply reconstructs propagate(${params.get('template')}). For other cues it iterates the params Map and assembles key:value pairs.


suppressScoreReload(ms)

Called at the start of every save operation. Prevents the server's file-watch event from triggering a full score reload for ms milliseconds. This gives the live DOM update time to take effect before the server reloads would overwrite it.

For propagate saves, the reload suppression is less critical because reexpandPropagateGroup handles the live update fully, but it is still called to be safe.

Tip: use ← → or ↑ ↓ to navigate the docs