Camunda / Zeebe
Camunda files always round-tripped verbatim; now their extension elements are editable. OGE_BPMN_CAMUNDA_PROVIDERS — the opt-in camundaProviders preset — adds Camunda 8 (Zeebe) task definitions, input / output mappings and task headers and Camunda 7 user-task assignment and input / output parameters to the properties panel. Event definitions keep their payloads (timer expressions, message / signal / error / escalation references with the definitions-level root elements, conditions, link names), and every element has a documentation field.
Camunda / Zeebe properties
Select Charge card for the Zeebe job type, retries, input / output mappings and task headers (list fields: add, edit, remove rows — each commit is one undo step), or Approve refund for the Camunda 7 assignee, candidate groups, form key and input parameters. Edits rewrite the editable extensionElements tree, declare xmlns:zeebe / xmlns:camunda when needed and leave unknown vendor elements and complex parameters untouched — export to see the XML.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeBpmnEditor, OGE_BPMN_CAMUNDA_PROVIDERS } from '@oge-ui/bpmn';
@Component({
selector: 'demo-root',
imports: [OgeBpmnEditor],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- the opt-in Camunda preset: Zeebe (Camunda 8) task definition,
io mappings and headers; Camunda 7 assignment and input/output -->
<oge-bpmn-editor
#editor
style="height: 480px"
[propertiesProviders]="providers"
/>
<button type="button" (click)="xml.set(editor.exportXml())">Export XML</button>
<pre>{{ xml() }}</pre>
`,
})
export class Demo {
protected readonly providers = OGE_BPMN_CAMUNDA_PROVIDERS;
protected readonly xml = signal('');
}Typed helpers
The providers sit on typed helpers in @oge-ui/bpmn-engine — bpmnZeebeTaskDefinition, bpmnZeebeIoMapping, bpmnZeebeTaskHeaders, bpmnCamundaInputOutput and their set…Command twins, plus bpmnBindingValue / setBpmnBindingCommand for the element-template bindings. They work on the plain model, so a migration script or a server endpoint uses the same code the panel does.
A headless helper — no canvas needed; see the Code tab.
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { OgeBpmnEditor } from '@oge-ui/bpmn';
import { bpmnZeebeIoMapping, readBpmnXml, setZeebeTaskDefinitionCommand, writeBpmnXml } from '@oge-ui/bpmn-engine';
@Component({
selector: 'demo-root',
imports: [OgeBpmnEditor],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<oge-bpmn-editor #editor style="height: 360px" />
`,
})
export class Demo {
/**
* The same typed helpers the panel uses, server-side: read a mapping, set a
* job type, write the XML back (xmlns:zeebe is declared when needed).
*/
protected retarget(xml: string): string {
const model = readBpmnXml(xml).model;
if (model === null) return xml;
console.log(bpmnZeebeIoMapping(model, 'Task_charge').inputs);
return writeBpmnXml(
setZeebeTaskDefinitionCommand('Task_charge', { type: 'payment-v2' }).apply(model),
);
}
}Event payloads, documentation & re-parenting
Select Every hour for the timer type and expression, or Bank confirmed for its message reference — pick New message to create one in <definitions>. Every element, and the process, has a documentation field. Drag a task into Fulfilment (the target highlights) or choose it in Move to — the keyboard path — to re-parent it in one undo step; flows that would cross scopes are removed. exportPng() rasterizes the SVG export.
import { ChangeDetectionStrategy, Component, viewChild } from '@angular/core';
import { OgeBpmnEditor } from '@oge-ui/bpmn';
@Component({
selector: 'demo-root',
imports: [OgeBpmnEditor],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- select the timer start or the message catch: timer type and
expression, message reference (or "New message") and documentation are
panel fields. Drag a task into "Fulfilment" — or pick it in "Move to" —
to re-parent it; one undo step either way. -->
<oge-bpmn-editor #editor style="height: 480px" />
<button type="button" (click)="downloadPng()">Export PNG</button>
`,
})
export class Demo {
private readonly editor = viewChild.required(OgeBpmnEditor);
protected async downloadPng(): Promise<void> {
// the SVG export rasterized on a canvas — no library
const blob = await this.editor().exportPng({ pixelRatio: 2 });
if (blob === null) return;
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'diagram.png';
link.click();
URL.revokeObjectURL(url);
}
}