@weasel-js/diagram 1.15.0 → 1.15.2

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.
Files changed (35) hide show
  1. package/README.md +114 -5
  2. package/dist/{chunk-GR2DK4S2.js → chunk-6YQSQXQT.js} +1485 -266
  3. package/dist/chunk-6YQSQXQT.js.map +1 -0
  4. package/dist/{chunk-FDCODMFW.js → chunk-7BV2MBED.js} +906 -606
  5. package/dist/chunk-7BV2MBED.js.map +1 -0
  6. package/dist/{chunk-IYUU5KUY.js → chunk-FQVZWS24.js} +11 -101
  7. package/dist/chunk-FQVZWS24.js.map +1 -0
  8. package/dist/{chunk-AXL4JQBM.js → chunk-LPTNAYER.js} +3 -3
  9. package/dist/{chunk-AXL4JQBM.js.map → chunk-LPTNAYER.js.map} +1 -1
  10. package/dist/{chunk-DE7UILJA.js → chunk-QGL6QJE4.js} +183 -23
  11. package/dist/chunk-QGL6QJE4.js.map +1 -0
  12. package/dist/{dataTypes-BIkhAOc4.d.ts → dataTypes-1ch6cMtq.d.ts} +12 -3
  13. package/dist/effect-audio.d.ts +2 -2
  14. package/dist/effect-audio.js +7 -11
  15. package/dist/effect-audio.js.map +1 -1
  16. package/dist/effect.d.ts +5 -5
  17. package/dist/effect.js +13 -20
  18. package/dist/effect.js.map +1 -1
  19. package/dist/icons.table.js +44 -0
  20. package/dist/icons.table.js.map +1 -1
  21. package/dist/icons.table.json +44 -0
  22. package/dist/index.d.ts +932 -37
  23. package/dist/index.js +707 -34
  24. package/dist/index.js.map +1 -1
  25. package/dist/{labelTrait-BBjRgxbz.d.ts → labelTrait-moIciyXr.d.ts} +4 -0
  26. package/dist/layout.d.ts +3 -3
  27. package/dist/{layout.entry-CbmGuqE-.d.ts → layout.entry-zstuljAl.d.ts} +2 -2
  28. package/dist/layout.js +2 -2
  29. package/dist/{stageTypes-UiO25hcn.d.ts → stageTypes-B7oF8Lww.d.ts} +1 -1
  30. package/dist/{types-B0n2CNTW.d.ts → types-D5ybp8Xh.d.ts} +32 -1
  31. package/package.json +12 -11
  32. package/dist/chunk-DE7UILJA.js.map +0 -1
  33. package/dist/chunk-FDCODMFW.js.map +0 -1
  34. package/dist/chunk-GR2DK4S2.js.map +0 -1
  35. package/dist/chunk-IYUU5KUY.js.map +0 -1
@@ -1,11 +1,11 @@
1
- import { createAvoidRouter, placeLabels, edgeHops, radial, DEFAULT_HOP_SIZE, HOPS_DOC_KEY, simplifyStops } from './chunk-IYUU5KUY.js';
2
- import { edgeRouter, registerMarkShape, withDiagramRegistry, diagramLabelOf, editEdgeLabelBinding, editEdgeLabelAction, diagramGroupOf, diagramContainerOf, headerBox, labelFollowsLayout, buildGraph, LABEL_DRAG_AFFORDANCE_KIND, EDIT_EDGE_LABEL_ACTION_ID, labelDragLayer, laneOutlineOf, recordedRows, keyedIndex, boxIndex, boxesMeeting, routerOf, labelScratchOf, ROUTERS, avoid, LABEL_PLACEMENT, editableLabels, titleIdOf, orthogonal, diagramAnchorOf, MIN_LANE_SIZE, EDGE_DERIVE_PATH, titleInFrame, regionSpecs, DIAGRAM_THEME_KEY, diagramThemeOf, endAims, resolveEnd, themeLabelFamily, bodyBox, boxCenter, roleEdgeStyle, roleStyle, themeLabelFont, firstLanes, DEFAULT_LANE_SIZE } from './chunk-DE7UILJA.js';
3
- import { diagramEdgeOf, isPromise, diagramNodeOf, portsOf, portSitesOf, outlinePath, outlineDetail, samePoints, layoutWritesRoute, plannerPlan, planOf, layered, dataKeyReader, layoutBody, forceRelaxation, center, layoutOwnsPort, chain, force, tree, portOf, labelCenter, labelSpotAt, isPinned, layoutOwnsRoute } from './chunk-FDCODMFW.js';
4
- import { bodyPredicate, dragPanContribution, WeaselProvider, useScene, useSelection, useLatest, SceneCanvas, useDepSource, RIGID_POSE_COMPOSITION, registerNodeShape, reconcileSpecs, pagesBounds, pagesLayer, AUTO_POSE_DESCRIPTOR, usePoseRun, drawnBoundsScene, pointAlongPath, scenePoseAdapter, drawnAABBOf, composeWorldPose, worldPosesOf, RECT_POSE_DESCRIPTOR, resolveFill, resolveStroke, commandFill, commandStroke, nodeGates, scenePoseFrame, fitViewToBounds, clampView, rebaseLocalPose, translatePoseViaDescriptor, carriedDescendants, sceneCarrySource, composeAffordanceLayer, createResizeAction, createDeleteAction, effectivePose, polylineFromPoints, SNAP_ROLES, ActionDisabledReason, refusalOf, snapMods, documentDepOf, definesFrame, paintsLayer, viewToTransform, ellipsePath, derivedDepOf, localizeWorldPoses, PathBuilder, startCarry, createInsertOp, createSetDataOp, documentPose } from '@weasel-js/core';
1
+ import { createAvoidRouter, placeLabels, edgeHops, DEFAULT_HOP_SIZE, HOPS_DOC_KEY, simplifyStops } from './chunk-FQVZWS24.js';
2
+ import { edgeRouter, registerMarkShape, withDiagramRegistry, diagramLabelOf, routesOf, layoutPoses, routeWrites, editEdgeLabelBinding, editEdgeLabelAction, diagramGroupOf, diagramContainerOf, headerBox, buildGraph, LAYOUTS, LABEL_DRAG_AFFORDANCE_KIND, EDIT_EDGE_LABEL_ACTION_ID, labelDragLayer, laneOutlineOf, recordedRows, keyedIndex, boxIndex, boxesMeeting, routerOf, labelScratchOf, ROUTERS, avoid, LABEL_PLACEMENT, editableLabels, titleIdOf, orthogonal, diagramAnchorOf, MIN_LANE_SIZE, EDGE_DERIVE_PATH, titleInFrame, regionSpecs, DIAGRAM_THEME_KEY, diagramThemeOf, endAims, resolveEnd, themeLabelFamily, diagramThemeFrom, themeFont, bodyBox, boxCenter, roleEdgeStyle, DEFAULT_FONT, withColors, roleStyle, GLYPH, themeLabelFont, firstLanes, DEFAULT_LANE_SIZE } from './chunk-QGL6QJE4.js';
3
+ import { diagramEdgeOf, isPromise, diagramNodeOf, portsOf, portSitesOf, outlinePath, outlineDetail, outlineSolid, samePoints, layoutWritesRoute, plannerPlan, planOf, layered, dataKeyReader, layoutBody, forceRelaxation, center, portOf, labelCenter, labelSpotAt, contentBox, boxForContent, grid, chain, tree, isPinned, layoutOwnsRoute } from './chunk-7BV2MBED.js';
4
+ import { bodyPredicate, dragPanContribution, WeaselProvider, useScene, useSelection, useLatest, SceneCanvas, useDepSource, viewRectToScreen, RIGID_POSE_COMPOSITION, registerNodeShape, reconcileSpecs, pagesBounds, pagesLayer, usePoseRun, drawnBoundsScene, pointAlongPath, scenePoseAdapter, drawnAABBOf, composeWorldPose, worldPosesOf, RECT_POSE_DESCRIPTOR, resolveFill, resolveStroke, commandFill, commandStroke, nodeGates, scenePoseFrame, fitViewToBounds, clampView, viewportWorldRect, AUTO_POSE_DESCRIPTOR, composeAffordanceLayer, createResizeAction, createDeleteAction, isTextData, effectivePose, polylineFromPoints, SNAP_ROLES, ActionDisabledReason, commitDeleteToScene, refusalOf, snapMods, documentDepOf, translatePoseViaDescriptor, definesFrame, paintsLayer, viewToTransform, ellipsePath, carriedDescendants, sceneCarrySource, derivedDepOf, localizeWorldPoses, PathBuilder, startCarry, createInsertOp, createSetDataOp, documentPose } from '@weasel-js/core';
5
5
  import { createSettledCache } from '@weasel-js/geom/nd';
6
6
  import { AUTO_POSE_DESCRIPTOR as AUTO_POSE_DESCRIPTOR$1, rotatePoint } from '@weasel-js/core/math';
7
- import { clampRectWithin, placeRect, pathContainsPoint } from '@weasel-js/geom';
8
- import { nudgeGap, moveItems, nearestGap, isNoopDrop } from '@weasel-js/headless';
7
+ import { unionRects, clampRectWithin, placeRect, lerpPoint, rectContainsPoint, pathContainsPoint, rectContainsPointHalfOpen } from '@weasel-js/geom';
8
+ import { nudgeGap, moveItems, nearestGap, isNoopDrop } from '@weasel-js/ui-logic';
9
9
  import { forwardRef, useMemo, useEffect, useState, useRef, useCallback, useImperativeHandle, useLayoutEffect } from 'react';
10
10
  import { useLatest as useLatest$1, adoptCssOnce } from '@weasel-js/react';
11
11
  import '@weasel-js/animator';
@@ -109,10 +109,10 @@ function createPortAffordance(participants, opts = {}) {
109
109
  const built = index();
110
110
  const rx = hitRadiusPx / Math.abs(view.scale.x);
111
111
  const ry = hitRadiusPx / Math.abs(view.scale.y);
112
- const near2 = built && boxesMeeting(built.boxes, wx - rx, wy - ry, wx + rx, wy + ry);
113
- if (!built || !near2) return regions(state);
112
+ const near = built && boxesMeeting(built.boxes, wx - rx, wy - ry, wx + rx, wy + ry);
113
+ if (!built || !near) return regions(state);
114
114
  const out = [];
115
- for (const i of near2) {
115
+ for (const i of near) {
116
116
  const entry = built.at[i];
117
117
  if (shown(entry.participant, state) && showsPort(entry.port)) out.push(regionAt(entry));
118
118
  }
@@ -136,8 +136,8 @@ function bodyAt(participants, at, opts = {}) {
136
136
  if (bounds.width <= 0 || bounds.height <= 0) continue;
137
137
  const rotation = geometry.getRotation?.(participant.pose) ?? 0;
138
138
  const local = rotation === 0 ? at : rotatePoint(at.x, at.y, bounds.x + bounds.width / 2, bounds.y + bounds.height / 2, -rotation);
139
- const inside2 = trait.outline === void 0 ? local.x >= bounds.x && local.x <= bounds.x + bounds.width && local.y >= bounds.y && local.y <= bounds.y + bounds.height : pathContainsPoint(outlinePath(trait.outline, bounds), local.x, local.y);
140
- if (inside2) hit = { end: { kind: "body", nodeId: node.id, point: { x: at.x, y: at.y } }, participant };
139
+ const inside = trait.outline === void 0 ? local.x >= bounds.x && local.x <= bounds.x + bounds.width && local.y >= bounds.y && local.y <= bounds.y + bounds.height : pathContainsPoint(outlinePath(trait.outline, bounds), local.x, local.y);
140
+ if (inside) hit = { end: { kind: "body", nodeId: node.id, point: { x: at.x, y: at.y } }, participant };
141
141
  }
142
142
  return hit;
143
143
  }
@@ -352,8 +352,6 @@ function createConnectAction(opts) {
352
352
  enabled: () => true
353
353
  };
354
354
  }
