@diagrammo/dgmo 0.8.22 → 0.8.23

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 (53) hide show
  1. package/dist/cli.cjs +111 -109
  2. package/dist/editor.cjs +3 -0
  3. package/dist/editor.cjs.map +1 -1
  4. package/dist/editor.js +3 -0
  5. package/dist/editor.js.map +1 -1
  6. package/dist/highlight.cjs +3 -0
  7. package/dist/highlight.cjs.map +1 -1
  8. package/dist/highlight.js +3 -0
  9. package/dist/highlight.js.map +1 -1
  10. package/dist/index.cjs +1010 -215
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +97 -11
  13. package/dist/index.d.ts +97 -11
  14. package/dist/index.js +1001 -213
  15. package/dist/index.js.map +1 -1
  16. package/dist/internal.cjs +380 -0
  17. package/dist/internal.cjs.map +1 -0
  18. package/dist/internal.d.cts +179 -0
  19. package/dist/internal.d.ts +179 -0
  20. package/dist/internal.js +337 -0
  21. package/dist/internal.js.map +1 -0
  22. package/docs/guide/chart-cycle.md +156 -0
  23. package/docs/guide/chart-journey-map.md +179 -0
  24. package/docs/guide/chart-pyramid.md +111 -0
  25. package/docs/guide/registry.json +5 -0
  26. package/docs/language-reference.md +62 -1
  27. package/gallery/fixtures/pyramid/dikw.dgmo +17 -0
  28. package/gallery/fixtures/pyramid/inverted-funnel.dgmo +16 -0
  29. package/gallery/fixtures/pyramid/minimal.dgmo +5 -0
  30. package/package.json +11 -1
  31. package/src/cli.ts +5 -35
  32. package/src/completion.ts +9 -44
  33. package/src/cycle/layout.ts +19 -28
  34. package/src/cycle/renderer.ts +59 -32
  35. package/src/cycle/types.ts +21 -0
  36. package/src/d3.ts +21 -1
  37. package/src/dgmo-router.ts +73 -3
  38. package/src/echarts.ts +1 -1
  39. package/src/editor/keywords.ts +3 -0
  40. package/src/index.ts +13 -2
  41. package/src/infra/parser.ts +2 -2
  42. package/src/internal.ts +16 -0
  43. package/src/journey-map/renderer.ts +112 -47
  44. package/src/org/collapse.ts +81 -0
  45. package/src/org/renderer.ts +212 -4
  46. package/src/pyramid/parser.ts +172 -0
  47. package/src/pyramid/renderer.ts +684 -0
  48. package/src/pyramid/types.ts +28 -0
  49. package/src/render.ts +2 -8
  50. package/src/sequence/parser.ts +62 -20
  51. package/src/sequence/renderer.ts +2 -2
  52. package/src/tech-radar/interactive.ts +54 -0
  53. package/src/utils/parsing.ts +1 -0
package/dist/index.js CHANGED
@@ -1957,7 +1957,8 @@ var init_parsing = __esm({
1957
1957
  "wireframe",
1958
1958
  "tech-radar",
1959
1959
  "cycle",
1960
- "journey-map"
1960
+ "journey-map",
1961
+ "pyramid"
1961
1962
  ]);
1962
1963
  COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
1963
1964
  OPTION_NOCOLON_RE = /^([a-z][a-z0-9-]*)\s+(.+)$/i;
