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:
- Hover any SVG cue element to see its DSL in a tooltip
- Click to open a form editor for the cue parameters
- Save changes back to
score.svgon the server with live re-application
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)
-
Propagate child redirect — if
data-oscilla-propagate-child="true", skip to the parent and return the parent'sdata-oscilla-propagate-idas the hit id. This means clicking any expanded child opens the parent propagate template, not the child's expanded DSL. -
data-oscillaattribute containing(— preferred source for elements that have been migrated from legacy id-DSL. -
idattribute 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:
data-oscilla(if present and contains()id(fallback)data-oscilla-propagate-id(for propagate parent groups whoseidwas removed after expansion)
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