@diagrammo/dgmo 0.8.22 → 0.8.25

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 (90) hide show
  1. package/.claude/commands/dgmo.md +60 -72
  2. package/dist/cli.cjs +123 -116
  3. package/dist/editor.cjs +3 -2
  4. package/dist/editor.cjs.map +1 -1
  5. package/dist/editor.js +3 -2
  6. package/dist/editor.js.map +1 -1
  7. package/dist/highlight.cjs +3 -2
  8. package/dist/highlight.cjs.map +1 -1
  9. package/dist/highlight.js +3 -2
  10. package/dist/highlight.js.map +1 -1
  11. package/dist/index.cjs +1649 -442
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.cts +196 -23
  14. package/dist/index.d.ts +196 -23
  15. package/dist/index.js +1631 -440
  16. package/dist/index.js.map +1 -1
  17. package/dist/internal.cjs +677 -0
  18. package/dist/internal.cjs.map +1 -0
  19. package/dist/internal.d.cts +267 -0
  20. package/dist/internal.d.ts +267 -0
  21. package/dist/internal.js +633 -0
  22. package/dist/internal.js.map +1 -0
  23. package/docs/guide/chart-area.md +17 -17
  24. package/docs/guide/chart-bar-stacked.md +12 -12
  25. package/docs/guide/chart-cycle.md +156 -0
  26. package/docs/guide/chart-doughnut.md +10 -10
  27. package/docs/guide/chart-funnel.md +9 -9
  28. package/docs/guide/chart-heatmap.md +10 -10
  29. package/docs/guide/chart-journey-map.md +179 -0
  30. package/docs/guide/chart-kanban.md +2 -0
  31. package/docs/guide/chart-line.md +19 -19
  32. package/docs/guide/chart-multi-line.md +16 -16
  33. package/docs/guide/chart-pie.md +11 -11
  34. package/docs/guide/chart-polar-area.md +10 -10
  35. package/docs/guide/chart-pyramid.md +111 -0
  36. package/docs/guide/chart-radar.md +9 -9
  37. package/docs/guide/chart-scatter.md +24 -27
  38. package/docs/guide/index.md +3 -3
  39. package/docs/guide/registry.json +5 -0
  40. package/docs/language-reference.md +108 -26
  41. package/fonts/Inter-Bold.ttf +0 -0
  42. package/fonts/Inter-Regular.ttf +0 -0
  43. package/fonts/LICENSE-Inter.txt +92 -0
  44. package/gallery/fixtures/bar-stacked.dgmo +12 -6
  45. package/gallery/fixtures/heatmap.dgmo +12 -6
  46. package/gallery/fixtures/multi-line.dgmo +11 -7
  47. package/gallery/fixtures/pyramid/dikw.dgmo +17 -0
  48. package/gallery/fixtures/pyramid/inverted-funnel.dgmo +16 -0
  49. package/gallery/fixtures/pyramid/minimal.dgmo +5 -0
  50. package/gallery/fixtures/quadrant.dgmo +8 -8
  51. package/gallery/fixtures/scatter.dgmo +12 -12
  52. package/package.json +14 -2
  53. package/src/boxes-and-lines/parser.ts +13 -2
  54. package/src/boxes-and-lines/renderer.ts +22 -13
  55. package/src/chart-type-scoring.ts +162 -0
  56. package/src/chart-types.ts +437 -0
  57. package/src/cli.ts +152 -101
  58. package/src/completion.ts +9 -48
  59. package/src/cycle/layout.ts +19 -28
  60. package/src/cycle/renderer.ts +59 -32
  61. package/src/cycle/types.ts +21 -0
  62. package/src/d3.ts +30 -3
  63. package/src/dgmo-router.ts +98 -73
  64. package/src/echarts.ts +1 -1
  65. package/src/editor/keywords.ts +3 -2
  66. package/src/fonts.ts +3 -2
  67. package/src/gantt/parser.ts +5 -1
  68. package/src/index.ts +37 -3
  69. package/src/infra/parser.ts +3 -3
  70. package/src/internal.ts +20 -0
  71. package/src/journey-map/layout.ts +7 -3
  72. package/src/journey-map/parser.ts +5 -1
  73. package/src/journey-map/renderer.ts +112 -47
  74. package/src/kanban/parser.ts +5 -1
  75. package/src/org/collapse.ts +82 -4
  76. package/src/org/parser.ts +1 -1
  77. package/src/org/renderer.ts +221 -4
  78. package/src/pyramid/parser.ts +172 -0
  79. package/src/pyramid/renderer.ts +684 -0
  80. package/src/pyramid/types.ts +28 -0
  81. package/src/render.ts +2 -8
  82. package/src/sequence/parser.ts +64 -22
  83. package/src/sequence/participant-inference.ts +0 -1
  84. package/src/sequence/renderer.ts +97 -265
  85. package/src/sharing.ts +0 -1
  86. package/src/sitemap/parser.ts +1 -1
  87. package/src/tech-radar/interactive.ts +54 -0
  88. package/src/utils/parsing.ts +1 -0
  89. package/src/utils/tag-groups.ts +35 -5
  90. package/src/wireframe/parser.ts +3 -1
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;
@@ -2085,7 +2086,8 @@ function validateTagValues(entities, tagGroups, pushWarning, suggestFn) {
2085
2086
  }
2086
2087
  }
2087
2088
  }
