@bpmnkit/editor 0.0.35 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The editor's DOM-free surface: operations, and the ids they mint.
3
+ *
4
+ * `@bpmnkit/editor` itself reaches for `document` the moment it is imported —
5
+ * it is an editor. But the part that decides *what an edit does* never touches
6
+ * the DOM, and that part has to run in two more places: a Durable Object
7
+ * replaying a writer's op to verify it, and a watcher applying the same op
8
+ * without an editor loaded at all.
9
+ *
10
+ * So this entry exists to be importable from a Worker. Everything reachable
11
+ * from here is pure, and the subpath keeps it that way — importing the package
12
+ * root instead would pull the canvas in behind it.
13
+ */
14
+ export { applyOp } from "./ops.js";
15
+ export type { EditorOp, OpResult, ShapeMove } from "./ops.js";
16
+ export { createIdFactory, genId, newIdSeed } from "./id.js";
17
+ export type { IdFactory } from "./id.js";
18
+ export { createEmptyDefinitions } from "./modeling.js";
19
+ export type { Clipboard } from "./modeling.js";
20
+ export type { CreateShapeType, PortDir } from "./types.js";
21
+ //# sourceMappingURL=headless.d.ts.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The editor's DOM-free surface: operations, and the ids they mint.
3
+ *
4
+ * `@bpmnkit/editor` itself reaches for `document` the moment it is imported —
5
+ * it is an editor. But the part that decides *what an edit does* never touches
6
+ * the DOM, and that part has to run in two more places: a Durable Object
7
+ * replaying a writer's op to verify it, and a watcher applying the same op
8
+ * without an editor loaded at all.
9
+ *
10
+ * So this entry exists to be importable from a Worker. Everything reachable
11
+ * from here is pure, and the subpath keeps it that way — importing the package
12
+ * root instead would pull the canvas in behind it.
13
+ */
14
+ export { applyOp } from "./ops.js";
15
+ export { createIdFactory, genId, newIdSeed } from "./id.js";
16
+ export { createEmptyDefinitions } from "./modeling.js";
17
+ //# sourceMappingURL=headless.js.map
package/dist/id.d.ts CHANGED
@@ -1,2 +1,27 @@
1
+ /**
2
+ * How new element ids are minted.
3
+ *
4
+ * Normally this is `Math.random()`, which is exactly right for one person
5
+ * editing one diagram. It is wrong the moment the same edit has to happen twice
6
+ * — replayed on a server, or on someone else's screen — because two runs would
7
+ * invent two different ids for the same new task.
8
+ *
9
+ * So the minting functions in `modeling.ts` take an optional {@link IdFactory}.
10
+ * An operation carries a seed, both sides build the same factory from it, and
11
+ * the same edit produces byte-identical XML wherever it runs.
12
+ */
13
+ /** Mints an id for a new element, given the prefix its type wants. */
14
+ export type IdFactory = (prefix: string) => string;
15
+ /** The default: unique enough for one editor, different on every call. */
1
16
  export declare function genId(prefix: string): string;
17
+ /**
18
+ * A factory that yields the same sequence of ids for the same seed.
19
+ *
20
+ * Deterministic *in call order*, which is the contract an operation replay
21
+ * relies on: the same function, given the same arguments, asks for the same
22
+ * prefixes in the same order, so it gets the same ids back.
23
+ */
24
+ export declare function createIdFactory(seed: string): IdFactory;
25
+ /** A seed with enough entropy that two operations cannot mint the same ids. */
26
+ export declare function newIdSeed(): string;
2
27
  //# sourceMappingURL=id.d.ts.map
package/dist/id.js CHANGED
@@ -1,4 +1,43 @@
1
+ /**
2
+ * How new element ids are minted.
3
+ *
4
+ * Normally this is `Math.random()`, which is exactly right for one person
5
+ * editing one diagram. It is wrong the moment the same edit has to happen twice
6
+ * — replayed on a server, or on someone else's screen — because two runs would
7
+ * invent two different ids for the same new task.
8
+ *
9
+ * So the minting functions in `modeling.ts` take an optional {@link IdFactory}.
10
+ * An operation carries a seed, both sides build the same factory from it, and
11
+ * the same edit produces byte-identical XML wherever it runs.
12
+ */
13
+ /** The default: unique enough for one editor, different on every call. */
1
14
  export function genId(prefix) {
2
15
  return `${prefix}_${Math.random().toString(36).slice(2, 9)}`;
3
16
  }
