@diagrammo/dgmo 0.84.0 → 0.85.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1034,8 +1034,19 @@ declare function isSequenceNote(el: SequenceElement): el is SequenceNote;
1034
1034
  */
1035
1035
  interface SequenceGroup {
1036
1036
  readonly name: string;
1037
+ /**
1038
+ * Every participant under this group, nested ones included, in source
1039
+ * order. Transitive so a frame — outer or inner — can size itself from one
1040
+ * list; `parent` is what distinguishes the two. Members of a nested group
1041
+ * are contiguous inside their parent's list because the source order that
1042
+ * built it is the order they were written in.
1043
+ */
1037
1044
  readonly participantIds: readonly ParticipantId[];
1038
1045
  readonly lineNumber: number;
1046
+ /** Name of the enclosing group, when this one is indented inside another. */
1047
+ readonly parent?: string;
1048
+ /** 0 for a top-level group, 1 for one nested inside it. Capped at MAX_GROUP_DEPTH - 1. */
1049
+ readonly depth: number;
1039
1050
  /** Pipe-delimited tag metadata (e.g. `[Backend | t: Product]`) */
1040
1051
  readonly metadata?: Readonly<Record<string, string>>;
1041
1052
  /** Whether this group is collapsed by default */
package/dist/advanced.js CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  computeActivations,
17
17
  groupMessagesBySection,
18
18
  renderSequenceDiagram
19
- } from "./chunk-AYA6C64M.js";
19
+ } from "./chunk-NJS62BAG.js";
20
20
  import {
21
21
  renderRing,
22
22
  renderRingForExport
@@ -320,8 +320,8 @@ import {
320
320
  renderSlopeChart,
321
321
  renderVenn,
322
322
  renderWordCloud
323
- } from "./chunk-QZRYCRT7.js";
324
- import "./chunk-VKGQX6P2.js";
323
+ } from "./chunk-ADAGWVWJ.js";
324
+ import "./chunk-OZTDXMRQ.js";
325
325
  import {
326
326
  CHART_TYPE_DESCRIPTIONS,
327
327
  chartTypeParsers,
@@ -331,7 +331,7 @@ import {
331
331
  knownChartTypeIds,
332
332
  parseDgmo,
333
333
  parseVisualization
334
- } from "./chunk-KABGWUND.js";
334
+ } from "./chunk-T327J6CE.js";
335
335
  import "./chunk-EOYPX6SF.js";
336
336
  import {
337
337
  normalizePlace,
@@ -527,7 +527,7 @@ import {
527
527
  } from "./chunk-RE27AX7O.js";
528
528
  import {
529
529
  parseDgmoChartType
530
- } from "./chunk-ECIXBEG2.js";
530
+ } from "./chunk-JVRDKR5R.js";
531
531
  import {
532
532
  allTasks,
533
533
  parseRaci
@@ -548,7 +548,7 @@ import {
548
548
  isSequenceNote,
549
549
  looksLikeSequence,
550
550
  parseSequenceDgmo
551
- } from "./chunk-6EV2F6W7.js";
551
+ } from "./chunk-VPASQOR3.js";
552
552
  import {
553
553
  RULE_COUNT,
554
554
  inferParticipantType
package/dist/block.js CHANGED
@@ -4,12 +4,12 @@ import {
4
4
  } from "./chunk-D3K4U3ZM.js";
5
5
  import {
6
6
  render
7
- } from "./chunk-QZRYCRT7.js";
7
+ } from "./chunk-ADAGWVWJ.js";
8
8
  import {
9
9
  docsLink,
10
10
  renderErrorCard
11
- } from "./chunk-VKGQX6P2.js";
12
- import "./chunk-KABGWUND.js";
11
+ } from "./chunk-OZTDXMRQ.js";
12
+ import "./chunk-T327J6CE.js";
13
13
  import "./chunk-EOYPX6SF.js";
14
14
  import "./chunk-WP2RLGDI.js";
15
15
  import "./chunk-A2RUBBVT.js";
@@ -68,7 +68,7 @@ import "./chunk-KAO7JL4I.js";
68
68
  import {
69
69
  encodeDiagramUrl
70
70
  } from "./chunk-RE27AX7O.js";
71
- import "./chunk-ECIXBEG2.js";
71
+ import "./chunk-JVRDKR5R.js";
72
72
  import "./chunk-3RYBTEF5.js";
73
73
  import "./chunk-UNNJCSSQ.js";
74
74
  import "./chunk-3TYUXC7D.js";
@@ -77,7 +77,7 @@ import "./chunk-EQW6HOP4.js";
77
77
  import {
78
78
  scanMonthNameDates
79
79
  } from "./chunk-DAOFNU6J.js";
80
- import "./chunk-6EV2F6W7.js";
80
+ import "./chunk-VPASQOR3.js";
81
81
  import "./chunk-C3Z4DX3V.js";
82
82
  import "./chunk-JRUEIRJB.js";
83
83
  import "./chunk-7TF3QOOD.js";
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-RE27AX7O.js";
11
11
  import {
12
12
  parseDgmoChartType
13
- } from "./chunk-ECIXBEG2.js";
13
+ } from "./chunk-JVRDKR5R.js";
14
14
  import {
15
15
  parseRaci
16
16
  } from "./chunk-3RYBTEF5.js";
@@ -19,7 +19,7 @@ import "./chunk-3TYUXC7D.js";
19
19
  import "./chunk-HMWZQ53A.js";
20
20
  import "./chunk-EQW6HOP4.js";
