@diagrammo/dgmo 0.65.0 → 0.67.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.
@@ -1,12 +1,14 @@
1
- import { a as DgmoError, P as PaletteConfig, T as TagGroup, f as PaletteColors, C as CompactViewState, k as TagEntry } from './dgmo-router-Dd5PTjxl.js';
2
- export { l as CHART_TYPE_DESCRIPTIONS, b as ChartTypeId, c as ChartTypeMeta, m as DecodedDiagramUrl, d as DgmoSeverity, n as EncodeDiagramUrlOptions, o as EncodeDiagramUrlResult, R as RenderCategory, q as autoTagColorCycle, s as chartTypeParsers, g as chartTypes, t as decodeDiagramUrl, u as decodeViewState, v as encodeDiagramUrl, w as encodeViewState, i as formatDgmoError, x as getAllChartTypes, y as getAvailablePalettes, j as getPalette, z as getRenderCategory, A as isExtendedChartType, B as isValidHex, F as knownChartTypeIds, G as makeDgmoError, p as parseDgmo, H as parseDgmoChartType, I as registerPalette, J as tagAttrKey, p as validate } from './dgmo-router-Dd5PTjxl.js';
3
- import { M as MapDataSource, P as ParsedMap, c as MapData, j as ResolvedMap, k as MapLayoutLegend, l as GeoExtent } from './themes-DiUAG4Bf.js';
4
- export { A as AirportData, B as BoundaryTopology, G as Gazetteer, a as GazetteerEntry, b as MapCompletionOptions, m as MapDirectives, n as MapEdge, d as MapLocationMatch, e as MapPlaceCompletion, o as MapPoi, q as MapRegion, f as MapRegionCompletion, r as MapRoute, u as PoiPos, v as ProjectionFamily, R as RegionName, g as RegionNames, w as ResolvedEdge, x as ResolvedPoi, y as ResolvedRegion, z as ResolvedRoute, T as Theme, h as completeMapPlaces, i as completeMapRegions, p as palettes, C as renderForExport, s as searchMapLocations, t as themes } from './themes-DiUAG4Bf.js';
1
+ import { D as DgmoError, P as PaletteConfig, T as TagGroup, a as PaletteColors, C as CompactViewState, g as TagEntry } from './tag-groups-NUA2F1I5.js';
2
+ export { h as DecodedDiagramUrl, c as DgmoSeverity, i as EncodeDiagramUrlOptions, j as EncodeDiagramUrlResult, k as autoTagColorCycle, l as decodeDiagramUrl, m as decodeViewState, n as encodeDiagramUrl, o as encodeViewState, f as formatDgmoError, p as makeDgmoError, t as tagAttrKey } from './tag-groups-NUA2F1I5.js';
3
+ import { M as MapDataSource, P as ParsedMap, b as MapData, d as ResolvedMap, e as MapLayoutLegend, f as GeoExtent } from './d3-Bfhj-XBX.js';
4
+ export { A as AirportData, B as BoundaryTopology, G as Gazetteer, a as GazetteerEntry, g as MapDirectives, h as MapEdge, i as MapPoi, j as MapRegion, k as MapRoute, l as PoiPos, m as ProjectionFamily, R as RegionName, c as RegionNames, n as ResolvedEdge, o as ResolvedPoi, p as ResolvedRegion, q as ResolvedRoute, r as renderForExport } from './d3-Bfhj-XBX.js';
5
+ export { b as CHART_TYPE_DESCRIPTIONS, C as ChartTypeId, a as ChartTypeMeta, R as RenderCategory, d as chartTypeParsers, c as chartTypes, e as getAllChartTypes, f as getAvailablePalettes, g as getPalette, h as getRenderCategory, i as isExtendedChartType, j as isValidHex, k as knownChartTypeIds, p as parseDgmo, l as parseDgmoChartType, m as registerPalette, p as validate } from './dgmo-router-EAW7XiBE.js';
5
6
  import { Selection } from 'd3-selection';
6
7
  import * as d3Scale from 'd3-scale';
7
- import { P as ParsedOrg, F as FillMode, R as RaciMarker, a as ParsedRaci, b as RaciVariant, c as RaciTask } from './chart-meta-D98NbYDp.js';
8
- export { A as ALL_CHART_TYPES, I as ImportSource, O as OrgNode, d as RaciPhase, e as RaciRoleAssignment, f as ReadFileFn, g as ResolveImportsResult, h as contrastText, i as getSeriesColors, j as hexToHSL, k as hexToHSLString, l as hslToHex, m as mix, n as normalizePertSourceForShare, p as parseFirstLine, o as parseOrg, q as parseRaci, r as resolveOrgImports, s as shade, t as shapeFill, u as tint } from './chart-meta-D98NbYDp.js';
8
+ import { P as ParsedOrg, F as FillMode, R as RaciMarker, a as ParsedRaci, b as RaciVariant, c as RaciTask } from './chart-meta-CpicJ1kj.js';
9
+ export { A as ALL_CHART_TYPES, I as ImportSource, O as OrgNode, d as RaciPhase, e as RaciRoleAssignment, f as ReadFileFn, g as ResolveImportsResult, h as contrastText, i as getSeriesColors, j as hexToHSL, k as hexToHSLString, l as hslToHex, m as mix, n as normalizePertSourceForShare, p as parseFirstLine, o as parseOrg, q as parseRaci, r as resolveOrgImports, s as shade, t as shapeFill, u as tint } from './chart-meta-CpicJ1kj.js';
9
10
  import { GeoProjection } from 'd3-geo';
