@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.cjs CHANGED
@@ -1959,7 +1959,8 @@ var init_parsing = __esm({
1959
1959
  "wireframe",
1960
1960
  "tech-radar",
1961
1961
  "cycle",
1962
- "journey-map"
1962
+ "journey-map",
1963
+ "pyramid"
1963
1964
  ]);
1964
1965
  COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
1965
1966
  OPTION_NOCOLON_RE = /^([a-z][a-z0-9-]*)\s+(.+)$/i;
@@ -3201,7 +3202,7 @@ function isSequenceSection(el) {
3201
3202
  function isSequenceNote(el) {
3202
3203
  return "kind" in el && el.kind === "note";
3203
3204
  }
3204
- function parseNoteLine(trimmed, participants, lastMsgFrom) {
3205
+ function parseNoteLine(trimmed, participants, participantIds, sortedParticipantsCache, lastMsgFrom) {
3205
3206
  const lower = trimmed.toLowerCase();
3206
3207
  if (!lower.startsWith("note")) return null;
3207
3208
  if (trimmed.length > 4 && trimmed[4] !== " ") return null;
@@ -3213,7 +3214,7 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3213
3214
  if (!lastMsgFrom) return { kind: "skip" };
3214
3215
  participantId = lastMsgFrom;
3215
3216
  }
3216
- if (participants.some((p) => p.id === participantId)) {
3217
+ if (participantIds.has(participantId)) {
3217
3218
  return { kind: "multi-head", position, participantId };
3218
3219
  }
3219
3220
  }
@@ -3232,11 +3233,15 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3232
3233
  }
3233
3234
  if (!afterPos) {
3234
3235
  if (!lastMsgFrom) return { kind: "skip" };
3235
- if (!participants.some((p) => p.id === lastMsgFrom))
3236
- return { kind: "skip" };
3236
+ if (!participantIds.has(lastMsgFrom)) return { kind: "skip" };
3237
3237
  return { kind: "multi-head", position, participantId: lastMsgFrom };
3238
3238
  }
3239
- const resolved = resolveParticipantAndText(afterPos, participants);
3239
+ const resolved = resolveParticipantAndText(
3240
+ afterPos,
3241
+ participants,
3242
+ participantIds,
3243
+ sortedParticipantsCache
3244
+ );
3240
3245
  if (resolved) {
3241
3246
  if (resolved.text) {
3242
3247
  return {
@@ -3255,8 +3260,7 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3255
3260
  }
3256
3261
  if (hadOf) return { kind: "skip" };
3257
3262
  if (!lastMsgFrom) return { kind: "skip" };
3258
- if (!participants.some((p) => p.id === lastMsgFrom))
3259
- return { kind: "skip" };
3263
+ if (!participantIds.has(lastMsgFrom)) return { kind: "skip" };
3260
3264
  return {
3261
3265
  kind: "single",
3262
3266
  position,
@@ -3265,8 +3269,7 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3265
3269
  };
3266
3270
  }
3267
3271
  if (!lastMsgFrom) return { kind: "skip" };
3268
- if (!participants.some((p) => p.id === lastMsgFrom))
3269
- return { kind: "skip" };
3272
+ if (!participantIds.has(lastMsgFrom)) return { kind: "skip" };
3270
3273
  return {
3271
3274
  kind: "single",
3272
3275
  position: "right",
@@ -3276,20 +3279,20 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3276
3279
  }
3277
3280
  return null;
3278
3281
  }
3279
- function resolveParticipantAndText(input, participants) {
3282
+ function resolveParticipantAndText(input, participants, participantIds, sortedParticipantsCache) {
3280
3283
  if (input.startsWith('"') || input.startsWith("'")) {
3281
3284
  const quote = input[0];
3282
3285
  const endQuote = input.indexOf(quote, 1);
3283
3286
  if (endQuote > 0) {
3284
3287
  const name = input.substring(1, endQuote);
3285
- if (participants.some((p) => p.id === name)) {
3288
+ if (participantIds.has(name)) {
3286
3289
  const text = input.substring(endQuote + 1).trim();
3287
3290
  return { participantId: name, text };
3288
3291
  }
3289
3292
  }
3290
3293
  return null;
3291
3294
  }
3292
- const sorted = [...participants].sort((a, b) => b.id.length - a.id.length);
3295
+ const sorted = sortedParticipantsCache;
3293
3296
  for (const p of sorted) {
3294
3297
  if (input.startsWith(p.id)) {
3295
3298
  const remaining = input.substring(p.id.length);
@@ -3350,6 +3353,18 @@ function parseSequenceDgmo(content) {
3350
3353
  break;
3351
3354
  }
3352
3355
  let activeGroup = null;
3356
+ const participantIds = /* @__PURE__ */ new Set();
3357
+ let sortedParticipantsCache = [];
3358
+ let sortedCacheDirty = true;
3359
+ const getSortedParticipants = () => {
3360
+ if (sortedCacheDirty) {
3361
+ sortedParticipantsCache = [...result.participants].sort(
3362
+ (a, b) => b.id.length - a.id.length
3363
+ );
3364
+ sortedCacheDirty = false;
3365
+ }
3366
+ return sortedParticipantsCache;
3367
+ };
3353
3368
  const participantGroupMap = /* @__PURE__ */ new Map();
3354
3369
  let currentTagGroup = null;
3355
3370
  const aliasMap = /* @__PURE__ */ new Map();
@@ -3595,7 +3610,9 @@ function parseSequenceDgmo(content) {
3595
3610
  const posMatch = remainder.match(/\bposition\s+(-?\d+)/i);
3596
3611
  const alias = akaMatch ? akaMatch[1].trim() : null;
3597
3612
  const position = posMatch ? parseInt(posMatch[1], 10) : void 0;
3598
- if (!result.participants.some((p) => p.id === id)) {
3613
+ if (!participantIds.has(id)) {
3614
+ participantIds.add(id);
3615
+ sortedCacheDirty = true;
3599
3616
  result.participants.push({
3600
3617
  id,
3601
3618
  label: alias || id,
@@ -3625,7 +3642,9 @@ function parseSequenceDgmo(content) {
3625
3642
  contentStarted = true;
3626
3643
  const id = posOnlyMatch[1];
3627
3644
  const position = parseInt(posOnlyMatch[2], 10);
3628
- if (!result.participants.some((p) => p.id === id)) {
3645
+ if (!participantIds.has(id)) {
3646
+ participantIds.add(id);
3647
+ sortedCacheDirty = true;
3629
3648
  result.participants.push({
3630
3649
  id,
3631
3650
  label: id,
@@ -3659,7 +3678,9 @@ function parseSequenceDgmo(content) {
3659
3678
  `'${id}(${color})' syntax is no longer supported \u2014 use 'tag:' groups for coloring`
3660
3679
  );
3661
3680
  contentStarted = true;
3662
- if (!result.participants.some((p) => p.id === id)) {
3681
+ if (!participantIds.has(id)) {
3682
+ participantIds.add(id);
3683
+ sortedCacheDirty = true;
3663
3684
  result.participants.push({
3664
3685
  id,
3665
3686
  label: id,
@@ -3688,7 +3709,8 @@ function parseSequenceDgmo(content) {
3688
3709
  if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !contentStarted || bareMeta)) {
3689
3710
  contentStarted = true;
3690
3711
  const id = bareCore;
3691
- if (!result.participants.some((p) => p.id === id)) {
3712
+ if (!participantIds.has(id)) {
3713
+ participantIds.add(id);
3692
3714
  result.participants.push({
3693
3715
  id,
3694
3716
  label: id,
@@ -3750,7 +3772,9 @@ function parseSequenceDgmo(content) {
3750
3772
  };
3751
3773
  result.messages.push(msg);
3752
3774
  currentContainer().push(msg);
3753
- if (!result.participants.some((p) => p.id === from)) {
3775
+ if (!participantIds.has(from)) {
3776
+ participantIds.add(from);
3777
+ sortedCacheDirty = true;
3754
3778
  result.participants.push({
3755
3779
  id: from,
3756
3780
  label: from,
@@ -3758,7 +3782,9 @@ function parseSequenceDgmo(content) {
3758
3782
  lineNumber
3759
3783
  });
3760
3784
  }
3761
- if (!result.participants.some((p) => p.id === to)) {
3785
+ if (!participantIds.has(to)) {
3786
+ participantIds.add(to);
3787
+ sortedCacheDirty = true;
3762
3788
  result.participants.push({
3763
3789
  id: to,
3764
3790
  label: to,
@@ -3825,7 +3851,9 @@ function parseSequenceDgmo(content) {
3825
3851
  };
3826
3852
  result.messages.push(msg);
3827
3853
  currentContainer().push(msg);
3828
- if (!result.participants.some((p) => p.id === from)) {
3854
+ if (!participantIds.has(from)) {
3855
+ participantIds.add(from);
3856
+ sortedCacheDirty = true;
3829
3857
  result.participants.push({
3830
3858
  id: from,
3831
3859
  label: from,
@@ -3833,7 +3861,9 @@ function parseSequenceDgmo(content) {
3833
3861
  lineNumber
3834
3862
  });
3835
3863
  }
3836
- if (!result.participants.some((p) => p.id === to)) {
3864
+ if (!participantIds.has(to)) {
3865
+ participantIds.add(to);
3866
+ sortedCacheDirty = true;
3837
3867
  result.participants.push({
3838
3868
  id: to,
3839
3869
  label: to,
@@ -3935,6 +3965,8 @@ function parseSequenceDgmo(content) {
3935
3965
  const noteParsed = parseNoteLine(
3936
3966
  trimmed,
3937
3967
  result.participants,
3968
+ participantIds,
3969
+ getSortedParticipants(),
3938
3970
  lastMsgFrom
3939
3971
  );
3940
3972
  if (noteParsed) {
@@ -7301,7 +7333,7 @@ function buildFunnelOption(parsed, textColor, colors, bg, titleConfig) {
7301
7333
  bottom: 20,
7302
7334
  width: "60%",
7303
7335
  sort: "descending",
7304
- gap: 2,
7336
+ gap: 0,
7305
7337
  minSize: "8%"
7306
7338
  };
7307
7339
  return {
@@ -10013,8 +10045,8 @@ function extractPipeMetadata(rest) {
10013
10045
  const tags = {};
10014
10046
  let clean = rest;
10015
10047
  let match;
10016
- const re = new RegExp(PIPE_META_RE.source, "g");
10017
- while ((match = re.exec(rest)) !== null) {
10048
+ PIPE_META_RE.lastIndex = 0;
10049
+ while ((match = PIPE_META_RE.exec(rest)) !== null) {
10018
10050
  tags[match[1].trim()] = match[2].trim();
10019
10051
  clean = clean.replace(match[0], "");
10020
10052
  }
@@ -14142,9 +14174,128 @@ var init_parser15 = __esm({
14142
14174
  }
14143
14175
  });
14144
14176
 
14177
+ // src/pyramid/parser.ts
14178
+ var parser_exports16 = {};
14179
+ __export(parser_exports16, {
14180
+ parsePyramid: () => parsePyramid
14181
+ });
14182
+ function parsePyramid(content) {
14183
+ const result = {
14184
+ type: "pyramid",
14185
+ title: "",
14186
+ titleLineNumber: 0,
14187
+ layers: [],
14188
+ inverted: false,
14189
+ options: {},
14190
+ diagnostics: [],
14191
+ error: null
14192
+ };
14193
+ const lines = content.split("\n");
14194
+ let headerParsed = false;
14195
+ let currentLayer = null;
14196
+ const fail = (line11, message) => {
14197
+ const diag = makeDgmoError(line11, message);
14198
+ result.diagnostics.push(diag);
14199
+ result.error = formatDgmoError(diag);
14200
+ return result;
14201
+ };
14202
+ const warn = (line11, message, severity = "warning") => {
14203
+ result.diagnostics.push(makeDgmoError(line11, message, severity));
14204
+ };
14205
+ const flushLayer = () => {
14206
+ if (currentLayer) {
14207
+ result.layers.push(currentLayer);
14208
+ currentLayer = null;
14209
+ }
14210
+ };
14211
+ for (let i = 0; i < lines.length; i++) {
14212
+ const lineNum = i + 1;
14213
+ const raw = lines[i];
14214
+ const trimmed = raw.trim();
14215
+ if (!trimmed || trimmed.startsWith("//")) continue;
14216
+ const indent = measureIndent(raw);
14217
+ if (!headerParsed) {
14218
+ const firstLineResult = parseFirstLine(trimmed);
14219
+ if (firstLineResult && firstLineResult.chartType === "pyramid") {
14220
+ result.title = firstLineResult.title ?? "";
14221
+ result.titleLineNumber = lineNum;
14222
+ headerParsed = true;
14223
+ continue;
14224
+ }
14225
+ return fail(lineNum, 'Expected "pyramid [Title]" as the first line.');
14226
+ }
14227
+ if (indent === 0 && trimmed.toLowerCase() === "inverted") {
14228
+ result.inverted = true;
14229
+ continue;
14230
+ }
14231
+ if (indent === 0) {
14232
+ flushLayer();
14233
+ const pipeIdx = trimmed.indexOf("|");
14234
+ let label;
14235
+ const description = [];
14236
+ let color;
14237
+ let restMeta = {};
14238
+ if (pipeIdx < 0) {
14239
+ label = trimmed;
14240
+ } else {
14241
+ label = trimmed.substring(0, pipeIdx).trim();
14242
+ const after = trimmed.substring(pipeIdx + 1).trim();
14243
+ if (!after) {
14244
+ } else if (KEY_VALUE_PREFIX_RE.test(after)) {
14245
+ const metadata = parsePipeMetadata([label, after]);
14246
+ color = metadata["color"];
14247
+ const descFromPipe = metadata["description"];
14248
+ if (descFromPipe) description.push(descFromPipe);
14249
+ restMeta = { ...metadata };
14250
+ delete restMeta["color"];
14251
+ delete restMeta["description"];
14252
+ } else {
14253
+ description.push(after);
14254
+ }
14255
+ }
14256
+ if (!label) {
14257
+ warn(lineNum, "Empty layer label.");
14258
+ continue;
14259
+ }
14260
+ currentLayer = {
14261
+ label,
14262
+ lineNumber: lineNum,
14263
+ color,
14264
+ description,
14265
+ metadata: restMeta
14266
+ };
14267
+ continue;
14268
+ }
14269
+ if (!currentLayer) {
14270
+ warn(lineNum, `Unexpected indented line: "${trimmed}".`);
14271
+ continue;
14272
+ }
14273
+ const descLine = trimmed.startsWith("- ") ? `\u2022 ${trimmed.substring(2)}` : trimmed;
14274
+ currentLayer.description.push(descLine);
14275
+ }
14276
+ flushLayer();
14277
+ if (result.layers.length < 2) {
14278
+ return fail(
14279
+ result.titleLineNumber || 1,
14280
+ "pyramid requires at least 2 layers."
14281
+ );
14282
+ }
14283
+ return result;
14284
+ }
14285
+ var KEY_VALUE_PREFIX_RE;
14286
+ var init_parser16 = __esm({
14287
+ "src/pyramid/parser.ts"() {
14288
+ "use strict";
14289
+ init_diagnostics();
14290
+ init_parsing();
14291
+ KEY_VALUE_PREFIX_RE = /^\s*[A-Za-z][A-Za-z0-9_-]*\s*:/;
14292
+ }
14293
+ });
14294
+
14145
14295
  // src/dgmo-router.ts
14146
14296
  var dgmo_router_exports = {};
14147
14297
  __export(dgmo_router_exports, {
14298
+ CHART_TYPE_DESCRIPTIONS: () => CHART_TYPE_DESCRIPTIONS,
14148
14299
  getAllChartTypes: () => getAllChartTypes,
14149
14300
  getRenderCategory: () => getRenderCategory,
14150
14301
  isExtendedChartType: () => isExtendedChartType,
@@ -14214,32 +14365,39 @@ function parseDgmo(content) {
14214
14365
  const colonDiag = detectColonChartType(content);
14215
14366
  if (colonDiag) {
14216
14367
  const fallback = parseVisualization(content).diagnostics;
14217
- return { diagnostics: [colonDiag, ...fallback] };
14368
+ return { diagnostics: [colonDiag, ...fallback], chartType: null };
14218
14369
  }
14219
- return { diagnostics: parseVisualization(content).diagnostics };
14370
+ return {
14371
+ diagnostics: parseVisualization(content).diagnostics,
14372
+ chartType: null
14373
+ };
14220
14374
  }
14221
14375
  const directParser = PARSE_DISPATCH.get(chartType);
14222
14376
  if (directParser) {
14223
14377
  const result2 = directParser(content);
14224
14378
  return {
14225
- diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)]
14379
+ diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)],
14380
+ chartType
14226
14381
  };
14227
14382
  }
14228
14383
  if (STANDARD_CHART_TYPES2.has(chartType)) {
14229
14384
  const result2 = parseChart(content);
14230
14385
  return {
14231
- diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)]
14386
+ diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)],
14387
+ chartType
14232
14388
  };
14233
14389
  }
14234
14390
  if (ECHART_TYPES.has(chartType)) {
14235
14391
  const result2 = parseExtendedChart(content);
14236
14392
  return {
14237
- diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)]
14393
+ diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)],
14394
+ chartType
14238
14395
  };
14239
14396
  }
14240
14397
  const result = parseVisualization(content);
14241
14398
  return {
14242
- diagnostics: [...result.diagnostics, ...detectEmptyContent(content)]
14399
+ diagnostics: [...result.diagnostics, ...detectEmptyContent(content)],
14400
+ chartType
14243
14401
  };
14244
14402
  }
14245
14403
  function detectColonChartType(content) {
@@ -14282,7 +14440,7 @@ function detectEmptyContent(content) {
14282
14440
  }
14283
14441
  return [];
14284
14442
  }
14285
- 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;
14443
+ 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;
14286
14444
  var init_dgmo_router = __esm({
14287
14445
  "src/dgmo-router.ts"() {
14288
14446
  "use strict";
@@ -14306,6 +14464,7 @@ var init_dgmo_router = __esm({
14306
14464
  init_parser13();
14307
14465
  init_parser14();
14308
14466
  init_parser15();
14467
+ init_parser16();
14309
14468
  init_parsing();
14310
14469
  init_diagnostics();
14311
14470
  GANTT_DURATION_RE = /^\d+(?:\.\d+)?(?:min|bd|d|w|m|q|y|h)(?:\?)?\s+/;
@@ -14336,7 +14495,8 @@ var init_dgmo_router = __esm({
14336
14495
  "venn",
14337
14496
  "quadrant",
14338
14497
  "tech-radar",
14339
- "cycle"
14498
+ "cycle",
14499
+ "pyramid"
14340
14500
  ]);
14341
14501
  DIAGRAM_TYPES = /* @__PURE__ */ new Set([
14342
14502
  "sequence",
@@ -14374,6 +14534,47 @@ var init_dgmo_router = __esm({
14374
14534
  "polar-area",
14375
14535
  "bar-stacked"
14376
14536
  ]);
14537
+ CHART_TYPE_DESCRIPTIONS = {
14538
+ bar: "Bar chart \u2014 categorical comparisons",
14539
+ line: "Line chart \u2014 trends over time; supports era bands (era start -> end Label (color)) for annotating named periods",
14540
+ "multi-line": "Multi-line chart \u2014 multiple series trends over time; supports era bands",
14541
+ area: "Area chart \u2014 filled line chart; supports era bands",
14542
+ pie: "Pie chart \u2014 part-to-whole proportions",
14543
+ doughnut: "Doughnut chart \u2014 ring-style pie chart",
14544
+ radar: "Radar chart \u2014 multi-dimensional metrics",
14545
+ "polar-area": "Polar area chart \u2014 radial bar chart",
14546
+ "bar-stacked": "Stacked bar chart \u2014 multi-series categorical",
14547
+ scatter: "Scatter plot \u2014 2D data points or bubble chart",
14548
+ sankey: "Sankey diagram \u2014 flow/allocation visualization",
14549
+ chord: "Chord diagram \u2014 circular flow relationships",
14550
+ function: "Function plot \u2014 mathematical expressions",
14551
+ heatmap: "Heatmap \u2014 matrix intensity visualization",
14552
+ funnel: "Funnel chart \u2014 conversion pipeline",
14553
+ slope: "Slope chart \u2014 change between two periods",
14554
+ wordcloud: "Word cloud \u2014 term frequency visualization",
14555
+ arc: "Arc diagram \u2014 network relationships",
14556
+ timeline: "Timeline \u2014 events, eras, and date ranges",
14557
+ venn: "Venn diagram \u2014 set overlaps",
14558
+ quadrant: "Quadrant chart \u2014 2x2 positioning matrix",
14559
+ "tech-radar": "Tech radar \u2014 technology adoption quadrants (adopt/trial/assess/hold)",
14560
+ cycle: "Cycle diagram \u2014 cyclical process visualization (PDCA, OODA, DevOps loops)",
14561
+ sequence: "Sequence diagram \u2014 message/interaction flows",
14562
+ flowchart: "Flowchart \u2014 decision trees and process flows",
14563
+ class: "Class diagram \u2014 UML class hierarchies",
14564
+ er: "ER diagram \u2014 database schemas and relationships",
14565
+ org: "Org chart \u2014 hierarchical tree structures",
14566
+ kanban: "Kanban board \u2014 task/workflow columns",
14567
+ c4: "C4 diagram \u2014 system architecture (context, container, component, deployment)",
14568
+ state: "State diagram \u2014 state machine / lifecycle transitions",
14569
+ sitemap: "Sitemap \u2014 navigable UI structure with pages, groups, and cross-link arrows",
14570
+ infra: "Infrastructure diagram \u2014 traffic flow with RPS computation, capacity modeling, and latency analysis",
14571
+ gantt: "Gantt chart \u2014 project scheduling with task dependencies and milestones",
14572
+ "boxes-and-lines": "Boxes and lines \u2014 general-purpose node-edge diagrams with nested groups, tags, and shape inference",
14573
+ mindmap: "Mindmap \u2014 radial hierarchy of ideas branching from a central topic",
14574
+ wireframe: "Wireframe \u2014 low-fidelity UI layout with panels, controls, and annotations",
14575
+ "journey-map": "Journey map \u2014 user experience flow with emotion scores, phases, and annotations",
14576
+ pyramid: "Pyramid \u2014 hierarchical layered pyramid (Maslow, DIKW, learning pyramid); inverted for funnel-of-learning style"
14577
+ };
14377
14578
  ECHART_TYPES = /* @__PURE__ */ new Set([
14378
14579
  "scatter",
14379
14580
  "sankey",
@@ -14399,7 +14600,8 @@ var init_dgmo_router = __esm({
14399
14600
  ["wireframe", (c) => parseWireframe(c)],
14400
14601
  ["tech-radar", (c) => parseTechRadar(c)],
14401
14602
  ["cycle", (c) => parseCycle(c)],
14402
- ["journey-map", (c) => parseJourneyMap(c)]
14603
+ ["journey-map", (c) => parseJourneyMap(c)],
14604
+ ["pyramid", (c) => parsePyramid(c)]
14403
14605
  ]);
14404
14606
  ALL_KNOWN_TYPES = /* @__PURE__ */ new Set([
14405
14607
  ...DATA_CHART_TYPES,
@@ -15208,7 +15410,8 @@ var init_layout = __esm({
15208
15410
  // src/org/collapse.ts
15209
15411
  var collapse_exports = {};
15210
15412
  __export(collapse_exports, {
15211
- collapseOrgTree: () => collapseOrgTree
15413
+ collapseOrgTree: () => collapseOrgTree,
15414
+ focusOrgTree: () => focusOrgTree
15212
15415
  });
15213
15416
  function cloneNode(node) {
15214
15417
  return {
@@ -15263,6 +15466,49 @@ function collapseOrgTree(original, collapsedIds) {
15263
15466
  hiddenCounts
15264
15467
  };
15265
15468
  }
15469
+ function findNodeWithPath(nodes, targetId, path) {
15470
+ for (const node of nodes) {
15471
+ if (node.id === targetId) {
15472
+ return { node, path };
15473
+ }
15474
+ const result = findNodeWithPath(node.children, targetId, [
15475
+ ...path,
15476
+ {
15477
+ id: node.id,
15478
+ label: node.label,
15479
+ lineNumber: node.lineNumber,
15480
+ color: node.color,
15481
+ metadata: { ...node.metadata },
15482
+ isContainer: node.isContainer
15483
+ }
15484
+ ]);
15485
+ if (result) return result;
15486
+ }
15487
+ return null;
15488
+ }
15489
+ function focusOrgTree(original, focusNodeId) {
15490
+ const found = findNodeWithPath(original.roots, focusNodeId, []);
15491
+ if (!found) return null;
15492
+ const isRoot = original.roots.some((r) => r.id === focusNodeId);
15493
+ if (isRoot) {
15494
+ return {
15495
+ parsed: {
15496
+ ...original,
15497
+ roots: [cloneNode(found.node)]
15498
+ },
15499
+ ancestorPath: []
15500
+ };
15501
+ }
15502
+ const cloned = cloneNode(found.node);
15503
+ cloned.parentId = null;
15504
+ return {
15505
+ parsed: {
15506
+ ...original,
15507
+ roots: [cloned]
15508
+ },
15509
+ ancestorPath: found.path
15510
+ };
15511
+ }
15266
15512
  var init_collapse = __esm({
15267
15513
  "src/org/collapse.ts"() {
15268
15514
  "use strict";
@@ -15321,7 +15567,7 @@ function containerFill(palette, isDark, nodeColor2) {
15321
15567
  function containerStroke(palette, nodeColor2) {
15322
15568
  return nodeColor2 ?? palette.textMuted;
15323
15569
  }
15324
- function renderOrg(container, parsed, layout, palette, isDark, onClickItem, exportDims, activeTagGroup, hiddenAttributes) {
15570
+ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, exportDims, activeTagGroup, hiddenAttributes, ancestorPath) {
15325
15571
  d3Selection.select(container).selectAll(":not([data-d3-tooltip])").remove();
15326
15572
  const width = exportDims?.width ?? container.clientWidth;
15327
15573
  const height = exportDims?.height ?? container.clientHeight;
@@ -15334,8 +15580,10 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
15334
15580
  const legendReserve = fixedLegend ? LEGEND_HEIGHT + LEGEND_FIXED_GAP : 0;
15335
15581
  const fixedTitle = !exportDims && !!parsed.title;
15336
15582
  const titleReserve = fixedTitle ? TITLE_HEIGHT : 0;
15583
+ const hasAncestorTrail = !exportDims && ancestorPath && ancestorPath.length > 0;
15584
+ const ancestorTrailHeight = hasAncestorTrail ? ancestorPath.length * ANCESTOR_ROW_HEIGHT + ANCESTOR_TRAIL_BOTTOM_GAP : 0;
15337
15585
  const diagramW = layout.width;
15338
- let diagramH = layout.height + (fixedTitle ? 0 : titleOffset);
15586
+ let diagramH = layout.height + (fixedTitle ? 0 : titleOffset) + ancestorTrailHeight;
15339
15587
  if (fixedLegend) {
15340
15588
  diagramH -= layoutLegendShift;
15341
15589
  }
@@ -15367,11 +15615,13 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
15367
15615
  }
15368
15616
  }
15369
15617
  }
15370
- const contentG = mainG.append("g").attr("transform", `translate(0, ${fixedTitle ? 0 : titleOffset})`);
15618
+ const contentYShift = (fixedTitle ? 0 : titleOffset) + ancestorTrailHeight;
15619
+ const contentG = mainG.append("g").attr("transform", `translate(0, ${contentYShift})`);
15371
15620
  const displayNames = /* @__PURE__ */ new Map();
15372
15621
  for (const group of parsed.tagGroups) {
15373
15622
  displayNames.set(group.name.toLowerCase(), group.name);
15374
15623
  }
15624
+ const rootNodeIds = new Set(parsed.roots.map((r) => r.id));
15375
15625
  const colorOff = parsed.options?.color === "off";
15376
15626
  for (const c of layout.containers) {
15377
15627
  const cG = contentG.append("g").attr("transform", `translate(${c.x}, ${c.y})`).attr("class", "org-container").attr("data-line-number", String(c.lineNumber));
@@ -15418,6 +15668,18 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
15418
15668
  cG.append("clipPath").attr("id", clipId).append("rect").attr("width", c.width).attr("height", c.height).attr("rx", CONTAINER_RADIUS);
15419
15669
  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");
15420
15670
  }
15671
+ if (!exportDims && c.hasChildren && !rootNodeIds.has(c.nodeId)) {
15672
+ const iconSize = 14;
15673
+ const iconPad = 5;
15674
+ const iconX = c.width - iconSize - iconPad;
15675
+ const iconY = iconPad;
15676
+ const focusG = cG.append("g").attr("class", "org-focus-icon").attr("data-focus-node", c.nodeId).attr("transform", `translate(${iconX}, ${iconY})`);
15677
+ focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
15678
+ const cx = iconSize / 2;
15679
+ const cy = iconSize / 2;
15680
+ 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);
15681
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
15682
+ }
15421
15683
  }
15422
15684
  for (const edge of layout.edges) {
15423
15685
  if (edge.points.length < 2) continue;
@@ -15476,6 +15738,58 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
15476
15738
  nodeG.append("clipPath").attr("id", clipId).append("rect").attr("width", node.width).attr("height", node.height).attr("rx", CARD_RADIUS);
15477
15739
  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");
15478
15740
  }
15741
+ if (!exportDims && node.hasChildren && !rootNodeIds.has(node.id)) {
15742
+ const iconSize = 14;
15743
+ const iconPad = 5;
15744
+ const iconX = node.width - iconSize - iconPad;
15745
+ const iconY = iconPad;
15746
+ const focusG = nodeG.append("g").attr("class", "org-focus-icon").attr("data-focus-node", node.id).attr("transform", `translate(${iconX}, ${iconY})`);
15747
+ focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
15748
+ const cx = iconSize / 2;
15749
+ const cy = iconSize / 2;
15750
+ 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);
15751
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
15752
+ }
15753
+ }
15754
+ if (hasAncestorTrail) {
15755
+ const rootNode = layout.nodes.find((n) => rootNodeIds.has(n.id));
15756
+ const rootContainer = !rootNode ? layout.containers.find((c) => rootNodeIds.has(c.nodeId)) : null;
15757
+ const rootCenterX = rootNode ? rootNode.x : rootContainer ? rootContainer.x + rootContainer.width / 2 : null;
15758
+ const rootTopY = rootNode ? rootNode.y : rootContainer ? rootContainer.y : null;
15759
+ if (rootCenterX !== null && rootTopY !== null) {
15760
+ const trailBottomY = rootTopY - ANCESTOR_TRAIL_BOTTOM_GAP;
15761
+ const trailG = contentG.append("g").attr("class", "org-ancestor-trail");
15762
+ const count = ancestorPath.length;
15763
+ const dotPositions = [];
15764
+ for (let i = 0; i < count; i++) {
15765
+ const fromBottom = count - 1 - i;
15766
+ dotPositions.push(trailBottomY - fromBottom * ANCESTOR_ROW_HEIGHT);
15767
+ }
15768
+ const lineTopY = dotPositions[0];
15769
+ 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);
15770
+ for (let i = 0; i < count; i++) {
15771
+ const ancestor = ancestorPath[i];
15772
+ const dotY = dotPositions[i];
15773
+ const resolvedColor = ancestor.color ?? resolveTagColor(
15774
+ ancestor.metadata,
15775
+ parsed.tagGroups,
15776
+ activeTagGroup ?? null,
15777
+ ancestor.isContainer
15778
+ );
15779
+ const dotColor = resolvedColor ?? palette.textMuted;
15780
+ const rowG = trailG.append("g").attr("class", "org-ancestor-node").attr("data-focus-ancestor", ancestor.id).style("cursor", "pointer").attr("transform", `translate(${rootCenterX}, ${dotY})`);
15781
+ 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");
15782
+ rowG.append("circle").attr("cx", 0).attr("cy", 0).attr("r", ANCESTOR_DOT_R).attr("fill", dotColor);
15783
+ 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);
15784
+ rowG.on("mouseenter", function() {
15785
+ d3Selection.select(this).select("circle").attr("r", ANCESTOR_DOT_R + 1);
15786
+ d3Selection.select(this).select("text").attr("fill", palette.text);
15787
+ }).on("mouseleave", function() {
15788
+ d3Selection.select(this).select("circle").attr("r", ANCESTOR_DOT_R);
15789
+ d3Selection.select(this).select("text").attr("fill", palette.textMuted);
15790
+ });
15791
+ }
15792
+ }
15479
15793
  }
15480
15794
  if (fixedLegend || legendOnly || exportDims && hasLegend) {
15481
15795
  const groups = layout.legend.map((g) => ({
@@ -15572,7 +15886,7 @@ function renderOrgForExport(content, theme, palette) {
15572
15886
  return extractExportSvg(container, theme);
15573
15887
  });
15574
15888
  }
15575
- var d3Selection, 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;
15889
+ var d3Selection, 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;
15576
15890
  var init_renderer = __esm({
15577
15891
  "src/org/renderer.ts"() {
15578
15892
  "use strict";
@@ -15580,6 +15894,7 @@ var init_renderer = __esm({
15580
15894
  init_fonts();
15581
15895
  init_export_container();
15582
15896
  init_color_utils();
15897
+ init_tag_groups();
15583
15898
  init_parser4();
15584
15899
  init_layout();
15585
15900
  init_legend_constants();
@@ -15603,6 +15918,10 @@ var init_renderer = __esm({
15603
15918
  CONTAINER_HEADER_HEIGHT = 28;
15604
15919
  COLLAPSE_BAR_HEIGHT = 6;
15605
15920
  COLLAPSE_BAR_INSET = 0;
15921
+ ANCESTOR_DOT_R = 4;
15922
+ ANCESTOR_LABEL_FONT_SIZE = 11;
15923
+ ANCESTOR_ROW_HEIGHT = 22;
15924
+ ANCESTOR_TRAIL_BOTTOM_GAP = 16;
15606
15925
  LEGEND_FIXED_GAP = 8;
15607
15926
  }
15608
15927
  });
@@ -30586,7 +30905,22 @@ function renderQuarterCircle(svg, parsed, quadrant, qColor, palette, isDark, wid
30586
30905
  const innerR = ri * ringBandWidth;
30587
30906
  const outerR = (ri + 1) * ringBandWidth;
30588
30907
  const fillColor = ri % 2 === 0 ? palette.bg : mix(palette.bg, palette.border, 0.15);
30908
+ const ringName = parsed.rings[ri].name;
30589
30909
  svg.append("path").attr("d", arcGen(innerR, outerR)).attr("fill", fillColor).attr("stroke", mutedColor).attr("stroke-width", 0.5);
30910
+ svg.append("path").attr("d", arcGen(innerR, outerR)).attr("fill", "transparent").attr("data-ring-arc", ringName).style("cursor", "pointer").on("mouseenter", () => {
30911
+ d3Selection14.select(rootContainer).selectAll("[data-ring-arc]").each(function() {
30912
+ const el = d3Selection14.select(this);
30913
+ const isMatch = this.getAttribute("data-ring-arc") === ringName;
30914
+ el.attr("fill", isMatch ? qColor : "transparent").attr(
30915
+ "opacity",
30916
+ isMatch ? 0.15 : 1
30917
+ );
30918
+ });
30919
+ dimExceptRing(rootContainer, ringName);
30920
+ }).on("mouseleave", () => {
30921
+ d3Selection14.select(rootContainer).selectAll("[data-ring-arc]").attr("fill", "transparent").attr("opacity", 1);
30922
+ clearDim(rootContainer);
30923
+ });
30590
30924
  }
30591
30925
  const ringOrder = parsed.rings.map((r) => r.name);
30592
30926
  const angularPadding = 0.08;
@@ -30649,10 +30983,21 @@ function dimExcept(root, lineNum) {
30649
30983
  el.style.opacity = el.getAttribute("data-line-number") === lineNum ? "1" : String(DIM_OPACITY);
30650
30984
  });
30651
30985
  }
30986
+ function dimExceptRing(root, ringName) {
30987
+ root.querySelectorAll("[data-line-number]").forEach((el) => {
30988
+ el.style.opacity = el.getAttribute("data-ring") === ringName ? "1" : String(DIM_OPACITY);
30989
+ });
30990
+ root.querySelectorAll("[data-ring-group]").forEach((el) => {
30991
+ el.style.opacity = el.getAttribute("data-ring-group") === ringName ? "1" : String(DIM_OPACITY);
30992
+ });
30993
+ }
30652
30994
  function clearDim(root) {
30653
30995
  root.querySelectorAll("[data-line-number]").forEach((el) => {
30654
30996
  el.style.opacity = "1";
30655
30997
  });
30998
+ root.querySelectorAll("[data-ring-group]").forEach((el) => {
30999
+ el.style.opacity = "1";
31000
+ });
30656
31001
  }
30657
31002
  function renderHtmlPanel(panel, parsed, quadrant, qColor, palette, isDark, rootContainer, onClickItem) {
30658
31003
  const ringOrder = parsed.rings.map((r) => r.name);
@@ -30664,11 +31009,13 @@ function renderHtmlPanel(panel, parsed, quadrant, qColor, palette, isDark, rootC
30664
31009
  const blips = quadrant.blips.filter((b) => b.ring === ringName);
30665
31010
  if (blips.length === 0) continue;
30666
31011
  const ringGroup = document.createElement("div");
31012
+ ringGroup.setAttribute("data-ring-group", ringName);
30667
31013
  ringGroup.style.cssText = `
30668
31014
  background: ${palette.surface};
30669
31015
  border-radius: 8px;
30670
31016
  padding: 10px;
30671
31017
  margin-bottom: 12px;
31018
+ transition: opacity 0.15s;
30672
31019
  `;
30673
31020
  const header = document.createElement("div");
30674
31021
  header.style.cssText = `
@@ -32021,7 +32368,13 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32021
32368
  const containerW = exportDims?.width ?? container.clientWidth;
32022
32369
  const containerH = exportDims?.height ?? container.clientHeight;
32023
32370
  const useContainerFit = !exportDims && containerW > 0 && containerH > 0;
32024
- 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);
32371
+ const svg = d3.select(container).append("svg").attr("xmlns", "http://www.w3.org/2000/svg").attr(
32372
+ "width",
32373
+ useContainerFit ? containerW : exportDims?.width ?? layout.totalWidth
32374
+ ).attr(
32375
+ "height",
32376
+ useContainerFit ? containerH : exportDims?.height ?? layout.totalHeight
32377
+ ).attr("viewBox", `0 0 ${layout.totalWidth} ${layout.totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("font-family", FONT_FAMILY);
32025
32378
  svg.append("rect").attr("width", layout.totalWidth).attr("height", layout.totalHeight).attr("fill", palette.bg);
32026
32379
  const defs = svg.append("defs");
32027
32380
  const curveGradient = defs.append("linearGradient").attr("id", "journey-curve-gradient").attr("x1", "0").attr("y1", "0").attr("x2", "0").attr("y2", "1");
@@ -32093,7 +32446,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32093
32446
  const legendY = PADDING2 + (parsed.title ? FONT_SIZE_TITLE + 8 : 0);
32094
32447
  const legendG = svg.append("g").attr("class", "journey-legend").attr("transform", `translate(${legendX},${legendY})`);
32095
32448
  const legendConfig = {
32096
- groups: allLegendGroups,
32449
+ groups: parsed.tagGroups,
32097
32450
  position: {
32098
32451
  placement: "top-center",
32099
32452
  titleRelation: "inline-with-title"
@@ -32162,7 +32515,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32162
32515
  const y = layout.curveAreaBottom - (score - 1) / 4 * (layout.curveAreaBottom - layout.curveAreaTop - 120) - 10;
32163
32516
  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");
32164
32517
  const SCORE_LABEL_R = 8;
32165
- const labelG = curveG.append("g").attr("class", "journey-score-label");
32518
+ const labelG = curveG.append("g").attr("class", "journey-score-label").attr("data-score", String(score));
32166
32519
  renderScoreFace(
32167
32520
  labelG,
32168
32521
  PADDING2 - SCORE_LABEL_R - 2,
@@ -32172,35 +32525,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32172
32525
  SCORE_LABEL_R
32173
32526
  );
32174
32527
  if (!exportDims) {
32175
- const scoreStr = String(score);
32176
32528
  labelG.style("cursor", "pointer");
32177
- labelG.on("mouseenter", () => {
32178
- svg.selectAll(".journey-step").each(function() {
32179
- const hit = this.getAttribute("data-score") === scoreStr;
32180
- d3.select(this).style("opacity", hit ? "1" : String(DIM_HOVER));
32181
- });
32182
- svg.selectAll(".journey-face").each(function() {
32183
- const hit = this.getAttribute("data-score") === scoreStr;
32184
- const sel = d3.select(this);
32185
- sel.style("opacity", hit ? "1" : String(DIM_HOVER));
32186
- if (hit) {
32187
- const fcx = parseFloat(sel.attr("data-cx") ?? "0");
32188
- const fcy = parseFloat(sel.attr("data-cy") ?? "0");
32189
- sel.attr(
32190
- "transform",
32191
- `translate(${fcx},${fcy}) scale(1.3) translate(${-fcx},${-fcy})`
32192
- );
32193
- } else {
32194
- sel.attr("transform", null);
32195
- }
32196
- });
32197
- svg.selectAll(".journey-thought").style("opacity", String(DIM_HOVER));
32198
- });
32199
- labelG.on("mouseleave", () => {
32200
- svg.selectAll(".journey-step").style("opacity", null);
32201
- svg.selectAll(".journey-face").style("opacity", null).attr("transform", null);
32202
- svg.selectAll(".journey-thought").style("opacity", null);
32203
- });
32204
32529
  }
32205
32530
  }
32206
32531
  if (layout.curvePoints.length >= 2) {
@@ -32386,6 +32711,41 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32386
32711
  if (!exportDims) {
32387
32712
  const DIM_OPACITY2 = 0.35;
32388
32713
  let lockedLine = null;
32714
+ let lockedScore = null;
32715
+ const applyScoreDimming = (activeScore) => {
32716
+ const scoreStr = String(activeScore);
32717
+ svg.selectAll(".journey-step").each(function() {
32718
+ const hit = this.getAttribute("data-score") === scoreStr;
32719
+ d3.select(this).style("opacity", hit ? "1" : String(DIM_HOVER));
32720
+ });
32721
+ svg.selectAll(".journey-face").each(function() {
32722
+ const hit = this.getAttribute("data-score") === scoreStr;
32723
+ const sel = d3.select(this);
32724
+ sel.style("opacity", hit ? "1" : String(DIM_HOVER));
32725
+ if (hit) {
32726
+ const fcx = parseFloat(sel.attr("data-cx") ?? "0");
32727
+ const fcy = parseFloat(sel.attr("data-cy") ?? "0");
32728
+ sel.attr(
32729
+ "transform",
32730
+ `translate(${fcx},${fcy}) scale(1.3) translate(${-fcx},${-fcy})`
32731
+ );
32732
+ } else {
32733
+ sel.attr("transform", null);
32734
+ }
32735
+ });
32736
+ svg.selectAll(".journey-thought").style("opacity", String(DIM_HOVER));
32737
+ svg.selectAll(".journey-score-label").each(function() {
32738
+ const sel = d3.select(this);
32739
+ const s = sel.attr("data-score");
32740
+ sel.style("opacity", s === scoreStr ? "1" : String(DIM_HOVER));
32741
+ });
32742
+ };
32743
+ const clearScoreDimming = () => {
32744
+ svg.selectAll(".journey-step").style("opacity", null);
32745
+ svg.selectAll(".journey-face").style("opacity", null).attr("transform", null);
32746
+ svg.selectAll(".journey-thought").style("opacity", null);
32747
+ svg.selectAll(".journey-score-label").style("opacity", null);
32748
+ };
32389
32749
  const applyDimming = (activeLine) => {
32390
32750
  svg.selectAll(".journey-step").each(function() {
32391
32751
  const el = d3.select(this);
@@ -32440,7 +32800,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32440
32800
  const lines = wrapText4(thoughtText, THOUGHT_MAX_W, THOUGHT_FONT);
32441
32801
  const textW = Math.min(
32442
32802
  THOUGHT_MAX_W,
32443
- Math.max(...lines.map((l) => l.length * 4.8))
32803
+ Math.max(...lines.map((l) => l.length * THOUGHT_FONT * 0.6))
32444
32804
  );
32445
32805
  const bw = textW + THOUGHT_PAD_X * 2;
32446
32806
  const bh = lines.length * THOUGHT_LINE_H + THOUGHT_PAD_Y * 2;
@@ -32461,9 +32821,11 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32461
32821
  };
32462
32822
  svg.on("click", (event) => {
32463
32823
  const target = event.target;
32464
- if (!target.closest(".journey-face") && !target.closest(".journey-step") && !target.closest(".journey-phase")) {
32824
+ if (!target.closest(".journey-face") && !target.closest(".journey-step") && !target.closest(".journey-phase") && !target.closest(".journey-score-label")) {
32465
32825
  lockedLine = null;
32826
+ lockedScore = null;
32466
32827
  clearDimming();
32828
+ clearScoreDimming();
32467
32829
  }
32468
32830
  });
32469
32831
  const showThoughtForLine = (ln) => {
@@ -32477,17 +32839,22 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32477
32839
  svg.selectAll(".journey-face").each(function() {
32478
32840
  const el = d3.select(this);
32479
32841
  el.on("mouseenter", () => {
32480
- if (lockedLine !== null) return;
32842
+ if (lockedLine !== null || lockedScore !== null) return;
32481
32843
  const ln = parseInt(el.attr("data-line-number") ?? "0", 10);
32482
32844
  if (ln) {
32483
32845
  applyDimming(ln);
32484
32846
  showThoughtForLine(ln);
32485
32847
  }
32486
32848
  }).on("mouseleave", () => {
32487
- if (lockedLine !== null) return;
32849
+ if (lockedLine !== null || lockedScore !== null) return;
32488
32850
  clearDimming();
32489
32851
  }).on("click", (event) => {
32490
32852
  event.stopPropagation();
32853
+ if (lockedScore !== null) {
32854
+ lockedScore = null;
32855
+ clearScoreDimming();
32856
+ return;
32857
+ }
32491
32858
  const ln = parseInt(el.attr("data-line-number") ?? "0", 10);
32492
32859
  if (lockedLine === ln) {
32493
32860
  lockedLine = null;
@@ -32503,17 +32870,22 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32503
32870
  svg.selectAll(".journey-step").each(function() {
32504
32871
  const el = d3.select(this);
32505
32872
  el.on("mouseenter", () => {
32506
- if (lockedLine !== null) return;
32873
+ if (lockedLine !== null || lockedScore !== null) return;
32507
32874
  const ln = parseInt(el.attr("data-line-number") ?? "0", 10);
32508
32875
  if (ln) {
32509
32876
  applyDimming(ln);
32510
32877
  showThoughtForLine(ln);
32511
32878
  }
32512
32879
  }).on("mouseleave", () => {
32513
- if (lockedLine !== null) return;
32880
+ if (lockedLine !== null || lockedScore !== null) return;
32514
32881
  clearDimming();
32515
32882
  }).on("click", (event) => {
32516
32883
  event.stopPropagation();
32884
+ if (lockedScore !== null) {
32885
+ lockedScore = null;
32886
+ clearScoreDimming();
32887
+ return;
32888
+ }
32517
32889
  const ln = parseInt(el.attr("data-line-number") ?? "0", 10);
32518
32890
  if (lockedLine === ln) {
32519
32891
  lockedLine = null;
@@ -32526,6 +32898,30 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32526
32898
  }
32527
32899
  });
32528
32900
  });
32901
+ svg.selectAll(".journey-score-label").each(function() {
32902
+ const el = d3.select(this);
32903
+ const score = parseInt(el.attr("data-score") ?? "0", 10);
32904
+ el.on("mouseenter", () => {
32905
+ if (lockedLine !== null || lockedScore !== null) return;
32906
+ applyScoreDimming(score);
32907
+ }).on("mouseleave", () => {
32908
+ if (lockedLine !== null || lockedScore !== null) return;
32909
+ clearScoreDimming();
32910
+ }).on("click", (event) => {
32911
+ event.stopPropagation();
32912
+ if (lockedLine !== null) {
32913
+ lockedLine = null;
32914
+ clearDimming();
32915
+ }
32916
+ if (lockedScore === score) {
32917
+ lockedScore = null;
32918
+ clearScoreDimming();
32919
+ } else {
32920
+ lockedScore = score;
32921
+ applyScoreDimming(score);
32922
+ }
32923
+ });
32924
+ });
32529
32925
  }
32530
32926
  }
32531
32927
  function resolveStepColor(step, scoreColor, activeGroup, tagGroups, _palette) {
@@ -32677,8 +33073,8 @@ function renderScoreFace(parent, cx, cy, score, palette, radius) {
32677
33073
  ).attr("fill", "none").attr("stroke", eyeColor).attr("stroke-width", 1.2).attr("stroke-linecap", "round");
32678
33074
  return g;
32679
33075
  }
32680
- function wrapText4(text, maxWidth, _fontSize) {
32681
- const charWidth = 4.8;
33076
+ function wrapText4(text, maxWidth, fontSize) {
33077
+ const charWidth = fontSize * 0.6;
32682
33078
  const maxChars = Math.floor(maxWidth / charWidth);
32683
33079
  if (maxChars <= 0) return [text];
32684
33080
  const words = text.split(/\s+/);
@@ -32697,7 +33093,7 @@ function wrapText4(text, maxWidth, _fontSize) {
32697
33093
  return lines;
32698
33094
  }
32699
33095
  function truncateText(text, maxWidth) {
32700
- const maxChars = Math.floor(maxWidth / 4.8);
33096
+ const maxChars = Math.floor(maxWidth / 6.6);
32701
33097
  if (text.length <= maxChars) return text;
32702
33098
  return text.substring(0, maxChars - 1) + "\u2026";
32703
33099
  }
@@ -32799,6 +33195,21 @@ var init_renderer13 = __esm({
32799
33195
  }
32800
33196
  });
32801
33197
 
33198
+ // src/cycle/types.ts
33199
+ function arrowHeadLength(strokeWidth) {
33200
+ return BASE_ARROW_SIZE + ARROW_SCALE * Math.sqrt(strokeWidth);
33201
+ }
33202
+ var DEFAULT_EDGE_WIDTH, MIN_EDGE_WIDTH, BASE_ARROW_SIZE, ARROW_SCALE;
33203
+ var init_types2 = __esm({
33204
+ "src/cycle/types.ts"() {
33205
+ "use strict";
33206
+ DEFAULT_EDGE_WIDTH = 3;
33207
+ MIN_EDGE_WIDTH = 2;
33208
+ BASE_ARROW_SIZE = 8;
33209
+ ARROW_SCALE = 6;
33210
+ }
33211
+ });
33212
+
32802
33213
  // src/cycle/layout.ts
32803
33214
  function computeCycleLayout(parsed, options) {
32804
33215
  const width = options?.width ?? 800;
@@ -33133,8 +33544,11 @@ function computeEdgePaths(layoutNodes, parsed, cx, cy, radius, isClockwise) {
33133
33544
  return parsed.edges.map((edge) => {
33134
33545
  const src = layoutNodes[edge.sourceIndex];
33135
33546
  const tgt = layoutNodes[edge.targetIndex];
33136
- const strokeWidth = edge.width ?? DEFAULT_EDGE_WIDTH;
33137
- const arrowLen = ARROWHEAD_MARKER_W * strokeWidth;
33547
+ const strokeWidth = Math.max(
33548
+ edge.width ?? DEFAULT_EDGE_WIDTH,
33549
+ MIN_EDGE_WIDTH
33550
+ );
33551
+ const arrowLen = arrowHeadLength(strokeWidth) * 0.9;
33138
33552
  const { path, labelX, labelY, labelAngle } = buildEdgeArc(
33139
33553
  src,
33140
33554
  tgt,
@@ -33156,7 +33570,7 @@ function computeEdgePaths(layoutNodes, parsed, cx, cy, radius, isClockwise) {
33156
33570
  });
33157
33571
  }
33158
33572
  function fitToCanvas(nodes, edges, parsed, cx, cy, radius, width, height, _isClockwise) {
33159
- const PADDING3 = 10;
33573
+ const PADDING3 = 30;
33160
33574
  let contentMinX = Infinity, contentMaxX = -Infinity;
33161
33575
  let contentMinY = Infinity, contentMaxY = -Infinity;
33162
33576
  for (const n of nodes) {
@@ -33209,17 +33623,7 @@ function fitToCanvas(nodes, edges, parsed, cx, cy, radius, width, height, _isClo
33209
33623
  }
33210
33624
  function buildEdgeArc(src, tgt, cx, cy, radius, isClockwise, arrowLength = 0) {
33211
33625
  const dir = isClockwise ? 1 : -1;
33212
- const startAngle = src.isCircle ? circleNodeExitAngle(src.width / 2, radius, src.angle, dir) : circleRectExitAngle(
33213
- src.x,
33214
- src.y,
33215
- src.width / 2,
33216
- src.height / 2,
33217
- cx,
33218
- cy,
33219
- radius,
33220
- src.angle,
33221
- dir
33222
- );
33626
+ const startAngle = src.angle;
33223
33627
  const nodeEndAngle = tgt.isCircle ? circleNodeExitAngle(tgt.width / 2, radius, tgt.angle, -dir) : circleRectExitAngle(
33224
33628
  tgt.x,
33225
33629
  tgt.y,
@@ -33249,10 +33653,11 @@ function buildEdgeArc(src, tgt, cx, cy, radius, isClockwise, arrowLength = 0) {
33249
33653
  const labelY = cy + labelR * Math.sin(midAngle);
33250
33654
  return { path, labelX, labelY, labelAngle: midAngle };
33251
33655
  }
33252
- 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;
33656
+ 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;
33253
33657
  var init_layout13 = __esm({
33254
33658
  "src/cycle/layout.ts"() {
33255
33659
  "use strict";
33660
+ init_types2();
33256
33661
  MIN_ARC_ANGLE = 15 * Math.PI / 180;
33257
33662
  LABEL_CHAR_W = 8;
33258
33663
  CIRCLE_LABEL_CHAR_W = 10;
@@ -33266,8 +33671,6 @@ var init_layout13 = __esm({
33266
33671
  NODE_PAD_X = 20;
33267
33672
  MIN_CIRCLE_RADIUS = 35;
33268
33673
  CIRCLE_PAD = 14;
33269
- DEFAULT_EDGE_WIDTH = 3;
33270
- ARROWHEAD_MARKER_W = 8;
33271
33674
  EDGE_LABEL_CHAR_W = 7;
33272
33675
  }
33273
33676
  });
@@ -33350,19 +33753,25 @@ function renderCycle(container, parsed, palette, isDark, onClickItem, exportDims
33350
33753
  const g = svg.append("g").attr("transform", `translate(0, ${diagramTop})`);
33351
33754
  const defs = svg.append("defs");
33352
33755
  const defaultNodeColor = palette.primary;
33353
- const arrowColors = /* @__PURE__ */ new Set();
33354
- for (let i = 0; i < parsed.edges.length; i++) {
33355
- const edge = parsed.edges[i];
33756
+ const markerKeys = /* @__PURE__ */ new Set();
33757
+ for (const edge of parsed.edges) {
33356
33758
  const color = resolveEdgeColor(edge, parsed, palette, defaultNodeColor);
33357
- arrowColors.add(color);
33759
+ const sw = Math.max(edge.width ?? DEFAULT_EDGE_WIDTH, MIN_EDGE_WIDTH);
33760
+ const key = `${color}|${sw}`;
33761
+ if (!markerKeys.has(key)) {
33762
+ markerKeys.add(key);
33763
+ ensureArrowMarker(defs, color, sw);
33764
+ }
33358
33765
  }
33359
- ensureArrowMarkers2(defs, arrowColors);
33360
33766
  for (let i = 0; i < layout.edges.length; i++) {
33361
33767
  const le = layout.edges[i];
33362
33768
  const edge = parsed.edges[i];
33363
33769
  const color = resolveEdgeColor(edge, parsed, palette, defaultNodeColor);
33364
- const strokeWidth = edge.width ?? DEFAULT_EDGE_WIDTH2;
33365
- const markerId = `cycle-arrow-${color.replace("#", "")}`;
33770
+ const strokeWidth = Math.max(
33771
+ edge.width ?? DEFAULT_EDGE_WIDTH,
33772
+ MIN_EDGE_WIDTH
33773
+ );
33774
+ const markerId = arrowMarkerId(color, strokeWidth);
33366
33775
  const edgeG = g.append("g").attr("class", "cycle-edge");
33367
33776
  if (edge.lineNumber) {
33368
33777
  edgeG.attr("data-line-number", edge.lineNumber);
@@ -33508,16 +33917,16 @@ function resolveEdgeColor(edge, parsed, palette, defaultNodeColor) {
33508
33917
  }
33509
33918
  return defaultNodeColor;
33510
33919
  }
33511
- function ensureArrowMarkers2(defs, colors) {
33512
- for (const color of colors) {
33513
- const id = `cycle-arrow-${color.replace("#", "")}`;
33514
- 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(
33515
- "points",
33516
- `0,0 ${ARROWHEAD_W5 * 2},${ARROWHEAD_H5} 0,${ARROWHEAD_H5 * 2}`
33517
- ).attr("fill", color);
33518
- }
33920
+ function arrowMarkerId(color, strokeWidth) {
33921
+ return `cycle-arrow-${color.replace("#", "")}-w${strokeWidth}`;
33519
33922
  }
33520
- var d3Selection16, DEFAULT_EDGE_WIDTH2, ARROWHEAD_W5, ARROWHEAD_H5, NODE_FONT_SIZE5, DESC_FONT_SIZE5, EDGE_LABEL_FONT_SIZE9, DESC_LINE_HEIGHT8, TITLE_AREA_HEIGHT;
33923
+ function ensureArrowMarker(defs, color, strokeWidth) {
33924
+ const id = arrowMarkerId(color, strokeWidth);
33925
+ const mw = arrowHeadLength(strokeWidth) / strokeWidth;
33926
+ const mh = Math.max(1.5, mw * 0.5);
33927
+ 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);
33928
+ }
33929
+ var d3Selection16, NODE_FONT_SIZE5, DESC_FONT_SIZE5, EDGE_LABEL_FONT_SIZE9, DESC_LINE_HEIGHT8, TITLE_AREA_HEIGHT;
33521
33930
  var init_renderer14 = __esm({
33522
33931
  "src/cycle/renderer.ts"() {
33523
33932
  "use strict";
@@ -33529,10 +33938,8 @@ var init_renderer14 = __esm({
33529
33938
  init_color_utils();
33530
33939
  init_colors();
33531
33940
  init_inline_markdown();
33941
+ init_types2();
33532
33942
  init_layout13();
33533
- DEFAULT_EDGE_WIDTH2 = 3;
33534
- ARROWHEAD_W5 = 8;
33535
- ARROWHEAD_H5 = 8;
33536
33943
  NODE_FONT_SIZE5 = 13;
33537
33944
  DESC_FONT_SIZE5 = 11;
33538
33945
  EDGE_LABEL_FONT_SIZE9 = 11;
@@ -33541,6 +33948,397 @@ var init_renderer14 = __esm({
33541
33948
  }
33542
33949
  });
33543
33950
 
33951
+ // src/pyramid/renderer.ts
33952
+ var renderer_exports15 = {};
33953
+ __export(renderer_exports15, {
33954
+ renderPyramid: () => renderPyramid,
33955
+ renderPyramidForExport: () => renderPyramidForExport
33956
+ });
33957
+ function renderPyramid(container, parsed, palette, isDark, onClickItem, exportDims) {
33958
+ if (parsed.layers.length === 0) return;
33959
+ d3Selection17.select(container).selectAll(":not([data-d3-tooltip])").remove();
33960
+ const width = exportDims?.width ?? container.clientWidth;
33961
+ const height = exportDims?.height ?? container.clientHeight;
33962
+ if (width <= 0 || height <= 0) return;
33963
+ const hasAnyDescription = parsed.layers.some((l) => l.description.length > 0);
33964
+ const titleH = parsed.title ? TITLE_AREA_HEIGHT2 : 0;
33965
+ const bodyTop = titleH + V_MARGIN;
33966
+ const bodyBottom = height - V_MARGIN;
33967
+ const bodyHeight = Math.max(60, bodyBottom - bodyTop);
33968
+ const sideMargin = width * H_MARGIN_FRAC;
33969
+ const usableWidth = width - sideMargin * 2;
33970
+ const N = parsed.layers.length;
33971
+ const singleProbe = computeLayout2({
33972
+ width,
33973
+ usableWidth,
33974
+ sideMargin,
33975
+ bodyTop,
33976
+ bodyHeight,
33977
+ layers: parsed.layers,
33978
+ hasDescription: hasAnyDescription,
33979
+ alternate: false
33980
+ });
33981
+ const anyOverflow = singleProbe.wraps.some((w) => w.overflows);
33982
+ const useAlternate = anyOverflow && hasAnyDescription && N >= 2;
33983
+ const layout = useAlternate ? computeLayout2({
33984
+ width,
33985
+ usableWidth,
33986
+ sideMargin,
33987
+ bodyTop,
33988
+ bodyHeight,
33989
+ layers: parsed.layers,
33990
+ hasDescription: true,
33991
+ alternate: true
33992
+ }) : singleProbe;
33993
+ 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);
33994
+ svg.append("style").text(
33995
+ ".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}"
33996
+ );
33997
+ svg.append("rect").attr("width", width).attr("height", height).attr("fill", palette.bg);
33998
+ if (parsed.title) {
33999
+ 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");
34000
+ if (onClickItem) {
34001
+ titleText.on("click", () => onClickItem(parsed.titleLineNumber));
34002
+ }
34003
+ }
34004
+ const seriesColors2 = getSeriesColors(palette);
34005
+ const layerBase = isDark ? palette.surface : palette.bg;
34006
+ const resolveSolid = (layer, i) => {
34007
+ if (layer.color) {
34008
+ const named = resolveColor(layer.color, palette);
34009
+ if (named) return named;
34010
+ }
34011
+ return seriesColors2[i % seriesColors2.length];
34012
+ };
34013
+ const diagramG = svg.append("g").attr("class", "pyramid-body");
34014
+ for (let i = 0; i < N; i++) {
34015
+ const layer = parsed.layers[i];
34016
+ const topEdgeY = layout.pyramidTop + i * layout.layerH;
34017
+ const botEdgeY = topEdgeY + layout.layerH;
34018
+ const topHalf = halfWidthAt(i, N, layout.baseWidth, parsed.inverted);
34019
+ const botHalf = halfWidthAt(i + 1, N, layout.baseWidth, parsed.inverted);
34020
+ const polyPoints = [
34021
+ [layout.pyramidCx - topHalf, topEdgeY],
34022
+ [layout.pyramidCx + topHalf, topEdgeY],
34023
+ [layout.pyramidCx + botHalf, botEdgeY],
34024
+ [layout.pyramidCx - botHalf, botEdgeY]
34025
+ ].map((p) => `${p[0]},${p[1]}`).join(" ");
34026
+ const solidColor = resolveSolid(layer, i);
34027
+ const fillColor = mix(solidColor, layerBase, 30);
34028
+ const layerG = diagramG.append("g").attr("class", "pyramid-layer").attr("data-line-number", layer.lineNumber);
34029
+ if (onClickItem) {
34030
+ const ln = layer.lineNumber;
34031
+ layerG.style("cursor", "pointer").on("click", () => onClickItem(ln));
34032
+ }
34033
+ layerG.append("polygon").attr("points", polyPoints).attr("fill", fillColor).attr("stroke", solidColor).attr("stroke-width", 2);
34034
+ const midY = (topEdgeY + botEdgeY) / 2;
34035
+ const labelFitsInside = Math.min(topHalf, botHalf) * 2 > layout.labelFont * 4;
34036
+ const textColor = labelFitsInside ? contrastText(fillColor, "#eceff4", "#2e3440") : palette.text;
34037
+ 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);
34038
+ renderInlineText(labelText, layer.label, palette);
34039
+ if (layer.description.length > 0) {
34040
+ const side = useAlternate ? i % 2 === 0 ? "right" : "left" : "right";
34041
+ const wrap = layout.wraps[i];
34042
+ renderLayerDescriptions(
34043
+ diagramG,
34044
+ layer,
34045
+ side,
34046
+ wrap,
34047
+ layout,
34048
+ midY,
34049
+ solidColor,
34050
+ palette,
34051
+ titleH + V_MARGIN,
34052
+ height - V_MARGIN,
34053
+ onClickItem
34054
+ );
34055
+ }
34056
+ }
34057
+ }
34058
+ function renderPyramidForExport(container, parsed, palette, isDark, exportDims) {
34059
+ renderPyramid(container, parsed, palette, isDark, void 0, exportDims);
34060
+ }
34061
+ function computeLayout2(input) {
34062
+ const {
34063
+ width,
34064
+ usableWidth,
34065
+ sideMargin,
34066
+ bodyTop,
34067
+ bodyHeight,
34068
+ layers,
34069
+ hasDescription,
34070
+ alternate
34071
+ } = input;
34072
+ const N = layers.length;
34073
+ const pyramidShareFrac = hasDescription ? alternate ? PYRAMID_SHARE_ALTERNATE : PYRAMID_SHARE_WITH_DESC : BASE_WIDTH_FRAC_NO_DESC;
34074
+ const pyramidBandWidth = usableWidth * pyramidShareFrac;
34075
+ const maxBaseByHeight = bodyHeight / PITCH_RATIO;
34076
+ const baseWidth = Math.min(pyramidBandWidth, maxBaseByHeight);
34077
+ const pyramidH = baseWidth * PITCH_RATIO;
34078
+ let pyramidCx;
34079
+ if (!hasDescription) {
34080
+ pyramidCx = width / 2;
34081
+ } else if (alternate) {
34082
+ pyramidCx = width / 2;
34083
+ } else {
34084
+ pyramidCx = sideMargin + pyramidBandWidth / 2;
34085
+ }
34086
+ const pyramidTop = bodyTop + (bodyHeight - pyramidH) / 2;
34087
+ const layerH = pyramidH / N;
34088
+ const labelFont = clamp(
34089
+ Math.round(layerH * 0.38),
34090
+ LABEL_FONT_MIN,
34091
+ LABEL_FONT_MAX
34092
+ );
34093
+ const descFont = clamp(
34094
+ Math.round(layerH * 0.22),
34095
+ DESC_FONT_MIN,
34096
+ DESC_FONT_MAX
34097
+ );
34098
+ const descLineHeight = Math.round(descFont * 1.35);
34099
+ const pyramidRightEdge = pyramidCx + baseWidth / 2;
34100
+ const pyramidLeftEdge = pyramidCx - baseWidth / 2;
34101
+ const rightAccentX = pyramidRightEdge + DESC_GAP;
34102
+ const rightTextX = rightAccentX + DESC_ACCENT_WIDTH + DESC_ACCENT_GAP;
34103
+ const rightTextWidth = Math.max(80, width - sideMargin - rightTextX);
34104
+ const leftAccentX = pyramidLeftEdge - DESC_GAP - DESC_ACCENT_WIDTH;
34105
+ const leftTextWidth = Math.max(
34106
+ 80,
34107
+ leftAccentX - DESC_ACCENT_GAP - sideMargin
34108
+ );
34109
+ const leftTextX = sideMargin;
34110
+ const wraps = layers.map((layer, i) => {
34111
+ if (layer.description.length === 0) {
34112
+ return { allLines: [], overflows: false, shortLineCount: 0 };
34113
+ }
34114
+ const side = alternate ? i % 2 === 0 ? "right" : "left" : "right";
34115
+ const colWidth = side === "right" ? rightTextWidth : leftTextWidth;
34116
+ const wrapped = [];
34117
+ for (const line11 of layer.description) {
34118
+ wrapped.push(...wrapText5(line11, colWidth, descFont));
34119
+ }
34120
+ const bandCap = Math.max(1, Math.floor(layerH / descLineHeight) - 0);
34121
+ const overflows = wrapped.length > bandCap;
34122
+ const shortLineCount = overflows ? bandCap : wrapped.length;
34123
+ return { allLines: wrapped, overflows, shortLineCount };
34124
+ });
34125
+ return {
34126
+ alternate,
34127
+ baseWidth,
34128
+ pyramidCx,
34129
+ pyramidTop,
34130
+ pyramidH,
34131
+ layerH,
34132
+ labelFont,
34133
+ descFont,
34134
+ descLineHeight,
34135
+ rightTextX,
34136
+ rightTextWidth,
34137
+ leftTextX,
34138
+ leftTextWidth,
34139
+ rightAccentX,
34140
+ leftAccentX,
34141
+ wraps
34142
+ };
34143
+ }
34144
+ function halfWidthAt(edgeIdx, n, baseWidth, inverted) {
34145
+ const topNarrow = !inverted;
34146
+ const frac = topNarrow ? edgeIdx / n : (n - edgeIdx) / n;
34147
+ return frac * baseWidth / 2;
34148
+ }
34149
+ function renderLayerDescriptions(parentG, layer, side, wrap, layout, midY, accentColor, palette, topBound, bottomBound, onClickItem) {
34150
+ const { descFont, descLineHeight } = layout;
34151
+ const accentX = side === "right" ? layout.rightAccentX : layout.leftAccentX;
34152
+ const textX = side === "right" ? layout.rightTextX : layout.leftTextX;
34153
+ const textAnchor = side === "right" ? "start" : "end";
34154
+ const textLineX = side === "right" ? textX : layout.leftAccentX - DESC_ACCENT_GAP;
34155
+ const availableH = bottomBound - topBound;
34156
+ const fullMaxLines = Math.max(1, Math.floor(availableH / descLineHeight));
34157
+ const fullLines = truncateWithEllipsis(wrap.allLines, fullMaxLines);
34158
+ if (!wrap.overflows) {
34159
+ renderDescriptionVariant({
34160
+ parentG,
34161
+ layer,
34162
+ lines: wrap.allLines,
34163
+ className: "pyramid-desc",
34164
+ accentX,
34165
+ accentColor,
34166
+ textX: textLineX,
34167
+ textAnchor,
34168
+ midY,
34169
+ descFont,
34170
+ descLineHeight,
34171
+ palette,
34172
+ topBound,
34173
+ bottomBound,
34174
+ onClickItem,
34175
+ variant: "short"
34176
+ });
34177
+ return;
34178
+ }
34179
+ const shortLines = buildShortLines(wrap);
34180
+ renderDescriptionVariant({
34181
+ parentG,
34182
+ layer,
34183
+ lines: shortLines,
34184
+ className: "pyramid-desc pyramid-desc-short",
34185
+ accentX,
34186
+ accentColor,
34187
+ textX: textLineX,
34188
+ textAnchor,
34189
+ midY,
34190
+ descFont,
34191
+ descLineHeight,
34192
+ palette,
34193
+ topBound,
34194
+ bottomBound,
34195
+ onClickItem,
34196
+ variant: "short"
34197
+ });
34198
+ renderDescriptionVariant({
34199
+ parentG,
34200
+ layer,
34201
+ lines: fullLines,
34202
+ className: "pyramid-desc pyramid-desc-full",
34203
+ accentX,
34204
+ accentColor,
34205
+ textX: textLineX,
34206
+ textAnchor,
34207
+ midY,
34208
+ descFont,
34209
+ descLineHeight,
34210
+ palette,
34211
+ topBound,
34212
+ bottomBound,
34213
+ onClickItem,
34214
+ variant: "full"
34215
+ });
34216
+ }
34217
+ function truncateWithEllipsis(lines, maxLines) {
34218
+ if (lines.length <= maxLines) return lines.slice();
34219
+ const visible = lines.slice(0, maxLines);
34220
+ if (visible.length === 0) return visible;
34221
+ const last = visible[visible.length - 1];
34222
+ visible[visible.length - 1] = last.endsWith("\u2026") ? last : `${last} \u2026`;
34223
+ return visible;
34224
+ }
34225
+ function buildShortLines(wrap) {
34226
+ if (!wrap.overflows) return wrap.allLines.slice();
34227
+ const visible = wrap.allLines.slice(0, wrap.shortLineCount);
34228
+ if (visible.length === 0) return [];
34229
+ const last = visible[visible.length - 1];
34230
+ visible[visible.length - 1] = last.endsWith("\u2026") ? last : `${last} \u2026`;
34231
+ return visible;
34232
+ }
34233
+ function renderDescriptionVariant(args) {
34234
+ const {
34235
+ parentG,
34236
+ layer,
34237
+ lines,
34238
+ className,
34239
+ accentX,
34240
+ accentColor,
34241
+ textX,
34242
+ textAnchor,
34243
+ midY,
34244
+ descFont,
34245
+ descLineHeight,
34246
+ palette,
34247
+ topBound,
34248
+ bottomBound,
34249
+ onClickItem,
34250
+ variant
34251
+ } = args;
34252
+ if (lines.length === 0) return;
34253
+ const totalH = lines.length * descLineHeight;
34254
+ let startY = midY - totalH / 2 + descLineHeight / 2;
34255
+ const accentPad = Math.max(4, Math.round(descFont * 0.3));
34256
+ const blockTop = startY - descLineHeight / 2 - accentPad;
34257
+ const blockBottom = startY + (lines.length - 1) * descLineHeight + descLineHeight / 2 + accentPad;
34258
+ let shift = 0;
34259
+ if (blockTop < topBound) shift = topBound - blockTop;
34260
+ else if (blockBottom > bottomBound) shift = bottomBound - blockBottom;
34261
+ startY += shift;
34262
+ const accentTop = startY - descLineHeight / 2 - accentPad;
34263
+ const accentH = totalH + accentPad * 2;
34264
+ const descG = parentG.append("g").attr("class", className).attr("data-line-number", layer.lineNumber).attr("data-variant", variant);
34265
+ if (onClickItem) {
34266
+ const ln = layer.lineNumber;
34267
+ descG.style("cursor", "pointer").on("click", () => onClickItem(ln));
34268
+ }
34269
+ 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);
34270
+ for (let j = 0; j < lines.length; j++) {
34271
+ 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);
34272
+ renderInlineText(t, lines[j], palette);
34273
+ }
34274
+ }
34275
+ function wrapText5(line11, maxWidth, fontSize) {
34276
+ if (line11 === "") return [""];
34277
+ const avgCharW = fontSize * CHAR_WIDTH_RATIO4;
34278
+ const maxChars = Math.max(8, Math.floor(maxWidth / avgCharW));
34279
+ const bulletMatch = line11.match(/^(\s*(?:•|-)\s+)(.*)$/);
34280
+ const indent = bulletMatch ? bulletMatch[1] : "";
34281
+ const body = bulletMatch ? bulletMatch[2] : line11;
34282
+ const hangingIndent = " ".repeat(indent.length);
34283
+ const words = body.split(/\s+/);
34284
+ const out = [];
34285
+ let current = indent;
34286
+ for (const word of words) {
34287
+ if (word === "") continue;
34288
+ const tentative = current.length === indent.length ? current + word : `${current} ${word}`;
34289
+ if (tentative.length <= maxChars) {
34290
+ current = tentative;
34291
+ } else {
34292
+ if (current.length > indent.length) out.push(current);
34293
+ if (word.length > maxChars - hangingIndent.length) {
34294
+ let remaining = word;
34295
+ while (remaining.length > maxChars - hangingIndent.length) {
34296
+ const slice = remaining.slice(0, maxChars - hangingIndent.length);
34297
+ out.push(hangingIndent + slice);
34298
+ remaining = remaining.slice(maxChars - hangingIndent.length);
34299
+ }
34300
+ current = remaining.length > 0 ? hangingIndent + remaining : "";
34301
+ } else {
34302
+ current = hangingIndent + word;
34303
+ }
34304
+ }
34305
+ }
34306
+ if (current.length > (out.length === 0 ? 0 : indent.length)) {
34307
+ if (current.trim().length > 0) out.push(current);
34308
+ }
34309
+ return out.length > 0 ? out : [line11];
34310
+ }
34311
+ function clamp(x, lo, hi) {
34312
+ return Math.max(lo, Math.min(hi, x));
34313
+ }
34314
+ var d3Selection17, 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;
34315
+ var init_renderer15 = __esm({
34316
+ "src/pyramid/renderer.ts"() {
34317
+ "use strict";
34318
+ d3Selection17 = __toESM(require("d3-selection"), 1);
34319
+ init_fonts();
34320
+ init_title_constants();
34321
+ init_color_utils();
34322
+ init_colors();
34323
+ init_inline_markdown();
34324
+ TITLE_AREA_HEIGHT2 = 50;
34325
+ H_MARGIN_FRAC = 0.03;
34326
+ V_MARGIN = 16;
34327
+ BASE_WIDTH_FRAC_NO_DESC = 0.78;
34328
+ PYRAMID_SHARE_WITH_DESC = 0.58;
34329
+ PYRAMID_SHARE_ALTERNATE = 0.42;
34330
+ PITCH_RATIO = 0.85;
34331
+ DESC_GAP = 28;
34332
+ DESC_ACCENT_WIDTH = 3;
34333
+ DESC_ACCENT_GAP = 12;
34334
+ CHAR_WIDTH_RATIO4 = 0.55;
34335
+ LABEL_FONT_MIN = 12;
34336
+ LABEL_FONT_MAX = 22;
34337
+ DESC_FONT_MIN = 11;
34338
+ DESC_FONT_MAX = 15;
34339
+ }
34340
+ });
34341
+
33544
34342
  // src/sequence/collapse.ts
33545
34343
  function applyCollapseProjection(parsed, collapsedGroups) {
33546
34344
  if (collapsedGroups.size === 0) {
@@ -33733,8 +34531,8 @@ var init_tag_resolution = __esm({
33733
34531
  });
33734
34532
 
33735
34533
  // src/sequence/renderer.ts
33736
- var renderer_exports15 = {};
33737
- __export(renderer_exports15, {
34534
+ var renderer_exports16 = {};
34535
+ __export(renderer_exports16, {
33738
34536
  applyGroupOrdering: () => applyGroupOrdering,
33739
34537
  applyPositionOverrides: () => applyPositionOverrides,
33740
34538
  buildNoteMessageMap: () => buildNoteMessageMap,
@@ -34101,7 +34899,7 @@ function applyGroupOrdering(participants, groups, messages = []) {
34101
34899
  return result;
34102
34900
  }
34103
34901
  function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateToLine, options) {
34104
- d3Selection17.select(container).selectAll("*").remove();
34902
+ d3Selection18.select(container).selectAll("*").remove();
34105
34903
  const { title, options: parsedOptions } = parsed;
34106
34904
  const effectiveCollapsedGroups = /* @__PURE__ */ new Set();
34107
34905
  for (const group of parsed.groups) {
@@ -34542,7 +35340,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34542
35340
  participants.forEach((p, i) => {
34543
35341
  participantX.set(p.id, offsetX + i * PARTICIPANT_GAP);
34544
35342
  });
34545
- 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);
35343
+ 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);
34546
35344
  const defs = svg.append("defs");
34547
35345
  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(
34548
35346
  "points",
@@ -34787,7 +35585,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34787
35585
  firstBranchStep = Math.min(firstBranchStep, first);
34788
35586
  }
34789
35587
  if (firstBranchStep < Infinity) {
34790
- const dividerY = stepY(firstBranchStep) - stepSpacing / 2;
35588
+ const dividerY = stepY(firstBranchStep) - BLOCK_HEADER_SPACE;
34791
35589
  deferredLines.push({
34792
35590
  x1: frameX,
34793
35591
  y1: dividerY,
@@ -34814,7 +35612,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34814
35612
  firstElseStep = Math.min(firstElseStep, first);
34815
35613
  }
34816
35614
  if (firstElseStep < Infinity) {
34817
- const dividerY = stepY(firstElseStep) - stepSpacing / 2;
35615
+ const dividerY = stepY(firstElseStep) - BLOCK_HEADER_SPACE;
34818
35616
  deferredLines.push({
34819
35617
  x1: frameX,
34820
35618
  y1: dividerY,
@@ -35272,11 +36070,11 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tag
35272
36070
  });
35273
36071
  }
35274
36072
  }
35275
- var d3Selection17, 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;
35276
- var init_renderer15 = __esm({
36073
+ var d3Selection18, 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;
36074
+ var init_renderer16 = __esm({
35277
36075
  "src/sequence/renderer.ts"() {
35278
36076
  "use strict";
35279
- d3Selection17 = __toESM(require("d3-selection"), 1);
36077
+ d3Selection18 = __toESM(require("d3-selection"), 1);
35280
36078
  init_color_utils();
35281
36079
  init_inline_markdown();
35282
36080
  init_fonts();
@@ -35334,15 +36132,15 @@ function renderChartTitle(svg, title, titleLineNumber, width, textColor, onClick
35334
36132
  titleEl.attr("data-line-number", titleLineNumber);
35335
36133
  if (onClickItem) {
35336
36134
  titleEl.on("click", () => onClickItem(titleLineNumber)).on("mouseenter", function() {
35337
- d3Selection18.select(this).attr("opacity", 0.7);
36135
+ d3Selection19.select(this).attr("opacity", 0.7);
35338
36136
  }).on("mouseleave", function() {
35339
- d3Selection18.select(this).attr("opacity", 1);
36137
+ d3Selection19.select(this).attr("opacity", 1);
35340
36138
  });
35341
36139
  }
35342
36140
  }
35343
36141
  }
35344
36142
  function initD3Chart(container, palette, exportDims) {
35345
- d3Selection18.select(container).selectAll(":not([data-d3-tooltip])").remove();
36143
+ d3Selection19.select(container).selectAll(":not([data-d3-tooltip])").remove();
35346
36144
  const width = exportDims?.width ?? container.clientWidth;
35347
36145
  const height = exportDims?.height ?? container.clientHeight;
35348
36146
  if (width <= 0 || height <= 0) return null;
@@ -35350,7 +36148,7 @@ function initD3Chart(container, palette, exportDims) {
35350
36148
  const mutedColor = palette.border;
35351
36149
  const bgColor = palette.bg;
35352
36150
  const colors = getSeriesColors(palette);
35353
- const svg = d3Selection18.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
36151
+ const svg = d3Selection19.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
35354
36152
  return { svg, width, height, textColor, mutedColor, bgColor, colors };
35355
36153
  }
35356
36154
  function parseTimelineDate(s) {
@@ -36693,14 +37491,14 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
36693
37491
  function handleMouseEnter(hovered) {
36694
37492
  const connected = neighbors.get(hovered);
36695
37493
  g.selectAll(".arc-link").each(function() {
36696
- const el = d3Selection18.select(this);
37494
+ const el = d3Selection19.select(this);
36697
37495
  const src = el.attr("data-source");
36698
37496
  const tgt = el.attr("data-target");
36699
37497
  const isRelated = src === hovered || tgt === hovered;
36700
37498
  el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY2);
36701
37499
  });
36702
37500
  g.selectAll(".arc-node").each(function() {
36703
- const el = d3Selection18.select(this);
37501
+ const el = d3Selection19.select(this);
36704
37502
  const name = el.attr("data-node");
36705
37503
  const isRelated = name === hovered || connected.has(name);
36706
37504
  el.attr("opacity", isRelated ? 1 : FADE_OPACITY2);
@@ -36725,23 +37523,23 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
36725
37523
  const members = groupNodeSets.get(groupName);
36726
37524
  if (!members) return;
36727
37525
  g.selectAll(".arc-link").each(function() {
36728
- const el = d3Selection18.select(this);
37526
+ const el = d3Selection19.select(this);
36729
37527
  const isRelated = members.has(el.attr("data-source")) || members.has(el.attr("data-target"));
36730
37528
  el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY2);
36731
37529
  });
36732
37530
  g.selectAll(".arc-node").each(function() {
36733
- const el = d3Selection18.select(this);
37531
+ const el = d3Selection19.select(this);
36734
37532
  el.attr("opacity", members.has(el.attr("data-node")) ? 1 : FADE_OPACITY2);
36735
37533
  });
36736
37534
  g.selectAll(".arc-group-band").each(function() {
36737
- const el = d3Selection18.select(this);
37535
+ const el = d3Selection19.select(this);
36738
37536
  el.attr(
36739
37537
  "fill-opacity",
36740
37538
  el.attr("data-group") === groupName ? 0.18 : 0.03
36741
37539
  );
36742
37540
  });
36743
37541
  g.selectAll(".arc-group-label").each(function() {
36744
- const el = d3Selection18.select(this);
37542
+ const el = d3Selection19.select(this);
36745
37543
  el.attr("fill-opacity", el.attr("data-group") === groupName ? 1 : 0.2);
36746
37544
  });
36747
37545
  }
@@ -36999,7 +37797,7 @@ function showEventDatesOnScale(g, scale, startDate, endDate, innerHeight, accent
36999
37797
  function hideEventDatesOnScale(g) {
37000
37798
  g.selectAll(".tl-event-date").remove();
37001
37799
  g.selectAll(".tl-scale-tick").each(function() {
37002
- const el = d3Selection18.select(this);
37800
+ const el = d3Selection19.select(this);
37003
37801
  const isDashed = el.attr("stroke-dasharray");
37004
37802
  el.attr("opacity", isDashed ? 0.15 : 0.4);
37005
37803
  });
@@ -37077,7 +37875,7 @@ function renderTimelineGroupLegend(g, groups, groupColorMap, textColor, palette,
37077
37875
  }
37078
37876
  }
37079
37877
  function renderTimeline(container, parsed, palette, isDark, onClickItem, exportDims, activeTagGroup, swimlaneTagGroup, onTagStateChange, viewMode) {
37080
- d3Selection18.select(container).selectAll(":not([data-d3-tooltip])").remove();
37878
+ d3Selection19.select(container).selectAll(":not([data-d3-tooltip])").remove();
37081
37879
  const {
37082
37880
  timelineEvents,
37083
37881
  timelineGroups,
@@ -37175,13 +37973,13 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37175
37973
  const FADE_OPACITY2 = 0.1;
37176
37974
  function fadeToGroup(g, groupName) {
37177
37975
  g.selectAll(".tl-event").each(function() {
37178
- const el = d3Selection18.select(this);
37976
+ const el = d3Selection19.select(this);
37179
37977
  const evGroup = el.attr("data-group");
37180
37978
  el.attr("opacity", evGroup === groupName ? 1 : FADE_OPACITY2);
37181
37979
  });
37182
37980
  g.selectAll(".tl-legend-item, .tl-lane-header").each(
37183
37981
  function() {
37184
- const el = d3Selection18.select(this);
37982
+ const el = d3Selection19.select(this);
37185
37983
  const name = el.attr("data-group");
37186
37984
  el.attr("opacity", name === groupName ? 1 : FADE_OPACITY2);
37187
37985
  }
@@ -37193,7 +37991,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37193
37991
  }
37194
37992
  function fadeToEra(g, eraStart, eraEnd) {
37195
37993
  g.selectAll(".tl-event").each(function() {
37196
- const el = d3Selection18.select(this);
37994
+ const el = d3Selection19.select(this);
37197
37995
  const date = parseFloat(el.attr("data-date"));
37198
37996
  const endDate = el.attr("data-end-date");
37199
37997
  const evEnd = endDate ? parseFloat(endDate) : date;
@@ -37205,14 +38003,14 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37205
38003
  FADE_OPACITY2
37206
38004
  );
37207
38005
  g.selectAll(".tl-era").each(function() {
37208
- const el = d3Selection18.select(this);
38006
+ const el = d3Selection19.select(this);
37209
38007
  const s = parseFloat(el.attr("data-era-start"));
37210
38008
  const e = parseFloat(el.attr("data-era-end"));
37211
38009
  const isSelf = s === eraStart && e === eraEnd;
37212
38010
  el.attr("opacity", isSelf ? 1 : FADE_OPACITY2);
37213
38011
  });
37214
38012
  g.selectAll(".tl-marker").each(function() {
37215
- const el = d3Selection18.select(this);
38013
+ const el = d3Selection19.select(this);
37216
38014
  const date = parseFloat(el.attr("data-marker-date"));
37217
38015
  const inside = date >= eraStart && date <= eraEnd;
37218
38016
  el.attr("opacity", inside ? 1 : FADE_OPACITY2);
@@ -37227,7 +38025,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37227
38025
  function fadeToTagValue(g, tagKey, tagValue) {
37228
38026
  const attrName = `data-tag-${tagKey}`;
37229
38027
  g.selectAll(".tl-event").each(function() {
37230
- const el = d3Selection18.select(this);
38028
+ const el = d3Selection19.select(this);
37231
38029
  const val = el.attr(attrName);
37232
38030
  el.attr("opacity", val === tagValue ? 1 : FADE_OPACITY2);
37233
38031
  });
@@ -37240,7 +38038,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37240
38038
  FADE_OPACITY2
37241
38039
  );
37242
38040
  g.selectAll(".tl-tag-legend-entry").each(function() {
37243
- const el = d3Selection18.select(this);
38041
+ const el = d3Selection19.select(this);
37244
38042
  const entryValue = el.attr("data-legend-entry");
37245
38043
  if (entryValue === "__group__") return;
37246
38044
  const entryGroup = el.attr("data-tag-group");
@@ -37292,7 +38090,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37292
38090
  const innerHeight = height - margin.top - margin.bottom;
37293
38091
  const laneWidth = innerWidth / laneCount;
37294
38092
  const yScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
37295
- 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);
38093
+ 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);
37296
38094
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
37297
38095
  renderChartTitle(
37298
38096
  svg,
@@ -37382,7 +38180,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37382
38180
  const gradientId = `uncertain-vg-${ev.lineNumber}`;
37383
38181
  const strokeGradientId = `uncertain-vg-s-${ev.lineNumber}`;
37384
38182
  const defs = svg.select("defs").node() || svg.append("defs").node();
37385
- const defsEl = d3Selection18.select(defs);
38183
+ const defsEl = d3Selection19.select(defs);
37386
38184
  defsEl.append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "0%").attr("y2", "100%").selectAll("stop").data([
37387
38185
  { offset: "0%", opacity: 1 },
37388
38186
  { offset: "80%", opacity: 1 },
@@ -37418,7 +38216,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37418
38216
  const axisX = 20;
37419
38217
  const yScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
37420
38218
  const sorted = timelineEvents.slice().sort((a, b) => parseTimelineDate(a.date) - parseTimelineDate(b.date));
37421
- 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);
38219
+ 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);
37422
38220
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
37423
38221
  renderChartTitle(
37424
38222
  svg,
@@ -37507,7 +38305,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37507
38305
  const gradientId = `uncertain-v-${ev.lineNumber}`;
37508
38306
  const strokeGradientId = `uncertain-v-s-${ev.lineNumber}`;
37509
38307
  const defs = svg.select("defs").node() || svg.append("defs").node();
37510
- const defsEl = d3Selection18.select(defs);
38308
+ const defsEl = d3Selection19.select(defs);
37511
38309
  defsEl.append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "0%").attr("y2", "100%").selectAll("stop").data([
37512
38310
  { offset: "0%", opacity: 1 },
37513
38311
  { offset: "80%", opacity: 1 },
@@ -37575,7 +38373,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37575
38373
  const totalGaps = (lanes.length - 1) * GROUP_GAP3;
37576
38374
  const rowH = Math.min(28, (innerHeight - totalGaps) / totalEventRows);
37577
38375
  const xScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
37578
- const svg = d3Selection18.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
38376
+ const svg = d3Selection19.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
37579
38377
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
37580
38378
  renderChartTitle(
37581
38379
  svg,
@@ -37688,7 +38486,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37688
38486
  const gradientId = `uncertain-${ev.lineNumber}`;
37689
38487
  const strokeGradientId = `uncertain-s-${ev.lineNumber}`;
37690
38488
  const defs = svg.select("defs").node() || svg.append("defs").node();
37691
- const defsEl = d3Selection18.select(defs);
38489
+ const defsEl = d3Selection19.select(defs);
37692
38490
  defsEl.append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
37693
38491
  { offset: "0%", opacity: 1 },
37694
38492
  { offset: "80%", opacity: 1 },
@@ -37736,7 +38534,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37736
38534
  const innerHeight = height - margin.top - margin.bottom;
37737
38535
  const rowH = Math.min(28, innerHeight / sorted.length);
37738
38536
  const xScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
37739
- const svg = d3Selection18.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
38537
+ const svg = d3Selection19.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
37740
38538
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
37741
38539
  renderChartTitle(
37742
38540
  svg,
@@ -37845,7 +38643,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37845
38643
  const gradientId = `uncertain-ts-${ev.lineNumber}`;
37846
38644
  const strokeGradientId = `uncertain-ts-s-${ev.lineNumber}`;
37847
38645
  const defs = svg.select("defs").node() || svg.append("defs").node();
37848
- const defsEl = d3Selection18.select(defs);
38646
+ const defsEl = d3Selection19.select(defs);
37849
38647
  defsEl.append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
37850
38648
  { offset: "0%", opacity: 1 },
37851
38649
  { offset: "80%", opacity: 1 },
@@ -37888,7 +38686,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37888
38686
  const LG_ENTRY_DOT_GAP = LEGEND_ENTRY_DOT_GAP;
37889
38687
  const LG_ENTRY_TRAIL = LEGEND_ENTRY_TRAIL;
37890
38688
  const LG_ICON_W = 20;
37891
- const mainSvg = d3Selection18.select(container).select("svg");
38689
+ const mainSvg = d3Selection19.select(container).select("svg");
37892
38690
  const mainG = mainSvg.select("g");
37893
38691
  if (!mainSvg.empty() && !mainG.empty()) {
37894
38692
  let drawSwimlaneIcon4 = function(parent, x, y, isSwimActive) {
@@ -37961,7 +38759,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37961
38759
  const tagVal = entryValue.toLowerCase();
37962
38760
  fadeToTagValue(mainG, tagKey, tagVal);
37963
38761
  mainSvg.selectAll("[data-legend-entry]").each(function() {
37964
- const el = d3Selection18.select(this);
38762
+ const el = d3Selection19.select(this);
37965
38763
  const ev = el.attr("data-legend-entry");
37966
38764
  const eg = el.attr("data-tag-group") ?? el.node()?.closest?.("[data-tag-group]")?.getAttribute("data-tag-group");
37967
38765
  el.attr(
@@ -38014,7 +38812,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
38014
38812
  }, recolorEvents2 = function() {
38015
38813
  const colorTG = currentActiveGroup ?? swimlaneTagGroup ?? null;
38016
38814
  mainG.selectAll(".tl-event").each(function() {
38017
- const el = d3Selection18.select(this);
38815
+ const el = d3Selection19.select(this);
38018
38816
  const lineNum = el.attr("data-line-number");
38019
38817
  const ev = lineNum ? eventByLine.get(lineNum) : void 0;
38020
38818
  if (!ev) return;
@@ -38112,7 +38910,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
38112
38910
  }
38113
38911
  function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
38114
38912
  return new Promise((resolve) => {
38115
- d3Selection18.select(container).selectAll(":not([data-d3-tooltip])").remove();
38913
+ d3Selection19.select(container).selectAll(":not([data-d3-tooltip])").remove();
38116
38914
  const { words, title, cloudOptions } = parsed;
38117
38915
  if (words.length === 0) {
38118
38916
  resolve();
@@ -38139,7 +38937,7 @@ function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
38139
38937
  return minSize + Math.sqrt(t) * (maxSize - minSize);
38140
38938
  };
38141
38939
  const rotateFn = getRotateFn(cloudOptions.rotate);
38142
- const svg = d3Selection18.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
38940
+ const svg = d3Selection19.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
38143
38941
  renderChartTitle(svg, title, parsed.titleLineNumber, width, textColor);
38144
38942
  const g = svg.append("g").attr(
38145
38943
  "transform",
@@ -38641,8 +39439,8 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38641
39439
  const LABEL_MAX_FONT = 48;
38642
39440
  const LABEL_MIN_FONT = 14;
38643
39441
  const LABEL_PAD3 = 40;
38644
- const CHAR_WIDTH_RATIO4 = 0.6;
38645
- const estTextWidth = (text, fontSize) => text.length * fontSize * CHAR_WIDTH_RATIO4;
39442
+ const CHAR_WIDTH_RATIO5 = 0.6;
39443
+ const estTextWidth = (text, fontSize) => text.length * fontSize * CHAR_WIDTH_RATIO5;
38646
39444
  const quadrantLabelLayout = (text, qw2, qh2) => {
38647
39445
  const availW = qw2 - LABEL_PAD3;
38648
39446
  const availH = qh2 - LABEL_PAD3;
@@ -38705,7 +39503,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38705
39503
  (d) => onClickItem && d.label?.lineNumber ? "pointer" : "default"
38706
39504
  ).each(function(d) {
38707
39505
  const layout = labelLayouts.get(d.label.text);
38708
- const el = d3Selection18.select(this);
39506
+ const el = d3Selection19.select(this);
38709
39507
  if (layout.lines.length === 1) {
38710
39508
  el.text(layout.lines[0]);
38711
39509
  } else {
@@ -38721,9 +39519,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38721
39519
  quadrantLabelTexts.on("click", (_, d) => {
38722
39520
  if (d.label?.lineNumber) onClickItem(d.label.lineNumber);
38723
39521
  }).on("mouseenter", function() {
38724
- d3Selection18.select(this).attr("opacity", 0.7);
39522
+ d3Selection19.select(this).attr("opacity", 0.7);
38725
39523
  }).on("mouseleave", function() {
38726
- d3Selection18.select(this).attr("opacity", 1);
39524
+ d3Selection19.select(this).attr("opacity", 1);
38727
39525
  });
38728
39526
  }
38729
39527
  if (quadrantXAxis) {
@@ -38744,9 +39542,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38744
39542
  if (onClickItem && quadrantXAxisLineNumber) {
38745
39543
  [xLowLabel, xHighLabel].forEach((label) => {
38746
39544
  label.on("click", () => onClickItem(quadrantXAxisLineNumber)).on("mouseenter", function() {
38747
- d3Selection18.select(this).attr("opacity", 0.7);
39545
+ d3Selection19.select(this).attr("opacity", 0.7);
38748
39546
  }).on("mouseleave", function() {
38749
- d3Selection18.select(this).attr("opacity", 1);
39547
+ d3Selection19.select(this).attr("opacity", 1);
38750
39548
  });
38751
39549
  });
38752
39550
  }
@@ -38771,9 +39569,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38771
39569
  if (onClickItem && quadrantYAxisLineNumber) {
38772
39570
  [yLowLabel, yHighLabel].forEach((label) => {
38773
39571
  label.on("click", () => onClickItem(quadrantYAxisLineNumber)).on("mouseenter", function() {
38774
- d3Selection18.select(this).attr("opacity", 0.7);
39572
+ d3Selection19.select(this).attr("opacity", 0.7);
38775
39573
  }).on("mouseleave", function() {
38776
- d3Selection18.select(this).attr("opacity", 1);
39574
+ d3Selection19.select(this).attr("opacity", 1);
38777
39575
  });
38778
39576
  });
38779
39577
  }
@@ -38790,7 +39588,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38790
39588
  const POINT_LABEL_FONT_SIZE = 12;
38791
39589
  const quadrantLabelObstacles = quadrantDefsWithLabel.map((d) => {
38792
39590
  const layout = labelLayouts.get(d.label.text);
38793
- const totalW = Math.max(...layout.lines.map((l) => l.length)) * layout.fontSize * CHAR_WIDTH_RATIO4;
39591
+ const totalW = Math.max(...layout.lines.map((l) => l.length)) * layout.fontSize * CHAR_WIDTH_RATIO5;
38794
39592
  const totalH = layout.lines.length * layout.fontSize * 1.2;
38795
39593
  return {
38796
39594
  x: d.labelX - totalW / 2,
@@ -38850,7 +39648,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38850
39648
  pointsG.selectAll("g.point-group").each(function(_2, i) {
38851
39649
  const pt = quadrantPoints[i];
38852
39650
  const ptQuad = getPointQuadrant(pt.x, pt.y);
38853
- d3Selection18.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
39651
+ d3Selection19.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
38854
39652
  });
38855
39653
  }).on("mouseleave", () => {
38856
39654
  quadrantRects.attr("opacity", 1);
@@ -39369,6 +40167,22 @@ async function renderForExport(content, theme, palette, viewState, options) {
39369
40167
  );
39370
40168
  return finalizeSvgExport(container2, theme, effectivePalette2);
39371
40169
  }
40170
+ if (detectedType === "pyramid") {
40171
+ const { parsePyramid: parsePyramid2 } = await Promise.resolve().then(() => (init_parser16(), parser_exports16));
40172
+ const { renderPyramidForExport: renderPyramidForExport2 } = await Promise.resolve().then(() => (init_renderer15(), renderer_exports15));
40173
+ const effectivePalette2 = await resolveExportPalette(theme, palette);
40174
+ const pyramidParsed = parsePyramid2(content);
40175
+ if (pyramidParsed.error || pyramidParsed.layers.length === 0) return "";
40176
+ const container2 = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
40177
+ renderPyramidForExport2(
40178
+ container2,
40179
+ pyramidParsed,
40180
+ effectivePalette2,
40181
+ theme === "dark",
40182
+ { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
40183
+ );
40184
+ return finalizeSvgExport(container2, theme, effectivePalette2);
40185
+ }
39372
40186
  const parsed = parseVisualization(content, palette);
39373
40187
  if (parsed.error && parsed.type !== "sequence") {
39374
40188
  const looksLikeSequence2 = /->|~>|<-/.test(content);
@@ -39392,7 +40206,7 @@ async function renderForExport(content, theme, palette, viewState, options) {
39392
40206
  };
39393
40207
  if (parsed.type === "sequence") {
39394
40208
  const { parseSequenceDgmo: parseSequenceDgmo2 } = await Promise.resolve().then(() => (init_parser(), parser_exports));
39395
- const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer15(), renderer_exports15));
40209
+ const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer16(), renderer_exports16));
39396
40210
  const seqParsed = parseSequenceDgmo2(content);
39397
40211
  if (seqParsed.error || seqParsed.participants.length === 0) return "";
39398
40212
  renderSequenceDiagram2(
@@ -39461,12 +40275,12 @@ async function renderForExport(content, theme, palette, viewState, options) {
39461
40275
  }
39462
40276
  return finalizeSvgExport(container, theme, effectivePalette);
39463
40277
  }
39464
- var d3Scale2, d3Selection18, d3Shape10, d3Array, import_d3_cloud, DEFAULT_CLOUD_OPTIONS, STOP_WORDS, SLOPE_MARGIN, SLOPE_LABEL_FONT_SIZE, SLOPE_CHAR_WIDTH, ARC_MARGIN, EXPORT_WIDTH, EXPORT_HEIGHT;
40278
+ var d3Scale2, d3Selection19, d3Shape10, d3Array, import_d3_cloud, DEFAULT_CLOUD_OPTIONS, STOP_WORDS, SLOPE_MARGIN, SLOPE_LABEL_FONT_SIZE, SLOPE_CHAR_WIDTH, ARC_MARGIN, EXPORT_WIDTH, EXPORT_HEIGHT;
39465
40279
  var init_d3 = __esm({
39466
40280
  "src/d3.ts"() {
39467
40281
  "use strict";
39468
40282
  d3Scale2 = __toESM(require("d3-scale"), 1);
39469
- d3Selection18 = __toESM(require("d3-selection"), 1);
40283
+ d3Selection19 = __toESM(require("d3-selection"), 1);
39470
40284
  d3Shape10 = __toESM(require("d3-shape"), 1);
39471
40285
  d3Array = __toESM(require("d3-array"), 1);
39472
40286
  import_d3_cloud = __toESM(require("d3-cloud"), 1);
@@ -40071,6 +40885,7 @@ __export(index_exports, {
40071
40885
  ALL_CHART_TYPES: () => ALL_CHART_TYPES,
40072
40886
  ARROW_DIAGNOSTIC_CODES: () => ARROW_DIAGNOSTIC_CODES,
40073
40887
  CHART_TYPES: () => CHART_TYPES,
40888
+ CHART_TYPE_DESCRIPTIONS: () => CHART_TYPE_DESCRIPTIONS,
40074
40889
  COMPLETION_REGISTRY: () => COMPLETION_REGISTRY,
40075
40890
  ENTITY_TYPES: () => ENTITY_TYPES,
40076
40891
  INFRA_BEHAVIOR_KEYS: () => INFRA_BEHAVIOR_KEYS,
@@ -40118,8 +40933,10 @@ __export(index_exports, {
40118
40933
  encodeViewState: () => encodeViewState,
40119
40934
  extractDiagramSymbols: () => extractDiagramSymbols,
40120
40935
  extractTagDeclarations: () => extractTagDeclarations,
40936
+ focusOrgTree: () => focusOrgTree,
40121
40937
  formatDateLabel: () => formatDateLabel,
40122
40938
  formatDgmoError: () => formatDgmoError,
40939
+ getAllChartTypes: () => getAllChartTypes,
40123
40940
  getAvailablePalettes: () => getAvailablePalettes,
40124
40941
  getExtendedChartLegendGroups: () => getExtendedChartLegendGroups,
40125
40942
  getLegendReservedHeight: () => getLegendReservedHeight,
@@ -40190,7 +41007,9 @@ __export(index_exports, {
40190
41007
  parseKanban: () => parseKanban,
40191
41008
  parseMindmap: () => parseMindmap,
40192
41009
  parseOrg: () => parseOrg,
41010
+ parsePyramid: () => parsePyramid,
40193
41011
  parseSequenceDgmo: () => parseSequenceDgmo,
41012
+ parseSequenceDiagram: () => parseSequenceDgmo,
40194
41013
  parseSitemap: () => parseSitemap,
40195
41014
  parseState: () => parseState,
40196
41015
  parseTechRadar: () => parseTechRadar,
@@ -40233,6 +41052,8 @@ __export(index_exports, {
40233
41052
  renderMindmapForExport: () => renderMindmapForExport,
40234
41053
  renderOrg: () => renderOrg,
40235
41054
  renderOrgForExport: () => renderOrgForExport,
41055
+ renderPyramid: () => renderPyramid,
41056
+ renderPyramidForExport: () => renderPyramidForExport,
40236
41057
  renderQuadrant: () => renderQuadrant,
40237
41058
  renderQuadrantFocus: () => renderQuadrantFocus,
40238
41059
  renderQuadrantFocusForExport: () => renderQuadrantFocusForExport,
@@ -40303,8 +41124,7 @@ async function render(content, options) {
40303
41124
  const theme = options?.theme ?? "light";
40304
41125
  const paletteName = options?.palette ?? "nord";
40305
41126
  const paletteColors = getPalette(paletteName)[theme === "dark" ? "dark" : "light"];
40306
- const { diagnostics } = parseDgmo(content);
40307
- const chartType = parseDgmoChartType(content);
41127
+ const { diagnostics, chartType } = parseDgmo(content);
40308
41128
  const category = chartType ? getRenderCategory(chartType) : null;
40309
41129
  const viewState = options?.viewState ?? (options?.legendState ? {
40310
41130
  tag: options.legendState.activeGroup ?? void 0,
@@ -40625,6 +41445,8 @@ init_renderer14();
40625
41445
  init_parser15();
40626
41446
  init_layout12();
40627
41447
  init_renderer13();
41448
+ init_parser16();
41449
+ init_renderer15();
40628
41450
 
40629
41451
  // src/org/resolver.ts
40630
41452
  init_diagnostics();
@@ -40960,7 +41782,7 @@ init_legend_constants();
40960
41782
  init_legend_d3();
40961
41783
  init_legend_layout();
40962
41784
  init_d3();
40963
- init_renderer15();
41785
+ init_renderer16();
40964
41786
  init_collapse4();
40965
41787
  init_colors();
40966
41788
  init_palettes();
@@ -41063,6 +41885,7 @@ init_flowchart_parser();
41063
41885
  init_parser8();
41064
41886
  init_parser2();
41065
41887
  init_parsing();
41888
+ init_dgmo_router();
41066
41889
  var extractorRegistry = /* @__PURE__ */ new Map();
41067
41890
  function registerExtractor(kind, fn) {
41068
41891
  extractorRegistry.set(kind, fn);
@@ -41378,51 +42201,16 @@ var COMPLETION_REGISTRY = /* @__PURE__ */ new Map([
41378
42201
  "no-legend": { description: "Hide the score legend" },
41379
42202
  persona: { description: "Define the journey persona" }
41380
42203
  })
42204
+ ],
42205
+ [
42206
+ "pyramid",
42207
+ withGlobals({
42208
+ inverted: { description: "Flip apex to the bottom (funnel orientation)" },
42209
+ color: { description: "Override layer color (pipe metadata)" },
42210
+ description: { description: "Layer description (pipe or indented body)" }
42211
+ })
41381
42212
  ]
41382
42213
  ]);
41383
- var CHART_TYPE_DESCRIPTIONS = {
41384
- // Data charts
41385
- bar: "Bar chart",
41386
- line: "Line chart",
41387
- pie: "Pie chart",
41388
- doughnut: "Doughnut chart",
41389
- area: "Area chart",
41390
- "polar-area": "Polar area chart",
41391
- radar: "Radar chart",
41392
- "bar-stacked": "Stacked bar chart",
41393
- // Extended charts
41394
- scatter: "Scatter plot",
41395
- heatmap: "Heatmap",
41396
- sankey: "Sankey flow diagram",
41397
- chord: "Chord diagram",
41398
- funnel: "Funnel chart",
41399
- function: "Mathematical function plot",
41400
- // Visualizations
41401
- slope: "Slope chart",
41402
- wordcloud: "Word cloud",
41403
- arc: "Arc diagram",
41404
- timeline: "Timeline",
41405
- venn: "Venn diagram",
41406
- quadrant: "Quadrant chart",
41407
- // Diagrams
41408
- sequence: "Sequence diagram",
41409
- flowchart: "Flowchart",
41410
- class: "Class diagram",
41411
- er: "Entity-relationship diagram",
41412
- org: "Organization chart",
41413
- kanban: "Kanban board",
41414
- c4: "C4 architecture diagram",
41415
- state: "State diagram",
41416
- sitemap: "Sitemap diagram",
41417
- infra: "Infrastructure diagram",
41418
- gantt: "Gantt chart",
41419
- "boxes-and-lines": "Boxes and lines diagram",
41420
- mindmap: "Mindmap diagram",
41421
- wireframe: "UI wireframe diagram",
41422
- "tech-radar": "Technology adoption radar (ThoughtWorks style)",
41423
- cycle: "Cycle diagram (circular process flow)",
41424
- "journey-map": "User journey map with emotion curve"
41425
- };
41426
42214
  var CHART_TYPES = [...ALL_CHART_TYPES].filter((t) => t !== "multi-line").map((name) => ({
41427
42215
  name,
41428
42216
  description: CHART_TYPE_DESCRIPTIONS[name] ?? name
@@ -42014,6 +42802,7 @@ init_parsing();
42014
42802
  ALL_CHART_TYPES,
42015
42803
  ARROW_DIAGNOSTIC_CODES,
42016
42804
  CHART_TYPES,
42805
+ CHART_TYPE_DESCRIPTIONS,
42017
42806
  COMPLETION_REGISTRY,
42018
42807
  ENTITY_TYPES,
42019
42808
  INFRA_BEHAVIOR_KEYS,
@@ -42061,8 +42850,10 @@ init_parsing();
42061
42850
  encodeViewState,
42062
42851
  extractDiagramSymbols,
42063
42852
  extractTagDeclarations,
42853
+ focusOrgTree,
42064
42854
  formatDateLabel,
42065
42855
  formatDgmoError,
42856
+ getAllChartTypes,
42066
42857
  getAvailablePalettes,
42067
42858
  getExtendedChartLegendGroups,
42068
42859
  getLegendReservedHeight,
@@ -42133,7 +42924,9 @@ init_parsing();
42133
42924
  parseKanban,
42134
42925
  parseMindmap,
42135
42926
  parseOrg,
42927
+ parsePyramid,
42136
42928
  parseSequenceDgmo,
42929
+ parseSequenceDiagram,
42137
42930
  parseSitemap,
42138
42931
  parseState,
42139
42932
  parseTechRadar,
@@ -42176,6 +42969,8 @@ init_parsing();
42176
42969
  renderMindmapForExport,
42177
42970
  renderOrg,
42178
42971
  renderOrgForExport,
42972
+ renderPyramid,
42973
+ renderPyramidForExport,
42179
42974
  renderQuadrant,
42180
42975
  renderQuadrantFocus,
42181
42976
  renderQuadrantFocusForExport,