21
21
  import "./chunk-DAOFNU6J.js";
22
- import "./chunk-6EV2F6W7.js";
22
+ import "./chunk-VPASQOR3.js";
23
23
  import "./chunk-C3Z4DX3V.js";
24
24
  import "./chunk-JRUEIRJB.js";
25
25
  import "./chunk-7TF3QOOD.js";
@@ -8,7 +8,7 @@ import {
8
8
  parseVenn,
9
9
  parseVisualization,
10
10
  parseWordcloud
11
- } from "./chunk-KABGWUND.js";
11
+ } from "./chunk-T327J6CE.js";
12
12
  import {
13
13
  MAP_DX
14
14
  } from "./chunk-EOYPX6SF.js";
@@ -1951,7 +1951,7 @@ function resolveArcChordOverride(content, detectedType, palette) {
1951
1951
  }
1952
1952
  async function renderForExport(content, theme, palette, viewState, options) {
1953
1953
  const exportMode = options?.exportMode ?? false;
1954
- const { parseDgmoChartType } = await import("./dgmo-router-YW4SBNYB.js");
1954
+ const { parseDgmoChartType } = await import("./dgmo-router-VILEOWQ2.js");
1955
1955
  const detectedTypeRaw = parseDgmoChartType(content);
1956
1956
  const override = resolveArcChordOverride(content, detectedTypeRaw, palette);
1957
1957
  const renderContent = override?.content ?? content;
@@ -2877,7 +2877,7 @@ async function exportTreemap(ctx) {
2877
2877
  async function exportLiveLink(ctx) {
2878
2878
  const { content, theme, palette } = ctx;
2879
2879
  const { parseLiveLink } = await import("./parser-6TXNMXNB.js");
2880
- const { renderLiveLinkCard } = await import("./renderer-QZW4RCZW.js");
2880
+ const { renderLiveLinkCard } = await import("./renderer-5A6VWIVX.js");
2881
2881
  const effectivePalette = await resolveExportPalette(theme, palette);
2882
2882
  return renderLiveLinkCard(parseLiveLink(content), effectivePalette, theme);
2883
2883
  }
@@ -3087,8 +3087,8 @@ async function exportVisualization(ctx) {
3087
3087
  const effectivePalette = await resolveExportPalette(theme, palette);
3088
3088
  const isDark = ctx.isDark;
3089
3089
  const container = createExportContainer(ctx.width, ctx.height);
3090
- const { parseSequenceDgmo } = await import("./parser-6PHQJO42.js");
3091
- const { renderSequenceDiagram } = await import("./renderer-GWNSV76V.js");
3090
+ const { parseSequenceDgmo } = await import("./parser-PVIWHVGY.js");
3091
+ const { renderSequenceDiagram } = await import("./renderer-DRLLZZ6P.js");
3092
3092
  const seqParsed = parseSequenceDgmo(content, effectivePalette);
3093
3093
  if (seqParsed.error || seqParsed.participants.length === 0) return "";
3094
3094
  const collapsedSections = viewState?.cs ? new Set(viewState.cs) : void 0;
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-HMWZQ53A.js";
7
7
  import {
8
8
  looksLikeSequence
9
- } from "./chunk-6EV2F6W7.js";
9
+ } from "./chunk-VPASQOR3.js";
10
10
  import {
11
11
  looksLikeFlowchart
12
12
  } from "./chunk-JRUEIRJB.js";
@@ -43,7 +43,7 @@ import {
43
43
  isSequenceBlock,
44
44
  isSequenceNote,
45
45
  isSequenceSection
46
- } from "./chunk-6EV2F6W7.js";
46
+ } from "./chunk-VPASQOR3.js";
47
47
  import {
48
48
  resolveActiveTagGroup,
49
49
  tagAttrKey
@@ -74,12 +74,28 @@ function applyCollapseProjection(parsed, collapsedGroups) {
74
74
  collapsedGroupIds: /* @__PURE__ */ new Map()
75
75
  };
76
76
  }
77
+ const byName = new Map(parsed.groups.map((g) => [g.name, g]));
78
+ const insideCollapsed = (group) => {
79
+ let parentName = group.parent;
80
+ const seen = /* @__PURE__ */ new Set();
81
+ while (parentName !== void 0 && !seen.has(parentName)) {
82
+ seen.add(parentName);
83
+ const ancestor = byName.get(parentName);
84
+ if (!ancestor) return false;
85
+ if (collapsedGroups.has(ancestor.lineNumber)) return true;
86
+ parentName = ancestor.parent;
87
+ }
88
+ return false;
89
+ };
77
90
  const memberToGroup = /* @__PURE__ */ new Map();
78
91
  const collapsedGroupNames = /* @__PURE__ */ new Set();
79
- for (const group of parsed.groups) {
80
- if (collapsedGroups.has(group.lineNumber)) {
81
- collapsedGroupNames.add(group.name);
82
- for (const memberId of group.participantIds) {
92
+ const byDepth = [...parsed.groups].sort((a, b) => a.depth - b.depth);
93
+ for (const group of byDepth) {
94
+ if (!collapsedGroups.has(group.lineNumber)) continue;
95
+ if (insideCollapsed(group)) continue;
96
+ collapsedGroupNames.add(group.name);
97
+ for (const memberId of group.participantIds) {
98
+ if (!memberToGroup.has(memberId)) {
83
99
  memberToGroup.set(memberId, group.name);
84
100
  }
85
101
  }
@@ -114,9 +130,14 @@ function applyCollapseProjection(parsed, collapsedGroups) {
114
130
  to: remap(msg.to)
115
131
  }));
116
132
  const elements = remapElements(parsed.elements, memberToGroup);
117
- const groups = parsed.groups.filter(
118
- (g) => !collapsedGroups.has(g.lineNumber)
119
- );
133
+ const groups = parsed.groups.filter((g) => !collapsedGroups.has(g.lineNumber) && !insideCollapsed(g)).map((g) => {
134
+ const remapped = [];
135
+ for (const id of g.participantIds) {
136
+ const to = memberToGroup.get(id) ?? id;
137
+ if (!remapped.includes(to)) remapped.push(to);
138
+ }
139
+ return { ...g, participantIds: remapped };
140
+ });
120
141
  return {
121
142
  participants,
122
143
  messages,
@@ -163,7 +184,7 @@ function remapElements(elements, memberToGroup) {
163
184
 
164
185
  // src/sequence/tag-resolution.ts
165
186
  function propagateGroupTags(participantMeta, groups) {
166
- for (const group of groups) {
187
+ for (const group of [...groups].sort((a, b) => b.depth - a.depth)) {
167
188
  if (!group.metadata) continue;
168
189
  for (const id of group.participantIds) {
169
190
  const meta = participantMeta.get(id);
@@ -592,6 +613,7 @@ function applyPositionOverrides(participants) {
592
613
  function applyGroupOrdering(participants, groups, messages = []) {
593
614
  const idToGroup = /* @__PURE__ */ new Map();
594
615
  for (const group of groups) {
616
+ if (group.parent !== void 0) continue;
595
617
  for (const id of group.participantIds) {
596
618
  idToGroup.set(id, group);
597
619
  }
@@ -672,6 +694,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
672
694
  if (groups.length > 0) {
673
695
  const pToG = /* @__PURE__ */ new Map();
674
696
  for (let gi = 0; gi < groups.length; gi++) {
697
+ if (groups[gi].parent !== void 0) continue;
675
698
  for (const pid of groups[gi].participantIds) pToG.set(pid, gi);
676
699
  }
677
700
  const LOOSE = -1;
@@ -1063,7 +1086,11 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1063
1086
  for (const region of sectionRegions) {
1064
1087
  sectionMsgCounts.set(region.section.lineNumber, region.msgIndices.length);
1065
1088
  }
1089
+ const sEdgePad = ctx.aesthetic(20);
1066
1090
  const GROUP_PADDING_X = 15;
1091
+ const GROUP_NEST_INSET_X = 6;
1092
+ const GROUP_NEST_MIN_PAD_X = 4;
1093
+ const GROUP_NEST_INSET_Y = 6;
1067
1094
  const GROUP_PADDING_TOP = 22;
1068
1095
  const GROUP_PADDING_BOTTOM = 8;
1069
1096
  const GROUP_LABEL_SIZE = 11;
@@ -1108,11 +1135,13 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1108
1135
  titleBaselineY: ctx.structural(TITLE_Y),
1109
1136
  titleFontSize: ctx.text(TITLE_FONT_SIZE)
1110
1137
  });
1111
- const groupOffset = parsed.groups.length > 0 ? GROUP_PADDING_TOP + GROUP_LABEL_SIZE : 0;
1138
+ const maxGroupDepth = parsed.groups.reduce((d, g) => Math.max(d, g.depth), 0);
1139
+ const groupOffset = parsed.groups.length > 0 ? (maxGroupDepth + 1) * GROUP_PADDING_TOP + GROUP_LABEL_SIZE : 0;
1140
+ const groupBottomPad = (depth) => GROUP_PADDING_BOTTOM + (maxGroupDepth - depth) * GROUP_NEST_INSET_Y;
1112
1141
  const participantStartY = sTopMargin + titleOffset + (header.inline ? 0 : legendTopSpace) + sParticipantYOffset + groupOffset;
1113
1142
  const lifelineStartY0 = participantStartY + sBoxH;
1114
1143
  const hasActors = participants.some((p) => p.type === "actor");
1115
- const messageStartOffset = sMsgStartOffset + (hasActors ? 20 : 0) + (parsed.groups.length > 0 ? GROUP_PADDING_BOTTOM : 0);
1144
+ const messageStartOffset = sMsgStartOffset + (hasActors ? 20 : 0) + (parsed.groups.length > 0 ? groupBottomPad(0) : 0);
1116
1145
  const stepYPositions = [];
1117
1146
  const sectionYPositions = /* @__PURE__ */ new Map();
1118
1147
  let layoutEndY;
@@ -1219,13 +1248,16 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1219
1248
  sWithinGap = sGap * 0.88;
1220
1249
  sBetweenGap = (totalGaps * sGap - numWithinGaps * sWithinGap) / numGroupGaps;
1221
1250
  }
1251
+ const edgeProjection = (id) => {
1252
+ if (!id) return sBoxW / 2;
1253
+ const framed = parsed.groups.some(
1254
+ (g) => g.participantIds.includes(id)
1255
+ );
1256
+ return framed ? sBoxW / 2 + GROUP_PADDING_X : sBoxW / 2;
1257
+ };
1222
1258
  const rightmostId = participants[participants.length - 1]?.id;
1223
- let rightProjection = sBoxW / 2;
1259
+ let rightProjection = edgeProjection(rightmostId);
1224
1260
  if (rightmostId) {
1225
- if (parsed.groups.some(
1226
- (g) => g.participantIds.includes(rightmostId)
1227
- ))
1228
- rightProjection = Math.max(rightProjection, sBoxW / 2 + GROUP_PADDING_X);
1229
1261
  for (const step of renderSteps) {
1230
1262
  if (step.from === step.to && step.from === rightmostId) {
1231
1263
  const selfProj = sActivationWidth + sSelfCallWidth;
@@ -1253,8 +1285,11 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1253
1285
  if (elements && hasBlockWithRightmost(elements))
1254
1286
  rightProjection = Math.max(rightProjection, sBoxW / 2 + blockFramePadX);
1255
1287
  }
1256
- let rightMargin = Math.max(rightProjection + 10, sGap / 2);
1257
- let leftMargin = sGap / 2;
1288
+ let rightMargin = Math.max(rightProjection + sEdgePad, sGap / 2);
1289
+ let leftMargin = Math.max(
1290
+ edgeProjection(participants[0]?.id) + sEdgePad,
1291
+ sGap / 2
1292
+ );
1258
1293
  const diagramWidth = Math.max(0, participants.length - 1) * sGap;
1259
1294
  let totalWidth = Math.max(
1260
1295
  leftMargin + diagramWidth + rightMargin,
@@ -1392,6 +1427,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1392
1427
  collapsedGroupNames.add(group.name);
1393
1428
  collapsedGroupMeta.set(group.name, {
1394
1429
  lineNumber: group.lineNumber,
1430
+ depth: group.depth,
1395
1431
  participantIds: group.participantIds,
1396
1432
  ...group.metadata !== void 0 && { metadata: group.metadata }
1397
1433
  });
@@ -1414,14 +1450,35 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1414
1450
  const hasExpandedGroup = groups.some(
1415
1451
  (g) => g.participantIds.some((id) => participantX.has(id))
1416
1452
  );
1417
- for (const group of groups) {
1453
+ const framedGroups = [...groups].sort((a, b) => a.depth - b.depth);
1454
+ const allColumnXs = [...participantX.values()].sort((a, b) => a - b);
1455
+ const frameExtents = /* @__PURE__ */ new Map();
1456
+ for (const group of framedGroups) {
1418
1457
  if (group.participantIds.length === 0) continue;
1419
1458
  const memberXs = group.participantIds.map((id) => participantX.get(id)).filter((x) => x !== void 0);
1420
1459
  if (memberXs.length === 0) continue;
1421
- const minX = Math.min(...memberXs) - sBoxW / 2 - GROUP_PADDING_X;
1422
- const maxX = Math.max(...memberXs) + sBoxW / 2 + GROUP_PADDING_X;
1423
- const boxY = participantStartY - GROUP_PADDING_TOP;
1424
- const boxH = sBoxH + GROUP_PADDING_TOP + GROUP_PADDING_BOTTOM;
1460
+ const firstX = Math.min(...memberXs);
1461
+ const lastX = Math.max(...memberXs);
1462
+ const padX = Math.max(
1463
+ GROUP_NEST_MIN_PAD_X,
1464
+ GROUP_PADDING_X - group.depth * GROUP_NEST_INSET_X
1465
+ );
1466
+ let minX = firstX - sBoxW / 2 - padX;
1467
+ let maxX = lastX + sBoxW / 2 + padX;
1468
+ if (group.depth > 0) {
1469
+ const prevX = allColumnXs.filter((x) => x < firstX).pop();
1470
+ const nextX = allColumnXs.find((x) => x > lastX);
1471
+ if (prevX !== void 0) minX = (prevX + firstX) / 2;
1472
+ if (nextX !== void 0) maxX = (lastX + nextX) / 2;
1473
+ const parent = group.parent ? frameExtents.get(group.parent) : void 0;
1474
+ if (parent) {
1475
+ minX = Math.max(minX, parent.minX + GROUP_NEST_MIN_PAD_X);
1476
+ maxX = Math.min(maxX, parent.maxX - GROUP_NEST_MIN_PAD_X);
1477
+ }
1478
+ }
1479
+ frameExtents.set(group.name, { minX, maxX });
1480
+ const boxY = participantStartY - (maxGroupDepth + 1 - group.depth) * GROUP_PADDING_TOP;
1481
+ const boxH = sBoxH + (participantStartY - boxY) + groupBottomPad(group.depth);
1425
1482
  const groupTagValue = tagKey && group.metadata?.[tagKey];
1426
1483
  const groupTagColor = getTagColor(groupTagValue || void 0);
1427
1484
  const fillColor = groupTagColor ? mix(groupTagColor, themeBaseBg(palette, isDark), isDark ? 15 : 20) : isDark ? palette.surface : palette.bg;
@@ -1496,8 +1553,8 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1496
1553
  const drillColor = effectiveTagColor ?? mix(palette.textMuted, themeBaseBg(palette, isDark), 55);
1497
1554
  const drillBarH = ctx.structural(COLLAPSE_BAR_HEIGHT);
1498
1555
  const boxW = sBoxW;
1499
- const padTop = hasExpandedGroup ? GROUP_PADDING_TOP : 0;
1500
- const padBottom = hasExpandedGroup ? GROUP_PADDING_BOTTOM : 0;
1556
+ const padTop = hasExpandedGroup ? (maxGroupDepth + 1 - meta.depth) * GROUP_PADDING_TOP : 0;
1557
+ const padBottom = hasExpandedGroup ? groupBottomPad(meta.depth) : 0;
1501
1558
  const fullH = sBoxH + padTop + padBottom;
1502
1559
  const clipId = `clip-drill-group-${participant.id.replace(/[^a-zA-Z0-9-]/g, "-")}`;
1503
1560
  const participantG = svg.select(
@@ -1515,7 +1572,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1515
1572
  participantG.append("clipPath").attr("id", clipId).append("rect").attr("x", -boxW / 2).attr("y", -padTop).attr("width", boxW).attr("height", fullH).attr("rx", 6);
1516
1573
  participantG.append("rect").attr("class", "sequence-drill-bar").attr("x", -boxW / 2).attr("y", -padTop + fullH - drillBarH).attr("width", boxW).attr("height", drillBarH).attr("fill", drillColor).attr("clip-path", `url(#${clipId})`);
1517
1574
  }
1518
- const llY = isCollapsedGroup ? lifelineStartY + (hasExpandedGroup ? GROUP_PADDING_BOTTOM : 0) : participant.type === "actor" ? lifelineStartY + ACTOR_LABEL_CLEARANCE : lifelineStartY;
1575
+ const llY = isCollapsedGroup ? lifelineStartY + (hasExpandedGroup ? groupBottomPad(collapsedGroupMeta.get(participant.id)?.depth ?? 0) : 0) : participant.type === "actor" ? lifelineStartY + ACTOR_LABEL_CLEARANCE : lifelineStartY;
1519
1576
  const llColor = effectiveTagColor || palette.textMuted;
1520
1577
  for (const [segTop, segBottom] of lifelineSegments(
1521
1578
  cx,
@@ -11,7 +11,7 @@ import {
11
11
  } from "./chunk-RE27AX7O.js";
12
12
  import {
13
13
  parseDgmoChartType
14
- } from "./chunk-ECIXBEG2.js";
14
+ } from "./chunk-JVRDKR5R.js";
15
15
  import {
16
16
  chartTypes
17
17
  } from "./chunk-44GP7PD5.js";
@@ -96,7 +96,7 @@ import {
96
96
  } from "./chunk-BAZT6CIF.js";
97
97
  import {
98
98
  parseDgmoChartType
99
- } from "./chunk-ECIXBEG2.js";
99
+ } from "./chunk-JVRDKR5R.js";
100
100
  import {
101
101
  parseRaci
102
102
  } from "./chunk-3RYBTEF5.js";
@@ -114,8 +114,9 @@ import {
114
114
  toInternal
115
115
  } from "./chunk-DAOFNU6J.js";
116
116
  import {
117
+ SEQUENCE_DIAGNOSTICS,
117
118
  parseSequenceDgmo
118
- } from "./chunk-6EV2F6W7.js";
119
+ } from "./chunk-VPASQOR3.js";
119
120
  import {
120
121
  parseFlowchart
121
122
  } from "./chunk-JRUEIRJB.js";
@@ -1608,6 +1609,7 @@ var REGISTRY = [
1608
1609
  ...PERT_DIAGNOSTICS,
1609
1610
  ...GRAPH_DIAGNOSTICS,
1610
1611
  ...COLOR_DIAGNOSTICS,
1612
+ ...SEQUENCE_DIAGNOSTICS,
1611
1613
  ...SKETCH_DIAGNOSTICS,
1612
1614
  ...FAMILY_DIAGNOSTICS,
1613
1615
  ...ALIAS_DIAGNOSTICS
@@ -41,6 +41,22 @@ import {
41
41
  suggest
42
42
  } from "./chunk-BYU54C3S.js";
43
43
 
44
+ // src/sequence/diagnostics.ts
45
+ var SEQUENCE_DIAGNOSTIC_CODES = {
46
+ GROUP_DEPTH: "E_SEQ_GROUP_DEPTH"
47
+ };
48
+ var SEQUENCE_DIAGNOSTICS = [
49
+ {
50
+ code: SEQUENCE_DIAGNOSTIC_CODES.GROUP_DEPTH,
51
+ severity: "error",
52
+ chartType: "sequence",
53
+ title: "Group nested too deep",
54
+ message: (p) => `Group "${String(p.label ?? "?")}" is nested ${String(p.depth ?? "?")} levels deep \u2014 participant groups nest to depth ${String(p.max ?? "?")}; its participants join the group above it`,
55
+ hint: "Move the inner [Group] up a level \u2014 two levels of bracket is the bound.",
56
+ example: "sequence\n[Monolith]\n [Linux]\n [Container]\n App"
57
+ }
58
+ ];
59
+
44
60
  // src/sequence/parser.ts
45
61
  var KNOWN_SEQ_OPTIONS = /* @__PURE__ */ new Set(["active-tag"]);
46
62
  var KNOWN_SEQ_BOOLEANS = /* @__PURE__ */ new Set([
@@ -70,6 +86,7 @@ function isSequenceNote(el) {
70
86
  }
71
87
  var IS_A_PATTERN = /^([^:]+?)\s+is\s+an?\s+(\w+)(?:\s+(.+))?$/i;
72
88
  var COLORED_PARTICIPANT_PATTERN = /^(\S+?)\((red|orange|yellow|green|blue|purple|teal|cyan|gray|black|white)\)\s*$/;
89
+ var MAX_GROUP_DEPTH = 2;
73
90
  var GROUP_HEADING_PATTERN = /^\[([^\]|]+?)(?:\(([^)]+)\))?\]\s*(.*)$/;
74
91
  var GROUP_HEADING_FALLBACK = /^\[([^\]]+)\]\s*(.*)$/;
75
92
  var SECTION_PATTERN = /^==\s+(.+?)(?:\s*==)?\s*$/;
@@ -218,8 +235,8 @@ function parseSequenceDgmo(content, palette) {
218
235
  return nameAliasMap.resolve(trimmed) ?? trimmed;
219
236
  };
220
237
  const fail = makeFail(result);
221
- const pushError = (line, message) => {
222
- const diag = makeDgmoError(line, message);
238
+ const pushError = (line, message, code) => {
239
+ const diag = makeDgmoError(line, message, "error", code);
223
240
  result.diagnostics.push(diag);
224
241
  if (!result.error) result.error = formatDgmoError(diag);
225
242
  };
@@ -249,7 +266,31 @@ function parseSequenceDgmo(content, palette) {
249
266
  }
250
267
  break;
251
268
  }
252
- let activeGroup = null;
269
+ const groupStack = [];
270
+ const activeGroup = () => groupStack.length > 0 ? groupStack[groupStack.length - 1].group : null;
271
+ const closeGroupsTo = (indent) => {
272
+ while (groupStack.length > 0 && groupStack[groupStack.length - 1].indent >= indent) {
273
+ groupStack.pop();
274
+ }
275
+ };
276
+ const joinActiveGroup = (key, id, lineNumber) => {
277
+ const innermost = activeGroup();
278
+ if (!innermost || innermost.participantIds.includes(key)) return;
279
+ const existingGroup = participantGroupMap.get(key);
280
+ if (existingGroup) {
281
+ pushError(
282
+ lineNumber,
283
+ `Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`
284
+ );
285
+ return;
286
+ }
287
+ for (const frame of groupStack) {
288
+ if (!frame.group.participantIds.includes(key)) {
289
+ frame.group.participantIds.push(key);
290
+ }
291
+ }
292
+ participantGroupMap.set(key, innermost.name);
293
+ };
253
294
  const participantIds = /* @__PURE__ */ new Set();
254
295
  const idByKey = /* @__PURE__ */ new Map();
255
296
  const addParticipant = (name, lineNumber, extras) => {
@@ -364,7 +405,7 @@ function parseSequenceDgmo(content, palette) {
364
405
  const lineNumber = i + 1;
365
406
  nameAliasMap.at(lineNumber);
366
407
  if (!trimmed) {
367
- activeGroup = null;
408
+ closeGroupsTo(0);
368
409
  currentTagGroup = null;
369
410
  continue;
370
411
  }
@@ -407,14 +448,29 @@ function parseSequenceDgmo(content, palette) {
407
448
  );
408
449
  }
409
450
  contentStarted = true;
410
- activeGroup = {
451
+ const headingIndent = measureIndent(raw);
452
+ closeGroupsTo(headingIndent);
453
+ const depth = groupStack.length;
454
+ if (depth >= MAX_GROUP_DEPTH) {
455
+ pushError(
456
+ lineNumber,
457
+ `Group '${groupName}' is nested ${String(depth + 1)} levels deep \u2014 participant groups nest to depth ${String(MAX_GROUP_DEPTH)}; its participants join the group above it`,
458
+ SEQUENCE_DIAGNOSTIC_CODES.GROUP_DEPTH
459
+ );
460
+ continue;
461
+ }
462
+ const parentGroup = activeGroup();
463
+ const group = {
411
464
  name: groupName,
412
465
  participantIds: [],
413
466
  lineNumber,
467
+ depth,
468
+ ...parentGroup ? { parent: parentGroup.name } : {},
414
469
  ...groupMeta ? { metadata: groupMeta } : {},
415
470
  ...isCollapsed ? { collapsed: true } : {}
416
471
  };
417
- result.groups.push(activeGroup);
472
+ result.groups.push(group);
473
+ groupStack.push({ group, indent: headingIndent });
418
474
  continue;
419
475
  }
420
476
  if (trimmed.startsWith("[")) {
@@ -431,9 +487,7 @@ function parseSequenceDgmo(content, palette) {
431
487
  continue;
432
488
  }
433
489
  }
434
- if (activeGroup && measureIndent(raw) === 0) {
435
- activeGroup = null;
436
- }
490
+ closeGroupsTo(measureIndent(raw));
437
491
  if (trimmed.startsWith("//")) continue;
438
492
  if (trimmed.startsWith("#")) {
439
493
  pushError(lineNumber, "Use // for comments");
@@ -623,18 +677,7 @@ function parseSequenceDgmo(content, palette) {
623
677
  ...position !== void 0 && { position },
624
678
  ...isAMeta !== void 0 && { metadata: isAMeta }
625
679
  });
626
- if (activeGroup && !activeGroup.participantIds.includes(key)) {
627
- const existingGroup = participantGroupMap.get(key);
628
- if (existingGroup) {
629
- pushError(
630
- lineNumber,
631
- `Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`
632
- );
633
- } else {
634
- activeGroup.participantIds.push(key);
635
- participantGroupMap.set(key, activeGroup.name);
636
- }
637
- }
680
+ joinActiveGroup(key, id, lineNumber);
638
681
  continue;
639
682
  }
640
683
  const { core: colorCore, meta: colorMeta } = splitPipe(trimmed, lineNumber);
@@ -650,18 +693,7 @@ function parseSequenceDgmo(content, palette) {
650
693
  const key = addParticipant(id, lineNumber, {
651
694
  ...colorMeta !== void 0 && { metadata: colorMeta }
652
695
  });
653
- if (activeGroup && !activeGroup.participantIds.includes(key)) {
654
- const existingGroup = participantGroupMap.get(key);
655
- if (existingGroup) {
656
- pushError(
657
- lineNumber,
658
- `Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`
659
- );
660
- } else {
661
- activeGroup.participantIds.push(key);
662
- participantGroupMap.set(key, activeGroup.name);
663
- }
664
- }
696
+ joinActiveGroup(key, id, lineNumber);
665
697
  continue;
666
698
  }
667
699
  {
@@ -670,7 +702,7 @@ function parseSequenceDgmo(content, palette) {
670
702
  meta: bareMeta,
671
703
  alias: bareAlias
672
704
  } = splitPipe(trimmed, lineNumber);
673
- const inGroup = activeGroup && measureIndent(raw) > 0;
705
+ const inGroup = activeGroup() !== null && measureIndent(raw) > 0;
674
706
  if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !bodyStarted || bareMeta)) {
675
707
  contentStarted = true;
676
708
  const id = bareCore;
@@ -682,18 +714,7 @@ function parseSequenceDgmo(content, palette) {
682
714
  ...position !== void 0 && { position },
683
715
  ...bareMeta !== void 0 && { metadata: bareMeta }
684
716
  });
685
- if (activeGroup && !activeGroup.participantIds.includes(key)) {
686
- const existingGroup = participantGroupMap.get(key);
687
- if (existingGroup) {
688
- pushError(
689
- lineNumber,
690
- `Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`
691
- );
692
- } else {
693
- activeGroup.participantIds.push(key);
694
- participantGroupMap.set(key, activeGroup.name);
695
- }
696
- }
717
+ joinActiveGroup(key, id, lineNumber);
697
718
  continue;
698
719
  }
699
720
  }
@@ -1066,6 +1087,7 @@ function looksLikeSequence(content) {
1066
1087
  }
1067
1088
 
1068
1089
  export {
1090
+ SEQUENCE_DIAGNOSTICS,
1069
1091
  isSequenceBlock,
1070
1092
  isSequenceSection,
1071
1093
  isSequenceNote,
@@ -6,7 +6,7 @@ import {
6
6
  isExtendedChartType,
7
7
  knownChartTypeIds,
8
8
  parseDgmo
9
- } from "./chunk-KABGWUND.js";
9
+ } from "./chunk-T327J6CE.js";
10
10
  import "./chunk-EOYPX6SF.js";
11
11
  import "./chunk-WP2RLGDI.js";
12
12
  import "./chunk-A2RUBBVT.js";
@@ -46,14 +46,14 @@ import {
46
46
  looksLikeC4,
47
47
  looksLikeGantt,
48
48
  parseDgmoChartType
49
- } from "./chunk-ECIXBEG2.js";
49
+ } from "./chunk-JVRDKR5R.js";
50
50
  import "./chunk-3RYBTEF5.js";
51
51
  import "./chunk-UNNJCSSQ.js";
52
52
  import "./chunk-3TYUXC7D.js";
53
53
  import "./chunk-HMWZQ53A.js";
54
54
  import "./chunk-EQW6HOP4.js";
55
55
  import "./chunk-DAOFNU6J.js";
56
- import "./chunk-6EV2F6W7.js";
56
+ import "./chunk-VPASQOR3.js";
57
57
  import "./chunk-C3Z4DX3V.js";
58
58
  import "./chunk-JRUEIRJB.js";
59
59
  import "./chunk-7TF3QOOD.js";
package/dist/index.js CHANGED
@@ -11,15 +11,15 @@ import {
11
11
  } from "./chunk-SQAG3UXH.js";
12
12
  import {
13
13
  render
14
- } from "./chunk-QZRYCRT7.js";
14
+ } from "./chunk-ADAGWVWJ.js";
15
15
  import {
16
16
  renderErrorCard
17
- } from "./chunk-VKGQX6P2.js";
17
+ } from "./chunk-OZTDXMRQ.js";
18
18
  import {
19
19
  getDiagnosticSpec,
20
20
  listDiagnosticCodes,
21
21
  parseDgmo
22
- } from "./chunk-KABGWUND.js";
22
+ } from "./chunk-T327J6CE.js";
23
23
  import "./chunk-EOYPX6SF.js";
24
24
  import "./chunk-WP2RLGDI.js";
25
25
  import "./chunk-A2RUBBVT.js";
@@ -82,14 +82,14 @@ import {
82
82
  decodeDiagramUrl,
83
83
  encodeDiagramUrl
84
84
  } from "./chunk-RE27AX7O.js";
85
- import "./chunk-ECIXBEG2.js";
85
+ import "./chunk-JVRDKR5R.js";
86
86
  import "./chunk-3RYBTEF5.js";
87
87
  import "./chunk-UNNJCSSQ.js";
88
88
  import "./chunk-3TYUXC7D.js";
89
89
  import "./chunk-HMWZQ53A.js";
90
90
  import "./chunk-EQW6HOP4.js";
91
91
  import "./chunk-DAOFNU6J.js";
92
- import "./chunk-6EV2F6W7.js";
92
+ import "./chunk-VPASQOR3.js";
93
93
  import "./chunk-C3Z4DX3V.js";
94
94
  import "./chunk-JRUEIRJB.js";
95
95
  import "./chunk-7TF3QOOD.js";
@@ -4,7 +4,7 @@ import {
4
4
  isSequenceSection,
5
5
  looksLikeSequence,
6
6
  parseSequenceDgmo
7
- } from "./chunk-6EV2F6W7.js";
7
+ } from "./chunk-VPASQOR3.js";
8
8
  import "./chunk-C3Z4DX3V.js";
9
9
  import "./chunk-N7VMGZCU.js";
10
10
  import "./chunk-NOTA6TEI.js";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  renderLiveLinkCard
3
3
  } from "./chunk-A6YINTY6.js";
4
- import "./chunk-VKGQX6P2.js";
5
- import "./chunk-KABGWUND.js";
4
+ import "./chunk-OZTDXMRQ.js";
5
+ import "./chunk-T327J6CE.js";
6
6
  import "./chunk-EOYPX6SF.js";
7
7
  import "./chunk-WP2RLGDI.js";
8
8
  import "./chunk-A2RUBBVT.js";
@@ -41,14 +41,14 @@ import "./chunk-GUTZCRBT.js";
41
41
  import "./chunk-BAZT6CIF.js";
42
42
  import "./chunk-KAO7JL4I.js";
43
43
  import "./chunk-RE27AX7O.js";
44
- import "./chunk-ECIXBEG2.js";
44
+ import "./chunk-JVRDKR5R.js";
45
45
  import "./chunk-3RYBTEF5.js";
46
46
  import "./chunk-UNNJCSSQ.js";
47
47
  import "./chunk-3TYUXC7D.js";
48
48
  import "./chunk-HMWZQ53A.js";
49
49
  import "./chunk-EQW6HOP4.js";
50
50
  import "./chunk-DAOFNU6J.js";
51
- import "./chunk-6EV2F6W7.js";
51
+ import "./chunk-VPASQOR3.js";
52
52
  import "./chunk-C3Z4DX3V.js";
53
53
  import "./chunk-JRUEIRJB.js";
54
54
  import "./chunk-7TF3QOOD.js";
@@ -7,7 +7,7 @@ import {
7
7
  groupMessagesBySection,
8
8
  renderSequenceDiagram,
9
9
  summarizeSectionParticipation
10
- } from "./chunk-AYA6C64M.js";
10
+ } from "./chunk-NJS62BAG.js";
11
11
  import "./chunk-WMIKC5XO.js";
12
12
  import {
13
13
  parseInlineMarkdown,
@@ -22,7 +22,7 @@ import "./chunk-FM3WAVB6.js";
22
22
  import "./chunk-ICLB27PZ.js";
23
23
  import "./chunk-RH2PROYG.js";
24
24
  import "./chunk-KAO7JL4I.js";
25
- import "./chunk-6EV2F6W7.js";
25
+ import "./chunk-VPASQOR3.js";
26
26
  import "./chunk-C3Z4DX3V.js";
27
27
  import "./chunk-N7VMGZCU.js";
28
28
  import "./chunk-NOTA6TEI.js";
@@ -629,6 +629,18 @@ Names that previously inferred to a removed type — `AuthService`, `WebApp`, `C
629
629
  ```
630
630
 
631
631
  - Metadata goes outside brackets: `[Backend] t: Eng`
632
+ - **Groups nest one level deep.** Indent a `[Group]` under another and its members one level deeper again; dedenting rejoins the outer group, so `FanScreen` below belongs to `Monolith`, not to `Linux`. A third level is refused with `E_SEQ_GROUP_DEPTH` and its participants join the group above.
633
+
634
+ ```
635
+ [Monolith]
636
+ Camera
637
+ [Linux]
638
+ FacialCapture
639
+ GAEEdgeSW
640
+ FanScreen
641
+ StaffScreen
642
+ ```
643
+
632
644
  - **Color participants by category with a tag group** (§1.3). Declare the group with named palette colors, then assign each participant `<alias>: <Value>` — the tag is on the participant, not the group line or a message:
633
645
 
634
646
  ```
@@ -0,0 +1,50 @@
1
+ sequence Night Signal Chain
2
+
3
+ tag Owner as o
4
+ Crown red
5
+ Guild blue
6
+
7
+ [Flagship] o: Crown
8
+ Lookout
9
+ [Gun Deck] o: Guild
10
+ PowderRoom
11
+ RangeMaster
12
+ Helm
13
+ QuarterMaster
14
+
15
+ [Harbour Watch] o: Crown
16
+ [Watchtower] o: Guild
17
+ BeaconKeeper
18
+ TideGauge
19
+
20
+ [Signal Chain]
21
+ Runner o: Crown
22
+ [Cipher Room] collapsed
23
+ CipherClerk
24
+ CodeBook
25
+
26
+ == Sighting ==
27
+ Lookout -sail on the horizon-> Helm
28
+ Helm -range the target-> RangeMaster
29
+
30
+ note right of RangeMaster
31
+ Bearing and elevation are
32
+ read off the same glass, so
33
+ one clerk owns both numbers
34
+
35
+ RangeMaster -draw two charges-> PowderRoom
36
+ PowderRoom -charges ready-> RangeMaster
37
+ RangeMaster -laid and ready-> QuarterMaster
38
+
39
+ == Raising the Alarm ==
40
+ QuarterMaster -light the beacon-> BeaconKeeper
41
+ BeaconKeeper -read the tide-> TideGauge
42
+ TideGauge -water is making-> BeaconKeeper
43
+ BeaconKeeper -beacon lit-> Runner
44
+
45
+ == Passing the Word ==
46
+ Runner -carry the dispatch-> CipherClerk
47
+ CipherClerk -look up the night code-> CodeBook
48
+ CodeBook -tonight's cipher-> CipherClerk
49
+ CipherClerk -sealed dispatch-> Runner
50
+ Runner -word is away-> QuarterMaster
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diagrammo/dgmo",
3
- "version": "0.84.0",
3
+ "version": "0.85.0",
4
4
  "description": "DGMO diagram markup language — parser, renderer, and color system",
5
5
  "license": "MIT",
6
6
  "author": "Diagrammo LLC",