11
+ export { M as MapCompletionOptions, a as MapLocationMatch, b as MapPlaceCompletion, c as MapRegionCompletion, T as Theme, d as completeMapPlaces, e as completeMapRegions, p as palettes, s as searchMapLocations, t as themes } from './themes-Dem5xalq.js';
10
12
 
11
13
  /**
12
14
  * Stable diagnostic codes for in-arrow label parsing errors.
package/dist/advanced.js CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  computeActivations,
17
17
  groupMessagesBySection,
18
18
  renderSequenceDiagram
19
- } from "./chunk-6IUL6LER.js";
19
+ } from "./chunk-WDLD5MU5.js";
20
20
  import {
21
21
  renderRing,
22
22
  renderRingForExport
@@ -313,7 +313,7 @@ import {
313
313
  renderSlopeChart,
314
314
  renderVenn,
315
315
  renderWordCloud
316
- } from "./chunk-ZZXPYVS4.js";
316
+ } from "./chunk-HJ62AO3J.js";
317
317
  import "./chunk-7L53J53J.js";
318
318
  import "./chunk-EOYPX6SF.js";
319
319
  import {
package/dist/block.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ import { M as MapDataSource } from './d3-Bfhj-XBX.js';
2
+ import './tag-groups-NUA2F1I5.js';
3
+
1
4
  /**
2
5
  * Canonical stylesheet for the standard DGMO embed block (BL-114).
3
6
  *
@@ -106,6 +109,20 @@ interface DgmoBlockOptions {
106
109
  * trusted, since these end up in markup verbatim.
107
110
  */
108
111
  dataAttributes?: Record<string, string>;
112
+ /**
113
+ * Map basemap assets, forwarded to `render()`. Without them a ` ```dgmo `
114
+ * fence containing a map emits the error card ("This map has no basemap
115
+ * data") — this module reads nothing from disk or the network on its own,
116
+ * and it has no environment default, because it runs in browsers as much as
117
+ * in build scripts.
118
+ *
119
+ * A Node host (a remark build, a static-site generator, a report writer)
120
+ * passes `loadMapData` from `@diagrammo/dgmo/advanced` — as the FUNCTION, so
121
+ * the eleven basemap JSON files are read only for a fence that turns out to
122
+ * be a map. A browser host supplies the data itself, or a loader that
123
+ * fetches it.
124
+ */
125
+ mapData?: MapDataSource;
109
126
  /** Receives palette-fallback warnings. Default: console.warn. */
110
127
  onWarn?: (message: string) => void;
111
128
  }
@@ -139,4 +156,4 @@ declare function buildDgmoBlockHtml(source: string, svgsHtml: string, options?:
139
156
  */
140
157
  declare function errorBlockHtml(err: unknown, source: string, options?: Pick<DgmoBlockOptions, 'className' | 'legacyClassNames'>): string;
141
158
 
142
- export { BLOCK_CSS, type BlockColorMode, type BlockMode, type DgmoBlockOptions, type DgmoBlockResult, EDITOR_BASE_URL, buildDgmoBlockHtml, errorBlockHtml, renderDgmoBlock };
159
+ export { BLOCK_CSS, type BlockColorMode, type BlockMode, type DgmoBlockOptions, type DgmoBlockResult, EDITOR_BASE_URL, MapDataSource, buildDgmoBlockHtml, errorBlockHtml, renderDgmoBlock };
package/dist/block.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-D3K4U3ZM.js";
5
5
  import {
6
6
  render
7
- } from "./chunk-ZZXPYVS4.js";
7
+ } from "./chunk-HJ62AO3J.js";
8
8
  import {
9
9
  docsLink,
10
10
  renderErrorCard
@@ -1052,6 +1052,7 @@ function resolveBlockOptions(opts) {
1052
1052
  legacyClassNames: opts.legacyClassNames ?? [],
1053
1053
  dataAttributes: opts.dataAttributes ?? {},
1054
1054
  title: opts.title,
1055
+ mapData: opts.mapData,
1055
1056
  onWarn: opts.onWarn ?? ((message) => console.warn(message))
1056
1057
  };
1057
1058
  }