17
+ /**
18
+ * A factory that yields the same sequence of ids for the same seed.
19
+ *
20
+ * Deterministic *in call order*, which is the contract an operation replay
21
+ * relies on: the same function, given the same arguments, asks for the same
22
+ * prefixes in the same order, so it gets the same ids back.
23
+ */
24
+ export function createIdFactory(seed) {
25
+ // FNV-1a over the seed, then xorshift32 per id. Small, dependency-free, and
26
+ // the output is shaped like `genId`'s so nothing downstream can tell them apart.
27
+ let state = 2166136261;
28
+ for (let i = 0; i < seed.length; i++) {
29
+ state ^= seed.charCodeAt(i);
30
+ state = Math.imul(state, 16777619);
31
+ }
32
+ return (prefix) => {
33
+ state ^= state << 13;
34
+ state ^= state >>> 17;
35
+ state ^= state << 5;
36
+ return `${prefix}_${(state >>> 0).toString(36).padStart(7, "0").slice(0, 7)}`;
37
+ };
38
+ }
39
+ /** A seed with enough entropy that two operations cannot mint the same ids. */
40
+ export function newIdSeed() {
41
+ return Math.random().toString(36).slice(2, 12);
42
+ }
4
43
  //# sourceMappingURL=id.js.map
package/dist/index.d.ts CHANGED
@@ -1,5 +1,11 @@
1
1
  export { BpmnEditor } from "./editor.js";
2
+ export { CHROME_CSS, CHROME_STYLE_ID, injectChromeStyles } from "./chrome.js";
3
+ export { injectStyle } from "./inject.js";
2
4
  export { createEmptyDefinitions } from "./modeling.js";
5
+ export { applyOp } from "./ops.js";
6
+ export type { EditorOp, OpResult, ShapeMove } from "./ops.js";
7
+ export { createIdFactory, genId, newIdSeed } from "./id.js";
8
+ export type { IdFactory } from "./id.js";
3
9
  export type { EditorEvents, EditorOptions, LabelPosition, Tool, CreateShapeType, HandleDir, PortDir, } from "./types.js";
4
10
  export { ELEMENT_GROUPS, ELEMENT_TYPE_LABELS, EXTERNAL_LABEL_TYPES, CONTEXTUAL_ADD_TYPES, getElementGroup, getValidLabelPositions, } from "./element-groups.js";
5
11
  export type { ElementGroup } from "./element-groups.js";
package/dist/index.js CHANGED
@@ -1,5 +1,9 @@
1
1
  export { BpmnEditor } from "./editor.js";
2
+ export { CHROME_CSS, CHROME_STYLE_ID, injectChromeStyles } from "./chrome.js";
3
+ export { injectStyle } from "./inject.js";
2
4
  export { createEmptyDefinitions } from "./modeling.js";
5
+ export { applyOp } from "./ops.js";
6
+ export { createIdFactory, genId, newIdSeed } from "./id.js";
3
7
  export { ELEMENT_GROUPS, ELEMENT_TYPE_LABELS, EXTERNAL_LABEL_TYPES, CONTEXTUAL_ADD_TYPES, getElementGroup, getValidLabelPositions, } from "./element-groups.js";
4
8
  export { initEditorHud } from "./hud.js";
5
9
  export { createTranslationRecorder, defaultTranslate, interpolate } from "./i18n.js";
