9. Layer 6 — Behavior
Contents of DISL 0.1
Behavior declares what happens in response to user actions, beyond the direct manipulation that the other layers already define: derived updates, cascades, generated names, multi-step operations.
{
"behavior": {
"hooks": [
{ "id": "nameNewState", "on": "create", "for": "State", "when": "!has(self.name)",
"actions": [ { "set": { "name": "'State ' + string(diagram.nodesOfType('State').size())" } } ],
"doc": "New states are numbered automatically." }
],
"operations": {
"makeFinal": {
"label": "Convert to final state", "for": "State", "icon": "flag",
"enabled": "self.outgoing.size() == 0",
"actions": [ { "retype": { "to": "'FinalState'" } } ]
}
},
"deletion": { "State": { "relations": "delete" } },
"clipboard": { "relations": "internal", "ids": "regenerate", "offset": [20, 20] },
"undo": { "mergeWindowMs": 500 }
}
}
| Property | Type | Description |
|---|---|---|
id |
identifier | Required. |
on |
Event or Event[] | create, delete, change, connect, disconnect, reconnect, reparent, move, resize, paste, duplicate, retype, load, save, viewpointOpen. |
for |
TypeRef or TypeRef[] | Element types; absent for diagram-level events (load, save). |
attribute |
path or path[] | For change: only when these attributes change. |
when |
Expression | Condition (context hook, 12.3). |
actions |
Action[] | Executed in order. |
phase |
"before", "after" |
before hooks run inside the gesture transaction before constraints and may abort; after hooks (default) run after the change and before live constraints, in the same undoable transaction. |
order |
int | Ordering among hooks for the same event. |
doc |
Doc |
Hook context variables: self (the element after the change), old (a snapshot before the change, or null for create), event ({kind, attribute, oldValue, newValue, source} where source is "user", "hook", "operation", "layout", "import", "remote").
Termination. Actions of a hook MAY trigger further hooks. Runtimes MUST detect cycles: the same hook MUST NOT run twice for the same element within one transaction, and the total number of hook executions per transaction is bounded by behavior.maxHookDepth (default 100); exceeding it aborts the transaction.
Remote changes. Hooks do not run for changes received from collaborators (event.source == "remote"); those changes were already processed on the originating client.
Operations are named, documented commands, invoked from tools, context tools, form buttons, quick fixes, keyboard shortcuts or menus.
| Property | Type | Description |
|---|---|---|
label, doc, icon, shortcut |
||
for |
TypeRef[], "selection", "diagram" |
What the operation applies to. For type lists, self is the target element; for selection, selection is the list. |
params |
map → Attribute | Parameters; if present, the runtime shows a generated dialog (or paramsForm). Available as p in actions. |
paramsForm |
form id | Custom dialog. |
enabled |
Expression | Whether available. |
confirm |
LocalizedText | Confirmation question before running. |
actions |
Action[] | Body, executed as one transaction. |
plugin |
{name, args} |
Alternatively implemented by a plugin. |
Actions are a small, closed set of declarative steps. All values in actions are Expressions (2.5d).
| Action | Form | Effect |
|---|---|---|
set |
{ "set": { "attr": expr, … }, "target": expr } |
Assign attributes of target (default self). |
unset |
{ "unset": ["attr"], "target": expr } |
Remove stored values. |
create |
{ "create": { "type": expr, "attributes": {…}, "parent": expr, "slot": expr, "at": expr, "size": expr }, "as": "name" } |
Create a node; the new element is available as name in subsequent actions. |
connect |
{ "connect": { "type": expr, "source": expr, "target": expr, "attributes": {…} }, "as": "name" } |
Create a relation. |
delete |
{ "delete": expr } |
Delete an element or list. |
move |
{ "move": { "target": expr, "x": expr, "y": expr, "parent": expr } } |
Change placement (domain values, snapped if applyToProgrammatic). |
resize |
{ "resize": { "target": expr, "width": expr, "height": expr } } |
|
retype |
{ "retype": { "to": expr, "target": expr } } |
Change the type, keeping compatible attributes and relations. |
reparent |
{ "reparent": { "target": expr, "parent": expr, "slot": expr } } |
|
let |
{ "let": { "name": expr } } |
Bind a variable for later actions. |
if |
{ "if": expr, "then": [ … ], "else": [ … ] } |
Conditional. |
forEach |
{ "forEach": expr, "as": "item", "do": [ … ] } |
Iterate a list (bounded: lists are finite). |
select |
{ "select": expr } |
Change the selection. |
reveal |
{ "reveal": expr } |
Scroll the canvas to elements. |
highlight |
{ "highlight": expr, "durationMs": 2000 } |
Apply the highlighted state. |
editLabel |
{ "editLabel": { "target": expr, "label": "id" } } |
Start inline editing. |
openForm |
{ "openForm": { "target": expr, "form": "id" } } |
|
notify |
{ "notify": { "message": expr, "severity": "info" } } |
Toast message. |
layout |
{ "layout": { "scope": expr, "algorithm": "id" } } |
Run a layout (section 10). |
abort |
{ "abort": { "message": expr } } |
Cancel the transaction (only in before hooks and operations). |
call |
{ "call": "operationId", "args": { … } } |
Run another operation. |
plugin |
{ "plugin": "name", "args": { … } } |
Delegate to a plugin action. |
Every action MAY carry when (skip unless true) and doc.
Deletion (behavior.deletion, per type):
| Property | Values | Description |
|---|---|---|
children |
"delete" (default), "reparent", "forbid" |
What happens to contained elements. reparent moves them to the deleted node's parent. |
relations |
"delete" (default), "forbid", "reconnect" |
Attached edges. reconnect bridges incoming to outgoing edges of the same type (removing a step from a chain). |
references |
"unset" (default), "delete-referencing", "forbid" |
Reference attributes pointing at the element. |
confirm |
LocalizedText | Ask before deleting. |
Clipboard (behavior.clipboard): relations ("internal" — only edges between copied elements, "all", "none"), ids ("regenerate"), names ("keep", "suffix" — adds " (copy)" to the label attribute), offset (canvas offset on paste; on time axes a duration), crossDocument (bool), formats (clipboard MIME types offered: application/vnd.did.fragment+json (DID, section 7), image/svg+xml, text/plain).
Retyping (behavior.retype): map of type → allowed target types, with attributeMapping (target attr → Expression over old).
Undo (behavior.undo): mergeWindowMs (typing in a label merges into one step), maxSteps, persistHistory (bool, stores history in the DID definition for collaborative review, default false).
Illustrated by
- Entity–relationship diagram: 1 items in this layer
- State machine: 9 items in this layer
- Project timeline: 3 items in this layer