2088
- function validateTagGroupNames(tagGroups, pushWarning) {
2089
+ function validateTagGroupNames(tagGroups, pushWarning, pushError) {
2090
+ const report = pushError ?? pushWarning;
2089
2091
  for (const group of tagGroups) {
2090
2092
  if (group.name.toLowerCase() === "none") {
2091
2093
  pushWarning(
@@ -2093,6 +2095,18 @@ function validateTagGroupNames(tagGroups, pushWarning) {
2093
2095
  `'none' is a reserved keyword and cannot be used as a tag group name`
2094
2096
  );
2095
2097
  }
2098
+ if (!VALID_TAG_IDENT_RE.test(group.name)) {
2099
+ report(
2100
+ group.lineNumber,
2101
+ `Tag group name "${group.name}" contains invalid characters \u2014 use a single identifier (letters, digits, underscore, hyphen)`
2102
+ );
2103
+ }
2104
+ if (group.alias != null && !VALID_TAG_IDENT_RE.test(group.alias)) {
2105
+ report(
2106
+ group.lineNumber,
2107
+ `Tag group alias "${group.alias}" contains invalid characters \u2014 use a single identifier (letters, digits, underscore, hyphen)`
2108
+ );
2109
+ }
2096
2110
  }
2097
2111
  }
2098
2112
  function injectDefaultTagMetadata(entities, tagGroups, skip) {
@@ -2131,12 +2145,13 @@ function resolveActiveTagGroup(tagGroups, explicitActiveTag, programmaticOverrid
2131
2145
  function matchTagBlockHeading(trimmed) {
2132
2146
  return parseTagDeclaration(trimmed);
2133
2147
  }
2134
- var TAG_BLOCK_NOCOLON_RE;
2148
+ var TAG_BLOCK_NOCOLON_RE, VALID_TAG_IDENT_RE;
2135
2149
  var init_tag_groups = __esm({
2136
2150
  "src/utils/tag-groups.ts"() {
2137
2151
  "use strict";
2138
2152
  init_parsing();
2139
2153
  TAG_BLOCK_NOCOLON_RE = /^tag\s+/i;
2154
+ VALID_TAG_IDENT_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
2140
2155
  }
2141
2156
  });
2142
2157
 
@@ -3072,7 +3087,6 @@ var init_participant_inference = __esm({
3072
3087
  { pattern: /^Admin$/i, type: "actor" },
3073
3088
  { pattern: /^User$/i, type: "actor" },
3074
3089
  { pattern: /^Customer$/i, type: "actor" },
3075
- { pattern: /^Client$/i, type: "actor" },
3076
3090
  { pattern: /^Agent$/i, type: "actor" },
3077
3091
  { pattern: /^Person$/i, type: "actor" },
3078
3092
  { pattern: /^Buyer$/i, type: "actor" },
@@ -3201,7 +3215,7 @@ function isSequenceSection(el) {
3201
3215
  function isSequenceNote(el) {
3202
3216
  return "kind" in el && el.kind === "note";
3203
3217
  }
3204
- function parseNoteLine(trimmed, participants, lastMsgFrom) {
3218
+ function parseNoteLine(trimmed, participants, participantIds, sortedParticipantsCache, lastMsgFrom) {
3205
3219
  const lower = trimmed.toLowerCase();
3206
3220
  if (!lower.startsWith("note")) return null;
3207
3221
  if (trimmed.length > 4 && trimmed[4] !== " ") return null;
@@ -3213,7 +3227,7 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3213
3227
  if (!lastMsgFrom) return { kind: "skip" };
3214
3228
  participantId = lastMsgFrom;
3215
3229
  }
3216
- if (participants.some((p) => p.id === participantId)) {
3230
+ if (participantIds.has(participantId)) {
3217
3231
  return { kind: "multi-head", position, participantId };
3218
3232
  }
3219
3233
  }
@@ -3232,11 +3246,15 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3232
3246
  }
3233
3247
  if (!afterPos) {
3234
3248
  if (!lastMsgFrom) return { kind: "skip" };
3235
- if (!participants.some((p) => p.id === lastMsgFrom))
3236
- return { kind: "skip" };
3249
+ if (!participantIds.has(lastMsgFrom)) return { kind: "skip" };
3237
3250
  return { kind: "multi-head", position, participantId: lastMsgFrom };
3238
3251
  }
3239
- const resolved = resolveParticipantAndText(afterPos, participants);
3252
+ const resolved = resolveParticipantAndText(
3253
+ afterPos,
3254
+ participants,
3255
+ participantIds,
3256
+ sortedParticipantsCache
3257
+ );
3240
3258
  if (resolved) {
3241
3259
  if (resolved.text) {
3242
3260
  return {
@@ -3255,8 +3273,7 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3255
3273
  }
3256
3274
  if (hadOf) return { kind: "skip" };
3257
3275
  if (!lastMsgFrom) return { kind: "skip" };
3258
- if (!participants.some((p) => p.id === lastMsgFrom))
3259
- return { kind: "skip" };
3276
+ if (!participantIds.has(lastMsgFrom)) return { kind: "skip" };
3260
3277
  return {
3261
3278
  kind: "single",
3262
3279
  position,
@@ -3265,8 +3282,7 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3265
3282
  };
3266
3283
  }
3267
3284
  if (!lastMsgFrom) return { kind: "skip" };
3268
- if (!participants.some((p) => p.id === lastMsgFrom))
3269
- return { kind: "skip" };
3285
+ if (!participantIds.has(lastMsgFrom)) return { kind: "skip" };
3270
3286
  return {
3271
3287
  kind: "single",
3272
3288
  position: "right",
@@ -3276,20 +3292,20 @@ function parseNoteLine(trimmed, participants, lastMsgFrom) {
3276
3292
  }
3277
3293
  return null;
3278
3294
  }
3279
- function resolveParticipantAndText(input, participants) {
3295
+ function resolveParticipantAndText(input, participants, participantIds, sortedParticipantsCache) {
3280
3296
  if (input.startsWith('"') || input.startsWith("'")) {
3281
3297
  const quote = input[0];
3282
3298
  const endQuote = input.indexOf(quote, 1);
3283
3299
  if (endQuote > 0) {
3284
3300
  const name = input.substring(1, endQuote);
3285
- if (participants.some((p) => p.id === name)) {
3301
+ if (participantIds.has(name)) {
3286
3302
  const text = input.substring(endQuote + 1).trim();
3287
3303
  return { participantId: name, text };
3288
3304
  }
3289
3305
  }
3290
3306
  return null;
3291
3307
  }
3292
- const sorted = [...participants].sort((a, b) => b.id.length - a.id.length);
3308
+ const sorted = sortedParticipantsCache;
3293
3309
  for (const p of sorted) {
3294
3310
  if (input.startsWith(p.id)) {
3295
3311
  const remaining = input.substring(p.id.length);
@@ -3350,6 +3366,18 @@ function parseSequenceDgmo(content) {
3350
3366
  break;
3351
3367
  }
3352
3368
  let activeGroup = null;
3369
+ const participantIds = /* @__PURE__ */ new Set();
3370
+ let sortedParticipantsCache = [];
3371
+ let sortedCacheDirty = true;
3372
+ const getSortedParticipants = () => {
3373
+ if (sortedCacheDirty) {
3374
+ sortedParticipantsCache = [...result.participants].sort(
3375
+ (a, b) => b.id.length - a.id.length
3376
+ );
3377
+ sortedCacheDirty = false;
3378
+ }
3379
+ return sortedParticipantsCache;
3380
+ };
3353
3381
  const participantGroupMap = /* @__PURE__ */ new Map();
3354
3382
  let currentTagGroup = null;
3355
3383
  const aliasMap = /* @__PURE__ */ new Map();
@@ -3595,7 +3623,9 @@ function parseSequenceDgmo(content) {
3595
3623
  const posMatch = remainder.match(/\bposition\s+(-?\d+)/i);
3596
3624
  const alias = akaMatch ? akaMatch[1].trim() : null;
3597
3625
  const position = posMatch ? parseInt(posMatch[1], 10) : void 0;
3598
- if (!result.participants.some((p) => p.id === id)) {
3626
+ if (!participantIds.has(id)) {
3627
+ participantIds.add(id);
3628
+ sortedCacheDirty = true;
3599
3629
  result.participants.push({
3600
3630
  id,
3601
3631
  label: alias || id,
@@ -3625,7 +3655,9 @@ function parseSequenceDgmo(content) {
3625
3655
  contentStarted = true;
3626
3656
  const id = posOnlyMatch[1];
3627
3657
  const position = parseInt(posOnlyMatch[2], 10);
3628
- if (!result.participants.some((p) => p.id === id)) {
3658
+ if (!participantIds.has(id)) {
3659
+ participantIds.add(id);
3660
+ sortedCacheDirty = true;
3629
3661
  result.participants.push({
3630
3662
  id,
3631
3663
  label: id,
@@ -3659,7 +3691,9 @@ function parseSequenceDgmo(content) {
3659
3691
  `'${id}(${color})' syntax is no longer supported \u2014 use 'tag:' groups for coloring`
3660
3692
  );
3661
3693
  contentStarted = true;
3662
- if (!result.participants.some((p) => p.id === id)) {
3694
+ if (!participantIds.has(id)) {
3695
+ participantIds.add(id);
3696
+ sortedCacheDirty = true;
3663
3697
  result.participants.push({
3664
3698
  id,
3665
3699
  label: id,
@@ -3688,7 +3722,8 @@ function parseSequenceDgmo(content) {
3688
3722
  if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !contentStarted || bareMeta)) {
3689
3723
  contentStarted = true;
3690
3724
  const id = bareCore;
3691
- if (!result.participants.some((p) => p.id === id)) {
3725
+ if (!participantIds.has(id)) {
3726
+ participantIds.add(id);
3692
3727
  result.participants.push({
3693
3728
  id,
3694
3729
  label: id,
@@ -3750,7 +3785,9 @@ function parseSequenceDgmo(content) {
3750
3785
  };
3751
3786
  result.messages.push(msg);
3752
3787
  currentContainer().push(msg);
3753
- if (!result.participants.some((p) => p.id === from)) {
3788
+ if (!participantIds.has(from)) {
3789
+ participantIds.add(from);
3790
+ sortedCacheDirty = true;
3754
3791
  result.participants.push({
3755
3792
  id: from,
3756
3793
  label: from,
@@ -3758,7 +3795,9 @@ function parseSequenceDgmo(content) {
3758
3795
  lineNumber
3759
3796
  });
3760
3797
  }
3761
- if (!result.participants.some((p) => p.id === to)) {
3798
+ if (!participantIds.has(to)) {
3799
+ participantIds.add(to);
3800
+ sortedCacheDirty = true;
3762
3801
  result.participants.push({
3763
3802
  id: to,
3764
3803
  label: to,
@@ -3825,7 +3864,9 @@ function parseSequenceDgmo(content) {
3825
3864
  };
3826
3865
  result.messages.push(msg);
3827
3866
  currentContainer().push(msg);
3828
- if (!result.participants.some((p) => p.id === from)) {
3867
+ if (!participantIds.has(from)) {
3868
+ participantIds.add(from);
3869
+ sortedCacheDirty = true;
3829
3870
  result.participants.push({
3830
3871
  id: from,
3831
3872
  label: from,
@@ -3833,7 +3874,9 @@ function parseSequenceDgmo(content) {
3833
3874
  lineNumber
3834
3875
  });
3835
3876
  }
3836
- if (!result.participants.some((p) => p.id === to)) {
3877
+ if (!participantIds.has(to)) {
3878
+ participantIds.add(to);
3879
+ sortedCacheDirty = true;
3837
3880
  result.participants.push({
3838
3881
  id: to,
3839
3882
  label: to,
@@ -3935,6 +3978,8 @@ function parseSequenceDgmo(content) {
3935
3978
  const noteParsed = parseNoteLine(
3936
3979
  trimmed,
3937
3980
  result.participants,
3981
+ participantIds,
3982
+ getSortedParticipants(),
3938
3983
  lastMsgFrom
3939
3984
  );
3940
3985
  if (noteParsed) {
@@ -4039,7 +4084,7 @@ function parseSequenceDgmo(content) {
4039
4084
  entities.push({ metadata: g.metadata, lineNumber: g.lineNumber });
4040
4085
  }
4041
4086
  validateTagValues(entities, result.tagGroups, pushWarning, suggest);
4042
- validateTagGroupNames(result.tagGroups, pushWarning);
4087
+ validateTagGroupNames(result.tagGroups, pushWarning, pushError);
4043
4088
  }
4044
4089
  return result;
4045
4090
  }
@@ -4062,7 +4107,7 @@ var init_parser = __esm({
4062
4107
  init_parsing();
4063
4108
  init_tag_groups();
4064
4109
  KNOWN_SEQ_OPTIONS = /* @__PURE__ */ new Set(["active-tag"]);
4065
- KNOWN_SEQ_BOOLEANS = /* @__PURE__ */ new Set(["activations", "collapse-notes"]);
4110
+ KNOWN_SEQ_BOOLEANS = /* @__PURE__ */ new Set(["activations"]);
4066
4111
  VALID_PARTICIPANT_TYPES = /* @__PURE__ */ new Set([
4067
4112
  "service",
4068
4113
  "database",
@@ -7301,7 +7346,7 @@ function buildFunnelOption(parsed, textColor, colors, bg, titleConfig) {
7301
7346
  bottom: 20,
7302
7347
  width: "60%",
7303
7348
  sort: "descending",
7304
- gap: 2,
7349
+ gap: 0,
7305
7350
  minSize: "8%"
7306
7351
  };
7307
7352
  return {
@@ -8420,7 +8465,7 @@ function parseOrg(content, palette) {
8420
8465
  };
8421
8466
  collectAll(result.roots);
8422
8467
  validateTagValues(allNodes, result.tagGroups, pushWarning, suggest);
8423
- validateTagGroupNames(result.tagGroups, pushWarning);
8468
+ validateTagGroupNames(result.tagGroups, pushWarning, pushError);
8424
8469
  }
8425
8470
  if (result.roots.length === 0 && result.tagGroups.length === 0 && !result.error) {
8426
8471
  const diag = makeDgmoError(1, "No nodes found in org chart");
@@ -8753,7 +8798,11 @@ function parseKanban(content, palette) {
8753
8798
  if (result.columns.length === 0 && !result.error) {
8754
8799
  return fail(1, "No columns found. Use [Column Name] to define columns");
8755
8800
  }
8756
- validateTagGroupNames(result.tagGroups, warn);
8801
+ validateTagGroupNames(result.tagGroups, warn, (line11, msg) => {
8802
+ const diag = makeDgmoError(line11, msg);
8803
+ result.diagnostics.push(diag);
8804
+ if (!result.error) result.error = formatDgmoError(diag);
8805
+ });
8757
8806
  return result;
8758
8807
  }
8759
8808
  function parseCardLine(trimmed, lineNumber, counter, aliasMap, _palette, _diagnostics) {
@@ -9879,7 +9928,7 @@ function parseSitemap(content, palette) {
9879
9928
  };
9880
9929
  collectAll(result.roots);
9881
9930
  validateTagValues(allNodes, result.tagGroups, pushWarning, suggest);
9882
- validateTagGroupNames(result.tagGroups, pushWarning);
9931
+ validateTagGroupNames(result.tagGroups, pushWarning, pushError);
9883
9932
  }
9884
9933
  if (result.roots.length === 0 && result.tagGroups.length === 0 && !result.error) {
9885
9934
  const diag = makeDgmoError(1, "No pages found in sitemap");
@@ -10013,8 +10062,8 @@ function extractPipeMetadata(rest) {
10013
10062
  const tags = {};
10014
10063
  let clean = rest;
10015
10064
  let match;
10016
- const re = new RegExp(PIPE_META_RE.source, "g");
10017
- while ((match = re.exec(rest)) !== null) {
10065
+ PIPE_META_RE.lastIndex = 0;
10066
+ while ((match = PIPE_META_RE.exec(rest)) !== null) {
10018
10067
  tags[match[1].trim()] = match[2].trim();
10019
10068
  clean = clean.replace(match[0], "");
10020
10069
  }
@@ -10516,7 +10565,7 @@ function parseInfra(content) {
10516
10565
  }
10517
10566
  }
10518
10567
  }
10519
- validateTagGroupNames(result.tagGroups, warn);
10568
+ validateTagGroupNames(result.tagGroups, warn, setError);
10520
10569
  return result;
10521
10570
  }
10522
10571
  function extractSymbols4(docText) {
@@ -11457,7 +11506,11 @@ function parseGantt(content, palette) {
11457
11506
  warn(0, "sort tag has no effect \u2014 no tag groups defined.");
11458
11507
  result.options.sort = "default";
11459
11508
  }
11460
- validateTagGroupNames(result.tagGroups, warn);
11509
+ validateTagGroupNames(result.tagGroups, warn, (line11, msg) => {
11510
+ const diag = makeDgmoError(line11, msg);
11511
+ diagnostics.push(diag);
11512
+ if (!result.error) result.error = formatDgmoError(diag);
11513
+ });
11461
11514
  const hasSprintOption = result.options.sprintLength !== null || result.options.sprintNumber !== null || result.options.sprintStart !== null;
11462
11515
  const hasSprintUnit = hasSprintDurationUnit(result.nodes);
11463
11516
  if (hasSprintOption) {
@@ -11721,6 +11774,7 @@ function parseBoxesAndLines(content) {
11721
11774
  const groupLabels = /* @__PURE__ */ new Set();
11722
11775
  let lastNodeLabel = null;
11723
11776
  let lastSourceIsGroup = false;
11777
+ let lastNodeIndent = 0;
11724
11778
  let descState = null;
11725
11779
  function flushDescription() {
11726
11780
  if (descState && descState.lines.length > 0) {
@@ -12018,7 +12072,8 @@ function parseBoxesAndLines(content) {
12018
12072
  if (trimmed.startsWith("->") || /^-[^>].*->/.test(trimmed)) {
12019
12073
  const gs2 = currentGroupState();
12020
12074
  const inGroup = gs2 && indent > gs2.indent;
12021
- if (inGroup) {
12075
+ const indentedUnderNode = lastNodeLabel && !lastSourceIsGroup && indent > lastNodeIndent;
12076
+ if (inGroup && !indentedUnderNode) {
12022
12077
  const sourcePrefix = `[${gs2.group.label}]`;
12023
12078
  edgeText = `${sourcePrefix} ${trimmed}`;
12024
12079
  } else if (lastNodeLabel) {
@@ -12058,6 +12113,7 @@ function parseBoxesAndLines(content) {
12058
12113
  }
12059
12114
  lastNodeLabel = node.label;
12060
12115
  lastSourceIsGroup = false;
12116
+ lastNodeIndent = indent;
12061
12117
  const gs = currentGroupState();
12062
12118
  const isGroupChild = gs && indent > gs.indent;
12063
12119
  if (nodeLabels.has(node.label)) {
@@ -12126,7 +12182,11 @@ function parseBoxesAndLines(content) {
12126
12182
  if (result.tagGroups.length > 0) {
12127
12183
  injectDefaultTagMetadata(result.nodes, result.tagGroups);
12128
12184
  validateTagValues(result.nodes, result.tagGroups, pushWarning, suggest);
12129
- validateTagGroupNames(result.tagGroups, pushWarning);
12185
+ validateTagGroupNames(result.tagGroups, pushWarning, (line11, msg) => {
12186
+ const diag = makeDgmoError(line11, msg);
12187
+ result.diagnostics.push(diag);
12188
+ if (!result.error) result.error = diag.message;
12189
+ });
12130
12190
  }
12131
12191
  return result;
12132
12192
  }
@@ -13102,7 +13162,9 @@ function parseWireframe(content) {
13102
13162
  }
13103
13163
  }
13104
13164
  }
13105
- validateTagGroupNames(tagGroups, pushWarning);
13165
+ validateTagGroupNames(tagGroups, pushWarning, (line11, msg) => {
13166
+ diagnostics.push(makeDgmoError(line11, msg));
13167
+ });
13106
13168
  const error = diagnostics.find((d) => d.severity === "error") ? formatDgmoError(diagnostics.find((d) => d.severity === "error")) : null;
13107
13169
  return {
13108
13170
  title,
@@ -14030,7 +14092,11 @@ function parseJourneyMap(content, palette) {
14030
14092
  if (result.phases.length === 0 && result.steps.length === 0 && !result.error) {
14031
14093
  return fail(1, "No phases or steps found");
14032
14094
  }
14033
- validateTagGroupNames(result.tagGroups, warn);
14095
+ validateTagGroupNames(result.tagGroups, warn, (line11, msg) => {
14096
+ const diag = makeDgmoError(line11, msg);
14097
+ result.diagnostics.push(diag);
14098
+ if (!result.error) result.error = formatDgmoError(diag);
14099
+ });
14034
14100
  return result;
14035
14101
  }
14036
14102
  function parseStepLine(trimmed, lineNumber, counter, aliasMap, warn) {
@@ -14142,12 +14208,543 @@ var init_parser15 = __esm({
14142
14208
  }
14143
14209
  });
14144
14210
 
14211
+ // src/pyramid/parser.ts
14212
+ var parser_exports16 = {};
14213
+ __export(parser_exports16, {
14214
+ parsePyramid: () => parsePyramid
14215
+ });
14216
+ function parsePyramid(content) {
14217
+ const result = {
14218
+ type: "pyramid",
14219
+ title: "",
14220
+ titleLineNumber: 0,
14221
+ layers: [],
14222
+ inverted: false,
14223
+ options: {},
14224
+ diagnostics: [],
14225
+ error: null
14226
+ };
14227
+ const lines = content.split("\n");
14228
+ let headerParsed = false;
14229
+ let currentLayer = null;
14230
+ const fail = (line11, message) => {
14231
+ const diag = makeDgmoError(line11, message);
14232
+ result.diagnostics.push(diag);
14233
+ result.error = formatDgmoError(diag);
14234
+ return result;
14235
+ };
14236
+ const warn = (line11, message, severity = "warning") => {
14237
+ result.diagnostics.push(makeDgmoError(line11, message, severity));
14238
+ };
14239
+ const flushLayer = () => {
14240
+ if (currentLayer) {
14241
+ result.layers.push(currentLayer);
14242
+ currentLayer = null;
14243
+ }
14244
+ };
14245
+ for (let i = 0; i < lines.length; i++) {
14246
+ const lineNum = i + 1;
14247
+ const raw = lines[i];
14248
+ const trimmed = raw.trim();
14249
+ if (!trimmed || trimmed.startsWith("//")) continue;
14250
+ const indent = measureIndent(raw);
14251
+ if (!headerParsed) {
14252
+ const firstLineResult = parseFirstLine(trimmed);
14253
+ if (firstLineResult && firstLineResult.chartType === "pyramid") {
14254
+ result.title = firstLineResult.title ?? "";
14255
+ result.titleLineNumber = lineNum;
14256
+ headerParsed = true;
14257
+ continue;
14258
+ }
14259
+ return fail(lineNum, 'Expected "pyramid [Title]" as the first line.');
14260
+ }
14261
+ if (indent === 0 && trimmed.toLowerCase() === "inverted") {
14262
+ result.inverted = true;
14263
+ continue;
14264
+ }
14265
+ if (indent === 0) {
14266
+ flushLayer();
14267
+ const pipeIdx = trimmed.indexOf("|");
14268
+ let label;
14269
+ const description = [];
14270
+ let color;
14271
+ let restMeta = {};
14272
+ if (pipeIdx < 0) {
14273
+ label = trimmed;
14274
+ } else {
14275
+ label = trimmed.substring(0, pipeIdx).trim();
14276
+ const after = trimmed.substring(pipeIdx + 1).trim();
14277
+ if (!after) {
14278
+ } else if (KEY_VALUE_PREFIX_RE.test(after)) {
14279
+ const metadata = parsePipeMetadata([label, after]);
14280
+ color = metadata["color"];
14281
+ const descFromPipe = metadata["description"];
14282
+ if (descFromPipe) description.push(descFromPipe);
14283
+ restMeta = { ...metadata };
14284
+ delete restMeta["color"];
14285
+ delete restMeta["description"];
14286
+ } else {
14287
+ description.push(after);
14288
+ }
14289
+ }
14290
+ if (!label) {
14291
+ warn(lineNum, "Empty layer label.");
14292
+ continue;
14293
+ }
14294
+ currentLayer = {
14295
+ label,
14296
+ lineNumber: lineNum,
14297
+ color,
14298
+ description,
14299
+ metadata: restMeta
14300
+ };
14301
+ continue;
14302
+ }
14303
+ if (!currentLayer) {
14304
+ warn(lineNum, `Unexpected indented line: "${trimmed}".`);
14305
+ continue;
14306
+ }
14307
+ const descLine = trimmed.startsWith("- ") ? `\u2022 ${trimmed.substring(2)}` : trimmed;
14308
+ currentLayer.description.push(descLine);
14309
+ }
14310
+ flushLayer();
14311
+ if (result.layers.length < 2) {
14312
+ return fail(
14313
+ result.titleLineNumber || 1,
14314
+ "pyramid requires at least 2 layers."
14315
+ );
14316
+ }
14317
+ return result;
14318
+ }
14319
+ var KEY_VALUE_PREFIX_RE;
14320
+ var init_parser16 = __esm({
14321
+ "src/pyramid/parser.ts"() {
14322
+ "use strict";
14323
+ init_diagnostics();
14324
+ init_parsing();
14325
+ KEY_VALUE_PREFIX_RE = /^\s*[A-Za-z][A-Za-z0-9_-]*\s*:/;
14326
+ }
14327
+ });
14328
+
14329
+ // src/chart-types.ts
14330
+ var chartTypes;
14331
+ var init_chart_types = __esm({
14332
+ "src/chart-types.ts"() {
14333
+ "use strict";
14334
+ chartTypes = [
14335
+ // ── Tier 1 — Narrative / architecture diagrams ────────────
14336
+ {
14337
+ id: "journey-map",
14338
+ description: "User experience flow with emotion scores, phases, and annotations",
14339
+ triggers: [
14340
+ "user journey",
14341
+ "customer journey",
14342
+ "customer experience",
14343
+ "customer goes through",
14344
+ "user goes through",
14345
+ "customer path",
14346
+ "customer touchpoints",
14347
+ "cx flow",
14348
+ "ux journey",
14349
+ "onboarding flow",
14350
+ "persona journey",
14351
+ "empathy map",
14352
+ "service blueprint"
14353
+ ]
14354
+ },
14355
+ {
14356
+ id: "c4",
14357
+ description: "System architecture (context, container, component, deployment)",
14358
+ triggers: [
14359
+ "c4 diagram",
14360
+ "system context",
14361
+ "container diagram",
14362
+ "component diagram",
14363
+ "architecture overview",
14364
+ "software architecture"
14365
+ ]
14366
+ },
14367
+ {
14368
+ id: "er",
14369
+ description: "Database schemas and relationships",
14370
+ triggers: [
14371
+ "database schema",
14372
+ "er diagram",
14373
+ "entity relationship",
14374
+ "data model",
14375
+ "tables and relationships",
14376
+ "foreign keys"
14377
+ ]
14378
+ },
14379
+ {
14380
+ id: "class",
14381
+ description: "UML class hierarchies",
14382
+ triggers: [
14383
+ "uml class",
14384
+ "class hierarchy",
14385
+ "class diagram",
14386
+ "inheritance tree",
14387
+ "oop structure"
14388
+ ]
14389
+ },
14390
+ {
14391
+ id: "sequence",
14392
+ description: "Message / interaction flows",
14393
+ triggers: [
14394
+ "sequence diagram",
14395
+ "message flow",
14396
+ "api call flow",
14397
+ "request lifecycle",
14398
+ "interaction diagram",
14399
+ "call sequence"
14400
+ ],
14401
+ fallback: true
14402
+ },
14403
+ {
14404
+ id: "state",
14405
+ description: "State machine / lifecycle transitions",
14406
+ triggers: [
14407
+ "state diagram",
14408
+ "state machine",
14409
+ "state transitions",
14410
+ "lifecycle diagram",
14411
+ "status transitions"
14412
+ ]
14413
+ },
14414
+ {
14415
+ id: "infra",
14416
+ description: "Infrastructure traffic flow with RPS computation",
14417
+ triggers: [
14418
+ "infrastructure diagram",
14419
+ "traffic flow",
14420
+ "request path",
14421
+ "rps",
14422
+ "capacity planning",
14423
+ "network topology"
14424
+ ]
14425
+ },
14426
+ {
14427
+ id: "gantt",
14428
+ description: "Project scheduling with task dependencies and milestones",
14429
+ triggers: [
14430
+ "gantt chart",
14431
+ "project schedule",
14432
+ "sprint plan",
14433
+ "project timeline",
14434
+ "task dependencies",
14435
+ "project milestones"
14436
+ ]
14437
+ },
14438
+ // ── Tier 2 — Specialized structural diagrams ──────────────
14439
+ {
14440
+ id: "timeline",
14441
+ description: "Events, eras, and date ranges",
14442
+ triggers: [
14443
+ "event timeline",
14444
+ "historical timeline",
14445
+ "era chart",
14446
+ "period chart",
14447
+ "project history"
14448
+ ]
14449
+ },
14450
+ {
14451
+ id: "org",
14452
+ description: "Reporting hierarchy",
14453
+ triggers: [
14454
+ "org chart",
14455
+ "organization chart",
14456
+ "reporting structure",
14457
+ "hierarchy chart",
14458
+ "team structure"
14459
+ ]
14460
+ },
14461
+ {
14462
+ id: "sitemap",
14463
+ description: "Site / app navigation structure",
14464
+ triggers: [
14465
+ "sitemap",
14466
+ "site structure",
14467
+ "page hierarchy",
14468
+ "navigation structure",
14469
+ "app navigation"
14470
+ ]
14471
+ },
14472
+ {
14473
+ id: "kanban",
14474
+ description: "Task board columns",
14475
+ triggers: [
14476
+ "kanban board",
14477
+ "task board",
14478
+ "workflow columns",
14479
+ "todo doing done",
14480
+ "agile board"
14481
+ ]
14482
+ },
14483
+ {
14484
+ id: "tech-radar",
14485
+ description: "Technology adoption quadrants (adopt/trial/assess/hold)",
14486
+ triggers: [
14487
+ "tech radar",
14488
+ "technology radar",
14489
+ "tech adoption",
14490
+ "adopt trial assess hold",
14491
+ "tech choices"
14492
+ ]
14493
+ },
14494
+ {
14495
+ id: "mindmap",
14496
+ description: "Radial hierarchy of ideas branching from a central topic",
14497
+ triggers: [
14498
+ "mind map",
14499
+ "brainstorm diagram",
14500
+ "concept map",
14501
+ "idea tree",
14502
+ "radial ideas"
14503
+ ]
14504
+ },
14505
+ {
14506
+ id: "wireframe",
14507
+ description: "Low-fidelity UI layout with panels, controls, and annotations",
14508
+ triggers: [
14509
+ "wireframe",
14510
+ "ui mockup",
14511
+ "screen layout",
14512
+ "page layout",
14513
+ "low-fidelity mockup"
14514
+ ]
14515
+ },
14516
+ {
14517
+ id: "cycle",
14518
+ description: "Cyclical process visualization (PDCA, OODA, DevOps loops)",
14519
+ triggers: [
14520
+ "pdca cycle",
14521
+ "ooda loop",
14522
+ "feedback loop",
14523
+ "cyclical process",
14524
+ "devops loop",
14525
+ "continuous loop"
14526
+ ]
14527
+ },
14528
+ {
14529
+ id: "pyramid",
14530
+ description: "Stacked hierarchy of layers with descriptions (Maslow, DIKW)",
14531
+ triggers: [
14532
+ "pyramid diagram",
14533
+ "layered hierarchy",
14534
+ "maslow hierarchy",
14535
+ "dikw pyramid",
14536
+ "layered model"
14537
+ ]
14538
+ },
14539
+ // ── Tier 3 — Specialized analytical charts ────────────────
14540
+ {
14541
+ id: "quadrant",
14542
+ description: "2x2 positioning matrix",
14543
+ triggers: [
14544
+ "2x2 matrix",
14545
+ "priority matrix",
14546
+ "quadrant chart",
14547
+ "impact effort matrix",
14548
+ "positioning matrix"
14549
+ ]
14550
+ },
14551
+ {
14552
+ id: "venn",
14553
+ description: "Set overlaps",
14554
+ triggers: [
14555
+ "venn diagram",
14556
+ "set overlap",
14557
+ "intersection of",
14558
+ "shared traits",
14559
+ "overlapping circles"
14560
+ ]
14561
+ },
14562
+ {
14563
+ id: "funnel",
14564
+ description: "Conversion pipeline",
14565
+ triggers: [
14566
+ "conversion funnel",
14567
+ "sales funnel",
14568
+ "user funnel",
14569
+ "pipeline stages",
14570
+ "drop-off funnel"
14571
+ ]
14572
+ },
14573
+ {
14574
+ id: "slope",
14575
+ description: "Change between two periods",
14576
+ triggers: [
14577
+ "slope chart",
14578
+ "before and after",
14579
+ "two-period change",
14580
+ "delta chart",
14581
+ "shift comparison"
14582
+ ]
14583
+ },
14584
+ {
14585
+ id: "sankey",
14586
+ description: "Flow / allocation visualization",
14587
+ triggers: [
14588
+ "sankey diagram",
14589
+ "flow allocation",
14590
+ "budget flow",
14591
+ "energy flow",
14592
+ "traffic allocation"
14593
+ ]
14594
+ },
14595
+ {
14596
+ id: "chord",
14597
+ description: "Circular flow relationships",
14598
+ triggers: [
14599
+ "chord diagram",
14600
+ "circular flow",
14601
+ "relationship wheel",
14602
+ "team connections"
14603
+ ]
14604
+ },
14605
+ {
14606
+ id: "arc",
14607
+ description: "Network relationships",
14608
+ triggers: [
14609
+ "arc diagram",
14610
+ "relationship chart",
14611
+ "connection arcs",
14612
+ "network arcs"
14613
+ ]
14614
+ },
14615
+ {
14616
+ id: "wordcloud",
14617
+ description: "Term frequency visualization",
14618
+ triggers: [
14619
+ "word cloud",
14620
+ "tag cloud",
14621
+ "term frequency",
14622
+ "keyword frequency"
14623
+ ]
14624
+ },
14625
+ {
14626
+ id: "heatmap",
14627
+ description: "Matrix intensity visualization",
14628
+ triggers: [
14629
+ "heatmap",
14630
+ "intensity matrix",
14631
+ "activity heatmap",
14632
+ "correlation matrix"
14633
+ ]
14634
+ },
14635
+ {
14636
+ id: "function",
14637
+ description: "Mathematical expressions",
14638
+ triggers: [
14639
+ "function plot",
14640
+ "mathematical plot",
14641
+ "equation chart",
14642
+ "graph y=f(x)"
14643
+ ]
14644
+ },
14645
+ // ── Tier 4 — General-purpose data charts ──────────────────
14646
+ {
14647
+ id: "bar",
14648
+ description: "Categorical comparisons",
14649
+ triggers: ["bar chart", "categorical comparison", "bar graph"],
14650
+ fallback: true
14651
+ },
14652
+ {
14653
+ id: "line",
14654
+ description: "Trends over time",
14655
+ triggers: ["line chart", "trend over time", "time series"],
14656
+ fallback: true
14657
+ },
14658
+ {
14659
+ id: "multi-line",
14660
+ description: "Multiple series trends over time",
14661
+ triggers: [
14662
+ "multi-line chart",
14663
+ "multiple trends",
14664
+ "multiple series over time"
14665
+ ]
14666
+ },
14667
+ {
14668
+ id: "area",
14669
+ description: "Filled line chart",
14670
+ triggers: ["area chart", "filled line", "cumulative trend"]
14671
+ },
14672
+ {
14673
+ id: "pie",
14674
+ description: "Part-to-whole proportions",
14675
+ triggers: ["pie chart", "part to whole", "percentage breakdown"]
14676
+ },
14677
+ {
14678
+ id: "doughnut",
14679
+ description: "Ring-style pie chart",
14680
+ triggers: ["doughnut chart", "donut chart", "ring chart"]
14681
+ },
14682
+ {
14683
+ id: "radar",
14684
+ description: "Multi-dimensional metrics",
14685
+ triggers: ["radar chart", "spider chart", "multi-dimensional metrics"]
14686
+ },
14687
+ {
14688
+ id: "polar-area",
14689
+ description: "Radial bar chart",
14690
+ triggers: ["polar area", "radial bar chart"]
14691
+ },
14692
+ {
14693
+ id: "bar-stacked",
14694
+ description: "Multi-series categorical",
14695
+ triggers: [
14696
+ "stacked bar",
14697
+ "stacked bar chart",
14698
+ "multi-series bar",
14699
+ "composite bar"
14700
+ ]
14701
+ },
14702
+ {
14703
+ id: "scatter",
14704
+ description: "2D data points or bubble chart",
14705
+ triggers: [
14706
+ "scatter plot",
14707
+ "correlation plot",
14708
+ "bubble chart",
14709
+ "2d data points"
14710
+ ]
14711
+ },
14712
+ // ── Tier 5 — Generic catch-alls (listed last on purpose) ──
14713
+ {
14714
+ id: "flowchart",
14715
+ description: "Decision trees and process flows",
14716
+ triggers: [
14717
+ "flowchart",
14718
+ "decision tree",
14719
+ "if-then diagram",
14720
+ "process flow with decisions"
14721
+ ],
14722
+ fallback: true
14723
+ },
14724
+ {
14725
+ id: "boxes-and-lines",
14726
+ description: "General-purpose node-edge diagrams with groups and tags",
14727
+ triggers: [
14728
+ "boxes and lines",
14729
+ "nodes and edges",
14730
+ "generic diagram",
14731
+ "general-purpose network"
14732
+ ],
14733
+ fallback: true
14734
+ }
14735
+ ];
14736
+ }
14737
+ });
14738
+
14145
14739
  // src/dgmo-router.ts
14146
14740
  var dgmo_router_exports = {};
14147
14741
  __export(dgmo_router_exports, {
14742
+ CHART_TYPE_DESCRIPTIONS: () => CHART_TYPE_DESCRIPTIONS,
14743
+ chartTypeParsers: () => chartTypeParsers,
14148
14744
  getAllChartTypes: () => getAllChartTypes,
14149
14745
  getRenderCategory: () => getRenderCategory,
14150
14746
  isExtendedChartType: () => isExtendedChartType,
14747
+ knownChartTypeIds: () => knownChartTypeIds,
14151
14748
  looksLikeC4: () => looksLikeC4,
14152
14749
  looksLikeGantt: () => looksLikeGantt,
14153
14750
  parseDgmo: () => parseDgmo,
@@ -14206,7 +14803,7 @@ function isExtendedChartType(chartType) {
14206
14803
  return EXTENDED_CHART_TYPES.has(chartType.toLowerCase());
14207
14804
  }
14208
14805
  function getAllChartTypes() {
14209
- return [...DATA_CHART_TYPES, ...VISUALIZATION_TYPES, ...DIAGRAM_TYPES];
14806
+ return chartTypes.map((c) => c.id);
14210
14807
  }
14211
14808
  function parseDgmo(content) {
14212
14809
  const chartType = parseDgmoChartType(content);
@@ -14214,32 +14811,25 @@ function parseDgmo(content) {
14214
14811
  const colonDiag = detectColonChartType(content);
14215
14812
  if (colonDiag) {
14216
14813
  const fallback = parseVisualization(content).diagnostics;
14217
- return { diagnostics: [colonDiag, ...fallback] };
14814
+ return { diagnostics: [colonDiag, ...fallback], chartType: null };
14218
14815
  }
14219
- return { diagnostics: parseVisualization(content).diagnostics };
14220
- }
14221
- const directParser = PARSE_DISPATCH.get(chartType);
14222
- if (directParser) {
14223
- const result2 = directParser(content);
14224
14816
  return {
14225
- diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)]
14817
+ diagnostics: parseVisualization(content).diagnostics,
14818
+ chartType: null
14226
14819
  };
14227
14820
  }
14228
- if (STANDARD_CHART_TYPES2.has(chartType)) {
14229
- const result2 = parseChart(content);
14821
+ const parser = PARSER_BY_ID.get(chartType);
14822
+ if (parser) {
14823
+ const result2 = parser(content);
14230
14824
  return {
14231
- diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)]
14232
- };
14233
- }
14234
- if (ECHART_TYPES.has(chartType)) {
14235
- const result2 = parseExtendedChart(content);
14236
- return {
14237
- diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)]
14825
+ diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)],
14826
+ chartType
14238
14827
  };
14239
14828
  }
14240
14829
  const result = parseVisualization(content);
14241
14830
  return {
14242
- diagnostics: [...result.diagnostics, ...detectEmptyContent(content)]
14831
+ diagnostics: [...result.diagnostics, ...detectEmptyContent(content)],
14832
+ chartType
14243
14833
  };
14244
14834
  }
14245
14835
  function detectColonChartType(content) {
@@ -14282,7 +14872,7 @@ function detectEmptyContent(content) {
14282
14872
  }
14283
14873
  return [];
14284
14874
  }
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;
14875
+ var GANTT_DURATION_RE, GANTT_DATE_RE, C4_TYPE_RE, DATA_CHART_TYPES, VISUALIZATION_TYPES, DIAGRAM_TYPES, EXTENDED_CHART_TYPES, CHART_TYPE_DESCRIPTIONS, chartTypeParsers, knownChartTypeIds, PARSER_BY_ID, ALL_KNOWN_TYPES;
14286
14876
  var init_dgmo_router = __esm({
14287
14877
  "src/dgmo-router.ts"() {
14288
14878
  "use strict";
@@ -14306,8 +14896,10 @@ var init_dgmo_router = __esm({
14306
14896
  init_parser13();
14307
14897
  init_parser14();
14308
14898
  init_parser15();
14899
+ init_parser16();
14309
14900
  init_parsing();
14310
14901
  init_diagnostics();
14902
+ init_chart_types();
14311
14903
  GANTT_DURATION_RE = /^\d+(?:\.\d+)?(?:min|bd|d|w|m|q|y|h)(?:\?)?\s+/;
14312
14904
  GANTT_DATE_RE = /^\d{4}-\d{2}-\d{2}(?:\s\d{2}:\d{2})?\s+/;
14313
14905
  C4_TYPE_RE = /\bis\s+an?\s+(person|system|container|component)\b/i;
@@ -14336,7 +14928,8 @@ var init_dgmo_router = __esm({
14336
14928
  "venn",
14337
14929
  "quadrant",
14338
14930
  "tech-radar",
14339
- "cycle"
14931
+ "cycle",
14932
+ "pyramid"
14340
14933
  ]);
14341
14934
  DIAGRAM_TYPES = /* @__PURE__ */ new Set([
14342
14935
  "sequence",
@@ -14363,49 +14956,57 @@ var init_dgmo_router = __esm({
14363
14956
  "heatmap",
14364
14957
  "funnel"
14365
14958
  ]);
14366
- STANDARD_CHART_TYPES2 = /* @__PURE__ */ new Set([
14367
- "bar",
14368
- "line",
14369
- "multi-line",
14370
- "area",
14371
- "pie",
14372
- "doughnut",
14373
- "radar",
14374
- "polar-area",
14375
- "bar-stacked"
14376
- ]);
14377
- ECHART_TYPES = /* @__PURE__ */ new Set([
14378
- "scatter",
14379
- "sankey",
14380
- "chord",
14381
- "function",
14382
- "heatmap",
14383
- "funnel"
14384
- ]);
14385
- PARSE_DISPATCH = /* @__PURE__ */ new Map([
14386
- ["sequence", (c) => parseSequenceDgmo(c)],
14387
- ["flowchart", (c) => parseFlowchart(c)],
14388
- ["class", (c) => parseClassDiagram(c)],
14389
- ["er", (c) => parseERDiagram(c)],
14390
- ["org", (c) => parseOrg(c)],
14391
- ["kanban", (c) => parseKanban(c)],
14392
- ["c4", (c) => parseC4(c)],
14393
- ["state", (c) => parseState(c)],
14394
- ["sitemap", (c) => parseSitemap(c)],
14395
- ["infra", (c) => parseInfra(c)],
14396
- ["gantt", (c) => parseGantt(c)],
14397
- ["boxes-and-lines", (c) => parseBoxesAndLines(c)],
14398
- ["mindmap", (c) => parseMindmap(c)],
14399
- ["wireframe", (c) => parseWireframe(c)],
14400
- ["tech-radar", (c) => parseTechRadar(c)],
14401
- ["cycle", (c) => parseCycle(c)],
14402
- ["journey-map", (c) => parseJourneyMap(c)]
14403
- ]);
14404
- ALL_KNOWN_TYPES = /* @__PURE__ */ new Set([
14405
- ...DATA_CHART_TYPES,
14406
- ...VISUALIZATION_TYPES,
14407
- ...DIAGRAM_TYPES
14408
- ]);
14959
+ CHART_TYPE_DESCRIPTIONS = Object.fromEntries(chartTypes.map((c) => [c.id, c.description]));
14960
+ chartTypeParsers = [
14961
+ // Structured diagrams (direct parsers)
14962
+ ["sequence", parseSequenceDgmo],
14963
+ ["flowchart", parseFlowchart],
14964
+ ["class", parseClassDiagram],
14965
+ ["er", parseERDiagram],
14966
+ ["state", parseState],
14967
+ ["org", parseOrg],
14968
+ ["kanban", parseKanban],
14969
+ ["c4", parseC4],
14970
+ ["sitemap", parseSitemap],
14971
+ ["infra", parseInfra],
14972
+ ["gantt", parseGantt],
14973
+ ["boxes-and-lines", parseBoxesAndLines],
14974
+ ["mindmap", parseMindmap],
14975
+ ["wireframe", parseWireframe],
14976
+ ["tech-radar", parseTechRadar],
14977
+ ["cycle", parseCycle],
14978
+ ["journey-map", parseJourneyMap],
14979
+ ["pyramid", parsePyramid],
14980
+ // Standard ECharts charts (parseChart)
14981
+ ["bar", parseChart],
14982
+ ["line", parseChart],
14983
+ ["multi-line", parseChart],
14984
+ ["area", parseChart],
14985
+ ["pie", parseChart],
14986
+ ["doughnut", parseChart],
14987
+ ["radar", parseChart],
14988
+ ["polar-area", parseChart],
14989
+ ["bar-stacked", parseChart],
14990
+ // Extended ECharts charts (parseExtendedChart)
14991
+ ["scatter", parseExtendedChart],
14992
+ ["sankey", parseExtendedChart],
14993
+ ["chord", parseExtendedChart],
14994
+ ["function", parseExtendedChart],
14995
+ ["heatmap", parseExtendedChart],
14996
+ ["funnel", parseExtendedChart],
14997
+ // D3 visualizations (parseVisualization)
14998
+ ["slope", parseVisualization],
14999
+ ["wordcloud", parseVisualization],
15000
+ ["arc", parseVisualization],
15001
+ ["timeline", parseVisualization],
15002
+ ["venn", parseVisualization],
15003
+ ["quadrant", parseVisualization]
15004
+ ];
15005
+ knownChartTypeIds = chartTypeParsers.map(
15006
+ ([id]) => id
15007
+ );
15008
+ PARSER_BY_ID = new Map(chartTypeParsers);
15009
+ ALL_KNOWN_TYPES = new Set(knownChartTypeIds);
14409
15010
  }
14410
15011
  });
14411
15012
 
@@ -15208,7 +15809,8 @@ var init_layout = __esm({
15208
15809
  // src/org/collapse.ts
15209
15810
  var collapse_exports = {};
15210
15811
  __export(collapse_exports, {
15211
- collapseOrgTree: () => collapseOrgTree
15812
+ collapseOrgTree: () => collapseOrgTree,
15813
+ focusOrgTree: () => focusOrgTree
15212
15814
  });
15213
15815
  function cloneNode(node) {
15214
15816
  return {
@@ -15263,6 +15865,49 @@ function collapseOrgTree(original, collapsedIds) {
15263
15865
  hiddenCounts
15264
15866
  };
15265
15867
  }
15868
+ function findNodeWithPath(nodes, targetId, path) {
15869
+ for (const node of nodes) {
15870
+ if (node.id === targetId) {
15871
+ return { node, path };
15872
+ }
15873
+ const result = findNodeWithPath(node.children, targetId, [
15874
+ ...path,
15875
+ {
15876
+ id: node.id,
15877
+ label: node.label,
15878
+ lineNumber: node.lineNumber,
15879
+ color: node.color,
15880
+ metadata: { ...node.metadata },
15881
+ isContainer: node.isContainer
15882
+ }
15883
+ ]);
15884
+ if (result) return result;
15885
+ }
15886
+ return null;
15887
+ }
15888
+ function focusOrgTree(original, focusNodeId) {
15889
+ const found = findNodeWithPath(original.roots, focusNodeId, []);
15890
+ if (!found) return null;
15891
+ const isRoot = original.roots.some((r) => r.id === focusNodeId);
15892
+ if (isRoot) {
15893
+ return {
15894
+ parsed: {
15895
+ ...original,
15896
+ roots: [cloneNode(found.node)]
15897
+ },
15898
+ ancestorPath: []
15899
+ };
15900
+ }
15901
+ const cloned = cloneNode(found.node);
15902
+ cloned.parentId = null;
15903
+ return {
15904
+ parsed: {
15905
+ ...original,
15906
+ roots: [cloned]
15907
+ },
15908
+ ancestorPath: found.path
15909
+ };
15910
+ }
15266
15911
  var init_collapse = __esm({
15267
15912
  "src/org/collapse.ts"() {
15268
15913
  "use strict";
@@ -15321,7 +15966,7 @@ function containerFill(palette, isDark, nodeColor2) {
15321
15966
  function containerStroke(palette, nodeColor2) {
15322
15967
  return nodeColor2 ?? palette.textMuted;
15323
15968
  }
15324
- function renderOrg(container, parsed, layout, palette, isDark, onClickItem, exportDims, activeTagGroup, hiddenAttributes) {
15969
+ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, exportDims, activeTagGroup, hiddenAttributes, ancestorPath) {
15325
15970
  d3Selection.select(container).selectAll(":not([data-d3-tooltip])").remove();
15326
15971
  const width = exportDims?.width ?? container.clientWidth;
15327
15972
  const height = exportDims?.height ?? container.clientHeight;
@@ -15334,8 +15979,10 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
15334
15979
  const legendReserve = fixedLegend ? LEGEND_HEIGHT + LEGEND_FIXED_GAP : 0;
15335
15980
  const fixedTitle = !exportDims && !!parsed.title;
15336
15981
  const titleReserve = fixedTitle ? TITLE_HEIGHT : 0;
15982
+ const hasAncestorTrail = !exportDims && ancestorPath && ancestorPath.length > 0;
15983
+ const ancestorTrailHeight = hasAncestorTrail ? ancestorPath.length * ANCESTOR_ROW_HEIGHT + ANCESTOR_TRAIL_BOTTOM_GAP : 0;
15337
15984
  const diagramW = layout.width;
15338
- let diagramH = layout.height + (fixedTitle ? 0 : titleOffset);
15985
+ let diagramH = layout.height + (fixedTitle ? 0 : titleOffset) + ancestorTrailHeight;
15339
15986
  if (fixedLegend) {
15340
15987
  diagramH -= layoutLegendShift;
15341
15988
  }
@@ -15367,11 +16014,13 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
15367
16014
  }
15368
16015
  }
15369
16016
  }
15370
- const contentG = mainG.append("g").attr("transform", `translate(0, ${fixedTitle ? 0 : titleOffset})`);
16017
+ const contentYShift = (fixedTitle ? 0 : titleOffset) + ancestorTrailHeight;
16018
+ const contentG = mainG.append("g").attr("transform", `translate(0, ${contentYShift})`);
15371
16019
  const displayNames = /* @__PURE__ */ new Map();
15372
16020
  for (const group of parsed.tagGroups) {
15373
16021
  displayNames.set(group.name.toLowerCase(), group.name);
15374
16022
  }
16023
+ const rootNodeIds = new Set(parsed.roots.map((r) => r.id));
15375
16024
  const colorOff = parsed.options?.color === "off";
15376
16025
  for (const c of layout.containers) {
15377
16026
  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 +16067,18 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
15418
16067
  cG.append("clipPath").attr("id", clipId).append("rect").attr("width", c.width).attr("height", c.height).attr("rx", CONTAINER_RADIUS);
15419
16068
  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
16069
  }
16070
+ if (!exportDims && c.hasChildren && !rootNodeIds.has(c.nodeId)) {
16071
+ const iconSize = 14;
16072
+ const iconPad = 5;
16073
+ const iconX = c.width - iconSize - iconPad;
16074
+ const iconY = iconPad;
16075
+ const focusG = cG.append("g").attr("class", "org-focus-icon").attr("data-focus-node", c.nodeId).attr("transform", `translate(${iconX}, ${iconY})`);
16076
+ focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
16077
+ const cx = iconSize / 2;
16078
+ const cy = iconSize / 2;
16079
+ 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);
16080
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
16081
+ }
15421
16082
  }
15422
16083
  for (const edge of layout.edges) {
15423
16084
  if (edge.points.length < 2) continue;
@@ -15476,6 +16137,58 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
15476
16137
  nodeG.append("clipPath").attr("id", clipId).append("rect").attr("width", node.width).attr("height", node.height).attr("rx", CARD_RADIUS);
15477
16138
  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
16139
  }
16140
+ if (!exportDims && node.hasChildren && !rootNodeIds.has(node.id)) {
16141
+ const iconSize = 14;
16142
+ const iconPad = 5;
16143
+ const iconX = node.width - iconSize - iconPad;
16144
+ const iconY = iconPad;
16145
+ const focusG = nodeG.append("g").attr("class", "org-focus-icon").attr("data-focus-node", node.id).attr("transform", `translate(${iconX}, ${iconY})`);
16146
+ focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
16147
+ const cx = iconSize / 2;
16148
+ const cy = iconSize / 2;
16149
+ 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);
16150
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
16151
+ }
16152
+ }
16153
+ if (hasAncestorTrail) {
16154
+ const rootNode = layout.nodes.find((n) => rootNodeIds.has(n.id));
16155
+ const rootContainer = !rootNode ? layout.containers.find((c) => rootNodeIds.has(c.nodeId)) : null;
16156
+ const rootCenterX = rootNode ? rootNode.x : rootContainer ? rootContainer.x + rootContainer.width / 2 : null;
16157
+ const rootTopY = rootNode ? rootNode.y : rootContainer ? rootContainer.y : null;
16158
+ if (rootCenterX !== null && rootTopY !== null) {
16159
+ const trailBottomY = rootTopY - ANCESTOR_TRAIL_BOTTOM_GAP;
16160
+ const trailG = contentG.append("g").attr("class", "org-ancestor-trail");
16161
+ const count = ancestorPath.length;
16162
+ const dotPositions = [];
16163
+ for (let i = 0; i < count; i++) {
16164
+ const fromBottom = count - 1 - i;
16165
+ dotPositions.push(trailBottomY - fromBottom * ANCESTOR_ROW_HEIGHT);
16166
+ }
16167
+ const lineTopY = dotPositions[0];
16168
+ 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);
16169
+ for (let i = 0; i < count; i++) {
16170
+ const ancestor = ancestorPath[i];
16171
+ const dotY = dotPositions[i];
16172
+ const resolvedColor = ancestor.color ?? resolveTagColor(
16173
+ ancestor.metadata,
16174
+ parsed.tagGroups,
16175
+ activeTagGroup ?? null,
16176
+ ancestor.isContainer
16177
+ );
16178
+ const dotColor = resolvedColor ?? palette.textMuted;
16179
+ const rowG = trailG.append("g").attr("class", "org-ancestor-node").attr("data-focus-ancestor", ancestor.id).style("cursor", "pointer").attr("transform", `translate(${rootCenterX}, ${dotY})`);
16180
+ 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");
16181
+ rowG.append("circle").attr("cx", 0).attr("cy", 0).attr("r", ANCESTOR_DOT_R).attr("fill", dotColor);
16182
+ 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);
16183
+ rowG.on("mouseenter", function() {
16184
+ d3Selection.select(this).select("circle").attr("r", ANCESTOR_DOT_R + 1);
16185
+ d3Selection.select(this).select("text").attr("fill", palette.text);
16186
+ }).on("mouseleave", function() {
16187
+ d3Selection.select(this).select("circle").attr("r", ANCESTOR_DOT_R);
16188
+ d3Selection.select(this).select("text").attr("fill", palette.textMuted);
16189
+ });
16190
+ }
16191
+ }
15479
16192
  }
15480
16193
  if (fixedLegend || legendOnly || exportDims && hasLegend) {
15481
16194
  const groups = layout.legend.map((g) => ({
@@ -15572,7 +16285,7 @@ function renderOrgForExport(content, theme, palette) {
15572
16285
  return extractExportSvg(container, theme);
15573
16286
  });
15574
16287
  }
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;
16288
+ 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
16289
  var init_renderer = __esm({
15577
16290
  "src/org/renderer.ts"() {
15578
16291
  "use strict";
@@ -15580,6 +16293,7 @@ var init_renderer = __esm({
15580
16293
  init_fonts();
15581
16294
  init_export_container();
15582
16295
  init_color_utils();
16296
+ init_tag_groups();
15583
16297
  init_parser4();
15584
16298
  init_layout();
15585
16299
  init_legend_constants();
@@ -15603,6 +16317,10 @@ var init_renderer = __esm({
15603
16317
  CONTAINER_HEADER_HEIGHT = 28;
15604
16318
  COLLAPSE_BAR_HEIGHT = 6;
15605
16319
  COLLAPSE_BAR_INSET = 0;
16320
+ ANCESTOR_DOT_R = 4;
16321
+ ANCESTOR_LABEL_FONT_SIZE = 11;
16322
+ ANCESTOR_ROW_HEIGHT = 22;
16323
+ ANCESTOR_TRAIL_BOTTOM_GAP = 16;
15606
16324
  LEGEND_FIXED_GAP = 8;
15607
16325
  }
15608
16326
  });
@@ -19324,7 +20042,16 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
19324
20042
  const clipId = `bl-clip-${group.label.replace(/[[\]\s]/g, "")}`;
19325
20043
  groupG.append("clipPath").attr("id", clipId).append("rect").attr("x", gx).attr("y", gy).attr("width", group.width).attr("height", group.height).attr("rx", NODE_RX);
19326
20044
  groupG.append("rect").attr("x", gx).attr("y", gy + group.height - COLLAPSE_BAR_HEIGHT3).attr("width", group.width).attr("height", COLLAPSE_BAR_HEIGHT3).attr("fill", strokeColor).attr("clip-path", `url(#${clipId})`).attr("class", "bl-collapse-bar");
19327
- groupG.append("text").attr("class", "bl-group-label").attr("x", group.x).attr("y", group.y).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-family", FONT_FAMILY).attr("font-size", GROUP_LABEL_FONT_SIZE).attr("font-weight", "600").attr("fill", palette.text).text(group.label);
20045
+ const maxLabelLines = Math.max(
20046
+ 2,
20047
+ Math.floor((group.height - 16) / (MIN_NODE_FONT_SIZE * 1.3))
20048
+ );
20049
+ const fitted = fitLabelToHeader(group.label, group.width, maxLabelLines);
20050
+ const lineH = fitted.fontSize * 1.3;
20051
+ const totalH = fitted.lines.length * lineH;
20052
+ for (let li = 0; li < fitted.lines.length; li++) {
20053
+ groupG.append("text").attr("class", "bl-group-label").attr("x", group.x).attr("y", group.y - totalH / 2 + lineH / 2 + li * lineH).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-family", FONT_FAMILY).attr("font-size", fitted.fontSize).attr("font-weight", "600").attr("fill", palette.text).text(fitted.lines[li]);
20054
+ }
19328
20055
  } else {
19329
20056
  groupG.append("rect").attr("x", gx).attr("y", gy).attr("width", group.width).attr("height", groupHeight).attr("rx", GROUP_RX).attr("ry", GROUP_RX).attr("fill", mix(palette.surface, palette.bg, 40)).attr("stroke", palette.textMuted).attr("stroke-width", 1).attr("stroke-opacity", 0.35);
19330
20057
  groupG.append("text").attr("class", "bl-group-label").attr("x", gx + group.width / 2).attr("y", gy + 18).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("font-size", GROUP_LABEL_FONT_SIZE).attr("font-weight", "600").attr("fill", palette.text).text(group.label);
@@ -30586,7 +31313,22 @@ function renderQuarterCircle(svg, parsed, quadrant, qColor, palette, isDark, wid
30586
31313
  const innerR = ri * ringBandWidth;
30587
31314
  const outerR = (ri + 1) * ringBandWidth;
30588
31315
  const fillColor = ri % 2 === 0 ? palette.bg : mix(palette.bg, palette.border, 0.15);
31316
+ const ringName = parsed.rings[ri].name;
30589
31317
  svg.append("path").attr("d", arcGen(innerR, outerR)).attr("fill", fillColor).attr("stroke", mutedColor).attr("stroke-width", 0.5);
31318
+ svg.append("path").attr("d", arcGen(innerR, outerR)).attr("fill", "transparent").attr("data-ring-arc", ringName).style("cursor", "pointer").on("mouseenter", () => {
31319
+ d3Selection14.select(rootContainer).selectAll("[data-ring-arc]").each(function() {
31320
+ const el = d3Selection14.select(this);
31321
+ const isMatch = this.getAttribute("data-ring-arc") === ringName;
31322
+ el.attr("fill", isMatch ? qColor : "transparent").attr(
31323
+ "opacity",
31324
+ isMatch ? 0.15 : 1
31325
+ );
31326
+ });
31327
+ dimExceptRing(rootContainer, ringName);
31328
+ }).on("mouseleave", () => {
31329
+ d3Selection14.select(rootContainer).selectAll("[data-ring-arc]").attr("fill", "transparent").attr("opacity", 1);
31330
+ clearDim(rootContainer);
31331
+ });
30590
31332
  }
30591
31333
  const ringOrder = parsed.rings.map((r) => r.name);
30592
31334
  const angularPadding = 0.08;
@@ -30649,10 +31391,21 @@ function dimExcept(root, lineNum) {
30649
31391
  el.style.opacity = el.getAttribute("data-line-number") === lineNum ? "1" : String(DIM_OPACITY);
30650
31392
  });
30651
31393
  }
31394
+ function dimExceptRing(root, ringName) {
31395
+ root.querySelectorAll("[data-line-number]").forEach((el) => {
31396
+ el.style.opacity = el.getAttribute("data-ring") === ringName ? "1" : String(DIM_OPACITY);
31397
+ });
31398
+ root.querySelectorAll("[data-ring-group]").forEach((el) => {
31399
+ el.style.opacity = el.getAttribute("data-ring-group") === ringName ? "1" : String(DIM_OPACITY);
31400
+ });
31401
+ }
30652
31402
  function clearDim(root) {
30653
31403
  root.querySelectorAll("[data-line-number]").forEach((el) => {
30654
31404
  el.style.opacity = "1";
30655
31405
  });
31406
+ root.querySelectorAll("[data-ring-group]").forEach((el) => {
31407
+ el.style.opacity = "1";
31408
+ });
30656
31409
  }
30657
31410
  function renderHtmlPanel(panel, parsed, quadrant, qColor, palette, isDark, rootContainer, onClickItem) {
30658
31411
  const ringOrder = parsed.rings.map((r) => r.name);
@@ -30664,11 +31417,13 @@ function renderHtmlPanel(panel, parsed, quadrant, qColor, palette, isDark, rootC
30664
31417
  const blips = quadrant.blips.filter((b) => b.ring === ringName);
30665
31418
  if (blips.length === 0) continue;
30666
31419
  const ringGroup = document.createElement("div");
31420
+ ringGroup.setAttribute("data-ring-group", ringName);
30667
31421
  ringGroup.style.cssText = `
30668
31422
  background: ${palette.surface};
30669
31423
  border-radius: 8px;
30670
31424
  padding: 10px;
30671
31425
  margin-bottom: 12px;
31426
+ transition: opacity 0.15s;
30672
31427
  `;
30673
31428
  const header = document.createElement("div");
30674
31429
  header.style.cssText = `
@@ -31807,7 +32562,8 @@ function layoutJourneyMap(parsed, palette, options) {
31807
32562
  const annoIconIndent = ANNO_ICON_SIZE + ANNO_ICON_GAP;
31808
32563
  const annoTextW = STEP_CARD_WIDTH - CARD_PADDING_X2 * 2 - annoIconIndent;
31809
32564
  const descTextWidth = STEP_CARD_WIDTH - CARD_PADDING_X2 * 2;
31810
- const charWidth = 4.8;
32565
+ const FONT_SIZE_META2 = 10;
32566
+ const charWidth = FONT_SIZE_META2 * 0.6;
31811
32567
  const titleTextWidth = STEP_CARD_WIDTH - CARD_PADDING_X2 * 2;
31812
32568
  const titleCharWidth = 6.5;
31813
32569
  const TITLE_LINE_HEIGHT2 = 16;
@@ -32021,7 +32777,13 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32021
32777
  const containerW = exportDims?.width ?? container.clientWidth;
32022
32778
  const containerH = exportDims?.height ?? container.clientHeight;
32023
32779
  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);
32780
+ const svg = d3.select(container).append("svg").attr("xmlns", "http://www.w3.org/2000/svg").attr(
32781
+ "width",
32782
+ useContainerFit ? containerW : exportDims?.width ?? layout.totalWidth
32783
+ ).attr(
32784
+ "height",
32785
+ useContainerFit ? containerH : exportDims?.height ?? layout.totalHeight
32786
+ ).attr("viewBox", `0 0 ${layout.totalWidth} ${layout.totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("font-family", FONT_FAMILY);
32025
32787
  svg.append("rect").attr("width", layout.totalWidth).attr("height", layout.totalHeight).attr("fill", palette.bg);
32026
32788
  const defs = svg.append("defs");
32027
32789
  const curveGradient = defs.append("linearGradient").attr("id", "journey-curve-gradient").attr("x1", "0").attr("y1", "0").attr("x2", "0").attr("y2", "1");
@@ -32093,7 +32855,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32093
32855
  const legendY = PADDING2 + (parsed.title ? FONT_SIZE_TITLE + 8 : 0);
32094
32856
  const legendG = svg.append("g").attr("class", "journey-legend").attr("transform", `translate(${legendX},${legendY})`);
32095
32857
  const legendConfig = {
32096
- groups: allLegendGroups,
32858
+ groups: parsed.tagGroups,
32097
32859
  position: {
32098
32860
  placement: "top-center",
32099
32861
  titleRelation: "inline-with-title"
@@ -32162,7 +32924,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32162
32924
  const y = layout.curveAreaBottom - (score - 1) / 4 * (layout.curveAreaBottom - layout.curveAreaTop - 120) - 10;
32163
32925
  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
32926
  const SCORE_LABEL_R = 8;
32165
- const labelG = curveG.append("g").attr("class", "journey-score-label");
32927
+ const labelG = curveG.append("g").attr("class", "journey-score-label").attr("data-score", String(score));
32166
32928
  renderScoreFace(
32167
32929
  labelG,
32168
32930
  PADDING2 - SCORE_LABEL_R - 2,
@@ -32172,35 +32934,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32172
32934
  SCORE_LABEL_R
32173
32935
  );
32174
32936
  if (!exportDims) {
32175
- const scoreStr = String(score);
32176
32937
  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
32938
  }
32205
32939
  }
32206
32940
  if (layout.curvePoints.length >= 2) {
@@ -32386,6 +33120,41 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32386
33120
  if (!exportDims) {
32387
33121
  const DIM_OPACITY2 = 0.35;
32388
33122
  let lockedLine = null;
33123
+ let lockedScore = null;
33124
+ const applyScoreDimming = (activeScore) => {
33125
+ const scoreStr = String(activeScore);
33126
+ svg.selectAll(".journey-step").each(function() {
33127
+ const hit = this.getAttribute("data-score") === scoreStr;
33128
+ d3.select(this).style("opacity", hit ? "1" : String(DIM_HOVER));
33129
+ });
33130
+ svg.selectAll(".journey-face").each(function() {
33131
+ const hit = this.getAttribute("data-score") === scoreStr;
33132
+ const sel = d3.select(this);
33133
+ sel.style("opacity", hit ? "1" : String(DIM_HOVER));
33134
+ if (hit) {
33135
+ const fcx = parseFloat(sel.attr("data-cx") ?? "0");
33136
+ const fcy = parseFloat(sel.attr("data-cy") ?? "0");
33137
+ sel.attr(
33138
+ "transform",
33139
+ `translate(${fcx},${fcy}) scale(1.3) translate(${-fcx},${-fcy})`
33140
+ );
33141
+ } else {
33142
+ sel.attr("transform", null);
33143
+ }
33144
+ });
33145
+ svg.selectAll(".journey-thought").style("opacity", String(DIM_HOVER));
33146
+ svg.selectAll(".journey-score-label").each(function() {
33147
+ const sel = d3.select(this);
33148
+ const s = sel.attr("data-score");
33149
+ sel.style("opacity", s === scoreStr ? "1" : String(DIM_HOVER));
33150
+ });
33151
+ };
33152
+ const clearScoreDimming = () => {
33153
+ svg.selectAll(".journey-step").style("opacity", null);
33154
+ svg.selectAll(".journey-face").style("opacity", null).attr("transform", null);
33155
+ svg.selectAll(".journey-thought").style("opacity", null);
33156
+ svg.selectAll(".journey-score-label").style("opacity", null);
33157
+ };
32389
33158
  const applyDimming = (activeLine) => {
32390
33159
  svg.selectAll(".journey-step").each(function() {
32391
33160
  const el = d3.select(this);
@@ -32440,7 +33209,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32440
33209
  const lines = wrapText4(thoughtText, THOUGHT_MAX_W, THOUGHT_FONT);
32441
33210
  const textW = Math.min(
32442
33211
  THOUGHT_MAX_W,
32443
- Math.max(...lines.map((l) => l.length * 4.8))
33212
+ Math.max(...lines.map((l) => l.length * THOUGHT_FONT * 0.6))
32444
33213
  );
32445
33214
  const bw = textW + THOUGHT_PAD_X * 2;
32446
33215
  const bh = lines.length * THOUGHT_LINE_H + THOUGHT_PAD_Y * 2;
@@ -32461,9 +33230,11 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32461
33230
  };
32462
33231
  svg.on("click", (event) => {
32463
33232
  const target = event.target;
32464
- if (!target.closest(".journey-face") && !target.closest(".journey-step") && !target.closest(".journey-phase")) {
33233
+ if (!target.closest(".journey-face") && !target.closest(".journey-step") && !target.closest(".journey-phase") && !target.closest(".journey-score-label")) {
32465
33234
  lockedLine = null;
33235
+ lockedScore = null;
32466
33236
  clearDimming();
33237
+ clearScoreDimming();
32467
33238
  }
32468
33239
  });
32469
33240
  const showThoughtForLine = (ln) => {
@@ -32477,17 +33248,22 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32477
33248
  svg.selectAll(".journey-face").each(function() {
32478
33249
  const el = d3.select(this);
32479
33250
  el.on("mouseenter", () => {
32480
- if (lockedLine !== null) return;
33251
+ if (lockedLine !== null || lockedScore !== null) return;
32481
33252
  const ln = parseInt(el.attr("data-line-number") ?? "0", 10);
32482
33253
  if (ln) {
32483
33254
  applyDimming(ln);
32484
33255
  showThoughtForLine(ln);
32485
33256
  }
32486
33257
  }).on("mouseleave", () => {
32487
- if (lockedLine !== null) return;
33258
+ if (lockedLine !== null || lockedScore !== null) return;
32488
33259
  clearDimming();
32489
33260
  }).on("click", (event) => {
32490
33261
  event.stopPropagation();
33262
+ if (lockedScore !== null) {
33263
+ lockedScore = null;
33264
+ clearScoreDimming();
33265
+ return;
33266
+ }
32491
33267
  const ln = parseInt(el.attr("data-line-number") ?? "0", 10);
32492
33268
  if (lockedLine === ln) {
32493
33269
  lockedLine = null;
@@ -32503,17 +33279,22 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32503
33279
  svg.selectAll(".journey-step").each(function() {
32504
33280
  const el = d3.select(this);
32505
33281
  el.on("mouseenter", () => {
32506
- if (lockedLine !== null) return;
33282
+ if (lockedLine !== null || lockedScore !== null) return;
32507
33283
  const ln = parseInt(el.attr("data-line-number") ?? "0", 10);
32508
33284
  if (ln) {
32509
33285
  applyDimming(ln);
32510
33286
  showThoughtForLine(ln);
32511
33287
  }
32512
33288
  }).on("mouseleave", () => {
32513
- if (lockedLine !== null) return;
33289
+ if (lockedLine !== null || lockedScore !== null) return;
32514
33290
  clearDimming();
32515
33291
  }).on("click", (event) => {
32516
33292
  event.stopPropagation();
33293
+ if (lockedScore !== null) {
33294
+ lockedScore = null;
33295
+ clearScoreDimming();
33296
+ return;
33297
+ }
32517
33298
  const ln = parseInt(el.attr("data-line-number") ?? "0", 10);
32518
33299
  if (lockedLine === ln) {
32519
33300
  lockedLine = null;
@@ -32526,6 +33307,30 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
32526
33307
  }
32527
33308
  });
32528
33309
  });
33310
+ svg.selectAll(".journey-score-label").each(function() {
33311
+ const el = d3.select(this);
33312
+ const score = parseInt(el.attr("data-score") ?? "0", 10);
33313
+ el.on("mouseenter", () => {
33314
+ if (lockedLine !== null || lockedScore !== null) return;
33315
+ applyScoreDimming(score);
33316
+ }).on("mouseleave", () => {
33317
+ if (lockedLine !== null || lockedScore !== null) return;
33318
+ clearScoreDimming();
33319
+ }).on("click", (event) => {
33320
+ event.stopPropagation();
33321
+ if (lockedLine !== null) {
33322
+ lockedLine = null;
33323
+ clearDimming();
33324
+ }
33325
+ if (lockedScore === score) {
33326
+ lockedScore = null;
33327
+ clearScoreDimming();
33328
+ } else {
33329
+ lockedScore = score;
33330
+ applyScoreDimming(score);
33331
+ }
33332
+ });
33333
+ });
32529
33334
  }
32530
33335
  }
32531
33336
  function resolveStepColor(step, scoreColor, activeGroup, tagGroups, _palette) {
@@ -32677,8 +33482,8 @@ function renderScoreFace(parent, cx, cy, score, palette, radius) {
32677
33482
  ).attr("fill", "none").attr("stroke", eyeColor).attr("stroke-width", 1.2).attr("stroke-linecap", "round");
32678
33483
  return g;
32679
33484
  }
32680
- function wrapText4(text, maxWidth, _fontSize) {
32681
- const charWidth = 4.8;
33485
+ function wrapText4(text, maxWidth, fontSize) {
33486
+ const charWidth = fontSize * 0.6;
32682
33487
  const maxChars = Math.floor(maxWidth / charWidth);
32683
33488
  if (maxChars <= 0) return [text];
32684
33489
  const words = text.split(/\s+/);
@@ -32697,7 +33502,7 @@ function wrapText4(text, maxWidth, _fontSize) {
32697
33502
  return lines;
32698
33503
  }
32699
33504
  function truncateText(text, maxWidth) {
32700
- const maxChars = Math.floor(maxWidth / 4.8);
33505
+ const maxChars = Math.floor(maxWidth / 6.6);
32701
33506
  if (text.length <= maxChars) return text;
32702
33507
  return text.substring(0, maxChars - 1) + "\u2026";
32703
33508
  }
@@ -32799,6 +33604,21 @@ var init_renderer13 = __esm({
32799
33604
  }
32800
33605
  });
32801
33606
 
33607
+ // src/cycle/types.ts
33608
+ function arrowHeadLength(strokeWidth) {
33609
+ return BASE_ARROW_SIZE + ARROW_SCALE * Math.sqrt(strokeWidth);
33610
+ }
33611
+ var DEFAULT_EDGE_WIDTH, MIN_EDGE_WIDTH, BASE_ARROW_SIZE, ARROW_SCALE;
33612
+ var init_types2 = __esm({
33613
+ "src/cycle/types.ts"() {
33614
+ "use strict";
33615
+ DEFAULT_EDGE_WIDTH = 3;
33616
+ MIN_EDGE_WIDTH = 2;
33617
+ BASE_ARROW_SIZE = 8;
33618
+ ARROW_SCALE = 6;
33619
+ }
33620
+ });
33621
+
32802
33622
  // src/cycle/layout.ts
32803
33623
  function computeCycleLayout(parsed, options) {
32804
33624
  const width = options?.width ?? 800;
@@ -33133,8 +33953,11 @@ function computeEdgePaths(layoutNodes, parsed, cx, cy, radius, isClockwise) {
33133
33953
  return parsed.edges.map((edge) => {
33134
33954
  const src = layoutNodes[edge.sourceIndex];
33135
33955
  const tgt = layoutNodes[edge.targetIndex];
33136
- const strokeWidth = edge.width ?? DEFAULT_EDGE_WIDTH;
33137
- const arrowLen = ARROWHEAD_MARKER_W * strokeWidth;
33956
+ const strokeWidth = Math.max(
33957
+ edge.width ?? DEFAULT_EDGE_WIDTH,
33958
+ MIN_EDGE_WIDTH
33959
+ );
33960
+ const arrowLen = arrowHeadLength(strokeWidth) * 0.9;
33138
33961
  const { path, labelX, labelY, labelAngle } = buildEdgeArc(
33139
33962
  src,
33140
33963
  tgt,
@@ -33156,7 +33979,7 @@ function computeEdgePaths(layoutNodes, parsed, cx, cy, radius, isClockwise) {
33156
33979
  });
33157
33980
  }
33158
33981
  function fitToCanvas(nodes, edges, parsed, cx, cy, radius, width, height, _isClockwise) {
33159
- const PADDING3 = 10;
33982
+ const PADDING3 = 30;
33160
33983
  let contentMinX = Infinity, contentMaxX = -Infinity;
33161
33984
  let contentMinY = Infinity, contentMaxY = -Infinity;
33162
33985
  for (const n of nodes) {
@@ -33209,17 +34032,7 @@ function fitToCanvas(nodes, edges, parsed, cx, cy, radius, width, height, _isClo
33209
34032
  }
33210
34033
  function buildEdgeArc(src, tgt, cx, cy, radius, isClockwise, arrowLength = 0) {
33211
34034
  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
- );
34035
+ const startAngle = src.angle;
33223
34036
  const nodeEndAngle = tgt.isCircle ? circleNodeExitAngle(tgt.width / 2, radius, tgt.angle, -dir) : circleRectExitAngle(
33224
34037
  tgt.x,
33225
34038
  tgt.y,
@@ -33249,10 +34062,11 @@ function buildEdgeArc(src, tgt, cx, cy, radius, isClockwise, arrowLength = 0) {
33249
34062
  const labelY = cy + labelR * Math.sin(midAngle);
33250
34063
  return { path, labelX, labelY, labelAngle: midAngle };
33251
34064
  }
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;
34065
+ 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
34066
  var init_layout13 = __esm({
33254
34067
  "src/cycle/layout.ts"() {
33255
34068
  "use strict";
34069
+ init_types2();
33256
34070
  MIN_ARC_ANGLE = 15 * Math.PI / 180;
33257
34071
  LABEL_CHAR_W = 8;
33258
34072
  CIRCLE_LABEL_CHAR_W = 10;
@@ -33266,8 +34080,6 @@ var init_layout13 = __esm({
33266
34080
  NODE_PAD_X = 20;
33267
34081
  MIN_CIRCLE_RADIUS = 35;
33268
34082
  CIRCLE_PAD = 14;
33269
- DEFAULT_EDGE_WIDTH = 3;
33270
- ARROWHEAD_MARKER_W = 8;
33271
34083
  EDGE_LABEL_CHAR_W = 7;
33272
34084
  }
33273
34085
  });
@@ -33350,19 +34162,25 @@ function renderCycle(container, parsed, palette, isDark, onClickItem, exportDims
33350
34162
  const g = svg.append("g").attr("transform", `translate(0, ${diagramTop})`);
33351
34163
  const defs = svg.append("defs");
33352
34164
  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];
34165
+ const markerKeys = /* @__PURE__ */ new Set();
34166
+ for (const edge of parsed.edges) {
33356
34167
  const color = resolveEdgeColor(edge, parsed, palette, defaultNodeColor);
33357
- arrowColors.add(color);
34168
+ const sw = Math.max(edge.width ?? DEFAULT_EDGE_WIDTH, MIN_EDGE_WIDTH);
34169
+ const key = `${color}|${sw}`;
34170
+ if (!markerKeys.has(key)) {
34171
+ markerKeys.add(key);
34172
+ ensureArrowMarker(defs, color, sw);
34173
+ }
33358
34174
  }
33359
- ensureArrowMarkers2(defs, arrowColors);
33360
34175
  for (let i = 0; i < layout.edges.length; i++) {
33361
34176
  const le = layout.edges[i];
33362
34177
  const edge = parsed.edges[i];
33363
34178
  const color = resolveEdgeColor(edge, parsed, palette, defaultNodeColor);
33364
- const strokeWidth = edge.width ?? DEFAULT_EDGE_WIDTH2;
33365
- const markerId = `cycle-arrow-${color.replace("#", "")}`;
34179
+ const strokeWidth = Math.max(
34180
+ edge.width ?? DEFAULT_EDGE_WIDTH,
34181
+ MIN_EDGE_WIDTH
34182
+ );
34183
+ const markerId = arrowMarkerId(color, strokeWidth);
33366
34184
  const edgeG = g.append("g").attr("class", "cycle-edge");
33367
34185
  if (edge.lineNumber) {
33368
34186
  edgeG.attr("data-line-number", edge.lineNumber);
@@ -33508,16 +34326,16 @@ function resolveEdgeColor(edge, parsed, palette, defaultNodeColor) {
33508
34326
  }
33509
34327
  return defaultNodeColor;
33510
34328
  }
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
- }
34329
+ function arrowMarkerId(color, strokeWidth) {
34330
+ return `cycle-arrow-${color.replace("#", "")}-w${strokeWidth}`;
34331
+ }
34332
+ function ensureArrowMarker(defs, color, strokeWidth) {
34333
+ const id = arrowMarkerId(color, strokeWidth);
34334
+ const mw = arrowHeadLength(strokeWidth) / strokeWidth;
34335
+ const mh = Math.max(1.5, mw * 0.5);
34336
+ 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);
33519
34337
  }
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;
34338
+ var d3Selection16, NODE_FONT_SIZE5, DESC_FONT_SIZE5, EDGE_LABEL_FONT_SIZE9, DESC_LINE_HEIGHT8, TITLE_AREA_HEIGHT;
33521
34339
  var init_renderer14 = __esm({
33522
34340
  "src/cycle/renderer.ts"() {
33523
34341
  "use strict";
@@ -33529,10 +34347,8 @@ var init_renderer14 = __esm({
33529
34347
  init_color_utils();
33530
34348
  init_colors();
33531
34349
  init_inline_markdown();
34350
+ init_types2();
33532
34351
  init_layout13();
33533
- DEFAULT_EDGE_WIDTH2 = 3;
33534
- ARROWHEAD_W5 = 8;
33535
- ARROWHEAD_H5 = 8;
33536
34352
  NODE_FONT_SIZE5 = 13;
33537
34353
  DESC_FONT_SIZE5 = 11;
33538
34354
  EDGE_LABEL_FONT_SIZE9 = 11;
@@ -33541,6 +34357,397 @@ var init_renderer14 = __esm({
33541
34357
  }
33542
34358
  });
33543
34359
 
34360
+ // src/pyramid/renderer.ts
34361
+ var renderer_exports15 = {};
34362
+ __export(renderer_exports15, {
34363
+ renderPyramid: () => renderPyramid,
34364
+ renderPyramidForExport: () => renderPyramidForExport
34365
+ });
34366
+ function renderPyramid(container, parsed, palette, isDark, onClickItem, exportDims) {
34367
+ if (parsed.layers.length === 0) return;
34368
+ d3Selection17.select(container).selectAll(":not([data-d3-tooltip])").remove();
34369
+ const width = exportDims?.width ?? container.clientWidth;
34370
+ const height = exportDims?.height ?? container.clientHeight;
34371
+ if (width <= 0 || height <= 0) return;
34372
+ const hasAnyDescription = parsed.layers.some((l) => l.description.length > 0);
34373
+ const titleH = parsed.title ? TITLE_AREA_HEIGHT2 : 0;
34374
+ const bodyTop = titleH + V_MARGIN;
34375
+ const bodyBottom = height - V_MARGIN;
34376
+ const bodyHeight = Math.max(60, bodyBottom - bodyTop);
34377
+ const sideMargin = width * H_MARGIN_FRAC;
34378
+ const usableWidth = width - sideMargin * 2;
34379
+ const N = parsed.layers.length;
34380
+ const singleProbe = computeLayout2({
34381
+ width,
34382
+ usableWidth,
34383
+ sideMargin,
34384
+ bodyTop,
34385
+ bodyHeight,
34386
+ layers: parsed.layers,
34387
+ hasDescription: hasAnyDescription,
34388
+ alternate: false
34389
+ });
34390
+ const anyOverflow = singleProbe.wraps.some((w) => w.overflows);
34391
+ const useAlternate = anyOverflow && hasAnyDescription && N >= 2;
34392
+ const layout = useAlternate ? computeLayout2({
34393
+ width,
34394
+ usableWidth,
34395
+ sideMargin,
34396
+ bodyTop,
34397
+ bodyHeight,
34398
+ layers: parsed.layers,
34399
+ hasDescription: true,
34400
+ alternate: true
34401
+ }) : singleProbe;
34402
+ 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);
34403
+ svg.append("style").text(
34404
+ ".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}"
34405
+ );
34406
+ svg.append("rect").attr("width", width).attr("height", height).attr("fill", palette.bg);
34407
+ if (parsed.title) {
34408
+ 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");
34409
+ if (onClickItem) {
34410
+ titleText.on("click", () => onClickItem(parsed.titleLineNumber));
34411
+ }
34412
+ }
34413
+ const seriesColors2 = getSeriesColors(palette);
34414
+ const layerBase = isDark ? palette.surface : palette.bg;
34415
+ const resolveSolid = (layer, i) => {
34416
+ if (layer.color) {
34417
+ const named = resolveColor(layer.color, palette);
34418
+ if (named) return named;
34419
+ }
34420
+ return seriesColors2[i % seriesColors2.length];
34421
+ };
34422
+ const diagramG = svg.append("g").attr("class", "pyramid-body");
34423
+ for (let i = 0; i < N; i++) {
34424
+ const layer = parsed.layers[i];
34425
+ const topEdgeY = layout.pyramidTop + i * layout.layerH;
34426
+ const botEdgeY = topEdgeY + layout.layerH;
34427
+ const topHalf = halfWidthAt(i, N, layout.baseWidth, parsed.inverted);
34428
+ const botHalf = halfWidthAt(i + 1, N, layout.baseWidth, parsed.inverted);
34429
+ const polyPoints = [
34430
+ [layout.pyramidCx - topHalf, topEdgeY],
34431
+ [layout.pyramidCx + topHalf, topEdgeY],
34432
+ [layout.pyramidCx + botHalf, botEdgeY],
34433
+ [layout.pyramidCx - botHalf, botEdgeY]
34434
+ ].map((p) => `${p[0]},${p[1]}`).join(" ");
34435
+ const solidColor = resolveSolid(layer, i);
34436
+ const fillColor = mix(solidColor, layerBase, 30);
34437
+ const layerG = diagramG.append("g").attr("class", "pyramid-layer").attr("data-line-number", layer.lineNumber);
34438
+ if (onClickItem) {
34439
+ const ln = layer.lineNumber;
34440
+ layerG.style("cursor", "pointer").on("click", () => onClickItem(ln));
34441
+ }
34442
+ layerG.append("polygon").attr("points", polyPoints).attr("fill", fillColor).attr("stroke", solidColor).attr("stroke-width", 2);
34443
+ const midY = (topEdgeY + botEdgeY) / 2;
34444
+ const labelFitsInside = Math.min(topHalf, botHalf) * 2 > layout.labelFont * 4;
34445
+ const textColor = labelFitsInside ? contrastText(fillColor, "#eceff4", "#2e3440") : palette.text;
34446
+ 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);
34447
+ renderInlineText(labelText, layer.label, palette);
34448
+ if (layer.description.length > 0) {
34449
+ const side = useAlternate ? i % 2 === 0 ? "right" : "left" : "right";
34450
+ const wrap = layout.wraps[i];
34451
+ renderLayerDescriptions(
34452
+ diagramG,
34453
+ layer,
34454
+ side,
34455
+ wrap,
34456
+ layout,
34457
+ midY,
34458
+ solidColor,
34459
+ palette,
34460
+ titleH + V_MARGIN,
34461
+ height - V_MARGIN,
34462
+ onClickItem
34463
+ );
34464
+ }
34465
+ }
34466
+ }
34467
+ function renderPyramidForExport(container, parsed, palette, isDark, exportDims) {
34468
+ renderPyramid(container, parsed, palette, isDark, void 0, exportDims);
34469
+ }
34470
+ function computeLayout2(input) {
34471
+ const {
34472
+ width,
34473
+ usableWidth,
34474
+ sideMargin,
34475
+ bodyTop,
34476
+ bodyHeight,
34477
+ layers,
34478
+ hasDescription,
34479
+ alternate
34480
+ } = input;
34481
+ const N = layers.length;
34482
+ const pyramidShareFrac = hasDescription ? alternate ? PYRAMID_SHARE_ALTERNATE : PYRAMID_SHARE_WITH_DESC : BASE_WIDTH_FRAC_NO_DESC;
34483
+ const pyramidBandWidth = usableWidth * pyramidShareFrac;
34484
+ const maxBaseByHeight = bodyHeight / PITCH_RATIO;
34485
+ const baseWidth = Math.min(pyramidBandWidth, maxBaseByHeight);
34486
+ const pyramidH = baseWidth * PITCH_RATIO;
34487
+ let pyramidCx;
34488
+ if (!hasDescription) {
34489
+ pyramidCx = width / 2;
34490
+ } else if (alternate) {
34491
+ pyramidCx = width / 2;
34492
+ } else {
34493
+ pyramidCx = sideMargin + pyramidBandWidth / 2;
34494
+ }
34495
+ const pyramidTop = bodyTop + (bodyHeight - pyramidH) / 2;
34496
+ const layerH = pyramidH / N;
34497
+ const labelFont = clamp(
34498
+ Math.round(layerH * 0.38),
34499
+ LABEL_FONT_MIN,
34500
+ LABEL_FONT_MAX
34501
+ );
34502
+ const descFont = clamp(
34503
+ Math.round(layerH * 0.22),
34504
+ DESC_FONT_MIN,
34505
+ DESC_FONT_MAX
34506
+ );
34507
+ const descLineHeight = Math.round(descFont * 1.35);
34508
+ const pyramidRightEdge = pyramidCx + baseWidth / 2;
34509
+ const pyramidLeftEdge = pyramidCx - baseWidth / 2;
34510
+ const rightAccentX = pyramidRightEdge + DESC_GAP;
34511
+ const rightTextX = rightAccentX + DESC_ACCENT_WIDTH + DESC_ACCENT_GAP;
34512
+ const rightTextWidth = Math.max(80, width - sideMargin - rightTextX);
34513
+ const leftAccentX = pyramidLeftEdge - DESC_GAP - DESC_ACCENT_WIDTH;
34514
+ const leftTextWidth = Math.max(
34515
+ 80,
34516
+ leftAccentX - DESC_ACCENT_GAP - sideMargin
34517
+ );
34518
+ const leftTextX = sideMargin;
34519
+ const wraps = layers.map((layer, i) => {
34520
+ if (layer.description.length === 0) {
34521
+ return { allLines: [], overflows: false, shortLineCount: 0 };
34522
+ }
34523
+ const side = alternate ? i % 2 === 0 ? "right" : "left" : "right";
34524
+ const colWidth = side === "right" ? rightTextWidth : leftTextWidth;
34525
+ const wrapped = [];
34526
+ for (const line11 of layer.description) {
34527
+ wrapped.push(...wrapText5(line11, colWidth, descFont));
34528
+ }
34529
+ const bandCap = Math.max(1, Math.floor(layerH / descLineHeight) - 0);
34530
+ const overflows = wrapped.length > bandCap;
34531
+ const shortLineCount = overflows ? bandCap : wrapped.length;
34532
+ return { allLines: wrapped, overflows, shortLineCount };
34533
+ });
34534
+ return {
34535
+ alternate,
34536
+ baseWidth,
34537
+ pyramidCx,
34538
+ pyramidTop,
34539
+ pyramidH,
34540
+ layerH,
34541
+ labelFont,
34542
+ descFont,
34543
+ descLineHeight,
34544
+ rightTextX,
34545
+ rightTextWidth,
34546
+ leftTextX,
34547
+ leftTextWidth,
34548
+ rightAccentX,
34549
+ leftAccentX,
34550
+ wraps
34551
+ };
34552
+ }
34553
+ function halfWidthAt(edgeIdx, n, baseWidth, inverted) {
34554
+ const topNarrow = !inverted;
34555
+ const frac = topNarrow ? edgeIdx / n : (n - edgeIdx) / n;
34556
+ return frac * baseWidth / 2;
34557
+ }
34558
+ function renderLayerDescriptions(parentG, layer, side, wrap, layout, midY, accentColor, palette, topBound, bottomBound, onClickItem) {
34559
+ const { descFont, descLineHeight } = layout;
34560
+ const accentX = side === "right" ? layout.rightAccentX : layout.leftAccentX;
34561
+ const textX = side === "right" ? layout.rightTextX : layout.leftTextX;
34562
+ const textAnchor = side === "right" ? "start" : "end";
34563
+ const textLineX = side === "right" ? textX : layout.leftAccentX - DESC_ACCENT_GAP;
34564
+ const availableH = bottomBound - topBound;
34565
+ const fullMaxLines = Math.max(1, Math.floor(availableH / descLineHeight));
34566
+ const fullLines = truncateWithEllipsis(wrap.allLines, fullMaxLines);
34567
+ if (!wrap.overflows) {
34568
+ renderDescriptionVariant({
34569
+ parentG,
34570
+ layer,
34571
+ lines: wrap.allLines,
34572
+ className: "pyramid-desc",
34573
+ accentX,
34574
+ accentColor,
34575
+ textX: textLineX,
34576
+ textAnchor,
34577
+ midY,
34578
+ descFont,
34579
+ descLineHeight,
34580
+ palette,
34581
+ topBound,
34582
+ bottomBound,
34583
+ onClickItem,
34584
+ variant: "short"
34585
+ });
34586
+ return;
34587
+ }
34588
+ const shortLines = buildShortLines(wrap);
34589
+ renderDescriptionVariant({
34590
+ parentG,
34591
+ layer,
34592
+ lines: shortLines,
34593
+ className: "pyramid-desc pyramid-desc-short",
34594
+ accentX,
34595
+ accentColor,
34596
+ textX: textLineX,
34597
+ textAnchor,
34598
+ midY,
34599
+ descFont,
34600
+ descLineHeight,
34601
+ palette,
34602
+ topBound,
34603
+ bottomBound,
34604
+ onClickItem,
34605
+ variant: "short"
34606
+ });
34607
+ renderDescriptionVariant({
34608
+ parentG,
34609
+ layer,
34610
+ lines: fullLines,
34611
+ className: "pyramid-desc pyramid-desc-full",
34612
+ accentX,
34613
+ accentColor,
34614
+ textX: textLineX,
34615
+ textAnchor,
34616
+ midY,
34617
+ descFont,
34618
+ descLineHeight,
34619
+ palette,
34620
+ topBound,
34621
+ bottomBound,
34622
+ onClickItem,
34623
+ variant: "full"
34624
+ });
34625
+ }
34626
+ function truncateWithEllipsis(lines, maxLines) {
34627
+ if (lines.length <= maxLines) return lines.slice();
34628
+ const visible = lines.slice(0, maxLines);
34629
+ if (visible.length === 0) return visible;
34630
+ const last = visible[visible.length - 1];
34631
+ visible[visible.length - 1] = last.endsWith("\u2026") ? last : `${last} \u2026`;
34632
+ return visible;
34633
+ }
34634
+ function buildShortLines(wrap) {
34635
+ if (!wrap.overflows) return wrap.allLines.slice();
34636
+ const visible = wrap.allLines.slice(0, wrap.shortLineCount);
34637
+ if (visible.length === 0) return [];
34638
+ const last = visible[visible.length - 1];
34639
+ visible[visible.length - 1] = last.endsWith("\u2026") ? last : `${last} \u2026`;
34640
+ return visible;
34641
+ }
34642
+ function renderDescriptionVariant(args) {
34643
+ const {
34644
+ parentG,
34645
+ layer,
34646
+ lines,
34647
+ className,
34648
+ accentX,
34649
+ accentColor,
34650
+ textX,
34651
+ textAnchor,
34652
+ midY,
34653
+ descFont,
34654
+ descLineHeight,
34655
+ palette,
34656
+ topBound,
34657
+ bottomBound,
34658
+ onClickItem,
34659
+ variant
34660
+ } = args;
34661
+ if (lines.length === 0) return;
34662
+ const totalH = lines.length * descLineHeight;
34663
+ let startY = midY - totalH / 2 + descLineHeight / 2;
34664
+ const accentPad = Math.max(4, Math.round(descFont * 0.3));
34665
+ const blockTop = startY - descLineHeight / 2 - accentPad;
34666
+ const blockBottom = startY + (lines.length - 1) * descLineHeight + descLineHeight / 2 + accentPad;
34667
+ let shift = 0;
34668
+ if (blockTop < topBound) shift = topBound - blockTop;
34669
+ else if (blockBottom > bottomBound) shift = bottomBound - blockBottom;
34670
+ startY += shift;
34671
+ const accentTop = startY - descLineHeight / 2 - accentPad;
34672
+ const accentH = totalH + accentPad * 2;
34673
+ const descG = parentG.append("g").attr("class", className).attr("data-line-number", layer.lineNumber).attr("data-variant", variant);
34674
+ if (onClickItem) {
34675
+ const ln = layer.lineNumber;
34676
+ descG.style("cursor", "pointer").on("click", () => onClickItem(ln));
34677
+ }
34678
+ 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);
34679
+ for (let j = 0; j < lines.length; j++) {
34680
+ 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);
34681
+ renderInlineText(t, lines[j], palette);
34682
+ }
34683
+ }
34684
+ function wrapText5(line11, maxWidth, fontSize) {
34685
+ if (line11 === "") return [""];
34686
+ const avgCharW = fontSize * CHAR_WIDTH_RATIO4;
34687
+ const maxChars = Math.max(8, Math.floor(maxWidth / avgCharW));
34688
+ const bulletMatch = line11.match(/^(\s*(?:•|-)\s+)(.*)$/);
34689
+ const indent = bulletMatch ? bulletMatch[1] : "";
34690
+ const body = bulletMatch ? bulletMatch[2] : line11;
34691
+ const hangingIndent = " ".repeat(indent.length);
34692
+ const words = body.split(/\s+/);
34693
+ const out = [];
34694
+ let current = indent;
34695
+ for (const word of words) {
34696
+ if (word === "") continue;
34697
+ const tentative = current.length === indent.length ? current + word : `${current} ${word}`;
34698
+ if (tentative.length <= maxChars) {
34699
+ current = tentative;
34700
+ } else {
34701
+ if (current.length > indent.length) out.push(current);
34702
+ if (word.length > maxChars - hangingIndent.length) {
34703
+ let remaining = word;
34704
+ while (remaining.length > maxChars - hangingIndent.length) {
34705
+ const slice = remaining.slice(0, maxChars - hangingIndent.length);
34706
+ out.push(hangingIndent + slice);
34707
+ remaining = remaining.slice(maxChars - hangingIndent.length);
34708
+ }
34709
+ current = remaining.length > 0 ? hangingIndent + remaining : "";
34710
+ } else {
34711
+ current = hangingIndent + word;
34712
+ }
34713
+ }
34714
+ }
34715
+ if (current.length > (out.length === 0 ? 0 : indent.length)) {
34716
+ if (current.trim().length > 0) out.push(current);
34717
+ }
34718
+ return out.length > 0 ? out : [line11];
34719
+ }
34720
+ function clamp(x, lo, hi) {
34721
+ return Math.max(lo, Math.min(hi, x));
34722
+ }
34723
+ 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;
34724
+ var init_renderer15 = __esm({
34725
+ "src/pyramid/renderer.ts"() {
34726
+ "use strict";
34727
+ d3Selection17 = __toESM(require("d3-selection"), 1);
34728
+ init_fonts();
34729
+ init_title_constants();
34730
+ init_color_utils();
34731
+ init_colors();
34732
+ init_inline_markdown();
34733
+ TITLE_AREA_HEIGHT2 = 50;
34734
+ H_MARGIN_FRAC = 0.03;
34735
+ V_MARGIN = 16;
34736
+ BASE_WIDTH_FRAC_NO_DESC = 0.78;
34737
+ PYRAMID_SHARE_WITH_DESC = 0.58;
34738
+ PYRAMID_SHARE_ALTERNATE = 0.42;
34739
+ PITCH_RATIO = 0.85;
34740
+ DESC_GAP = 28;
34741
+ DESC_ACCENT_WIDTH = 3;
34742
+ DESC_ACCENT_GAP = 12;
34743
+ CHAR_WIDTH_RATIO4 = 0.55;
34744
+ LABEL_FONT_MIN = 12;
34745
+ LABEL_FONT_MAX = 22;
34746
+ DESC_FONT_MIN = 11;
34747
+ DESC_FONT_MAX = 15;
34748
+ }
34749
+ });
34750
+
33544
34751
  // src/sequence/collapse.ts
33545
34752
  function applyCollapseProjection(parsed, collapsedGroups) {
33546
34753
  if (collapsedGroups.size === 0) {
@@ -33733,13 +34940,12 @@ var init_tag_resolution = __esm({
33733
34940
  });
33734
34941
 
33735
34942
  // src/sequence/renderer.ts
33736
- var renderer_exports15 = {};
33737
- __export(renderer_exports15, {
34943
+ var renderer_exports16 = {};
34944
+ __export(renderer_exports16, {
33738
34945
  applyGroupOrdering: () => applyGroupOrdering,
33739
34946
  applyPositionOverrides: () => applyPositionOverrides,
33740
34947
  buildNoteMessageMap: () => buildNoteMessageMap,
33741
34948
  buildRenderSequence: () => buildRenderSequence,
33742
- collectNoteLineNumbers: () => collectNoteLineNumbers,
33743
34949
  computeActivations: () => computeActivations,
33744
34950
  groupMessagesBySection: () => groupMessagesBySection,
33745
34951
  parseInlineMarkdown: () => parseInlineMarkdown,
@@ -34101,7 +35307,7 @@ function applyGroupOrdering(participants, groups, messages = []) {
34101
35307
  return result;
34102
35308
  }
34103
35309
  function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateToLine, options) {
34104
- d3Selection17.select(container).selectAll("*").remove();
35310
+ d3Selection18.select(container).selectAll("*").remove();
34105
35311
  const { title, options: parsedOptions } = parsed;
34106
35312
  const effectiveCollapsedGroups = /* @__PURE__ */ new Set();
34107
35313
  for (const group of parsed.groups) {
@@ -34122,9 +35328,6 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34122
35328
  const groups = collapsed ? collapsed.groups : parsed.groups;
34123
35329
  const collapsedGroupIds = collapsed?.collapsedGroupIds ?? /* @__PURE__ */ new Map();
34124
35330
  const collapsedSections = options?.collapsedSections;
34125
- const expandedNoteLines = options?.expandedNoteLines;
34126
- const collapseNotesDisabled = parsedOptions["collapse-notes"]?.toLowerCase() === "no";
34127
- const isNoteExpanded = (note) => expandedNoteLines === void 0 || collapseNotesDisabled || expandedNoteLines.has(note.lineNumber);
34128
35331
  const sourceParticipants = collapsed ? collapsed.participants : parsed.participants;
34129
35332
  const participants = applyPositionOverrides(
34130
35333
  applyGroupOrdering(sourceParticipants, groups, messages)
@@ -34293,7 +35496,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34293
35496
  const note = els[j];
34294
35497
  const sc = isNoteAfterSelfCall(note);
34295
35498
  const maxW = noteEffectiveMaxW(note.participantId, note.position, sc);
34296
- const noteH = isNoteExpanded(note) ? computeNoteHeight(note.text, charsForWidth(maxW)) : COLLAPSED_NOTE_H;
35499
+ const noteH = computeNoteHeight(note.text, charsForWidth(maxW));
34297
35500
  totalExtent += noteH + NOTE_OFFSET_BELOW;
34298
35501
  j++;
34299
35502
  }
@@ -34489,7 +35692,10 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34489
35692
  prevNote.position,
34490
35693
  isNoteAfterSelfCall(prevNote)
34491
35694
  );
34492
- const prevNoteH = isNoteExpanded(prevNote) ? computeNoteHeight(prevNote.text, charsForWidth(prevMaxW)) : COLLAPSED_NOTE_H;
35695
+ const prevNoteH = computeNoteHeight(
35696
+ prevNote.text,
35697
+ charsForWidth(prevMaxW)
35698
+ );
34493
35699
  noteTopY = prevNoteY + prevNoteH + NOTE_OFFSET_BELOW;
34494
35700
  } else {
34495
35701
  noteTopY = stepY(si) + noteOffsetBelow(el);
@@ -34520,7 +35726,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34520
35726
  note.position,
34521
35727
  isNoteAfterSelfCall(note)
34522
35728
  );
34523
- const noteH = isNoteExpanded(note) ? computeNoteHeight(note.text, charsForWidth(maxW)) : COLLAPSED_NOTE_H;
35729
+ const noteH = computeNoteHeight(note.text, charsForWidth(maxW));
34524
35730
  contentBottomY = Math.max(
34525
35731
  contentBottomY,
34526
35732
  noteTopY + noteH + NOTE_TRAILING_GAP
@@ -34542,7 +35748,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34542
35748
  participants.forEach((p, i) => {
34543
35749
  participantX.set(p.id, offsetX + i * PARTICIPANT_GAP);
34544
35750
  });
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);
35751
+ 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
35752
  const defs = svg.append("defs");
34547
35753
  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
35754
  "points",
@@ -34590,24 +35796,6 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34590
35796
  titleEl.attr("data-line-number", parsed.titleLineNumber);
34591
35797
  }
34592
35798
  }
34593
- const allNoteLineNumbers = [];
34594
- const collectNoteLines = (els) => {
34595
- for (const el of els) {
34596
- if (isSequenceNote(el)) {
34597
- allNoteLineNumbers.push(el.lineNumber);
34598
- } else if (isSequenceBlock(el)) {
34599
- collectNoteLines(el.children);
34600
- if ("elseChildren" in el) collectNoteLines(el.elseChildren);
34601
- if ("branches" in el && Array.isArray(el.branches)) {
34602
- for (const branch of el.branches) {
34603
- collectNoteLines(branch.children);
34604
- }
34605
- }
34606
- }
34607
- }
34608
- };
34609
- collectNoteLines(elements);
34610
- const showNotesControl = allNoteLineNumbers.length > 0 && !collapseNotesDisabled && expandedNoteLines !== void 0;
34611
35799
  const hasTagGroups = parsed.tagGroups.length > 0;
34612
35800
  const collapsedGroupNames = /* @__PURE__ */ new Set();
34613
35801
  const collapsedGroupMeta = /* @__PURE__ */ new Map();
@@ -34787,7 +35975,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34787
35975
  firstBranchStep = Math.min(firstBranchStep, first);
34788
35976
  }
34789
35977
  if (firstBranchStep < Infinity) {
34790
- const dividerY = stepY(firstBranchStep) - stepSpacing / 2;
35978
+ const dividerY = stepY(firstBranchStep) - BLOCK_HEADER_SPACE;
34791
35979
  deferredLines.push({
34792
35980
  x1: frameX,
34793
35981
  y1: dividerY,
@@ -34814,7 +36002,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
34814
36002
  firstElseStep = Math.min(firstElseStep, first);
34815
36003
  }
34816
36004
  if (firstElseStep < Infinity) {
34817
- const dividerY = stepY(firstElseStep) - stepSpacing / 2;
36005
+ const dividerY = stepY(firstElseStep) - BLOCK_HEADER_SPACE;
34818
36006
  deferredLines.push({
34819
36007
  x1: frameX,
34820
36008
  y1: dividerY,
@@ -35019,7 +36207,6 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
35019
36207
  }
35020
36208
  });
35021
36209
  const noteFill = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.bg, palette.surface, 15);
35022
- const collapsedNoteFill = mix(palette.textMuted, palette.bg, 15);
35023
36210
  const renderNoteElements = (els) => {
35024
36211
  for (const el of els) {
35025
36212
  if (isSequenceNote(el)) {
@@ -35027,83 +36214,54 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
35027
36214
  if (px === void 0) continue;
35028
36215
  const noteTopY = noteYMap.get(el);
35029
36216
  if (noteTopY === void 0) continue;
35030
- const expanded = isNoteExpanded(el);
35031
36217
  const isRight = el.position === "right";
35032
- if (expanded) {
35033
- const afterSelfCall = isNoteAfterSelfCall(el);
35034
- const maxW = noteEffectiveMaxW(
35035
- el.participantId,
35036
- el.position,
35037
- afterSelfCall
35038
- );
35039
- const maxChars = charsForWidth(maxW);
35040
- const wrappedLines = wrapTextLines(el.text, maxChars);
35041
- const noteH = wrappedLines.length * NOTE_LINE_H + NOTE_PAD_V * 2;
35042
- const maxLineLen = Math.max(...wrappedLines.map((l) => l.length));
35043
- const noteW = Math.min(
35044
- maxW,
35045
- Math.max(80, maxLineLen * NOTE_CHAR_W + NOTE_PAD_H * 2 + NOTE_FOLD)
35046
- );
35047
- const rightOffset = afterSelfCall && isRight ? ACTIVATION_WIDTH / 2 + SELF_CALL_WIDTH + NOTE_GAP : ACTIVATION_WIDTH + NOTE_GAP;
35048
- const noteX = isRight ? px + rightOffset : px - ACTIVATION_WIDTH - NOTE_GAP - noteW;
35049
- const noteG = svg.append("g").attr("class", "note").attr("data-note-toggle", "").attr("data-line-number", String(el.lineNumber)).attr("data-line-end", String(el.endLineNumber));
35050
- noteG.append("path").attr(
35051
- "d",
35052
- [
35053
- `M ${noteX} ${noteTopY}`,
35054
- `L ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
35055
- `L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`,
35056
- `L ${noteX + noteW} ${noteTopY + noteH}`,
35057
- `L ${noteX} ${noteTopY + noteH}`,
35058
- "Z"
35059
- ].join(" ")
35060
- ).attr("fill", noteFill).attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-box");
35061
- noteG.append("path").attr(
35062
- "d",
35063
- [
35064
- `M ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
35065
- `L ${noteX + noteW - NOTE_FOLD} ${noteTopY + NOTE_FOLD}`,
35066
- `L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`
35067
- ].join(" ")
35068
- ).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-fold");
35069
- wrappedLines.forEach((line11, li) => {
35070
- const textY = noteTopY + NOTE_PAD_V + (li + 1) * NOTE_LINE_H - 3;
35071
- const isBullet = line11.startsWith("- ");
35072
- const bulletIndent = isBullet ? 10 : 0;
35073
- const displayLine = isBullet ? line11.slice(2) : line11;
35074
- const textEl = noteG.append("text").attr("x", noteX + NOTE_PAD_H + bulletIndent).attr("y", textY).attr("fill", palette.text).attr("font-size", NOTE_FONT_SIZE).attr("class", "note-text");
35075
- if (isBullet) {
35076
- noteG.append("text").attr("x", noteX + NOTE_PAD_H).attr("y", textY).attr("fill", palette.text).attr("font-size", NOTE_FONT_SIZE).text("\u2022");
35077
- }
35078
- renderInlineText(textEl, displayLine, palette, NOTE_FONT_SIZE);
35079
- });
35080
- } else {
35081
- const cFold = 6;
35082
- const afterSelfCallC = isNoteAfterSelfCall(el);
35083
- const rightOffsetC = afterSelfCallC && isRight ? ACTIVATION_WIDTH / 2 + SELF_CALL_WIDTH + NOTE_GAP : ACTIVATION_WIDTH + NOTE_GAP;
35084
- const noteX = isRight ? px + rightOffsetC : px - ACTIVATION_WIDTH - NOTE_GAP - COLLAPSED_NOTE_W;
35085
- const noteG = svg.append("g").attr("class", "note note-collapsed").attr("data-note-toggle", "").attr("data-line-number", String(el.lineNumber)).attr("data-line-end", String(el.endLineNumber)).style("cursor", "pointer");
35086
- noteG.append("path").attr(
35087
- "d",
35088
- [
35089
- `M ${noteX} ${noteTopY}`,
35090
- `L ${noteX + COLLAPSED_NOTE_W - cFold} ${noteTopY}`,
35091
- `L ${noteX + COLLAPSED_NOTE_W} ${noteTopY + cFold}`,
35092
- `L ${noteX + COLLAPSED_NOTE_W} ${noteTopY + COLLAPSED_NOTE_H}`,
35093
- `L ${noteX} ${noteTopY + COLLAPSED_NOTE_H}`,
35094
- "Z"
35095
- ].join(" ")
35096
- ).attr("fill", collapsedNoteFill).attr("stroke", palette.border).attr("stroke-width", 0.75).attr("class", "note-box");
35097
- noteG.append("path").attr(
35098
- "d",
35099
- [
35100
- `M ${noteX + COLLAPSED_NOTE_W - cFold} ${noteTopY}`,
35101
- `L ${noteX + COLLAPSED_NOTE_W - cFold} ${noteTopY + cFold}`,
35102
- `L ${noteX + COLLAPSED_NOTE_W} ${noteTopY + cFold}`
35103
- ].join(" ")
35104
- ).attr("fill", "none").attr("stroke", palette.border).attr("stroke-width", 0.75).attr("class", "note-fold");
35105
- noteG.append("text").attr("x", noteX + COLLAPSED_NOTE_W / 2).attr("y", noteTopY + COLLAPSED_NOTE_H / 2 + 3).attr("text-anchor", "middle").attr("fill", palette.textMuted).attr("font-size", 9).attr("class", "note-text").text("\u2026");
35106
- }
36218
+ const afterSelfCall = isNoteAfterSelfCall(el);
36219
+ const maxW = noteEffectiveMaxW(
36220
+ el.participantId,
36221
+ el.position,
36222
+ afterSelfCall
36223
+ );
36224
+ const maxChars = charsForWidth(maxW);
36225
+ const wrappedLines = wrapTextLines(el.text, maxChars);
36226
+ const noteH = wrappedLines.length * NOTE_LINE_H + NOTE_PAD_V * 2;
36227
+ const maxLineLen = Math.max(...wrappedLines.map((l) => l.length));
36228
+ const noteW = Math.min(
36229
+ maxW,
36230
+ Math.max(80, maxLineLen * NOTE_CHAR_W + NOTE_PAD_H * 2 + NOTE_FOLD)
36231
+ );
36232
+ const rightOffset = afterSelfCall && isRight ? ACTIVATION_WIDTH / 2 + SELF_CALL_WIDTH + NOTE_GAP : ACTIVATION_WIDTH + NOTE_GAP;
36233
+ const noteX = isRight ? px + rightOffset : px - ACTIVATION_WIDTH - NOTE_GAP - noteW;
36234
+ const noteG = svg.append("g").attr("class", "note").attr("data-note-toggle", "").attr("data-line-number", String(el.lineNumber)).attr("data-line-end", String(el.endLineNumber));
36235
+ noteG.append("path").attr(
36236
+ "d",
36237
+ [
36238
+ `M ${noteX} ${noteTopY}`,
36239
+ `L ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
36240
+ `L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`,
36241
+ `L ${noteX + noteW} ${noteTopY + noteH}`,
36242
+ `L ${noteX} ${noteTopY + noteH}`,
36243
+ "Z"
36244
+ ].join(" ")
36245
+ ).attr("fill", noteFill).attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-box");
36246
+ noteG.append("path").attr(
36247
+ "d",
36248
+ [
36249
+ `M ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
36250
+ `L ${noteX + noteW - NOTE_FOLD} ${noteTopY + NOTE_FOLD}`,
36251
+ `L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`
36252
+ ].join(" ")
36253
+ ).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-fold");
36254
+ wrappedLines.forEach((line11, li) => {
36255
+ const textY = noteTopY + NOTE_PAD_V + (li + 1) * NOTE_LINE_H - 3;
36256
+ const isBullet = line11.startsWith("- ");
36257
+ const bulletIndent = isBullet ? 10 : 0;
36258
+ const displayLine = isBullet ? line11.slice(2) : line11;
36259
+ const textEl = noteG.append("text").attr("x", noteX + NOTE_PAD_H + bulletIndent).attr("y", textY).attr("fill", palette.text).attr("font-size", NOTE_FONT_SIZE).attr("class", "note-text");
36260
+ if (isBullet) {
36261
+ noteG.append("text").attr("x", noteX + NOTE_PAD_H).attr("y", textY).attr("fill", palette.text).attr("font-size", NOTE_FONT_SIZE).text("\u2022");
36262
+ }
36263
+ renderInlineText(textEl, displayLine, palette, NOTE_FONT_SIZE);
36264
+ });
35107
36265
  } else if (isSequenceBlock(el)) {
35108
36266
  renderNoteElements(el.children);
35109
36267
  if (el.elseIfBranches) {
@@ -35118,8 +36276,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
35118
36276
  if (elements && elements.length > 0) {
35119
36277
  renderNoteElements(elements);
35120
36278
  }
35121
- if (hasTagGroups || showNotesControl) {
35122
- const controlsExpanded = options?.controlsExpanded ?? false;
36279
+ if (hasTagGroups) {
35123
36280
  const legendY = TOP_MARGIN + titleOffset;
35124
36281
  const resolvedGroups = parsed.tagGroups.filter((tg) => tg.entries.length > 0).map((tg) => ({
35125
36282
  name: tg.name,
@@ -35128,37 +36285,16 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
35128
36285
  color: e.color
35129
36286
  }))
35130
36287
  }));
35131
- const allExpanded = showNotesControl && (options?.expandAllNotes ?? false);
35132
- const controlsGroup = showNotesControl ? {
35133
- toggles: [
35134
- {
35135
- id: "expand-all-notes",
35136
- type: "toggle",
35137
- label: "Expand Notes",
35138
- active: allExpanded,
35139
- onToggle: () => {
35140
- }
35141
- }
35142
- ]
35143
- } : void 0;
35144
36288
  const legendConfig = {
35145
36289
  groups: resolvedGroups,
35146
36290
  position: { placement: "top-center", titleRelation: "below-title" },
35147
- mode: "fixed",
35148
- controlsGroup
36291
+ mode: "fixed"
35149
36292
  };
35150
36293
  const legendState = {
35151
36294
  activeGroup: activeTagGroup ?? null,
35152
- controlsExpanded
35153
- };
35154
- const legendCallbacks = {
35155
- onControlsExpand: () => {
35156
- options?.onToggleControlsExpand?.();
35157
- },
35158
- onControlsToggle: (_toggleId, active) => {
35159
- options?.onExpandAllNotes?.(active);
35160
- }
36295
+ controlsExpanded: false
35161
36296
  };
36297
+ const legendCallbacks = {};
35162
36298
  const legendG = svg.append("g").attr("class", "sequence-legend").attr("transform", `translate(0,${legendY})`);
35163
36299
  renderLegendD3(
35164
36300
  legendG,
@@ -35197,26 +36333,6 @@ function buildNoteMessageMap(elements) {
35197
36333
  walk(elements);
35198
36334
  return map;
35199
36335
  }
35200
- function collectNoteLineNumbers(elements) {
35201
- const result = [];
35202
- const walk = (els) => {
35203
- for (const el of els) {
35204
- if (isSequenceNote(el)) {
35205
- result.push(el.lineNumber);
35206
- } else if (isSequenceBlock(el)) {
35207
- walk(el.children);
35208
- if (el.elseIfBranches) {
35209
- for (const branch of el.elseIfBranches) {
35210
- walk(branch.children);
35211
- }
35212
- }
35213
- walk(el.elseChildren);
35214
- }
35215
- }
35216
- };
35217
- walk(elements);
35218
- return result;
35219
- }
35220
36336
  function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tagAttr) {
35221
36337
  const g = svg.append("g").attr("transform", `translate(${cx}, ${cy})`).attr("class", "participant").attr("data-participant-id", participant.id);
35222
36338
  if (tagAttr) {
@@ -35272,11 +36388,11 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tag
35272
36388
  });
35273
36389
  }
35274
36390
  }
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({
36391
+ 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, ACTIVATION_WIDTH, SELF_CALL_HEIGHT, SELF_CALL_WIDTH, NOTE_LANE_MAX, LABEL_CHAR_WIDTH, LABEL_MAX_CHARS, fill, stroke, SW, W, H;
36392
+ var init_renderer16 = __esm({
35277
36393
  "src/sequence/renderer.ts"() {
35278
36394
  "use strict";
35279
- d3Selection17 = __toESM(require("d3-selection"), 1);
36395
+ d3Selection18 = __toESM(require("d3-selection"), 1);
35280
36396
  init_color_utils();
35281
36397
  init_inline_markdown();
35282
36398
  init_fonts();
@@ -35308,8 +36424,6 @@ var init_renderer15 = __esm({
35308
36424
  NOTE_CHARS_PER_LINE = Math.floor(
35309
36425
  (NOTE_MAX_W - NOTE_PAD_H * 2 - NOTE_FOLD) / NOTE_CHAR_W
35310
36426
  );
35311
- COLLAPSED_NOTE_H = 20;
35312
- COLLAPSED_NOTE_W = 40;
35313
36427
  ACTIVATION_WIDTH = 10;
35314
36428
  SELF_CALL_HEIGHT = 25;
35315
36429
  SELF_CALL_WIDTH = 30;
@@ -35334,15 +36448,15 @@ function renderChartTitle(svg, title, titleLineNumber, width, textColor, onClick
35334
36448
  titleEl.attr("data-line-number", titleLineNumber);
35335
36449
  if (onClickItem) {
35336
36450
  titleEl.on("click", () => onClickItem(titleLineNumber)).on("mouseenter", function() {
35337
- d3Selection18.select(this).attr("opacity", 0.7);
36451
+ d3Selection19.select(this).attr("opacity", 0.7);
35338
36452
  }).on("mouseleave", function() {
35339
- d3Selection18.select(this).attr("opacity", 1);
36453
+ d3Selection19.select(this).attr("opacity", 1);
35340
36454
  });
35341
36455
  }
35342
36456
  }
35343
36457
  }
35344
36458
  function initD3Chart(container, palette, exportDims) {
35345
- d3Selection18.select(container).selectAll(":not([data-d3-tooltip])").remove();
36459
+ d3Selection19.select(container).selectAll(":not([data-d3-tooltip])").remove();
35346
36460
  const width = exportDims?.width ?? container.clientWidth;
35347
36461
  const height = exportDims?.height ?? container.clientHeight;
35348
36462
  if (width <= 0 || height <= 0) return null;
@@ -35350,7 +36464,7 @@ function initD3Chart(container, palette, exportDims) {
35350
36464
  const mutedColor = palette.border;
35351
36465
  const bgColor = palette.bg;
35352
36466
  const colors = getSeriesColors(palette);
35353
- const svg = d3Selection18.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
36467
+ const svg = d3Selection19.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
35354
36468
  return { svg, width, height, textColor, mutedColor, bgColor, colors };
35355
36469
  }
35356
36470
  function parseTimelineDate(s) {
@@ -36275,7 +37389,12 @@ function parseVisualization(content, palette) {
36275
37389
  );
36276
37390
  validateTagGroupNames(
36277
37391
  result.timelineTagGroups,
36278
- (line11, msg) => result.diagnostics.push(makeDgmoError(line11, msg, "warning"))
37392
+ (line11, msg) => result.diagnostics.push(makeDgmoError(line11, msg, "warning")),
37393
+ (line11, msg) => {
37394
+ const diag = makeDgmoError(line11, msg);
37395
+ result.diagnostics.push(diag);
37396
+ if (!result.error) result.error = formatDgmoError(diag);
37397
+ }
36279
37398
  );
36280
37399
  for (const group of result.timelineTagGroups) {
36281
37400
  if (!group.defaultValue) continue;
@@ -36693,14 +37812,14 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
36693
37812
  function handleMouseEnter(hovered) {
36694
37813
  const connected = neighbors.get(hovered);
36695
37814
  g.selectAll(".arc-link").each(function() {
36696
- const el = d3Selection18.select(this);
37815
+ const el = d3Selection19.select(this);
36697
37816
  const src = el.attr("data-source");
36698
37817
  const tgt = el.attr("data-target");
36699
37818
  const isRelated = src === hovered || tgt === hovered;
36700
37819
  el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY2);
36701
37820
  });
36702
37821
  g.selectAll(".arc-node").each(function() {
36703
- const el = d3Selection18.select(this);
37822
+ const el = d3Selection19.select(this);
36704
37823
  const name = el.attr("data-node");
36705
37824
  const isRelated = name === hovered || connected.has(name);
36706
37825
  el.attr("opacity", isRelated ? 1 : FADE_OPACITY2);
@@ -36725,23 +37844,23 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
36725
37844
  const members = groupNodeSets.get(groupName);
36726
37845
  if (!members) return;
36727
37846
  g.selectAll(".arc-link").each(function() {
36728
- const el = d3Selection18.select(this);
37847
+ const el = d3Selection19.select(this);
36729
37848
  const isRelated = members.has(el.attr("data-source")) || members.has(el.attr("data-target"));
36730
37849
  el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY2);
36731
37850
  });
36732
37851
  g.selectAll(".arc-node").each(function() {
36733
- const el = d3Selection18.select(this);
37852
+ const el = d3Selection19.select(this);
36734
37853
  el.attr("opacity", members.has(el.attr("data-node")) ? 1 : FADE_OPACITY2);
36735
37854
  });
36736
37855
  g.selectAll(".arc-group-band").each(function() {
36737
- const el = d3Selection18.select(this);
37856
+ const el = d3Selection19.select(this);
36738
37857
  el.attr(
36739
37858
  "fill-opacity",
36740
37859
  el.attr("data-group") === groupName ? 0.18 : 0.03
36741
37860
  );
36742
37861
  });
36743
37862
  g.selectAll(".arc-group-label").each(function() {
36744
- const el = d3Selection18.select(this);
37863
+ const el = d3Selection19.select(this);
36745
37864
  el.attr("fill-opacity", el.attr("data-group") === groupName ? 1 : 0.2);
36746
37865
  });
36747
37866
  }
@@ -36999,7 +38118,7 @@ function showEventDatesOnScale(g, scale, startDate, endDate, innerHeight, accent
36999
38118
  function hideEventDatesOnScale(g) {
37000
38119
  g.selectAll(".tl-event-date").remove();
37001
38120
  g.selectAll(".tl-scale-tick").each(function() {
37002
- const el = d3Selection18.select(this);
38121
+ const el = d3Selection19.select(this);
37003
38122
  const isDashed = el.attr("stroke-dasharray");
37004
38123
  el.attr("opacity", isDashed ? 0.15 : 0.4);
37005
38124
  });
@@ -37077,7 +38196,7 @@ function renderTimelineGroupLegend(g, groups, groupColorMap, textColor, palette,
37077
38196
  }
37078
38197
  }
37079
38198
  function renderTimeline(container, parsed, palette, isDark, onClickItem, exportDims, activeTagGroup, swimlaneTagGroup, onTagStateChange, viewMode) {
37080
- d3Selection18.select(container).selectAll(":not([data-d3-tooltip])").remove();
38199
+ d3Selection19.select(container).selectAll(":not([data-d3-tooltip])").remove();
37081
38200
  const {
37082
38201
  timelineEvents,
37083
38202
  timelineGroups,
@@ -37175,13 +38294,13 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37175
38294
  const FADE_OPACITY2 = 0.1;
37176
38295
  function fadeToGroup(g, groupName) {
37177
38296
  g.selectAll(".tl-event").each(function() {
37178
- const el = d3Selection18.select(this);
38297
+ const el = d3Selection19.select(this);
37179
38298
  const evGroup = el.attr("data-group");
37180
38299
  el.attr("opacity", evGroup === groupName ? 1 : FADE_OPACITY2);
37181
38300
  });
37182
38301
  g.selectAll(".tl-legend-item, .tl-lane-header").each(
37183
38302
  function() {
37184
- const el = d3Selection18.select(this);
38303
+ const el = d3Selection19.select(this);
37185
38304
  const name = el.attr("data-group");
37186
38305
  el.attr("opacity", name === groupName ? 1 : FADE_OPACITY2);
37187
38306
  }
@@ -37193,7 +38312,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37193
38312
  }
37194
38313
  function fadeToEra(g, eraStart, eraEnd) {
37195
38314
  g.selectAll(".tl-event").each(function() {
37196
- const el = d3Selection18.select(this);
38315
+ const el = d3Selection19.select(this);
37197
38316
  const date = parseFloat(el.attr("data-date"));
37198
38317
  const endDate = el.attr("data-end-date");
37199
38318
  const evEnd = endDate ? parseFloat(endDate) : date;
@@ -37205,14 +38324,14 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37205
38324
  FADE_OPACITY2
37206
38325
  );
37207
38326
  g.selectAll(".tl-era").each(function() {
37208
- const el = d3Selection18.select(this);
38327
+ const el = d3Selection19.select(this);
37209
38328
  const s = parseFloat(el.attr("data-era-start"));
37210
38329
  const e = parseFloat(el.attr("data-era-end"));
37211
38330
  const isSelf = s === eraStart && e === eraEnd;
37212
38331
  el.attr("opacity", isSelf ? 1 : FADE_OPACITY2);
37213
38332
  });
37214
38333
  g.selectAll(".tl-marker").each(function() {
37215
- const el = d3Selection18.select(this);
38334
+ const el = d3Selection19.select(this);
37216
38335
  const date = parseFloat(el.attr("data-marker-date"));
37217
38336
  const inside = date >= eraStart && date <= eraEnd;
37218
38337
  el.attr("opacity", inside ? 1 : FADE_OPACITY2);
@@ -37227,7 +38346,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37227
38346
  function fadeToTagValue(g, tagKey, tagValue) {
37228
38347
  const attrName = `data-tag-${tagKey}`;
37229
38348
  g.selectAll(".tl-event").each(function() {
37230
- const el = d3Selection18.select(this);
38349
+ const el = d3Selection19.select(this);
37231
38350
  const val = el.attr(attrName);
37232
38351
  el.attr("opacity", val === tagValue ? 1 : FADE_OPACITY2);
37233
38352
  });
@@ -37240,7 +38359,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37240
38359
  FADE_OPACITY2
37241
38360
  );
37242
38361
  g.selectAll(".tl-tag-legend-entry").each(function() {
37243
- const el = d3Selection18.select(this);
38362
+ const el = d3Selection19.select(this);
37244
38363
  const entryValue = el.attr("data-legend-entry");
37245
38364
  if (entryValue === "__group__") return;
37246
38365
  const entryGroup = el.attr("data-tag-group");
@@ -37292,7 +38411,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37292
38411
  const innerHeight = height - margin.top - margin.bottom;
37293
38412
  const laneWidth = innerWidth / laneCount;
37294
38413
  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);
38414
+ 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
38415
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
37297
38416
  renderChartTitle(
37298
38417
  svg,
@@ -37382,7 +38501,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37382
38501
  const gradientId = `uncertain-vg-${ev.lineNumber}`;
37383
38502
  const strokeGradientId = `uncertain-vg-s-${ev.lineNumber}`;
37384
38503
  const defs = svg.select("defs").node() || svg.append("defs").node();
37385
- const defsEl = d3Selection18.select(defs);
38504
+ const defsEl = d3Selection19.select(defs);
37386
38505
  defsEl.append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "0%").attr("y2", "100%").selectAll("stop").data([
37387
38506
  { offset: "0%", opacity: 1 },
37388
38507
  { offset: "80%", opacity: 1 },
@@ -37418,7 +38537,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37418
38537
  const axisX = 20;
37419
38538
  const yScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
37420
38539
  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);
38540
+ 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
38541
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
37423
38542
  renderChartTitle(
37424
38543
  svg,
@@ -37507,7 +38626,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37507
38626
  const gradientId = `uncertain-v-${ev.lineNumber}`;
37508
38627
  const strokeGradientId = `uncertain-v-s-${ev.lineNumber}`;
37509
38628
  const defs = svg.select("defs").node() || svg.append("defs").node();
37510
- const defsEl = d3Selection18.select(defs);
38629
+ const defsEl = d3Selection19.select(defs);
37511
38630
  defsEl.append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "0%").attr("y2", "100%").selectAll("stop").data([
37512
38631
  { offset: "0%", opacity: 1 },
37513
38632
  { offset: "80%", opacity: 1 },
@@ -37575,7 +38694,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37575
38694
  const totalGaps = (lanes.length - 1) * GROUP_GAP3;
37576
38695
  const rowH = Math.min(28, (innerHeight - totalGaps) / totalEventRows);
37577
38696
  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);
38697
+ const svg = d3Selection19.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
37579
38698
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
37580
38699
  renderChartTitle(
37581
38700
  svg,
@@ -37688,7 +38807,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37688
38807
  const gradientId = `uncertain-${ev.lineNumber}`;
37689
38808
  const strokeGradientId = `uncertain-s-${ev.lineNumber}`;
37690
38809
  const defs = svg.select("defs").node() || svg.append("defs").node();
37691
- const defsEl = d3Selection18.select(defs);
38810
+ const defsEl = d3Selection19.select(defs);
37692
38811
  defsEl.append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
37693
38812
  { offset: "0%", opacity: 1 },
37694
38813
  { offset: "80%", opacity: 1 },
@@ -37736,7 +38855,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37736
38855
  const innerHeight = height - margin.top - margin.bottom;
37737
38856
  const rowH = Math.min(28, innerHeight / sorted.length);
37738
38857
  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);
38858
+ const svg = d3Selection19.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
37740
38859
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
37741
38860
  renderChartTitle(
37742
38861
  svg,
@@ -37845,7 +38964,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37845
38964
  const gradientId = `uncertain-ts-${ev.lineNumber}`;
37846
38965
  const strokeGradientId = `uncertain-ts-s-${ev.lineNumber}`;
37847
38966
  const defs = svg.select("defs").node() || svg.append("defs").node();
37848
- const defsEl = d3Selection18.select(defs);
38967
+ const defsEl = d3Selection19.select(defs);
37849
38968
  defsEl.append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
37850
38969
  { offset: "0%", opacity: 1 },
37851
38970
  { offset: "80%", opacity: 1 },
@@ -37888,7 +39007,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37888
39007
  const LG_ENTRY_DOT_GAP = LEGEND_ENTRY_DOT_GAP;
37889
39008
  const LG_ENTRY_TRAIL = LEGEND_ENTRY_TRAIL;
37890
39009
  const LG_ICON_W = 20;
37891
- const mainSvg = d3Selection18.select(container).select("svg");
39010
+ const mainSvg = d3Selection19.select(container).select("svg");
37892
39011
  const mainG = mainSvg.select("g");
37893
39012
  if (!mainSvg.empty() && !mainG.empty()) {
37894
39013
  let drawSwimlaneIcon4 = function(parent, x, y, isSwimActive) {
@@ -37961,7 +39080,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
37961
39080
  const tagVal = entryValue.toLowerCase();
37962
39081
  fadeToTagValue(mainG, tagKey, tagVal);
37963
39082
  mainSvg.selectAll("[data-legend-entry]").each(function() {
37964
- const el = d3Selection18.select(this);
39083
+ const el = d3Selection19.select(this);
37965
39084
  const ev = el.attr("data-legend-entry");
37966
39085
  const eg = el.attr("data-tag-group") ?? el.node()?.closest?.("[data-tag-group]")?.getAttribute("data-tag-group");
37967
39086
  el.attr(
@@ -38014,7 +39133,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
38014
39133
  }, recolorEvents2 = function() {
38015
39134
  const colorTG = currentActiveGroup ?? swimlaneTagGroup ?? null;
38016
39135
  mainG.selectAll(".tl-event").each(function() {
38017
- const el = d3Selection18.select(this);
39136
+ const el = d3Selection19.select(this);
38018
39137
  const lineNum = el.attr("data-line-number");
38019
39138
  const ev = lineNum ? eventByLine.get(lineNum) : void 0;
38020
39139
  if (!ev) return;
@@ -38112,7 +39231,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
38112
39231
  }
38113
39232
  function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
38114
39233
  return new Promise((resolve) => {
38115
- d3Selection18.select(container).selectAll(":not([data-d3-tooltip])").remove();
39234
+ d3Selection19.select(container).selectAll(":not([data-d3-tooltip])").remove();
38116
39235
  const { words, title, cloudOptions } = parsed;
38117
39236
  if (words.length === 0) {
38118
39237
  resolve();
@@ -38139,7 +39258,7 @@ function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
38139
39258
  return minSize + Math.sqrt(t) * (maxSize - minSize);
38140
39259
  };
38141
39260
  const rotateFn = getRotateFn(cloudOptions.rotate);
38142
- const svg = d3Selection18.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
39261
+ const svg = d3Selection19.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
38143
39262
  renderChartTitle(svg, title, parsed.titleLineNumber, width, textColor);
38144
39263
  const g = svg.append("g").attr(
38145
39264
  "transform",
@@ -38641,8 +39760,8 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38641
39760
  const LABEL_MAX_FONT = 48;
38642
39761
  const LABEL_MIN_FONT = 14;
38643
39762
  const LABEL_PAD3 = 40;
38644
- const CHAR_WIDTH_RATIO4 = 0.6;
38645
- const estTextWidth = (text, fontSize) => text.length * fontSize * CHAR_WIDTH_RATIO4;
39763
+ const CHAR_WIDTH_RATIO5 = 0.6;
39764
+ const estTextWidth = (text, fontSize) => text.length * fontSize * CHAR_WIDTH_RATIO5;
38646
39765
  const quadrantLabelLayout = (text, qw2, qh2) => {
38647
39766
  const availW = qw2 - LABEL_PAD3;
38648
39767
  const availH = qh2 - LABEL_PAD3;
@@ -38705,7 +39824,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38705
39824
  (d) => onClickItem && d.label?.lineNumber ? "pointer" : "default"
38706
39825
  ).each(function(d) {
38707
39826
  const layout = labelLayouts.get(d.label.text);
38708
- const el = d3Selection18.select(this);
39827
+ const el = d3Selection19.select(this);
38709
39828
  if (layout.lines.length === 1) {
38710
39829
  el.text(layout.lines[0]);
38711
39830
  } else {
@@ -38721,9 +39840,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38721
39840
  quadrantLabelTexts.on("click", (_, d) => {
38722
39841
  if (d.label?.lineNumber) onClickItem(d.label.lineNumber);
38723
39842
  }).on("mouseenter", function() {
38724
- d3Selection18.select(this).attr("opacity", 0.7);
39843
+ d3Selection19.select(this).attr("opacity", 0.7);
38725
39844
  }).on("mouseleave", function() {
38726
- d3Selection18.select(this).attr("opacity", 1);
39845
+ d3Selection19.select(this).attr("opacity", 1);
38727
39846
  });
38728
39847
  }
38729
39848
  if (quadrantXAxis) {
@@ -38744,9 +39863,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38744
39863
  if (onClickItem && quadrantXAxisLineNumber) {
38745
39864
  [xLowLabel, xHighLabel].forEach((label) => {
38746
39865
  label.on("click", () => onClickItem(quadrantXAxisLineNumber)).on("mouseenter", function() {
38747
- d3Selection18.select(this).attr("opacity", 0.7);
39866
+ d3Selection19.select(this).attr("opacity", 0.7);
38748
39867
  }).on("mouseleave", function() {
38749
- d3Selection18.select(this).attr("opacity", 1);
39868
+ d3Selection19.select(this).attr("opacity", 1);
38750
39869
  });
38751
39870
  });
38752
39871
  }
@@ -38771,9 +39890,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38771
39890
  if (onClickItem && quadrantYAxisLineNumber) {
38772
39891
  [yLowLabel, yHighLabel].forEach((label) => {
38773
39892
  label.on("click", () => onClickItem(quadrantYAxisLineNumber)).on("mouseenter", function() {
38774
- d3Selection18.select(this).attr("opacity", 0.7);
39893
+ d3Selection19.select(this).attr("opacity", 0.7);
38775
39894
  }).on("mouseleave", function() {
38776
- d3Selection18.select(this).attr("opacity", 1);
39895
+ d3Selection19.select(this).attr("opacity", 1);
38777
39896
  });
38778
39897
  });
38779
39898
  }
@@ -38790,7 +39909,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38790
39909
  const POINT_LABEL_FONT_SIZE = 12;
38791
39910
  const quadrantLabelObstacles = quadrantDefsWithLabel.map((d) => {
38792
39911
  const layout = labelLayouts.get(d.label.text);
38793
- const totalW = Math.max(...layout.lines.map((l) => l.length)) * layout.fontSize * CHAR_WIDTH_RATIO4;
39912
+ const totalW = Math.max(...layout.lines.map((l) => l.length)) * layout.fontSize * CHAR_WIDTH_RATIO5;
38794
39913
  const totalH = layout.lines.length * layout.fontSize * 1.2;
38795
39914
  return {
38796
39915
  x: d.labelX - totalW / 2,
@@ -38850,7 +39969,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
38850
39969
  pointsG.selectAll("g.point-group").each(function(_2, i) {
38851
39970
  const pt = quadrantPoints[i];
38852
39971
  const ptQuad = getPointQuadrant(pt.x, pt.y);
38853
- d3Selection18.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
39972
+ d3Selection19.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
38854
39973
  });
38855
39974
  }).on("mouseleave", () => {
38856
39975
  quadrantRects.attr("opacity", 1);
@@ -39369,6 +40488,22 @@ async function renderForExport(content, theme, palette, viewState, options) {
39369
40488
  );
39370
40489
  return finalizeSvgExport(container2, theme, effectivePalette2);
39371
40490
  }
40491
+ if (detectedType === "pyramid") {
40492
+ const { parsePyramid: parsePyramid2 } = await Promise.resolve().then(() => (init_parser16(), parser_exports16));
40493
+ const { renderPyramidForExport: renderPyramidForExport2 } = await Promise.resolve().then(() => (init_renderer15(), renderer_exports15));
40494
+ const effectivePalette2 = await resolveExportPalette(theme, palette);
40495
+ const pyramidParsed = parsePyramid2(content);
40496
+ if (pyramidParsed.error || pyramidParsed.layers.length === 0) return "";
40497
+ const container2 = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
40498
+ renderPyramidForExport2(
40499
+ container2,
40500
+ pyramidParsed,
40501
+ effectivePalette2,
40502
+ theme === "dark",
40503
+ { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
40504
+ );
40505
+ return finalizeSvgExport(container2, theme, effectivePalette2);
40506
+ }
39372
40507
  const parsed = parseVisualization(content, palette);
39373
40508
  if (parsed.error && parsed.type !== "sequence") {
39374
40509
  const looksLikeSequence2 = /->|~>|<-/.test(content);
@@ -39392,7 +40527,7 @@ async function renderForExport(content, theme, palette, viewState, options) {
39392
40527
  };
39393
40528
  if (parsed.type === "sequence") {
39394
40529
  const { parseSequenceDgmo: parseSequenceDgmo2 } = await Promise.resolve().then(() => (init_parser(), parser_exports));
39395
- const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer15(), renderer_exports15));
40530
+ const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer16(), renderer_exports16));
39396
40531
  const seqParsed = parseSequenceDgmo2(content);
39397
40532
  if (seqParsed.error || seqParsed.participants.length === 0) return "";
39398
40533
  renderSequenceDiagram2(
@@ -39461,12 +40596,12 @@ async function renderForExport(content, theme, palette, viewState, options) {
39461
40596
  }
39462
40597
  return finalizeSvgExport(container, theme, effectivePalette);
39463
40598
  }
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;
40599
+ 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
40600
  var init_d3 = __esm({
39466
40601
  "src/d3.ts"() {
39467
40602
  "use strict";
39468
40603
  d3Scale2 = __toESM(require("d3-scale"), 1);
39469
- d3Selection18 = __toESM(require("d3-selection"), 1);
40604
+ d3Selection19 = __toESM(require("d3-selection"), 1);
39470
40605
  d3Shape10 = __toESM(require("d3-shape"), 1);
39471
40606
  d3Array = __toESM(require("d3-array"), 1);
39472
40607
  import_d3_cloud = __toESM(require("d3-cloud"), 1);
@@ -40069,13 +41204,16 @@ var require_lz_string = __commonJS({
40069
41204
  var index_exports = {};
40070
41205
  __export(index_exports, {
40071
41206
  ALL_CHART_TYPES: () => ALL_CHART_TYPES,
41207
+ AMBIGUITY_THRESHOLD: () => AMBIGUITY_THRESHOLD,
40072
41208
  ARROW_DIAGNOSTIC_CODES: () => ARROW_DIAGNOSTIC_CODES,
40073
41209
  CHART_TYPES: () => CHART_TYPES,
41210
+ CHART_TYPE_DESCRIPTIONS: () => CHART_TYPE_DESCRIPTIONS,
40074
41211
  COMPLETION_REGISTRY: () => COMPLETION_REGISTRY,
40075
41212
  ENTITY_TYPES: () => ENTITY_TYPES,
40076
41213
  INFRA_BEHAVIOR_KEYS: () => INFRA_BEHAVIOR_KEYS,
40077
41214
  LEGEND_HEIGHT: () => LEGEND_HEIGHT,
40078
41215
  METADATA_KEY_SET: () => METADATA_KEY_SET,
41216
+ MIN_PRIMARY_SCORE: () => MIN_PRIMARY_SCORE,
40079
41217
  PIPE_METADATA: () => PIPE_METADATA,
40080
41218
  RECOGNIZED_COLOR_NAMES: () => RECOGNIZED_COLOR_NAMES,
40081
41219
  RULE_COUNT: () => RULE_COUNT,
@@ -40091,13 +41229,15 @@ __export(index_exports, {
40091
41229
  buildTagLaneRowList: () => buildTagLaneRowList,
40092
41230
  calculateSchedule: () => calculateSchedule,
40093
41231
  catppuccinPalette: () => catppuccinPalette,
41232
+ chartTypeConfidence: () => confidence,
41233
+ chartTypeParsers: () => chartTypeParsers,
41234
+ chartTypes: () => chartTypes,
40094
41235
  collapseBoxesAndLines: () => collapseBoxesAndLines,
40095
41236
  collapseMindmapTree: () => collapseMindmapTree,
40096
41237
  collapseOrgTree: () => collapseOrgTree,
40097
41238
  collapseSitemapTree: () => collapseSitemapTree,
40098
41239
  collapseStateGroups: () => collapseStateGroups,
40099
41240
  collectDiagramRoles: () => collectDiagramRoles,
40100
- collectNoteLineNumbers: () => collectNoteLineNumbers,
40101
41241
  collectTasks: () => collectTasks,
40102
41242
  colorNames: () => colorNames,
40103
41243
  computeActivations: () => computeActivations,
@@ -40118,8 +41258,10 @@ __export(index_exports, {
40118
41258
  encodeViewState: () => encodeViewState,
40119
41259
  extractDiagramSymbols: () => extractDiagramSymbols,
40120
41260
  extractTagDeclarations: () => extractTagDeclarations,
41261
+ focusOrgTree: () => focusOrgTree,
40121
41262
  formatDateLabel: () => formatDateLabel,
40122
41263
  formatDgmoError: () => formatDgmoError,
41264
+ getAllChartTypes: () => getAllChartTypes,
40123
41265
  getAvailablePalettes: () => getAvailablePalettes,
40124
41266
  getExtendedChartLegendGroups: () => getExtendedChartLegendGroups,
40125
41267
  getLegendReservedHeight: () => getLegendReservedHeight,
@@ -40141,6 +41283,7 @@ __export(index_exports, {
40141
41283
  isSequenceBlock: () => isSequenceBlock,
40142
41284
  isSequenceNote: () => isSequenceNote,
40143
41285
  isValidHex: () => isValidHex,
41286
+ knownChartTypeIds: () => knownChartTypeIds,
40144
41287
  layoutBoxesAndLines: () => layoutBoxesAndLines,
40145
41288
  layoutC4Components: () => layoutC4Components,
40146
41289
  layoutC4Containers: () => layoutC4Containers,
@@ -40163,10 +41306,12 @@ __export(index_exports, {
40163
41306
  looksLikeState: () => looksLikeState,
40164
41307
  makeDgmoError: () => makeDgmoError,
40165
41308
  matchColorParens: () => matchColorParens,
41309
+ matchesContiguously: () => matchesContiguously,
40166
41310
  mix: () => mix,
40167
41311
  monokaiPalette: () => monokaiPalette,
40168
41312
  nord: () => nord,
40169
41313
  nordPalette: () => nordPalette,
41314
+ normalizeChartTypePrompt: () => normalize,
40170
41315
  oneDarkPalette: () => oneDarkPalette,
40171
41316
  orderArcNodes: () => orderArcNodes,
40172
41317
  parseAndLayoutInfra: () => parseAndLayoutInfra,
@@ -40190,7 +41335,9 @@ __export(index_exports, {
40190
41335
  parseKanban: () => parseKanban,
40191
41336
  parseMindmap: () => parseMindmap,
40192
41337
  parseOrg: () => parseOrg,
41338
+ parsePyramid: () => parsePyramid,
40193
41339
  parseSequenceDgmo: () => parseSequenceDgmo,
41340
+ parseSequenceDiagram: () => parseSequenceDgmo,
40194
41341
  parseSitemap: () => parseSitemap,
40195
41342
  parseState: () => parseState,
40196
41343
  parseTechRadar: () => parseTechRadar,
@@ -40233,6 +41380,8 @@ __export(index_exports, {
40233
41380
  renderMindmapForExport: () => renderMindmapForExport,
40234
41381
  renderOrg: () => renderOrg,
40235
41382
  renderOrgForExport: () => renderOrgForExport,
41383
+ renderPyramid: () => renderPyramid,
41384
+ renderPyramidForExport: () => renderPyramidForExport,
40236
41385
  renderQuadrant: () => renderQuadrant,
40237
41386
  renderQuadrantFocus: () => renderQuadrantFocus,
40238
41387
  renderQuadrantFocusForExport: () => renderQuadrantFocusForExport,
@@ -40254,9 +41403,11 @@ __export(index_exports, {
40254
41403
  resolveTaskName: () => resolveTaskName,
40255
41404
  rollUpContextRelationships: () => rollUpContextRelationships,
40256
41405
  rosePinePalette: () => rosePinePalette,
41406
+ scoreChartType: () => scoreChartType,
40257
41407
  seriesColors: () => seriesColors,
40258
41408
  shade: () => shade,
40259
41409
  solarizedPalette: () => solarizedPalette,
41410
+ suggestChartTypes: () => suggestChartTypes,
40260
41411
  tint: () => tint,
40261
41412
  tokyoNightPalette: () => tokyoNightPalette,
40262
41413
  truncateBareUrl: () => truncateBareUrl,
@@ -40303,8 +41454,7 @@ async function render(content, options) {
40303
41454
  const theme = options?.theme ?? "light";
40304
41455
  const paletteName = options?.palette ?? "nord";
40305
41456
  const paletteColors = getPalette(paletteName)[theme === "dark" ? "dark" : "light"];
40306
- const { diagnostics } = parseDgmo(content);
40307
- const chartType = parseDgmoChartType(content);
41457
+ const { diagnostics, chartType } = parseDgmo(content);
40308
41458
  const category = chartType ? getRenderCategory(chartType) : null;
40309
41459
  const viewState = options?.viewState ?? (options?.legendState ? {
40310
41460
  tag: options.legendState.activeGroup ?? void 0,
@@ -40328,6 +41478,83 @@ async function render(content, options) {
40328
41478
  return { svg, diagnostics };
40329
41479
  }
40330
41480
 
41481
+ // src/index.ts
41482
+ init_chart_types();
41483
+
41484
+ // src/chart-type-scoring.ts
41485
+ init_chart_types();
41486
+ var TYPOGRAPHIC_REPLACEMENTS = [
41487
+ [/[‘’]/g, "'"],
41488
+ // curly single quotes
41489
+ [/[“”]/g, '"'],
41490
+ // curly double quotes
41491
+ [/[–—]/g, "-"],
41492
+ // en/em dash
41493
+ [/×/g, "x"]
41494
+ // unicode multiplication → ASCII (for "2×2" vs "2x2")
41495
+ ];
41496
+ function normalize(s) {
41497
+ let out = s.normalize("NFKD").toLowerCase();
41498
+ for (const [re, repl] of TYPOGRAPHIC_REPLACEMENTS)
41499
+ out = out.replace(re, repl);
41500
+ return out.split(/[^a-z0-9]+/).filter(Boolean);
41501
+ }
41502
+ function matchesContiguously(promptTokens, triggerTokens) {
41503
+ if (triggerTokens.length === 0 || triggerTokens.length > promptTokens.length)
41504
+ return false;
41505
+ outer: for (let i = 0; i <= promptTokens.length - triggerTokens.length; i++) {
41506
+ for (let j = 0; j < triggerTokens.length; j++) {
41507
+ if (promptTokens[i + j] !== triggerTokens[j]) continue outer;
41508
+ }
41509
+ return true;
41510
+ }
41511
+ return false;
41512
+ }
41513
+ function scoreChartType(prompt, type) {
41514
+ const promptTokens = normalize(prompt);
41515
+ const matched = [];
41516
+ let score = 0;
41517
+ for (const trigger of type.triggers) {
41518
+ const triggerTokens = normalize(trigger);
41519
+ if (matchesContiguously(promptTokens, triggerTokens)) {
41520
+ matched.push(trigger);
41521
+ score += triggerTokens.length;
41522
+ }
41523
+ }
41524
+ const descTokens = new Set(normalize(type.description));
41525
+ let descHits = 0;
41526
+ for (const t of promptTokens) if (descTokens.has(t)) descHits++;
41527
+ score += descHits * 0.25;
41528
+ return { score, matched };
41529
+ }
41530
+ var MIN_PRIMARY_SCORE = 1;
41531
+ var AMBIGUITY_THRESHOLD = 0.5;
41532
+ function confidence(top, second) {
41533
+ if (top < MIN_PRIMARY_SCORE) return "ambiguous";
41534
+ if (second === 0) return "high";
41535
+ if (top >= second * 2) return "high";
41536
+ if (top - second < AMBIGUITY_THRESHOLD) return "ambiguous";
41537
+ return "medium";
41538
+ }
41539
+ function suggestChartTypes(prompt) {
41540
+ const scored = [];
41541
+ for (const type of chartTypes) {
41542
+ const { score, matched } = scoreChartType(prompt, type);
41543
+ if (score > 0) scored.push({ type, score, matched });
41544
+ }
41545
+ scored.sort((a, b) => b.score - a.score);
41546
+ const fallback = chartTypes.filter((c) => c.fallback);
41547
+ const topScore = scored[0]?.score ?? 0;
41548
+ const secondScore = scored[1]?.score ?? 0;
41549
+ const fellBack = topScore < MIN_PRIMARY_SCORE;
41550
+ return {
41551
+ ranked: scored,
41552
+ fallback,
41553
+ confidence: confidence(topScore, secondScore),
41554
+ fellBack
41555
+ };
41556
+ }
41557
+
40331
41558
  // src/index.ts
40332
41559
  init_dgmo_router();
40333
41560
  init_chart();
@@ -40625,6 +41852,8 @@ init_renderer14();
40625
41852
  init_parser15();
40626
41853
  init_layout12();
40627
41854
  init_renderer13();
41855
+ init_parser16();
41856
+ init_renderer15();
40628
41857
 
40629
41858
  // src/org/resolver.ts
40630
41859
  init_diagnostics();
@@ -40960,7 +42189,7 @@ init_legend_constants();
40960
42189
  init_legend_d3();
40961
42190
  init_legend_layout();
40962
42191
  init_d3();
40963
- init_renderer15();
42192
+ init_renderer16();
40964
42193
  init_collapse4();
40965
42194
  init_colors();
40966
42195
  init_palettes();
@@ -41063,6 +42292,7 @@ init_flowchart_parser();
41063
42292
  init_parser8();
41064
42293
  init_parser2();
41065
42294
  init_parsing();
42295
+ init_dgmo_router();
41066
42296
  var extractorRegistry = /* @__PURE__ */ new Map();
41067
42297
  function registerExtractor(kind, fn) {
41068
42298
  extractorRegistry.set(kind, fn);
@@ -41236,10 +42466,6 @@ var COMPLETION_REGISTRY = /* @__PURE__ */ new Map([
41236
42466
  description: "Show activation bars",
41237
42467
  values: ["on", "off"]
41238
42468
  },
41239
- "collapse-notes": {
41240
- description: "Collapse note blocks",
41241
- values: ["yes", "no"]
41242
- },
41243
42469
  "active-tag": { description: "Active tag group name" }
41244
42470
  })
41245
42471
  ],
@@ -41378,51 +42604,16 @@ var COMPLETION_REGISTRY = /* @__PURE__ */ new Map([
41378
42604
  "no-legend": { description: "Hide the score legend" },
41379
42605
  persona: { description: "Define the journey persona" }
41380
42606
  })
42607
+ ],
42608
+ [
42609
+ "pyramid",
42610
+ withGlobals({
42611
+ inverted: { description: "Flip apex to the bottom (funnel orientation)" },
42612
+ color: { description: "Override layer color (pipe metadata)" },
42613
+ description: { description: "Layer description (pipe or indented body)" }
42614
+ })
41381
42615
  ]
41382
42616
  ]);
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
42617
  var CHART_TYPES = [...ALL_CHART_TYPES].filter((t) => t !== "multi-line").map((name) => ({
41427
42618
  name,
41428
42619
  description: CHART_TYPE_DESCRIPTIONS[name] ?? name
@@ -42012,13 +43203,16 @@ init_parsing();
42012
43203
  // Annotate the CommonJS export names for ESM import in node:
42013
43204
  0 && (module.exports = {
42014
43205
  ALL_CHART_TYPES,
43206
+ AMBIGUITY_THRESHOLD,
42015
43207
  ARROW_DIAGNOSTIC_CODES,
42016
43208
  CHART_TYPES,
43209
+ CHART_TYPE_DESCRIPTIONS,
42017
43210
  COMPLETION_REGISTRY,
42018
43211
  ENTITY_TYPES,
42019
43212
  INFRA_BEHAVIOR_KEYS,
42020
43213
  LEGEND_HEIGHT,
42021
43214
  METADATA_KEY_SET,
43215
+ MIN_PRIMARY_SCORE,
42022
43216
  PIPE_METADATA,
42023
43217
  RECOGNIZED_COLOR_NAMES,
42024
43218
  RULE_COUNT,
@@ -42034,13 +43228,15 @@ init_parsing();
42034
43228
  buildTagLaneRowList,
42035
43229
  calculateSchedule,
42036
43230
  catppuccinPalette,
43231
+ chartTypeConfidence,
43232
+ chartTypeParsers,
43233
+ chartTypes,
42037
43234
  collapseBoxesAndLines,
42038
43235
  collapseMindmapTree,
42039
43236
  collapseOrgTree,
42040
43237
  collapseSitemapTree,
42041
43238
  collapseStateGroups,
42042
43239
  collectDiagramRoles,
42043
- collectNoteLineNumbers,
42044
43240
  collectTasks,
42045
43241
  colorNames,
42046
43242
  computeActivations,
@@ -42061,8 +43257,10 @@ init_parsing();
42061
43257
  encodeViewState,
42062
43258
  extractDiagramSymbols,
42063
43259
  extractTagDeclarations,
43260
+ focusOrgTree,
42064
43261
  formatDateLabel,
42065
43262
  formatDgmoError,
43263
+ getAllChartTypes,
42066
43264
  getAvailablePalettes,
42067
43265
  getExtendedChartLegendGroups,
42068
43266
  getLegendReservedHeight,
@@ -42084,6 +43282,7 @@ init_parsing();
42084
43282
  isSequenceBlock,
42085
43283
  isSequenceNote,
42086
43284
  isValidHex,
43285
+ knownChartTypeIds,
42087
43286
  layoutBoxesAndLines,
42088
43287
  layoutC4Components,
42089
43288
  layoutC4Containers,
@@ -42106,10 +43305,12 @@ init_parsing();
42106
43305
  looksLikeState,
42107
43306
  makeDgmoError,
42108
43307
  matchColorParens,
43308
+ matchesContiguously,
42109
43309
  mix,
42110
43310
  monokaiPalette,
42111
43311
  nord,
42112
43312
  nordPalette,
43313
+ normalizeChartTypePrompt,
42113
43314
  oneDarkPalette,
42114
43315
  orderArcNodes,
42115
43316
  parseAndLayoutInfra,
@@ -42133,7 +43334,9 @@ init_parsing();
42133
43334
  parseKanban,
42134
43335
  parseMindmap,
42135
43336
  parseOrg,
43337
+ parsePyramid,
42136
43338
  parseSequenceDgmo,
43339
+ parseSequenceDiagram,
42137
43340
  parseSitemap,
42138
43341
  parseState,
42139
43342
  parseTechRadar,
@@ -42176,6 +43379,8 @@ init_parsing();
42176
43379
  renderMindmapForExport,
42177
43380
  renderOrg,
42178
43381
  renderOrgForExport,
43382
+ renderPyramid,
43383
+ renderPyramidForExport,
42179
43384
  renderQuadrant,
42180
43385
  renderQuadrantFocus,
42181
43386
  renderQuadrantFocusForExport,
@@ -42197,9 +43402,11 @@ init_parsing();
42197
43402
  resolveTaskName,
42198
43403
  rollUpContextRelationships,
42199
43404
  rosePinePalette,
43405
+ scoreChartType,
42200
43406
  seriesColors,
42201
43407
  shade,
42202
43408
  solarizedPalette,
43409
+ suggestChartTypes,
42203
43410
  tint,
42204
43411
  tokyoNightPalette,
42205
43412
  truncateBareUrl,