@@ -1062,7 +1063,11 @@ async function renderDgmoBlock(source, options = {}) {
1062
1063
  const diagnostics = [];
1063
1064
  let chartType;
1064
1065
  const renderTheme = async (theme) => {
1065
- const r = await render(trimmed, { palette: palette.id, theme });
1066
+ const r = await render(trimmed, {
1067
+ palette: palette.id,
1068
+ theme,
1069
+ ...opts.mapData !== void 0 ? { mapData: opts.mapData } : {}
1070
+ });
1066
1071
  chartType = r.chartType;
1067
1072
  diagnostics.push(...r.diagnostics);
1068
1073
  const errors = r.diagnostics.filter((d) => d.severity === "error");
@@ -1,4 +1,5 @@
1
- import { f as PaletteColors, T as TagGroup, a as DgmoError } from './dgmo-router-Dd5PTjxl.js';
1
+ import './dgmo-router-EAW7XiBE.js';
2
+ import { a as PaletteColors, T as TagGroup, D as DgmoError } from './tag-groups-NUA2F1I5.js';
2
3
 
3
4
  /** Convert hex (#RRGGBB or #RGB) to { h, s, l } with h in degrees, s/l as percentages. */
4
5
  declare function hexToHSL(hex: string): {
@@ -1,2 +1,3 @@
1
- export { b as ChartTypeId, c as ChartTypeMeta, C as CompactViewState, m as DecodedDiagramUrl, n as EncodeDiagramUrlOptions, o as EncodeDiagramUrlResult, f as PaletteColors, g as chartTypes, t as decodeDiagramUrl, u as decodeViewState, v as encodeDiagramUrl, w as encodeViewState, y as getAvailablePalettes, j as getPalette, H as parseDgmoChartType } from './dgmo-router-Dd5PTjxl.js';
2
- export { A as ALL_CHART_TYPES, O as OrgNode, P as ParsedOrg, k as hexToHSLString, n as normalizePertSourceForShare, p as parseFirstLine, o as parseOrg, q as parseRaci, r as resolveOrgImports } from './chart-meta-D98NbYDp.js';
1
+ export { C as ChartTypeId, a as ChartTypeMeta, c as chartTypes, f as getAvailablePalettes, g as getPalette, l as parseDgmoChartType } from './dgmo-router-EAW7XiBE.js';
2
+ export { A as ALL_CHART_TYPES, O as OrgNode, P as ParsedOrg, k as hexToHSLString, n as normalizePertSourceForShare, p as parseFirstLine, o as parseOrg, q as parseRaci, r as resolveOrgImports } from './chart-meta-CpicJ1kj.js';
3
+ export { C as CompactViewState, h as DecodedDiagramUrl, i as EncodeDiagramUrlOptions, j as EncodeDiagramUrlResult, a as PaletteColors, l as decodeDiagramUrl, m as decodeViewState, n as encodeDiagramUrl, o as encodeViewState } from './tag-groups-NUA2F1I5.js';
@@ -3055,7 +3055,7 @@ async function exportVisualization(ctx) {
3055
3055
  const isDark = ctx.isDark;
3056
3056
  const container = createExportContainer(ctx.width, ctx.height);
3057
3057
  const { parseSequenceDgmo } = await import("./parser-5WIZGKNE.js");
3058
- const { renderSequenceDiagram } = await import("./renderer-ALP46W2X.js");
3058
+ const { renderSequenceDiagram } = await import("./renderer-KY6PADCM.js");
3059
3059
  const seqParsed = parseSequenceDgmo(content, effectivePalette);
3060
3060
  if (seqParsed.error || seqParsed.participants.length === 0) return "";
3061
3061
  const collapsedSections = viewState?.cs ? new Set(viewState.cs) : void 0;
@@ -3718,4 +3718,4 @@ export {
3718
3718
  renderForExport,
3719
3719
  render
3720
3720
  };
3721
- //# sourceMappingURL=chunk-ZZXPYVS4.js.map
3721
+ //# sourceMappingURL=chunk-HJ62AO3J.js.map
@@ -25,6 +25,9 @@ import {
25
25
  import {
26
26
  FONT_FAMILY
27
27
  } from "./chunk-UTCPNJND.js";
28
+ import {
29
+ legendChromeColors
30
+ } from "./chunk-3HOCNSPD.js";
28
31
  import {
29
32
  measureText,
30
33
  truncateText
@@ -253,6 +256,7 @@ var NOTE_FONT_SIZE = 10;
253
256
  var NOTE_LINE_H = 14;
254
257
  var NOTE_GAP = 15;
255
258
  var ACTIVATION_WIDTH = 10;
259
+ var ACTIVATION_IMPLICIT_END_CLEARANCE = 8;
256
260
  var SELF_CALL_HEIGHT = 25;
257
261
  var SELF_CALL_WIDTH = 30;
258
262
  var ACTOR_LABEL_CLEARANCE = 22;
@@ -401,6 +405,34 @@ function groupMessagesBySection(elements, messages) {
401
405
  }
402
406
  return groups;
403
407
  }
408
+ var SECTION_BAND_HEIGHT = 22;
409
+ var COLLAPSED_SECTION_BAND_HEIGHT = 36;
410
+ var COLLAPSED_SECTION_BAND_EXTRA = COLLAPSED_SECTION_BAND_HEIGHT - SECTION_BAND_HEIGHT;
411
+ var COLLAPSED_SECTION_MARK_OFFSET = 9;
412
+ var COLLAPSED_SECTION_LABEL_BASELINE = -4;
413
+ var SECTION_LABEL_CLEARANCE_X = 8;
414
+ var SECTION_LABEL_CLEARANCE_ABOVE = 12;
415
+ var SECTION_LABEL_CLEARANCE_BELOW = 5;
416
+ function summarizeSectionParticipation(messages, msgIndices) {
417
+ const summary = /* @__PURE__ */ new Map();
418
+ const bump = (id, key) => {
419
+ const entry = summary.get(id) ?? { sends: 0, receives: 0 };
420
+ entry[key] += 1;
421
+ summary.set(id, entry);
422
+ };
423
+ for (const idx of msgIndices) {
424
+ const msg = messages[idx];
425
+ if (!msg) continue;
426
+ bump(msg.from, "sends");
427
+ bump(msg.to, "receives");
428
+ }
429
+ return summary;
430
+ }
431
+ function sectionMarkRadius(touches) {
432
+ if (touches >= 4) return 6;
433
+ if (touches >= 2) return 4.5;
434
+ return 3.5;
435
+ }
404
436
  function buildRenderSequence(messages) {
405
437
  const steps = [];
406
438
  const stack = [];
@@ -732,11 +764,18 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
732
764
  }
733
765
  const lastStepIdx = Math.max(renderSteps.length - 1, 0);
734
766
  const clampStep = (i) => Math.min(Math.max(i, 0), lastStepIdx);
735
- const activations = activationsOff ? [] : computeActivations(allRenderSteps).map((a) => ({
736
- ...a,
737
- startStep: clampStep(allToFiltered[a.startStep] ?? 0),
738
- endStep: clampStep(allToFiltered[a.endStep] ?? 0)
739
- }));
767
+ const activations = activationsOff ? [] : computeActivations(allRenderSteps).map((a) => {
768
+ const closingStep = allRenderSteps[a.endStep];
769
+ return {
770
+ ...a,
771
+ startStep: clampStep(allToFiltered[a.startStep] ?? 0),
772
+ endStep: clampStep(allToFiltered[a.endStep] ?? 0),
773
+ // True when the return that closes this bar was dropped from the
774
+ // layout, so `endStep` is not the bar's own event but whatever
775
+ // happens next. The bar has to stop short of it — see the render.
776
+ implicitEnd: closingStep !== void 0 && !stepSurvives(closingStep)
777
+ };
778
+ });
740
779
  const stepSpacing = 35;
741
780
  const BLOCK_HEADER_SPACE = 30;
742
781
  const BLOCK_AFTER_SPACE = 15;
@@ -803,6 +842,8 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
803
842
  }
804
843
  return -1;
805
844
  };
845
+ const isSectionCollapsed = (sec) => collapsedSections?.has(sec.lineNumber) ?? false;
846
+ const collapsedBandPad = (sec) => isSectionCollapsed(sec) ? COLLAPSED_SECTION_BAND_EXTRA / 2 : 0;
806
847
  const SECTION_TOP_PAD = 35;
807
848
  const SECTION_BOTTOM_PAD = 45;
808
849
  const extraBeforeMsg = /* @__PURE__ */ new Map();
@@ -1048,9 +1089,10 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1048
1089
  const sections = sectionsBeforeStep.get(i);
1049
1090
  if (sections) {
1050
1091
  for (const sec of sections) {
1051
- curY += SECTION_TOP_PAD;
1092
+ const bandPad = collapsedBandPad(sec);
1093
+ curY += SECTION_TOP_PAD + bandPad;
1052
1094
  sectionYPositions.set(sec.lineNumber, curY);
1053
- curY += SECTION_BOTTOM_PAD;
1095
+ curY += SECTION_BOTTOM_PAD + bandPad;
1054
1096
  }
1055
1097
  }
1056
1098
  const step = renderSteps[i];
@@ -1063,9 +1105,10 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1063
1105
  curY += isSelfCall ? sSelfCallHeight + 25 : stepSpacing;
1064
1106
  }
1065
1107
  for (const sec of trailingSections) {
1066
- curY += SECTION_TOP_PAD;
1108
+ const bandPad = collapsedBandPad(sec);
1109
+ curY += SECTION_TOP_PAD + bandPad;
1067
1110
  sectionYPositions.set(sec.lineNumber, curY);
1068
- curY += SECTION_BOTTOM_PAD;
1111
+ curY += SECTION_BOTTOM_PAD + bandPad;
1069
1112
  }
1070
1113
  curY += trailingNoteSpace;
1071
1114
  layoutEndY = curY;
@@ -1324,6 +1367,13 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1324
1367
  });