@@ -0,0 +1,3 @@
1
+ /** Injects a CSS string into `<head>` exactly once, keyed by id. */
2
+ export declare function injectStyle(id: string, css: string): void;
3
+ //# sourceMappingURL=inject.d.ts.map
package/dist/inject.js ADDED
@@ -0,0 +1,12 @@
1
+ /** Injects a CSS string into `<head>` exactly once, keyed by id. */
2
+ export function injectStyle(id, css) {
3
+ if (typeof document === "undefined")
4
+ return;
5
+ if (document.getElementById(id))
6
+ return;
7
+ const style = document.createElement("style");
8
+ style.id = id;
9
+ style.textContent = css;
10
+ document.head.appendChild(style);
11
+ }
12
+ //# sourceMappingURL=inject.js.map
package/dist/modal.js CHANGED
@@ -1,5 +1,7 @@
1
+ import { injectChromeStyles } from "./chrome.js";
1
2
  const MODAL_STYLE_ID = "bpmnkit-hud-modal-styles";
2
3
  function injectModalStyles() {
4
+ injectChromeStyles();
3
5
  if (document.getElementById(MODAL_STYLE_ID))
4
6
  return;
5
7
  const style = document.createElement("style");
@@ -7,76 +9,54 @@ function injectModalStyles() {
7
9
  style.textContent = `
8
10
  .bpmnkit-hud-modal-overlay {
9
11
  position: fixed; inset: 0; z-index: 300;
10
- background: rgba(0,0,0,0.5);
12
+ background: color-mix(in srgb, var(--bpmnkit-ds-dark, #14161a) 55%, transparent);
11
13
  display: flex; align-items: center; justify-content: center;
12
14
  }
13
15
  .bpmnkit-hud-modal {
14
- background: rgba(22, 22, 30, 0.97);
15
- border: 1px solid rgba(255,255,255,0.12);
16
- border-radius: 10px;
17
- padding: 20px;
18
- min-width: 300px; max-width: 90vw;
19
- color: rgba(255,255,255,0.85);
20
- font-family: system-ui, -apple-system, sans-serif;
21
- box-shadow: 0 16px 48px rgba(0,0,0,0.6);
22
- display: flex; flex-direction: column; gap: 12px;
16
+ background: var(--bpmnkit-chrome-ground, #ffffff);
17
+ border: 1px solid var(--bpmnkit-chrome-line, rgba(255, 255, 255, 0.14));
18
+ padding: 24px 26px;
19
+ min-width: 320px; max-width: 90vw;
20
+ color: var(--bpmnkit-chrome-ink-2, #c8ccd2);
21
+ font-family: var(--bpmnkit-ds-font-sans, system-ui, -apple-system, sans-serif);
22
+ display: flex; flex-direction: column; gap: 14px;
23
23
  }
24
24
  .bpmnkit-hud-modal-title {
25
- font-size: 14px; font-weight: 600;
26
- color: rgba(255,255,255,0.95);
25
+ font-family: var(--bpmnkit-ds-font-mono, ui-monospace, monospace);
26
+ font-size: var(--bpmnkit-ds-t-mono-micro, 10.5px); letter-spacing: 0.12em;
27
+ text-transform: uppercase; color: var(--bpmnkit-chrome-ink-4, #9aa1aa);
27
28
  }
28
29
  .bpmnkit-hud-modal-input {
29
- width: 100%; padding: 6px 10px;
30
- background: rgba(255,255,255,0.08);
31
- border: 1px solid rgba(255,255,255,0.15);
32
- border-radius: 6px;
33
- color: rgba(255,255,255,0.9);
34
- font-size: 13px;
30
+ width: 100%; padding: 7px 9px;
31
+ background: transparent;
32
+ border: 1px solid var(--bpmnkit-chrome-line, rgba(255, 255, 255, 0.14));
33
+ color: var(--bpmnkit-chrome-ink, #f4f5f7);
34
+ font-family: var(--bpmnkit-ds-font-mono, ui-monospace, monospace);
35
+ font-size: 12.5px;
35
36
  outline: none;
36
37
  box-sizing: border-box;
37
38
  }
38
- .bpmnkit-hud-modal-input:focus { border-color: rgba(60,120,220,0.6); }
39
- .bpmnkit-hud-modal-actions { display: flex; gap: 8px; justify-content: flex-end; }
39
+ .bpmnkit-hud-modal-input:focus { border-color: var(--bpmnkit-chrome-accent, #c9755c); }
40
+ .bpmnkit-hud-modal-actions { display: flex; gap: 10px; justify-content: flex-end; }
40
41
  .bpmnkit-hud-modal-btn {
41
- font-size: 13px; padding: 6px 14px; border-radius: 6px;
42
- cursor: pointer; font-weight: 500;
43
- border: 1px solid rgba(255,255,255,0.12);
44
- background: rgba(255,255,255,0.06);
45
- color: rgba(255,255,255,0.8);
42
+ font-family: var(--bpmnkit-ds-font-mono, ui-monospace, monospace);
43
+ font-size: 12px; padding: 7px 16px;
44
+ cursor: pointer;
45
+ border: 1px solid var(--bpmnkit-chrome-line, rgba(255, 255, 255, 0.14));
46
+ background: transparent;
47
+ color: var(--bpmnkit-chrome-ink-2, #c8ccd2);
46
48
  }
47
- .bpmnkit-hud-modal-btn:hover { background: rgba(255,255,255,0.1); }
49
+ .bpmnkit-hud-modal-btn:hover { background: var(--bpmnkit-chrome-hover, rgba(255,255,255,0.07)); color: var(--bpmnkit-chrome-ink, #f4f5f7); }
48
50
  .bpmnkit-hud-modal-btn--primary {
49
- background: rgba(60,120,220,0.5);
50
- border-color: rgba(60,120,220,0.7);
51
+ background: var(--bpmnkit-ds-accent, #a8503a);
52
+ border-color: var(--bpmnkit-ds-accent, #a8503a);
51
53
  color: #fff;
52
54
  }
53
- .bpmnkit-hud-modal-btn--primary:hover { background: rgba(60,120,220,0.65); }
54
- /* Light theme */
55
- [data-bpmnkit-hud-theme="light"] .bpmnkit-hud-modal {
56
- background: rgba(252,252,254,0.98);
57
- border-color: rgba(0,0,0,0.1);
58
- color: rgba(0,0,0,0.8);
59
- box-shadow: 0 8px 32px rgba(0,0,0,0.15);
60
- }
61
- [data-bpmnkit-hud-theme="light"] .bpmnkit-hud-modal-title { color: rgba(0,0,0,0.88); }
62
- [data-bpmnkit-hud-theme="light"] .bpmnkit-hud-modal-input {
63
- background: rgba(0,0,0,0.04);
64
- border-color: rgba(0,0,0,0.15);
65
- color: rgba(0,0,0,0.9);
66
- }
67
- [data-bpmnkit-hud-theme="light"] .bpmnkit-hud-modal-input:focus { border-color: rgba(0,80,200,0.4); }
68
- [data-bpmnkit-hud-theme="light"] .bpmnkit-hud-modal-btn {
69
- border-color: rgba(0,0,0,0.12);
70
- background: rgba(0,0,0,0.04);
71
- color: rgba(0,0,0,0.7);
72
- }
73
- [data-bpmnkit-hud-theme="light"] .bpmnkit-hud-modal-btn:hover { background: rgba(0,0,0,0.08); }
74
- [data-bpmnkit-hud-theme="light"] .bpmnkit-hud-modal-btn--primary {
75
- background: rgba(0,80,200,0.85);
76
- border-color: rgba(0,80,200,0.9);
55
+ .bpmnkit-hud-modal-btn--primary:hover {
56
+ background: var(--bpmnkit-ds-accent-hover, #8f412e);
57
+ border-color: var(--bpmnkit-ds-accent-hover, #8f412e);
77
58
  color: #fff;
78
59
  }
79
- [data-bpmnkit-hud-theme="light"] .bpmnkit-hud-modal-btn--primary:hover { background: rgba(0,80,200,1); }
80
60
  `;
81
61
  document.head.appendChild(style);
82
62
  }
@@ -1,16 +1,17 @@
1
1
  import type { BpmnBounds, BpmnDefinitions, BpmnDiEdge, BpmnDiShape, BpmnFlowElement, BpmnSequenceFlow, BpmnWaypoint, DiColor } from "@bpmnkit/core";
2
+ import { type IdFactory } from "./id.js";
2
3
  import type { CreateShapeType, PortDir } from "./types.js";
3
4
  /** Creates a minimal valid BpmnDefinitions with one process and one diagram. */
4
5
  export declare function createEmptyDefinitions(): BpmnDefinitions;
5
- export declare function createShape(defs: BpmnDefinitions, type: CreateShapeType, bounds: BpmnBounds, name?: string): {
6
+ export declare function createShape(defs: BpmnDefinitions, type: CreateShapeType, bounds: BpmnBounds, name?: string, ids?: IdFactory): {
6
7
  defs: BpmnDefinitions;
7
8
  id: string;
8
9
  };
9
- export declare function createBoundaryEvent(defs: BpmnDefinitions, hostId: string, eventDefType: string | null, bounds: BpmnBounds, cancelActivity?: boolean): {
10
+ export declare function createBoundaryEvent(defs: BpmnDefinitions, hostId: string, eventDefType: string | null, bounds: BpmnBounds, cancelActivity?: boolean, ids?: IdFactory): {
10
11
  defs: BpmnDefinitions;
11
12
  id: string;
12
13
  };
13
- export declare function createConnection(defs: BpmnDefinitions, sourceId: string, targetId: string, waypoints: BpmnWaypoint[]): {
14
+ export declare function createConnection(defs: BpmnDefinitions, sourceId: string, targetId: string, waypoints: BpmnWaypoint[], ids?: IdFactory): {
14
15
  defs: BpmnDefinitions;
15
16
  id: string;
16
17
  };
@@ -74,16 +75,16 @@ export interface Clipboard {
74
75
  edges: BpmnDiEdge[];
75
76
  }
76
77
  export declare function copyElements(defs: BpmnDefinitions, ids: string[]): Clipboard;
77
- export declare function pasteElements(defs: BpmnDefinitions, clipboard: Clipboard, offsetX: number, offsetY: number): {
78
+ export declare function pasteElements(defs: BpmnDefinitions, clipboard: Clipboard, offsetX: number, offsetY: number, ids?: IdFactory): {
78
79
  defs: BpmnDefinitions;
79
80
  newIds: Map<string, string>;
80
81
  topLevelIds: string[];
81
82
  };
82
- export declare function createAnnotation(defs: BpmnDefinitions, bounds: BpmnBounds, text?: string): {
83
+ export declare function createAnnotation(defs: BpmnDefinitions, bounds: BpmnBounds, text?: string, ids?: IdFactory): {
83
84
  defs: BpmnDefinitions;
84
85
  id: string;
85
86
  };
86
- export declare function createAnnotationWithLink(defs: BpmnDefinitions, bounds: BpmnBounds, sourceId: string, sourceBounds: BpmnBounds, text?: string): {
87
+ export declare function createAnnotationWithLink(defs: BpmnDefinitions, bounds: BpmnBounds, sourceId: string, sourceBounds: BpmnBounds, text?: string, ids?: IdFactory): {
87
88
  defs: BpmnDefinitions;
88
89
  annotationId: string;
89
90
  associationId: string;
package/dist/modeling.js CHANGED
@@ -338,9 +338,9 @@ function updateRefInElements(flowElements, id, updateFn) {
338
338
  });
339
339
  }
340
340
  // ── Create shape ─────────────────────────────────────────────────────────────
341
- export function createShape(defs, type, bounds, name) {
342
- const id = genId(type);
343
- const shapeId = genId(`${type}_di`);
341
+ export function createShape(defs, type, bounds, name, ids = genId) {
342
+ const id = ids(type);
343
+ const shapeId = ids(`${type}_di`);
344
344
  const flowElement = makeFlowElement(type, id, name);
345
345
  const process = defs.processes[0];
346
346
  if (!process)
@@ -382,9 +382,9 @@ export function createShape(defs, type, bounds, name) {
382
382
  return { defs: newDefs, id };
383
383
  }
384
384
  // ── Create boundary event ─────────────────────────────────────────────────────
385
- export function createBoundaryEvent(defs, hostId, eventDefType, bounds, cancelActivity = true) {
386
- const id = genId("BoundaryEvent");
387
- const shapeId = genId("BoundaryEvent_di");
385
+ export function createBoundaryEvent(defs, hostId, eventDefType, bounds, cancelActivity = true, ids = genId) {
386
+ const id = ids("BoundaryEvent");
387
+ const shapeId = ids("BoundaryEvent_di");
388
388
  const process = defs.processes[0];
389
389
  if (!process)
390
390
  return { defs, id };
@@ -434,9 +434,9 @@ export function createBoundaryEvent(defs, hostId, eventDefType, bounds, cancelAc
434
434
  };
435
435
  }
436
436
  // ── Create connection ─────────────────────────────────────────────────────────
437
- export function createConnection(defs, sourceId, targetId, waypoints) {
438
- const id = genId("Flow");
439
- const edgeId = genId("Flow_di");
437
+ export function createConnection(defs, sourceId, targetId, waypoints, ids = genId) {
438
+ const id = ids("Flow");
439
+ const edgeId = ids("Flow_di");
440
440
  const process = defs.processes[0];
441
441
  if (!process)
442
442
  return { defs, id };
@@ -1373,19 +1373,19 @@ export function copyElements(defs, ids) {
1373
1373
  return { elements, flows, shapes, edges };
1374
1374
  }
1375
1375
  /** Assigns a fresh id to `el` and every descendant / nested edge, recording the mapping. */
1376
- function buildIdMap(el, map) {
1377
- map.set(el.id, genId(el.type));
1376
+ function buildIdMap(el, map, ids) {
1377
+ map.set(el.id, ids(el.type));
1378
1378
  if (hasChildren(el)) {
1379
1379
  for (const child of el.flowElements)
1380
- buildIdMap(child, map);
1380
+ buildIdMap(child, map, ids);
1381
1381
  for (const sf of el.sequenceFlows)
1382
- map.set(sf.id, genId("Flow"));
1382
+ map.set(sf.id, ids("Flow"));
1383
1383
  for (const ta of el.textAnnotations)
1384
- map.set(ta.id, genId("TextAnnotation"));
1384
+ map.set(ta.id, ids("TextAnnotation"));
1385
1385
  for (const a of el.associations)
1386
- map.set(a.id, genId("Association"));
1386
+ map.set(a.id, ids("Association"));
1387
1387
  for (const g of el.groups)
1388
- map.set(g.id, genId("Group"));
1388
+ map.set(g.id, ids("Group"));
1389
1389
  }
1390
1390
  }
1391
1391
  /** Deep-clones `el`, remapping its own id, refs, and all nested children through `map`. */
@@ -1424,13 +1424,13 @@ function remapFlow(sf, map) {
1424
1424
  targetRef: map.get(sf.targetRef) ?? sf.targetRef,
1425
1425
  };
1426
1426
  }
1427
- export function pasteElements(defs, clipboard, offsetX, offsetY) {
1427
+ export function pasteElements(defs, clipboard, offsetX, offsetY, ids = genId) {
1428
1428
  const newIds = new Map();
1429
1429
  // Generate new IDs for every element (recursively) and top-level flow.
1430
1430
  for (const el of clipboard.elements)
1431
- buildIdMap(el, newIds);
1431
+ buildIdMap(el, newIds, ids);
1432
1432
  for (const sf of clipboard.flows)
1433
- newIds.set(sf.id, genId("Flow"));
1433
+ newIds.set(sf.id, ids("Flow"));
1434
1434
  const process = defs.processes[0];
1435
1435
  const diagram = defs.diagrams[0];
1436
1436
  if (!process || !diagram)
@@ -1442,7 +1442,7 @@ export function pasteElements(defs, clipboard, offsetX, offsetY) {
1442
1442
  const newElId = newIds.get(s.bpmnElement) ?? s.bpmnElement;
1443
1443
  return {
1444
1444
  ...s,
1445
- id: genId(`${newElId}_di`),
1445
+ id: ids(`${newElId}_di`),
1446
1446
  bpmnElement: newElId,
1447
1447
  bounds: {
1448
1448
  ...s.bounds,
@@ -1456,7 +1456,7 @@ export function pasteElements(defs, clipboard, offsetX, offsetY) {
1456
1456
  const newFlowId = newIds.get(e.bpmnElement) ?? e.bpmnElement;
1457
1457
  return {
1458
1458
  ...e,
1459
- id: genId(`${newFlowId}_di`),
1459
+ id: ids(`${newFlowId}_di`),
1460
1460
  bpmnElement: newFlowId,
1461
1461
  waypoints: e.waypoints.map((wp) => ({
1462
1462
  x: wp.x + offsetX,
@@ -1492,9 +1492,9 @@ export function pasteElements(defs, clipboard, offsetX, offsetY) {
1492
1492
  return { defs: newDefs, newIds, topLevelIds };
1493
1493
  }
1494
1494
  // ── Create text annotation ────────────────────────────────────────────────────
1495
- export function createAnnotation(defs, bounds, text) {
1496
- const id = genId("TextAnnotation");
1497
- const shapeId = genId("TextAnnotation_di");
1495
+ export function createAnnotation(defs, bounds, text, ids = genId) {
1496
+ const id = ids("TextAnnotation");
1497
+ const shapeId = ids("TextAnnotation_di");
1498
1498
  const annotation = { id, text, unknownAttributes: {} };
1499
1499
  const diShape = { id: shapeId, bpmnElement: id, bounds, unknownAttributes: {} };
1500
1500
  const process = defs.processes[0];
@@ -1521,11 +1521,11 @@ export function createAnnotation(defs, bounds, text) {
1521
1521
  id,
1522
1522
  };
1523
1523
  }
1524
- export function createAnnotationWithLink(defs, bounds, sourceId, sourceBounds, text) {
1525
- const annotResult = createAnnotation(defs, bounds, text);
1524
+ export function createAnnotationWithLink(defs, bounds, sourceId, sourceBounds, text, ids = genId) {
1525
+ const annotResult = createAnnotation(defs, bounds, text, ids);
1526
1526
  const annotationId = annotResult.id;
1527
- const assocId = genId("Association");
1528
- const edgeId = genId("Association_di");
1527
+ const assocId = ids("Association");
1528
+ const edgeId = ids("Association_di");
1529
1529
  const assoc = {
1530
1530
  id: assocId,
1531
1531
  sourceRef: sourceId,
package/dist/ops.d.ts ADDED
@@ -0,0 +1,158 @@
1
+ /**
2
+ * The editor's operations: what a change *was*, rather than what it produced.
3
+ *
4
+ * `diagram:change` hands out the whole new document, which is all a single
5
+ * editor needs. Sending that over a wire on every keystroke is not: a
6
+ * `moveShapes` op is a few hundred bytes where a serialised `BpmnDefinitions`
7
+ * is hundreds of kilobytes. So every edit also describes itself, and
8
+ * {@link applyOp} turns the description back into the edit.
9
+ *
10
+ * **`applyOp` is the only way the editor performs these edits**, locally as well
11
+ * as on replay. That is deliberate: if the editor composed the modeling calls
12
+ * itself and `applyOp` composed them again, the two could drift, and the whole
13
+ * point is that the same op yields byte-identical XML wherever it runs. Ids are
14
+ * the other half of that promise — creating ops carry a seed rather than
15
+ * inventing ids at random, so a replay mints the same ones (see `id.ts`).
16
+ */
17
+ import type { BpmnBounds, BpmnDefinitions, BpmnWaypoint, DiColor } from "@bpmnkit/core";
18
+ import { type Clipboard } from "./modeling.js";
19
+ import type { CreateShapeType, PortDir } from "./types.js";
20
+ /** One shape's displacement. */
21
+ export interface ShapeMove {
22
+ id: string;
23
+ dx: number;
24
+ dy: number;
25
+ }
26
+ /**
27
+ * A single editor edit.
28
+ *
29
+ * `seed` appears on every op that creates something: it is what makes the new
30
+ * element's id a property of the op rather than of the machine that ran it.
31
+ */
32
+ export type EditorOp = {
33
+ kind: "createShape";
34
+ type: CreateShapeType;
35
+ bounds: BpmnBounds;
36
+ name?: string;
37
+ onEdge?: string;
38
+ seed: string;
39
+ } | {
40
+ kind: "createBoundaryEvent";
41
+ hostId: string;
42
+ eventDefType: string | null;
43
+ bounds: BpmnBounds;
44
+ seed: string;
45
+ } | {
46
+ kind: "createAnnotation";
47
+ bounds: BpmnBounds;
48
+ text?: string;
49
+ seed: string;
50
+ } | {
51
+ kind: "createAnnotationFor";
52
+ sourceId: string;
53
+ bounds: BpmnBounds;
54
+ sourceBounds: BpmnBounds;
55
+ seed: string;
56
+ } | {
57
+ kind: "createConnection";
58
+ sourceId: string;
59
+ targetId: string;
60
+ waypoints: BpmnWaypoint[];
61
+ seed: string;
62
+ }
63
+ /**
64
+ * A shape and the flow reaching it, as one edit — the "add element" affordance.
65
+ *
66
+ * Placement and routing are decided by the editor, from shapes only it can see,
67
+ * and travel in the op: a replay that recomputed them would need the same
68
+ * obstacles on screen to land in the same place.
69
+ */
70
+ | {
71
+ kind: "createConnected";
72
+ sourceId: string;
73
+ type: CreateShapeType;
74
+ name?: string;
75
+ bounds: BpmnBounds;
76
+ waypoints: BpmnWaypoint[];
77
+ seed: string;
78
+ } | {
79
+ kind: "paste";
80
+ clipboard: Clipboard;
81
+ offsetX: number;
82
+ offsetY: number;
83
+ seed: string;
84
+ } | {
85
+ kind: "move";
86
+ moves: ShapeMove[];
87
+ onEdge?: {
88
+ edgeId: string;
89
+ shapeId: string;
90
+ };
91
+ } | {
92
+ kind: "resize";
93
+ id: string;
94
+ bounds: BpmnBounds;
95
+ } | {
96
+ kind: "delete";
97
+ ids: string[];
98
+ } | {
99
+ kind: "rename";
100
+ id: string;
101
+ name: string;
102
+ } | {
103
+ kind: "labelPosition";
104
+ id: string;
105
+ bounds: BpmnBounds;
106
+ } | {
107
+ kind: "color";
108
+ id: string;
109
+ color: DiColor;
110
+ } | {
111
+ kind: "changeType";
112
+ id: string;
113
+ type: CreateShapeType;
114
+ } | {
115
+ kind: "reconnect";
116
+ edgeId: string;
117
+ isStart: boolean;
118
+ port: PortDir;
119
+ } | {
120
+ kind: "insertWaypoint";
121
+ edgeId: string;
122
+ segIdx: number;
123
+ point: BpmnWaypoint;
124
+ } | {
125
+ kind: "moveWaypoint";
126
+ edgeId: string;
127
+ wpIdx: number;
128
+ point: BpmnWaypoint;
129
+ } | {
130
+ kind: "moveSegment";
131
+ edgeId: string;
132
+ segIdx: number;
133
+ isHoriz: boolean;
134
+ delta: number;
135
+ } | {
136
+ kind: "autoLayout";
137
+ }
138
+ /**
139
+ * The escape hatch: a whole document, for edits that have no description.
140
+ *
141
+ * `applyChange` takes an arbitrary function — the properties panel uses it —
142
+ * and a function cannot be replayed. Under a single writer a whole-document
143
+ * op is still correct, just larger on the wire, so nothing is unreplayable;
144
+ * it simply costs more.
145
+ */
146
+ | {
147
+ kind: "snapshot";
148
+ defs: BpmnDefinitions;
149
+ };
150
+ /** The result of an op: the new document, and anything it brought into being. */
151
+ export interface OpResult {
152
+ defs: BpmnDefinitions;
153
+ /** Ids created by this op, in the order they were made. Empty for edits. */
154
+ created: string[];
155
+ }
156
+ /** Performs an op. The same op, on the same document, anywhere. */
157
+ export declare function applyOp(defs: BpmnDefinitions, op: EditorOp): OpResult;
158
+ //# sourceMappingURL=ops.d.ts.map