7. Layer 4 — Toolbox, context tools and forms
Contents of DISL 0.1
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, 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. |
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". |
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").
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[] | Element types the context tools apply to. |
when |
Expression | Additional condition (element context). |
placement |
"around", "toolbar", "menu", "radial" |
Presentation. |
tools |
ContextTool[] | Items. |
ContextTool kinds: 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. All kinds accept label, doc, icon, shortcut, enabled, position.
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.
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, doc, icon |
Title and help of the form. |
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 |
LocalizedText | Default: attribute label. |
doc |
Doc | Field help. Default: attribute doc. Shown below the field (summary) and via an info icon (description, rationale). |
placeholder |
LocalizedText | Hint text inside empty inputs. |
visible, enabled |
Expression | Context form (12.3): self, value, diagram. |
required |
bool | Visual required marker; may tighten but not loosen the attribute. |
validate |
{rule, message, severity}[] |
Field-local validation shown immediately; does not replace constraints. |
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 | |
onChange |
Action[] | Actions run after the value is committed (9.4). |
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, 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, 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), button (label, icon, operation or actions, enabled, confirm), problems (list of the element's current constraint problems with quick fixes), 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
- Entity–relationship diagram: 6 items in this layer
- State machine: 12 items in this layer
- Project timeline: 6 items in this layer