1325
1368
  }
1326
1369
  }
1370
+ const participantTagColor = (id) => {
1371
+ const direct = getTagColor(tagMap?.participants.get(id));
1372
+ if (direct) return direct;
1373
+ if (!collapsedGroupNames.has(id) || !tagKey) return void 0;
1374
+ const meta = collapsedGroupMeta.get(id);
1375
+ return meta?.metadata ? getTagColor(meta.metadata[tagKey]) : void 0;
1376
+ };
1327
1377
  for (const group of groups) {
1328
1378
  if (group.participantIds.length === 0) continue;
1329
1379
  const memberXs = group.participantIds.map((id) => participantX.get(id)).filter((x) => x !== void 0);
@@ -1342,21 +1392,50 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1342
1392
  groupG.append("rect").attr("x", minX).attr("y", boxY).attr("width", maxX - minX).attr("height", participantStartY - boxY).attr("fill", "transparent").attr("class", "group-label-hit");
1343
1393
  groupG.append("text").attr("x", (minX + maxX) / 2).attr("y", boxY + GROUP_LABEL_SIZE + 4).attr("text-anchor", "middle").attr("fill", strokeColor).attr("font-size", GROUP_LABEL_SIZE).attr("font-weight", "bold").attr("opacity", 0.7).attr("pointer-events", "none").attr("class", "group-label").text(group.name);
1344
1394
  }
1395
+ const bandLeftmostX = Math.min(...Array.from(participantX.values()));
1396
+ const bandRightmostX = Math.max(...Array.from(participantX.values()));
1397
+ const sectionLineX1 = bandLeftmostX - sBoxW / 2 - 10;
1398
+ const sectionLineX2 = bandRightmostX + sBoxW / 2 + 10;
1399
+ const sectionLabelX = (sectionLineX1 + sectionLineX2) / 2;
1400
+ const sectionLabelTextFor = (sec) => {
1401
+ if (!isSectionCollapsed(sec)) return sec.label;
1402
+ const count = sectionMsgCounts.get(sec.lineNumber) ?? 0;
1403
+ return `${sec.label} (${count} ${count === 1 ? "message" : "messages"})`;
1404
+ };
1405
+ const sectionLabelBaselineFor = (sec, secY) => isSectionCollapsed(sec) ? secY + COLLAPSED_SECTION_LABEL_BASELINE : secY + 4;
1406
+ const sectionLabelBoxes = sectionRegions.map((region) => {
1407
+ const secY = sectionYPositions.get(region.section.lineNumber);
1408
+ if (secY === void 0) return null;
1409
+ const width = measureText(sectionLabelTextFor(region.section), 11, {
1410
+ bold: true
1411
+ });
1412
+ const baseline = sectionLabelBaselineFor(region.section, secY);
1413
+ return {
1414
+ x1: sectionLabelX - width / 2 - SECTION_LABEL_CLEARANCE_X,
1415
+ x2: sectionLabelX + width / 2 + SECTION_LABEL_CLEARANCE_X,
1416
+ y1: baseline - SECTION_LABEL_CLEARANCE_ABOVE,
1417
+ y2: baseline + SECTION_LABEL_CLEARANCE_BELOW
1418
+ };
1419
+ }).filter((box) => box !== null);
1420
+ const lifelineSegments = (cx, yTop, yBottom) => {
1421
+ const cuts = sectionLabelBoxes.filter((b) => cx >= b.x1 && cx <= b.x2 && b.y2 > yTop && b.y1 < yBottom).sort((a, b) => a.y1 - b.y1);
1422
+ const segments = [];
1423
+ let y = yTop;
1424
+ for (const cut of cuts) {
1425
+ if (cut.y1 > y) segments.push([y, cut.y1]);
1426
+ y = Math.max(y, cut.y2);
1427
+ }
1428
+ if (y < yBottom) segments.push([y, yBottom]);
1429
+ return segments;
1430
+ };
1345
1431
  const lifelineStartY = lifelineStartY0;