@@ -3199,7 +3200,7 @@ function isSequenceSection(el) {
3199
3200
  function isSequenceNote(el) {
3200
3201
  return "kind" in el && el.kind === "note";
3201
3202
  }
3202
- function parseNoteLine(trimmed, participants, lastMsgFrom) {
3203
+ function parseNoteLine(trimmed, participants, participantIds, sortedParticipantsCache, lastMsgFrom) {
3203
3204
  const lower = trimmed.toLowerCase();
3204
3205
  if (!lower.startsWith("note")) return null;
3205
3206
  if (trimmed.length > 4 && trimmed[4] !== " ") return null;
@@ -3211,7 +3212,7 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3211
3212
  if (!lastMsgFrom) return { kind: "skip" };
3212
3213
  participantId = lastMsgFrom;
3213
3214
  }
3214
- if (participants.some((p) => p.id === participantId)) {
3215
+ if (participantIds.has(participantId)) {
3215
3216
  return { kind: "multi-head", position, participantId };
3216
3217
  }
3217
3218
  }
@@ -3230,11 +3231,15 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3230
3231
  }
3231
3232
  if (!afterPos) {
3232
3233
  if (!lastMsgFrom) return { kind: "skip" };
3233
- if (!participants.some((p) => p.id === lastMsgFrom))
3234
- return { kind: "skip" };
3234
+ if (!participantIds.has(lastMsgFrom)) return { kind: "skip" };
3235
3235
  return { kind: "multi-head", position, participantId: lastMsgFrom };
3236
3236
  }
3237
- const resolved = resolveParticipantAndText(afterPos, participants);
3237
+ const resolved = resolveParticipantAndText(
3238
+ afterPos,
3239
+ participants,
3240
+ participantIds,
3241
+ sortedParticipantsCache
3242
+ );
3238
3243
  if (resolved) {
3239
3244
  if (resolved.text) {
3240
3245
  return {
@@ -3253,8 +3258,7 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3253
3258
  }
3254
3259
  if (hadOf) return { kind: "skip" };
3255
3260
  if (!lastMsgFrom) return { kind: "skip" };
3256
- if (!participants.some((p) => p.id === lastMsgFrom))
3257
- return { kind: "skip" };
3261
+ if (!participantIds.has(lastMsgFrom)) return { kind: "skip" };
3258
3262
  return {
3259
3263
  kind: "single",
3260
3264
  position,
@@ -3263,8 +3267,7 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3263
3267
  };
3264
3268
  }
3265
3269
  if (!lastMsgFrom) return { kind: "skip" };
3266
- if (!participants.some((p) => p.id === lastMsgFrom))
3267
- return { kind: "skip" };
3270
+ if (!participantIds.has(lastMsgFrom)) return { kind: "skip" };
3268
3271
  return {
3269
3272
  kind: "single",
3270
3273
  position: "right",
@@ -3274,20 +3277,20 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3274
3277
  }
3275
3278
  return null;
3276
3279
  }
3277
- function resolveParticipantAndText(input, participants) {
3280
+ function resolveParticipantAndText(input, participants, participantIds, sortedParticipantsCache) {
3278
3281
  if (input.startsWith('"') || input.startsWith("'")) {
3279
3282
  const quote = input[0];
3280
3283
  const endQuote = input.indexOf(quote, 1);
3281
3284
  if (endQuote > 0) {
3282
3285
  const name = input.substring(1, endQuote);
3283
- if (participants.some((p) => p.id === name)) {
3286
+ if (participantIds.has(name)) {
3284
3287
  const text = input.substring(endQuote + 1).trim();
3285
3288
  return { participantId: name, text };
3286
3289
  }
3287
3290
  }
3288
3291
  return null;
3289
3292
  }
3290
- const sorted = [...participants].sort((a, b) => b.id.length - a.id.length);
3293
+ const sorted = sortedParticipantsCache;
3291
3294
  for (const p of sorted) {
3292
3295
  if (input.startsWith(p.id)) {
3293
3296
  const remaining = input.substring(p.id.length);
@@ -3348,6 +3351,18 @@ function parseSequenceDgmo(content) {
3348
3351
  break;
3349
3352
  }
3350
3353
  let activeGroup = null;
3354
+ const participantIds = /* @__PURE__ */ new Set();
3355
+ let sortedParticipantsCache = [];
3356
+ let sortedCacheDirty = true;
3357
+ const getSortedParticipants = () => {
3358
+ if (sortedCacheDirty) {
3359
+ sortedParticipantsCache = [...result.participants].sort(
3360
+ (a, b) => b.id.length - a.id.length
3361
+ );
3362
+ sortedCacheDirty = false;
3363
+ }
3364
+ return sortedParticipantsCache;
3365
+ };
3351
3366
  const participantGroupMap = /* @__PURE__ */ new Map();
3352
3367
  let currentTagGroup = null;
3353
3368
  const aliasMap = /* @__PURE__ */ new Map();
@@ -3593,7 +3608,9 @@ function parseSequenceDgmo(content) {
3593
3608
  const posMatch = remainder.match(/\bposition\s+(-?\d+)/i);
3594
3609
  const alias = akaMatch ? akaMatch[1].trim() : null;
3595
3610
  const position = posMatch ? parseInt(posMatch[1], 10) : void 0;
3596
- if (!result.participants.some((p) => p.id === id)) {
3611
+ if (!participantIds.has(id)) {
3612
+ participantIds.add(id);
3613
+ sortedCacheDirty = true;
3597
3614
  result.participants.push({
3598
3615
  id,
3599
3616
  label: alias || id,
@@ -3623,7 +3640,9 @@ function parseSequenceDgmo(content) {
3623
3640
  contentStarted = true;
3624
3641
  const id = posOnlyMatch[1];
3625
3642
  const position = parseInt(posOnlyMatch[2], 10);
3626
- if (!result.participants.some((p) => p.id === id)) {
3643
+ if (!participantIds.has(id)) {
3644
+ participantIds.add(id);
3645
+ sortedCacheDirty = true;
3627
3646
  result.participants.push({
3628
3647
  id,
3629
3648
  label: id,
@@ -3657,7 +3676,9 @@ function parseSequenceDgmo(content) {
3657
3676
  `'${id}(${color})' syntax is no longer supported \u2014 use 'tag:' groups for coloring`
3658
3677
  );
3659
3678
  contentStarted = true;
3660
- if (!result.participants.some((p) => p.id === id)) {
3679
+ if (!participantIds.has(id)) {
3680
+ participantIds.add(id);
3681
+ sortedCacheDirty = true;
3661
3682
  result.participants.push({
3662
3683
  id,
3663
3684
  label: id,
@@ -3686,7 +3707,8 @@ function parseSequenceDgmo(content) {
3686
3707
  if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !contentStarted || bareMeta)) {
3687
3708
  contentStarted = true;
3688
3709
  const id = bareCore;
3689
- if (!result.participants.some((p) => p.id === id)) {
3710
+ if (!participantIds.has(id)) {
3711
+ participantIds.add(id);
3690
3712
  result.participants.push({
3691
3713
  id,
3692
3714
  label: id,
@@ -3748,7 +3770,9 @@ function parseSequenceDgmo(content) {
3748
3770
  };
3749
3771
  result.messages.push(msg);
3750
3772
  currentContainer().push(msg);
3751
- if (!result.participants.some((p) => p.id === from)) {
3773
+ if (!participantIds.has(from)) {
3774
+ participantIds.add(from);
3775
+ sortedCacheDirty = true;
3752
3776
  result.participants.push({
3753
3777
  id: from,
3754
3778
  label: from,
@@ -3756,7 +3780,9 @@ function parseSequenceDgmo(content) {
3756
3780
  lineNumber
3757
3781
  });
3758
3782
  }
3759
- if (!result.participants.some((p) => p.id === to)) {
3783
+ if (!participantIds.has(to)) {
3784
+ participantIds.add(to);
3785
+ sortedCacheDirty = true;
3760
3786
  result.participants.push({
3761
3787
  id: to,
3762
3788
  label: to,
@@ -3823,7 +3849,9 @@ function parseSequenceDgmo(content) {
3823
3849
  };
3824
3850
  result.messages.push(msg);
3825
3851
  currentContainer().push(msg);
3826
- if (!result.participants.some((p) => p.id === from)) {
3852
+ if (!participantIds.has(from)) {
3853
+ participantIds.add(from);
3854
+ sortedCacheDirty = true;
3827
3855
  result.participants.push({
3828
3856
  id: from,
3829
3857
  label: from,
@@ -3831,7 +3859,9 @@ function parseSequenceDgmo(content) {
3831
3859
  lineNumber
3832
3860
  });
3833
3861
  }
3834
- if (!result.participants.some((p) => p.id === to)) {
3862
+ if (!participantIds.has(to)) {
3863
+ participantIds.add(to);
3864
+ sortedCacheDirty = true;
3835
3865
  result.participants.push({
3836
3866
  id: to,
3837
3867
  label: to,
@@ -3933,6 +3963,8 @@ function parseSequenceDgmo(content) {
3933
3963
  const noteParsed = parseNoteLine(
3934
3964
  trimmed,
3935
3965
  result.participants,
3966
+ participantIds,
3967
+ getSortedParticipants(),
3936
3968
  lastMsgFrom
3937
3969
  );
3938
3970
  if (noteParsed) {
@@ -7321,7 +7353,7 @@ function buildFunnelOption(parsed, textColor, colors, bg, titleConfig) {
7321
7353
  bottom: 20,
7322
7354
  width: "60%",
7323
7355
  sort: "descending",
7324
- gap: 2,
7356
+ gap: 0,
7325
7357
  minSize: "8%"
7326
7358
  };
7327
7359
  return {
@@ -10029,8 +10061,8 @@ function extractPipeMetadata(rest) {
10029
10061
  const tags = {};
10030
10062
  let clean = rest;
10031
10063
  let match;
10032
- const re = new RegExp(PIPE_META_RE.source, "g");
10033
- while ((match = re.exec(rest)) !== null) {
10064
+ PIPE_META_RE.lastIndex = 0;
10065
+ while ((match = PIPE_META_RE.exec(rest)) !== null) {
10034
10066
  tags[match[1].trim()] = match[2].trim();
10035
10067
  clean = clean.replace(match[0], "");
10036
10068
  }
@@ -14158,9 +14190,128 @@ var init_parser15 = __esm({
14158
14190
  }
14159
14191
  });
14160
14192
 
14193
+ // src/pyramid/parser.ts
14194
+ var parser_exports16 = {};
14195
+ __export(parser_exports16, {
14196
+ parsePyramid: () => parsePyramid
14197
+ });
14198
+ function parsePyramid(content) {
14199
+ const result = {
14200
+ type: "pyramid",
14201
+ title: "",
14202
+ titleLineNumber: 0,
14203
+ layers: [],
14204
+ inverted: false,
14205
+ options: {},
14206
+ diagnostics: [],
14207
+ error: null
14208
+ };
14209
+ const lines = content.split("\n");
14210
+ let headerParsed = false;
14211
+ let currentLayer = null;
14212
+ const fail = (line11, message) => {
14213
+ const diag = makeDgmoError(line11, message);
14214
+ result.diagnostics.push(diag);
14215
+ result.error = formatDgmoError(diag);
14216
+ return result;
14217
+ };
14218
+ const warn = (line11, message, severity = "warning") => {
14219
+ result.diagnostics.push(makeDgmoError(line11, message, severity));
14220
+ };
14221
+ const flushLayer = () => {
14222
+ if (currentLayer) {
14223
+ result.layers.push(currentLayer);
14224
+ currentLayer = null;
14225
+ }
14226
+ };
14227
+ for (let i = 0; i < lines.length; i++) {
14228
+ const lineNum = i + 1;
14229
+ const raw = lines[i];
14230
+ const trimmed = raw.trim();
14231
+ if (!trimmed || trimmed.startsWith("//")) continue;
14232
+ const indent = measureIndent(raw);
14233
+ if (!headerParsed) {
14234
+ const firstLineResult = parseFirstLine(trimmed);
14235
+ if (firstLineResult && firstLineResult.chartType === "pyramid") {
14236
+ result.title = firstLineResult.title ?? "";
14237
+ result.titleLineNumber = lineNum;
14238
+ headerParsed = true;
14239
+ continue;
14240
+ }
14241
+ return fail(lineNum, 'Expected "pyramid [Title]" as the first line.');
14242
+ }
14243
+ if (indent === 0 && trimmed.toLowerCase() === "inverted") {
14244
+ result.inverted = true;
14245
+ continue;
14246
+ }
14247
+ if (indent === 0) {
14248
+ flushLayer();
14249
+ const pipeIdx = trimmed.indexOf("|");
14250
+ let label;
14251
+ const description = [];
14252
+ let color;
14253
+ let restMeta = {};
14254
+ if (pipeIdx < 0) {
14255
+ label = trimmed;
14256
+ } else {
14257
+ label = trimmed.substring(0, pipeIdx).trim();
14258
+ const after = trimmed.substring(pipeIdx + 1).trim();
14259
+ if (!after) {
14260
+ } else if (KEY_VALUE_PREFIX_RE.test(after)) {
14261
+ const metadata = parsePipeMetadata([label, after]);
14262
+ color = metadata["color"];
14263
+ const descFromPipe = metadata["description"];
14264
+ if (descFromPipe) description.push(descFromPipe);
14265
+ restMeta = { ...metadata };
14266
+ delete restMeta["color"];
14267
+ delete restMeta["description"];
14268
+ } else {
14269
+ description.push(after);
14270
+ }
14271
+ }
14272
+ if (!label) {
14273
+ warn(lineNum, "Empty layer label.");
14274
+ continue;
14275
+ }
14276
+ currentLayer = {
14277
+ label,
14278
+ lineNumber: lineNum,
14279
+ color,
14280
+ description,
14281
+ metadata: restMeta
14282
+ };
14283
+ continue;
14284
+ }
14285
+ if (!currentLayer) {
14286
+ warn(lineNum, `Unexpected indented line: "${trimmed}".`);
14287
+ continue;
14288
+ }
14289
+ const descLine = trimmed.startsWith("- ") ? `\u2022 ${trimmed.substring(2)}` : trimmed;
14290
+ currentLayer.description.push(descLine);
14291
+ }
14292
+ flushLayer();
14293
+ if (result.layers.length < 2) {
14294
+ return fail(
14295
+ result.titleLineNumber || 1,
14296
+ "pyramid requires at least 2 layers."
14297
+ );
14298
+ }
14299
+ return result;
14300
+ }
14301
+ var KEY_VALUE_PREFIX_RE;
14302
+ var init_parser16 = __esm({
14303
+ "src/pyramid/parser.ts"() {
14304
+ "use strict";
14305
+ init_diagnostics();
14306
+ init_parsing();
14307
+ KEY_VALUE_PREFIX_RE = /^\s*[A-Za-z][A-Za-z0-9_-]*\s*:/;
14308
+ }
14309
+ });
14310
+
14161
14311
  // src/dgmo-router.ts
14162
14312
  var dgmo_router_exports = {};
14163
14313
  __export(dgmo_router_exports, {
14314
+ CHART_TYPE_DESCRIPTIONS: () => CHART_TYPE_DESCRIPTIONS,
14164
14315
  getAllChartTypes: () => getAllChartTypes,
14165
14316
  getRenderCategory: () => getRenderCategory,
14166
14317
  isExtendedChartType: () => isExtendedChartType,
@@ -14230,32 +14381,39 @@ function parseDgmo(content) {
14230
14381
  const colonDiag = detectColonChartType(content);
14231
14382
  if (colonDiag) {
14232
14383
  const fallback = parseVisualization(content).diagnostics;
14233
- return { diagnostics: [colonDiag, ...fallback] };
14384
+ return { diagnostics: [colonDiag, ...fallback], chartType: null };
14234
14385
  }
14235
- return { diagnostics: parseVisualization(content).diagnostics };
14386
+ return {
14387
+ diagnostics: parseVisualization(content).diagnostics,
14388
+ chartType: null
14389
+ };
14236
14390
  }
14237
14391
  const directParser = PARSE_DISPATCH.get(chartType);
14238
14392
  if (directParser) {
14239
14393
  const result2 = directParser(content);
14240
14394
  return {
14241
- diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)]
14395
+ diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)],
14396
+ chartType
14242
14397
  };
14243
14398
  }
14244
14399
  if (STANDARD_CHART_TYPES2.has(chartType)) {
14245
14400
  const result2 = parseChart(content);
14246
14401
  return {
14247
- diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)]
14402
+ diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)],
14403
+ chartType
14248
14404
  };
14249
14405
  }
14250
14406
  if (ECHART_TYPES.has(chartType)) {
14251
14407
  const result2 = parseExtendedChart(content);
14252
14408
  return {
14253
- diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)]
14409
+ diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)],
14410
+ chartType
14254
14411
  };
14255
14412
  }
14256
14413
  const result = parseVisualization(content);
14257
14414
  return {
14258
- diagnostics: [...result.diagnostics, ...detectEmptyContent(content)]
14415
+ diagnostics: [...result.diagnostics, ...detectEmptyContent(content)],
14416
+ chartType
14259
14417
  };
14260
14418
  }
14261
14419
  function detectColonChartType(content) {
@@ -14298,7 +14456,7 @@ function detectEmptyContent(content) {
14298
14456
  }
14299
14457
  return [];
14300
14458
  }
14301
- var GANTT_DURATION_RE, GANTT_DATE_RE, C4_TYPE_RE, DATA_CHART_TYPES, VISUALIZATION_TYPES, DIAGRAM_TYPES, EXTENDED_CHART_TYPES, STANDARD_CHART_TYPES2, ECHART_TYPES, PARSE_DISPATCH, ALL_KNOWN_TYPES;
14459
+ var GANTT_DURATION_RE, GANTT_DATE_RE, C4_TYPE_RE, DATA_CHART_TYPES, VISUALIZATION_TYPES, DIAGRAM_TYPES, EXTENDED_CHART_TYPES, STANDARD_CHART_TYPES2, CHART_TYPE_DESCRIPTIONS, ECHART_TYPES, PARSE_DISPATCH, ALL_KNOWN_TYPES;
14302
14460
  var init_dgmo_router = __esm({
14303
14461
  "src/dgmo-router.ts"() {
14304
14462
  "use strict";
@@ -14322,6 +14480,7 @@ var init_dgmo_router = __esm({
14322
14480
  init_parser13();
14323
14481
  init_parser14();
14324
14482
  init_parser15();
14483
+ init_parser16();
14325
14484
  init_parsing();
14326
14485
  init_diagnostics();
14327
14486
  GANTT_DURATION_RE = /^\d+(?:\.\d+)?(?:min|bd|d|w|m|q|y|h)(?:\?)?\s+/;
@@ -14352,7 +14511,8 @@ var init_dgmo_router = __esm({
14352
14511
  "venn",
14353
14512
  "quadrant",
14354
14513
  "tech-radar",
14355
- "cycle"
14514
+ "cycle",
14515
+ "pyramid"
14356
14516
  ]);
14357
14517
  DIAGRAM_TYPES = /* @__PURE__ */ new Set([
14358
14518
  "sequence",
@@ -14390,6 +14550,47 @@ var init_dgmo_router = __esm({
14390
14550
  "polar-area",
14391
14551
  "bar-stacked"
14392
14552
  ]);
14553
+ CHART_TYPE_DESCRIPTIONS = {
14554
+ bar: "Bar chart \u2014 categorical comparisons",
14555
+ line: "Line chart \u2014 trends over time; supports era bands (era start -> end Label (color)) for annotating named periods",
14556
+ "multi-line": "Multi-line chart \u2014 multiple series trends over time; supports era bands",
14557
+ area: "Area chart \u2014 filled line chart; supports era bands",
14558
+ pie: "Pie chart \u2014 part-to-whole proportions",
14559
+ doughnut: "Doughnut chart \u2014 ring-style pie chart",
14560
+ radar: "Radar chart \u2014 multi-dimensional metrics",
14561
+ "polar-area": "Polar area chart \u2014 radial bar chart",
14562
+ "bar-stacked": "Stacked bar chart \u2014 multi-series categorical",
14563
+ scatter: "Scatter plot \u2014 2D data points or bubble chart",
14564
+ sankey: "Sankey diagram \u2014 flow/allocation visualization",
14565
+ chord: "Chord diagram \u2014 circular flow relationships",
14566
+ function: "Function plot \u2014 mathematical expressions",
14567
+ heatmap: "Heatmap \u2014 matrix intensity visualization",
14568
+ funnel: "Funnel chart \u2014 conversion pipeline",
14569
+ slope: "Slope chart \u2014 change between two periods",
14570
+ wordcloud: "Word cloud \u2014 term frequency visualization",
14571
+ arc: "Arc diagram \u2014 network relationships",
14572
+ timeline: "Timeline \u2014 events, eras, and date ranges",
14573
+ venn: "Venn diagram \u2014 set overlaps",
14574
+ quadrant: "Quadrant chart \u2014 2x2 positioning matrix",
14575
+ "tech-radar": "Tech radar \u2014 technology adoption quadrants (adopt/trial/assess/hold)",
14576
+ cycle: "Cycle diagram \u2014 cyclical process visualization (PDCA, OODA, DevOps loops)",
14577
+ sequence: "Sequence diagram \u2014 message/interaction flows",
14578
+ flowchart: "Flowchart \u2014 decision trees and process flows",
14579
+ class: "Class diagram \u2014 UML class hierarchies",
14580
+ er: "ER diagram \u2014 database schemas and relationships",
14581
+ org: "Org chart \u2014 hierarchical tree structures",
14582
+ kanban: "Kanban board \u2014 task/workflow columns",
14583
+ c4: "C4 diagram \u2014 system architecture (context, container, component, deployment)",
14584
+ state: "State diagram \u2014 state machine / lifecycle transitions",
14585
+ sitemap: "Sitemap \u2014 navigable UI structure with pages, groups, and cross-link arrows",
14586
+ infra: "Infrastructure diagram \u2014 traffic flow with RPS computation, capacity modeling, and latency analysis",
14587
+ gantt: "Gantt chart \u2014 project scheduling with task dependencies and milestones",
14588
+ "boxes-and-lines": "Boxes and lines \u2014 general-purpose node-edge diagrams with nested groups, tags, and shape inference",
14589
+ mindmap: "Mindmap \u2014 radial hierarchy of ideas branching from a central topic",
14590
+ wireframe: "Wireframe \u2014 low-fidelity UI layout with panels, controls, and annotations",
14591
+ "journey-map": "Journey map \u2014 user experience flow with emotion scores, phases, and annotations",
14592
+ pyramid: "Pyramid \u2014 hierarchical layered pyramid (Maslow, DIKW, learning pyramid); inverted for funnel-of-learning style"
14593
+ };
14393
14594
  ECHART_TYPES = /* @__PURE__ */ new Set([
14394
14595
  "scatter",
14395
14596
  "sankey",
@@ -14415,7 +14616,8 @@ var init_dgmo_router = __esm({
14415
14616
  ["wireframe", (c) => parseWireframe(c)],
14416
14617
  ["tech-radar", (c) => parseTechRadar(c)],
14417
14618
  ["cycle", (c) => parseCycle(c)],
14418
- ["journey-map", (c) => parseJourneyMap(c)]
14619
+ ["journey-map", (c) => parseJourneyMap(c)],
14620
+ ["pyramid", (c) => parsePyramid(c)]
14419
14621
  ]);
14420
14622
  ALL_KNOWN_TYPES = /* @__PURE__ */ new Set([
14421
14623
  ...DATA_CHART_TYPES,
@@ -15224,7 +15426,8 @@ var init_layout = __esm({
15224
15426
  // src/org/collapse.ts
15225
15427
  var collapse_exports = {};
15226
15428
  __export(collapse_exports, {
15227
- collapseOrgTree: () => collapseOrgTree
15429
+ collapseOrgTree: () => collapseOrgTree,
15430
+ focusOrgTree: () => focusOrgTree
15228
15431
  });
15229
15432
  function cloneNode(node) {
15230
15433
  return {
@@ -15279,6 +15482,49 @@ function collapseOrgTree(original, collapsedIds) {
15279
15482
  hiddenCounts
15280
15483
  };
15281
15484
  }
15485
+ function findNodeWithPath(nodes, targetId, path) {
15486
+ for (const node of nodes) {
15487
+ if (node.id === targetId) {
15488
+ return { node, path };
15489
+ }
15490
+ const result = findNodeWithPath(node.children, targetId, [
15491
+ ...path,
15492
+ {
15493
+ id: node.id,
15494
+ label: node.label,
15495
+ lineNumber: node.lineNumber,
15496
+ color: node.color,
15497
+ metadata: { ...node.metadata },
15498
+ isContainer: node.isContainer
15499
+ }
15500
+ ]);
15501
+ if (result) return result;
15502
+ }
15503
+ return null;
15504
+ }
15505
+ function focusOrgTree(original, focusNodeId) {
15506
+ const found = findNodeWithPath(original.roots, focusNodeId, []);
15507
+ if (!found) return null;
15508
+ const isRoot = original.roots.some((r) => r.id === focusNodeId);
15509
+ if (isRoot) {
15510
+ return {
15511
+ parsed: {
15512
+ ...original,
15513
+ roots: [cloneNode(found.node)]
15514
+ },
15515
+ ancestorPath: []
15516
+ };
15517
+ }
15518
+ const cloned = cloneNode(found.node);
15519
+ cloned.parentId = null;
15520
+ return {
15521
+ parsed: {
15522
+ ...original,
15523
+ roots: [cloned]
15524
+ },
15525
+ ancestorPath: found.path
15526
+ };
15527
+ }
15282
15528
  var init_collapse = __esm({
15283
15529
  "src/org/collapse.ts"() {
15284
15530
  "use strict";
@@ -15338,7 +15584,7 @@ function containerFill(palette, isDark, nodeColor2) {
15338
15584
  function containerStroke(palette, nodeColor2) {
15339
15585
  return nodeColor2 ?? palette.textMuted;
15340
15586
  }
15341
- function renderOrg(container, parsed, layout, palette, isDark, onClickItem, exportDims, activeTagGroup, hiddenAttributes) {
15587
+ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, exportDims, activeTagGroup, hiddenAttributes, ancestorPath) {
15342
15588
  d3Selection.select(container).selectAll(":not([data-d3-tooltip])").remove();
15343
15589
  const width = exportDims?.width ?? container.clientWidth;
15344
15590
  const height = exportDims?.height ?? container.clientHeight;
@@ -15351,8 +15597,10 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
15351
15597
  const legendReserve = fixedLegend ? LEGEND_HEIGHT + LEGEND_FIXED_GAP : 0;
15352
15598
  const fixedTitle = !exportDims && !!parsed.title;
15353
15599
  const titleReserve = fixedTitle ? TITLE_HEIGHT : 0;
15600
+ const hasAncestorTrail = !exportDims && ancestorPath && ancestorPath.length > 0;
15601
+ const ancestorTrailHeight = hasAncestorTrail ? ancestorPath.length * ANCESTOR_ROW_HEIGHT + ANCESTOR_TRAIL_BOTTOM_GAP : 0;
15354
15602
  const diagramW = layout.width;
15355
- let diagramH = layout.height + (fixedTitle ? 0 : titleOffset);
15603
+ let diagramH = layout.height + (fixedTitle ? 0 : titleOffset) + ancestorTrailHeight;
15356
15604
  if (fixedLegend) {
15357
15605
  diagramH -= layoutLegendShift;
15358
15606
  }
@@ -15384,11 +15632,13 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
15384
15632
  }
15385
15633
  }
15386
15634
  }
15387
- const contentG = mainG.append("g").attr("transform", `translate(0, ${fixedTitle ? 0 : titleOffset})`);
15635
+ const contentYShift = (fixedTitle ? 0 : titleOffset) + ancestorTrailHeight;
15636
+ const contentG = mainG.append("g").attr("transform", `translate(0, ${contentYShift})`);
15388
15637
  const displayNames = /* @__PURE__ */ new Map();
15389
15638
  for (const group of parsed.tagGroups) {
15390
15639
  displayNames.set(group.name.toLowerCase(), group.name);
15391
15640
  }
15641
+ const rootNodeIds = new Set(parsed.roots.map((r) => r.id));
15392
15642
  const colorOff = parsed.options?.color === "off";
15393
15643
  for (const c of layout.containers) {
15394
15644
  const cG = contentG.append("g").attr("transform", `translate(${c.x}, ${c.y})`).attr("class", "org-container").attr("data-line-number", String(c.lineNumber));
@@ -15435,6 +15685,18 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
15435
15685
  cG.append("clipPath").attr("id", clipId).append("rect").attr("width", c.width).attr("height", c.height).attr("rx", CONTAINER_RADIUS);
15436
15686
  cG.append("rect").attr("x", COLLAPSE_BAR_INSET).attr("y", c.height - COLLAPSE_BAR_HEIGHT).attr("width", c.width - COLLAPSE_BAR_INSET * 2).attr("height", COLLAPSE_BAR_HEIGHT).attr("fill", containerStroke(palette, colorOff ? void 0 : c.color)).attr("clip-path", `url(#${clipId})`).attr("class", "org-collapse-bar");
15437
15687
  }
15688
+ if (!exportDims && c.hasChildren && !rootNodeIds.has(c.nodeId)) {
15689
+ const iconSize = 14;
15690
+ const iconPad = 5;
15691
+ const iconX = c.width - iconSize - iconPad;
15692
+ const iconY = iconPad;
15693
+ const focusG = cG.append("g").attr("class", "org-focus-icon").attr("data-focus-node", c.nodeId).attr("transform", `translate(${iconX}, ${iconY})`);
15694
+ focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
15695
+ const cx = iconSize / 2;
15696
+ const cy = iconSize / 2;
15697
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 1.5);
15698
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
15699
+ }
15438
15700
  }
15439
15701
  for (const edge of layout.edges) {
15440
15702
  if (edge.points.length < 2) continue;
@@ -15493,6 +15755,58 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
15493
15755
  nodeG.append("clipPath").attr("id", clipId).append("rect").attr("width", node.width).attr("height", node.height).attr("rx", CARD_RADIUS);
15494
15756
  nodeG.append("rect").attr("x", COLLAPSE_BAR_INSET).attr("y", node.height - COLLAPSE_BAR_HEIGHT).attr("width", node.width - COLLAPSE_BAR_INSET * 2).attr("height", COLLAPSE_BAR_HEIGHT).attr("fill", nodeStroke(palette, colorOff ? void 0 : node.color)).attr("clip-path", `url(#${clipId})`).attr("class", "org-collapse-bar");
15495
15757
  }
15758
+ if (!exportDims && node.hasChildren && !rootNodeIds.has(node.id)) {
15759
+ const iconSize = 14;
15760
+ const iconPad = 5;
15761
+ const iconX = node.width - iconSize - iconPad;
15762
+ const iconY = iconPad;
15763
+ const focusG = nodeG.append("g").attr("class", "org-focus-icon").attr("data-focus-node", node.id).attr("transform", `translate(${iconX}, ${iconY})`);
15764
+ focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
15765
+ const cx = iconSize / 2;
15766
+ const cy = iconSize / 2;
15767
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 1.5);
15768
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
15769
+ }
15770
+ }
15771
+ if (hasAncestorTrail) {
15772
+ const rootNode = layout.nodes.find((n) => rootNodeIds.has(n.id));
15773
+ const rootContainer = !rootNode ? layout.containers.find((c) => rootNodeIds.has(c.nodeId)) : null;
15774
+ const rootCenterX = rootNode ? rootNode.x : rootContainer ? rootContainer.x + rootContainer.width / 2 : null;
15775
+ const rootTopY = rootNode ? rootNode.y : rootContainer ? rootContainer.y : null;
15776
+ if (rootCenterX !== null && rootTopY !== null) {
15777
+ const trailBottomY = rootTopY - ANCESTOR_TRAIL_BOTTOM_GAP;
15778
+ const trailG = contentG.append("g").attr("class", "org-ancestor-trail");
15779
+ const count = ancestorPath.length;
15780
+ const dotPositions = [];
15781
+ for (let i = 0; i < count; i++) {
15782
+ const fromBottom = count - 1 - i;
15783
+ dotPositions.push(trailBottomY - fromBottom * ANCESTOR_ROW_HEIGHT);
15784
+ }
15785
+ const lineTopY = dotPositions[0];
15786
+ trailG.append("line").attr("x1", rootCenterX).attr("y1", lineTopY).attr("x2", rootCenterX).attr("y2", rootTopY).attr("stroke", palette.textMuted).attr("stroke-width", 1.5).attr("stroke-opacity", 0.4);
15787
+ for (let i = 0; i < count; i++) {
15788
+ const ancestor = ancestorPath[i];
15789
+ const dotY = dotPositions[i];
15790
+ const resolvedColor = ancestor.color ?? resolveTagColor(
15791
+ ancestor.metadata,
15792
+ parsed.tagGroups,
15793
+ activeTagGroup ?? null,
15794
+ ancestor.isContainer
15795
+ );
15796
+ const dotColor = resolvedColor ?? palette.textMuted;
15797
+ const rowG = trailG.append("g").attr("class", "org-ancestor-node").attr("data-focus-ancestor", ancestor.id).style("cursor", "pointer").attr("transform", `translate(${rootCenterX}, ${dotY})`);
15798
+ rowG.append("rect").attr("x", -ANCESTOR_DOT_R - 2).attr("y", -ANCESTOR_DOT_R - 2).attr("width", 120).attr("height", ANCESTOR_DOT_R * 2 + 4).attr("fill", "transparent");
15799
+ rowG.append("circle").attr("cx", 0).attr("cy", 0).attr("r", ANCESTOR_DOT_R).attr("fill", dotColor);
15800
+ rowG.append("text").attr("x", ANCESTOR_DOT_R + 6).attr("y", ANCESTOR_LABEL_FONT_SIZE * 0.35).attr("fill", palette.textMuted).attr("font-size", ANCESTOR_LABEL_FONT_SIZE).text(ancestor.label);
15801
+ rowG.on("mouseenter", function() {
15802
+ d3Selection.select(this).select("circle").attr("r", ANCESTOR_DOT_R + 1);
15803
+ d3Selection.select(this).select("text").attr("fill", palette.text);
15804
+ }).on("mouseleave", function() {
15805
+ d3Selection.select(this).select("circle").attr("r", ANCESTOR_DOT_R);
15806
+ d3Selection.select(this).select("text").attr("fill", palette.textMuted);
15807
+ });
15808
+ }
15809
+ }
15496
15810
  }
15497
15811
  if (fixedLegend || legendOnly || exportDims && hasLegend) {
15498
15812
  const groups = layout.legend.map((g) => ({
@@ -15589,13 +15903,14 @@ function renderOrgForExport(content, theme, palette) {
15589
15903
  return extractExportSvg(container, theme);
15590
15904
  });
15591
15905
  }
15592
- var DIAGRAM_PADDING, MAX_SCALE, TITLE_HEIGHT, LABEL_FONT_SIZE, META_FONT_SIZE, META_LINE_HEIGHT2, HEADER_HEIGHT2, SEPARATOR_GAP2, EDGE_STROKE_WIDTH, NODE_STROKE_WIDTH, CARD_RADIUS, CONTAINER_RADIUS, CONTAINER_LABEL_FONT_SIZE, CONTAINER_META_FONT_SIZE, CONTAINER_META_LINE_HEIGHT2, CONTAINER_HEADER_HEIGHT, COLLAPSE_BAR_HEIGHT, COLLAPSE_BAR_INSET, LEGEND_FIXED_GAP;
15906
+ var DIAGRAM_PADDING, MAX_SCALE, TITLE_HEIGHT, LABEL_FONT_SIZE, META_FONT_SIZE, META_LINE_HEIGHT2, HEADER_HEIGHT2, SEPARATOR_GAP2, EDGE_STROKE_WIDTH, NODE_STROKE_WIDTH, CARD_RADIUS, CONTAINER_RADIUS, CONTAINER_LABEL_FONT_SIZE, CONTAINER_META_FONT_SIZE, CONTAINER_META_LINE_HEIGHT2, CONTAINER_HEADER_HEIGHT, COLLAPSE_BAR_HEIGHT, COLLAPSE_BAR_INSET, ANCESTOR_DOT_R, ANCESTOR_LABEL_FONT_SIZE, ANCESTOR_ROW_HEIGHT, ANCESTOR_TRAIL_BOTTOM_GAP, LEGEND_FIXED_GAP;
15593
15907
  var init_renderer = __esm({
15594
15908
  "src/org/renderer.ts"() {
15595
15909
  "use strict";
15596
15910
  init_fonts();
15597
15911
  init_export_container();
15598
15912
  init_color_utils();
15913
+ init_tag_groups();
15599
15914
  init_parser4();
15600
15915
  init_layout();
15601
15916
  init_legend_constants();
@@ -15619,6 +15934,10 @@ var init_renderer = __esm({
15619
15934
  CONTAINER_HEADER_HEIGHT = 28;
15620
15935
  COLLAPSE_BAR_HEIGHT = 6;
15621
15936
  COLLAPSE_BAR_INSET = 0;
15937
+ ANCESTOR_DOT_R = 4;
15938
+ ANCESTOR_LABEL_FONT_SIZE = 11;
15939
+ ANCESTOR_ROW_HEIGHT = 22;
15940
+ ANCESTOR_TRAIL_BOTTOM_GAP = 16;
15622
15941
  LEGEND_FIXED_GAP = 8;
15623
15942
  }
15624
15943
  });
@@ -30603,7 +30922,22 @@ function renderQuarterCircle(svg, parsed, quadrant, qColor, palette, isDark, wid
30603
30922
  const innerR = ri * ringBandWidth;
30604
30923
  const outerR = (ri + 1) * ringBandWidth;
30605
30924
  const fillColor = ri % 2 === 0 ? palette.bg : mix(palette.bg, palette.border, 0.15);
30925
+ const ringName = parsed.rings[ri].name;
30606
30926
  svg.append("path").attr("d", arcGen(innerR, outerR)).attr("fill", fillColor).attr("stroke", mutedColor).attr("stroke-width", 0.5);
30927
+ svg.append("path").attr("d", arcGen(innerR, outerR)).attr("fill", "transparent").attr("data-ring-arc", ringName).style("cursor", "pointer").on("mouseenter", () => {
30928
+ d3Selection14.select(rootContainer).selectAll("[data-ring-arc]").each(function() {
30929
+ const el = d3Selection14.select(this);
30930
+ const isMatch = this.getAttribute("data-ring-arc") === ringName;
30931
+ el.attr("fill", isMatch ? qColor : "transparent").attr(
30932
+ "opacity",
30933
+ isMatch ? 0.15 : 1
30934
+ );
30935
+ });
30936
+ dimExceptRing(rootContainer, ringName);
30937
+ }).on("mouseleave", () => {
30938
+ d3Selection14.select(rootContainer).selectAll("[data-ring-arc]").attr("fill", "transparent").attr("opacity", 1);
30939
+ clearDim(rootContainer);
30940
+ });
30607
30941
  }
30608
30942
  const ringOrder = parsed.rings.map((r) => r.name);
30609
30943
  const angularPadding = 0.08;
@@ -30666,10 +31000,21 @@ function dimExcept(root, lineNum) {
30666
31000
  el.style.opacity = el.getAttribute("data-line-number") === lineNum ? "1" : String(DIM_OPACITY);
30667
31001
  });
30668
31002
  }
31003
+ function dimExceptRing(root, ringName) {
31004
+ root.querySelectorAll("[data-line-number]").forEach((el) => {
31005
+ el.style.opacity = el.getAttribute("data-ring") === ringName ? "1" : String(DIM_OPACITY);
31006
+ });
31007
+ root.querySelectorAll("[data-ring-group]").forEach((el) => {
31008
+ el.style.opacity = el.getAttribute("data-ring-group") === ringName ? "1" : String(DIM_OPACITY);
31009
+ });
31010
+ }
30669
31011
  function clearDim(root) {
30670
31012
  root.querySelectorAll("[data-line-number]").forEach((el) => {
30671
31013
  el.style.opacity = "1";
30672
31014
  });
31015
+ root.querySelectorAll("[data-ring-group]").forEach((el) => {
31016
+ el.style.opacity = "1";
31017
+ });
30673
31018
  }
30674
31019
  function renderHtmlPanel(panel, parsed, quadrant, qColor, palette, isDark, rootContainer, onClickItem) {
30675
31020
  const ringOrder = parsed.rings.map((r) => r.name);
@@ -30681,11 +31026,13 @@ function renderHtmlPanel(panel, parsed, quadrant, qColor, palette, isDark, rootC
30681
31026
  const blips = quadrant.blips.filter((b) => b.ring === ringName);
30682
31027
  if (blips.length === 0) continue;
30683
31028
  const ringGroup = document.createElement("div");
31029
+ ringGroup.setAttribute("data-ring-group", ringName);
30684
31030
  ringGroup.style.cssText = `
30685
31031
  background: ${palette.surface};
30686
31032
  border-radius: 8px;
30687
31033
  padding: 10px;
30688
31034
  margin-bottom: 12px;
31035
+ transition: opacity 0.15s;
30689
31036
  `;
30690
31037
  const header = document.createElement("div");
30691
31038
  header.style.cssText = `
@@ -32039,7 +32386,13 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32039
32386
  const containerW = exportDims?.width ?? container.clientWidth;
32040
32387
  const containerH = exportDims?.height ?? container.clientHeight;
32041
32388
  const useContainerFit = !exportDims && containerW > 0 && containerH > 0;
32042
- const svg = d3.select(container).append("svg").attr("xmlns", "http://www.w3.org/2000/svg").attr("width", useContainerFit ? containerW : layout.totalWidth).attr("height", useContainerFit ? containerH : layout.totalHeight).attr("viewBox", `0 0 ${layout.totalWidth} ${layout.totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("font-family", FONT_FAMILY);
32389
+ const svg = d3.select(container).append("svg").attr("xmlns", "http://www.w3.org/2000/svg").attr(
32390
+ "width",
32391
+ useContainerFit ? containerW : exportDims?.width ?? layout.totalWidth
32392
+ ).attr(
32393
+ "height",
32394
+ useContainerFit ? containerH : exportDims?.height ?? layout.totalHeight
32395
+ ).attr("viewBox", `0 0 ${layout.totalWidth} ${layout.totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("font-family", FONT_FAMILY);
32043
32396
  svg.append("rect").attr("width", layout.totalWidth).attr("height", layout.totalHeight).attr("fill", palette.bg);
32044
32397
  const defs = svg.append("defs");
32045
32398
  const curveGradient = defs.append("linearGradient").attr("id", "journey-curve-gradient").attr("x1", "0").attr("y1", "0").attr("x2", "0").attr("y2", "1");
@@ -32111,7 +32464,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32111
32464
  const legendY = PADDING2 + (parsed.title ? FONT_SIZE_TITLE + 8 : 0);
32112
32465
  const legendG = svg.append("g").attr("class", "journey-legend").attr("transform", `translate(${legendX},${legendY})`);
32113
32466
  const legendConfig = {
32114
- groups: allLegendGroups,
32467
+ groups: parsed.tagGroups,
32115
32468
  position: {
32116
32469
  placement: "top-center",
32117
32470
  titleRelation: "inline-with-title"
@@ -32180,7 +32533,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32180
32533
  const y = layout.curveAreaBottom - (score - 1) / 4 * (layout.curveAreaBottom - layout.curveAreaTop - 120) - 10;
32181
32534
  curveG.append("line").attr("x1", PADDING2).attr("x2", layout.totalWidth - PADDING2).attr("y1", y).attr("y2", y).attr("stroke", palette.textMuted).attr("stroke-opacity", GRID_LINE_OPACITY).attr("stroke-dasharray", "4,4");
32182
32535
  const SCORE_LABEL_R = 8;
32183
- const labelG = curveG.append("g").attr("class", "journey-score-label");
32536
+ const labelG = curveG.append("g").attr("class", "journey-score-label").attr("data-score", String(score));
32184
32537
  renderScoreFace(
32185
32538
  labelG,
32186
32539
  PADDING2 - SCORE_LABEL_R - 2,
@@ -32190,35 +32543,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32190
32543
  SCORE_LABEL_R
32191
32544
  );
32192
32545
  if (!exportDims) {
32193
- const scoreStr = String(score);
32194
32546
  labelG.style("cursor", "pointer");
32195
- labelG.on("mouseenter", () => {
32196
- svg.selectAll(".journey-step").each(function() {
32197
- const hit = this.getAttribute("data-score") === scoreStr;
32198
- d3.select(this).style("opacity", hit ? "1" : String(DIM_HOVER));
32199
- });
32200
- svg.selectAll(".journey-face").each(function() {
32201
- const hit = this.getAttribute("data-score") === scoreStr;
32202
- const sel = d3.select(this);
32203
- sel.style("opacity", hit ? "1" : String(DIM_HOVER));
32204
- if (hit) {
32205
- const fcx = parseFloat(sel.attr("data-cx") ?? "0");
32206
- const fcy = parseFloat(sel.attr("data-cy") ?? "0");
32207
- sel.attr(
32208
- "transform",
32209
- `translate(${fcx},${fcy}) scale(1.3) translate(${-fcx},${-fcy})`
32210
- );
32211
- } else {
32212
- sel.attr("transform", null);
32213
- }
32214
- });
32215
- svg.selectAll(".journey-thought").style("opacity", String(DIM_HOVER));
32216
- });
32217
- labelG.on("mouseleave", () => {
32218
- svg.selectAll(".journey-step").style("opacity", null);
32219
- svg.selectAll(".journey-face").style("opacity", null).attr("transform", null);
32220
- svg.selectAll(".journey-thought").style("opacity", null);
32221
- });
32222
32547
  }
32223
32548
  }
32224
32549
  if (layout.curvePoints.length >= 2) {
@@ -32404,6 +32729,41 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32404
32729
  if (!exportDims) {
32405
32730
  const DIM_OPACITY2 = 0.35;
32406
32731
  let lockedLine = null;
32732
+ let lockedScore = null;
32733
+ const applyScoreDimming = (activeScore) => {
32734
+ const scoreStr = String(activeScore);
32735
+ svg.selectAll(".journey-step").each(function() {
32736
+ const hit = this.getAttribute("data-score") === scoreStr;
32737
+ d3.select(this).style("opacity", hit ? "1" : String(DIM_HOVER));
32738
+ });
32739
+ svg.selectAll(".journey-face").each(function() {
32740
+ const hit = this.getAttribute("data-score") === scoreStr;
32741
+ const sel = d3.select(this);
32742
+ sel.style("opacity", hit ? "1" : String(DIM_HOVER));
32743
+ if (hit) {
32744
+ const fcx = parseFloat(sel.attr("data-cx") ?? "0");
32745
+ const fcy = parseFloat(sel.attr("data-cy") ?? "0");
32746
+ sel.attr(
32747
+ "transform",
32748
+ `translate(${fcx},${fcy}) scale(1.3) translate(${-fcx},${-fcy})`
32749
+ );
32750
+ } else {
32751
+ sel.attr("transform", null);
32752
+ }
32753
+ });
32754
+ svg.selectAll(".journey-thought").style("opacity", String(DIM_HOVER));
32755
+ svg.selectAll(".journey-score-label").each(function() {
32756
+ const sel = d3.select(this);
32757
+ const s = sel.attr("data-score");
32758
+ sel.style("opacity", s === scoreStr ? "1" : String(DIM_HOVER));
32759
+ });
32760
+ };
32761
+ const clearScoreDimming = () => {
32762
+ svg.selectAll(".journey-step").style("opacity", null);
32763
+ svg.selectAll(".journey-face").style("opacity", null).attr("transform", null);
32764
+ svg.selectAll(".journey-thought").style("opacity", null);
32765
+ svg.selectAll(".journey-score-label").style("opacity", null);
32766
+ };
32407
32767
  const applyDimming = (activeLine) => {
32408
32768
  svg.selectAll(".journey-step").each(function() {
32409
32769
  const el = d3.select(this);
@@ -32458,7 +32818,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32458
32818
  const lines = wrapText4(thoughtText, THOUGHT_MAX_W, THOUGHT_FONT);
32459
32819
  const textW = Math.min(
32460
32820
  THOUGHT_MAX_W,
32461
- Math.max(...lines.map((l) => l.length * 4.8))
32821
+ Math.max(...lines.map((l) => l.length * THOUGHT_FONT * 0.6))
32462
32822
  );
32463
32823
  const bw = textW + THOUGHT_PAD_X * 2;
32464
32824
  const bh = lines.length * THOUGHT_LINE_H + THOUGHT_PAD_Y * 2;
@@ -32479,9 +32839,11 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32479
32839
  };
32480
32840
  svg.on("click", (event) => {
32481
32841
  const target = event.target;
32482
- if (!target.closest(".journey-face") && !target.closest(".journey-step") && !target.closest(".journey-phase")) {
32842
+ if (!target.closest(".journey-face") && !target.closest(".journey-step") && !target.closest(".journey-phase") && !target.closest(".journey-score-label")) {
32483
32843
  lockedLine = null;
32844
+ lockedScore = null;
32484
32845
  clearDimming();
32846
+ clearScoreDimming();
32485
32847
  }
32486
32848
  });
32487
32849
  const showThoughtForLine = (ln) => {
@@ -32495,17 +32857,22 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32495
32857
  svg.selectAll(".journey-face").each(function() {
32496
32858
  const el = d3.select(this);
32497
32859
  el.on("mouseenter", () => {
32498
- if (lockedLine !== null) return;
32860
+ if (lockedLine !== null || lockedScore !== null) return;
32499
32861
  const ln = parseInt(el.attr("data-line-number") ?? "0", 10);
32500
32862
  if (ln) {
32501
32863
  applyDimming(ln);
32502
32864
  showThoughtForLine(ln);
32503
32865
  }
32504
32866
  }).on("mouseleave", () => {
32505
- if (lockedLine !== null) return;
32867
+ if (lockedLine !== null || lockedScore !== null) return;
32506
32868
  clearDimming();
32507
32869
  }).on("click", (event) => {
32508
32870
  event.stopPropagation();
32871
+ if (lockedScore !== null) {
32872
+ lockedScore = null;
32873
+ clearScoreDimming();
32874
+ return;
32875
+ }
32509
32876
  const ln = parseInt(el.attr("data-line-number") ?? "0", 10);
32510
32877
  if (lockedLine === ln) {
32511
32878
  lockedLine = null;
@@ -32521,17 +32888,22 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32521
32888
  svg.selectAll(".journey-step").each(function() {
32522
32889
  const el = d3.select(this);
32523
32890
  el.on("mouseenter", () => {
32524
- if (lockedLine !== null) return;
32891
+ if (lockedLine !== null || lockedScore !== null) return;
32525
32892
  const ln = parseInt(el.attr("data-line-number") ?? "0", 10);
32526
32893
  if (ln) {
32527
32894
  applyDimming(ln);
32528
32895
  showThoughtForLine(ln);
32529
32896
  }
32530
32897
  }).on("mouseleave", () => {
32531
- if (lockedLine !== null) return;
32898
+ if (lockedLine !== null || lockedScore !== null) return;
32532
32899
  clearDimming();
32533
32900
  }).on("click", (event) => {
32534
32901
  event.stopPropagation();
32902
+ if (lockedScore !== null) {
32903
+ lockedScore = null;
32904
+ clearScoreDimming();
32905
+ return;
32906
+ }
32535
32907
  const ln = parseInt(el.attr("data-line-number") ?? "0", 10);
32536
32908
  if (lockedLine === ln) {
32537
32909
  lockedLine = null;
@@ -32544,6 +32916,30 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32544
32916
  }
32545
32917
  });
32546
32918
  });
32919
+ svg.selectAll(".journey-score-label").each(function() {
32920
+ const el = d3.select(this);
32921
+ const score = parseInt(el.attr("data-score") ?? "0", 10);
32922
+ el.on("mouseenter", () => {
32923
+ if (lockedLine !== null || lockedScore !== null) return;
32924
+ applyScoreDimming(score);
32925
+ }).on("mouseleave", () => {
32926
+ if (lockedLine !== null || lockedScore !== null) return;
32927
+ clearScoreDimming();
32928
+ }).on("click", (event) => {
32929
+ event.stopPropagation();
32930
+ if (lockedLine !== null) {
32931
+ lockedLine = null;
32932
+ clearDimming();
32933
+ }
32934
+ if (lockedScore === score) {
32935
+ lockedScore = null;
32936
+ clearScoreDimming();
32937
+ } else {
32938
+ lockedScore = score;
32939
+ applyScoreDimming(score);
32940
+ }
32941
+ });
32942
+ });
32547
32943
  }
32548
32944
  }
32549
32945
  function resolveStepColor(step, scoreColor, activeGroup, tagGroups, _palette) {
@@ -32695,8 +33091,8 @@ function renderScoreFace(parent, cx, cy, score, palette, radius) {
32695
33091
  ).attr("fill", "none").attr("stroke", eyeColor).attr("stroke-width", 1.2).attr("stroke-linecap", "round");
32696
33092
  return g;
32697
33093
  }
32698
- function wrapText4(text, maxWidth, _fontSize) {
32699
- const charWidth = 4.8;
33094
+ function wrapText4(text, maxWidth, fontSize) {
33095
+ const charWidth = fontSize * 0.6;
32700
33096
  const maxChars = Math.floor(maxWidth / charWidth);
32701
33097
  if (maxChars <= 0) return [text];
32702
33098
  const words = text.split(/\s+/);
@@ -32715,7 +33111,7 @@ function wrapText4(text, maxWidth, _fontSize) {
32715
33111
  return lines;
32716
33112
  }
32717
33113
  function truncateText(text, maxWidth) {
32718
- const maxChars = Math.floor(maxWidth / 4.8);
33114
+ const maxChars = Math.floor(maxWidth / 6.6);
32719
33115
  if (text.length <= maxChars) return text;
32720
33116
  return text.substring(0, maxChars - 1) + "\u2026";
32721
33117
  }
@@ -32815,6 +33211,21 @@ var init_renderer13 = __esm({
32815
33211
  }
32816
33212
  });
32817
33213
 
33214
+ // src/cycle/types.ts
33215
+ function arrowHeadLength(strokeWidth) {
33216
+ return BASE_ARROW_SIZE + ARROW_SCALE * Math.sqrt(strokeWidth);
33217
+ }
33218
+ var DEFAULT_EDGE_WIDTH, MIN_EDGE_WIDTH, BASE_ARROW_SIZE, ARROW_SCALE;
33219
+ var init_types2 = __esm({
33220
+ "src/cycle/types.ts"() {
33221
+ "use strict";
33222
+ DEFAULT_EDGE_WIDTH = 3;
33223
+ MIN_EDGE_WIDTH = 2;
33224
+ BASE_ARROW_SIZE = 8;
33225
+ ARROW_SCALE = 6;
33226
+ }
33227
+ });
33228
+
32818
33229
  // src/cycle/layout.ts
32819
33230
  function computeCycleLayout(parsed, options) {
32820
33231
  const width = options?.width ?? 800;
@@ -33149,8 +33560,11 @@ function computeEdgePaths(layoutNodes, parsed, cx, cy, radius, isClockwise) {
33149
33560
  return parsed.edges.map((edge) => {
33150
33561
  const src = layoutNodes[edge.sourceIndex];
33151
33562
  const tgt = layoutNodes[edge.targetIndex];
33152
- const strokeWidth = edge.width ?? DEFAULT_EDGE_WIDTH;
33153
- const arrowLen = ARROWHEAD_MARKER_W * strokeWidth;
33563
+ const strokeWidth = Math.max(
33564
+ edge.width ?? DEFAULT_EDGE_WIDTH,
33565
+ MIN_EDGE_WIDTH
33566
+ );
33567
+ const arrowLen = arrowHeadLength(strokeWidth) * 0.9;
33154
33568
  const { path, labelX, labelY, labelAngle } = buildEdgeArc(
33155
33569
  src,
33156
33570
  tgt,
@@ -33172,7 +33586,7 @@ function computeEdgePaths(layoutNodes, parsed, cx, cy, radius, isClockwise) {
33172
33586
  });
33173
33587
  }
33174
33588
  function fitToCanvas(nodes, edges, parsed, cx, cy, radius, width, height, _isClockwise) {
33175
- const PADDING3 = 10;
33589
+ const PADDING3 = 30;
33176
33590
  let contentMinX = Infinity, contentMaxX = -Infinity;
33177
33591
  let contentMinY = Infinity, contentMaxY = -Infinity;
33178
33592
  for (const n of nodes) {
@@ -33225,17 +33639,7 @@ function fitToCanvas(nodes, edges, parsed, cx, cy, radius, width, height, _isClo
33225
33639
  }
33226
33640
  function buildEdgeArc(src, tgt, cx, cy, radius, isClockwise, arrowLength = 0) {
33227
33641
  const dir = isClockwise ? 1 : -1;
33228
- const startAngle = src.isCircle ? circleNodeExitAngle(src.width / 2, radius, src.angle, dir) : circleRectExitAngle(
33229
- src.x,
33230
- src.y,
33231
- src.width / 2,
33232
- src.height / 2,
33233
- cx,
33234
- cy,
33235
- radius,
33236
- src.angle,
33237
- dir
33238
- );
33642
+ const startAngle = src.angle;
33239
33643
  const nodeEndAngle = tgt.isCircle ? circleNodeExitAngle(tgt.width / 2, radius, tgt.angle, -dir) : circleRectExitAngle(
33240
33644
  tgt.x,
33241
33645
  tgt.y,
@@ -33265,10 +33669,11 @@ function buildEdgeArc(src, tgt, cx, cy, radius, isClockwise, arrowLength = 0) {
33265
33669
  const labelY = cy + labelR * Math.sin(midAngle);
33266
33670
  return { path, labelX, labelY, labelAngle: midAngle };
33267
33671
  }
33268
- var MIN_ARC_ANGLE, LABEL_CHAR_W, CIRCLE_LABEL_CHAR_W, DESC_CHAR_W, MIN_NODE_WIDTH3, MAX_NODE_WIDTH2, PLAIN_NODE_HEIGHT, HEADER_HEIGHT5, DESC_LINE_HEIGHT7, DESC_PAD_Y, NODE_PAD_X, MIN_CIRCLE_RADIUS, CIRCLE_PAD, DEFAULT_EDGE_WIDTH, ARROWHEAD_MARKER_W, EDGE_LABEL_CHAR_W;
33672
+ var MIN_ARC_ANGLE, LABEL_CHAR_W, CIRCLE_LABEL_CHAR_W, DESC_CHAR_W, MIN_NODE_WIDTH3, MAX_NODE_WIDTH2, PLAIN_NODE_HEIGHT, HEADER_HEIGHT5, DESC_LINE_HEIGHT7, DESC_PAD_Y, NODE_PAD_X, MIN_CIRCLE_RADIUS, CIRCLE_PAD, EDGE_LABEL_CHAR_W;
33269
33673
  var init_layout13 = __esm({
33270
33674
  "src/cycle/layout.ts"() {
33271
33675
  "use strict";
33676
+ init_types2();
33272
33677
  MIN_ARC_ANGLE = 15 * Math.PI / 180;
33273
33678
  LABEL_CHAR_W = 8;
33274
33679
  CIRCLE_LABEL_CHAR_W = 10;
@@ -33282,8 +33687,6 @@ var init_layout13 = __esm({
33282
33687
  NODE_PAD_X = 20;
33283
33688
  MIN_CIRCLE_RADIUS = 35;
33284
33689
  CIRCLE_PAD = 14;
33285
- DEFAULT_EDGE_WIDTH = 3;
33286
- ARROWHEAD_MARKER_W = 8;
33287
33690
  EDGE_LABEL_CHAR_W = 7;
33288
33691
  }
33289
33692
  });
@@ -33367,19 +33770,25 @@ function renderCycle(container, parsed, palette, isDark, onClickItem, exportDims
33367
33770
  const g = svg.append("g").attr("transform", `translate(0, ${diagramTop})`);
33368
33771
  const defs = svg.append("defs");
33369
33772
  const defaultNodeColor = palette.primary;
33370
- const arrowColors = /* @__PURE__ */ new Set();
33371
- for (let i = 0; i < parsed.edges.length; i++) {
33372
- const edge = parsed.edges[i];
33773
+ const markerKeys = /* @__PURE__ */ new Set();
33774
+ for (const edge of parsed.edges) {
33373
33775
  const color = resolveEdgeColor(edge, parsed, palette, defaultNodeColor);
33374
- arrowColors.add(color);
33776
+ const sw = Math.max(edge.width ?? DEFAULT_EDGE_WIDTH, MIN_EDGE_WIDTH);
33777
+ const key = `${color}|${sw}`;
33778
+ if (!markerKeys.has(key)) {
33779
+ markerKeys.add(key);
33780
+ ensureArrowMarker(defs, color, sw);
33781
+ }
33375
33782
  }
33376
- ensureArrowMarkers2(defs, arrowColors);
33377
33783
  for (let i = 0; i < layout.edges.length; i++) {
33378
33784
  const le = layout.edges[i];
33379
33785
  const edge = parsed.edges[i];
33380
33786
  const color = resolveEdgeColor(edge, parsed, palette, defaultNodeColor);
33381
- const strokeWidth = edge.width ?? DEFAULT_EDGE_WIDTH2;
33382
- const markerId = `cycle-arrow-${color.replace("#", "")}`;
33787
+ const strokeWidth = Math.max(
33788
+ edge.width ?? DEFAULT_EDGE_WIDTH,
33789
+ MIN_EDGE_WIDTH
33790
+ );
33791
+ const markerId = arrowMarkerId(color, strokeWidth);
33383
33792
  const edgeG = g.append("g").attr("class", "cycle-edge");
33384
33793
  if (edge.lineNumber) {
33385
33794
  edgeG.attr("data-line-number", edge.lineNumber);
@@ -33525,16 +33934,16 @@ function resolveEdgeColor(edge, parsed, palette, defaultNodeColor) {
33525
33934
  }
33526
33935
  return defaultNodeColor;
33527
33936
  }
33528
- function ensureArrowMarkers2(defs, colors) {
33529
- for (const color of colors) {
33530
- const id = `cycle-arrow-${color.replace("#", "")}`;
33531
- defs.append("marker").attr("id", id).attr("viewBox", `0 0 ${ARROWHEAD_W5 * 2} ${ARROWHEAD_H5 * 2}`).attr("refX", 0).attr("refY", ARROWHEAD_H5).attr("markerWidth", ARROWHEAD_W5).attr("markerHeight", ARROWHEAD_H5).attr("orient", "auto").append("polygon").attr(
33532
- "points",
33533
- `0,0 ${ARROWHEAD_W5 * 2},${ARROWHEAD_H5} 0,${ARROWHEAD_H5 * 2}`
33534
- ).attr("fill", color);
33535
- }
33937
+ function arrowMarkerId(color, strokeWidth) {
33938
+ return `cycle-arrow-${color.replace("#", "")}-w${strokeWidth}`;
33536
33939
  }
33537
- var DEFAULT_EDGE_WIDTH2, ARROWHEAD_W5, ARROWHEAD_H5, NODE_FONT_SIZE5, DESC_FONT_SIZE5, EDGE_LABEL_FONT_SIZE9, DESC_LINE_HEIGHT8, TITLE_AREA_HEIGHT;
33940
+ function ensureArrowMarker(defs, color, strokeWidth) {
33941
+ const id = arrowMarkerId(color, strokeWidth);
33942
+ const mw = arrowHeadLength(strokeWidth) / strokeWidth;
33943
+ const mh = Math.max(1.5, mw * 0.5);
33944
+ defs.append("marker").attr("id", id).attr("viewBox", `0 0 ${mw} ${mh}`).attr("refX", mw * 0.1).attr("refY", mh / 2).attr("markerWidth", mw).attr("markerHeight", mh).attr("orient", "auto").append("polygon").attr("points", `0,0 ${mw},${mh / 2} 0,${mh}`).attr("fill", color);
33945
+ }
33946
+ var NODE_FONT_SIZE5, DESC_FONT_SIZE5, EDGE_LABEL_FONT_SIZE9, DESC_LINE_HEIGHT8, TITLE_AREA_HEIGHT;
33538
33947
  var init_renderer14 = __esm({
33539
33948
  "src/cycle/renderer.ts"() {
33540
33949
  "use strict";
@@ -33545,10 +33954,8 @@ var init_renderer14 = __esm({
33545
33954
  init_color_utils();
33546
33955
  init_colors();
33547
33956
  init_inline_markdown();
33957
+ init_types2();
33548
33958
  init_layout13();
33549
- DEFAULT_EDGE_WIDTH2 = 3;
33550
- ARROWHEAD_W5 = 8;
33551
- ARROWHEAD_H5 = 8;
33552
33959
  NODE_FONT_SIZE5 = 13;
33553
33960
  DESC_FONT_SIZE5 = 11;
33554
33961
  EDGE_LABEL_FONT_SIZE9 = 11;
@@ -33557,6 +33964,397 @@ var init_renderer14 = __esm({
33557
33964
  }
33558
33965
  });
33559
33966
 
33967
+ // src/pyramid/renderer.ts
33968
+ var renderer_exports15 = {};
33969
+ __export(renderer_exports15, {
33970
+ renderPyramid: () => renderPyramid,
33971
+ renderPyramidForExport: () => renderPyramidForExport
33972
+ });
33973
+ import * as d3Selection17 from "d3-selection";
33974
+ function renderPyramid(container, parsed, palette, isDark, onClickItem, exportDims) {
33975
+ if (parsed.layers.length === 0) return;
33976
+ d3Selection17.select(container).selectAll(":not([data-d3-tooltip])").remove();
33977
+ const width = exportDims?.width ?? container.clientWidth;
33978
+ const height = exportDims?.height ?? container.clientHeight;
33979
+ if (width <= 0 || height <= 0) return;
33980
+ const hasAnyDescription = parsed.layers.some((l) => l.description.length > 0);
33981
+ const titleH = parsed.title ? TITLE_AREA_HEIGHT2 : 0;
33982
+ const bodyTop = titleH + V_MARGIN;
33983
+ const bodyBottom = height - V_MARGIN;
33984
+ const bodyHeight = Math.max(60, bodyBottom - bodyTop);
33985
+ const sideMargin = width * H_MARGIN_FRAC;
33986
+ const usableWidth = width - sideMargin * 2;
33987
+ const N = parsed.layers.length;
33988
+ const singleProbe = computeLayout2({
33989
+ width,
33990
+ usableWidth,
33991
+ sideMargin,
33992
+ bodyTop,
33993
+ bodyHeight,
33994
+ layers: parsed.layers,
33995
+ hasDescription: hasAnyDescription,
33996
+ alternate: false
33997
+ });
33998
+ const anyOverflow = singleProbe.wraps.some((w) => w.overflows);
33999
+ const useAlternate = anyOverflow && hasAnyDescription && N >= 2;
34000
+ const layout = useAlternate ? computeLayout2({
34001
+ width,
34002
+ usableWidth,
34003
+ sideMargin,
34004
+ bodyTop,
34005
+ bodyHeight,
34006
+ layers: parsed.layers,
34007
+ hasDescription: true,
34008
+ alternate: true
34009
+ }) : singleProbe;
34010
+ const svg = d3Selection17.select(container).append("svg").attr("width", width).attr("height", height).attr("xmlns", "http://www.w3.org/2000/svg").style("font-family", FONT_FAMILY);
34011
+ svg.append("style").text(
34012
+ ".pyramid-desc-full{display:none}.dgmo-pyramid-layer-highlight.pyramid-desc-short{display:none}.dgmo-pyramid-layer-highlight.pyramid-desc-full{display:inline}.dgmo-pyramid-hidden{display:none}"
34013
+ );
34014
+ svg.append("rect").attr("width", width).attr("height", height).attr("fill", palette.bg);
34015
+ if (parsed.title) {
34016
+ const titleText = svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", TITLE_Y).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-family", FONT_FAMILY).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).attr("data-line-number", parsed.titleLineNumber).text(parsed.title).style("cursor", onClickItem ? "pointer" : "default");
34017
+ if (onClickItem) {
34018
+ titleText.on("click", () => onClickItem(parsed.titleLineNumber));
34019
+ }
34020
+ }
34021
+ const seriesColors2 = getSeriesColors(palette);
34022
+ const layerBase = isDark ? palette.surface : palette.bg;
34023
+ const resolveSolid = (layer, i) => {
34024
+ if (layer.color) {
34025
+ const named = resolveColor(layer.color, palette);
34026
+ if (named) return named;
34027
+ }
34028
+ return seriesColors2[i % seriesColors2.length];
34029
+ };
34030
+ const diagramG = svg.append("g").attr("class", "pyramid-body");
34031
+ for (let i = 0; i < N; i++) {
34032
+ const layer = parsed.layers[i];
34033
+ const topEdgeY = layout.pyramidTop + i * layout.layerH;
34034
+ const botEdgeY = topEdgeY + layout.layerH;
34035
+ const topHalf = halfWidthAt(i, N, layout.baseWidth, parsed.inverted);
34036
+ const botHalf = halfWidthAt(i + 1, N, layout.baseWidth, parsed.inverted);
34037
+ const polyPoints = [
34038
+ [layout.pyramidCx - topHalf, topEdgeY],
34039
+ [layout.pyramidCx + topHalf, topEdgeY],
34040
+ [layout.pyramidCx + botHalf, botEdgeY],
34041
+ [layout.pyramidCx - botHalf, botEdgeY]
34042
+ ].map((p) => `${p[0]},${p[1]}`).join(" ");
34043
+ const solidColor = resolveSolid(layer, i);
34044
+ const fillColor = mix(solidColor, layerBase, 30);
34045
+ const layerG = diagramG.append("g").attr("class", "pyramid-layer").attr("data-line-number", layer.lineNumber);
34046
+ if (onClickItem) {
34047
+ const ln = layer.lineNumber;
34048
+ layerG.style("cursor", "pointer").on("click", () => onClickItem(ln));
34049
+ }
34050
+ layerG.append("polygon").attr("points", polyPoints).attr("fill", fillColor).attr("stroke", solidColor).attr("stroke-width", 2);
34051
+ const midY = (topEdgeY + botEdgeY) / 2;
34052
+ const labelFitsInside = Math.min(topHalf, botHalf) * 2 > layout.labelFont * 4;
34053
+ const textColor = labelFitsInside ? contrastText(fillColor, "#eceff4", "#2e3440") : palette.text;
34054
+ const labelText = layerG.append("text").attr("x", layout.pyramidCx).attr("y", midY).attr("dy", "0.35em").attr("text-anchor", "middle").attr("fill", textColor).attr("font-family", FONT_FAMILY).attr("font-size", layout.labelFont).attr("font-weight", 600);
34055
+ renderInlineText(labelText, layer.label, palette);
34056
+ if (layer.description.length > 0) {
34057
+ const side = useAlternate ? i % 2 === 0 ? "right" : "left" : "right";
34058
+ const wrap = layout.wraps[i];
34059
+ renderLayerDescriptions(
34060
+ diagramG,
34061
+ layer,
34062
+ side,
34063
+ wrap,
34064
+ layout,
34065
+ midY,
34066
+ solidColor,
34067
+ palette,
34068
+ titleH + V_MARGIN,
34069
+ height - V_MARGIN,
34070
+ onClickItem
34071
+ );
34072
+ }
34073
+ }
34074
+ }
34075
+ function renderPyramidForExport(container, parsed, palette, isDark, exportDims) {
34076
+ renderPyramid(container, parsed, palette, isDark, void 0, exportDims);
34077
+ }
34078
+ function computeLayout2(input) {
34079
+ const {
34080
+ width,
34081
+ usableWidth,
34082
+ sideMargin,
34083
+ bodyTop,
34084
+ bodyHeight,
34085
+ layers,
34086
+ hasDescription,
34087
+ alternate
34088
+ } = input;
34089
+ const N = layers.length;
34090
+ const pyramidShareFrac = hasDescription ? alternate ? PYRAMID_SHARE_ALTERNATE : PYRAMID_SHARE_WITH_DESC : BASE_WIDTH_FRAC_NO_DESC;
34091
+ const pyramidBandWidth = usableWidth * pyramidShareFrac;
34092
+ const maxBaseByHeight = bodyHeight / PITCH_RATIO;
34093
+ const baseWidth = Math.min(pyramidBandWidth, maxBaseByHeight);
34094
+ const pyramidH = baseWidth * PITCH_RATIO;
34095
+ let pyramidCx;
34096
+ if (!hasDescription) {
34097
+ pyramidCx = width / 2;
34098
+ } else if (alternate) {
34099
+ pyramidCx = width / 2;
34100
+ } else {
34101
+ pyramidCx = sideMargin + pyramidBandWidth / 2;
34102
+ }
34103
+ const pyramidTop = bodyTop + (bodyHeight - pyramidH) / 2;
34104
+ const layerH = pyramidH / N;
34105
+ const labelFont = clamp(
34106
+ Math.round(layerH * 0.38),
34107
+ LABEL_FONT_MIN,
34108
+ LABEL_FONT_MAX
34109
+ );
34110
+ const descFont = clamp(
34111
+ Math.round(layerH * 0.22),
34112
+ DESC_FONT_MIN,
34113
+ DESC_FONT_MAX
34114
+ );
34115
+ const descLineHeight = Math.round(descFont * 1.35);
34116
+ const pyramidRightEdge = pyramidCx + baseWidth / 2;
34117
+ const pyramidLeftEdge = pyramidCx - baseWidth / 2;
34118
+ const rightAccentX = pyramidRightEdge + DESC_GAP;
34119
+ const rightTextX = rightAccentX + DESC_ACCENT_WIDTH + DESC_ACCENT_GAP;
34120
+ const rightTextWidth = Math.max(80, width - sideMargin - rightTextX);
34121
+ const leftAccentX = pyramidLeftEdge - DESC_GAP - DESC_ACCENT_WIDTH;
34122
+ const leftTextWidth = Math.max(
34123
+ 80,
34124
+ leftAccentX - DESC_ACCENT_GAP - sideMargin
34125
+ );
34126
+ const leftTextX = sideMargin;
34127
+ const wraps = layers.map((layer, i) => {
34128
+ if (layer.description.length === 0) {
34129
+ return { allLines: [], overflows: false, shortLineCount: 0 };
34130
+ }
34131
+ const side = alternate ? i % 2 === 0 ? "right" : "left" : "right";
34132
+ const colWidth = side === "right" ? rightTextWidth : leftTextWidth;
34133
+ const wrapped = [];
34134
+ for (const line11 of layer.description) {
34135
+ wrapped.push(...wrapText5(line11, colWidth, descFont));
34136
+ }
34137
+ const bandCap = Math.max(1, Math.floor(layerH / descLineHeight) - 0);
34138
+ const overflows = wrapped.length > bandCap;
34139
+ const shortLineCount = overflows ? bandCap : wrapped.length;
34140
+ return { allLines: wrapped, overflows, shortLineCount };
34141
+ });
34142
+ return {
34143
+ alternate,
34144
+ baseWidth,
34145
+ pyramidCx,
34146
+ pyramidTop,
34147
+ pyramidH,
34148
+ layerH,
34149
+ labelFont,
34150
+ descFont,
34151
+ descLineHeight,
34152
+ rightTextX,
34153
+ rightTextWidth,
34154
+ leftTextX,
34155
+ leftTextWidth,
34156
+ rightAccentX,
34157
+ leftAccentX,
34158
+ wraps
34159
+ };
34160
+ }
34161
+ function halfWidthAt(edgeIdx, n, baseWidth, inverted) {
34162
+ const topNarrow = !inverted;
34163
+ const frac = topNarrow ? edgeIdx / n : (n - edgeIdx) / n;
34164
+ return frac * baseWidth / 2;
34165
+ }
34166
+ function renderLayerDescriptions(parentG, layer, side, wrap, layout, midY, accentColor, palette, topBound, bottomBound, onClickItem) {
34167
+ const { descFont, descLineHeight } = layout;
34168
+ const accentX = side === "right" ? layout.rightAccentX : layout.leftAccentX;
34169
+ const textX = side === "right" ? layout.rightTextX : layout.leftTextX;
34170
+ const textAnchor = side === "right" ? "start" : "end";
34171
+ const textLineX = side === "right" ? textX : layout.leftAccentX - DESC_ACCENT_GAP;
34172
+ const availableH = bottomBound - topBound;
34173
+ const fullMaxLines = Math.max(1, Math.floor(availableH / descLineHeight));
34174
+ const fullLines = truncateWithEllipsis(wrap.allLines, fullMaxLines);
34175
+ if (!wrap.overflows) {
34176
+ renderDescriptionVariant({
34177
+ parentG,
34178
+ layer,
34179
+ lines: wrap.allLines,
34180
+ className: "pyramid-desc",
34181
+ accentX,
34182
+ accentColor,
34183
+ textX: textLineX,
34184
+ textAnchor,
34185
+ midY,
34186
+ descFont,
34187
+ descLineHeight,
34188
+ palette,
34189
+ topBound,
34190
+ bottomBound,
34191
+ onClickItem,
34192
+ variant: "short"
34193
+ });
34194
+ return;
34195
+ }
34196
+ const shortLines = buildShortLines(wrap);
34197
+ renderDescriptionVariant({
34198
+ parentG,
34199
+ layer,
34200
+ lines: shortLines,
34201
+ className: "pyramid-desc pyramid-desc-short",
34202
+ accentX,
34203
+ accentColor,
34204
+ textX: textLineX,
34205
+ textAnchor,
34206
+ midY,
34207
+ descFont,
34208
+ descLineHeight,
34209
+ palette,
34210
+ topBound,
34211
+ bottomBound,
34212
+ onClickItem,
34213
+ variant: "short"
34214
+ });
34215
+ renderDescriptionVariant({
34216
+ parentG,
34217
+ layer,
34218
+ lines: fullLines,
34219
+ className: "pyramid-desc pyramid-desc-full",
34220
+ accentX,
34221
+ accentColor,
34222
+ textX: textLineX,
34223
+ textAnchor,
34224
+ midY,
34225
+ descFont,
34226
+ descLineHeight,
34227
+ palette,
34228
+ topBound,
34229
+ bottomBound,
34230
+ onClickItem,
34231
+ variant: "full"
34232
+ });
34233
+ }
34234
+ function truncateWithEllipsis(lines, maxLines) {
34235
+ if (lines.length <= maxLines) return lines.slice();
34236
+ const visible = lines.slice(0, maxLines);
34237
+ if (visible.length === 0) return visible;
34238
+ const last = visible[visible.length - 1];
34239
+ visible[visible.length - 1] = last.endsWith("\u2026") ? last : `${last} \u2026`;
34240
+ return visible;
34241
+ }
34242
+ function buildShortLines(wrap) {
34243
+ if (!wrap.overflows) return wrap.allLines.slice();
34244
+ const visible = wrap.allLines.slice(0, wrap.shortLineCount);
34245
+ if (visible.length === 0) return [];
34246
+ const last = visible[visible.length - 1];
34247
+ visible[visible.length - 1] = last.endsWith("\u2026") ? last : `${last} \u2026`;
34248
+ return visible;
34249
+ }
34250
+ function renderDescriptionVariant(args) {
34251
+ const {
34252
+ parentG,
34253
+ layer,
34254
+ lines,
34255
+ className,
34256
+ accentX,
34257
+ accentColor,
34258
+ textX,
34259
+ textAnchor,
34260
+ midY,
34261
+ descFont,
34262
+ descLineHeight,
34263
+ palette,
34264
+ topBound,
34265
+ bottomBound,
34266
+ onClickItem,
34267
+ variant
34268
+ } = args;
34269
+ if (lines.length === 0) return;
34270
+ const totalH = lines.length * descLineHeight;
34271
+ let startY = midY - totalH / 2 + descLineHeight / 2;
34272
+ const accentPad = Math.max(4, Math.round(descFont * 0.3));
34273
+ const blockTop = startY - descLineHeight / 2 - accentPad;
34274
+ const blockBottom = startY + (lines.length - 1) * descLineHeight + descLineHeight / 2 + accentPad;
34275
+ let shift = 0;
34276
+ if (blockTop < topBound) shift = topBound - blockTop;
34277
+ else if (blockBottom > bottomBound) shift = bottomBound - blockBottom;
34278
+ startY += shift;
34279
+ const accentTop = startY - descLineHeight / 2 - accentPad;
34280
+ const accentH = totalH + accentPad * 2;
34281
+ const descG = parentG.append("g").attr("class", className).attr("data-line-number", layer.lineNumber).attr("data-variant", variant);
34282
+ if (onClickItem) {
34283
+ const ln = layer.lineNumber;
34284
+ descG.style("cursor", "pointer").on("click", () => onClickItem(ln));
34285
+ }
34286
+ descG.append("rect").attr("x", accentX).attr("y", accentTop).attr("width", DESC_ACCENT_WIDTH).attr("height", accentH).attr("rx", DESC_ACCENT_WIDTH / 2).attr("fill", accentColor);
34287
+ for (let j = 0; j < lines.length; j++) {
34288
+ const t = descG.append("text").attr("x", textX).attr("y", startY + j * descLineHeight).attr("dy", "0.35em").attr("text-anchor", textAnchor).attr("fill", palette.text).attr("font-family", FONT_FAMILY).attr("font-size", descFont).attr("font-weight", j === 0 ? 500 : 400);
34289
+ renderInlineText(t, lines[j], palette);
34290
+ }
34291
+ }
34292
+ function wrapText5(line11, maxWidth, fontSize) {
34293
+ if (line11 === "") return [""];
34294
+ const avgCharW = fontSize * CHAR_WIDTH_RATIO4;
34295
+ const maxChars = Math.max(8, Math.floor(maxWidth / avgCharW));
34296
+ const bulletMatch = line11.match(/^(\s*(?:•|-)\s+)(.*)$/);
34297
+ const indent = bulletMatch ? bulletMatch[1] : "";
34298
+ const body = bulletMatch ? bulletMatch[2] : line11;
34299
+ const hangingIndent = " ".repeat(indent.length);
34300
+ const words = body.split(/\s+/);
34301
+ const out = [];
34302
+ let current = indent;
34303
+ for (const word of words) {
34304
+ if (word === "") continue;
34305
+ const tentative = current.length === indent.length ? current + word : `${current} ${word}`;
34306
+ if (tentative.length <= maxChars) {
34307
+ current = tentative;
34308
+ } else {
34309
+ if (current.length > indent.length) out.push(current);
34310
+ if (word.length > maxChars - hangingIndent.length) {
34311
+ let remaining = word;
34312
+ while (remaining.length > maxChars - hangingIndent.length) {
34313
+ const slice = remaining.slice(0, maxChars - hangingIndent.length);
34314
+ out.push(hangingIndent + slice);
34315
+ remaining = remaining.slice(maxChars - hangingIndent.length);
34316
+ }
34317
+ current = remaining.length > 0 ? hangingIndent + remaining : "";
34318
+ } else {
34319
+ current = hangingIndent + word;
34320
+ }
34321
+ }
34322
+ }
34323
+ if (current.length > (out.length === 0 ? 0 : indent.length)) {
34324
+ if (current.trim().length > 0) out.push(current);
34325
+ }
34326
+ return out.length > 0 ? out : [line11];
34327
+ }
34328
+ function clamp(x, lo, hi) {
34329
+ return Math.max(lo, Math.min(hi, x));
34330
+ }
34331
+ var TITLE_AREA_HEIGHT2, H_MARGIN_FRAC, V_MARGIN, BASE_WIDTH_FRAC_NO_DESC, PYRAMID_SHARE_WITH_DESC, PYRAMID_SHARE_ALTERNATE, PITCH_RATIO, DESC_GAP, DESC_ACCENT_WIDTH, DESC_ACCENT_GAP, CHAR_WIDTH_RATIO4, LABEL_FONT_MIN, LABEL_FONT_MAX, DESC_FONT_MIN, DESC_FONT_MAX;
34332
+ var init_renderer15 = __esm({
34333
+ "src/pyramid/renderer.ts"() {
34334
+ "use strict";
34335
+ init_fonts();
34336
+ init_title_constants();
34337
+ init_color_utils();
34338
+ init_colors();
34339
+ init_inline_markdown();
34340
+ TITLE_AREA_HEIGHT2 = 50;
34341
+ H_MARGIN_FRAC = 0.03;
34342
+ V_MARGIN = 16;
34343
+ BASE_WIDTH_FRAC_NO_DESC = 0.78;
34344
+ PYRAMID_SHARE_WITH_DESC = 0.58;
34345
+ PYRAMID_SHARE_ALTERNATE = 0.42;
34346
+ PITCH_RATIO = 0.85;
34347
+ DESC_GAP = 28;
34348
+ DESC_ACCENT_WIDTH = 3;
34349
+ DESC_ACCENT_GAP = 12;
34350
+ CHAR_WIDTH_RATIO4 = 0.55;
34351
+ LABEL_FONT_MIN = 12;
34352
+ LABEL_FONT_MAX = 22;
34353
+ DESC_FONT_MIN = 11;
34354
+ DESC_FONT_MAX = 15;
34355
+ }
34356
+ });
34357
+
33560
34358
  // src/sequence/collapse.ts
33561
34359
  function applyCollapseProjection(parsed, collapsedGroups) {
33562
34360
  if (collapsedGroups.size === 0) {
@@ -33749,8 +34547,8 @@ var init_tag_resolution = __esm({
33749
34547
  });
33750
34548
 
33751
34549
  // src/sequence/renderer.ts
33752
- var renderer_exports15 = {};
33753
- __export(renderer_exports15, {
34550
+ var renderer_exports16 = {};
34551
+ __export(renderer_exports16, {
33754
34552
  applyGroupOrdering: () => applyGroupOrdering,
33755
34553
  applyPositionOverrides: () => applyPositionOverrides,
33756
34554
  buildNoteMessageMap: () => buildNoteMessageMap,
@@ -33762,7 +34560,7 @@ __export(renderer_exports15, {
33762
34560
  renderSequenceDiagram: () => renderSequenceDiagram,
33763
34561
  truncateBareUrl: () => truncateBareUrl
33764
34562
  });
33765
- import * as d3Selection17 from "d3-selection";
34563
+ import * as d3Selection18 from "d3-selection";
33766
34564
  function wrapTextLines(text, maxChars) {
33767
34565
  const rawLines = text.split("\n");
33768
34566
  const wrapped = [];
@@ -34118,7 +34916,7 @@ function applyGroupOrdering(participants, groups, messages = []) {
34118
34916
  return result;
34119
34917
  }
34120
34918
  function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateToLine, options) {
34121
- d3Selection17.select(container).selectAll("*").remove();
34919
+ d3Selection18.select(container).selectAll("*").remove();
34122
34920
  const { title, options: parsedOptions } = parsed;
34123
34921
  const effectiveCollapsedGroups = /* @__PURE__ */ new Set();
34124
34922
  for (const group of parsed.groups) {
@@ -34559,7 +35357,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34559
35357
  participants.forEach((p, i) => {
34560
35358
  participantX.set(p.id, offsetX + i * PARTICIPANT_GAP);
34561
35359
  });
34562
- const svg = d3Selection17.select(container).append("svg").attr("width", "100%").attr("height", totalHeight).attr("viewBox", `0 0 ${svgWidth} ${totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("class", "sequence-diagram").style("font-family", FONT_FAMILY);
35360
+ const svg = d3Selection18.select(container).append("svg").attr("width", "100%").attr("height", totalHeight).attr("viewBox", `0 0 ${svgWidth} ${totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("class", "sequence-diagram").style("font-family", FONT_FAMILY);
34563
35361
  const defs = svg.append("defs");
34564
35362
  defs.append("marker").attr("id", "seq-arrowhead").attr("viewBox", `0 0 ${ARROWHEAD_SIZE} ${ARROWHEAD_SIZE}`).attr("refX", ARROWHEAD_SIZE).attr("refY", ARROWHEAD_SIZE / 2).attr("markerWidth", ARROWHEAD_SIZE).attr("markerHeight", ARROWHEAD_SIZE).attr("orient", "auto").append("polygon").attr(
34565
35363
  "points",
@@ -34804,7 +35602,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34804
35602
  firstBranchStep = Math.min(firstBranchStep, first);
34805
35603
  }
34806
35604
  if (firstBranchStep < Infinity) {
34807
- const dividerY = stepY(firstBranchStep) - stepSpacing / 2;
35605
+ const dividerY = stepY(firstBranchStep) - BLOCK_HEADER_SPACE;
34808
35606
  deferredLines.push({
34809
35607
  x1: frameX,
34810
35608
  y1: dividerY,
@@ -34831,7 +35629,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34831
35629
  firstElseStep = Math.min(firstElseStep, first);
34832
35630
  }
34833
35631
  if (firstElseStep < Infinity) {
34834
- const dividerY = stepY(firstElseStep) - stepSpacing / 2;
35632
+ const dividerY = stepY(firstElseStep) - BLOCK_HEADER_SPACE;
34835
35633
  deferredLines.push({
34836
35634
  x1: frameX,
34837
35635
  y1: dividerY,
@@ -35290,7 +36088,7 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tag
35290
36088
  }
35291
36089
  }
35292
36090
  var PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, TOP_MARGIN, TITLE_HEIGHT8, PARTICIPANT_Y_OFFSET, SERVICE_BORDER_RADIUS, MESSAGE_START_OFFSET, LIFELINE_TAIL, ARROWHEAD_SIZE, NOTE_MAX_W, NOTE_FOLD, NOTE_PAD_H, NOTE_PAD_V, NOTE_FONT_SIZE, NOTE_LINE_H, NOTE_GAP, NOTE_CHAR_W, NOTE_CHARS_PER_LINE, COLLAPSED_NOTE_H, COLLAPSED_NOTE_W, ACTIVATION_WIDTH, SELF_CALL_HEIGHT, SELF_CALL_WIDTH, NOTE_LANE_MAX, LABEL_CHAR_WIDTH, LABEL_MAX_CHARS, fill, stroke, SW, W, H;
35293
- var init_renderer15 = __esm({
36091
+ var init_renderer16 = __esm({
35294
36092
  "src/sequence/renderer.ts"() {
35295
36093
  "use strict";
35296
36094
  init_color_utils();
@@ -35344,7 +36142,7 @@ var init_renderer15 = __esm({
35344
36142
 
35345
36143
  // src/d3.ts
35346
36144
  import * as d3Scale2 from "d3-scale";
35347
- import * as d3Selection18 from "d3-selection";
36145
+ import * as d3Selection19 from "d3-selection";
35348
36146
  import * as d3Shape10 from "d3-shape";
35349
36147
  import * as d3Array from "d3-array";
35350
36148
  import cloud from "d3-cloud";
@@ -35355,15 +36153,15 @@ function renderChartTitle(svg, title, titleLineNumber, width, textColor, onClick
35355
36153
  titleEl.attr("data-line-number", titleLineNumber);
35356
36154
  if (onClickItem) {
35357
36155
  titleEl.on("click", () => onClickItem(titleLineNumber)).on("mouseenter", function() {
35358
- d3Selection18.select(this).attr("opacity", 0.7);
36156
+ d3Selection19.select(this).attr("opacity", 0.7);
35359
36157
  }).on("mouseleave", function() {
35360
- d3Selection18.select(this).attr("opacity", 1);
36158
+ d3Selection19.select(this).attr("opacity", 1);
35361
36159
  });
35362
36160
  }
35363
36161
  }
35364
36162
  }
35365
36163
  function initD3Chart(container, palette, exportDims) {
35366
- d3Selection18.select(container).selectAll(":not([data-d3-tooltip])").remove();
36164
+ d3Selection19.select(container).selectAll(":not([data-d3-tooltip])").remove();
35367
36165
  const width = exportDims?.width ?? container.clientWidth;
35368
36166
  const height = exportDims?.height ?? container.clientHeight;
35369
36167
  if (width <= 0 || height <= 0) return null;
@@ -35371,7 +36169,7 @@ function initD3Chart(container, palette, exportDims) {
35371
36169
  const mutedColor = palette.border;
35372
36170
  const bgColor = palette.bg;
35373
36171
  const colors = getSeriesColors(palette);
35374
- const svg = d3Selection18.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
36172
+ const svg = d3Selection19.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
35375
36173
  return { svg, width, height, textColor, mutedColor, bgColor, colors };
35376
36174
  }
35377
36175
  function parseTimelineDate(s) {
@@ -36714,14 +37512,14 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
36714
37512
  function handleMouseEnter(hovered) {
36715
37513
  const connected = neighbors.get(hovered);
36716
37514
  g.selectAll(".arc-link").each(function() {
36717
- const el = d3Selection18.select(this);
37515
+ const el = d3Selection19.select(this);
36718
37516
  const src = el.attr("data-source");
36719
37517
  const tgt = el.attr("data-target");
36720
37518
  const isRelated = src === hovered || tgt === hovered;
36721
37519
  el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY2);
36722
37520
  });
36723
37521
  g.selectAll(".arc-node").each(function() {
36724
- const el = d3Selection18.select(this);
37522
+ const el = d3Selection19.select(this);
36725
37523
  const name = el.attr("data-node");
36726
37524
  const isRelated = name === hovered || connected.has(name);
36727
37525
  el.attr("opacity", isRelated ? 1 : FADE_OPACITY2);
@@ -36746,23 +37544,23 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
36746
37544
  const members = groupNodeSets.get(groupName);
36747
37545
  if (!members) return;
36748
37546
  g.selectAll(".arc-link").each(function() {
36749
- const el = d3Selection18.select(this);
37547
+ const el = d3Selection19.select(this);
36750
37548
  const isRelated = members.has(el.attr("data-source")) || members.has(el.attr("data-target"));
36751
37549
  el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY2);
36752
37550
  });
36753
37551
  g.selectAll(".arc-node").each(function() {
36754
- const el = d3Selection18.select(this);
37552
+ const el = d3Selection19.select(this);
36755
37553
  el.attr("opacity", members.has(el.attr("data-node")) ? 1 : FADE_OPACITY2);
36756
37554
  });
36757
37555
  g.selectAll(".arc-group-band").each(function() {
36758
- const el = d3Selection18.select(this);
37556
+ const el = d3Selection19.select(this);
36759
37557
  el.attr(
36760
37558
  "fill-opacity",
36761
37559
  el.attr("data-group") === groupName ? 0.18 : 0.03
36762
37560
  );
36763
37561
  });
36764
37562
  g.selectAll(".arc-group-label").each(function() {
36765
- const el = d3Selection18.select(this);
37563
+ const el = d3Selection19.select(this);
36766
37564
  el.attr("fill-opacity", el.attr("data-group") === groupName ? 1 : 0.2);
36767
37565
  });
36768
37566
  }
@@ -37020,7 +37818,7 @@ function showEventDatesOnScale(g, scale, startDate, endDate, innerHeight, accent
37020
37818
  function hideEventDatesOnScale(g) {
37021
37819
  g.selectAll(".tl-event-date").remove();
37022
37820
  g.selectAll(".tl-scale-tick").each(function() {
37023
- const el = d3Selection18.select(this);
37821
+ const el = d3Selection19.select(this);
37024
37822
  const isDashed = el.attr("stroke-dasharray");
37025
37823
  el.attr("opacity", isDashed ? 0.15 : 0.4);
37026
37824
  });
@@ -37098,7 +37896,7 @@ function renderTimelineGroupLegend(g, groups, groupColorMap, textColor, palette,
37098
37896
  }
37099
37897
  }
37100
37898
  function renderTimeline(container, parsed, palette, isDark, onClickItem, exportDims, activeTagGroup, swimlaneTagGroup, onTagStateChange, viewMode) {
37101
- d3Selection18.select(container).selectAll(":not([data-d3-tooltip])").remove();
37899
+ d3Selection19.select(container).selectAll(":not([data-d3-tooltip])").remove();
37102
37900
  const {
37103
37901
  timelineEvents,
37104
37902
  timelineGroups,
@@ -37196,13 +37994,13 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37196
37994
  const FADE_OPACITY2 = 0.1;
37197
37995
  function fadeToGroup(g, groupName) {
37198
37996
  g.selectAll(".tl-event").each(function() {
37199
- const el = d3Selection18.select(this);
37997
+ const el = d3Selection19.select(this);
37200
37998
  const evGroup = el.attr("data-group");
37201
37999
  el.attr("opacity", evGroup === groupName ? 1 : FADE_OPACITY2);
37202
38000
  });
37203
38001
  g.selectAll(".tl-legend-item, .tl-lane-header").each(
37204
38002
  function() {
37205
- const el = d3Selection18.select(this);
38003
+ const el = d3Selection19.select(this);
37206
38004
  const name = el.attr("data-group");
37207
38005
  el.attr("opacity", name === groupName ? 1 : FADE_OPACITY2);
37208
38006
  }
@@ -37214,7 +38012,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37214
38012
  }
37215
38013
  function fadeToEra(g, eraStart, eraEnd) {
37216
38014
  g.selectAll(".tl-event").each(function() {
37217
- const el = d3Selection18.select(this);
38015
+ const el = d3Selection19.select(this);
37218
38016
  const date = parseFloat(el.attr("data-date"));
37219
38017
  const endDate = el.attr("data-end-date");
37220
38018
  const evEnd = endDate ? parseFloat(endDate) : date;
@@ -37226,14 +38024,14 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37226
38024
  FADE_OPACITY2
37227
38025
  );
37228
38026
  g.selectAll(".tl-era").each(function() {
37229
- const el = d3Selection18.select(this);
38027
+ const el = d3Selection19.select(this);
37230
38028
  const s = parseFloat(el.attr("data-era-start"));
37231
38029
  const e = parseFloat(el.attr("data-era-end"));
37232
38030
  const isSelf = s === eraStart && e === eraEnd;
37233
38031
  el.attr("opacity", isSelf ? 1 : FADE_OPACITY2);
37234
38032
  });
37235
38033
  g.selectAll(".tl-marker").each(function() {
37236
- const el = d3Selection18.select(this);
38034
+ const el = d3Selection19.select(this);
37237
38035
  const date = parseFloat(el.attr("data-marker-date"));
37238
38036
  const inside = date >= eraStart && date <= eraEnd;
37239
38037
  el.attr("opacity", inside ? 1 : FADE_OPACITY2);
@@ -37248,7 +38046,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37248
38046
  function fadeToTagValue(g, tagKey, tagValue) {
37249
38047
  const attrName = `data-tag-${tagKey}`;
37250
38048
  g.selectAll(".tl-event").each(function() {
37251
- const el = d3Selection18.select(this);
38049
+ const el = d3Selection19.select(this);
37252
38050
  const val = el.attr(attrName);
37253
38051
  el.attr("opacity", val === tagValue ? 1 : FADE_OPACITY2);
37254
38052
  });
@@ -37261,7 +38059,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37261
38059
  FADE_OPACITY2
37262
38060
  );
37263
38061
  g.selectAll(".tl-tag-legend-entry").each(function() {
37264
- const el = d3Selection18.select(this);
38062
+ const el = d3Selection19.select(this);
37265
38063
  const entryValue = el.attr("data-legend-entry");
37266
38064
  if (entryValue === "__group__") return;
37267
38065
  const entryGroup = el.attr("data-tag-group");
@@ -37313,7 +38111,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37313
38111
  const innerHeight = height - margin.top - margin.bottom;
37314
38112
  const laneWidth = innerWidth / laneCount;
37315
38113
  const yScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
37316
- const svg = d3Selection18.select(container).append("svg").attr("viewBox", `0 0 ${width} ${height}`).attr("width", exportDims ? width : "100%").attr("preserveAspectRatio", "xMidYMin meet").style("background", bgColor);
38114
+ const svg = d3Selection19.select(container).append("svg").attr("viewBox", `0 0 ${width} ${height}`).attr("width", exportDims ? width : "100%").attr("preserveAspectRatio", "xMidYMin meet").style("background", bgColor);
37317
38115
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
37318
38116
  renderChartTitle(
37319
38117
  svg,
@@ -37403,7 +38201,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37403
38201
  const gradientId = `uncertain-vg-${ev.lineNumber}`;
37404
38202
  const strokeGradientId = `uncertain-vg-s-${ev.lineNumber}`;
37405
38203
  const defs = svg.select("defs").node() || svg.append("defs").node();
37406
- const defsEl = d3Selection18.select(defs);
38204
+ const defsEl = d3Selection19.select(defs);
37407
38205
  defsEl.append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "0%").attr("y2", "100%").selectAll("stop").data([
37408
38206
  { offset: "0%", opacity: 1 },
37409
38207
  { offset: "80%", opacity: 1 },
@@ -37439,7 +38237,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37439
38237
  const axisX = 20;
37440
38238
  const yScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
37441
38239
  const sorted = timelineEvents.slice().sort((a, b) => parseTimelineDate(a.date) - parseTimelineDate(b.date));
37442
- const svg = d3Selection18.select(container).append("svg").attr("viewBox", `0 0 ${width} ${height}`).attr("width", exportDims ? width : "100%").attr("preserveAspectRatio", "xMidYMin meet").style("background", bgColor);
38240
+ const svg = d3Selection19.select(container).append("svg").attr("viewBox", `0 0 ${width} ${height}`).attr("width", exportDims ? width : "100%").attr("preserveAspectRatio", "xMidYMin meet").style("background", bgColor);
37443
38241
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
37444
38242
  renderChartTitle(
37445
38243
  svg,
@@ -37528,7 +38326,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37528
38326
  const gradientId = `uncertain-v-${ev.lineNumber}`;
37529
38327
  const strokeGradientId = `uncertain-v-s-${ev.lineNumber}`;
37530
38328
  const defs = svg.select("defs").node() || svg.append("defs").node();
37531
- const defsEl = d3Selection18.select(defs);
38329
+ const defsEl = d3Selection19.select(defs);
37532
38330
  defsEl.append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "0%").attr("y2", "100%").selectAll("stop").data([
37533
38331
  { offset: "0%", opacity: 1 },
37534
38332
  { offset: "80%", opacity: 1 },
@@ -37596,7 +38394,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37596
38394
  const totalGaps = (lanes.length - 1) * GROUP_GAP3;
37597
38395
  const rowH = Math.min(28, (innerHeight - totalGaps) / totalEventRows);
37598
38396
  const xScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
37599
- const svg = d3Selection18.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
38397
+ const svg = d3Selection19.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
37600
38398
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
37601
38399
  renderChartTitle(
37602
38400
  svg,
@@ -37709,7 +38507,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37709
38507
  const gradientId = `uncertain-${ev.lineNumber}`;
37710
38508
  const strokeGradientId = `uncertain-s-${ev.lineNumber}`;
37711
38509
  const defs = svg.select("defs").node() || svg.append("defs").node();
37712
- const defsEl = d3Selection18.select(defs);
38510
+ const defsEl = d3Selection19.select(defs);
37713
38511
  defsEl.append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
37714
38512
  { offset: "0%", opacity: 1 },
37715
38513
  { offset: "80%", opacity: 1 },
@@ -37757,7 +38555,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37757
38555
  const innerHeight = height - margin.top - margin.bottom;
37758
38556
  const rowH = Math.min(28, innerHeight / sorted.length);
37759
38557
  const xScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
37760
- const svg = d3Selection18.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
38558
+ const svg = d3Selection19.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
37761
38559
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
37762
38560
  renderChartTitle(
37763
38561
  svg,
@@ -37866,7 +38664,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37866
38664
  const gradientId = `uncertain-ts-${ev.lineNumber}`;
37867
38665
  const strokeGradientId = `uncertain-ts-s-${ev.lineNumber}`;
37868
38666
  const defs = svg.select("defs").node() || svg.append("defs").node();
37869
- const defsEl = d3Selection18.select(defs);
38667
+ const defsEl = d3Selection19.select(defs);
37870
38668
  defsEl.append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
37871
38669
  { offset: "0%", opacity: 1 },
37872
38670
  { offset: "80%", opacity: 1 },
@@ -37909,7 +38707,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37909
38707
  const LG_ENTRY_DOT_GAP = LEGEND_ENTRY_DOT_GAP;
37910
38708
  const LG_ENTRY_TRAIL = LEGEND_ENTRY_TRAIL;
37911
38709
  const LG_ICON_W = 20;
37912
- const mainSvg = d3Selection18.select(container).select("svg");
38710
+ const mainSvg = d3Selection19.select(container).select("svg");
37913
38711
  const mainG = mainSvg.select("g");
37914
38712
  if (!mainSvg.empty() && !mainG.empty()) {
37915
38713
  let drawSwimlaneIcon4 = function(parent, x, y, isSwimActive) {
@@ -37982,7 +38780,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37982
38780
  const tagVal = entryValue.toLowerCase();
37983
38781
  fadeToTagValue(mainG, tagKey, tagVal);
37984
38782
  mainSvg.selectAll("[data-legend-entry]").each(function() {
37985
- const el = d3Selection18.select(this);
38783
+ const el = d3Selection19.select(this);
37986
38784
  const ev = el.attr("data-legend-entry");
37987
38785
  const eg = el.attr("data-tag-group") ?? el.node()?.closest?.("[data-tag-group]")?.getAttribute("data-tag-group");
37988
38786
  el.attr(
@@ -38035,7 +38833,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
38035
38833
  }, recolorEvents2 = function() {
38036
38834
  const colorTG = currentActiveGroup ?? swimlaneTagGroup ?? null;
38037
38835
  mainG.selectAll(".tl-event").each(function() {
38038
- const el = d3Selection18.select(this);
38836
+ const el = d3Selection19.select(this);
38039
38837
  const lineNum = el.attr("data-line-number");
38040
38838
  const ev = lineNum ? eventByLine.get(lineNum) : void 0;
38041
38839
  if (!ev) return;
@@ -38133,7 +38931,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
38133
38931
  }
38134
38932
  function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
38135
38933
  return new Promise((resolve) => {
38136
- d3Selection18.select(container).selectAll(":not([data-d3-tooltip])").remove();
38934
+ d3Selection19.select(container).selectAll(":not([data-d3-tooltip])").remove();
38137
38935
  const { words, title, cloudOptions } = parsed;
38138
38936
  if (words.length === 0) {
38139
38937
  resolve();
@@ -38160,7 +38958,7 @@ function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
38160
38958
  return minSize + Math.sqrt(t) * (maxSize - minSize);
38161
38959
  };
38162
38960
  const rotateFn = getRotateFn(cloudOptions.rotate);
38163
- const svg = d3Selection18.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
38961
+ const svg = d3Selection19.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
38164
38962
  renderChartTitle(svg, title, parsed.titleLineNumber, width, textColor);
38165
38963
  const g = svg.append("g").attr(
38166
38964
  "transform",
@@ -38662,8 +39460,8 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38662
39460
  const LABEL_MAX_FONT = 48;
38663
39461
  const LABEL_MIN_FONT = 14;
38664
39462
  const LABEL_PAD3 = 40;
38665
- const CHAR_WIDTH_RATIO4 = 0.6;
38666
- const estTextWidth = (text, fontSize) => text.length * fontSize * CHAR_WIDTH_RATIO4;
39463
+ const CHAR_WIDTH_RATIO5 = 0.6;
39464
+ const estTextWidth = (text, fontSize) => text.length * fontSize * CHAR_WIDTH_RATIO5;
38667
39465
  const quadrantLabelLayout = (text, qw2, qh2) => {
38668
39466
  const availW = qw2 - LABEL_PAD3;
38669
39467
  const availH = qh2 - LABEL_PAD3;
@@ -38726,7 +39524,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38726
39524
  (d) => onClickItem && d.label?.lineNumber ? "pointer" : "default"
38727
39525
  ).each(function(d) {
38728
39526
  const layout = labelLayouts.get(d.label.text);
38729
- const el = d3Selection18.select(this);
39527
+ const el = d3Selection19.select(this);
38730
39528
  if (layout.lines.length === 1) {
38731
39529
  el.text(layout.lines[0]);
38732
39530
  } else {
@@ -38742,9 +39540,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38742
39540
  quadrantLabelTexts.on("click", (_, d) => {
38743
39541
  if (d.label?.lineNumber) onClickItem(d.label.lineNumber);
38744
39542
  }).on("mouseenter", function() {
38745
- d3Selection18.select(this).attr("opacity", 0.7);
39543
+ d3Selection19.select(this).attr("opacity", 0.7);
38746
39544
  }).on("mouseleave", function() {
38747
- d3Selection18.select(this).attr("opacity", 1);
39545
+ d3Selection19.select(this).attr("opacity", 1);
38748
39546
  });
38749
39547
  }
38750
39548
  if (quadrantXAxis) {
@@ -38765,9 +39563,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38765
39563
  if (onClickItem && quadrantXAxisLineNumber) {
38766
39564
  [xLowLabel, xHighLabel].forEach((label) => {
38767
39565
  label.on("click", () => onClickItem(quadrantXAxisLineNumber)).on("mouseenter", function() {
38768
- d3Selection18.select(this).attr("opacity", 0.7);
39566
+ d3Selection19.select(this).attr("opacity", 0.7);
38769
39567
  }).on("mouseleave", function() {
38770
- d3Selection18.select(this).attr("opacity", 1);
39568
+ d3Selection19.select(this).attr("opacity", 1);
38771
39569
  });
38772
39570
  });
38773
39571
  }
@@ -38792,9 +39590,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38792
39590
  if (onClickItem && quadrantYAxisLineNumber) {
38793
39591
  [yLowLabel, yHighLabel].forEach((label) => {
38794
39592
  label.on("click", () => onClickItem(quadrantYAxisLineNumber)).on("mouseenter", function() {
38795
- d3Selection18.select(this).attr("opacity", 0.7);
39593
+ d3Selection19.select(this).attr("opacity", 0.7);
38796
39594
  }).on("mouseleave", function() {
38797
- d3Selection18.select(this).attr("opacity", 1);
39595
+ d3Selection19.select(this).attr("opacity", 1);
38798
39596
  });
38799
39597
  });
38800
39598
  }
@@ -38811,7 +39609,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38811
39609
  const POINT_LABEL_FONT_SIZE = 12;
38812
39610
  const quadrantLabelObstacles = quadrantDefsWithLabel.map((d) => {
38813
39611
  const layout = labelLayouts.get(d.label.text);
38814
- const totalW = Math.max(...layout.lines.map((l) => l.length)) * layout.fontSize * CHAR_WIDTH_RATIO4;
39612
+ const totalW = Math.max(...layout.lines.map((l) => l.length)) * layout.fontSize * CHAR_WIDTH_RATIO5;
38815
39613
  const totalH = layout.lines.length * layout.fontSize * 1.2;
38816
39614
  return {
38817
39615
  x: d.labelX - totalW / 2,
@@ -38871,7 +39669,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38871
39669
  pointsG.selectAll("g.point-group").each(function(_2, i) {
38872
39670
  const pt = quadrantPoints[i];
38873
39671
  const ptQuad = getPointQuadrant(pt.x, pt.y);
38874
- d3Selection18.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
39672
+ d3Selection19.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
38875
39673
  });
38876
39674
  }).on("mouseleave", () => {
38877
39675
  quadrantRects.attr("opacity", 1);
@@ -39390,6 +40188,22 @@ async function renderForExport(content, theme, palette, viewState, options) {
39390
40188
  );
39391
40189
  return finalizeSvgExport(container2, theme, effectivePalette2);
39392
40190
  }
40191
+ if (detectedType === "pyramid") {
40192
+ const { parsePyramid: parsePyramid2 } = await Promise.resolve().then(() => (init_parser16(), parser_exports16));
40193
+ const { renderPyramidForExport: renderPyramidForExport2 } = await Promise.resolve().then(() => (init_renderer15(), renderer_exports15));
40194
+ const effectivePalette2 = await resolveExportPalette(theme, palette);
40195
+ const pyramidParsed = parsePyramid2(content);
40196
+ if (pyramidParsed.error || pyramidParsed.layers.length === 0) return "";
40197
+ const container2 = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
40198
+ renderPyramidForExport2(
40199
+ container2,
40200
+ pyramidParsed,
40201
+ effectivePalette2,
40202
+ theme === "dark",
40203
+ { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
40204
+ );
40205
+ return finalizeSvgExport(container2, theme, effectivePalette2);
40206
+ }
39393
40207
  const parsed = parseVisualization(content, palette);
39394
40208
  if (parsed.error && parsed.type !== "sequence") {
39395
40209
  const looksLikeSequence2 = /->|~>|<-/.test(content);
@@ -39413,7 +40227,7 @@ async function renderForExport(content, theme, palette, viewState, options) {
39413
40227
  };
39414
40228
  if (parsed.type === "sequence") {
39415
40229
  const { parseSequenceDgmo: parseSequenceDgmo2 } = await Promise.resolve().then(() => (init_parser(), parser_exports));
39416
- const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer15(), renderer_exports15));
40230
+ const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer16(), renderer_exports16));
39417
40231
  const seqParsed = parseSequenceDgmo2(content);
39418
40232
  if (seqParsed.error || seqParsed.participants.length === 0) return "";
39419
40233
  renderSequenceDiagram2(
@@ -40120,8 +40934,7 @@ async function render(content, options) {
40120
40934
  const theme = options?.theme ?? "light";
40121
40935
  const paletteName = options?.palette ?? "nord";
40122
40936
  const paletteColors = getPalette(paletteName)[theme === "dark" ? "dark" : "light"];
40123
- const { diagnostics } = parseDgmo(content);
40124
- const chartType = parseDgmoChartType(content);
40937
+ const { diagnostics, chartType } = parseDgmo(content);
40125
40938
  const category = chartType ? getRenderCategory(chartType) : null;
40126
40939
  const viewState = options?.viewState ?? (options?.legendState ? {
40127
40940
  tag: options.legendState.activeGroup ?? void 0,
@@ -40442,6 +41255,8 @@ init_renderer14();
40442
41255
  init_parser15();
40443
41256
  init_layout12();
40444
41257
  init_renderer13();
41258
+ init_parser16();
41259
+ init_renderer15();
40445
41260
 
40446
41261
  // src/org/resolver.ts
40447
41262
  init_diagnostics();
@@ -40777,7 +41592,7 @@ init_legend_constants();
40777
41592
  init_legend_d3();
40778
41593
  init_legend_layout();
40779
41594
  init_d3();
40780
- init_renderer15();
41595
+ init_renderer16();
40781
41596
  init_collapse4();
40782
41597
  init_colors();
40783
41598
  init_palettes();
@@ -40880,6 +41695,7 @@ init_flowchart_parser();
40880
41695
  init_parser8();
40881
41696
  init_parser2();
40882
41697
  init_parsing();
41698
+ init_dgmo_router();
40883
41699
  var extractorRegistry = /* @__PURE__ */ new Map();
40884
41700
  function registerExtractor(kind, fn) {
40885
41701
  extractorRegistry.set(kind, fn);
@@ -41195,51 +42011,16 @@ var COMPLETION_REGISTRY = /* @__PURE__ */ new Map([
41195
42011
  "no-legend": { description: "Hide the score legend" },
41196
42012
  persona: { description: "Define the journey persona" }
41197
42013
  })
42014
+ ],
42015
+ [
42016
+ "pyramid",
42017
+ withGlobals({
42018
+ inverted: { description: "Flip apex to the bottom (funnel orientation)" },
42019
+ color: { description: "Override layer color (pipe metadata)" },
42020
+ description: { description: "Layer description (pipe or indented body)" }
42021
+ })
41198
42022
  ]
41199
42023
  ]);
41200
- var CHART_TYPE_DESCRIPTIONS = {
41201
- // Data charts
41202
- bar: "Bar chart",
41203
- line: "Line chart",
41204
- pie: "Pie chart",
41205
- doughnut: "Doughnut chart",
41206
- area: "Area chart",
41207
- "polar-area": "Polar area chart",
41208
- radar: "Radar chart",
41209
- "bar-stacked": "Stacked bar chart",
41210
- // Extended charts
41211
- scatter: "Scatter plot",
41212
- heatmap: "Heatmap",
41213
- sankey: "Sankey flow diagram",
41214
- chord: "Chord diagram",
41215
- funnel: "Funnel chart",
41216
- function: "Mathematical function plot",
41217
- // Visualizations
41218
- slope: "Slope chart",
41219
- wordcloud: "Word cloud",
41220
- arc: "Arc diagram",
41221
- timeline: "Timeline",
41222
- venn: "Venn diagram",
41223
- quadrant: "Quadrant chart",
41224
- // Diagrams
41225
- sequence: "Sequence diagram",
41226
- flowchart: "Flowchart",
41227
- class: "Class diagram",
41228
- er: "Entity-relationship diagram",
41229
- org: "Organization chart",
41230
- kanban: "Kanban board",
41231
- c4: "C4 architecture diagram",
41232
- state: "State diagram",
41233
- sitemap: "Sitemap diagram",
41234
- infra: "Infrastructure diagram",
41235
- gantt: "Gantt chart",
41236
- "boxes-and-lines": "Boxes and lines diagram",
41237
- mindmap: "Mindmap diagram",
41238
- wireframe: "UI wireframe diagram",
41239
- "tech-radar": "Technology adoption radar (ThoughtWorks style)",
41240
- cycle: "Cycle diagram (circular process flow)",
41241
- "journey-map": "User journey map with emotion curve"
41242
- };
41243
42024
  var CHART_TYPES = [...ALL_CHART_TYPES].filter((t) => t !== "multi-line").map((name) => ({
41244
42025
  name,
41245
42026
  description: CHART_TYPE_DESCRIPTIONS[name] ?? name
@@ -41830,6 +42611,7 @@ export {
41830
42611
  ALL_CHART_TYPES,
41831
42612
  ARROW_DIAGNOSTIC_CODES,
41832
42613
  CHART_TYPES,
42614
+ CHART_TYPE_DESCRIPTIONS,
41833
42615
  COMPLETION_REGISTRY,
41834
42616
  ENTITY_TYPES,
41835
42617
  INFRA_BEHAVIOR_KEYS,
@@ -41877,8 +42659,10 @@ export {
41877
42659
  encodeViewState,
41878
42660
  extractDiagramSymbols,
41879
42661
  extractTagDeclarations,
42662
+ focusOrgTree,
41880
42663
  formatDateLabel,
41881
42664
  formatDgmoError,
42665
+ getAllChartTypes,
41882
42666
  getAvailablePalettes,
41883
42667
  getExtendedChartLegendGroups,
41884
42668
  getLegendReservedHeight,
@@ -41949,7 +42733,9 @@ export {
41949
42733
  parseKanban,
41950
42734
  parseMindmap,
41951
42735
  parseOrg,
42736
+ parsePyramid,
41952
42737
  parseSequenceDgmo,
42738
+ parseSequenceDgmo as parseSequenceDiagram,
41953
42739
  parseSitemap,
41954
42740
  parseState,
41955
42741
  parseTechRadar,
@@ -41992,6 +42778,8 @@ export {
41992
42778
  renderMindmapForExport,
41993
42779
  renderOrg,
41994
42780
  renderOrgForExport,
42781
+ renderPyramid,
42782
+ renderPyramidForExport,
41995
42783
  renderQuadrant,
41996
42784
  renderQuadrantFocus,
41997
42785
  renderQuadrantFocusForExport,