7. Layer 4 — Toolbox, context tools and forms
Contents of DISL 0.8
This layer defines how users create and edit elements: the palette (toolbox), tools that appear next to a selection, reusable templates, and forms — both property inspectors and widgets embedded directly in nodes.
{
"toolbox": {
"layout": "list",
"searchable": true,
"showRecent": 5,
"groups": [
{ "id": "states", "label": "States", "icon": "square-rounded",
"doc": "Elements that describe where the system can be.",
"tools": [
{ "id": "state", "creates": "State", "shortcut": "S" },
{ "id": "initial", "creates": "InitialState", "shortcut": "I" },
{ "id": "final", "creates": "FinalState" }
] },
{ "id": "connections", "label": "Connections",
"tools": [ { "id": "transition", "creates": "Transition", "mode": "drag", "shortcut": "T" } ] }
]
}
}
| Property | Type | Description |
|---|---|---|
groups |
ToolGroup[] | Ordered groups. |
tools |
map → Tool | Library of tools referenced from groups by id (a group entry may be a string id). |
layout |
"list", "grid", "icons", "compact" |
Presentation. Default "list". |
position |
"left", "right", "top", "floating" |
Suggested placement; runtimes MAY ignore it. |
searchable |
bool | Offer a search box (matches labels, doc.summary, doc.tags). Default true. |
showRecent |
int | Show a group with the n most recently used tools. |
generic |
{select, pan, lasso, text, note, image, freehand} of bool |
Which generic tools are offered. Defaults: select and pan true, others false. |
doc |
Doc |
If toolbox is absent, the runtime generates one group per 10 concrete types, alphabetically, with one creation tool per concrete node and relation type.
ToolGroup: id, label, doc, icon, collapsed (bool), visible (Expression in the diagram context), viewpoints (string[]), tools (Tool or tool id)[], and nested groups for sub-groups.
| Property | Type | Description |
|---|---|---|
id |
identifier | Required. |
kind |
ToolKind | Default: "create-node" or "create-edge" depending on creates; otherwise required. |
label |
Message | Palette text; context element with self bound to the diagram. Default: from the created type. |
doc, icon |
Default: from the created type. doc.summary is the tooltip. |
|
creates |
TypeRef | Type to create. |
initial |
map attribute → value or {cel} |
Initial attribute values, overriding metamodel defaults. CEL values use the create context (12.3). |
size |
Size | Initial size overriding the notation default. |
variantOf |
tool id | Presents this tool as a variant (dropdown) of another. |
mode |
see below | Interaction mode. |
sticky |
bool | Tool stays active after use until Escape. Default false; shift-click MAY toggle. |
shortcut |
string | Keyboard shortcut ("S", "Ctrl+Shift+E"). Conflicts are a validator warning. |
enabled, visible |
Expression | Context diagram. |
unavailable |
Reason[] | Why the tool cannot be used now; context element with self bound to the diagram. A tool of kind operation also inherits its operation's reasons (9.3). |
preview |
{style, shape} |
Ghost appearance while placing. |
after |
"select", "editLabel", "openForm", "none" |
What happens after creation. Default "editLabel" when the type has an editable label. |
template |
template id | For kind: "template". |
operation |
operation id | For kind: "operation". |
plugin |
{name, args} |
For kind: "plugin". |
drop |
DropSpec | What a drop of the tool does, by what lies under it (below). |
createSource, createTarget |
TypeRef or CreateEnd; createTarget also "ask" |
For edge tools: the node created when the gesture starts or ends on empty canvas (below). |
A runtime MUST evaluate a CEL label each time it presents the entry, for the entry's current target. Where an entry is listed without a target (a list of keyboard shortcuts, toolbox search), a runtime MUST use its doc.summary, or else its label evaluated with no target if that evaluates, or else the label derived from its id (2.4); toolbox search MUST match the evaluated label and doc.summary. A tool that is visible but unavailable MUST be shown disabled with the applicable reason, as 9.3 describes for operations; a tool whose visible is false is not shown.
ToolKind: create-node, create-edge, template (inserts a fragment), operation (runs an operation on the selection or diagram), select, pan, lasso, text, note, image, freehand, plugin.
Modes. For node tools: "click" (place at default size), "drag" (drag out the size; default), "stamp" (click repeatedly, implies sticky). For edge tools: "drag" (drag from source to target; default), "click-click" (click source, then target, with intermediate clicks adding bendpoints), "chain" (each click continues from the last target), "auto" (drag between nodes or start from a port). When a node tool is dragged on a coordinate system with bound placement, the dragged extent is written to the bound attributes: dragging across five days of a time axis creates a task with start and end set to those days, dropped into a lane it sets the lane attribute. Edge tools that start on empty canvas may create the source node when createSource: TypeRef is set; ending on empty canvas may create the target when createTarget: TypeRef is set (or offer a context menu of valid targets when createTarget: "ask"). For every tool, "drop" means the user drags the palette entry and releases it (below).
Creating an end with the relation. createSource and createTarget also accept a CreateEnd ($defs/CreateEnd), { "type": TypeRef, "initial": map attribute → value or {cel}, "size": Size }. initial is evaluated in the create context (12.3), where position is the release point in domain values (a timestamp on a time axis) and parent the container under it, with other (the existing end) and relationType also bound. Creating the missing end, the relation, their hooks and their snapping MUST be one transaction and one undo step, and the created node MUST be placed at the release point through its placement, snapped as a drop (5.9).
{ "id": "relate", "creates": "Relation", "mode": "drag",
"createTarget": { "type": "Element", "initial": {
"label": "New element",
"begin": { "cel": "position.x.startOf('day')" },
"end": { "cel": "position.x.startOf('day') + duration('336h')" } } } }
A timeline relation dragged to empty canvas creates a two-week element starting on the day under the pointer and relates it, in one undo step.
Drops. With mode: "drop", the user drags the palette entry and releases it: the release point is the drop point, and the topmost element under it is the drop target (null on empty canvas). For the click mode, the click point and the element under it play the same roles. DropSpec ($defs/DropSpec) is:
| Property | Type | Default | Description |
|---|---|---|---|
targets |
DropTarget[] | [] |
Tried in order; the first whose on matches runs. |
elsewhere |
"create", "ignore", "refuse", "menu" |
"create" |
What happens when no target matches: create, the tool's own creation at the drop point; ignore, nothing and no message; refuse, the refusal is shown; menu, the context menu of the empty canvas (7.3) opens at the drop point. |
refusal |
Reason | the runtime's sentence | For refuse; context create plus dropTarget. |
place |
placement id | – | (DISL 0.3) A parent placement of behavior.placements (9.1) that chooses the parent and the index of the created element by geometry; targets and elsewhere are then not consulted (below). |
A DropTarget ($defs/DropTarget) is a context tool (7.3) of any kind plus on, a list of TypeRefs and the keyword "canvas"; its self is the drop target (the diagram for "canvas"), and position is the drop point.
- A runtime MUST resolve a drop by trying
targetsin order against the drop target's type (onmatches subtypes, 2.7) and MUST run the first match as one transaction; with no match it MUST applyelsewhere. - A matching target that is unavailable (9.3) MUST refuse the drop with its reason and MUST NOT fall through to
elsewhere. - While the entry is dragged, a runtime at level Standard or above SHOULD apply the
dropTargetstate to an element a target accepts and thedropRejectstate otherwise, and SHOULD show the refusal before the release. - A
create-nodetool withoutdropbehaves as in 0.1: it creates at the point, inside a container under it when containment allows. Anoperationtool withoutdropruns on the drop target when the operation'sforadmits it, else on the diagram. "canvas"is a reserved word inon; a type namedcanvascannot be listed there.
Drops placed by geometry (DISL 0.3). A drop with place creates the tool's element where the named parent placement (9.1) puts it, which need not be at or inside the drop target: the drop point is bound as position in the placement.
- A drop with
placeMUST NOT consulttargetsorelsewhere; a validator SHOULD warn when they are given beside it. - While the entry is dragged, a runtime at level Standard or above SHOULD apply the
dropTargetstate to the parent the placement would choose, and SHOULD show the placement's refusal before the release when it would choose none. - The created element is placed among the parent's children at the placement's index, and its position on the canvas follows from its placement and the layout (section 10).
{ "id": "check", "creates": "Check", "mode": "drop", "label": "Check", "icon": "mdi-help-circle-outline",
"initial": { "label": "New question" }, "after": "editLabel", "drop": { "place": "underNearestAbove" } }
An agent behavior model's Check, dropped below a node, becomes a child of the nearest node above the drop point that can take another child (9.1), not of the element under the pointer.
The create gesture context (8.4) binds dropTarget and tool, so a create constraint can refuse per target with a sentence that names what lies under the pointer.
{ "id": "link", "label": "Causal link", "icon": "mdi-arrow-right-thin", "mode": "drop", "creates": "CausalLink",
"drop": { "targets": [ { "on": ["Variable"], "kind": "connect", "via": "CausalLink" } ],
"elsewhere": "refuse", "refusal": "Drop a link or a loop onto a variable." } }
Context tools appear next to the selected element (a "quick bar" or radial menu) and are the fastest way to grow a diagram. They are declared in toolbox.contextTools.
{
"contextTools": [
{
"for": ["State"],
"placement": "around",
"tools": [
{ "kind": "create-connected", "creates": "State", "via": "Transition", "direction": "outgoing",
"label": "Add next state", "icon": "arrow-right", "position": "right" },
{ "kind": "connect", "via": "Transition", "icon": "link", "position": "bottom" },
{ "kind": "operation", "operation": "makeFinal", "icon": "flag", "position": "top-right" },
{ "kind": "delete", "position": "top-right" }
]
}
]
}
| Property | Type | Description |
|---|---|---|
for |
TypeRef[], or the keywords "diagram" and "connection" |
Element types the context tools apply to; "diagram" is the empty canvas and "connection" a pending connection (below). |
when |
Expression | Additional condition (element context; connection context for a pending connection). |
placement |
"around", "toolbar", "menu", "radial" |
Presentation. |
tools |
ContextTool[] | Items. |
runSingle |
bool | When exactly one entry is available, run it without showing the set. Default false. |
ContextTool kinds: pin, unpin and unpinAll (DISL 0.4, below), create-connected (create a node of type creates connected via relation via, placed at position using the layout's spacing; direction outgoing or incoming), connect (start an edge of type via from this element), create-child (create a child of creates in slot), operation, delete, duplicate, editLabel, openForm, plugin, moveUp and moveDown (move the element one place earlier or later among its siblings with the reorder action, 9.4). All kinds accept label, doc, icon, shortcut, enabled, position, visible, unavailable and group:
| Property | Type | Description |
|---|---|---|
label |
Message | Entry text, evaluated for the element the entry is on; context element. |
enabled |
Expression | Whether the entry can run; context element. False shows the entry disabled. |
visible |
Expression | Whether the entry is offered at all; context element. False hides the entry instead of disabling it. Default true. |
unavailable |
Reason[] | Why the entry cannot run now; context element. An entry of kind operation also inherits the operation's enabled and unavailable (9.3). |
group |
SimpleId | (DISL 0.3) The menu group the entry is shown in (below). Default: none. |
An entry whose visible is false, or whose operation's for does not admit the target, MUST NOT be shown. A visible entry that is unavailable MUST be shown disabled with the applicable reason and MUST NOT run (9.3). Actions that do not apply are either not offered or shown disabled with their reason, never silently absent where the specification declares a reason:
{ "kind": "operation", "operation": "addSibling", "label": "Add sibling", "visible": "self.parent != null" },
{ "kind": "operation", "operation": "editNotes", "label": { "cel": "self.notes != '' ? 'Edit notes…' : 'Add notes…'" } }
Right-click context menus are defined the same way under toolbox.contextMenus with placement: "menu"; runtimes add standard entries (cut, copy, paste, delete, arrange) unless standardEntries: false.
Menu groups (DISL 0.3). An entry's group names the group it is shown in, so that related entries stand together and apart from the rest, as a hype cycle trigger's Rename and Remove stand apart from Arrange diagram.
- Consecutive entries of one set's
toolswith an equalgroupMUST form one group, and consecutive entries withoutgroupone group too. A group never spans two sets, and a name that comes back after another group has started starts a new group. - Groups are decided by the entries as declared, before
visibleandforEachare applied. A group none of whose entries is shown MUST NOT be shown, and the groups on either side of it stay two groups. - A runtime MUST show the groups in order and SHOULD set them apart from one another, for example with a divider. The name only tells groups apart; it is not shown.
- A set whose entries carry no
groupis one group, which is how a 0.2 menu reads.
{ "for": [ "Trigger" ], "placement": "menu", "standardEntries": false, "tools": [
{ "kind": "editLabel", "group": "edit", "label": "Rename…", "shortcut": "F2" },
{ "kind": "delete", "group": "edit", "label": "Remove", "shortcut": "Delete" },
{ "kind": "operation", "operation": "arrange", "group": "arrange", "label": "Arrange diagram" } ] }
A hype cycle trigger's menu shows Rename… and Remove, then, set apart, Arrange diagram.
Pin and unpin (DISL 0.4). Three standard entries change which elements an automatic layout leaves where they are (10, respect: "pinned"); each is one edit when pinned is stored view data (11.6), and viewer state otherwise:
pin: pins the element at its current position. Offered for a node that is not pinned; default label "Lock position".unpin: removes the element's pin and its stored position, after which the layout places it. Offered for a pinned node; default label "Unlock position".unpinAll: in a set for"diagram"only; unpins every pinned element of the view, as one edit. Unavailable while nothing is pinned, with the reason the runtime gives; default label "Unlock all positions".
A runtime MUST offer pin and unpin only where the specification lists them, and MUST refuse them for an element whose id is ephemeral (11.5.3). The view action sets pinned for the same effect (9.4).
Move up and move down. moveUp at the first position among the siblings and moveDown at the last MUST be unavailable, with the reason behavior.messages gives for std.atStart or std.atEnd (9.1), or else the runtime's sentence. They are refused like any reorder by reorder gesture constraints (8.4).
Choosing the other end from a list. A connect or create-connected entry chooses the other end by target:
| Property | Type | Default | Description |
|---|---|---|---|
target |
"drag", "pick", "either" |
"drag" |
drag: the user drags to the other end (0.1); pick: a picker lists the candidates; either: both are offered. |
candidates |
Expression → list(Element) | every element the relation's ends and gesture constraints admit | The elements the picker offers; context element. |
candidateLabel |
Message | item.label() |
The text of each row; context element plus item. |
pickerTitle |
Message | the entry's label | The picker's title. |
emptyText |
Message | the runtime's sentence | Shown when there are no candidates; the entry is then unavailable with it as its reason. |
With pick, a runtime MUST list only the candidates for which the connect gesture checks (8.4) would pass, in the order the expression returns them, and MUST make the entry unavailable with emptyText when none remain. Choosing a candidate MUST run the same transaction a drag to that element would.
{ "kind": "connect", "via": "Dependency", "label": "Link to…", "target": "pick",
"candidates": "diagram.nodesOfType('Positioned').filter(n, n != self)",
"candidateLabel": { "cel": "item.name" }, "emptyText": "There is nothing else on this map to link to." }
One entry per item. Any entry MAY carry forEach (Expression → list), as (the name the item is bound to, default item), args (map parameter → Expression, the invoked operation's parameters, usable without forEach too) and submenu (a Message).
- An entry with
forEachMUST produce one entry per list item, in list order, with the item (under itsasname) andindexbound in itslabel,visible,enabled,unavailable,argsandicon; an empty list produces no entry. - When
argsgives every required parameter of the operation, the runtime MUST NOT open the parameter dialog. - The entries MAY be grouped under one submenu labelled
submenu, and a runtime MAY collapse more generated entries than it can show into the submenu or a picker.
{ "kind": "operation", "operation": "disconnect", "forEach": "self.incomingOf('Dependency')", "as": "dep",
"label": { "cel": "'Disconnect from \\'' + dep.source.key + '\\''" }, "args": { "dependency": "dep" } }
A Databricks task offers one "Disconnect from" entry per task it depends on.
The empty canvas. A set whose for includes "diagram" is the context menu of the canvas background. A right-click (or the platform's context gesture) on empty canvas MUST open the sets whose for includes "diagram" and whose when holds, with self bound to the diagram, plus the standard entries unless standardEntries: false. Its entries see position, the clicked point in domain values, snapped as a drop would be (5.9); the operation context binds position too, null when an operation is not invoked at a point, so the operation can ask for the value instead (initial, 7.5).
{ "for": ["diagram"], "placement": "menu", "when": "self.ontologyDeclared", "tools": [
{ "kind": "operation", "operation": "addClass", "label": "Add class here…", "icon": "mdi:shape-circle-plus" } ] }
Pending connections. A set whose for includes "connection" is offered for a pending connection: a connect gesture that ends on an element when no relation type's connect (6.10) claims the anchor it started from. Its when and entries are evaluated in the connection context (12.3), which binds source and target (after the anchor assignment of 6.10), sourceAnchor, position, diagram and env; entries are typically operations that take source and target through args.
- A pending connection MUST NOT create any model element until an entry runs; cancelling the menu MUST leave the model unchanged and record nothing.
- The entry that runs MUST see
sourceandtargetas they were when the gesture ended. - With
runSingle: true, when exactly one entry is available it MUST run without the menu being shown.
A drop whose elsewhere is "menu" (7.2) opens the empty-canvas menu at the drop point, the other transient target. "diagram" and "connection" are reserved words in for: a type with one of these names cannot be listed there.
{ "for": ["connection"], "placement": "menu", "when": "source.isA('Resource') && target.isA('Resource')",
"tools": [ { "kind": "operation", "operation": "relate", "label": "Relate…", "args": { "from": "source", "to": "target" } } ] }
Templates insert pre-built fragments — patterns, starter structures, snippets. They are declared in toolbox.templates:
{
"templates": {
"retryLoop": {
"label": "Retry loop",
"doc": "A state that retries up to three times before failing.",
"params": { "attempts": { "type": "int", "default": 3, "label": "Attempts" } },
"fragment": {
"nodes": [
{ "ref": "try", "type": "State", "attributes": { "name": "'Trying'" }, "at": [0, 0] },
{ "ref": "fail", "type": "FinalState", "attributes": { "name": "'Failed'" }, "at": [200, 0] }
],
"relations": [
{ "type": "Transition", "source": "try", "target": "try", "attributes": { "guard": "'retries < ' + string(p.attempts)" } },
{ "type": "Transition", "source": "try", "target": "fail", "attributes": { "guard": "'retries >= ' + string(p.attempts)" } }
]
},
"preview": "templates/retry.svg"
}
}
}
fragment uses local ref names instead of ids; attribute values are Expressions (strings are CEL) evaluated with template parameters as p. at positions are relative to the insertion point, in the coordinate system of the target container (time values are durations relative to the drop time on time axes). Templates are placed with a kind: "template" tool or dragged from a template gallery. Templates MAY also be offered on the creation of a new diagram (language-level starter templates: toolbox.starters: ["retryLoop", …]).
A form is a declarative description of editable fields for one element type. Forms are used as property inspectors (side panel or popover), as creation dialogs, and — embedded — inside nodes on the canvas.
If no form is defined for a type, runtimes generate one: one field per non-derived attribute, grouped by the attribute group, ordered by order then declaration order, with widgets chosen by type (Appendix B.7).
{
"forms": {
"taskInspector": {
"for": "Task",
"usage": ["inspector", "create"],
"layout": { "columns": 2 },
"items": [
{ "kind": "section", "title": "General", "items": [
{ "attribute": "title", "colSpan": 2, "widget": "text" },
{ "attribute": "assignee", "widget": "reference",
"options": { "cel": "diagram.nodesOfType('Resource')" } },
{ "attribute": "priority", "widget": "segmented" }
] },
{ "kind": "section", "title": "Schedule", "items": [
{ "attribute": "start", "widget": "date" },
{ "attribute": "end", "widget": "date",
"validate": [ { "rule": "value >= self.start", "message": "End must not be before start." } ] },
{ "kind": "computed", "label": "Duration",
"value": { "cel": "string(workingDays(self.start, self.end, 'project')) + ' working days'" } }
] },
{ "kind": "section", "title": "Notes", "collapsed": true, "items": [
{ "attribute": "notes", "widget": "markdown", "rows": 6 }
] },
{ "kind": "button", "label": "Split task", "operation": "splitTask", "icon": "scissors" }
]
}
}
}
Form
| Property | Type | Description |
|---|---|---|
for |
TypeRef or TypeRef[] | Types edited. For relations, the relation type. "diagram" edits the diagram root. |
usage |
("inspector", "create", "embedded", "popover", "bulk")[] |
Where the form is used. create shows it as a dialog before creation; bulk supports editing several selected elements (fields show "mixed" values). Default ["inspector"]. |
layout |
"vertical", "horizontal", {columns, labelPosition, density} |
labelPosition: "top", "left", "inline", "none"; density: "comfortable", "compact". |
items |
FormItem[] | Fields and containers. |
commit |
"immediate", "onBlur", "explicit" |
When edits become model changes (each commit is one undoable transaction). Default "immediate" for inspectors, "explicit" for create dialogs. |
label |
Message | Title of the form or dialog; context form with self bound to the target. |
doc, icon |
Help and icon of the form. | |
submitLabel |
Message | Label of the confirming button of a dialog (create, popover, and an operation's paramsForm). Default: the runtime's "OK". |
cancelLabel |
Message | Label of the cancelling button of a dialog. Default: the runtime's "Cancel". |
danger |
bool | Styles the confirming button as destructive. Default false. |
Field (an item with attribute, or kind: "field"):
| Property | Type | Description |
|---|---|---|
attribute |
path | Attribute edited (dotted path into structs). Required for fields. |
widget |
Widget | Default by type (Appendix B.7). |
label |
Message | Default: attribute label. Context form. |
doc |
Doc | Field help. Default: attribute doc. Shown below the field (summary) and via an info icon (description, rationale). |
placeholder |
Message | Hint text inside empty inputs. Context form. |
initial |
Expression | The value the field is pre-filled with when a dialog opens, evaluated once in the form context with self bound to the operation's target (the element, or the diagram) and position to the point the operation was invoked at, or null; for a create form, in the create context. |
visible, enabled |
Expression | Context form (12.3): self, value, diagram. |
required |
bool | Visual required marker; may tighten but not loosen the attribute. |
validate |
FieldValidation[] | Field-local validation; does not replace constraints (below). |
options |
{cel} or {enum} or literal list |
Choices for select-like widgets: values or {value, label, icon, doc} maps, or elements for reference. |
widgetOptions |
object | Widget-specific settings (below). |
colSpan, width |
int, length | Grid layout. |
prefix, suffix, unit |
LocalizedText | Adornments. |
format |
string | Display format for numbers and dates. |
rows |
int | For multi-line widgets. |
readOnly |
Bindable bool | |
readOnlyReasons |
Reason[] | Why the field cannot be edited, in priority order; context form. Shown before the attribute's own reasons (4.3). |
absentText, emptyText |
Message | Override the attribute's texts for an absent or empty value (4.3). Context form. |
showAbsent |
bool | Whether the field is shown while its value is absent. Default true. |
onChange |
Action[] | Actions run after the value is committed (9.4). |
display |
Message | (DISL 0.3) The text the field shows for its value, in place of the widget's own rendering; context form (below). |
parse |
FieldParse | (DISL 0.3) Makes the item editable as text, on an attribute or a computed item (below). |
id |
SimpleId | (DISL 0.3) The item's row id, on a field, a computed item or a type item (below). Default: none. |
FieldValidation ($defs/FieldValidation): rule (Expression → bool, context form), message (Message, context form), severity (default "error"), and timing: "input" (default, as in 0.1: checked and shown as the user types) or "commit" (checked when the value is committed or the dialog is submitted, for input that is only judged complete). A validation message may call a declared function or a plugin's CEL function, so a format's own term grammar can word the refusal.
Fields that show and accept their own text (DISL 0.3). A field normally shows its attribute's value as its widget renders it, and accepts what the widget produces. Some values are read and written as a phrase: the number of phases a trend shows as "Peak and Trough", a note's size as 160 x 64, an influence's end as plateau/bottom/0.3.
- An item with
displayMUST show its value asdisplayevaluates, in theformcontext withvaluebound to the attribute's value; the widget still edits the value itself. - An item with
parseis editable as text, even when it iscomputed. FieldParse ($defs/FieldParse) is{ "accepts": Expression → bool, "write": Action[], "refusal": Message }. On commit, the runtime MUST bindvalueto the entered text, MUST refuse withrefusalunlessacceptsis true, and otherwise MUST runwriteas one transaction. Read-only reasons (4.3) andvalidateapply beforeaccepts.
{ "kind": "computed", "label": "From attachment",
"value": { "cel": "endSpec(self.?fromPhase, self.?fromEdge, self.?fromAt)" },
"parse": {
"accepts": "value.trim().split('/').size() == 3 && value.trim().split('/')[0].trim() in ['peak', 'trough', 'slope', 'plateau'] && value.trim().split('/')[1].trim() in ['top', 'bottom']",
"write": [ { "set": { "fromPhase": "value.trim().split('/')[0].trim()", "fromEdge": "value.trim().split('/')[1].trim()",
"fromAt": "double(value.trim().split('/')[2].trim())" } } ],
"refusal": { "cel": "'\\'' + value + '\\' is not an attachment; write it as phase/edge/at, such as plateau/bottom/0.3.'" } } }
On a hype cycle influence, plateau/bottom/0.3 sets fromPhase, fromEdge and fromAt in one undo step, and plateau/side/0.3 is refused with "'plateau/side/0.3' is not an attachment; write it as phase/edge/at, such as plateau/bottom/0.3."
Retyping in a form (DISL 0.3). An item of kind type shows the element's type and changes it through behavior.retype (9.5). It has label, options (in the form FormItem options already takes, a {cel} value or a literal list, giving the type names on offer), optionLabel (Expression → string, with item bound to a type name) and refusals (Reason[], whose expressions see self and newValue, the chosen type).
- A
typeitem MUST show the element's type, labelled byoptionLabelwithitembound to the type. Its choices MUST be the typesoptionsgives, in that order, each labelled the same way; withoutoptions, they are the element's own type followed by the types inbehavior.retype[self.type].towhosewhenholds (9.5). The choices are not limited further bybehavior.retype. - The element's own type MUST be among the choices, so that the list holds the value it shows:
options, when given, MUST yield it. Anoptionsthat filters the types by the element's state, ascanBecome(t, self)below does, keeps it, because an element can always stay what it is. - Choosing a different type MUST retype the element as
behavior.retypedescribes, applyingattributeMapping, as one transaction and one undo step. - Choosing the element's own type MUST retype it to that same type: the
attributeMappingof the entry for its type is applied, as one transaction and one undo step, and nothing else of the element changes. Where the mapping changes no value, the model is unchanged, though a format binding MAY rewrite the element in its canonical form. - A value not among the choices (typed, pasted or sent by another client) MUST be refused with the first of
refusalswhosewhenholds, or with the runtime's sentence when none does. A chosen type thatbehavior.retypedoes not allow for the element (9.5) is refused the same way.
{ "kind": "type", "label": "Kind",
"options": { "cel": "kinds().filter(t, canBecome(t, self))" },
"optionLabel": "kindChoice(item)",
"refusals": [
{ "when": "familyOf(newValue) == 'leaf' && self.children.size() > 0",
"message": { "cel": "'\"' + kindChoice(newValue) + '\" holds no children, and this node has ' + (self.children.size() == 1 ? '1 child' : string(self.children.size()) + ' children') + '.'" } } ] }
An agent behavior model's Kind row lists the node's own kind and the kinds it can become without losing a child: a leaf only when the node has no children. Choosing another kind rewrites the node's keyword as one undoable step; a kind typed past the list is refused with the sentence that says why. Choosing the node's own kind applies the mapping only: a Retry with 2 attempts chosen as Retry again gets 3, as a node changed into a Retry does, and a Do chosen as Do is unchanged, except that an item written without a keyword is given its kind's keyword.
Row ids (DISL 0.3). A field, a computed item and a type item each show one row, and a host addresses that row by its row id, for example to map it to a property id of its own. The row id is the item's id when it has one, else its attribute, else its label as evaluated.
idMUST NOT change what the row shows, edits or refuses; it only names the row.- Items of one form whose row ids would otherwise be equal, such as two
computeditems labelled alike in two sections, SHOULD each be given anid, and the row ids of one form SHOULD be unique.
{ "kind": "section", "title": "Peak", "items": [
{ "kind": "computed", "id": "peakInfluences", "label": "Influence", "widget": "textarea",
"value": { "cel": "listOrNone(self.outgoingOf('Influence').filter(r, r.?fromPhase.orValue('') == 'peak').map(r, endText(r.target, r.?toPhase.orValue(''))))" } } ] }
A hype cycle trend's form lists the influences of each of its four phases under the label Influence; each list has its own row id, peakInfluences for the peak's, so a host can tell the four apart.
Dialogs. A form used as a dialog (usage create or popover, or an operation's paramsForm) MUST show placeholder in an empty input, MUST pre-fill initial, and MUST label its confirming button with submitLabel. It MUST NOT submit while a validation of severity error fails, and MUST show that validation's message beside the field. When the invoking entry supplies every parameter of the operation, the dialog MUST NOT open.
"renameDialog": {
"for": [ "Resource" ], "usage": [ "popover" ],
"label": "Rename resource", "icon": "mdi:pencil-outline", "submitLabel": "Rename",
"items": [ {
"attribute": "newName", "placeholder": "New IRI or prefixed name",
"initial": "rdfCompress(self.iri)",
"validate": [
{ "rule": "rdfResolveTerm(value) != ''", "message": { "cel": "rdfTermRefusal(value)" }, "timing": "commit" },
{ "rule": "!diagram.nodesOfType('Resource').exists(r, r.iri == rdfResolveTerm(value) && r != self)",
"message": { "cel": "rdfResolveTerm(value) + ' already names something in this document; renaming onto it would silently merge two resources.'" } }
] } ]
}
Widgets: text, textarea, markdown, code (with widgetOptions.language: "cel", "json", "sql", …; the cel language is type-checked in the attribute's context), number, slider (min, max, step, marks), spinner, rating (max, icon), checkbox, switch, select, combobox (free entry allowed), radio, segmented (button group), multiselect, tags, date, datetime, time, duration, month (a yearMonth value), daterange (edits two attributes: attribute and widgetOptions.endAttribute), color (palette, alpha), icon (icon picker), reference (element picker, with widgetOptions.pickOnCanvas: true to select by clicking), references (for many references), list (editable list of primitives), table (list of structs, with widgetOptions.columns), struct (nested sub-form), file (binary or URI, mediaTypes), image, link, progress (read-only bar), readonly (plain text), plugin.
Containers and other items (by kind): section (title as a Message, collapsible, collapsed, items), row (horizontal group), tabs (tabs: [{title, icon, items}]), group (a bordered box with title), text (static help text, Markdown), divider, computed (read-only value Bindable; id, DISL 0.3), button (label as a Message, icon, operation or actions, enabled, unavailable as Reason[], confirm as a Message or Confirmation (9.5); a button that runs an operation inherits its reasons, 9.3), findings (list of the element's current findings with quick fixes; problems is its deprecated alias, section 18), type (the element's type, changed through behavior.retype; DISL 0.3, above), plugin.
Nodes MAY display live form widgets on the canvas — checkboxes in a to-do card, a slider on a parameter block, a dropdown on a gateway, a table inside an entity — via NodeNotation.form:
{
"form": {
"form": "taskCard",
"region": { "x": 8, "y": 30, "w": "w - 16", "h": "h - 38" },
"interaction": "always",
"minZoom": 0.6,
"fallback": "labels",
"autoSize": true
}
}
| Property | Type | Description |
|---|---|---|
form |
form id or inline Form | The form (its usage SHOULD include "embedded"). |
region |
Box of GeomExpr | Area inside the node, in shape coordinates. Default: the shape's text area. |
interaction |
"always", "onSelect", "onDoubleClick", "readOnly" |
When the widgets accept input. readOnly renders values only. Default "onSelect". |
minZoom |
number | Below this zoom, the form is not rendered; fallback decides what is shown instead ("labels", "none", "summary"). |
autoSize |
bool | The node grows to fit the form. |
style |
StyleRef | Styling of embedded widgets (font, density). Runtimes SHOULD render embedded widgets in the node's style, not the platform style. |
Embedded forms MUST NOT capture canvas gestures outside their widgets: dragging on a non-interactive area moves the node. Every widget change is an undoable model transaction, exactly as in the inspector.
Illustrated by
- C4 container diagram: 9 items in this layer
- Causal loop diagram: 4 items in this layer
- Databricks job: 8 items in this layer
- Entity–relationship diagram: 6 items in this layer
- Functional decomposition graph: 4 items in this layer
- Gartner hype cycle graph: 6 items in this layer
- Mind map: 4 items in this layer
- RDF graph: 17 items in this layer
- Service calls: 1 items in this layer
- State machine: 12 items in this layer
- Project timeline: 6 items in this layer
- Work board: 3 items in this layer
Referenced from
- 2. Foundations
- 3. Specification structure, layers and viewpoints
- 4. Layer 1 — Metamodel
- 5. Layer 2 — Coordinate systems, placement and snapping
- 6. Layer 3 — Notation (visual definition)
- 9. Layer 6 — Behavior
- 10. Layer 7 — Layout
- 11. Layer 8 — Persistence
- 12. The CEL environment
- 17. Complete examples
- 18. Deprecated aliases
- Appendix B — Built-in catalogues