1346
1432
  participants.forEach((participant) => {
1347
1433
  const cx = participantX.get(participant.id);
1348
1434
  const cy = participantStartY;
1349
1435
  const pTagValue = tagMap?.participants.get(participant.id);
1350
- const pTagColor = getTagColor(pTagValue);
1351
1436
  const pTagAttr = tagKey && pTagValue ? { key: tagKey, value: pTagValue.toLowerCase() } : void 0;
1352
1437
  const isCollapsedGroup = collapsedGroupNames.has(participant.id);
1353
- let effectiveTagColor = pTagColor;
1354
- if (isCollapsedGroup && !effectiveTagColor) {
1355
- const meta = collapsedGroupMeta.get(participant.id);
1356
- if (meta?.metadata && tagKey) {
1357
- effectiveTagColor = getTagColor(meta.metadata[tagKey]);
1358
- }
1359
- }
1438
+ const effectiveTagColor = participantTagColor(participant.id);
1360
1439
  renderParticipant(
1361
1440
  svg,
1362
1441
  participant,
@@ -1392,10 +1471,16 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1392
1471
  participantG.append("rect").attr("class", "sequence-drill-bar").attr("x", -boxW / 2).attr("y", -GROUP_PADDING_TOP + fullH - drillBarH).attr("width", boxW).attr("height", drillBarH).attr("fill", drillColor).attr("clip-path", `url(#${clipId})`);
1393
1472
  }
1394
1473
  const llY = isCollapsedGroup ? lifelineStartY + GROUP_PADDING_BOTTOM : participant.type === "actor" ? lifelineStartY + ACTOR_LABEL_CLEARANCE : lifelineStartY;
1395
- const llColor = isCollapsedGroup ? effectiveTagColor || palette.textMuted : pTagColor || palette.textMuted;
1396
- const lifelineEl = svg.append("line").attr("x1", cx).attr("y1", llY).attr("x2", cx).attr("y2", lifelineStartY + lifelineLength).attr("stroke", llColor).attr("stroke-width", 1).attr("stroke-dasharray", "6 4").attr("class", "lifeline").attr("data-participant-id", participant.id);
1397
- if (tagKey && pTagValue) {
1398
- lifelineEl.attr(`data-tag-${tagKey}`, pTagValue.toLowerCase());
1474
+ const llColor = effectiveTagColor || palette.textMuted;
1475
+ for (const [segTop, segBottom] of lifelineSegments(
1476
+ cx,
1477
+ llY,
1478
+ lifelineStartY + lifelineLength
1479
+ )) {
1480
+ const lifelineEl = svg.append("line").attr("x1", cx).attr("y1", segTop).attr("x2", cx).attr("y2", segBottom).attr("stroke", llColor).attr("stroke-width", 1).attr("stroke-dasharray", "6 4").attr("class", "lifeline").attr("data-participant-id", participant.id);
1481
+ if (tagKey && pTagValue) {
1482
+ lifelineEl.attr(`data-tag-${tagKey}`, pTagValue.toLowerCase());
1483
+ }
1399
1484
  }
1400
1485
  });
1401
1486
  const FRAME_PADDING_X = 30;
@@ -1403,7 +1488,6 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1403
1488
  const FRAME_LABEL_HEIGHT = 18;
1404
1489
  const SELF_ARROW_PROJECTION = sActivationWidth / 2 + sSelfCallWidth;
1405
1490
  const SELF_ARROW_FRAME_PAD = 10;
1406
- const frameRightmostX = Math.max(...Array.from(participantX.values()));
1407
1491
  const collectMsgIndices = (els) => {
1408
1492
  const indices = [];
1409
1493
  for (const el of els) {
@@ -1576,7 +1660,8 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1576
1660
  if (px === void 0) return;
1577
1661
  const x = px - sActivationWidth / 2 + act.depth * ACTIVATION_NEST_OFFSET;
1578
1662
  const y1 = stepY(act.startStep);
1579
- const y2 = stepY(act.endStep);
1663
+ const rawY2 = stepY(act.endStep);
1664
+ const y2 = act.implicitEnd && rawY2 - ACTIVATION_IMPLICIT_END_CLEARANCE > y1 + sActivationWidth ? rawY2 - ACTIVATION_IMPLICIT_END_CLEARANCE : rawY2;
1580
1665
  const coveredLines = [];
1581
1666
  for (let si = act.startStep; si <= act.endStep; si++) {
1582
1667
  const step = renderSteps[si];
@@ -1626,31 +1711,57 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1626
1711
  const offset = depth * ACTIVATION_NEST_OFFSET;
1627
1712
  return side === "right" ? px + sActivationWidth / 2 + offset : px - sActivationWidth / 2 + offset;
1628
1713
  };
1629
- const leftmostX = Math.min(...Array.from(participantX.values()));
1630
- const rightmostX = frameRightmostX;
1631
- const sectionLineX1 = leftmostX - sBoxW / 2 - 10;
1632
- const sectionLineX2 = rightmostX + sBoxW / 2 + 10;
1633
1714
  for (const region of sectionRegions) {
1634
1715
  const sec = region.section;
1635
1716
  const secY = sectionYPositions.get(sec.lineNumber);
1636
1717
  if (secY === void 0) continue;
1637
- const isCollapsed = collapsedSections?.has(sec.lineNumber) ?? false;
1718
+ const isCollapsed = isSectionCollapsed(sec);
1638
1719
  const lineColor = palette.textMuted;
1639
1720
  const sectionG = svg.append("g").attr("data-section-toggle", "").attr("data-line-number", String(sec.lineNumber)).attr("data-section", "").attr("tabindex", "0").attr("role", "button").attr("aria-expanded", String(!isCollapsed));
1640
- const BAND_HEIGHT = 22;
1721
+ const BAND_HEIGHT = isCollapsed ? COLLAPSED_SECTION_BAND_HEIGHT : SECTION_BAND_HEIGHT;
1641
1722
  const bandX = sectionLineX1 - 10;
1642
1723
  const bandWidth = sectionLineX2 - sectionLineX1 + 20;
1643
- const bandOpacity = isCollapsed ? isDark ? 0.35 : 0.25 : isDark ? 0.1 : 0.08;
1644
- sectionG.append("rect").attr("x", bandX).attr("y", secY - BAND_HEIGHT / 2).attr("width", bandWidth).attr("height", BAND_HEIGHT).attr("fill", lineColor).attr("opacity", bandOpacity).attr("rx", 2).attr("pointer-events", "none").attr("class", "section-divider");
1645
- const msgCount = sectionMsgCounts.get(sec.lineNumber) ?? 0;
1646
- const labelText = isCollapsed ? `${sec.label} (${msgCount} ${msgCount === 1 ? "message" : "messages"})` : sec.label;
1647
- const labelX = (sectionLineX1 + sectionLineX2) / 2;
1648
- const labelHitW = Math.max(
1649
- 80,
1650
- measureText(labelText, 11, { bold: true }) + 24
1651
- );
1724
+ const bandFill = isCollapsed ? legendChromeColors(palette, isDark).groupBg : lineColor;
1725
+ const bandOpacity = isCollapsed ? 1 : isDark ? 0.1 : 0.08;
1726
+ sectionG.append("rect").attr("x", bandX).attr("y", secY - BAND_HEIGHT / 2).attr("width", bandWidth).attr("height", BAND_HEIGHT).attr("fill", bandFill).attr("opacity", bandOpacity).attr("rx", 2).attr("pointer-events", "none").attr("class", "section-divider");
1727
+ const labelText = sectionLabelTextFor(sec);
1728
+ const labelWidth = measureText(labelText, 11, { bold: true });
1729
+ const labelX = sectionLabelX;
1730
+ const labelBaseline = sectionLabelBaselineFor(sec, secY);
1731
+ const labelHitW = Math.max(80, labelWidth + 24);
1652
1732
  sectionG.append("rect").attr("x", labelX - labelHitW / 2).attr("y", secY - BAND_HEIGHT / 2).attr("width", labelHitW).attr("height", BAND_HEIGHT).attr("fill", "transparent").attr("class", "section-label-hit");
1653
- sectionG.append("text").attr("x", labelX).attr("y", secY + 4).attr("text-anchor", "middle").attr("fill", lineColor).attr("font-size", 11).attr("font-weight", "bold").attr("class", "section-label").text(labelText);
1733
+ sectionG.append("text").attr("x", labelX).attr("y", labelBaseline).attr("text-anchor", "middle").attr("fill", lineColor).attr("font-size", 11).attr("font-weight", "bold").attr("class", "section-label").text(labelText);
1734
+ if (!isCollapsed) continue;
1735
+ const participation = summarizeSectionParticipation(
1736
+ messages,
1737
+ region.msgIndices
1738
+ );
1739
+ const markY = secY + COLLAPSED_SECTION_MARK_OFFSET;
1740
+ const involvedXs = participants.filter((p) => participation.has(p.id)).map((p) => participantX.get(p.id)).filter((x) => x !== void 0);
1741
+ if (involvedXs.length > 1) {
1742
+ sectionG.append("line").attr("x1", Math.min(...involvedXs)).attr("x2", Math.max(...involvedXs)).attr("y1", markY).attr("y2", markY).attr("stroke", lineColor).attr("stroke-width", 1).attr("stroke-dasharray", "2 3").attr("opacity", 0.5).attr("pointer-events", "none").attr("class", "section-reach");
1743
+ }
1744
+ for (const participant of participants) {
1745
+ const markX = participantX.get(participant.id);
1746
+ if (markX === void 0) continue;
1747
+ const part = participation.get(participant.id);
1748
+ const mark = sectionG.append("circle").attr("cx", markX).attr("cy", markY).attr("pointer-events", "none").attr("data-participant-id", participant.id);
1749
+ if (!part) {
1750
+ mark.attr("r", 2.5).attr("fill", "none").attr("stroke", lineColor).attr("stroke-width", 1).attr("opacity", 0.5).attr("class", "section-mark section-mark-absent");
1751
+ continue;
1752
+ }
1753
+ const markColor = participantTagColor(participant.id) ?? palette.text;
1754
+ const radius = sectionMarkRadius(part.sends + part.receives);
1755
+ if (part.sends === 0) {
1756
+ mark.attr("r", radius).attr("fill", bandFill).attr("stroke", markColor).attr("stroke-width", 1.6).attr("class", "section-mark section-mark-receives");
1757
+ } else {
1758
+ mark.attr("r", radius).attr("fill", markColor).attr("class", "section-mark section-mark-sends");
1759
+ }
1760
+ }
1761
+ const involvedLabels = participants.filter((p) => participation.has(p.id)).map((p) => p.label);
1762
+ if (involvedLabels.length > 0) {
1763
+ sectionG.attr("aria-label", `${labelText}, ${involvedLabels.join(", ")}`);
1764
+ }
1654
1765
  }
1655
1766
  renderSteps.forEach((step, i) => {
1656
1767
  const fromX = participantX.get(step.from);
@@ -1903,6 +2014,7 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tag
1903
2014
  export {
1904
2015
  applyCollapseProjection,
1905
2016
  groupMessagesBySection,
2017
+ summarizeSectionParticipation,
1906
2018
  buildRenderSequence,
1907
2019
  computeActivations,
1908
2020
  applyPositionOverrides,
@@ -1910,4 +2022,4 @@ export {
1910
2022
  renderSequenceDiagram,
1911
2023
  buildNoteMessageMap
1912
2024
  };
1913
- //# sourceMappingURL=chunk-6IUL6LER.js.map
2025
+ //# sourceMappingURL=chunk-WDLD5MU5.js.map
@@ -1,4 +1,4 @@
1
- import { T as TagGroup, a as DgmoError, P as PaletteConfig, f as PaletteColors, C as CompactViewState } from './dgmo-router-Dd5PTjxl.js';
1
+ import { T as TagGroup, D as DgmoError, a as PaletteColors, C as CompactViewState } from './tag-groups-NUA2F1I5.js';
2
2
 
3
3
  /** A POI / route-stop position: gazetteer name (+ optional ISO scope) or coords. */
4
4
  type PoiPos = {
@@ -458,25 +458,6 @@ interface ResolvedMap {
458
458
  readonly error: string | null;
459
459
  }
460
460
 
461
- /**
462
- * All built-in palettes, keyed by camelCase id. Use directly with render():
463
- *
464
- * await render(text, { palette: palettes.catppuccin });
465
- *
466
- * For preference/settings storage, the `.id` field of each entry is the
467
- * canonical string (e.g. `'tokyo-night'`, `'nord'`) — that's the wire format
468
- * used by share URLs and the CLI `--palette` flag.
469
- */
470
- declare const palettes: {
471
- readonly atlas: PaletteConfig;
472
- readonly blueprint: PaletteConfig;
473
- readonly slate: PaletteConfig;
474
- readonly tidewater: PaletteConfig;
475
- readonly nord: PaletteConfig;
476
- readonly catppuccin: PaletteConfig;
477
- readonly tokyoNight: PaletteConfig;
478
- };
479
-
480
461
  /**
481
462
  * How a host supplies map basemap assets: the data itself, or a loader that
482
463
  * returns it. Declared here (rather than accepting only `MapData`) so a Node
@@ -512,120 +493,4 @@ type RenderForExportOptions = {
512
493
  };
513
494
  declare function renderForExport(content: string, theme: 'light' | 'dark' | 'transparent', palette?: PaletteColors, viewState?: CompactViewState, options?: RenderForExportOptions): Promise<string>;
514
495
 
515
- interface MapPlaceCompletion {
516
- /** Canonical display name (original casing), e.g. `Portland`. */
517
- readonly name: string;
518
- /** Text to insert. ISO-qualified (`Portland US-OR`) iff the name is
519
- * ambiguous in the gazetteer; bare otherwise (disambiguate-once, §24B.8). */
520
- readonly insert: string;
521
- /** Menu label (`Portland — US-OR` when ambiguous, else `Portland`). */
522
- readonly label: string;
523
- /** Secondary detail, e.g. `US-OR · 652,503`. */
524
- readonly detail: string;
525
- readonly iso: string;
526
- readonly sub?: string;
527
- readonly pop: number;
528
- /** `'airport'` for IATA-code entries (icon/grouping affordance); absent or
529
- * `'city'` for gazetteer cities. Cities rank above airports for a shared
530
- * prefix so ~1500 codes never bury city names (ADR-5). */
531
- readonly kind?: 'city' | 'airport';
532
- }
533
- interface MapCompletionOptions {
534
- /** Max results (default 12). */
535
- readonly limit?: number;
536
- /** IATA-coded airports (`airports.json`). When supplied, airport codes
537
- * matching the prefix are offered as a second (post-city) group. Optional —
538
- * absent (old DI bundles / no asset) just yields city-only completions. */
539
- readonly airports?: AirportData;
540
- /** Resolver-inferred map scope (country `US` or subdivision `US-CA`). Biases
541
- * airport ranking so in-region airports sort above out-of-region same-prefix
542
- * ones (ADR-6). Pure rank, never a filter — cross-region airports still
543
- * appear. App passes the document's inferred scope in (Slice 2). */
544
- readonly scopeISO?: string;
545
- }
546
- /**
547
- * Prefix-match city names + alternate-name aliases against the gazetteer,
548
- * rank by population (desc; stable tie-break by gazetteer index), and emit
549
- * ISO-qualified insert text only for ambiguous (same-named) places.
550
- *
551
- * Pure + deterministic. Empty/blank query → `[]` (the caller gates min length).
552
- */
553
- declare function completeMapPlaces(query: string, gazetteer: Gazetteer, opts?: MapCompletionOptions): MapPlaceCompletion[];
554
- /** A POI search hit — a city or airport the resolver can resolve, with the
555
- * exact `token` to paste into DGMO (a `poi`/route endpoint). Powers the CLI
556
- * `dgmo map search` command and the MCP `lookup_map_location` tool, so authors
557
- * (and AI agents) can DISCOVER valid place tokens instead of guessing — e.g.
558
- * learn that "New York" must be `New York City` and that `JFK` is bundled. */
559
- interface MapLocationMatch {
560
- /** Canonical display name (cities) or full airport name. */
561
- readonly name: string;
562
- /** Exact text to use in DGMO. Cities: the name, ISO-qualified when ambiguous
563
- * (`Portland US-OR`). Airports: the upper-case IATA code (`JFK`). */
564
- readonly token: string;
565
- readonly kind: 'city' | 'airport';
566
- readonly iso: string;
567
- readonly sub?: string;
568
- /** City population (0 for airports). */
569
- readonly pop: number;
570
- /** Human detail, e.g. `US-OR · 652,503` or `Airport · John F Kennedy Intl`. */
571
- readonly detail: string;
572
- }
573
- /**
574
- * Substring-search cities + airports for a discovery surface (CLI / MCP), so an
575
- * author can find the exact token the resolver expects. Unlike
576
- * {@link completeMapPlaces} (prefix-only, editor type-ahead), this matches
577
- * anywhere in a city name OR an airport code/name, so "york" finds
578
- * `New York City` and "kennedy" finds `JFK`.
579
- *
580
- * Ranking: exact-name (or exact-IATA) → prefix → substring; within a tier,
581
- * cities by population desc (deterministic index tie-break), cities before
582
- * airports. Pure + deterministic. Empty query → `[]`.
583
- */
584
- declare function searchMapLocations(query: string, gazetteer: Gazetteer, opts?: {
585
- readonly limit?: number;
586
- readonly airports?: AirportData;
587
- }): MapLocationMatch[];
588
- interface MapRegionCompletion {
589
- /** Display name = insert text (the resolver disambiguates cross-layer
590
- * collisions like Georgia by map scope, §24B.8). */
591
- readonly name: string;
592
- /** ISO 3166-1/3166-2 id. */
593
- readonly iso: string;
594
- readonly layer: 'country' | 'us-state';
595
- /** Secondary detail, e.g. `US state · US-CA` or `Country · DE`. */
596
- readonly detail: string;
597
- }
598
- /**
599
- * Prefix-match fill-able region names (countries + US states) for region-fill
600
- * lines. Matches the folded display name OR the ISO code; deterministic
601
- * (alphabetical by name, then layer). Pure. Empty query → `[]`.
602
- *
603
- * `regions` is injected (the `region-names.json` asset, shipped in dist/map-data
604
- * alongside the gazetteer). Cross-layer same-name (Georgia: country GE + state
605
- * US-GA) yields both entries, distinguished by `detail`.
606
- */
607
- declare function completeMapRegions(query: string, regions: readonly RegionName[], opts?: MapCompletionOptions): MapRegionCompletion[];
608
-
609
- /**
610
- * Theme — render mode flag. Selects which palette variant the renderer uses
611
- * for background and text:
612
- * - 'light' → palette.light colors
613
- * - 'dark' → palette.dark colors
614
- * - 'transparent' → no background fill (for embedding in colored containers)
615
- */
616
- type Theme = 'light' | 'dark' | 'transparent';
617
- /**
618
- * `themes` namespace — use with render() options for a typed handle:
619
- *
620
- * await render(text, { theme: themes.dark });
621
- *
622
- * Passing the raw string `'dark'` also works (the underlying type is the
623
- * string-literal union); the namespace is the conventional path.
624
- */
625
- declare const themes: {
626
- readonly light: "light";
627
- readonly dark: "dark";
628
- readonly transparent: "transparent";
629
- };
630
-
631
- export { type AirportData as A, type BoundaryTopology as B, renderForExport as C, type Gazetteer as G, type MapDataSource as M, type ParsedMap as P, type RegionName as R, type Theme as T, type GazetteerEntry as a, type MapCompletionOptions as b, type MapData as c, type MapLocationMatch as d, type MapPlaceCompletion as e, type MapRegionCompletion as f, type RegionNames as g, completeMapPlaces as h, completeMapRegions as i, type ResolvedMap as j, type MapLayoutLegend as k, type GeoExtent as l, type MapDirectives as m, type MapEdge as n, type MapPoi as o, palettes as p, type MapRegion as q, type MapRoute as r, searchMapLocations as s, themes as t, type PoiPos as u, type ProjectionFamily as v, type ResolvedEdge as w, type ResolvedPoi as x, type ResolvedRegion as y, type ResolvedRoute as z };
496
+ export { type AirportData as A, type BoundaryTopology as B, type Gazetteer as G, type MapDataSource as M, type ParsedMap as P, type RegionName as R, type GazetteerEntry as a, type MapData as b, type RegionNames as c, type ResolvedMap as d, type MapLayoutLegend as e, type GeoExtent as f, type MapDirectives as g, type MapEdge as h, type MapPoi as i, type MapRegion as j, type MapRoute as k, type PoiPos as l, type ProjectionFamily as m, type ResolvedEdge as n, type ResolvedPoi as o, type ResolvedRegion as p, type ResolvedRoute as q, renderForExport as r };