355
-
356
- // src/containers/canDropInto.ts
357
355
  function diagramCanDropInto(scene, canReparent) {
358
356
  const nodeOf = (id) => scene.get(id);
359
357
  const roleOf = (id) => {
@@ -391,8 +389,7 @@ function diagramDropParentAt(scene, point, role) {
391
389
  const world = frame === null || compose === void 0 ? pose : compose(frame, pose);
392
390
  const trait = diagramContainerOf(node);
393
391
  const b = bodyBox(world, trait?.header);
394
- const inside2 = point.x >= b.x && point.x < b.x + b.width && point.y >= b.y && point.y < b.y + b.height;
395
- if (!inside2) continue;
392
+ if (!rectContainsPointHalfOpen(b, point.x, point.y)) continue;
396
393
  if (trait?.role === "container" || trait?.role === "lane") best = id;
397
394
  walk(scene.childrenOf(id), world);
398
395
  }
@@ -452,7 +449,6 @@ function slideOthers(scene, slots, carried) {
452
449
  // src/reorder.ts
453
450
  var REORDER_ACTION_ID = "diagram.reorder";
454
451
  var END_GAP = 24;
455
- var inside = (p, b) => p.x >= b.x && p.x <= b.x + b.width && p.y >= b.y && p.y <= b.y + b.height;
456
452
  function sequenceSlots(axis, items) {
457
453
  const [along2, across, size, depth] = axis === "x" ? ["x", "y", "width", "height"] : ["y", "x", "height", "width"];
458
454
  const lo = (b) => b[along2];
@@ -474,7 +470,7 @@ function sequenceSlots(axis, items) {
474
470
  return { axis, items, gaps };
475
471
  }
476
472
  function pickAt(slots, at) {
477
- const from = slots?.items.findIndex((i) => i.grip !== null && inside(at, i.grip ?? i.bounds)) ?? -1;
473
+ const from = slots?.items.findIndex((i) => i.grip !== null && rectContainsPoint(i.grip ?? i.bounds, at.x, at.y)) ?? -1;
478
474
  return from < 0 ? null : { from, item: slots.items[from] };
479
475
  }
480
476
  function gapAt(held, by) {
@@ -507,8 +503,8 @@ function createReorderAction(opts) {
507
503
  if (slots === null || picked === null) return {};
508
504
  const held = { slots, ...picked };
509
505
  const { axis } = slots;
510
- const region = opts.region?.(ctx.deps, held.item) ?? null;
511
- const leaves = (at) => region !== null && !inside(at, region);
506
+ const region2 = opts.region?.(ctx.deps, held.item) ?? null;
507
+ const leaves = (at) => region2 !== null && !rectContainsPoint(region2, at.x, at.y);
512
508
  let by = ctx.world[axis] - start[axis];
513
509
  let open = true;
514
510
  const verdicts = /* @__PURE__ */ new Map();
@@ -597,14 +593,6 @@ function worldRectOf(scene, id) {
597
593
  const live = { ...scenePoseAdapter(scene), getPose: (at) => effectivePose(scene, scene.get(at)) };
598
594
  return drawnAABBOf(drawn, id, composeWorldPose(live, id, scene.composition.compose), { includeStroke: false });
599
595
  }
600
- function screenRectOf(rect, view) {
601
- return {
602
- x: (rect.x - view.x) * view.scale.x,
603
- y: (rect.y - view.y) * view.scale.y,
604
- width: rect.width * view.scale.x,
605
- height: rect.height * view.scale.y
606
- };
607
- }
608
596
  var CENTER = "center";
609
597
  function place(el, overlay, anchor, view) {
610
598
  const size = { width: el.offsetWidth, height: el.offsetHeight };
@@ -626,7 +614,7 @@ function useOverlayPlacement(canvasRef, scene, overlays) {
626
614
  const world = worldRectOf(scene, overlay.target);
627
615
  el.hidden = world === null;
628
616
  if (world === null) continue;
629
- const rect = screenRectOf(world, view);
617
+ const rect = viewRectToScreen(view, world);
630
618
  const anchor = { ...rect, x: rect.x + surface.offsetLeft, y: rect.y + surface.offsetTop };
631
619
  place(el, overlay, anchor, {
632
620
  x: surface.offsetLeft,
@@ -920,6 +908,7 @@ function themeWrites(scene, theme, ids, from) {
920
908
  const data = node.data;
921
909
  if (data === null || typeof data !== "object") continue;
922
910
  const trait = traitOf(data);
911
+ if (trait !== null && ("seq" in trait || "sequence" in trait)) continue;
923
912
  if (trait !== null && "from" in trait && "to" in trait) {
924
913
  if (ids !== void 0 && !ids.has(node.id)) continue;
925
914
  const look2 = edgeLook(trait, theme);
@@ -980,7 +969,7 @@ function containerBuild(stencil, bounds, opts) {
980
969
  const id = opts.newId();
981
970
  const axis = stencil.axis ?? "y";
982
971
  const at = placedBounds(stencil, bounds);
983
- const title = stencil.role === "pool" ? "Pool" : stencil.label;
972
+ const title = stencil.title ?? (stencil.role === "pool" ? "Pool" : stencil.label);
984
973
  const style = stencil.role === "pool" && opts.laneStyles ? opts.laneStyles.header : opts.style;
985
974
  const specs = [
986
975
  ...regionSpecs(stencil.role, axis, at, title, {
@@ -989,7 +978,7 @@ function containerBuild(stencil, bounds, opts) {
989
978
  parent: opts.parent ?? null,
990
979
  ...style ? { style } : {}
991
980
  }),
992
- ...stencil.role === "pool" ? firstLanes(id, axis, at, opts) : []
981
+ ...stencil.role === "pool" ? firstLanes(id, axis, at, opts, stencil.lanes) : []
993
982
  ];
994
983
  const label = `Insert ${stencil.label}`;
995
984
  const stored = localizeWorldPoses(specs, RIGID_POSE_COMPOSITION);
@@ -1442,19 +1431,19 @@ function createLabelDragAction(opts = {}) {
1442
1431
  if (p === null) return {};
1443
1432
  let spot = null;
1444
1433
  let open = true;
1445
- const proposed = () => open ? spot : null;
1434
+ const proposed2 = () => open ? spot : null;
1446
1435
  return {
1447
1436
  kind: "diagram-move-label",
1448
1437
  onMove(moveCtx) {
1449
1438
  spot = spotAt(p, moveCtx);
1450
1439
  },
1451
- previewIds: () => proposed() === null ? null : [p.labelId],
1440
+ previewIds: () => proposed2() === null ? null : [p.labelId],
1452
1441
  previewPose: (id) => {
1453
- const next = proposed();
1442
+ const next = proposed2();
1454
1443
  return id === p.labelId && next !== null ? poseAt(p, next) : null;
1455
1444
  },
1456
1445
  previewData: (id) => {
1457
- const next = proposed();
1446
+ const next = proposed2();
1458
1447
  return id === p.labelId && next !== null ? movedLabelData(p.data, next) : null;
1459
1448
  },
1460
1449
  onEnd(endCtx, reason) {
@@ -1928,16 +1917,16 @@ function createRouteEditAction(opts) {
1928
1917
  const session = beginSnap(ctx, p, snapPx);
1929
1918
  let waypoints = null;
1930
1919
  let open = true;
1931
- const proposed = () => open ? waypoints : null;
1920
+ const proposed2 = () => open ? waypoints : null;
1932
1921
  return {
1933
1922
  kind: "diagram-edit-route",
1934
1923
  onMove(moveCtx) {
1935
1924
  waypoints = waypointsAt(p, moveOf2(p, session, moveCtx));
1936
1925
  },
1937
- previewIds: () => proposed() === null ? null : [p.edgeId],
1938
- previewPose: (id) => id === p.edgeId && proposed() !== null ? p.pose : null,
1926
+ previewIds: () => proposed2() === null ? null : [p.edgeId],
1927
+ previewPose: (id) => id === p.edgeId && proposed2() !== null ? p.pose : null,
1939
1928
  previewData: (id) => {
1940
- const next = proposed();
1929
+ const next = proposed2();
1941
1930
  return id === p.edgeId && next !== null ? routedData(p.data, p.trait, next) : null;
1942
1931
  },
1943
1932
  onEnd(endCtx, reason) {
@@ -2255,8 +2244,8 @@ function avoidSceneInput(scene, opts = {}, poseOf) {
2255
2244
  const header = container === null ? null : headerBox(box, container.header);
2256
2245
  if (header !== null) obstacles.push({ id: headerIdOf(node.id), kind: "shape", box: header });
2257
2246
  if (container?.role === "lane") continue;
2258
- const region = container !== null || diagramGroupOf(node) !== null;
2259
- obstacles.push(region ? { id: node.id, kind: "region", box, members: members(sc, node, /* @__PURE__ */ new Set()) } : { id: node.id, kind: "shape", box });
2247
+ const region2 = container !== null || diagramGroupOf(node) !== null;
2248
+ obstacles.push(region2 ? { id: node.id, kind: "region", box, members: members(sc, node, /* @__PURE__ */ new Set()) } : { id: node.id, kind: "shape", box });
2260
2249
  }
2261
2250
  for (const id of split) {
2262
2251
  const node = labelNodes.get(id);
@@ -2304,10 +2293,10 @@ function obstaclesOf(specs, world) {
2304
2293
  if (s.parent == null) continue;
2305
2294
  children.set(s.parent, [...children.get(s.parent) ?? [], s.id]);
2306
2295
  }
2307
- const inside2 = (id, into) => {
2296
+ const inside = (id, into) => {
2308
2297
  for (const child of children.get(id) ?? []) {
2309
2298
  into.add(child);
2310
- inside2(child, into);
2299
+ inside(child, into);
2311
2300
  }
2312
2301
  return into;
2313
2302
  };
@@ -2315,13 +2304,13 @@ function obstaclesOf(specs, world) {
2315
2304
  for (const spec of specs) {
2316
2305
  const node = { id: spec.id, kind: spec.kind, data: spec.data };
2317
2306
  if (spec.derivePose !== void 0 || diagramEdgeOf(spec) !== null) continue;
2318
- const region = diagramContainerOf(spec);
2319
- if (diagramNodeOf(node) === null && region?.role !== "lane") continue;
2307
+ const region2 = diagramContainerOf(spec);
2308
+ if (diagramNodeOf(node) === null && region2?.role !== "lane") continue;
2320
2309
  const box = world.get(node.id) ?? spec.pose;
2321
- const header = region === null ? null : headerBox(box, region.header);
2310
+ const header = region2 === null ? null : headerBox(box, region2.header);
2322
2311
  if (header !== null) out.push({ id: headerIdOf(node.id), kind: "shape", box: header });
2323
- if (region?.role === "lane") continue;
2324
- out.push(region === null ? { id: node.id, kind: "shape", box } : { id: node.id, kind: "region", box, members: inside2(node.id, /* @__PURE__ */ new Set()) });
2312
+ if (region2?.role === "lane") continue;
2313
+ out.push(region2 === null ? { id: node.id, kind: "shape", box } : { id: node.id, kind: "region", box, members: inside(node.id, /* @__PURE__ */ new Set()) });
2325
2314
  }
2326
2315
  return out;
2327
2316
  }
@@ -2684,168 +2673,27 @@ function createSpecLabelPlacer(opts = {}) {
2684
2673
  };
2685
2674
  }
2686
2675
 
2676
+ // src/sequence/trait.ts
2677
+ var traitOf2 = (node) => {
2678
+ const data = node.data;
2679
+ if (data === null || typeof data !== "object") return null;
2680
+ const trait = data.diagram;
2681
+ return trait !== null && typeof trait === "object" ? trait : null;
2682
+ };
2683
+ function sequenceRootOf(node) {
2684
+ const trait = traitOf2(node);
2685
+ return trait !== null && "sequence" in trait && "geometry" in trait ? trait : null;
2686
+ }
2687
+ function sequencePartOf(node) {
2688
+ const seq = traitOf2(node)?.seq;
2689
+ return seq !== null && typeof seq === "object" && typeof seq.root === "string" ? seq : null;
2690
+ }
2691
+
2687
2692
  // src/resizable.ts
2688
2693
  function diagramResizable(scene) {
2689
2694
  return (id) => {
2690
2695
  const node = scene.get(id);
2691
- return node === void 0 || diagramEdgeOf(node) === null && diagramLabelOf(node) === null && diagramGroupOf(node) === null && diagramContainerOf(node)?.role !== "lane";
2692
- };
2693
- }
2694
-
2695
- // src/routeWrites.ts
2696
- var ROUTE_TOLERANCE = 1e-3;
2697
- function routesOf(graph, plan) {
2698
- return {
2699
- edges: new Set(graph.edges.map((e) => e.origin?.id ?? e.id)),
2700
- waypoints: plan.waypoints ?? /* @__PURE__ */ new Map(),
2701
- ...plan.labels !== void 0 ? { labels: plan.labels } : {},
2702
- ...plan.ports !== void 0 ? { ports: plan.ports } : {}
2703
- };
2704
- }
2705
- function routeWrites(scene, routes) {
2706
- const out = /* @__PURE__ */ new Map();
2707
- const owned = /* @__PURE__ */ new Set();
2708
- for (const id of routes.edges) {
2709
- const node = scene.get(id);
2710
- const edge = node === void 0 ? null : diagramEdgeOf(node);
2711
- if (node === void 0 || edge === null || scene.isLocked(id)) {
2712
- if (edge !== null && layoutWritesRoute(edge)) owned.add(id);
2713
- continue;
2714
- }
2715
- let next = edge;
2716
- if (layoutWritesRoute(edge)) {
2717
- owned.add(id);
2718
- next = rerouted(edge, routes.waypoints.get(id));
2719
- }
2720
- const ports = routes.ports?.get(id);
2721
- const from = renamed(edge.from, ports?.from);
2722
- const to = renamed(edge.to, ports?.to);
2723
- if (from !== edge.from || to !== edge.to) next = { ...next, from, to };
2724
- if (next !== edge) out.set(id, withTrait(node.data, next));
2725
- }
2726
- const stationed = /* @__PURE__ */ new Set();
2727
- for (const node of scene.renderOrderNodes()) {
2728
- const label = diagramLabelOf(node);
2729
- const deps = node.dependsOn;
2730
- if (label === null || !Array.isArray(deps) || deps.length !== 1 || !owned.has(deps[0])) continue;
2731
- if (!labelFollowsLayout(label.at) || stationed.has(deps[0])) continue;
2732
- stationed.add(deps[0]);
2733
- const near2 = routes.labels?.get(deps[0]);
2734
- const at = near2 === void 0 ? "mid" : { near: near2 };
2735
- if (sameAt(label.at, at) || scene.isLocked(node.id)) continue;
2736
- out.set(node.id, withTrait(node.data, { ...label, at }, "label"));
2737
- }
2738
- return out;
2739
- }
2740
- function rerouted(edge, route) {
2741
- if (route === void 0 || route.length === 0) {
2742
- if ((edge.waypoints ?? []).length === 0 && edge.laidOut === void 0) return edge;
2743
- const { waypoints: _w, laidOut: _l, ...rest } = edge;
2744
- return rest;
2745
- }
2746
- if (edge.waypoints !== void 0 && near(edge.waypoints, route)) return edge;
2747
- return { ...edge, waypoints: route, laidOut: route };
2748
- }
2749
- function renamed(end, port) {
2750
- if (!layoutOwnsPort(end) || end.port === port && (port === void 0 || end.laidOut === port)) return end;
2751
- const { port: _p, laidOut: _l, ...rest } = end;
2752
- return port === void 0 ? rest : { ...rest, port, laidOut: port };
2753
- }
2754
- function near(a, b) {
2755
- return a.length === b.length && a.every((p, i) => Math.abs(p.x - b[i].x) <= ROUTE_TOLERANCE && Math.abs(p.y - b[i].y) <= ROUTE_TOLERANCE);
2756
- }
2757
- function sameAt(a, b) {
2758
- if (typeof a === "object" && typeof b === "object") return near([a.near], [b.near]);
2759
- return a === b;
2760
- }
2761
- function withTrait(data, value, key) {
2762
- const own = data;
2763
- const diagram = key === void 0 ? value : { ...own.diagram, [key]: value };
2764
- return { ...own, diagram };
2765
- }
2766
- var LAYOUTS = Object.freeze({
2767
- layered,
2768
- tree,
2769
- force,
2770
- chain,
2771
- radial
2772
- });
2773
- function applyLayout(scene, result, opts = {}) {
2774
- const poses = layoutPoses(scene, result, opts.geometry);
2775
- const data = opts.routes === void 0 ? /* @__PURE__ */ new Map() : routeWrites(scene, opts.routes);
2776
- if (poses.size === 0 && data.size === 0) return 0;
2777
- const open = [...poses].filter(([id]) => !scene.isLocked(id));
2778
- if (open.length === 0 && data.size === 0) return 0;
2779
- scene.batch(opts.label ?? "Layout", () => {
2780
- for (const [id, pose] of open) scene.setPose(id, pose);
2781
- for (const [id, d] of data) scene.update(id, { data: d });
2782
- });
2783
- const wrote = new Set(open.map(([id]) => id));
2784
- let moved = 0;
2785
- for (const id of result.keys()) if (wrote.has(id)) moved++;
2786
- return moved;
2787
- }
2788
- function layoutPoses(scene, result, geometry = AUTO_POSE_DESCRIPTOR) {
2789
- const sc = scene;
2790
- const { compose, decompose } = sc.composition;
2791
- const stored = scenePoseAdapter(sc);
2792
- const out = /* @__PURE__ */ new Map();
2793
- const decided = { ...stored, getPose: (id) => out.has(id) ? out.get(id) : stored.getPose(id) };
2794
- const depth = (id) => {
2795
- let d = 0;
2796
- for (let at = scene.get(id)?.parent; at != null; at = scene.get(at)?.parent) d++;
2797
- return d;
2798
- };
2799
- const ids = [...result.keys()].filter((id) => scene.get(id) !== void 0).sort((a, b) => depth(a) - depth(b));
2800
- for (const id of ids) {
2801
- const at = result.get(id);
2802
- const world = composeWorldPose(stored, id, compose);
2803
- const bounds = geometry.getBounds(world);
2804
- const dx = at.x - bounds.x;
2805
- const dy = at.y - bounds.y;
2806
- if (dx === 0 && dy === 0) continue;
2807
- const parent = scene.get(id).parent;
2808
- out.set(id, rebaseLocalPose(decided, translatePoseViaDescriptor(world, dx, dy, geometry), parent, compose, decompose));
2809
- const carried = carriedDescendants(sceneCarrySource(sc), id, (w) => translatePoseViaDescriptor(w, dx, dy, geometry));
2810
- for (const [d, pose] of carried) out.set(d, pose);
2811
- }
2812
- return out;
2813
- }
2814
- var LAYOUT_ACTION_ID = "diagram.layout";
2815
- function layoutDiagram(scene, source, algorithm, opts = {}) {
2816
- const fn = typeof algorithm === "function" ? algorithm : LAYOUTS[algorithm] ?? layered;
2817
- const graph = buildGraph(source, {
2818
- ...opts.read ? { read: opts.read } : {},
2819
- ...opts.geometry ? { geometry: opts.geometry } : {}
2820
- });
2821
- const plan = planOf(fn, graph, opts.layout);
2822
- return applyLayout(scene, plan.moves, {
2823
- ...opts.geometry ? { geometry: opts.geometry } : {},
2824
- ...opts.label ? { label: opts.label } : {},
2825
- routes: routesOf(graph, plan)
2826
- });
2827
- }
2828
- function createLayoutAction(opts) {
2829
- const { read, geometry, layout, label } = opts;
2830
- const options = {
2831
- ...read ? { read } : {},
2832
- ...geometry ? { geometry } : {},
2833
- ...layout ? { layout } : {},
2834
- ...label ? { label } : {}
2835
- };
2836
- return {
2837
- id: opts.id ?? LAYOUT_ACTION_ID,
2838
- label: opts.label ?? "Lay out diagram",
2839
- group: opts.group ?? "diagram",
2840
- requires: ["scene"],
2841
- invoker: {
2842
- timing: "immediate",
2843
- run: (deps) => {
2844
- const scene = deps["scene"];
2845
- if (scene !== void 0) layoutDiagram(scene, opts.source, opts.algorithm ?? "layered", options);
2846
- }
2847
- },
2848
- enabled: () => true
2696
+ return node === void 0 || diagramEdgeOf(node) === null && diagramLabelOf(node) === null && diagramGroupOf(node) === null && diagramContainerOf(node)?.role !== "lane" && sequencePartOf(node) === null;
2849
2697
  };
2850
2698
  }
2851
2699
  var EASE_FRAMES = 24;
@@ -2880,14 +2728,14 @@ function forceProducer(graph, opts = {}) {
2880
2728
  }
2881
2729
  relaxation.sim.alphaTarget(pinned.size > 0 ? dragAlpha : 0);
2882
2730
  relaxation.sim.tick();
2883
- let moved = 0;
2731
+ let moved2 = 0;
2884
2732
  relaxation.bodies.forEach((body, i) => {
2885
2733
  const at = was[i];
2886
- moved = Math.max(moved, Math.hypot(body.x - at.x, body.y - at.y));
2734
+ moved2 = Math.max(moved2, Math.hypot(body.x - at.x, body.y - at.y));
2887
2735
  at.x = body.x;
2888
2736
  at.y = body.y;
2889
2737
  });
2890
- still = pinned.size === 0 && moved < restDistance ? still + 1 : 0;
2738
+ still = pinned.size === 0 && moved2 < restDistance ? still + 1 : 0;
2891
2739
  const done = still >= restTicks || relaxation.sim.isSettled();
2892
2740
  return { result: relaxation.placed(), done };
2893
2741
  };
@@ -2956,19 +2804,18 @@ function usePendingPlan() {
2956
2804
  useEffect(() => () => pending.drop(), [pending]);
2957
2805
  return pending;
2958
2806
  }
2959
- var lerp = (a, b, u) => ({ x: a.x + (b.x - a.x) * u, y: a.y + (b.y - a.y) * u });
2960
2807
  function along(points, s) {
2961
2808
  if (points.length === 1) return points[0];
2962
2809
  const lengths = points.slice(1).map((p, i) => Math.hypot(p.x - points[i].x, p.y - points[i].y));
2963
2810
  const total = lengths.reduce((a, b) => a + b, 0);
2964
2811
  let left = s * total;
2965
2812
  for (const [i, len] of lengths.entries()) {
2966
- if (left <= len || i === lengths.length - 1) return lerp(points[i], points[i + 1], len === 0 ? 0 : Math.min(left / len, 1));
2813
+ if (left <= len || i === lengths.length - 1) return lerpPoint(points[i], points[i + 1], len === 0 ? 0 : Math.min(left / len, 1));
2967
2814
  left -= len;
2968
2815
  }
2969
2816
  return points[points.length - 1];
2970
2817
  }
2971
- var straightRun = (count, start, end) => Array.from({ length: count }, (_, k) => lerp(start, end, (k + 1) / (count + 1)));
2818
+ var straightRun = (count, start, end) => Array.from({ length: count }, (_, k) => lerpPoint(start, end, (k + 1) / (count + 1)));
2972
2819
  function respread(points, count, start, end) {
2973
2820
  if (points.length === count) return [...points];
2974
2821
  if (points.length === 0) return straightRun(count, start, end);
@@ -3029,7 +2876,7 @@ function easedPaths(scene, easings, u, poseOf, geometry = AUTO_POSE_DESCRIPTOR)
3029
2876
  const [a, b] = deps;
3030
2877
  target = u >= 1 || a === void 0 || b === void 0 ? [] : straightRun(from.length, centerOf(a), centerOf(b));
3031
2878
  }
3032
- const waypoints = target.length === 0 && u >= 1 ? [] : from.map((p, k) => lerp(p, target[k], u));
2879
+ const waypoints = target.length === 0 && u >= 1 ? [] : from.map((p, k) => lerpPoint(p, target[k], u));
3033
2880
  const data = node.data;
3034
2881
  const diagram = { ...data.diagram, ...ends, waypoints };
3035
2882
  const path = node.derivePath({ ...node, data: { ...data, diagram } }, deps);
@@ -3143,7 +2990,7 @@ function useLiveLayout(opts) {
3143
2990
  const o = optsRef.current;
3144
2991
  producerFrame.current = 0;
3145
2992
  held.current = false;
3146
- routes.current = routesOf(graph, plan ?? { });
2993
+ routes.current = routesOf(graph, plan ?? { moves: /* @__PURE__ */ new Map() });
3147
2994
  eased.set(routeEasings(o.scene, graph, routes.current));
3148
2995
  producer.current = producerFor(graph, plan, o);
3149
2996
  }, [optsRef, eased]);
@@ -3168,9 +3015,9 @@ function useLiveLayout(opts) {
3168
3015
  scene: opts.scene,
3169
3016
  step,
3170
3017
  // Eased routes go once the routes are in the document, so no frame shows the old ones.
3171
- onCommit: (moved) => {
3018
+ onCommit: (moved2) => {
3172
3019
  eased.drop();
3173
- optsRef.current.onSettle?.(moved);
3020
+ optsRef.current.onSettle?.(moved2);
3174
3021
  },
3175
3022
  // Routes match where the layout was going, so only a run that got there
3176
3023
  // with nothing held along the way writes them.
@@ -3237,7 +3084,8 @@ function useLiveLayout(opts) {
3237
3084
  var EASED_LAYOUTS = Object.freeze({
3238
3085
  layered,
3239
3086
  tree,
3240
- chain
3087
+ chain,
3088
+ grid
3241
3089
  });
3242
3090
  function bodyRules(trait, box) {
3243
3091
  const layout = trait.body;
@@ -3254,7 +3102,7 @@ function bodyRules(trait, box) {
3254
3102
  // src/shape.ts
3255
3103
  function diagramShape(opts = {}) {
3256
3104
  const geometry = opts.geometry ?? RECT_POSE_DESCRIPTOR;
3257
- const outlineOf2 = (node) => diagramNodeOf(node, opts.read)?.outline ?? laneOutlineOf(node);
3105
+ const outlineOf2 = (node) => diagramNodeOf(node, opts.read)?.outline ?? laneOutlineOf(node) ?? sequenceOutlineOf(node);
3258
3106
  return {
3259
3107
  id: opts.id ?? "diagram:outline",
3260
3108
  matches: (node) => outlineOf2(node) !== void 0,
@@ -3271,6 +3119,9 @@ function diagramShape(opts = {}) {
3271
3119
  const divider = header === null || !stroke ? [] : [{ kind: "path", path: dividerPath(box, header), stroke }];
3272
3120
  const detailPath = stroke ? outlineDetail(outline, box) : null;
3273
3121
  const detail = stroke && detailPath ? [{ kind: "path", path: detailPath, stroke }] : [];
3122
+ const solidPath = declaredStroke ? outlineSolid(outline, box) : null;
3123
+ const solidFill = declaredStroke && solidPath ? commandFill({ paint: declaredStroke.paint }, box) : null;
3124
+ const solid = solidPath && solidFill ? [{ kind: "path", path: solidPath, fill: solidFill }] : [];
3274
3125
  const trait = diagramNodeOf(node, opts.read);
3275
3126
  const rules = stroke && trait !== null ? bodyRules(trait, box).map((path) => ({ kind: "path", path, stroke })) : [];
3276
3127
  return [{
@@ -3278,7 +3129,7 @@ function diagramShape(opts = {}) {
3278
3129
  path: outlinePath(outline, box),
3279
3130
  ...fill ? { fill } : {},
3280
3131
  ...stroke ? { stroke } : {}
3281
- }, ...detail, ...rules, ...divider];
3132
+ }, ...solid, ...detail, ...rules, ...divider];
3282
3133
  },
3283
3134
  // The drawn boundary, so picking and clipping follow the diamond rather
3284
3135
  // than its bounding box.
@@ -3288,6 +3139,10 @@ function diagramShape(opts = {}) {
3288
3139
  }
3289
3140
  };
3290
3141
  }
3142
+ function sequenceOutlineOf(node) {
3143
+ if (sequencePartOf(node) === null) return void 0;
3144
+ return node.data.diagram?.outline;
3145
+ }
3291
3146
  function dividerPath(box, header) {
3292
3147
  return header.width < box.width ? polylineFromPoints([{ x: header.x + header.width, y: box.y }, { x: header.x + header.width, y: box.y + box.height }]) : polylineFromPoints([{ x: box.x, y: header.y + header.height }, { x: box.x + box.width, y: header.y + header.height }]);
3293
3148
  }
@@ -3298,6 +3153,1338 @@ function registerDiagramShape(opts = {}) {
3298
3153
  // src/bodyNode.ts
3299
3154
  var isDiagramBody = (node) => diagramContainerOf(node) === null && diagramGroupOf(node) === null && dataKeyReader(node) !== null;
3300
3155
  var isBodyOrRegion = (node) => diagramContainerOf(node) !== null || isDiagramBody(node);
3156
+
3157
+ // src/sequence/edit.ts
3158
+ var sameList = (a, b) => a === b || a !== null && b !== null && a.fragment === b.fragment && a.section === b.section;
3159
+ var hasId = (s) => "id" in s;
3160
+ function stepListOf(steps, parent) {
3161
+ if (parent === null) return steps;
3162
+ const frag = findStep(steps, parent.fragment);
3163
+ return frag?.kind === "fragment" ? frag.sections[parent.section]?.steps ?? null : null;
3164
+ }
3165
+ function findStep(steps, id) {
3166
+ for (const s of steps) {
3167
+ if (hasId(s) && s.id === id) return s;
3168
+ if (s.kind === "fragment") {
3169
+ for (const section of s.sections) {
3170
+ const found = findStep(section.steps, id);
3171
+ if (found !== null) return found;
3172
+ }
3173
+ }
3174
+ }
3175
+ return null;
3176
+ }
3177
+ function placeOf(steps, id, parent = null) {
3178
+ for (const [index, s] of steps.entries()) {
3179
+ if (hasId(s) && s.id === id) return { parent, index };
3180
+ if (s.kind === "fragment") {
3181
+ for (const [k, section] of s.sections.entries()) {
3182
+ const found = placeOf(section.steps, id, { fragment: s.id, section: k });
3183
+ if (found !== null) return found;
3184
+ }
3185
+ }
3186
+ }
3187
+ return null;
3188
+ }
3189
+ function mapSteps(steps, fn) {
3190
+ let changed = false;
3191
+ const out = steps.map((s) => {
3192
+ let next = fn(s);
3193
+ if (next.kind === "fragment") {
3194
+ const frag = next;
3195
+ const sections = frag.sections.map((section) => {
3196
+ const inner = mapSteps(section.steps, fn);
3197
+ return inner === section.steps ? section : { ...section, steps: inner };
3198
+ });
3199
+ if (sections.some((sec, k) => sec !== frag.sections[k])) next = { ...frag, sections };
3200
+ }
3201
+ if (next !== s) changed = true;
3202
+ return next;
3203
+ });
3204
+ return changed ? out : steps;
3205
+ }
3206
+ function pruneSteps(steps, fn) {
3207
+ let changed = false;
3208
+ const out = [];
3209
+ for (const s of steps) {
3210
+ let step = s;
3211
+ if (s.kind === "fragment") {
3212
+ const sections = s.sections.map((sec) => {
3213
+ const inner = pruneSteps(sec.steps, fn);
3214
+ return inner === sec.steps ? sec : { ...sec, steps: inner };
3215
+ });
3216
+ if (sections.some((sec, k) => sec !== s.sections[k])) step = { ...s, sections };
3217
+ }
3218
+ const next = fn(step);
3219
+ if (next !== s) changed = true;
3220
+ if (next !== null) out.push(next);
3221
+ }
3222
+ return changed ? out : steps;
3223
+ }
3224
+ function withList(steps, parent, fn) {
3225
+ if (parent === null) return fn(steps);
3226
+ return mapSteps(steps, (s) => {
3227
+ if (s.kind !== "fragment" || s.id !== parent.fragment) return s;
3228
+ const sections = s.sections.map((sec, k) => k === parent.section ? { ...sec, steps: fn(sec.steps) } : sec);
3229
+ return { ...s, sections };
3230
+ });
3231
+ }
3232
+ var contains = (step, id) => step.kind === "fragment" && step.sections.some((sec) => sec.steps.some((s) => hasId(s) && s.id === id || contains(s, id)));
3233
+ function moveStep(data, id, to) {
3234
+ const from = placeOf(data.steps, id);
3235
+ const step = findStep(data.steps, id);
3236
+ if (from === null || step === null) return data;
3237
+ if (to.parent !== null && (to.parent.fragment === id || contains(step, to.parent.fragment))) return data;
3238
+ if (stepListOf(data.steps, to.parent) === null) return data;
3239
+ const within = sameList(from.parent, to.parent);
3240
+ if (within && (to.index === from.index || to.index === from.index + 1)) return data;
3241
+ const removed = withList(data.steps, from.parent, (list) => list.filter((_, i) => i !== from.index));
3242
+ const index = within && to.index > from.index ? to.index - 1 : to.index;
3243
+ const steps = withList(removed, to.parent, (list) => [...list.slice(0, index), step, ...list.slice(index)]);
3244
+ return { ...data, steps };
3245
+ }
3246
+ function moveParticipant(data, id, index) {
3247
+ const from = data.participants.findIndex((p2) => p2.id === id);
3248
+ if (from < 0 || index === from || index === from + 1) return data;
3249
+ const p = data.participants[from];
3250
+ const rest = data.participants.filter((_, i) => i !== from);
3251
+ const at = index > from ? index - 1 : index;
3252
+ return { ...data, participants: [...rest.slice(0, at), p, ...rest.slice(at)] };
3253
+ }
3254
+ function reconnectMessage(data, id, end, participant) {
3255
+ const step = findStep(data.steps, id);
3256
+ if (step?.kind !== "message" || step[end] === participant || !data.participants.some((p) => p.id === participant)) return data;
3257
+ return { ...data, steps: mapSteps(data.steps, (s) => s === step ? { ...step, [end]: participant } : s) };
3258
+ }
3259
+ function setSequenceText(data, target, text) {
3260
+ if ("participant" in target) {
3261
+ const at = data.participants.findIndex((p2) => p2.id === target.participant);
3262
+ const p = data.participants[at];
3263
+ if (p === void 0 || (p.label ?? p.id) === text) return data;
3264
+ const { label: _label, ...rest } = p;
3265
+ const next = text === "" || text === p.id ? rest : { ...rest, label: text };
3266
+ return { ...data, participants: data.participants.map((q, i) => i === at ? next : q) };
3267
+ }
3268
+ if ("box" in target) {
3269
+ const boxes = data.boxes?.map((b) => {
3270
+ if (b.id !== target.box || (b.label ?? "") === text) return b;
3271
+ const { label: _label, ...rest } = b;
3272
+ return text === "" ? rest : { ...rest, label: text };
3273
+ });
3274
+ return boxes === void 0 || boxes.every((b, i) => b === data.boxes[i]) ? data : { ...data, boxes };
3275
+ }
3276
+ if ("fragment" in target) {
3277
+ const steps2 = mapSteps(data.steps, (s) => {
3278
+ if (s.kind !== "fragment" || s.id !== target.fragment) return s;
3279
+ const section = s.sections[target.section];
3280
+ if (section === void 0 || (section.label ?? "") === text) return s;
3281
+ const { label: _label, ...rest } = section;
3282
+ const next = text === "" ? rest : { ...rest, label: text };
3283
+ return { ...s, sections: s.sections.map((sec, k) => k === target.section ? next : sec) };
3284
+ });
3285
+ return steps2 === data.steps ? data : { ...data, steps: steps2 };
3286
+ }
3287
+ const steps = mapSteps(data.steps, (s) => {
3288
+ if (s.kind === "note" && s.id === target.step && s.text !== text) return { ...s, text };
3289
+ if (s.kind !== "message" || s.id !== target.step || (s.text ?? "") === text) return s;
3290
+ const { text: _text, ...rest } = s;
3291
+ return text === "" ? rest : { ...rest, text };
3292
+ });
3293
+ return steps === data.steps ? data : { ...data, steps };
3294
+ }
3295
+ function sequenceTextOf(data, target) {
3296
+ if ("participant" in target) {
3297
+ const p = data.participants.find((q) => q.id === target.participant);
3298
+ return p === void 0 ? null : p.label ?? p.id;
3299
+ }
3300
+ if ("box" in target) {
3301
+ const b = data.boxes?.find((q) => q.id === target.box);
3302
+ return b === void 0 ? null : b.label ?? "";
3303
+ }
3304
+ if ("fragment" in target) {
3305
+ const f = findStep(data.steps, target.fragment);
3306
+ const section = f?.kind === "fragment" ? f.sections[target.section] : void 0;
3307
+ return section === void 0 ? null : section.label ?? "";
3308
+ }
3309
+ const s = findStep(data.steps, target.step);
3310
+ return s?.kind === "message" ? s.text ?? "" : s?.kind === "note" ? s.text : null;
3311
+ }
3312
+
3313
+ // src/sequence/metrics.ts
3314
+ var SEQ = {
3315
+ /** Around the whole diagram. */
3316
+ margin: 12,
3317
+ /** Between a head's outline and its text. */
3318
+ headPad: 10,
3319
+ headMinWidth: 80,
3320
+ headMinHeight: 36,
3321
+ /** The least room between two heads. */
3322
+ columnGap: 32,
3323
+ /** Either side of a message's text along its line. */
3324
+ textPad: 10,
3325
+ /** Above every row. */
3326
+ rowGap: 14,
3327
+ /** Between a message's text and its line. */
3328
+ textGap: 3,
3329
+ /** A self-message's loop: how far it reaches right, and how tall it is. */
3330
+ loopWidth: 32,
3331
+ loopHeight: 20,
3332
+ /** An activation bar's width, and how far each nested one steps right. */
3333
+ barWidth: 10,
3334
+ barStep: 5,
3335
+ notePad: 6,
3336
+ noteMinWidth: 60,
3337
+ /** Between a lifeline and a note beside it. */
3338
+ noteGap: 10,
3339
+ /** How far a note over two lifelines reaches past them. */
3340
+ noteOverhang: 16,
3341
+ /** Between a fragment's box and the lifelines it holds, and between nested boxes. */
3342
+ fragmentPad: 14,
3343
+ fragmentNest: 8,
3344
+ /** Around a fragment's type label in its tab. */
3345
+ tabPad: 6,
3346
+ /** Between a box's border and the heads it holds. */
3347
+ boxPad: 10,
3348
+ /** Below the last row. */
3349
+ footer: 16,
3350
+ /** The X at a destroyed lifeline's end. */
3351
+ cross: 12
3352
+ };
3353
+ function seqMeasure(measure, font = DEFAULT_FONT) {
3354
+ const line = measure ?? ((text, s) => {
3355
+ const size = s.fontSize ?? font.fontSize;
3356
+ return { width: text.length * GLYPH * size, height: Math.ceil(1.4 * size) };
3357
+ });
3358
+ return {
3359
+ font,
3360
+ text: (text, style = {}) => {
3361
+ const s = { ...style, fontFamily: style.fontFamily ?? font.fontFamily, fontSize: style.fontSize ?? font.fontSize };
3362
+ const sizes = text.split("\n").map((l) => line(l, s));
3363
+ return { width: Math.max(0, ...sizes.map((z) => z.width)), height: sizes.reduce((h, z) => h + z.height, 0) };
3364
+ }
3365
+ };
3366
+ }
3367
+ function headSize(p, m) {
3368
+ const t = m.text(p.label ?? p.id);
3369
+ const outline = p.outline ?? "rect";
3370
+ const box = boxForContent(outline, { width: t.width + SEQ.headPad * 2, height: t.height + SEQ.headPad * 2 });
3371
+ return { width: Math.max(SEQ.headMinWidth, Math.ceil(box.width)), height: Math.max(SEQ.headMinHeight, Math.ceil(box.height)) };
3372
+ }
3373
+
3374
+ // src/sequence/columns.ts
3375
+ function* flatSteps(steps) {
3376
+ for (const s of steps) {
3377
+ yield s;
3378
+ if (s.kind === "fragment") for (const section of s.sections) yield* flatSteps(section.steps);
3379
+ }
3380
+ }
3381
+ function noteNeeds(over, side, width, half) {
3382
+ const [a, b] = [Math.min(...over), Math.max(...over)];
3383
+ const room = width + SEQ.noteGap * 2;
3384
+ if (side === "right") return [{ a, b: a + 1, room: room + half(a + 1) }];
3385
+ if (side === "left") return [{ a: a - 1, b: a, room: room + half(a - 1) }];
3386
+ if (a === b) return [{ a: a - 1, b: a, room: width / 2 + half(a - 1) + SEQ.noteGap }, { a, b: a + 1, room: width / 2 + half(a + 1) + SEQ.noteGap }];
3387
+ return [{ a, b, room: width - SEQ.noteOverhang * 2 }];
3388
+ }
3389
+ function needsOf(data, m, index, half) {
3390
+ const out = [];
3391
+ const numbered = data.autonumber !== void 0;
3392
+ for (const s of flatSteps(data.steps)) {
3393
+ if (s.kind === "message") {
3394
+ const a = index.get(s.from);
3395
+ const b = index.get(s.to);
3396
+ if (a === void 0 || b === void 0) continue;
3397
+ const text = (s.text ?? "") === "" ? 0 : m.text(s.text).width;
3398
+ const room = text + SEQ.textPad * 2 + SEQ.barWidth * 2 + (numbered ? SEQ.textPad * 2 : 0);
3399
+ if (a === b) out.push({ a, b: a + 1, room: SEQ.loopWidth + room + half(a + 1) });
3400
+ else out.push({ a: Math.min(a, b), b: Math.max(a, b), room });
3401
+ } else if (s.kind === "note") {
3402
+ const over = s.over.map((id) => index.get(id)).filter((i) => i !== void 0);
3403
+ if (over.length > 0) out.push(...noteNeeds(over, s.side, Math.max(SEQ.noteMinWidth, m.text(s.text).width + SEQ.notePad * 2), half));
3404
+ }
3405
+ }
3406
+ return out;
3407
+ }
3408
+ function columnsOf(data, m, left) {
3409
+ const ps = data.participants;
3410
+ const heads = ps.map((p) => headSize(p, m));
3411
+ const index = new Map(ps.map((p, i) => [p.id, i]));
3412
+ const boxOf = /* @__PURE__ */ new Map();
3413
+ for (const b of data.boxes ?? []) for (const id of b.members) if (!boxOf.has(id)) boxOf.set(id, b.id);
3414
+ const half = (i) => (heads[i]?.width ?? 0) / 2;
3415
+ const boxPad = (i) => i >= 0 && i < ps.length && boxOf.has(ps[i].id) ? SEQ.boxPad : 0;
3416
+ const gaps = Array.from({ length: ps.length + 1 }, (_, i) => {
3417
+ if (i === 0) return half(0) + boxPad(0);
3418
+ if (i === ps.length) return half(i - 1) + boxPad(i - 1);
3419
+ const split = boxOf.get(ps[i - 1].id) !== boxOf.get(ps[i].id);
3420
+ return half(i - 1) + half(i) + SEQ.columnGap + (split ? boxPad(i - 1) + boxPad(i) : 0);
3421
+ });
3422
+ const needs = needsOf(data, m, index, half).sort((p, q) => p.b - p.a - (q.b - q.a));
3423
+ for (const { a, b, room } of needs) {
3424
+ const lo = Math.max(a, -1) + 1;
3425
+ const hi = Math.min(b, ps.length) + 1;
3426
+ let have = 0;
3427
+ for (let i = lo; i < hi; i++) have += gaps[i];
3428
+ const lack = room - have;
3429
+ if (lack > 0) for (let i = lo; i < hi; i++) gaps[i] += lack / (hi - lo);
3430
+ }
3431
+ const x = [];
3432
+ let at = left;
3433
+ for (let i = 0; i < ps.length; i++) {
3434
+ at += gaps[i];
3435
+ x.push(at);
3436
+ }
3437
+ return { x, heads, right: at + gaps[ps.length] };
3438
+ }
3439
+
3440
+ // src/sequence/rows.ts
3441
+ var spanOf = (rects) => {
3442
+ const x0 = Math.min(...rects.map((r) => r.x));
3443
+ return { x0, x1: Math.max(...rects.map((r) => r.x + r.width)) };
3444
+ };
3445
+ var RowWalk = class {
3446
+ constructor(data, m, cols, top) {
3447
+ this.data = data;
3448
+ this.m = m;
3449
+ this.cols = cols;
3450
+ this.index = new Map(data.participants.map((p, i) => [p.id, i]));
3451
+ this.created = new Set(flatMarks(data.steps, "create"));
3452
+ this.number = data.autonumber;
3453
+ const shown = data.participants.filter((p) => !this.created.has(p.id));
3454
+ const tall = Math.max(SEQ.headMinHeight, ...shown.map((p) => cols.heads[this.index.get(p.id)].height));
3455
+ for (const p of shown) this.placeHead(p.id, top + tall - cols.heads[this.index.get(p.id)].height, false);
3456
+ for (const l of this.lifeline.values()) l.y0 = top + tall;
3457
+ this.y = top + tall;
3458
+ }
3459
+ data;
3460
+ m;
3461
+ cols;
3462
+ out = { heads: [], lifelines: [], messages: [], activations: [], notes: [], fragments: [], slots: [], bottom: 0 };
3463
+ index;
3464
+ created;
3465
+ lifeline = /* @__PURE__ */ new Map();
3466
+ bars = /* @__PURE__ */ new Map();
3467
+ /** Lifelines a `destroy` ends at the next message to them, each with where it ends if none comes. */
3468
+ destroying = /* @__PURE__ */ new Map();
3469
+ frames = [];
3470
+ last = null;
3471
+ number;
3472
+ y;
3473
+ cx(id) {
3474
+ return this.cols.x[this.index.get(id)];
3475
+ }
3476
+ headRect(id) {
3477
+ const size = this.cols.heads[this.index.get(id)];
3478
+ return { x: this.cx(id) - size.width / 2, y: 0, width: size.width, height: 0 };
3479
+ }
3480
+ placeHead(id, y, created) {
3481
+ const size = this.cols.heads[this.index.get(id)];
3482
+ this.out.heads.push({ id, box: { x: this.cx(id) - size.width / 2, y, width: size.width, height: size.height }, created });
3483
+ const line = { id, x: this.cx(id), y0: y + size.height, y1: null };
3484
+ this.lifeline.set(id, line);
3485
+ this.out.lifelines.push(line);
3486
+ return y + size.height;
3487
+ }
3488
+ /** Into every open fragment's reach. */
3489
+ touch(rect) {
3490
+ for (const f of this.frames) f.push(rect);
3491
+ }
3492
+ /** Where a line meets `id`'s lifeline from `side`: the edge of its innermost open activation. */
3493
+ endX(id, side) {
3494
+ const depth = this.bars.get(id)?.length ?? 0;
3495
+ if (depth === 0) return this.cx(id);
3496
+ return this.cx(id) + side * (SEQ.barWidth / 2) + (depth - 1) * SEQ.barStep;
3497
+ }
3498
+ open(id, y, opener) {
3499
+ const stack = this.bars.get(id) ?? [];
3500
+ stack.push({ y, opener });
3501
+ this.bars.set(id, stack);
3502
+ }
3503
+ close(id, y, closer) {
3504
+ const stack = this.bars.get(id);
3505
+ const bar = stack?.pop();
3506
+ if (bar === void 0) return;
3507
+ const start = bar.y;
3508
+ const depth = stack.length;
3509
+ const x = this.cx(id) - SEQ.barWidth / 2 + depth * SEQ.barStep;
3510
+ const box = { x, y: start, width: SEQ.barWidth, height: Math.max(y, start + 8) - start };
3511
+ this.out.activations.push({ participant: id, depth, box, opener: bar.opener, ...closer ? { closer } : {} });
3512
+ }
3513
+ steps(list, parent) {
3514
+ list.forEach((step, index) => {
3515
+ this.out.slots.push({ y: this.y, place: { parent, index } });
3516
+ if (step.kind === "message") this.message(step);
3517
+ else if (step.kind === "note") this.note(step);
3518
+ else if (step.kind === "fragment") this.fragment(step);
3519
+ else this.mark(step, { parent, index });
3520
+ });
3521
+ this.out.slots.push({ y: this.y, place: { parent, index: list.length } });
3522
+ }
3523
+ message(s) {
3524
+ if (!this.index.has(s.from) || !this.index.has(s.to)) return;
3525
+ this.y += SEQ.rowGap;
3526
+ const text = (s.text ?? "") === "" ? null : this.m.text(s.text);
3527
+ const lineY = this.y + Math.max(text === null ? 0 : text.height + SEQ.textGap, 6);
3528
+ if (s.activate !== void 0) this.open(s[s.activate], lineY, { message: s.id, flag: "activate" });
3529
+ const self = s.from === s.to;
3530
+ const dir = self ? 1 : Math.sign(this.cx(s.to) - this.cx(s.from));
3531
+ const x1 = this.endX(s.from, dir);
3532
+ const x2 = self ? x1 : this.endX(s.to, -dir);
3533
+ const place2 = { id: s.id, y: lineY, x1, x2 };
3534
+ if (self) place2.loopY = lineY + SEQ.loopHeight;
3535
+ if (text !== null) {
3536
+ const x = self ? x1 + SEQ.textGap + 2 : (x1 + x2) / 2 - text.width / 2;
3537
+ place2.text = { x, y: lineY - SEQ.textGap - text.height, ...text };
3538
+ this.touch(place2.text);
3539
+ }
3540
+ if (this.number !== void 0) {
3541
+ const n = this.m.text(String(this.number), { fontSize: Math.max(8, this.m.font.fontSize - 2) });
3542
+ const d = Math.max(16, n.width + 6);
3543
+ place2.number = { n: this.number++, box: { x: x1 - d / 2, y: lineY - d / 2, width: d, height: d } };
3544
+ }
3545
+ this.out.messages.push(place2);
3546
+ this.touch(this.headRect(s.from));
3547
+ this.touch(this.headRect(s.to));
3548
+ if (self) this.touch({ x: x1, y: lineY, width: SEQ.loopWidth, height: 0 });
3549
+ if (s.deactivate !== void 0) this.close(s[s.deactivate], self ? place2.loopY : lineY, { message: s.id, flag: "deactivate" });
3550
+ for (const id of /* @__PURE__ */ new Set([s.from, s.to])) {
3551
+ if (!this.destroying.delete(id)) continue;
3552
+ const line = this.lifeline.get(id);
3553
+ if (line !== void 0) line.y1 = self ? place2.loopY : lineY;
3554
+ }
3555
+ this.y = place2.loopY ?? lineY;
3556
+ this.last = { y: this.y, ends: [s.from, s.to] };
3557
+ }
3558
+ note(s) {
3559
+ const over = s.over.filter((id) => this.index.has(id));
3560
+ if (over.length === 0) return;
3561
+ this.y += SEQ.rowGap;
3562
+ const t = this.m.text(s.text);
3563
+ const width = Math.max(SEQ.noteMinWidth, t.width + SEQ.notePad * 2);
3564
+ const height = t.height + SEQ.notePad * 2;
3565
+ const xs = over.map((id) => this.cx(id));
3566
+ const [lo, hi] = [Math.min(...xs), Math.max(...xs)];
3567
+ const x = s.side === "right" ? lo + SEQ.barWidth / 2 + SEQ.noteGap : s.side === "left" ? lo - SEQ.barWidth / 2 - SEQ.noteGap - width : lo === hi ? lo - width / 2 : Math.min(lo - SEQ.noteOverhang, (lo + hi) / 2 - width / 2);
3568
+ const box = { x, y: this.y, width: s.side === void 0 && lo !== hi ? Math.max(width, hi - lo + SEQ.noteOverhang * 2) : width, height };
3569
+ this.out.notes.push({ id: s.id, box });
3570
+ this.touch(box);
3571
+ for (const id of over) this.touch(this.headRect(id));
3572
+ this.y += height;
3573
+ this.last = null;
3574
+ }
3575
+ mark(s, at) {
3576
+ if (!this.index.has(s.participant)) return;
3577
+ this.touch(this.headRect(s.participant));
3578
+ const y = this.last?.ends.includes(s.participant) ? this.last.y : this.y + SEQ.rowGap / 2;
3579
+ if (s.kind === "activate") this.open(s.participant, y, { place: at });
3580
+ else if (s.kind === "deactivate") this.close(s.participant, y, { place: at });
3581
+ else if (s.kind === "destroy") {
3582
+ this.destroying.set(s.participant, this.last?.ends.includes(s.participant) ? this.last.y : null);
3583
+ const line = this.lifeline.get(s.participant);
3584
+ if (line !== void 0) line.destroyedBy = at;
3585
+ } else if (this.out.heads.every((h) => h.id !== s.participant)) {
3586
+ this.y = this.placeHead(s.participant, this.y + SEQ.rowGap, true);
3587
+ this.last = null;
3588
+ }
3589
+ }
3590
+ fragment(s) {
3591
+ this.y += SEQ.rowGap;
3592
+ const top = this.y;
3593
+ const tabText = s.type === "region" ? null : this.m.text(s.type, { bold: true });
3594
+ const tab = tabText === null ? null : { width: tabText.width + SEQ.tabPad * 2, height: tabText.height + SEQ.tabPad };
3595
+ const labels = s.sections.map((sec) => (sec.label ?? "") === "" ? null : this.m.text(sec.label));
3596
+ this.y += Math.max(tab?.height ?? 0, (labels[0]?.height ?? 0) + 4, 8);
3597
+ const frame = [];
3598
+ this.frames.push(frame);
3599
+ const sections = [];
3600
+ s.sections.forEach((sec, k) => {
3601
+ if (k > 0) {
3602
+ this.y += SEQ.rowGap / 2;
3603
+ sections.push({ y: this.y, height: 0 });
3604
+ this.y += (labels[k]?.height ?? 0) + 4;
3605
+ } else sections.push({ y: top, height: 0 });
3606
+ this.steps(sec.steps, { fragment: s.id, section: k });
3607
+ this.last = null;
3608
+ });
3609
+ this.frames.pop();
3610
+ this.y += SEQ.rowGap / 2;
3611
+ const reach = frame.length > 0 ? spanOf(frame) : { x0: this.cols.x[0] ?? 0, x1: this.cols.x.at(-1) ?? 0 };
3612
+ const minWidth = (tab?.width ?? 0) + (labels[0]?.width ?? 0) + SEQ.tabPad * 4;
3613
+ const x0 = reach.x0 - SEQ.fragmentPad / 2;
3614
+ const width = Math.max(reach.x1 - reach.x0 + SEQ.fragmentPad, minWidth);
3615
+ const box = { x: x0, y: top, width, height: this.y - top };
3616
+ const place2 = {
3617
+ id: s.id,
3618
+ type: s.type,
3619
+ box,
3620
+ sections: sections.map((sec, k) => {
3621
+ const l = labels[k];
3622
+ if (l === null || l === void 0) return { y: sec.y };
3623
+ const label = k === 0 ? { x: x0 + (tab?.width ?? 0) + SEQ.tabPad * 2, y: top + 2, ...l } : { x: x0 + width / 2 - l.width / 2, y: sec.y + 2, ...l };
3624
+ return { y: sec.y, label };
3625
+ })
3626
+ };
3627
+ if (tab !== null) place2.tab = { x: x0, y: top, ...tab };
3628
+ this.out.fragments.push(place2);
3629
+ this.touch({ x: box.x - SEQ.fragmentNest, y: top, width: box.width + SEQ.fragmentNest * 2, height: 0 });
3630
+ }
3631
+ };
3632
+ function flatMarks(steps, kind) {
3633
+ const out = [];
3634
+ for (const s of steps) {
3635
+ if (s.kind === kind) out.push(s.participant);
3636
+ if (s.kind === "fragment") for (const sec of s.sections) out.push(...flatMarks(sec.steps, kind));
3637
+ }
3638
+ return out;
3639
+ }
3640
+ function walkRows(data, m, cols, top) {
3641
+ const walk = new RowWalk(data, m, cols, top);
3642
+ walk.steps(data.steps, null);
3643
+ const bottom = walk.y + SEQ.rowGap;
3644
+ for (const [id, fallback] of walk.destroying) {
3645
+ const line = walk.lifeline.get(id);
3646
+ if (line !== void 0) line.y1 = fallback ?? bottom;
3647
+ }
3648
+ for (const [id, stack] of walk.bars) while (stack.length > 0) walk.close(id, bottom + SEQ.footer);
3649
+ walk.out.bottom = bottom;
3650
+ return walk.out;
3651
+ }
3652
+
3653
+ // src/sequence/layout.ts
3654
+ function sequenceLayout(data, opts = {}) {
3655
+ const m = seqMeasure(opts.measure, opts.font);
3656
+ const boxLabelHeight = Math.max(0, ...(data.boxes ?? []).map((b) => (b.label ?? "") === "" ? 0 : m.text(b.label).height + 4));
3657
+ const cols = columnsOf(data, m, SEQ.margin);
3658
+ const top = SEQ.margin + (data.boxes?.length ? SEQ.boxPad : 0) + boxLabelHeight;
3659
+ const rows = walkRows(data, m, cols, top);
3660
+ const columns = data.participants.map((p, i) => ({ id: p.id, x: cols.x[i] }));
3661
+ const foot = rows.bottom + SEQ.footer;
3662
+ const lifelines = rows.lifelines.map((l) => ({ ...l, y1: l.y1 ?? foot, destroyed: l.y1 !== null }));
3663
+ const boxes = (data.boxes ?? []).flatMap((b) => {
3664
+ const heads = rows.heads.filter((h) => b.members.includes(h.id)).map((h) => h.box);
3665
+ const around = unionRects(heads);
3666
+ if (around === null) return [];
3667
+ const label = (b.label ?? "") === "" ? null : m.text(b.label);
3668
+ const x = around.x - SEQ.boxPad;
3669
+ const width = around.width + SEQ.boxPad * 2;
3670
+ const y = SEQ.margin;
3671
+ const box = { x, y, width, height: foot + SEQ.boxPad - y };
3672
+ return [{ id: b.id, box, ...label ? { label: { x: x + (width - label.width) / 2, y: y + 2, ...label } } : {} }];
3673
+ });
3674
+ const all = unionRects([
3675
+ ...rows.heads.map((h) => h.box),
3676
+ ...rows.notes.map((n) => n.box),
3677
+ ...rows.fragments.map((f) => f.box),
3678
+ ...boxes.map((b) => b.box),
3679
+ ...rows.messages.flatMap((msg) => msg.text ? [msg.text] : []),
3680
+ { x: 0, y: 0, width: cols.right, height: foot }
3681
+ ]);
3682
+ return {
3683
+ width: Math.ceil(all.x + all.width + SEQ.margin),
3684
+ height: Math.ceil(Math.max(foot, all.y + all.height) + SEQ.margin),
3685
+ columns,
3686
+ heads: rows.heads,
3687
+ lifelines,
3688
+ messages: rows.messages,
3689
+ activations: rows.activations,
3690
+ notes: rows.notes,
3691
+ fragments: rows.fragments,
3692
+ boxes,
3693
+ slots: rows.slots
3694
+ };
3695
+ }
3696
+ var SEQUENCE_ROLES = { note: "warning", number: "accent" };
3697
+ var DASH = [6, 4];
3698
+ var LIFELINE_DASH = [4, 4];
3699
+ var MARKERS = { arrow: "arrow", open: "arrow-open", cross: null, none: null };
3700
+ var PartSpecs = class {
3701
+ constructor(root, at, font) {
3702
+ this.root = root;
3703
+ this.at = at;
3704
+ this.font = font;
3705
+ }
3706
+ root;
3707
+ at;
3708
+ font;
3709
+ specs = [];
3710
+ place(r) {
3711
+ return { x: this.at.x + r.x, y: this.at.y + r.y, width: r.width, height: r.height };
3712
+ }
3713
+ push(id, seq, r, data) {
3714
+ this.specs.push({
3715
+ id: `${this.root}/${id}`,
3716
+ kind: "mark",
3717
+ layer: "main",
3718
+ parent: this.root,
3719
+ pickable: false,
3720
+ pose: this.place(r),
3721
+ data: { ...data, diagram: { ...data.diagram, seq: { root: this.root, ...seq } } }
3722
+ });
3723
+ }
3724
+ body(id, part, of, r, outline, style, dash) {
3725
+ const lines = dash ?? style.dash;
3726
+ this.push(id, { part, ...of !== void 0 ? { id: of } : {} }, r, {
3727
+ diagram: { outline },
3728
+ fill: { paint: { color: style.fill } },
3729
+ stroke: { paint: { color: style.stroke }, width: style.strokeWidth ?? 1.5, ...lines !== void 0 && lines.length > 0 ? { dash: lines } : {} }
3730
+ });
3731
+ }
3732
+ line(id, part, of, r, d, stroke, fill) {
3733
+ this.push(id, { part, ...of !== void 0 ? { id: of } : {} }, r, {
3734
+ diagram: { mark: { d } },
3735
+ stroke,
3736
+ ...fill !== void 0 ? { fill: { paint: { color: fill } } } : {}
3737
+ });
3738
+ }
3739
+ /** The marks the part just pushed draws, onto its trait. */
3740
+ marks(refs) {
3741
+ const seq = this.specs.at(-1).data.diagram.seq;
3742
+ seq.marks = refs;
3743
+ }
3744
+ text(id, r, text, color, target, face = {}, centered = true) {
3745
+ const box = { ...r, x: r.x - 1, width: r.width + 2 };
3746
+ this.push(id, { part: "text", ...target ? { target } : {} }, box, {
3747
+ text,
3748
+ style: { ...this.font, ...centered ? { align: "center" } : {}, ...face },
3749
+ verticalAlign: "center",
3750
+ fill: { paint: { color } }
3751
+ });
3752
+ }
3753
+ };
3754
+ function participantSpecs(out, data, layout, theme) {
3755
+ for (const l of layout.lifelines) {
3756
+ const p = data.participants.find((q) => q.id === l.id);
3757
+ const style = withColors(roleStyle(theme, p.style), p.colors);
3758
+ out.line(
3759
+ `life:${l.id}`,
3760
+ "lifeline",
3761
+ l.id,
3762
+ { x: l.x - 1, y: l.y0, width: 2, height: Math.max(l.y1 - l.y0, 0) },
3763
+ "M0.5 0V1",
3764
+ { paint: { color: style.stroke }, width: 1, dash: LIFELINE_DASH }
3765
+ );
3766
+ if (l.destroyed) {
3767
+ const c = SEQ.cross;
3768
+ out.line(
3769
+ `destroy:${l.id}`,
3770
+ "destroy",
3771
+ l.id,
3772
+ { x: l.x - c / 2, y: l.y1 - c / 2, width: c, height: c },
3773
+ "M0 0L1 1M1 0L0 1",
3774
+ { paint: { color: style.stroke }, width: 2 }
3775
+ );
3776
+ if (l.destroyedBy) out.marks([{ place: l.destroyedBy }]);
3777
+ }
3778
+ }
3779
+ for (const [i, a] of layout.activations.entries()) {
3780
+ const p = data.participants.find((q) => q.id === a.participant);
3781
+ out.body(`act:${i}`, "activation", a.participant, a.box, "rect", withColors(roleStyle(theme, p.style), p.colors));
3782
+ out.marks(a.closer ? [a.opener, a.closer] : [a.opener]);
3783
+ }
3784
+ }
3785
+ function headSpecs(out, data, layout, theme) {
3786
+ for (const h of layout.heads) {
3787
+ const p = data.participants.find((q) => q.id === h.id);
3788
+ const style = withColors(roleStyle(theme, p.style), p.colors);
3789
+ const outline = p.outline ?? "rect";
3790
+ out.body(`head:${p.id}`, "head", p.id, h.box, outline, style);
3791
+ out.text(`head:${p.id}/text`, contentBox(outline, h.box), p.label ?? p.id, style.text, { participant: p.id });
3792
+ }
3793
+ }
3794
+ function messageSpecs(out, steps, layout, theme) {
3795
+ const byId = new Map(layout.messages.map((m) => [m.id, m]));
3796
+ const edge = roleEdgeStyle(theme);
3797
+ const width = edge.width ?? 1.5;
3798
+ for (const msg of flatSteps(steps)) {
3799
+ const at = msg.kind === "message" ? byId.get(msg.id) : void 0;
3800
+ if (at === void 0 || msg.kind !== "message") continue;
3801
+ const s = msg;
3802
+ const marker = MARKERS[msg.head];
3803
+ const stroke = {
3804
+ paint: { color: edge.stroke },
3805
+ width,
3806
+ ...msg.line === "dashed" ? { dash: DASH } : {},
3807
+ ...marker !== null ? { markerEnd: marker } : {},
3808
+ ...marker !== null && msg.both ? { markerStart: marker } : {}
3809
+ };
3810
+ if (at.loopY !== void 0) {
3811
+ out.line(`msg:${s.id}`, "message", s.id, { x: at.x1, y: at.y, width: SEQ.loopWidth, height: at.loopY - at.y }, "M0 0H1V1H0", stroke);
3812
+ } else {
3813
+ const d = at.x2 >= at.x1 ? "M0 0H1" : "M1 0H0";
3814
+ out.line(`msg:${s.id}`, "message", s.id, { x: Math.min(at.x1, at.x2), y: at.y, width: Math.abs(at.x2 - at.x1), height: 0 }, d, stroke);
3815
+ }
3816
+ if (msg.head === "cross") {
3817
+ const c = SEQ.cross * 0.8;
3818
+ const end = { x: at.x2, y: at.loopY ?? at.y };
3819
+ out.line(
3820
+ `msg:${s.id}/cross`,
3821
+ "message",
3822
+ s.id,
3823
+ { x: end.x - c / 2, y: end.y - c / 2, width: c, height: c },
3824
+ "M0 0L1 1M1 0L0 1",
3825
+ { paint: { color: edge.stroke }, width }
3826
+ );
3827
+ }
3828
+ if (at.text) out.text(`msg:${s.id}/text`, at.text, msg.text ?? "", edge.label ?? edge.stroke, { step: s.id }, {}, msg.from !== msg.to);
3829
+ if (at.number) {
3830
+ const style = roleStyle(theme, SEQUENCE_ROLES.number);
3831
+ out.body(`msg:${s.id}/number`, "number", s.id, at.number.box, "ellipse", style);
3832
+ out.text(`msg:${s.id}/number/text`, at.number.box, String(at.number.n), style.text, void 0, { fontSize: Math.max(8, out.font.fontSize - 2) });
3833
+ }
3834
+ }
3835
+ }
3836
+ function noteSpecs(out, steps, layout, theme) {
3837
+ const byId = new Map(layout.notes.map((n) => [n.id, n]));
3838
+ const style = roleStyle(theme, SEQUENCE_ROLES.note);
3839
+ for (const s of flatSteps(steps)) {
3840
+ const at = s.kind === "note" ? byId.get(s.id) : void 0;
3841
+ if (at === void 0 || s.kind !== "note") continue;
3842
+ out.body(`note:${s.id}`, "note", s.id, at.box, "note", style);
3843
+ out.text(`note:${s.id}/text`, contentBox("note", at.box), s.text, style.text, { step: s.id });
3844
+ }
3845
+ }
3846
+ function fragmentSpecs(out, steps, layout, theme, boxesOnly) {
3847
+ const byId = new Map(layout.fragments.map((f) => [f.id, f]));
3848
+ for (const s of flatSteps(steps)) {
3849
+ const at = s.kind === "fragment" ? byId.get(s.id) : void 0;
3850
+ if (at === void 0 || s.kind !== "fragment") continue;
3851
+ const style = withColors({ ...theme.container, ...s.type === "region" ? {} : { fill: "transparent" } }, s.colors);
3852
+ if (boxesOnly) {
3853
+ out.body(`frag:${s.id}`, "fragment", s.id, at.box, "rect", { ...style, strokeWidth: 1 }, []);
3854
+ continue;
3855
+ }
3856
+ at.sections.forEach((sec, k) => {
3857
+ if (k > 0) {
3858
+ out.line(
3859
+ `frag:${s.id}/divider:${k}`,
3860
+ "divider",
3861
+ s.id,
3862
+ { x: at.box.x, y: sec.y, width: at.box.width, height: 0 },
3863
+ "M0 0H1",
3864
+ { paint: { color: style.stroke }, width: 1, dash: DASH }
3865
+ );
3866
+ }
3867
+ if (sec.label) out.text(`frag:${s.id}/label:${k}`, sec.label, s.sections[k].label ?? "", style.text, { fragment: s.id, section: k });
3868
+ });
3869
+ if (at.tab) {
3870
+ out.line(`frag:${s.id}/tab`, "fragment", s.id, at.tab, "M0 0H1V0.6L0.85 1H0Z", { paint: { color: style.stroke }, width: 1 }, theme.container.fill);
3871
+ out.text(`frag:${s.id}/type`, at.tab, s.type, style.text, void 0, { fontWeight: 700 });
3872
+ }
3873
+ }
3874
+ }
3875
+ function boxSpecs(out, data, layout, theme) {
3876
+ for (const b of layout.boxes) {
3877
+ const own = data.boxes?.find((q) => q.id === b.id);
3878
+ const style = withColors(theme.container, own?.colors);
3879
+ out.body(`box:${b.id}`, "box", b.id, b.box, "rect", { ...style, strokeWidth: 1 }, []);
3880
+ if (b.label) out.text(`box:${b.id}/text`, b.label, own?.label ?? "", style.text, { box: b.id });
3881
+ }
3882
+ }
3883
+ var geometryOf = (layout) => ({
3884
+ columns: layout.columns,
3885
+ slots: layout.slots,
3886
+ messages: layout.messages,
3887
+ notes: layout.notes,
3888
+ heads: layout.heads.map(({ id, box }) => ({ id, box })),
3889
+ fragments: layout.fragments.map(({ id, box, tab }) => ({ id, grip: tab ?? { ...box, height: Math.min(box.height, SEQ.rowGap) } })),
3890
+ boxes: layout.boxes.map(({ id, box, label }) => ({ id, grip: { ...box, height: (label?.height ?? 0) + SEQ.boxPad } })),
3891
+ marks: [
3892
+ ...layout.activations.map((a, i) => ({ id: `act:${i}`, grip: a.box })),
3893
+ ...layout.lifelines.flatMap((l) => l.destroyedBy === void 0 ? [] : [{ id: `destroy:${l.id}`, grip: crossGrip(l.x, l.y1) }])
3894
+ ]
3895
+ });
3896
+ var crossGrip = (x, y) => {
3897
+ const c = Math.max(SEQ.cross, 12);
3898
+ return { x: x - c / 2, y: y - c / 2, width: c, height: c };
3899
+ };
3900
+ function sequenceSpecs(data, opts) {
3901
+ const theme = opts.theme ?? diagramThemeFrom(void 0);
3902
+ const font = opts.font ?? themeFont(theme);
3903
+ const layout = sequenceLayout(data, { font, ...opts.measure ? { measure: opts.measure } : {} });
3904
+ const at = opts.at ?? { x: 0, y: 0 };
3905
+ const out = new PartSpecs(opts.id, at, font);
3906
+ out.specs.push({
3907
+ id: opts.id,
3908
+ kind: "mark",
3909
+ layer: "main",
3910
+ pose: out.place({ x: 0, y: 0, width: layout.width, height: layout.height }),
3911
+ data: { diagram: { sequence: data, geometry: geometryOf(layout) } }
3912
+ });
3913
+ boxSpecs(out, data, layout, theme);
3914
+ fragmentSpecs(out, data.steps, layout, theme, true);
3915
+ participantSpecs(out, data, layout, theme);
3916
+ messageSpecs(out, data.steps, layout, theme);
3917
+ noteSpecs(out, data.steps, layout, theme);
3918
+ headSpecs(out, data, layout, theme);
3919
+ fragmentSpecs(out, data.steps, layout, theme, false);
3920
+ const layer = opts.layer ?? "main";
3921
+ return localizeWorldPoses(out.specs, opts.composition ?? RIGID_POSE_COMPOSITION).map((s) => ({ ...s, layer }));
3922
+ }
3923
+ var worldPose = (scene, id) => composeWorldPose(scenePoseAdapter(scene), id, scene.composition.compose);
3924
+ function rebuild(scene, id, data, opts) {
3925
+ const root = scene.get(id);
3926
+ const at = worldPose(scene, id);
3927
+ const [own, ...parts] = sequenceSpecs(data, {
3928
+ id,
3929
+ at,
3930
+ theme: opts.theme ?? diagramThemeOf(scene),
3931
+ layer: root.layer,
3932
+ composition: scene.composition,
3933
+ ...opts.measure ? { measure: opts.measure } : {}
3934
+ });
3935
+ const selected = scene.getSelection();
3936
+ scene.removeMany([...scene.childrenOf(id)]);
3937
+ const pose = root.pose;
3938
+ scene.update(id, { data: own.data });
3939
+ scene.setPose(id, { ...pose, width: own.pose.width, height: own.pose.height });
3940
+ for (const part of parts) scene.add(part);
3941
+ const kept = selected.filter((s) => scene.get(s) !== void 0);
3942
+ if (kept.length !== scene.getSelection().length) scene.setSelection(kept);
3943
+ }
3944
+ function commitSequence(scene, id, data, label, opts = {}) {
3945
+ const node = scene.get(id);
3946
+ if (node === void 0 || sequenceRootOf(node) === null) return;
3947
+ scene.batch(label, () => rebuild(scene, id, data, opts));
3948
+ }
3949
+ function startSequence(scene, at, data, label, opts = {}) {
3950
+ const id = scene.mintId();
3951
+ const layer = opts.layer ?? scene.layers[0]?.id ?? "main";
3952
+ const specs = sequenceSpecs(data, {
3953
+ id,
3954
+ at,
3955
+ theme: opts.theme ?? diagramThemeOf(scene),
3956
+ layer,
3957
+ composition: scene.composition,
3958
+ ...opts.measure ? { measure: opts.measure } : {}
3959
+ });
3960
+ scene.batch(label, () => {
3961
+ for (const spec of specs) scene.add(spec);
3962
+ });
3963
+ return id;
3964
+ }
3965
+ function redrawSequences(scene, opts = {}) {
3966
+ for (const node of scene.renderOrderNodes()) {
3967
+ const root = sequenceRootOf(node);
3968
+ if (root !== null) rebuild(scene, node.id, root.sequence, opts);
3969
+ }
3970
+ }
3971
+ function foldSequenceText(scene, textId, commit) {
3972
+ const node = scene.get(textId);
3973
+ const part = node === void 0 ? null : sequencePartOf(node);
3974
+ const root = part === null ? void 0 : scene.get(part.root);
3975
+ const held = root === void 0 ? null : sequenceRootOf(root);
3976
+ if (part === null || held === null || part.target === void 0) return;
3977
+ const text = node.data.text;
3978
+ if (typeof text !== "string") return;
3979
+ const next = setSequenceText(held.sequence, part.target, text);
3980
+ if (next === held.sequence) return;
3981
+ scene.undo();
3982
+ commit({ id: part.root, data: next, label: "Edit text" });
3983
+ }
3984
+ function sequenceCommitOf(opts) {
3985
+ return opts.commit ?? ((edit, ctx) => {
3986
+ const scene = ctx.deps.scene;
3987
+ if (scene !== void 0) commitSequence(scene, edit.id, edit.data, edit.label, opts);
3988
+ });
3989
+ }
3990
+
3991
+ // src/sequence/author.ts
3992
+ function idsOf(data) {
3993
+ const ids = new Set(data.participants.map((p) => p.id));
3994
+ for (const s of flatSteps(data.steps)) if (hasId(s)) ids.add(s.id);
3995
+ for (const b of data.boxes ?? []) ids.add(b.id);
3996
+ return ids;
3997
+ }
3998
+ var seqIdTaken = (data, id) => idsOf(data).has(id);
3999
+ function freshSeqId(data, prefix) {
4000
+ const ids = idsOf(data);
4001
+ let n = 1;
4002
+ while (ids.has(`${prefix}${n}`)) n++;
4003
+ return `${prefix}${n}`;
4004
+ }
4005
+ function newParticipant(data, kind = "participant") {
4006
+ const id = freshSeqId(data, kind === "actor" ? "A" : "P");
4007
+ const label = `${kind === "actor" ? "Actor" : "Participant"} ${id.slice(1)}`;
4008
+ return { id, label, ...kind === "actor" ? { outline: "actor" } : {} };
4009
+ }
4010
+ function addParticipant(data, p, index = data.participants.length) {
4011
+ if (seqIdTaken(data, p.id)) return data;
4012
+ const at = Math.max(0, Math.min(index, data.participants.length));
4013
+ return { ...data, participants: [...data.participants.slice(0, at), p, ...data.participants.slice(at)] };
4014
+ }
4015
+ function removeParticipant(data, id) {
4016
+ if (!data.participants.some((p) => p.id === id)) return data;
4017
+ const steps = pruneSteps(data.steps, (s) => {
4018
+ if (s.kind === "message") return s.from === id || s.to === id ? null : s;
4019
+ if (s.kind === "note") {
4020
+ if (!s.over.includes(id)) return s;
4021
+ const over = s.over.filter((q) => q !== id);
4022
+ return over.length === 0 ? null : { ...s, over };
4023
+ }
4024
+ if (s.kind === "fragment") return s;
4025
+ return s.participant === id ? null : s;
4026
+ });
4027
+ const boxes = data.boxes?.flatMap((b) => {
4028
+ if (!b.members.includes(id)) return [b];
4029
+ const members2 = b.members.filter((m) => m !== id);
4030
+ return members2.length === 0 ? [] : [{ ...b, members: members2 }];
4031
+ });
4032
+ const { boxes: _boxes, ...rest } = data;
4033
+ return {
4034
+ ...rest,
4035
+ participants: data.participants.filter((p) => p.id !== id),
4036
+ steps,
4037
+ ...boxes !== void 0 && boxes.length > 0 ? { boxes } : {}
4038
+ };
4039
+ }
4040
+ function setParticipantOutline(data, id, outline) {
4041
+ const at = data.participants.findIndex((p2) => p2.id === id);
4042
+ const p = data.participants[at];
4043
+ const next = outline === "rect" ? void 0 : outline;
4044
+ if (p === void 0 || p.outline === next) return data;
4045
+ const { outline: _outline, ...rest } = p;
4046
+ const q = next === void 0 ? rest : { ...rest, outline: next };
4047
+ return { ...data, participants: data.participants.map((r, i) => i === at ? q : r) };
4048
+ }
4049
+ function addBox(data, box) {
4050
+ const members2 = data.participants.filter((p) => box.members.includes(p.id));
4051
+ if (members2.length === 0 || seqIdTaken(data, box.id)) return data;
4052
+ const taken = new Set(members2.map((p) => p.id));
4053
+ const first = data.participants.findIndex((p) => taken.has(p.id));
4054
+ const before = data.participants.slice(0, first).filter((p) => !taken.has(p.id));
4055
+ const after = data.participants.slice(first).filter((p) => !taken.has(p.id));
4056
+ const others = (data.boxes ?? []).flatMap((b) => {
4057
+ const left = b.members.filter((m) => !taken.has(m));
4058
+ return left.length === 0 ? [] : [left.length === b.members.length ? b : { ...b, members: left }];
4059
+ });
4060
+ return {
4061
+ ...data,
4062
+ participants: [...before, ...members2, ...after],
4063
+ boxes: [...others, { ...box, members: members2.map((p) => p.id) }]
4064
+ };
4065
+ }
4066
+ function removeBox(data, id) {
4067
+ if (!data.boxes?.some((b) => b.id === id)) return data;
4068
+ const boxes = data.boxes.filter((b) => b.id !== id);
4069
+ const { boxes: _boxes, ...rest } = data;
4070
+ return boxes.length === 0 ? rest : { ...rest, boxes };
4071
+ }
4072
+ function setAutonumber(data, from) {
4073
+ if (data.autonumber === from) return data;
4074
+ const { autonumber: _n, ...rest } = data;
4075
+ return from === void 0 ? rest : { ...rest, autonumber: from };
4076
+ }
4077
+
4078
+ // src/sequence/authorSteps.ts
4079
+ var SEQ_ARROWS = {
4080
+ /** A call: a solid line, a filled head. */
4081
+ sync: { line: "solid", head: "arrow" },
4082
+ /** An answer: a dashed line, a filled head. */
4083
+ reply: { line: "dashed", head: "arrow" },
4084
+ /** Sent without waiting: a solid line, an open head. */
4085
+ async: { line: "solid", head: "open", async: true },
4086
+ /** Lost or refused: a solid line ending in a cross. */
4087
+ lost: { line: "solid", head: "cross" },
4088
+ /** A plain solid line. */
4089
+ line: { line: "solid", head: "none" },
4090
+ /** A plain dashed line. */
4091
+ dashed: { line: "dashed", head: "none" },
4092
+ /** Both ways: a solid line, filled heads at both ends. */
4093
+ both: { line: "solid", head: "arrow", both: true }
4094
+ };
4095
+ var SEQ_MULTI_SECTION = /* @__PURE__ */ new Set(["alt", "par", "critical"]);
4096
+ function insertStep(data, step, at) {
4097
+ const list = stepListOf(data.steps, at.parent);
4098
+ if (list === null || hasId(step) && seqIdTaken(data, step.id)) return data;
4099
+ const index = Math.max(0, Math.min(at.index, list.length));
4100
+ return { ...data, steps: withList(data.steps, at.parent, (l) => [...l.slice(0, index), step, ...l.slice(index)]) };
4101
+ }
4102
+ var known = (data, ...ids) => ids.every((id) => data.participants.some((p) => p.id === id));
4103
+ function insertMessage(data, m) {
4104
+ if (!known(data, m.from, m.to)) return data;
4105
+ const arrow = typeof m.arrow === "object" ? m.arrow : SEQ_ARROWS[m.arrow ?? "sync"];
4106
+ const step = {
4107
+ kind: "message",
4108
+ id: m.id ?? freshSeqId(data, "m"),
4109
+ from: m.from,
4110
+ to: m.to,
4111
+ ...arrow,
4112
+ ...m.text ? { text: m.text } : {}
4113
+ };
4114
+ return insertStep(data, step, m.at);
4115
+ }
4116
+ function insertNote(data, n) {
4117
+ if (n.over.length === 0 || !known(data, ...n.over)) return data;
4118
+ const step = {
4119
+ kind: "note",
4120
+ id: n.id ?? freshSeqId(data, "n"),
4121
+ over: n.side === void 0 ? [...n.over] : [n.over[0]],
4122
+ ...n.side ? { side: n.side } : {},
4123
+ text: n.text ?? "Note"
4124
+ };
4125
+ return insertStep(data, step, n.at);
4126
+ }
4127
+ function insertMark(data, mark, at) {
4128
+ if (!known(data, mark.participant)) return data;
4129
+ if (mark.kind === "create" && hasMark(data.steps, "create", mark.participant)) return data;
4130
+ return insertStep(data, { kind: mark.kind, participant: mark.participant }, at);
4131
+ }
4132
+ function hasMark(steps, kind, participant) {
4133
+ return steps.some((s) => s.kind === kind && s.participant === participant || s.kind === "fragment" && s.sections.some((sec) => hasMark(sec.steps, kind, participant)));
4134
+ }
4135
+ function removeStep(data, id) {
4136
+ if (findStep(data.steps, id) === null) return data;
4137
+ return { ...data, steps: pruneSteps(data.steps, (s) => hasId(s) && s.id === id ? null : s) };
4138
+ }
4139
+ function removeStepAt(data, at) {
4140
+ const list = stepListOf(data.steps, at.parent);
4141
+ if (list === null || at.index < 0 || at.index >= list.length) return data;
4142
+ return { ...data, steps: withList(data.steps, at.parent, (l) => l.filter((_, i) => i !== at.index)) };
4143
+ }
4144
+ function removeMarks(data, refs) {
4145
+ const key = (p) => p === null ? "" : `${p.fragment}/${p.section}`;
4146
+ const places = refs.flatMap((r) => "place" in r ? [r.place] : []).filter((p) => {
4147
+ const s = stepListOf(data.steps, p.parent)?.[p.index];
4148
+ return s !== void 0 && !hasId(s);
4149
+ }).sort((a, b) => key(a.parent) === key(b.parent) ? b.index - a.index : key(a.parent) < key(b.parent) ? -1 : 1);
4150
+ let next = places.reduce((d, p) => removeStepAt(d, p), data);
4151
+ for (const r of refs) if ("message" in r) next = setMessageActivation(next, r.message, { [r.flag]: void 0 });
4152
+ return next;
4153
+ }
4154
+ function removeMarksOf(data, kind, participant) {
4155
+ const steps = pruneSteps(data.steps, (s) => s.kind === kind && s.participant === participant ? null : s);
4156
+ return steps === data.steps ? data : { ...data, steps };
4157
+ }
4158
+ function wrapSteps(data, parent, start, end, f) {
4159
+ const list = stepListOf(data.steps, parent);
4160
+ const id = f.id ?? freshSeqId(data, "f");
4161
+ if (list === null || seqIdTaken(data, id)) return data;
4162
+ const lo = Math.max(0, Math.min(start, end, list.length));
4163
+ const hi = Math.max(lo, Math.min(Math.max(start, end), list.length));
4164
+ const frag = {
4165
+ kind: "fragment",
4166
+ id,
4167
+ type: f.type,
4168
+ sections: [{ ...f.label ? { label: f.label } : {}, steps: list.slice(lo, hi) }]
4169
+ };
4170
+ return { ...data, steps: withList(data.steps, parent, (l) => [...l.slice(0, lo), frag, ...l.slice(hi)]) };
4171
+ }
4172
+ function unwrapFragment(data, id) {
4173
+ const place2 = placeOf(data.steps, id);
4174
+ const frag = findStep(data.steps, id);
4175
+ if (place2 === null || frag?.kind !== "fragment") return data;
4176
+ const inner = frag.sections.flatMap((sec) => sec.steps);
4177
+ return { ...data, steps: withList(data.steps, place2.parent, (l) => [...l.slice(0, place2.index), ...inner, ...l.slice(place2.index + 1)]) };
4178
+ }
4179
+ function withFragment(data, id, fn) {
4180
+ const steps = mapSteps(data.steps, (s) => s.kind === "fragment" && s.id === id ? fn(s) : s);
4181
+ return steps === data.steps ? data : { ...data, steps };
4182
+ }
4183
+ function addSection(data, id, label, index) {
4184
+ return withFragment(data, id, (f) => {
4185
+ if (!SEQ_MULTI_SECTION.has(f.type)) return f;
4186
+ const at = Math.max(1, Math.min(index ?? f.sections.length, f.sections.length));
4187
+ const section = { ...label ? { label } : {}, steps: [] };
4188
+ return { ...f, sections: [...f.sections.slice(0, at), section, ...f.sections.slice(at)] };
4189
+ });
4190
+ }
4191
+ function removeSection(data, id, index) {
4192
+ return withFragment(data, id, (f) => f.sections.length < 2 || f.sections[index] === void 0 ? f : { ...f, sections: f.sections.filter((_, k) => k !== index) });
4193
+ }
4194
+ function setFragmentType(data, id, type) {
4195
+ return withFragment(data, id, (f) => f.type === type || f.sections.length > 1 && !SEQ_MULTI_SECTION.has(type) ? f : { ...f, type });
4196
+ }
4197
+ function withMessage(data, id, fn) {
4198
+ const steps = mapSteps(data.steps, (s) => s.kind === "message" && s.id === id ? fn(s) : s);
4199
+ return steps === data.steps ? data : { ...data, steps };
4200
+ }
4201
+ function setMessageArrow(data, id, arrow) {
4202
+ const next = typeof arrow === "object" ? arrow : SEQ_ARROWS[arrow];
4203
+ return withMessage(data, id, (m) => {
4204
+ if (m.line === next.line && m.head === next.head && m.both === next.both && m.async === next.async) return m;
4205
+ const { both: _both, async: _async, ...rest } = m;
4206
+ return { ...rest, line: next.line, head: next.head, ...next.both ? { both: true } : {}, ...next.async ? { async: true } : {} };
4207
+ });
4208
+ }
4209
+ function setMessageActivation(data, id, flags) {
4210
+ return withMessage(data, id, (m) => {
4211
+ const activate = "activate" in flags ? flags.activate : m.activate;
4212
+ const deactivate = "deactivate" in flags ? flags.deactivate : m.deactivate;
4213
+ if (activate === m.activate && deactivate === m.deactivate) return m;
4214
+ const { activate: _a, deactivate: _d, ...rest } = m;
4215
+ return { ...rest, ...activate ? { activate } : {}, ...deactivate ? { deactivate } : {} };
4216
+ });
4217
+ }
4218
+ var SEQUENCE_LAYER_ID = "diagram-sequence";
4219
+ var SEQUENCE_AFFORDANCE_KIND = `layer:${SEQUENCE_LAYER_ID}`;
4220
+ function sequenceScratchOf(hit) {
4221
+ const p = hit?.payload;
4222
+ return p !== void 0 && typeof p.root === "string" && typeof p.id === "string" && typeof p.part === "string" ? p : null;
4223
+ }
4224
+ function placedSequences(scene) {
4225
+ const out = [];
4226
+ const poses = scenePoseAdapter(scene);
4227
+ for (const node of scene.renderOrderNodes()) {
4228
+ const root = sequenceRootOf(node);
4229
+ if (root === null || scene.isLocked(node.id) || scene.isHidden(node.id)) continue;
4230
+ const at = composeWorldPose(poses, node.id, scene.composition.compose);
4231
+ out.push({ id: node.id, origin: { x: at.x, y: at.y }, width: at.width, height: at.height, geometry: root.geometry });
4232
+ }
4233
+ return out;
4234
+ }
4235
+ var REACH = 6;
4236
+ var END = 7;
4237
+ var moved = (r, o) => ({ x: r.x + o.x, y: r.y + o.y, width: r.width, height: r.height });
4238
+ function messageReach(m) {
4239
+ const x0 = Math.min(m.x1, m.x2);
4240
+ const x1 = m.loopY !== void 0 ? m.x1 + 32 : Math.max(m.x1, m.x2);
4241
+ const y1 = (m.loopY ?? m.y) + REACH;
4242
+ const top = Math.min(m.y - REACH, m.text?.y ?? Infinity);
4243
+ const left = Math.min(x0, m.text?.x ?? Infinity);
4244
+ const right = Math.max(x1, m.text ? m.text.x + m.text.width : -Infinity);
4245
+ return { x: left, y: top, width: right - left, height: y1 - top };
4246
+ }
4247
+ function region(id, r, scratch, cursor) {
4248
+ return {
4249
+ id,
4250
+ targetId: null,
4251
+ shape: { kind: "rect", x: r.x, y: r.y, width: r.width, height: r.height },
4252
+ cursor,
4253
+ strength: "exclusive",
4254
+ claimedKinds: ["pointer"],
4255
+ bind: () => ({ initialScratch: scratch })
4256
+ };
4257
+ }
4258
+ function regionsOf(all) {
4259
+ const parts = [];
4260
+ const ends = [];
4261
+ for (const { id: root, origin, geometry } of all) {
4262
+ const base = { targetId: root, root };
4263
+ for (const h of geometry.heads) parts.push(region(`${root}/head:${h.id}`, moved(h.box, origin), { ...base, part: "head", id: h.id }, "ew-resize"));
4264
+ for (const m of geometry.marks ?? []) parts.push(region(`${root}/${m.id}`, moved(m.grip, origin), { ...base, part: "mark", id: m.id }, "default"));
4265
+ for (const b of geometry.boxes ?? []) parts.push(region(`${root}/box:${b.id}`, moved(b.grip, origin), { ...base, part: "box", id: b.id }, "default"));
4266
+ for (const f of geometry.fragments ?? []) parts.push(region(`${root}/frag:${f.id}`, moved(f.grip, origin), { ...base, part: "step", id: f.id }, "ns-resize"));
4267
+ for (const n of geometry.notes) parts.push(region(`${root}/note:${n.id}`, moved(n.box, origin), { ...base, part: "step", id: n.id }, "ns-resize"));
4268
+ for (const m of geometry.messages) {
4269
+ parts.push(region(`${root}/msg:${m.id}`, moved(messageReach(m), origin), { ...base, part: "step", id: m.id }, "ns-resize"));
4270
+ const at = [["from", m.x1, m.y], ["to", m.x2, m.loopY ?? m.y]];
4271
+ for (const [end, x, y] of at) {
4272
+ const r = { x: x - END + origin.x, y: y - END + origin.y, width: END * 2, height: END * 2 };
4273
+ ends.push(region(`${root}/msg:${m.id}/${end}`, r, { ...base, part: "end", id: m.id, end }, "crosshair"));
4274
+ }
4275
+ }
4276
+ }
4277
+ return { parts, ends };
4278
+ }
4279
+ var INDEXES = /* @__PURE__ */ new WeakMap();
4280
+ function regionsIn(scene) {
4281
+ let index = INDEXES.get(scene);
4282
+ if (index === void 0) {
4283
+ index = keyedIndex(
4284
+ () => typeof scene.getVersion === "function" ? [scene.getVersion()] : void 0,
4285
+ () => regionsOf(placedSequences(scene))
4286
+ );
4287
+ INDEXES.set(scene, index);
4288
+ }
4289
+ return index() ?? regionsOf(placedSequences(scene));
4290
+ }
4291
+ function sequenceLayer(scene) {
4292
+ const parts = { id: SEQUENCE_LAYER_ID, regions: () => regionsIn(scene).parts };
4293
+ const ends = { id: `${SEQUENCE_LAYER_ID}-ends`, regions: () => regionsIn(scene).ends };
4294
+ return composeAffordanceLayer(SEQUENCE_LAYER_ID, "Sequence diagram parts", [parts, ends]);
4295
+ }
4296
+ var SELECT_SEQUENCE_PART_ACTION_ID = "diagram.sequence.select";
4297
+ function sequencePartNodeOf(scene, s) {
4298
+ if (s.part === "mark") return scene.get(`${s.root}/${s.id}`) === void 0 ? null : `${s.root}/${s.id}`;
4299
+ const names = s.part === "head" ? [`head:${s.id}`] : s.part === "box" ? [`box:${s.id}`] : s.part === "end" ? [`msg:${s.id}`] : [`msg:${s.id}`, `note:${s.id}`, `frag:${s.id}`];
4300
+ return names.map((n) => `${s.root}/${n}`).find((id) => scene.get(id) !== void 0) ?? null;
4301
+ }
4302
+ var selectSequencePartAction = {
4303
+ id: SELECT_SEQUENCE_PART_ACTION_ID,
4304
+ label: "Select sequence part",
4305
+ group: "diagram",
4306
+ requires: ["scene", "selection"],
4307
+ invoker: {
4308
+ timing: "immediate",
4309
+ run(deps, params) {
4310
+ const scene = deps.scene;
4311
+ const selection = deps.selection;
4312
+ const scratch = sequenceScratchOf(params?.affordance);
4313
+ const id = scene === void 0 || scratch === null ? null : sequencePartNodeOf(scene, scratch);
4314
+ if (id === null || selection === void 0) return;
4315
+ const held = selection.get();
4316
+ const shift = params?.mods?.shift === true;
4317
+ selection.set(!shift ? [id] : held.includes(id) ? held.filter((h) => h !== id) : [...held, id]);
4318
+ }
4319
+ },
4320
+ enabled: () => true
4321
+ };
4322
+ function removing(data, part) {
4323
+ if (part.part === "head" || part.part === "lifeline") return removeParticipant(data, part.id);
4324
+ if (part.part === "box") return removeBox(data, part.id);
4325
+ if (part.part === "message" || part.part === "note" || part.part === "fragment" || part.part === "divider") return removeStep(data, part.id);
4326
+ return data;
4327
+ }
4328
+ function sequenceDeletes(scene, ids) {
4329
+ const byRoot = /* @__PURE__ */ new Map();
4330
+ const rest = [];
4331
+ for (const id of ids) {
4332
+ const node = scene.get(id);
4333
+ const part = node === void 0 ? null : sequencePartOf(node);
4334
+ if (part === null) {
4335
+ rest.push(id);
4336
+ continue;
4337
+ }
4338
+ const root = scene.get(part.root);
4339
+ const held = root === void 0 ? null : sequenceRootOf(root);
4340
+ if (held === null) continue;
4341
+ const at = byRoot.get(part.root) ?? { was: held.sequence, marks: [], parts: [] };
4342
+ byRoot.set(part.root, at);
4343
+ if (part.marks !== void 0) at.marks.push(...part.marks);
4344
+ else if (part.id !== void 0) at.parts.push({ part: part.part, id: part.id });
4345
+ }
4346
+ const edits = [...byRoot].flatMap(([id, { was, marks, parts }]) => {
4347
+ const data = parts.reduce(removing, removeMarks(was, marks));
4348
+ return data === was ? [] : [{ id, data }];
4349
+ });
4350
+ return { edits, rest };
4351
+ }
4352
+ function createSequenceDeleteAction(opts = {}) {
4353
+ const commit = sequenceCommitOf(opts);
4354
+ const others = opts.others ?? commitDeleteToScene;
4355
+ return createDeleteAction({
4356
+ commit: (ids, deps) => {
4357
+ const scene = deps.scene;
4358
+ if (scene === void 0) return;
4359
+ const { edits, rest } = sequenceDeletes(scene, ids);
4360
+ for (const edit of edits) commit({ ...edit, label: "Delete" }, { deps });
4361
+ if (rest.length > 0) others(rest, deps);
4362
+ else deps.selection?.set([]);
4363
+ }
4364
+ });
4365
+ }
4366
+
4367
+ // src/sequence/actions.ts
4368
+ var SEQUENCE_DRAG_ACTION_ID = "diagram.sequence.drag";
4369
+ var GRAB_SEQUENCE_ACTION_ID = "diagram.sequence.grab";
4370
+ function carries(s, part) {
4371
+ if (s.part === "head") {
4372
+ if (part.target !== void 0) return "participant" in part.target && part.target.participant === s.id;
4373
+ return part.id === s.id && ["head", "lifeline", "destroy", "activation"].includes(part.part);
4374
+ }
4375
+ if (s.part === "step") {
4376
+ return part.id === s.id || part.target !== void 0 && ("step" in part.target && part.target.step === s.id || "fragment" in part.target && part.target.fragment === s.id);
4377
+ }
4378
+ return s.part === "end" && part.part === "message" && part.id === s.id;
4379
+ }
4380
+ function pressOf2(ctx) {
4381
+ const scratch = sequenceScratchOf(ctx.drag?.affordance);
4382
+ const scene = ctx.deps.scene;
4383
+ const node = scratch === null ? void 0 : scene?.get(scratch.root);
4384
+ const held = node === void 0 ? null : sequenceRootOf(node);
4385
+ const root = scene === void 0 || held === null ? void 0 : placedSequences(scene).find((p) => p.id === scratch.root);
4386
+ if (scratch === null || scene === void 0 || held === null || root === void 0) return null;
4387
+ const carried = /* @__PURE__ */ new Map();
4388
+ for (const id of scene.childrenOf(scratch.root)) {
4389
+ const child = scene.get(id);
4390
+ const part = sequencePartOf(child);
4391
+ if (part !== null && carries(scratch, part)) carried.set(id, child.pose);
4392
+ }
4393
+ return { scratch, scene, root, data: held.sequence, start: ctx.drag?.start ?? { x: ctx.world.x, y: ctx.world.y }, carried };
4394
+ }
4395
+ var nearest = (xs, x) => xs.reduce((best, v, i) => Math.abs(v - x) < Math.abs(xs[best] - x) ? i : best, 0);
4396
+ function proposed(p, at) {
4397
+ const { geometry, origin } = p.root;
4398
+ const local = { x: at.x - origin.x, y: at.y - origin.y };
4399
+ const { scratch } = p;
4400
+ if (scratch.part === "box" || scratch.part === "mark") return { data: p.data, label: "" };
4401
+ if (scratch.part === "head") {
4402
+ const index = geometry.columns.filter((c) => c.x < local.x).length;
4403
+ return { data: moveParticipant(p.data, scratch.id, index), label: "Move participant" };
4404
+ }
4405
+ if (scratch.part === "end") {
4406
+ const column = geometry.columns[nearest(geometry.columns.map((c) => c.x), local.x)];
4407
+ if (column === void 0) return { data: p.data, label: "" };
4408
+ return { data: reconnectMessage(p.data, scratch.id, scratch.end, column.id), label: "Reconnect message" };
4409
+ }
4410
+ const slot = geometry.slots[nearest(geometry.slots.map((s) => s.y), local.y)];
4411
+ if (slot === void 0) return { data: p.data, label: "" };
4412
+ return { data: moveStep(p.data, scratch.id, slot.place), label: "Move step" };
4413
+ }
4414
+ function previewOf(p, id, pose, at) {
4415
+ const dx = at.x - p.start.x;
4416
+ const dy = at.y - p.start.y;
4417
+ if (p.scratch.part === "head") return { ...pose, x: pose.x + dx };
4418
+ if (p.scratch.part === "step") return { ...pose, y: pose.y + dy };
4419
+ const m = p.root.geometry.messages.find((q) => q.id === p.scratch.id);
4420
+ if (m === void 0 || id !== `${p.scratch.root}/msg:${p.scratch.id}` || m.loopY !== void 0) return pose;
4421
+ const shift = pose.x - Math.min(m.x1, m.x2);
4422
+ const still = (p.scratch.end === "from" ? m.x2 : m.x1) + shift;
4423
+ const moving = (p.scratch.end === "from" ? m.x1 : m.x2) + shift + dx;
4424
+ return { ...pose, x: Math.min(still, moving), width: Math.abs(moving - still) };
4425
+ }
4426
+ function createSequenceDragAction(opts = {}) {
4427
+ const commit = sequenceCommitOf(opts);
4428
+ return {
4429
+ id: opts.id ?? SEQUENCE_DRAG_ACTION_ID,
4430
+ label: "Move sequence part",
4431
+ group: "diagram",
4432
+ requires: ["scene"],
4433
+ eligible: { mode: "normal" },
4434
+ invoker: {
4435
+ timing: "ongoing",
4436
+ start(ctx) {
4437
+ const p = pressOf2(ctx);
4438
+ if (p === null) return {};
4439
+ let at = null;
4440
+ let open = true;
4441
+ return {
4442
+ kind: "diagram-sequence-drag",
4443
+ onMove(move) {
4444
+ at = { x: move.world.x, y: move.world.y };
4445
+ },
4446
+ previewIds: () => open && at !== null ? [...p.carried.keys()] : null,
4447
+ previewPose: (id) => {
4448
+ const pose = p.carried.get(id);
4449
+ return open && at !== null && pose !== void 0 ? previewOf(p, id, pose, at) : null;
4450
+ },
4451
+ onEnd(end, reason) {
4452
+ open = false;
4453
+ if (reason !== "commit" || at === null) return;
4454
+ const next = proposed(p, { x: end.world.x, y: end.world.y });
4455
+ if (next.data !== p.data) commit({ id: p.scratch.root, data: next.data, label: next.label }, { ...end, deps: { ...end.deps, scene: p.scene } });
4456
+ }
4457
+ };
4458
+ }
4459
+ },
4460
+ enabled: () => true
4461
+ };
4462
+ }
4463
+ var grab = {
4464
+ id: GRAB_SEQUENCE_ACTION_ID,
4465
+ label: "Grab sequence part",
4466
+ group: "diagram",
4467
+ invoker: { timing: "immediate", run: () => {
4468
+ } },
4469
+ enabled: () => true
4470
+ };
4471
+ function diagramSequence(opts) {
4472
+ const actionId = opts.id ?? SEQUENCE_DRAG_ACTION_ID;
4473
+ const target = `affordance:${SEQUENCE_AFFORDANCE_KIND}`;
4474
+ return {
4475
+ layer: sequenceLayer(opts.scene),
4476
+ contribution: {
4477
+ id: "weasel-diagram-sequence",
4478
+ eligibility: { claimed: true },
4479
+ actions: [createSequenceDragAction({ ...opts, id: actionId }), grab, selectSequencePartAction],
4480
+ bindings: [
4481
+ { spec: { kind: "drag", target }, actionId, opts: { id: "weasel-diagram-sequence.drag" } },
4482
+ { spec: { kind: "pointerDown", target }, actionId: GRAB_SEQUENCE_ACTION_ID, opts: { id: "weasel-diagram-sequence.pointerDown" } },
4483
+ { spec: { kind: "click", target, mods: { shift: "optional" } }, actionId: SELECT_SEQUENCE_PART_ACTION_ID, opts: { id: "weasel-diagram-sequence.click" } }
4484
+ ]
4485
+ }
4486
+ };
4487
+ }
3301
4488
  function agrees(selection, want) {
3302
4489
  if (typeof want === "string" || want === null) return (selection.at(-1) ?? null) === want;
3303
4490
  return selection.length === want.length && want.every((id) => selection.includes(id));
@@ -3407,11 +4594,11 @@ function diagramLayoutOf(specs) {
3407
4594
  const id = s.id;
3408
4595
  if (id === void 0) continue;
3409
4596
  if (isBodyOrRegion(s)) {
3410
- const region = diagramContainerOf(s);
4597
+ const region2 = diagramContainerOf(s);
3411
4598
  const pinned = isPinned({ id, kind: s.kind, data: s.data });
3412
4599
  const { x, y, width, height } = world.get(id);
3413
- if (region !== null) {
3414
- if (!pinned && region.role !== "lane" && region.fitted !== true) {
4600
+ if (region2 !== null) {
4601
+ if (!pinned && region2.role !== "lane" && region2.fitted !== true) {
3415
4602
  containers.push({ kind: "box", id, box: { x, y, width, height } });
3416
4603
  }
3417
4604
  } else if (!pinned) moves.push({ id, x, y });
@@ -3439,8 +4626,8 @@ function changes(was, now) {
3439
4626
  for (const [id, after] of now.routes) {
3440
4627
  const before = was.routes.get(id);
3441
4628
  if (before === void 0) continue;
3442
- const moved = before.waypoints !== after.waypoints && !samePoints(before.waypoints, after.waypoints);
3443
- if (moved || !samePorts(before.ports, after.ports)) routes.push({ id, ...after });
4629
+ const moved2 = before.waypoints !== after.waypoints && !samePoints(before.waypoints, after.waypoints);
4630
+ if (moved2 || !samePorts(before.ports, after.ports)) routes.push({ id, ...after });
3444
4631
  }
3445
4632
  return { moves, routes, containers: regionChanges(was.regions, now.regions) };
3446
4633
  }
@@ -3449,11 +4636,11 @@ function useSceneSync(scene, specs, reports) {
3449
4636
  const syncing = useRef(false);
3450
4637
  const applied = useRef(specs);
3451
4638
  useEffect(() => {
3452
- let known = readScene(scene);
4639
+ let known2 = readScene(scene);
3453
4640
  return scene.subscribe(() => {
3454
4641
  const now = readScene(scene);
3455
- const found = syncing.current ? null : changes(known, now);
3456
- known = now;
4642
+ const found = syncing.current ? null : changes(known2, now);
4643
+ known2 = now;
3457
4644
  if (found === null) return;
3458
4645
  if (found.moves.length > 0 || found.routes.length > 0) reportsRef.current.onMove?.(found.moves, found.routes);
3459
4646
  for (const edit of found.containers) reportsRef.current.onEditContainer?.(edit);
@@ -3470,6 +4657,15 @@ function useSceneSync(scene, specs, reports) {
3470
4657
  applied.current = specs;
3471
4658
  }, [scene, specs]);
3472
4659
  }
4660
+ var isSequenceText = (data) => isTextData(data) && sequencePartOf({ data })?.target !== void 0;
4661
+ function useSequenceTextEdit(scene, report) {
4662
+ const latest = useLatest(report);
4663
+ const on = report !== void 0;
4664
+ return useMemo(() => on ? {
4665
+ isText: isSequenceText,
4666
+ onEnd: (id) => foldSequenceText(scene, id, (edit) => latest.current?.(edit))
4667
+ } : void 0, [on, scene, latest]);
4668
+ }
3473
4669
 
3474
4670
  // src/regionEditing.ts
3475
4671
  function laneResizeReports(scene, resize) {
@@ -3488,9 +4684,9 @@ function laneResizeReports(scene, resize) {
3488
4684
  const { x, y, width, height } = after(id);
3489
4685
  const was = before(id);
3490
4686
  if (x === was.x && y === was.y) continue;
3491
- const region = diagramContainerOf(node);
3492
- if (region !== null) {
3493
- if (region.role === "container" && region.fitted !== true) boxes.push({ kind: "box", id, box: { x, y, width, height } });
4687
+ const region2 = diagramContainerOf(node);
4688
+ if (region2 !== null) {
4689
+ if (region2.role === "container" && region2.fitted !== true) boxes.push({ kind: "box", id, box: { x, y, width, height } });
3494
4690
  } else if (isDiagramBody(node)) moves.push({ id, x, y });
3495
4691
  }
3496
4692
  }
@@ -3565,7 +4761,16 @@ function reportDeleteAction(report) {
3565
4761
  }
3566
4762
  });
3567
4763
  }
3568
- function reportResizeAction(report, region) {
4764
+ function sequenceDeleteAction(scene, edit, report) {
4765
+ return createSequenceDeleteAction({
4766
+ commit: edit,
4767
+ others: (ids) => {
4768
+ const removed = removedData(scene, ids);
4769
+ if (report !== null && (removed.nodes.length > 0 || removed.edges.length > 0)) report(removed);
4770
+ }
4771
+ });
4772
+ }
4773
+ function reportResizeAction(report, region2) {
3569
4774
  return createResizeAction({
3570
4775
  commit: (poses, deps) => {
3571
4776
  const picked = new Set(deps.selection?.get() ?? []);
@@ -3574,7 +4779,7 @@ function reportResizeAction(report, region) {
3574
4779
  if (!picked.has(id)) continue;
3575
4780
  const { x, y, width, height } = pose;
3576
4781
  const node = deps.scene?.get(id);
3577
- if (node !== void 0 && diagramContainerOf(node) !== null) region?.({ kind: "box", id, box: { x, y, width, height } });
4782
+ if (node !== void 0 && diagramContainerOf(node) !== null) region2?.({ kind: "box", id, box: { x, y, width, height } });
3578
4783
  else resizes.push({ id, x, y, width, height });
3579
4784
  }
3580
4785
  if (resizes.length > 0) report?.(resizes);
@@ -3601,6 +4806,7 @@ function useDiagramEditing(props) {
3601
4806
  const reorders = props.onReorder !== void 0;
3602
4807
  const resizes = props.onResize !== void 0;
3603
4808
  const regionEdits = props.onEditContainer !== void 0;
4809
+ const sequences = props.onEditSequence !== void 0;
3604
4810
  const ports = useMemo(() => mode === "connect" ? diagramPorts({
3605
4811
  ...portOptions,
3606
4812
  participants: source,
@@ -3648,24 +4854,28 @@ function useDiagramEditing(props) {
3648
4854
  [reorders, report]
3649
4855
  );
3650
4856
  const regions = useMemo(() => regionEdits ? regionEditing(scene, selected, report) : null, [regionEdits, scene, selected, report]);
4857
+ const sequence = useMemo(
4858
+ () => sequences ? diagramSequence({ scene, commit: (edit) => report.current.onEditSequence?.(edit) }) : null,
4859
+ [sequences, scene, report]
4860
+ );
3651
4861
  const actions = useMemo(() => ({
3652
4862
  ...NO_MARQUEE,
3653
- ...deletes ? { delete: reportDeleteAction((r) => report.current.onDelete?.(r)) } : {},
4863
+ ...sequences ? { delete: sequenceDeleteAction(scene, (edit) => report.current.onEditSequence?.(edit), deletes ? (r) => report.current.onDelete?.(r) : null) } : deletes ? { delete: reportDeleteAction((r) => report.current.onDelete?.(r)) } : {},
3654
4864
  ...resizes || regionEdits ? { resize: reportResizeAction((r) => report.current.onResize?.(r), (e) => report.current.onEditContainer?.(e)) } : {}
3655
- }), [deletes, resizes, regionEdits, report]);
4865
+ }), [deletes, sequences, scene, resizes, regionEdits, report]);
3656
4866
  const portsLayer = ports?.layer ?? shown;
3657
4867
  const layers = useMemo(
3658
- // Edge ends last: an edge's end sits on a port and beside a route handle, and the last layer is hit first.
3659
- () => [regions?.layer, labelDrag?.layer, portsLayer, route?.layer, ends?.layer].filter((l) => l != null),
3660
- [regions, labelDrag, portsLayer, route, ends]
4868
+ // Edge ends after ports and routes: an edge's end sits on a port and beside a route handle, and the last layer is hit first.
4869
+ () => [regions?.layer, labelDrag?.layer, portsLayer, route?.layer, ends?.layer, sequence?.layer].filter((l) => l != null),
4870
+ [regions, labelDrag, portsLayer, route, ends, sequence]
3661
4871
  );
3662
4872
  const extra = props.ambient;
3663
4873
  const ambient = useMemo(() => [
3664
4874
  ...AMBIENT,
3665
- ...[ports?.contribution, route?.contribution, ends?.contribution, labelDrag?.contribution, label, reorder].filter((c) => c != null),
4875
+ ...[ports?.contribution, route?.contribution, ends?.contribution, labelDrag?.contribution, label, reorder, sequence?.contribution].filter((c) => c != null),
3666
4876
  ...regions?.contributions ?? [],
3667
4877
  ...extra ?? []
3668
- ], [ports, route, ends, labelDrag, label, reorder, regions, extra]);
4878
+ ], [ports, route, ends, labelDrag, label, reorder, sequence, regions, extra]);
3669
4879
  return { layers, ambient, actions };
3670
4880
  }
3671
4881
  function usePagesView(pages) {
@@ -3688,15 +4898,7 @@ function diagramBounds(specs, extra) {
3688
4898
  for (const p of [...edge?.waypoints ?? [], ...edge?.route ?? []]) boxes.push({ x: p.x, y: p.y, width: 0, height: 0 });
3689
4899
  }
3690
4900
  if (extra !== void 0) boxes.push(extra);
3691
- if (boxes.length === 0) return null;
3692
- const x = Math.min(...boxes.map((b) => b.x));
3693
- const y = Math.min(...boxes.map((b) => b.y));
3694
- return {
3695
- x,
3696
- y,
3697
- width: Math.max(...boxes.map((b) => b.x + b.width)) - x,
3698
- height: Math.max(...boxes.map((b) => b.y + b.height)) - y
3699
- };
4901
+ return unionRects(boxes);
3700
4902
  }
3701
4903
  function fitDiagram(specs, size, fit = {}, extra) {
3702
4904
  const bounds = diagramBounds(specs, extra);
@@ -3716,9 +4918,8 @@ function diagramViewBounds(specs, padding, extra) {
3716
4918
  var IDENTITY = { x: 0, y: 0, scale: { x: 1, y: 1 } };
3717
4919
  var sameView = (a, b) => a.x === b.x && a.y === b.y && a.scale.x === b.scale.x && a.scale.y === b.scale.y;
3718
4920
  function inView(inner, view, size) {
3719
- const x1 = view.x + size.width / view.scale.x;
3720
- const y1 = view.y + size.height / view.scale.y;
3721
- return inner.x >= view.x && inner.y >= view.y && inner.x + inner.width <= x1 && inner.y + inner.height <= y1;
4921
+ const shown = viewportWorldRect(view, size);
4922
+ return inner.x >= shown.x && inner.y >= shown.y && inner.x + inner.width <= shown.x + shown.width && inner.y + inner.height <= shown.y + shown.height;
3722
4923
  }
3723
4924
  function useDiagramFit(args) {
3724
4925
  const { canvasRef, specs, controlled, size, fit, extra } = args;
@@ -3801,6 +5002,7 @@ function Inner({
3801
5002
  onDelete,
3802
5003
  onEditLabel,
3803
5004
  onMoveLabel,
5005
+ onEditSequence,
3804
5006
  onReorder,
3805
5007
  onEditContainer,
3806
5008
  connectBodies,
@@ -3810,6 +5012,8 @@ function Inner({
3810
5012
  pickEdges,
3811
5013
  overlays = NO_OVERLAYS,
3812
5014
  ambient: extraAmbient,
5015
+ tools,
5016
+ onToolsCreated,
3813
5017
  enableKeybindings,
3814
5018
  canConnect,
3815
5019
  portOptions,
@@ -3873,16 +5077,16 @@ function Inner({
3873
5077
  ...refit ? { refit } : {}
3874
5078
  });
3875
5079
  const { defaultView } = fitting;
3876
- const settled = useLatest((moved) => {
3877
- liveOpts?.onSettle?.(moved);
3878
- if (moved !== 0) fitting.settle();
5080
+ const settled = useLatest((moved2) => {
5081
+ liveOpts?.onSettle?.(moved2);
5082
+ if (moved2 !== 0) fitting.settle();
3879
5083
  });
3880
5084
  const live = useLiveLayout({
3881
5085
  ...liveOpts,
3882
5086
  scene,
3883
5087
  source,
3884
5088
  algorithm: run?.algorithm ?? "force",
3885
- onSettle: (moved) => settled.current(moved)
5089
+ onSettle: (moved2) => settled.current(moved2)
3886
5090
  });
3887
5091
  useEffect(() => {
3888
5092
  if (run !== null) live.restart();
@@ -3904,6 +5108,7 @@ function Inner({
3904
5108
  onResize,
3905
5109
  onEditContainer,
3906
5110
  onMove,
5111
+ onEditSequence,
3907
5112
  connectBodies,
3908
5113
  canConnect,
3909
5114
  portOptions,
@@ -3911,16 +5116,19 @@ function Inner({
3911
5116
  });
3912
5117
  const itemDrop = useItemDrop(source, { onDropItem, canDropItem });
3913
5118
  const ambient = useMemo(() => itemDrop === null ? editing.ambient : [...editing.ambient, itemDrop], [editing.ambient, itemDrop]);
3914
- useEffect(() => {
3915
- const canvas = canvasRef.current;
3916
- if (canvas === null) return;
3917
- const disposers = editing.layers.map((layer) => canvas.registerLayer(layer));
3918
- return () => disposers.forEach((dispose) => dispose());
3919
- }, [editing.layers]);
5119
+ useRegisteredLayers(canvasRef, editing.layers);
3920
5120
  const moves = onMove !== void 0 || onEditContainer !== void 0;
3921
5121
  const resizes = onResize !== void 0 || onEditContainer !== void 0;
3922
- const edits = moves || onConnect !== void 0 || onReorder !== void 0 || resizes || edgesPick;
3923
- const features = useMemo(() => ["view", "pick", ...moves ? ["move"] : [], ...resizes ? ["resize"] : []], [moves, resizes]);
5122
+ const sequences = onEditSequence !== void 0;
5123
+ const edits = moves || onConnect !== void 0 || onReorder !== void 0 || resizes || edgesPick || sequences;
5124
+ const features = useMemo(() => [
5125
+ "view",
5126
+ "pick",
5127
+ ...moves ? ["move"] : [],
5128
+ ...resizes ? ["resize"] : [],
5129
+ ...sequences ? ["edit"] : []
5130
+ ], [moves, resizes, sequences]);
5131
+ const textEditor = useSequenceTextEdit(scene, onEditSequence);
3924
5132
  const selectTool = useMemo(() => ({ resize: { resizable: diagramResizable(scene) } }), [scene]);
3925
5133
  const overlayRef = useOverlayPlacement(canvasRef, scene, overlays);
3926
5134
  const fillsOn = [width === void 0 ? "fill-x" : "", height === void 0 ? "fill-y" : ""].join(" ").trim();
@@ -3933,9 +5141,12 @@ function Inner({
3933
5141
  selection,
3934
5142
  features,
3935
5143
  ...resizes ? { selectTool } : {},
5144
+ ...textEditor ? { textEditor } : {},
3936
5145
  enableKeybindings: enableKeybindings ?? edits,
3937
5146
  ambient,
3938
5147
  actions: editing.actions,
5148
+ ...tools ? { tools } : {},
5149
+ ...onToolsCreated ? { onToolsCreated } : {},
3939
5150
  onClick,
3940
5151
  ...onHover ? { onHover } : {},
3941
5152
  width,
@@ -3957,6 +5168,14 @@ function Inner({
3957
5168
  ] });
3958
5169
  }
3959
5170
  var NO_OVERLAYS = [];
5171
+ function useRegisteredLayers(canvasRef, layers) {
5172
+ useEffect(() => {
5173
+ const canvas = canvasRef.current;
5174
+ if (canvas === null) return;
5175
+ const disposers = layers.map((layer) => canvas.registerLayer(layer));
5176
+ return () => disposers.forEach((dispose) => dispose());
5177
+ }, [canvasRef, layers]);
5178
+ }
3960
5179
  function DropRules({ scene, canReparent }) {
3961
5180
  const latest = useLatest(canReparent);
3962
5181
  const asks = canReparent !== void 0;
@@ -3989,7 +5208,7 @@ function useDiagramApi(apiRef, canvasRef, scene, live, setRun, fitNow) {
3989
5208
  const world = worldRectOf(scene, id);
3990
5209
  const r = canvas?.element?.getBoundingClientRect();
3991
5210
  if (canvas == null || world === null || r === void 0) return null;
3992
- const rect = screenRectOf(world, canvas.getView());
5211
+ const rect = viewRectToScreen(canvas.getView(), world);
3993
5212
  return { ...rect, x: rect.x + r.left, y: rect.y + r.top };
3994
5213
  },
3995
5214
  subscribeFrame: (fn) => canvasRef.current?.subscribeFrame(() => fn()) ?? (() => {
@@ -3997,6 +5216,6 @@ function useDiagramApi(apiRef, canvasRef, scene, live, setRun, fitNow) {
3997
5216
  }), [canvasRef, live, scene, setRun, fitNow]);
3998
5217
  }
3999
5218
 
4000
- export { ADD_LANE_ACTION_ID, ANYWHERE, CONNECT_ACTION_ID, CONTAINERS_GROUP, CONTAINER_STENCILS, DEFAULT_EDGE_STROKE, DIAGRAM_ITEM_CARRY, DiagramView, EASED_LAYOUTS, EDGE_END_AFFORDANCE_KIND, EDGE_END_LAYER_ID, EDIT_CONTAINER_TITLE_ACTION_ID, GRAB_EDGE_END_ACTION_ID, GRAB_LABEL_ACTION_ID, GRAB_PORT_ACTION_ID, GRAB_WAYPOINT_ACTION_ID, ITEM_DROP_ACTION_ID, LANE_AFFORDANCE_KIND, LANE_LAYER_ID, LANE_RESIZE_ACTION_ID, LAYOUTS, LAYOUT_ACTION_ID, MOVE_LABEL_ACTION_ID, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, RECONNECT_ACTION_ID, RELEASE_MEMBERS_ACTION_ID, REMOVE_WAYPOINT_ACTION_ID, REORDER_ACTION_ID, RESET_LABEL_ACTION_ID, RESET_ROUTE_ACTION_ID, ROUTER_LABELS, ROUTE_EDIT_ACTION_ID, SELECT_LABEL_EDGE_ACTION_ID, SET_HOPS_ACTION_ID, SET_ROUTER_ACTION_IDS, STEP_REORDER_ACTION_ID, WAYPOINT_AFFORDANCE_KIND, WAYPOINT_HIT_PX, WAYPOINT_LAYER_ID, addLane, applyLayout, avoidSceneInput, bodyAt, commitEdgeToScene, commitLabelToScene, commitLaneResizeToScene, commitReconnectToScene, commitRouteToScene, commitRouterToScene, connectBinding, containerBuild, createAddLaneAction, createConnectAction, createDiagramContribution, createEdgeEndAffordance, createItemDropAction, createLabelDragAction, createLaneAffordance, createLaneResizeAction, createLayoutAction, createPortAffordance, createReconnectAction, createReleaseMembersAction, createRemoveWaypointAction, createReorderAction, createResetLabelAction, createResetRouteAction, createRouteEditAction, createSetHopsAction, createSetRouterAction, createSpecHops, createSpecLabelPlacer, createSpecRouter, createStepReorderAction, createWaypointAffordance, defaultCanConnect, diagramCanDropInto, diagramContainers, diagramDropParentAt, diagramEdgeEnds, diagramLabelDrag, diagramLayoutOf, diagramPorts, diagramResizable, diagramShape, diagramWaypoints, dragItemProps, dragSegment, dragVertex, easedProducer, edgeEndLayer, edgeEndScratchOf, edgePolylines, editContainerTitleAction, editContainerTitleBinding, forceProducer, grabPortAction, grabPortBindings, groupReorderContribution, groupReorderSlots, headerIdOf, hopDataOps, inOrUnder, insertWaypoint, isBodyEnd, isBodyOrRegion, isDiagramBody, itemDropContribution, laneLayer, laneResizeFrame, laneScratchOf, lanesOf, layoutDiagram, layoutPoses, movedLabelData, pinSegment, poolAxisOf, poolsOf, portLayer, portScratchOf, reconnectBinding, regionHeaderAt, registerDiagramShape, releaseMembers, removeSegment, removeWaypoint, reorderBinding, reorderContribution, routeHandles, routeStateOf, routeWrites, routedData, routerActions, routesOf, sceneEdges, sceneParticipants, segmentAxis, sequenceSlots, startItemDrag, themeWrites, unpinSegment, useLiveLayout, useMirroredSelection, waypointLayer, waypointScratchOf, withHopsData, withRouter, worldWith };
4001
- //# sourceMappingURL=chunk-GR2DK4S2.js.map
4002
- //# sourceMappingURL=chunk-GR2DK4S2.js.map
5219
+ export { ADD_LANE_ACTION_ID, ANYWHERE, CONNECT_ACTION_ID, CONTAINERS_GROUP, CONTAINER_STENCILS, DEFAULT_EDGE_STROKE, DIAGRAM_ITEM_CARRY, DiagramView, EASED_LAYOUTS, EDGE_END_AFFORDANCE_KIND, EDGE_END_LAYER_ID, EDIT_CONTAINER_TITLE_ACTION_ID, GRAB_EDGE_END_ACTION_ID, GRAB_LABEL_ACTION_ID, GRAB_PORT_ACTION_ID, GRAB_SEQUENCE_ACTION_ID, GRAB_WAYPOINT_ACTION_ID, ITEM_DROP_ACTION_ID, LANE_AFFORDANCE_KIND, LANE_LAYER_ID, LANE_RESIZE_ACTION_ID, MOVE_LABEL_ACTION_ID, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, RECONNECT_ACTION_ID, RELEASE_MEMBERS_ACTION_ID, REMOVE_WAYPOINT_ACTION_ID, REORDER_ACTION_ID, RESET_LABEL_ACTION_ID, RESET_ROUTE_ACTION_ID, ROUTER_LABELS, ROUTE_EDIT_ACTION_ID, SELECT_LABEL_EDGE_ACTION_ID, SELECT_SEQUENCE_PART_ACTION_ID, SEQ, SEQUENCE_AFFORDANCE_KIND, SEQUENCE_DRAG_ACTION_ID, SEQUENCE_LAYER_ID, SEQUENCE_ROLES, SEQ_ARROWS, SEQ_MULTI_SECTION, SET_HOPS_ACTION_ID, SET_ROUTER_ACTION_IDS, STEP_REORDER_ACTION_ID, WAYPOINT_AFFORDANCE_KIND, WAYPOINT_HIT_PX, WAYPOINT_LAYER_ID, addBox, addLane, addParticipant, addSection, avoidSceneInput, bodyAt, commitEdgeToScene, commitLabelToScene, commitLaneResizeToScene, commitReconnectToScene, commitRouteToScene, commitRouterToScene, commitSequence, connectBinding, containerBuild, createAddLaneAction, createConnectAction, createDiagramContribution, createEdgeEndAffordance, createItemDropAction, createLabelDragAction, createLaneAffordance, createLaneResizeAction, createPortAffordance, createReconnectAction, createReleaseMembersAction, createRemoveWaypointAction, createReorderAction, createResetLabelAction, createResetRouteAction, createRouteEditAction, createSequenceDeleteAction, createSequenceDragAction, createSetHopsAction, createSetRouterAction, createSpecHops, createSpecLabelPlacer, createSpecRouter, createStepReorderAction, createWaypointAffordance, defaultCanConnect, diagramCanDropInto, diagramContainers, diagramDropParentAt, diagramEdgeEnds, diagramLabelDrag, diagramLayoutOf, diagramPorts, diagramResizable, diagramSequence, diagramShape, diagramWaypoints, dragItemProps, dragSegment, dragVertex, easedProducer, edgeEndLayer, edgeEndScratchOf, edgePolylines, editContainerTitleAction, editContainerTitleBinding, findStep, foldSequenceText, forceProducer, freshSeqId, grabPortAction, grabPortBindings, groupReorderContribution, groupReorderSlots, headerIdOf, hopDataOps, inOrUnder, insertMark, insertMessage, insertNote, insertStep, insertWaypoint, isBodyEnd, isBodyOrRegion, isDiagramBody, isSequenceText, itemDropContribution, laneLayer, laneResizeFrame, laneScratchOf, lanesOf, moveParticipant, moveStep, movedLabelData, newParticipant, pinSegment, placeOf, placedSequences, poolAxisOf, poolsOf, portLayer, portScratchOf, reconnectBinding, reconnectMessage, redrawSequences, regionHeaderAt, registerDiagramShape, releaseMembers, removeBox, removeMarks, removeMarksOf, removeParticipant, removeSection, removeSegment, removeStep, removeStepAt, removeWaypoint, reorderBinding, reorderContribution, routeHandles, routeStateOf, routedData, routerActions, sameList, sceneEdges, sceneParticipants, segmentAxis, selectSequencePartAction, seqIdTaken, sequenceCommitOf, sequenceDeletes, sequenceLayout, sequencePartNodeOf, sequencePartOf, sequenceRootOf, sequenceScratchOf, sequenceSlots, sequenceSpecs, sequenceTextOf, setAutonumber, setFragmentType, setMessageActivation, setMessageArrow, setParticipantOutline, setSequenceText, startItemDrag, startSequence, stepListOf, themeWrites, unpinSegment, unwrapFragment, useLiveLayout, useMirroredSelection, useSequenceTextEdit, waypointLayer, waypointScratchOf, withHopsData, withRouter, worldWith, wrapSteps };
5220
+ //# sourceMappingURL=chunk-6YQSQXQT.js.map
5221
+ //# sourceMappingURL=chunk-6YQSQXQT.js.map